diff --git a/build_ffi_module.py b/build_ffi_module.py index 057662c..0ee2f1f 100644 --- a/build_ffi_module.py +++ b/build_ffi_module.py @@ -680,6 +680,7 @@ drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); drflac* drflac_open_file(const char* filename, const drflac_allocation_callbacks* pAllocationCallbacks); +drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); drflac* drflac_open_memory(const void* data, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); @@ -723,6 +724,7 @@ drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); void drmp3_uninit(drmp3* pMP3); drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); @@ -795,6 +797,7 @@ } drwav_allocation_callbacks; drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); +drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); drwav_int32 drwav_uninit(drwav* pWav); void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); diff --git a/miniaudio.py b/miniaudio.py index 6efaf49..68849e4 100644 --- a/miniaudio.py +++ b/miniaudio.py @@ -336,8 +336,12 @@ def vorbis_stream_file(filename: str, seek_frame: int = 0) -> Generator[array.ar def flac_get_file_info(filename: str) -> SoundFileInfo: """Fetch some information about the audio file (flac format).""" - filenamebytes = _get_filename_bytes(filename) - flac = lib.drflac_open_file(filenamebytes, ffi.NULL) + if sys.platform == "win32": + filenamebytesw = _get_filename_bytes_w(filename) + flac = lib.drflac_open_file_w(filenamebytesw, ffi.NULL) + else: + filenamebytes = _get_filename_bytes(filename) + flac = lib.drflac_open_file(filenamebytes, ffi.NULL) if not flac: raise DecodeError("could not open/decode file") try: @@ -476,8 +480,12 @@ def flac_stream_file(filename: str, frames_to_read: int = 1024, """Streams the flac audio file as interleaved 16 bit signed integer sample arrays segments. This uses a fixed chunk size and cannot be used as a generic miniaudio decoder input stream. Consider using stream_file() instead.""" - filenamebytes = _get_filename_bytes(filename) - flac = lib.drflac_open_file(filenamebytes, ffi.NULL) + if sys.platform == "win32": + filenamebytesw = _get_filename_bytes_w(filename) + flac = lib.drflac_open_file_w(filenamebytesw, ffi.NULL) + else: + filenamebytes = _get_filename_bytes(filename) + flac = lib.drflac_open_file(filenamebytes, ffi.NULL) if not flac: raise DecodeError("could not open/decode file") if seek_frame > 0: @@ -501,9 +509,14 @@ def flac_stream_file(filename: str, frames_to_read: int = 1024, 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): + if sys.platform == "win32": + filenamebytesw = _get_filename_bytes_w(filename) + result = lib.drmp3_init_file_w(mp3, filenamebytesw, ffi.NULL) + else: + filenamebytes = _get_filename_bytes(filename) + result = lib.drmp3_init_file(mp3, filenamebytes, ffi.NULL) + if not result: raise DecodeError("could not open/decode file") try: num_frames = lib.drmp3_get_pcm_frame_count(mp3) @@ -594,9 +607,14 @@ def mp3_stream_file(filename: str, frames_to_read: int = 1024, seek_frame: int = """Streams the mp3 audio file as interleaved 16 bit signed integer sample arrays segments. This uses a fixed chunk size and cannot be used as a generic miniaudio decoder input stream. Consider using stream_file() instead.""" - filenamebytes = _get_filename_bytes(filename) with ffi.new("drmp3 *") as mp3: - if not lib.drmp3_init_file(mp3, filenamebytes, ffi.NULL): + if sys.platform == "win32": + filenamebytesw = _get_filename_bytes_w(filename) + result = lib.drmp3_init_file_w(mp3, filenamebytesw, ffi.NULL) + else: + filenamebytes = _get_filename_bytes(filename) + result = lib.drmp3_init_file(mp3, filenamebytes, ffi.NULL) + if not result: raise DecodeError("could not open/decode file") if seek_frame > 0: result = lib.drmp3_seek_to_pcm_frame(mp3, seek_frame) @@ -619,9 +637,15 @@ def mp3_stream_file(filename: str, frames_to_read: int = 1024, seek_frame: int = 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): + if sys.platform == "win32": + # Windows filepaths with non-ascii characters fail to open using fopen, cast filenames to wchar_t* + filenamebytesw = _get_filename_bytes_w(filename) + result = lib.drwav_init_file_w(wav, filenamebytesw, ffi.NULL) + else: + filenamebytes = _get_filename_bytes(filename) + result = lib.drwav_init_file(wav, filenamebytes, ffi.NULL) + if not result: raise DecodeError("could not open/decode file") try: duration = wav.totalPCMFrameCount / wav.sampleRate @@ -760,9 +784,14 @@ def wav_stream_file(filename: str, frames_to_read: int = 1024, """Streams the WAV audio file as interleaved 16 bit signed integer sample arrays segments. This uses a fixed chunk size and cannot be used as a generic miniaudio decoder input stream. Consider using stream_file() instead.""" - filenamebytes = _get_filename_bytes(filename) with ffi.new("drwav*") as wav: - if not lib.drwav_init_file(wav, filenamebytes, ffi.NULL): + if sys.platform == "win32": + filenamebytesw = _get_filename_bytes_w(filename) + result = lib.drwav_init_file_w(wav, filenamebytesw, ffi.NULL) + else: + filenamebytes = _get_filename_bytes(filename) + result = lib.drwav_init_file(wav, filenamebytes, ffi.NULL) + if not result: raise DecodeError("could not open/decode file") if seek_frame > 0: result = lib.drwav_seek_to_pcm_frame(wav, seek_frame) @@ -817,6 +846,12 @@ def _get_filename_bytes(filename: str) -> bytes: return filename2.encode(sys.getfilesystemencoding()) +def _get_filename_bytes_w(filename: str): + filename2 = os.path.expanduser(filename) + if not os.path.isfile(filename2): + raise FileNotFoundError(filename) + return ffi.new("wchar_t[]", filename2) + class Devices: """Query the audio playback and record devices that miniaudio provides""" def __init__(self, backends: Optional[List[Backend]] = None) -> None: @@ -1027,11 +1062,15 @@ def stream_file(filename: str, output_format: SampleFormat = SampleFormat.SIGNED 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 sys.platform == "win32": + filenamebytesw = _get_filename_bytes_w(filename) + result = lib.ma_decoder_init_file_w(filenamebytesw, ffi.addressof(decoder_config), decoder) + else: + filenamebytes = _get_filename_bytes(filename) + 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: