As the title says, I've been trying to send chunks audio data using discord.py to a voice channel. So using pydub I basically load an mp3 file (and playback works so file is ok), make chunks, send them as packages. And then I tried to convert the mp3 file to opus file. It's playing too. Both files are ok.
There are a few stuff:
So the issue is, after I make the raw audio chunks list and try to send each element in it:
a) With Encode=True, I get an error about NoneType object not having encode attribute.
b) With Encode=False, it actually sends the data but I don't hear anything at all.
c) With Encode=False, but when loading the mp3 file with additional codec="opus" parameter, I hear gibberish noise. So it's not for conversion i guess okay...
d) With Encode=False, but when loading the opus file with additional codec="opus" parameter (without that codec="opus" i cant play the audio anyway), it's still the NoneType error.
-I have all the 3 executables (ffmpeg.exe, ffplay.exe and ffprobe.exe) in the same directory as the script. But not in PATH. (Didn't tell me anything about it and playback worked, plus i heard the gibberish noise too at least so no problem here)
-I also have m1.mp3 and m1.opus in the same directory as the script. It's ok.
-There are no subdirectories or anything else.
-I have PyNaCl and discord.py[audio] installed.
I don't know where I'm doing wrong. What kind of data was i supposed to send if not this? Can I encode the raw audio myself to opus without too much work and definitely not saving a file even if it is temp? I don't want to just load the mp3 file all at once and play, I need to be able to make chunks. I don't want to save these chunks anywhere either.
Here is the code:
import discord
import pydub
import os
from pydub.utils import make_chunks
import asyncio
TOKEN = ":("
base_path = os.getcwd()
pydub.AudioSegment.ffmpeg = os.path.join(base_path, "ffmpeg.exe")
pydub.AudioSegment.ffprobe = os.path.join(base_path, "ffprobe.exe")
pydub.AudioSegment.ffplay = os.path.join(base_path, "ffplay.exe")
pydub.AudioSegment.converter = os.path.join(base_path, "ffmpeg.exe")
#audio = pydub.AudioSegment.from_file(os.path.join(base_path, "m1.mp3"), format="mp3")#, codec="opus")
audio = pydub.AudioSegment.from_file(os.path.join(base_path, "m1.opus"), codec="opus") #dont add format="opus". format isnt extension ig. https://github.com/jiaaro/pydub/issues/257
#audio = pydub.AudioSegment.from_file(os.path.join(base_path, "m1.opus")) #didnt hear anything at all evn with play(audio) below.
audio = audio[:9*1000] #first 9 seconds
#let me be sure that it actually plays.
from pydub.playback import play #needs simpleaudio to work idk...
play(audio)
chunks = make_chunks(audio, (1/20)*1000)
client = discord.Client()
@client.event
async def on_ready():
print("Ready.")
@client.event
async def on_message(message):
if message.content.startswith(",test"):
#connect to vc
vp = await message.author.voice.channel.connect()
#i tried with chunk._data as well. they are same anyway.
chunks_raw = [chunk.raw_data for chunk in chunks]
#print(chunks_raw == [chunk._data for chunk in chunks]) #it's True
print("Sending?")
for i, chunk in enumerate(chunks_raw, start=1):
vp.send_audio_packet(chunk, encode=True) #false --> not heard, true --> weird error (that occurs no matter if i have codec="opus" or not)
print("Sent", i, "out of", len(chunks), "chunks.")
await asyncio.sleep(1/20)
vp.cleanup()
await vp.disconnect()
await message.channel.send("Playback is over.")
client.run(TOKEN)
so if encode=True i get this (and i have no idea why):
Ignoring exception in on_message
Traceback (most recent call last):
File "C:\Users\h\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\client.py", line 343, in _run_event
await coro(*args, **kwargs)
File "C:\Users\h\Desktop\randomly lying pys\bas\broken_audio_showcase.py", line 38, in on_message
vp.send_audio_packet(chunk, encode=True) #false --> not heard, true --> weird error
File "C:\Users\h\AppData\Local\Programs\Python\Python39\lib\site-packages\discord\voice_client.py", line 633, in send_audio_packet
encoded_data = self.encoder.encode(data, self.encoder.SAMPLES_PER_FRAME)
AttributeError: 'NoneType' object has no attribute 'encode'
and if encode=False i can see "Sent i out of len(chunks) chunks" in console, yet i cant hear anything. I also see "Playback is over" message when it ends so it actually sends something...
if i go ahead and change the voice_client.py myself to add this to the first line of send_audio_packet function:
self.encoder = opus.Encoder() #manually added
and then i set encode to True, then it actually sends a hearable voice. of course its gibberish again, but at least i hear something from discord voice chat.
can anyone actually help me with this?
Well I knew sound would be confusing, but never expected it to be this hard. I have no idea what counts as opus if not an opus file itself that's been converted via ffmpeg, not by literally changing the extension or anything.