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 internal_data_callback(device: ffi.CData, output: ffi.CData, input: ffi.CData, framecount: int) -> None: if framecount == 0 or not device.pUserData: return userdata_id = struct.unpack( 'q', ffi.unpack(ffi.cast("char *", device.pUserData), struct.calcsize('q')))[0] playback_device = _callback_data[userdata_id] # type: PlaybackDevice playback_device.data_callback(device, output, input, framecount)
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)