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 __init__(self, ma_output_format: int = ma_format_s16, nchannels: int = 2, sample_rate: int = 44100, buffersize_msec: int = 200): self.format = ma_output_format self.sample_width, self.samples_array_proto = _decode_ma_format( ma_output_format) self.nchannels = nchannels self.sample_rate = sample_rate self.buffersize_msec = buffersize_msec self._device = ffi.new("ma_device *") _callback_data[id(self)] = self self.userdata_ptr = ffi.new("char[]", struct.pack('q', id(self))) self._devconfig = lib.ma_device_config_init( lib.ma_device_type_playback) lib.ma_device_config_set_params(ffi.addressof(self._devconfig), self.sample_rate, self.buffersize_msec, 0, self.format, self.nchannels, 0, 0) self._devconfig.pUserData = self.userdata_ptr self._devconfig.dataCallback = lib.internal_data_callback self.audio_producer = None # type: Optional[AudioProducerType] result = lib.ma_device_init(ffi.NULL, ffi.addressof(self._devconfig), self._device) if result != lib.MA_SUCCESS: raise MiniaudioError("failed to init device", result) if self._device.pContext.backend == lib.ma_backend_null: raise MiniaudioError("no suitable audio backend found") self.backend = ffi.string( lib.ma_get_backend_name(self._device.pContext.backend)).decode()
def get_devices() -> Tuple[List[str], List[str]]: """Get two lists of supported audio devices: playback devices, recording devices.""" playback_infos = ffi.new("ma_device_info**") playback_count = ffi.new("ma_uint32*") capture_infos = ffi.new("ma_device_info**") capture_count = ffi.new("ma_uint32*") context = ffi.new("ma_context*") result = lib.ma_context_init(ffi.NULL, 0, ffi.NULL, context) if result != lib.MA_SUCCESS: raise MiniaudioError("cannot init context", result) try: result = lib.ma_context_get_devices(context, playback_infos, playback_count, capture_infos, capture_count) if result != lib.MA_SUCCESS: raise MiniaudioError("cannot get device infos", result) devs_playback = [] devs_captures = [] for i in range(playback_count[0]): ma_device_info = playback_infos[0][i] devs_playback.append(ffi.string(ma_device_info.name).decode()) # rest of the info structure is not filled... for i in range(capture_count[0]): ma_device_info = capture_infos[0][i] devs_captures.append(ffi.string(ma_device_info.name).decode()) # rest of the info structure is not filled... return devs_playback, devs_captures finally: lib.ma_context_uninit(context)
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 mp3_get_info(data: bytes) -> SoundFileInfo: """Fetch some information about the audio data (mp3 format).""" config = ffi.new("drmp3_config *") config.outputChannels = 0 config.outputSampleRate = 0 mp3 = ffi.new("drmp3 *") if not lib.drmp3_init_memory(mp3, data, len(data), config): raise DecodeError("could not open/decode data") try: num_frames = lib.drmp3_get_pcm_frame_count(mp3) duration = num_frames / mp3.sampleRate return SoundFileInfo("<memory>", "mp3", mp3.channels, mp3.sampleRate, 2, ma_format_s16, duration, num_frames, 0) finally: lib.drmp3_uninit(mp3)
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 stream_file(filename: str, output_format: LibSampleFormat = LibSampleFormat.SIGNED16, nchannels: int = 2, sample_rate: int = 44100, frames_to_read: int = 1024, dither: DitherMode = DitherMode.NONE, seek_frame: int = 0) -> Generator[array.array, int, None]: """ Convenience generator function to decode and stream any supported audio file as chunks of raw PCM samples in the chosen format. If you send() a number into the generator rather than just using next() on it, you'll get that given number of frames, instead of the default configured amount. This is particularly useful to plug this stream into an audio device callback that wants a variable number of frames per call. """ filenamebytes = _get_filename_bytes(filename) decoder = ffi.new("ma_decoder *") decoder_config = lib.ma_decoder_config_init(output_format.value, nchannels, sample_rate) decoder_config.ditherMode = dither.value result = lib.ma_decoder_init_file(filenamebytes, ffi.addressof(decoder_config), decoder) if result != lib.MA_SUCCESS: raise DecodeError("failed to init decoder", result) if seek_frame > 0: result = lib.ma_decoder_seek_to_pcm_frame(decoder, seek_frame) if result != lib.MA_SUCCESS: raise DecodeError("failed to seek to frame", result) g = _samples_stream_generator(frames_to_read, nchannels, output_format, decoder, None) dummy = next(g) assert len(dummy) == 0 return g
def stream_memory( data: bytes, ma_output_format: int = ma_format_s16, nchannels: int = 2, sample_rate: int = 44100, frames_to_read: int = 1024) -> Generator[array.array, int, None]: """ Convenience generator function to decode and stream any supported audio file in memory as chunks of raw PCM samples in the chosen format. If you send() a number into the generator rather than just using next() on it, you'll get that given number of frames, instead of the default configured amount. This is particularly useful to plug this stream into an audio device callback that wants a variable number of frames per call. """ decoder = ffi.new("ma_decoder *") decoder_config = lib.ma_decoder_config_init(ma_output_format, nchannels, sample_rate) result = lib.ma_decoder_init_memory(data, len(data), ffi.addressof(decoder_config), decoder) if result != lib.MA_SUCCESS: raise DecodeError("failed to decode memory", result) g = _samples_generator(frames_to_read, nchannels, ma_output_format, decoder, data) dummy = next(g) assert len(dummy) == 0 return g
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 mp3_get_file_info(filename: str) -> SoundFileInfo: """Fetch some information about the audio file (mp3 format).""" filenamebytes = _get_filename_bytes(filename) config = ffi.new("drmp3_config *") config.outputChannels = 0 config.outputSampleRate = 0 mp3 = ffi.new("drmp3 *") if not lib.drmp3_init_file(mp3, filenamebytes, config): raise DecodeError("could not open/decode file") try: num_frames = lib.drmp3_get_pcm_frame_count(mp3) duration = num_frames / mp3.sampleRate return SoundFileInfo(filename, "mp3", mp3.channels, mp3.sampleRate, 2, ma_format_s16, duration, num_frames, 0) finally: lib.drmp3_uninit(mp3)
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 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 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 wav_write_file(filename: str, data, nchannels, sample_rate, sample_width) -> None: """Writes the pcm sound to a WAV file""" with ffi.new("drwav_data_format*") as fmt, ffi.new("drwav*") as pwav: fmt.container = lib.drwav_container_riff fmt.format = lib.DR_WAVE_FORMAT_PCM fmt.channels = nchannels fmt.sampleRate = sample_rate fmt.bitsPerSample = sample_width * 8 # what about floating point format? filename_bytes = filename.encode(sys.getfilesystemencoding()) buffer_len = int(len(data) / sample_width) if not lib.drwav_init_file_write_sequential(pwav, filename_bytes, fmt, buffer_len, ffi.NULL): raise IOError("can't open file for writing") try: lib.drwav_write_pcm_frames(pwav, int(buffer_len / nchannels), data) finally: lib.drwav_uninit(pwav)
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 mp3_get_info(data: bytes) -> SoundFileInfo: """Fetch some information about the audio data (mp3 format).""" with ffi.new("drmp3 *") as mp3: if not lib.drmp3_init_memory(mp3, data, len(data), ffi.NULL): raise DecodeError("could not open/decode data") try: num_frames = lib.drmp3_get_pcm_frame_count(mp3) duration = num_frames / mp3.sampleRate return SoundFileInfo("<memory>", FileFormat.MP3, mp3.channels, mp3.sampleRate, LibSampleFormat.SIGNED16, duration, num_frames) finally: lib.drmp3_uninit(mp3)
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 mp3_get_file_info(filename: str) -> SoundFileInfo: """Fetch some information about the audio file (mp3 format).""" filenamebytes = _get_filename_bytes(filename) with ffi.new("drmp3 *") as mp3: if not lib.drmp3_init_file(mp3, filenamebytes, ffi.NULL): raise DecodeError("could not open/decode file") try: num_frames = lib.drmp3_get_pcm_frame_count(mp3) duration = num_frames / mp3.sampleRate return SoundFileInfo(filename, FileFormat.MP3, mp3.channels, mp3.sampleRate, LibSampleFormat.SIGNED16, duration, num_frames) finally: lib.drmp3_uninit(mp3)
def wav_get_info(data: bytes) -> SoundFileInfo: """Fetch some information about the audio data (wav format).""" with ffi.new("drwav*") as wav: if not lib.drwav_init_memory(wav, data, len(data), ffi.NULL): raise DecodeError("could not open/decode data") try: duration = wav.totalPCMFrameCount / wav.sampleRate sample_width = wav.bitsPerSample // 8 is_float = wav.translatedFormatTag == lib.DR_WAVE_FORMAT_IEEE_FLOAT return SoundFileInfo("<memory>", FileFormat.WAV, wav.channels, wav.sampleRate, _format_from_width(sample_width, is_float), duration, wav.totalPCMFrameCount) finally: lib.drwav_uninit(wav)
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
def vorbis_get_info(data: bytes) -> SoundFileInfo: """Fetch some information about the audio data (vorbis format).""" with ffi.new("int *") as error: vorbis = lib.stb_vorbis_open_memory(data, len(data), error, ffi.NULL) if not vorbis: raise DecodeError("could not open/decode data") try: info = lib.stb_vorbis_get_info(vorbis) duration = lib.stb_vorbis_stream_length_in_seconds(vorbis) num_frames = lib.stb_vorbis_stream_length_in_samples(vorbis) return SoundFileInfo("<memory>", FileFormat.VORBIS, info.channels, info.sample_rate, LibSampleFormat.SIGNED16, duration, num_frames) finally: lib.stb_vorbis_close(vorbis)
def vorbis_get_info(data: bytes) -> SoundFileInfo: """Fetch some information about the audio data (vorbis format).""" error = ffi.new("int *") vorbis = lib.stb_vorbis_open_memory(data, len(data), error, ffi.NULL) if not vorbis: raise DecodeError("could not open/decode data") try: info = lib.stb_vorbis_get_info(vorbis) duration = lib.stb_vorbis_stream_length_in_seconds(vorbis) num_frames = lib.stb_vorbis_stream_length_in_samples(vorbis) return SoundFileInfo("<memory>", "vorbis", info.channels, info.sample_rate, 2, ma_format_s16, duration, num_frames, info.max_frame_size) finally: lib.stb_vorbis_close(vorbis)
def wav_get_file_info(filename: str) -> SoundFileInfo: """Fetch some information about the audio file (wav format).""" filenamebytes = _get_filename_bytes(filename) with ffi.new("drwav*") as wav: if not lib.drwav_init_file(wav, filenamebytes, ffi.NULL): raise DecodeError("could not open/decode file") try: duration = wav.totalPCMFrameCount / wav.sampleRate sample_width = wav.bitsPerSample // 8 is_float = wav.translatedFormatTag == lib.DR_WAVE_FORMAT_IEEE_FLOAT return SoundFileInfo(filename, FileFormat.WAV, wav.channels, wav.sampleRate, _format_from_width(sample_width, is_float), duration, wav.totalPCMFrameCount) finally: lib.drwav_uninit(wav)
def vorbis_get_file_info(filename: str) -> SoundFileInfo: """Fetch some information about the audio file (vorbis format).""" 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) duration = lib.stb_vorbis_stream_length_in_seconds(vorbis) num_frames = lib.stb_vorbis_stream_length_in_samples(vorbis) return SoundFileInfo(filename, "vorbis", info.channels, info.sample_rate, 2, ma_format_s16, duration, num_frames, info.max_frame_size) finally: lib.stb_vorbis_close(vorbis)
def vorbis_get_file_info(filename: str) -> SoundFileInfo: """Fetch some information about the audio file (vorbis format).""" filenamebytes = _get_filename_bytes(filename) with ffi.new("int *") as error: 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) duration = lib.stb_vorbis_stream_length_in_seconds(vorbis) num_frames = lib.stb_vorbis_stream_length_in_samples(vorbis) return SoundFileInfo(filename, FileFormat.VORBIS, info.channels, info.sample_rate, LibSampleFormat.SIGNED16, duration, num_frames) finally: lib.stb_vorbis_close(vorbis)
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)