This now works with basic audio effects, though clips a bit with too many running...
authorjweigele <jweigele@local>
Thu, 15 Sep 2022 22:36:18 +0000 (15:36 -0700)
committerjweigele <jweigele@local>
Thu, 15 Sep 2022 22:36:18 +0000 (15:36 -0700)
ffmpegfile.py

index 927fd48309296fc79e082e46e401e95f9a566979..47cbd43bd114f38bb3165018eb01921a7bc939b8 100644 (file)
@@ -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