// Copyright 2021 Jean Pierre Cimalando // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // SPDX-License-Identifier: Apache-2.0 // #include "ysfx_audio_wav.hpp" #include "ysfx_utils.hpp" #include #include #if defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wunused-function" #endif #define DR_WAV_IMPLEMENTATION #define DRWAV_API static #define DRWAV_PRIVATE static #include "dr_wav.h" #if defined(__GNUC__) # pragma GCC diagnostic pop #endif struct ysfx_wav_reader_t { ~ysfx_wav_reader_t() { drwav_uninit(wav.get()); } std::unique_ptr wav; uint32_t nbuff = 0; std::unique_ptr buff; }; static bool ysfx_wav_can_handle(const char *path) { return ysfx::path_has_suffix(path, "wav"); } static ysfx_audio_reader_t *ysfx_wav_open(const char *path) { std::unique_ptr wav{new drwav}; #if !defined(_WIN32) drwav_bool32 initok = drwav_init_file(wav.get(), path, nullptr); #else drwav_bool32 initok = drwav_init_file_w(wav.get(), ysfx::widen(path).c_str(), nullptr); #endif if (!initok) return nullptr; std::unique_ptr reader{new ysfx_wav_reader_t}; reader->wav = std::move(wav); reader->buff.reset(new float[reader->wav->channels]); return (ysfx_audio_reader_t *)reader.release(); } static void ysfx_wav_close(ysfx_audio_reader_t *reader_) { ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; delete reader; } static ysfx_audio_file_info_t ysfx_wav_info(ysfx_audio_reader_t *reader_) { ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; ysfx_audio_file_info_t info; info.channels = reader->wav->channels; info.sample_rate = (ysfx_real)reader->wav->sampleRate; return info; } static uint64_t ysfx_wav_avail(ysfx_audio_reader_t *reader_) { ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; return reader->nbuff + reader->wav->channels * (reader->wav->totalPCMFrameCount - reader->wav->readCursorInPCMFrames); } static void ysfx_wav_rewind(ysfx_audio_reader_t *reader_) { ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; drwav_seek_to_pcm_frame(reader->wav.get(), 0); reader->nbuff = 0; } static uint64_t ysfx_wav_unload_buffer(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) { ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; uint32_t nbuff = reader->nbuff; if (nbuff > count) nbuff = (uint32_t)count; if (nbuff == 0) return 0; const float *src = &reader->buff[reader->wav->channels - reader->nbuff]; for (uint32_t i = 0; i < nbuff; ++i) samples[i] = src[i]; reader->nbuff -= nbuff; return nbuff; } static uint64_t ysfx_wav_read(ysfx_audio_reader_t *reader_, ysfx_real *samples, uint64_t count) { ysfx_wav_reader_t *reader = (ysfx_wav_reader_t *)reader_; uint32_t channels = reader->wav->channels; uint64_t readtotal = 0; if (count == 0) return readtotal; else { uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count); samples += copied; count -= copied; readtotal += copied; } if (count == 0) return readtotal; else { float *f32buf = (float *)samples; uint64_t readframes = drwav_read_pcm_frames_f32(reader->wav.get(), count / channels, f32buf); uint64_t readsamples = channels * readframes; // f32->f64 for (uint64_t i = readsamples; i-- > 0; ) samples[i] = f32buf[i]; samples += readsamples; count -= readsamples; readtotal += readsamples; } if (count == 0) return readtotal; else if (drwav_read_pcm_frames_f32(reader->wav.get(), 1, reader->buff.get()) == 1) { reader->nbuff = channels; uint64_t copied = ysfx_wav_unload_buffer(reader_, samples, count); samples += copied; count -= copied; readtotal += copied; } return readtotal; } const ysfx_audio_format_t ysfx_audio_format_wav = { &ysfx_wav_can_handle, &ysfx_wav_open, &ysfx_wav_close, &ysfx_wav_info, &ysfx_wav_avail, &ysfx_wav_rewind, &ysfx_wav_read, };