def vorbis_stream_file(filename: str) -> Generator[array.array, None, None]: """Streams the ogg vorbis audio file as interleaved 16 bit signed integer sample arrays segments.""" filenamebytes = _get_filename_bytes(filename) error = ffi.new("int *") vorbis = lib.stb_vorbis_open_filename(filenamebytes, error, ffi.NULL) if not vorbis: raise DecodeError("could not open/decode file") try: info = lib.stb_vorbis_get_info(vorbis) decode_buffer1 = ffi.new("short[]", 4096 * info.channels) decodebuf_ptr1 = ffi.cast("short *", decode_buffer1) decode_buffer2 = ffi.new("short[]", 4096 * info.channels) decodebuf_ptr2 = ffi.cast("short *", decode_buffer2) # note: we decode several frames to reduce the overhead of very small sample sizes a little while True: num_samples1 = lib.stb_vorbis_get_frame_short_interleaved( vorbis, info.channels, decodebuf_ptr1, 4096 * info.channels) num_samples2 = lib.stb_vorbis_get_frame_short_interleaved( vorbis, info.channels, decodebuf_ptr2, 4096 * info.channels) if num_samples1 + num_samples2 <= 0: break buffer = ffi.buffer(decode_buffer1, num_samples1 * 2 * info.channels) samples = _create_int_array(2) samples.frombytes(buffer) if num_samples2 > 0: buffer = ffi.buffer(decode_buffer2, num_samples2 * 2 * info.channels) samples.frombytes(buffer) yield samples finally: lib.stb_vorbis_close(vorbis)
def _samples_generator(frames_to_read: int, nchannels: int, ma_output_format: int, decoder: ffi.CData, data: Any) -> Generator[array.array, int, None]: _reference = data # make sure any data passed in is not garbage collected sample_width, samples_proto = _decode_ma_format(ma_output_format) allocated_buffer_frames = max(frames_to_read, 16384) try: decodebuffer = ffi.new( "int8_t[]", allocated_buffer_frames * nchannels * sample_width) buf_ptr = ffi.cast("void *", decodebuffer) want_frames = (yield samples_proto) or frames_to_read while True: if want_frames > allocated_buffer_frames: raise MiniaudioError( "wanted to read more frames than storage was allocated for ({} vs {})" .format(want_frames, allocated_buffer_frames)) num_frames = lib.ma_decoder_read_pcm_frames( decoder, buf_ptr, want_frames) if num_frames <= 0: break buffer = ffi.buffer(decodebuffer, num_frames * sample_width * nchannels) samples = array.array(samples_proto.typecode) samples.frombytes(buffer) want_frames = (yield samples) or frames_to_read finally: lib.ma_decoder_uninit(decoder)
def mp3_stream_file( filename: str, frames_to_read: int = 1024, want_nchannels: int = 0, want_sample_rate: int = 0) -> Generator[array.array, None, None]: """Streams the mp3 audio file as interleaved 16 bit signed integer sample arrays segments.""" filenamebytes = _get_filename_bytes(filename) config = ffi.new("drmp3_config *") config.outputChannels = want_nchannels config.outputSampleRate = want_sample_rate mp3 = ffi.new("drmp3 *") if not lib.drmp3_init_file(mp3, filenamebytes, config): raise DecodeError("could not open/decode file") try: decodebuffer = ffi.new("drmp3_int16[]", frames_to_read * mp3.channels) buf_ptr = ffi.cast("drmp3_int16 *", decodebuffer) while True: num_samples = lib.drmp3_read_pcm_frames_s16( mp3, frames_to_read, buf_ptr) if num_samples <= 0: break buffer = ffi.buffer(decodebuffer, num_samples * 2 * mp3.channels) samples = _create_int_array(2) samples.frombytes(buffer) yield samples finally: lib.drmp3_uninit(mp3)
def _samples_stream_generator( frames_to_read: int, nchannels: int, output_format: LibSampleFormat, decoder: ffi.CData, data: Any, on_close: Optional[Callable] = None ) -> Generator[array.array, int, None]: _reference = data # make sure any data passed in is not garbage collected sample_width = _width_from_format(output_format) samples_proto = _array_proto_from_format(output_format) allocated_buffer_frames = max(frames_to_read, 16384) try: with ffi.new("int8_t[]", allocated_buffer_frames * nchannels * sample_width) as decodebuffer: buf_ptr = ffi.cast("void *", decodebuffer) want_frames = (yield samples_proto) or frames_to_read while True: num_frames = lib.ma_decoder_read_pcm_frames( decoder, buf_ptr, want_frames) if num_frames <= 0: break buffer = ffi.buffer(decodebuffer, num_frames * sample_width * nchannels) samples = array.array(samples_proto.typecode) samples.frombytes(buffer) want_frames = (yield samples) or frames_to_read finally: if on_close: on_close() lib.ma_decoder_uninit(decoder)
def wav_read_f32(data: bytes) -> DecodedSoundFile: """Reads and decodes the whole wav audio data. Resulting sample format is 32 bits float.""" channels = ffi.new("unsigned int *") sample_rate = ffi.new("unsigned int *") num_frames = ffi.new("drwav_uint64 *") memory = lib.drwav_open_memory_and_read_pcm_frames_f32( data, len(data), channels, sample_rate, num_frames) if not memory: raise DecodeError("cannot load/decode data") try: samples = array.array('f') buffer = ffi.buffer(memory, num_frames[0] * channels[0] * 4) samples.frombytes(buffer) return DecodedSoundFile("<memory>", channels[0], sample_rate[0], 4, ma_format_f32, samples) finally: lib.drwav_free(memory)
def flac_read_s16(data: bytes) -> DecodedSoundFile: """Reads and decodes the whole flac audio data. Resulting sample format is 16 bits signed integer.""" channels = ffi.new("unsigned int *") sample_rate = ffi.new("unsigned int *") num_frames = ffi.new("drflac_uint64 *") memory = lib.drflac_open_memory_and_read_pcm_frames_s16( data, len(data), channels, sample_rate, num_frames) if not memory: raise DecodeError("cannot load/decode data") try: samples = _create_int_array(2) buffer = ffi.buffer(memory, num_frames[0] * channels[0] * 2) samples.frombytes(buffer) return DecodedSoundFile("<memory>", channels[0], sample_rate[0], 2, ma_format_s16, samples) finally: lib.drflac_free(memory)
def vorbis_read(data: bytes) -> DecodedSoundFile: """Reads and decodes the whole vorbis audio data. Resulting sample format is 16 bits signed integer.""" channels = ffi.new("int *") sample_rate = ffi.new("int *") output = ffi.new("short **") num_samples = lib.stb_vorbis_decode_memory(data, len(data), channels, sample_rate, output) if num_samples <= 0: raise DecodeError("cannot load/decode data") try: buffer = ffi.buffer(output[0], num_samples * channels[0] * 2) samples = _create_int_array(2) samples.frombytes(buffer) return DecodedSoundFile("<memory>", channels[0], sample_rate[0], 2, ma_format_s16, samples) finally: lib.free(output[0])
def wav_read_file_s16(filename: str) -> DecodedSoundFile: """Reads and decodes the whole wav audio file. Resulting sample format is 16 bits signed integer.""" filenamebytes = _get_filename_bytes(filename) channels = ffi.new("unsigned int *") sample_rate = ffi.new("unsigned int *") num_frames = ffi.new("drwav_uint64 *") memory = lib.drwav_open_file_and_read_pcm_frames_s16( filenamebytes, channels, sample_rate, num_frames) if not memory: raise DecodeError("cannot load/decode file") try: samples = _create_int_array(2) buffer = ffi.buffer(memory, num_frames[0] * channels[0] * 2) samples.frombytes(buffer) return DecodedSoundFile(filename, channels[0], sample_rate[0], 2, ma_format_s16, samples) finally: lib.drwav_free(memory)
def flac_read_file_f32(filename: str) -> DecodedSoundFile: """Reads and decodes the whole flac audio file. Resulting sample format is 32 bits float.""" filenamebytes = _get_filename_bytes(filename) channels = ffi.new("unsigned int *") sample_rate = ffi.new("unsigned int *") num_frames = ffi.new("drflac_uint64 *") memory = lib.drflac_open_file_and_read_pcm_frames_f32( filenamebytes, channels, sample_rate, num_frames) if not memory: raise DecodeError("cannot load/decode file") try: samples = array.array('f') buffer = ffi.buffer(memory, num_frames[0] * channels[0] * 4) samples.frombytes(buffer) return DecodedSoundFile(filename, channels[0], sample_rate[0], 4, ma_format_f32, samples) finally: lib.drflac_free(memory)
def vorbis_read_file(filename: str) -> DecodedSoundFile: """Reads and decodes the whole vorbis audio file. Resulting sample format is 16 bits signed integer.""" filenamebytes = _get_filename_bytes(filename) channels = ffi.new("int *") sample_rate = ffi.new("int *") output = ffi.new("short **") num_frames = lib.stb_vorbis_decode_filename(filenamebytes, channels, sample_rate, output) if num_frames <= 0: raise DecodeError("cannot load/decode file") try: buffer = ffi.buffer(output[0], num_frames * channels[0] * 2) samples = _create_int_array(2) samples.frombytes(buffer) return DecodedSoundFile(filename, channels[0], sample_rate[0], 2, ma_format_s16, samples) finally: lib.free(output[0])
def decode(data: bytes, ma_output_format: int = ma_format_s16, nchannels: int = 2, sample_rate: int = 44100) -> DecodedSoundFile: """Convenience function to decode any supported audio file in memory to raw PCM samples in your chosen format.""" sample_width, samples = _decode_ma_format(ma_output_format) frames = ffi.new("ma_uint64 *") memory = ffi.new("void **") decoder_config = lib.ma_decoder_config_init(ma_output_format, nchannels, sample_rate) result = lib.ma_decode_memory(data, len(data), ffi.addressof(decoder_config), frames, memory) if result != lib.MA_SUCCESS: raise DecodeError("failed to decode data", result) buffer = ffi.buffer(memory[0], frames[0] * nchannels * sample_width) samples.frombytes(buffer) return DecodedSoundFile("<memory>", nchannels, sample_rate, sample_width, ma_output_format, samples)
def mp3_read_s16(data: bytes, want_nchannels: int = 0, want_sample_rate: int = 0) -> DecodedSoundFile: """Reads and decodes the whole mp3 audio data. Resulting sample format is 16 bits signed integer.""" config = ffi.new("drmp3_config *") config.outputChannels = want_nchannels config.outputSampleRate = want_sample_rate num_frames = ffi.new("drmp3_uint64 *") memory = lib.drmp3_open_memory_and_read_s16(data, len(data), config, num_frames) if not memory: raise DecodeError("cannot load/decode data") try: samples = _create_int_array(2) buffer = ffi.buffer(memory, num_frames[0] * config.outputChannels * 2) samples.frombytes(buffer) return DecodedSoundFile("<memory>", config.outputChannels, config.outputSampleRate, 2, ma_format_s16, samples) finally: lib.drmp3_free(memory)
def wav_stream_file( filename: str, frames_to_read: int = 1024) -> Generator[array.array, None, None]: """Streams the WAV audio file as interleaved 16 bit signed integer sample arrays segments.""" filenamebytes = _get_filename_bytes(filename) wav = lib.drwav_open_file(filenamebytes) if not wav: raise DecodeError("could not open/decode file") try: decodebuffer = ffi.new("drwav_int16[]", frames_to_read * wav.channels) buf_ptr = ffi.cast("drwav_int16 *", decodebuffer) while True: num_samples = lib.drwav_read_pcm_frames_s16( wav, frames_to_read, buf_ptr) if num_samples <= 0: break buffer = ffi.buffer(decodebuffer, num_samples * 2 * wav.channels) samples = _create_int_array(2) samples.frombytes(buffer) yield samples finally: lib.drwav_close(wav)
def mp3_read_file_f32(filename: str, want_nchannels: int = 0, want_sample_rate: int = 0) -> DecodedSoundFile: """Reads and decodes the whole mp3 audio file. Resulting sample format is 32 bits float.""" filenamebytes = _get_filename_bytes(filename) config = ffi.new("drmp3_config *") config.outputChannels = want_nchannels config.outputSampleRate = want_sample_rate num_frames = ffi.new("drmp3_uint64 *") memory = lib.drmp3_open_file_and_read_f32(filenamebytes, config, num_frames) if not memory: raise DecodeError("cannot load/decode file") try: samples = array.array('f') buffer = ffi.buffer(memory, num_frames[0] * config.outputChannels * 4) samples.frombytes(buffer) return DecodedSoundFile(filename, config.outputChannels, config.outputSampleRate, 4, ma_format_f32, samples) finally: lib.drmp3_free(memory)
def decode_file(filename: str, output_format: LibSampleFormat = LibSampleFormat.SIGNED16, nchannels: int = 2, sample_rate: int = 44100, dither: DitherMode = DitherMode.NONE): """Convenience function to decode any supported audio file to raw PCM samples in your chosen format.""" sample_width = _width_from_format(output_format) filenamebytes = _get_filename_bytes(filename) with ffi.new("ma_uint64 *") as frames, ffi.new("void **") as memory: decoder_config = lib.ma_decoder_config_init(output_format.value, nchannels, sample_rate) decoder_config.ditherMode = dither.value result = lib.ma_decode_file(filenamebytes, ffi.addressof(decoder_config), frames, memory) if result != lib.MA_SUCCESS: raise DecodeError("failed to decode file", result) buflen = frames[0] * nchannels * sample_width buffer = ffi.buffer(memory[0], buflen) # byte = bytearray(buflen) # byte[:] = buffer byte = bytes(buffer) lib.ma_free(memory[0], ffi.NULL) return byte