From: jweigele Date: Thu, 15 Sep 2022 22:36:18 +0000 (-0700) Subject: This now works with basic audio effects, though clips a bit with too many running... X-Git-Url: http://git.hexthepla.net/?a=commitdiff_plain;h=3e99f9c48fba501463441e4929a5149ab6328212;p=grahbot This now works with basic audio effects, though clips a bit with too many running at once --- diff --git a/ffmpegfile.py b/ffmpegfile.py index 927fd48..47cbd43 100644 --- a/ffmpegfile.py +++ b/ffmpegfile.py @@ -10,6 +10,7 @@ import logging import threading import numpy import io +import pedalboard from pedalboard.io import AudioFile log = logging.getLogger(__name__) @@ -119,6 +120,7 @@ class AudioBuffer(discord.AudioSource): retval = AudioChunk(b'') return retval + # get the number of samples in the deck back up to DECK_SIZE def fill_deck(self): # block if you got nothin if self.deck_size == 0: @@ -135,6 +137,7 @@ class AudioBuffer(discord.AudioSource): self.on_deck.append(self.get_next_sample()) self.sample_lock.release() + # once the deck empties, will fill with zeroes and thus silence def terminate_all(self): self.sample_lock.acquire() self.samples = [] @@ -145,70 +148,49 @@ class AudioBuffer(discord.AudioSource): #fifo_path = '/tmp/fifo{}'.format(rand_str) locked = False try: - #os.mkfifo(fifo_path) - #args = ['ffmpeg', '-i', '{}'.format(filename)] - #args += ['-f', 's16le', '-acodec', 'pcm_s16le', '-ac', '2', '-ar', '48000', '-'] - #p = await asyncio.create_subprocess_exec(*args, stdin=None, stderr=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE) - #return_fifo = open(fifo_path, 'rb') - #return_bytes = await asyncio.to_thread(return_fifo.read) - #return_bytes = return_fifo.read() - #stdout, stderr = await p.communicate() - #log.debug(stdout) - #log.debug(stderr) - #return_bytes = stdout - #await p.wait() - #return_bytes = p.communicate()[0] - #byte_length = len(return_bytes) - #sample_index = 0 #len(self.samples) min_deck_size = self.DECK_SIZE - byte_start = 0 #return_byte_length = len(return_bytes) chunks_to_append = [] #byte_reader = io.BytesIO(return_bytes) # byte processing and chunk generation - #while byte_length > 0: - #log.debug(byte_length) - # keeps us from tailing off the end of the bytestream -# if MAX_LENGTH > byte_length: -# sample_bytes = byte_length -# else: -# sample_bytes = MAX_LENGTH -# log.debug('byte start {} sample bytes {}'.format(byte_start, sample_bytes)) - # read in the next chunk, store for now #next_bytes = byte_reader.read(MAX_LENGTH) + buffer_samples = None with AudioFile(filename).resampled_to(target_sample_rate=SAMPLE_RATE) as f: - #audio = f.read(f.frames) - next_samples = f.read(MAX_SAMPLES) + # we're reading in the whole file at once, maybe don't want to later for better responsiveness? + buffer_samples = f.read(f.frames) + #log.debug('samples length: {}'.format(len(buffer_samples))) + # process here with pedalboard + shift_tones = random.randint(-10, 10) + board = pedalboard.Pedalboard([ + pedalboard.PitchShift(semitones=shift_tones), + pedalboard.Gain(gain_db=-3.0) + ]) + buffer_samples = board(buffer_samples, SAMPLE_RATE) + index = 0 + + next_samples = buffer_samples[0][index:index+MAX_SAMPLES] + while len(next_samples) > 0: channels = len(next_samples.shape) - #log.debug(next_samples) - while numpy.size(next_samples) > 0: - # switch to mono if needed (channel shouldn't change from initial) + while len(next_samples) > 0: + # this is to format the samples in the style opus wants #log.debug('channels are {}'.format(channels)) if channels == 2: - #log.debug('before deleting {}'.format(next_samples.shape)) - #log.debug(next_samples[0][:10]) - #next_samples = numpy.squeeze(next_samples.reshape(1, -1)) - # total length * 2 - rechannel = numpy.empty(next_samples[0].shape[0]*2, dtype='float32') - # first channel - rechannel[::2] = next_samples[0] - # second channel - rechannel[1::2] = next_samples[1] - next_samples = rechannel - #log.debug('after deleting {}'.format(next_samples.shape)) - #log.debug(next_samples[:10]) + # interleave the samples L/R to form a bytestream eventually + next_samples = numpy.squeeze(numpy.dstack((next_samples[0], next_samples[1])).reshape(1, -1)) + # right now, this is the only path taken (because we discard the other channel) + else: + next_samples = numpy.repeat(next_samples, 2) # for the downcast to int16 next_samples *= 32768.0 next_bytes = next_samples.astype('int16') chunk = AudioChunk(init_samples=next_bytes) #log.debug(chunk, chunk.samples) chunks_to_append.append(chunk) - next_samples = f.read(MAX_SAMPLES) + # get next chunk + index += MAX_SAMPLES + next_samples = buffer_samples[0][index:index+MAX_SAMPLES] - # chomp - #byte_start += sample_bytes - #byte_length = return_byte_length - byte_start # chunk append/insertion (along with mixing) # this is the part that actually needs locked access