def createDecoder(freq, channels): # Just as with the encoder, to create a decoder, we must first # allocate resources for it. We want Python to be responsible for # the memory deallocation, and thus Python must be responsible for # the initial memory allocation. # The frequency must be passed in as a 32-bit int freq = opus.opus_int32(freq) # The number of channels must also be passed in as a 32-bit int channels = opus.opus_int32(channels) # Obtain the number of bytes of memory required for the decoder size = opus.opus_decoder_get_size(channels) # Allocate the required memory for the decoder memory = ctypes.create_string_buffer(size) # Cast the newly-allocated memory as a pointer to a decoder. We # could also have used opus.od_p as the pointer type, but writing # it out in full may be clearer. decoder = ctypes.cast(memory, ctypes.POINTER(opus.OpusDecoder)) # Initialise the decoder error = opus.opus_decoder_init(decoder, freq, channels) # Check that there hasn't been an error when initialising the # decoder if error != opus.OPUS_OK: raise Exception("An error occurred while creating the decoder: " + opus.opus_strerror(error).decode("utf")) # Return our newly-created decoder return decoder
def encode_next_frame(source_ptr): global length_bytes, bytesProcessed, frame_size, frame_sizes, frame_size_index if bytesProcessed >= length_bytes: print("WARNING: No more data") return None print("Processing frame at source_ptr ", source_ptr.value) # Check if we have enough source data remaining to process at # the current frame size print("length_bytes: ", length_bytes) print("bytesProcessed: ", bytesProcessed) print("bytes remaining (length_bytes - bytesProcessed):", length_bytes - bytesProcessed) print("frameSizeBytes(frame_size):", frame_size_bytes(frame_size)) while length_bytes - bytesProcessed < frame_size_bytes(frame_size): print("Warning! Not enough data for frame.") frame_size_index -= 1 if frame_size_index < 0: # The data is less than the smallest number of samples # in a frame. Either we ignore the remaining samples # and shorten the audio, or we pad the frame with # zeros and lengthen the audio. We'll take the easy # option and shorten the audio. break frame_size = frame_sizes[frame_size_index] print("Decreased frame size to ", frame_size) if frame_size_index < 0: print("Warning! Ignoring samples at the end of the audio\n" + "as they do not fit into even the smallest frame.") # FIXME: This last frame probably isn't correct return # Encode the audio print("Encoding audio") numBytes = opus.opus_encode( encoder, ctypes.cast(source_ptr, ctypes.POINTER(opus.opus_int16)), frame_size, encoded_frame_ptr, max_encoded_frame_bytes) print("numBytes: ", numBytes) # Check for any errors during encoding if numBytes < 0: raise Exception("Encoder error detected: " + opus.opus_strerror(numBytes).decode("utf")) # Move to next position in the buffer: encoder oldAddress = source_ptr.value #print("oldAddress:",oldAddress) deltaBytes = frame_size * source_channels * 2 newAddress = oldAddress + deltaBytes #print("newAddress:",newAddress) source_ptr = ctypes.c_void_p(newAddress) bytesProcessed = source_ptr.value - source_ptr_init.value return (source_ptr, encoded_frame_ptr, numBytes)
def create_encoder(npBufSource, samples_per_second): # To create an encoder, we must first allocate resources for it. # We want Python to be responsible for the memory deallocation, # and thus Python must be responsible for the initial memory # allocation. # Opus can encode both speech and music, and it can automatically # detect when the source swaps between the two. Here we specify # automatic detection. application = opus.OPUS_APPLICATION_AUDIO # The frequency must be passed in as a 32-bit int samples_per_second = opus.opus_int32(samples_per_second) # The number of channels can be obtained from the shape of the # NumPy array that was passed in as npBufSource channels = npBufSource.shape[1] # Obtain the number of bytes of memory required for the encoder size = opus.opus_encoder_get_size(channels); # Allocate the required memory for the encoder memory = ctypes.create_string_buffer(size) # Cast the newly-allocated memory as a pointer to an encoder. We # could also have used opus.oe_p as the pointer type, but writing # it out in full may be clearer. encoder = ctypes.cast(memory, ctypes.POINTER(opus.OpusEncoder)) # Initialise the encoder error = opus.opus_encoder_init( encoder, samples_per_second, channels, application ) # Check that there hasn't been an error when initialising the # encoder if error != opus.OPUS_OK: raise Exception("An error occurred while creating the encoder: "+ opus.opus_strerror(error).decode("utf")) # Return our newly-created encoder return encoder
def datagramReceived(self, encoded_frame, addr): #print("Received data from", addr) # TODO: Once we've started receiving data from a given # address, should we close down this port to other addresses? # Create a buffer to hold the PCM data pcm_buf = PCMBufType() # Get pointer to first element of the PCM buffer pcm_buf_ptr = ctypes.cast(pcm_buf, ctypes.POINTER(opus.opus_int16)) # Get pointer to encoded frame encoded_frame_ptr = ctypes.cast(encoded_frame, ctypes.POINTER(ctypes.c_ubyte)) encoded_frame_bytes = len(encoded_frame) # Decode the frame num_samples = opus.opus_decode( decoder_ptr, encoded_frame_ptr, encoded_frame_bytes, pcm_buf_ptr, samples_per_channel_in_buf, 0 # FIXME: What's this about? ) # Check for any errors during decoding if num_samples < 0: raise Exception("Decoder error detected: "+ opus.opus_strerror(numSamples).decode("utf")) # Create a numpy array to hold the decoded PCM data. Note # that the numpy array has the correct shape (whereas the # original PCM buffer had sufficient space allocated for the # largest possible frame. np_pcm_buf = numpy.ctypeslib.as_array( pcm_buf, (num_samples//channels, channels) ) np_pcm_buf = np_pcm_buf[0:num_samples] # Put the samples on the queue to play q.put_nowait(np_pcm_buf)
def datagramReceived(self, data, addr): print("Received data from", addr) print("len(data):", len(data)) #print(data) # Create a buffer to hold the PCM data buf = TypeBuf() # Get pointer to first element of buf bufPtr = ctypes.cast(buf, ctypes.POINTER(opus.opus_int16)) # Get pointer to encoded frame encodedFramePtr = ctypes.cast(data, ctypes.POINTER(ctypes.c_ubyte)) numBytes = len(data) # Decode the frame numSamples = opus.opus_decode( decoderPtr, encodedFramePtr, numBytes, bufPtr, samples_per_channel_in_buf, 0 # FIXME: What's this about? ) print("numSamples: ", numSamples) # Check for any errors during decoding if numSamples < 0: raise Exception("Decoder error detected: " + opus.opus_strerror(numSamples).decode("utf")) # Put the samples on the queue to play np_buf = numpy.ctypeslib.as_array(buf, (numSamples // channels, channels)) np_buf = np_buf[0:numSamples] print("data placed on queue") #print("channels:", channels) print("shape:", np_buf.shape) q.put_nowait(np_buf)
def write_opus(self, output_filename): # Go through the frames and save them as an OggOpus file # Create a new stream state with a random serial number stream_state = opus_helpers.create_stream_state() # Create a packet (reused for each pass) ogg_packet = ogg.ogg_packet() # Flag to indicate the start of stream start_of_stream = 1 # Packet counter count_packets = 0 # PCM samples counter count_samples = 0 # Allocate memory for a page ogg_page = ogg.ogg_page() # Allocate storage space for the encoded frame. 4,000 bytes # is the recommended maximum buffer size for the encoded # frame. max_bytes_in_encoded_frame = opus.opus_int32(4000) EncodedFrameType = ctypes.c_ubyte * max_bytes_in_encoded_frame.value encoded_frame = EncodedFrameType() # Create a pointer to the first byte of the buffer for the # encoded frame. encoded_frame_ptr = ctypes.cast( ctypes.pointer(encoded_frame), ctypes.POINTER(ctypes.c_ubyte) ) # Open file for writing f = open(output_filename, "wb") # Headers # ======= # Specify the identification header id_header = opus.make_identification_header( pre_skip = self._pre_skip ) # Specify the packet containing the identification header ogg_packet.packet = ctypes.cast(id_header, ogg.c_uchar_p) ogg_packet.bytes = len(id_header) ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 0 ogg_packet.granulepos = 0 ogg_packet.packetno = count_packets start_of_stream = 0 count_packets += 1 # Write the header result = ogg.ogg_stream_packetin( stream_state, ogg_packet ) if result != 0: raise Exception("Failed to write Opus identification header") # Specify the comment header comment_header = opus.make_comment_header() # Specify the packet containing the identification header ogg_packet.packet = ctypes.cast(comment_header, ogg.c_uchar_p) ogg_packet.bytes = len(comment_header) ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 0 ogg_packet.granulepos = 0 ogg_packet.packetno = count_packets count_packets += 1 # Write the header result = ogg.ogg_stream_packetin( stream_state, ogg_packet ) if result != 0: raise Exception("Failed to write Opus comment header") # Write out pages to file while ogg.ogg_stream_flush(ctypes.pointer(stream_state), ctypes.pointer(ogg_page)) != 0: # Write page print("Writing header page") f.write(bytes(ogg_page.header[0:ogg_page.header_len])) f.write(bytes(ogg_page.body[0:ogg_page.body_len])) # Frames # ====== # Loop through the PCM frames in the queue while not q.empty(): # Get the frame from the queue frame_pcm = q.get_nowait() # Convert to opus_int16 frame_pcm = numpy.array(frame_pcm * 2**15, dtype=opus.opus_int16) # Create a pointer to the start of the frame's data source_ptr = frame_pcm.ctypes.data_as(ctypes.c_void_p) #print("Processing frame at sourcePtr ", sourcePtr.value) # Check if we have enough source data remaining to process at # the current frame size samples_per_frame = 960 assert len(frame_pcm) == samples_per_frame # Encode the audio #print("Encoding audio") num_bytes = opus.opus_encode( self._encoder, ctypes.cast(source_ptr, ctypes.POINTER(opus.opus_int16)), samples_per_frame, encoded_frame_ptr, max_bytes_in_encoded_frame ) #print("num_bytes: ", num_bytes) # Check for any errors during encoding if num_bytes < 0: raise Exception("Encoder error detected: "+ opus.opus_strerror(num_bytes).decode("utf")) # Writing OggOpus # =============== # Increase the number of samples count_samples += samples_per_frame # Place data into the packet ogg_packet.packet = encoded_frame_ptr ogg_packet.bytes = num_bytes ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 0 # FIXME: It needs to end! ogg_packet.granulepos = count_samples ogg_packet.packetno = count_packets # No longer the start of stream start_of_stream = 0 # Increase the number of packets count_packets += 1 # Place the packet in to the stream result = ogg.ogg_stream_packetin( stream_state, ogg_packet ) # Check for errors if result != 0: raise Exception("Error while placing packet in Ogg stream") # Write out pages to file while ogg.ogg_stream_pageout(ctypes.pointer(stream_state), ctypes.pointer(ogg_page)) != 0: # Write page print("Writing page") f.write(bytes(ogg_page.header[0:ogg_page.header_len])) f.write(bytes(ogg_page.body[0:ogg_page.body_len])) # Force the writing of the final page while ogg.ogg_stream_flush(ctypes.pointer(stream_state), ctypes.pointer(ogg_page)) != 0: # Write page print("Writing final page") f.write(bytes(ogg_page.header[0:ogg_page.header_len])) f.write(bytes(ogg_page.body[0:ogg_page.body_len])) # Make sure the queue is empty if not q.empty(): print("WARNING: Failed to completely process all the recorded frames") # Finished f.close() print("Finished writing file")
def process_frame(self, encoded_frame, seq_no): # Aquire the lock to ensure that we're threadsafe with self.__lock: # Mark the fact that we've started self.__started = True # Is the sequence number the one we're expecting? If not, # consider storing it for later processing. print("Expecting seq no", self.__expected_seq_no) if encoded_frame is None: print("Did not get seq no", seq_no) else: print("Got seq no", seq_no) if seq_no != self.__expected_seq_no: print("Found out of order frame") # Calculate the new frame's distance from the # currently expected sequence number. Because the # sequence numbers may roll over, we need to account # for that. distance = JitterBuffer.__calc_distance( seq_no, self.__expected_seq_no, JitterBuffer.seq_rollover) # Check if the frame is too late if distance > 0: print("Frame is ahead of what we were expecting; storing") self.__out_of_order_frames[seq_no] = encoded_frame else: print("Frame is behind what we were expecting; discarding") pass # Are there frames still in the queue? If not, then we # have a problem. The current frame isn't what we need, # and we're in immediate need of the one we're missing. # It's time to give up on the currently expected frame. if q.empty(): self.give_up_on_expected_sequence() return # The encoded frame is the next in the sequence, so process it # now. # Create a buffer to hold the PCM data pcm_buf = self.__PCMFrameBufferType() # Get pointer to first element of the PCM buffer pcm_buf_ptr = ctypes.cast(pcm_buf, ctypes.POINTER(opus.opus_int16)) # Get pointer to encoded frame if encoded_frame is None: encoded_frame_ptr = None encoded_frame_bytes = 0 else: encoded_frame_ptr = ctypes.cast(encoded_frame, ctypes.POINTER(ctypes.c_ubyte)) encoded_frame_bytes = len(encoded_frame) # Decode the frame num_samples = opus.opus_decode( self.__decoder_ptr, encoded_frame_ptr, encoded_frame_bytes, pcm_buf_ptr, self.__samples_per_channel_in_buf, 0 # FIXME: What's this about? ) # Check for any errors during decoding if num_samples < 0: raise Exception("Decoder error detected: " + opus.opus_strerror(numSamples).decode("utf")) # Create a numpy array to hold the decoded PCM data. Note # that the numpy array has the correct shape (whereas the # original PCM buffer had sufficient space allocated for the # largest possible frame. np_pcm_buf = numpy.ctypeslib.as_array( pcm_buf, (num_samples // channels, channels)) np_pcm_buf = np_pcm_buf[0:num_samples] # Put the samples on the queue to play q.put_nowait(np_pcm_buf) # We can now expect the next sequence number self.__expected_seq_no += 1 # If the next frame is already in the out-of-order dictionary, # process it now seq_no = self.__expected_seq_no if (seq_no in self.__out_of_order_frames): print("Processing previously stored, out-of-order, frame") self.process_frame(self.__out_of_order_frames[seq_no], seq_no) del self.__out_of_order_frames[seq_no]
def encodeThenDecode(npBufSource, npBufTarget, freq): # Encoding # ======== # Extract the number of channels in the source sourceChannels = npBufSource.shape[1] # Create an encoder encoder = createEncoder(npBufSource, freq) # Frame sizes are measured in number of samples. There are only a # specified number of possible valid frame durations for Opus, # which (assuming a frequency of 48kHz) gives the following valid # sizes. frameSizes = [120, 240, 480, 960, 1920, 2880] # Specify the desired frame size. This will be used for the vast # majority of the encoding, except possibly at the end of the # buffer (as there may not be sufficient data left to fill a # frame.) frameSizeIndex = 5 frameSize = frameSizes[frameSizeIndex] # Function to calculate the size of a frame in bytes def frameSizeBytes(frameSize): global bytesPerSample return frameSize * sourceChannels * bytesPerSample # Allocate storage space for the encoded frame. 4,000 bytes is # the recommended maximum buffer size for the encoded frame. maxEncodedFrameBytes = opus.opus_int32(4000) encodedFrameType = ctypes.c_ubyte * maxEncodedFrameBytes.value encodedFrame = encodedFrameType() # Create a pointer to the first byte of the buffer for the encoded # frame. encodedFramePtr = ctypes.cast(ctypes.pointer(encodedFrame), ctypes.POINTER(ctypes.c_ubyte)) # Number of bytes to process in buffer bytesPerSample = 2 lengthBytes = buf.shape[0] * buf.shape[1] * bytesPerSample # Saving # ====== # Create a new stream state with a random serial number stream_state = createStreamState() # Create a packet (reused for each pass) ogg_packet = ogg.ogg_packet() # Flag to indicate the start of stream start_of_stream = 1 # Packet counter count_packets = 0 # PCM samples counter count_samples = 0 # Allocate memory for a page ogg_page = ogg.ogg_page() # Open file for writing output_filename = "test.opus" f = open(output_filename, "wb") # Specify the identification header id_header = opus.make_identification_header(pre_skip=312) # Specify the packet containing the identification header ogg_packet.packet = ctypes.cast(id_header, ogg.c_uchar_p) ogg_packet.bytes = len(id_header) ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 0 ogg_packet.granulepos = 0 ogg_packet.packetno = count_packets start_of_stream = 0 count_packets += 1 # Write the header result = ogg.ogg_stream_packetin(stream_state, ogg_packet) if result != 0: raise Exception("Failed to write Opus identification header") # Specify the comment header comment_header = opus.make_comment_header() # Specify the packet containing the identification header ogg_packet.packet = ctypes.cast(comment_header, ogg.c_uchar_p) ogg_packet.bytes = len(comment_header) ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 0 ogg_packet.granulepos = 0 ogg_packet.packetno = count_packets count_packets += 1 # Write the header result = ogg.ogg_stream_packetin(stream_state, ogg_packet) if result != 0: raise Exception("Failed to write Opus comment header") # Write out pages to file while ogg.ogg_stream_flush(ctypes.pointer(stream_state), ctypes.pointer(ogg_page)) != 0: # Write page print("Writing page") f.write(bytes(ogg_page.header[0:ogg_page.header_len])) f.write(bytes(ogg_page.body[0:ogg_page.body_len])) # Decoding # ======== # Extract the number of channels for the target targetChannels = npBufTarget.shape[1] # Create a decoder decoderFreq = 48000 # TODO: Test changes to this decoderPtr = createDecoder(decoderFreq, targetChannels) # Encode and re-decode the audio # ============================== # Pointer to a location in the source buffer. We will increment # this as we progress through the encoding of the buffer. It # starts pointing to the first byte. sourcePtr = npBufSource.ctypes.data_as(ctypes.c_void_p) sourcePtr_init = sourcePtr # The number of bytes processed will be the difference between the # pointer's current location and the address of the first byte. bytesProcessed = sourcePtr.value - sourcePtr_init.value # Pointer to a location in the target buffer. We will increment # this as we progress through re-decoding each encoded frame. targetPtr = npBufTarget.ctypes.data_as(ctypes.c_void_p) # Loop through the source buffer while bytesProcessed < lengthBytes: print("Processing frame at sourcePtr ", sourcePtr.value) # Check if we have enough source data remaining to process at # the current frame size print("lengthBytes: ", lengthBytes) print("bytesProcessed: ", bytesProcessed) print("bytes remaining (lengthBytes - bytesProcessed):", lengthBytes - bytesProcessed) print("frameSizeBytes(frameSize):", frameSizeBytes(frameSize)) while lengthBytes - bytesProcessed < frameSizeBytes(frameSize): print("Warning! Not enough data for frame.") frameSizeIndex -= 1 if frameSizeIndex < 0: # The data is less than the smallest number of samples # in a frame. Either we ignore the remaining samples # and shorten the audio, or we pad the frame with # zeros and lengthen the audio. We'll take the easy # option and shorten the audio. break frameSize = frameSizes[frameSizeIndex] print("Decreased frame size to ", frameSize) if frameSizeIndex < 0: print("Warning! Ignoring samples at the end of the audio\n" + "as they do not fit into even the smallest frame.") break # Encode the audio print("Encoding audio") numBytes = opus.opus_encode( encoder, ctypes.cast(sourcePtr, ctypes.POINTER(opus.opus_int16)), frameSize, encodedFramePtr, maxEncodedFrameBytes) print("numBytes: ", numBytes) # Check for any errors during encoding if numBytes < 0: raise Exception("Encoder error detected: " + opus.opus_strerror(numBytes).decode("utf")) # Move to next position in the buffer: encoder oldAddress = sourcePtr.value #print("oldAddress:",oldAddress) deltaBytes = frameSize * sourceChannels * 2 newAddress = oldAddress + deltaBytes #print("newAddress:",newAddress) sourcePtr = ctypes.c_void_p(newAddress) bytesProcessed = sourcePtr.value - sourcePtr_init.value # Writing OggOpus # =============== # Increase the number of samples count_samples += frameSize # Place data into the packet ogg_packet.packet = encodedFramePtr ogg_packet.bytes = numBytes ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 0 # FIXME: It needs to end! ogg_packet.granulepos = count_samples ogg_packet.packetno = count_packets # No longer the start of stream start_of_stream = 0 # Increase the number of packets count_packets += 1 # Place the packet in to the stream result = ogg.ogg_stream_packetin(stream_state, ogg_packet) # Check for errors if result != 0: raise Exception("Error while placing packet in Ogg stream") # Write out pages to file while ogg.ogg_stream_pageout(ctypes.pointer(stream_state), ctypes.pointer(ogg_page)) != 0: # Write page print("Writing page") f.write(bytes(ogg_page.header[0:ogg_page.header_len])) f.write(bytes(ogg_page.body[0:ogg_page.body_len])) # Decode the audio if True: print("Decoding audio") numSamples = opus.opus_decode( decoderPtr, encodedFramePtr, numBytes, ctypes.cast(targetPtr, ctypes.POINTER(ctypes.c_short)), 5760, # Max space required in PCM 0 # What's this about? ) print("numSamples: ", numSamples) # Check for any errors during decoding if numSamples < 0: raise Exception("Decoder error detected: " + opus.opus_strerror(numSamples).decode("utf")) # Move to next position in the buffer: decoder targetPtr.value += numSamples * targetChannels * 2 # Write a packet saying we're at the end of the stream # Place data into the packet ogg_packet.packet = None ogg_packet.bytes = 0 ogg_packet.b_o_s = start_of_stream ogg_packet.e_o_s = 1 ogg_packet.granulepos = count_samples ogg_packet.packetno = count_packets # Increase the number of packets count_packets += 1 # Place the packet in to the stream #result = ogg.ogg_stream_packetin( # stream_state, # ogg_packet #) # Check for errors if result != 0: raise Exception("Error while placing packet in Ogg stream") # Write out pages to file while ogg.ogg_stream_pageout(ctypes.pointer(stream_state), ctypes.pointer(ogg_page)) != 0: # Write page print("Writing page") f.write(bytes(ogg_page.header[0:ogg_page.header_len])) f.write(bytes(ogg_page.body[0:ogg_page.body_len])) # Close file f.close()
def encodeThenDecode(npBufSource, npBufTarget, freq): # Encoding # ======== # Extract the number of channels in the source sourceChannels = npBufSource.shape[1] # Create an encoder encoder = createEncoder(npBufSource, freq) # Frame sizes are measured in number of samples. There are only a # specified number of possible valid frame durations for Opus, # which (assuming a frequency of 48kHz) gives the following valid # sizes. frameSizes = [120, 240, 480, 960, 1920, 2880] # Specify the desired frame size. This will be used for the vast # majority of the encoding, except possibly at the end of the # buffer (as there may not be sufficient data left to fill a # frame.) frameSizeIndex = 5 frameSize = frameSizes[frameSizeIndex] # Function to calculate the size of a frame in bytes def frameSizeBytes(frameSize): global bytesPerSample return frameSize * sourceChannels * bytesPerSample # Allocate storage space for the encoded frame. 4,000 bytes is # the recommended maximum buffer size for the encoded frame. maxEncodedFrameBytes = opus.opus_int32(4000) encodedFrameType = ctypes.c_ubyte * maxEncodedFrameBytes.value encodedFrame = encodedFrameType() # Create a pointer to the first byte of the buffer for the encoded # frame. encodedFramePtr = ctypes.cast(ctypes.pointer(encodedFrame), ctypes.POINTER(ctypes.c_ubyte)) # Number of bytes to process in buffer bytesPerSample = 2 lengthBytes = buf.shape[0] * buf.shape[1] * bytesPerSample # Decoding # ======== # Extract the number of channels for the target targetChannels = npBufTarget.shape[1] # Create a decoder decoderFreq = 48000 # TODO: Test changes to this decoderPtr = createDecoder(decoderFreq, targetChannels) # Encode and re-decode the audio # ============================== # Pointer to a location in the source buffer. We will increment # this as we progress through the encoding of the buffer. It # starts pointing to the first byte. sourcePtr = npBufSource.ctypes.data_as(ctypes.c_void_p) sourcePtr_init = sourcePtr # The number of bytes processed will be the difference between the # pointer's current location and the address of the first byte. bytesProcessed = sourcePtr.value - sourcePtr_init.value # Pointer to a location in the target buffer. We will increment # this as we progress through re-decoding each encoded frame. targetPtr = npBufTarget.ctypes.data_as(ctypes.c_void_p) # Loop through the source buffer count = 0 # FIXME: debugging only while bytesProcessed < lengthBytes: print("processing frame: ", count) count += 1 print("Processing frame at sourcePtr ", sourcePtr.value) # Check if we have enough source data remaining to process at # the current frame size print("lengthBytes: ", lengthBytes) print("bytesProcessed: ", bytesProcessed) print("bytes remaining (lengthBytes - bytesProcessed):", lengthBytes - bytesProcessed) print("frameSizeBytes(frameSize):", frameSizeBytes(frameSize)) while lengthBytes - bytesProcessed < frameSizeBytes(frameSize): print("Warning! Not enough data for frame.") frameSizeIndex -= 1 if frameSizeIndex < 0: # The data is less than the smallest number of samples # in a frame. Either we ignore the remaining samples # and shorten the audio, or we pad the frame with # zeros and lengthen the audio. We'll take the easy # option and shorten the audio. break frameSize = frameSizes[frameSizeIndex] print("Decreased frame size to ", frameSize) if frameSizeIndex < 0: print("Warning! Ignoring samples at the end of the audio\n" + "as they do not fit into even the smallest frame.") break # Encode the audio if True: # Print out the PCM data to see that it's readable #p = sourcePtr #d = 0 #while d < frameSize*2*2: # p2 = ctypes.c_void_p(ctypes.cast(p,ctypes.c_void_p).value + d) # print("p[",p2.value,"]:",ctypes.cast(p2, opus.opus_int16_p).contents.value) # d += 2 print("Encoding audio") numBytes = opus.opus_encode( encoder, ctypes.cast(sourcePtr, ctypes.POINTER(opus.opus_int16)), frameSize, encodedFramePtr, maxEncodedFrameBytes) print("numBytes: ", numBytes) # Check for any errors during encoding if numBytes < 0: raise Exception("Encoder error detected: " + opus.opus_strerror(numBytes).decode("utf")) # Move to next position in the buffer: encoder oldAddress = sourcePtr.value #print("oldAddress:",oldAddress) deltaBytes = frameSize * sourceChannels * 2 newAddress = oldAddress + deltaBytes #print("newAddress:",newAddress) sourcePtr = ctypes.c_void_p(newAddress) bytesProcessed = sourcePtr.value - sourcePtr_init.value # Decode the audio if True: print("Decoding audio") numSamples = opus.opus_decode( decoderPtr, encodedFramePtr, numBytes, ctypes.cast(targetPtr, ctypes.POINTER(ctypes.c_short)), 5760, # Max space required in PCM 0 # What's this about? ) print("numSamples: ", numSamples) # Check for any errors during decoding if numSamples < 0: raise Exception("Decoder error detected: " + opus.opus_strerror(numSamples).decode("utf")) # Move to next position in the buffer: decoder targetPtr.value += numSamples * targetChannels * 2