| #include "rive/audio/audio_source.hpp" | |
| #ifdef WITH_RIVE_AUDIO | |
| #include "rive/audio/audio_engine.hpp" | |
| #include "rive/audio/audio_sound.hpp" | |
| #include "rive/audio/audio_reader.hpp" | |
| #include <algorithm> | |
| #endif | |
| using namespace rive; | |
| #ifdef WITH_RIVE_AUDIO | |
| // Both of these should be make_rcp but it is currently getting refed directy | |
| // somewhere else so until we refactor that we have to not ref here. | |
| rcp<AudioSource> AudioSource::MakeAudioSource(rive::Span<uint8_t> fileBytes) | |
| { | |
| ma_decoder decoder; | |
| ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0); | |
| if (ma_decoder_init_memory( | |
| fileBytes.data(), | |
| fileBytes.size(), | |
| &config, | |
| &decoder) != MA_SUCCESS) | |
| { | |
| return nullptr; | |
| } | |
| ma_decoder_uninit(&decoder); | |
| return rcp<AudioSource>(new AudioSource(std::move(fileBytes))); | |
| } | |
| rcp<AudioSource> AudioSource::MakeAudioSource( | |
| rive::SimpleArray<uint8_t> fileBytes) | |
| { | |
| ma_decoder decoder; | |
| ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0); | |
| auto result = ma_decoder_init_memory( | |
| fileBytes.data(), | |
| fileBytes.size(), | |
| &config, | |
| &decoder); | |
| if (result != MA_SUCCESS) | |
| { | |
| fprintf( | |
| stderr, | |
| "Failed to decode audio with error %s\n", | |
| ma_result_description(result)); | |
| return nullptr; | |
| } | |
| ma_decoder_uninit(&decoder); | |
| return rcp<AudioSource>(new AudioSource(std::move(fileBytes))); | |
| } | |
| AudioSource::AudioSource( | |
| rive::Span<float> samples, | |
| uint32_t numChannels, | |
| uint32_t sampleRate) | |
| : m_isBuffered(true) | |
| , m_channels(numChannels) | |
| , m_sampleRate(sampleRate) | |
| , m_duration(-1.0f) | |
| , m_ownedBytes((uint8_t*)samples.data(), samples.size() * sizeof(float)) | |
| { | |
| assert(numChannels != 0); | |
| assert(sampleRate != 0); | |
| } | |
| AudioSource::AudioSource(rive::Span<uint8_t> fileBytes) | |
| : m_isBuffered(false) | |
| , m_channels(0) | |
| , m_sampleRate(0) | |
| , m_duration(-1.0f) | |
| , m_fileBytes(fileBytes) | |
| {} | |
| AudioSource::AudioSource(rive::SimpleArray<uint8_t> fileBytes) | |
| : m_isBuffered(false) | |
| , m_channels(0) | |
| , m_sampleRate(0) | |
| , m_duration(-1.0f) | |
| , m_fileBytes(fileBytes.data(), fileBytes.size()) | |
| , m_ownedBytes(std::move(fileBytes)) | |
| {} | |
| const rive::Span<float> AudioSource::bufferedSamples() const | |
| { | |
| assert(m_isBuffered); | |
| return rive::Span<float>( | |
| (float*)m_ownedBytes.data(), | |
| m_ownedBytes.size() / sizeof(float)); | |
| } | |
| class AudioSourceDecoder | |
| { | |
| public: | |
| AudioSourceDecoder(rive::Span<uint8_t> fileBytes) : m_decoder({}) | |
| { | |
| ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0); | |
| if (ma_decoder_init_memory( | |
| fileBytes.data(), | |
| fileBytes.size(), | |
| &config, | |
| &m_decoder) != MA_SUCCESS) | |
| { | |
| fprintf(stderr, "AudioSourceDecoder - Failed to initialize decoder.\n"); | |
| } | |
| } | |
| ~AudioSourceDecoder() { ma_decoder_uninit(&m_decoder); } | |
| uint32_t channels() { return (uint32_t)m_decoder.outputChannels; } | |
| uint32_t sampleRate() { return (uint32_t)m_decoder.outputSampleRate; } | |
| private: | |
| ma_decoder m_decoder; | |
| }; | |
| uint32_t AudioSource::channels() | |
| { | |
| if (m_channels != 0) | |
| { | |
| return m_channels; | |
| } | |
| AudioSourceDecoder audioDecoder(m_fileBytes); | |
| return m_channels = audioDecoder.channels(); | |
| } | |
| uint32_t AudioSource::sampleRate() | |
| { | |
| if (m_sampleRate != 0) | |
| { | |
| return m_sampleRate; | |
| } | |
| AudioSourceDecoder audioDecoder(m_fileBytes); | |
| return m_sampleRate = audioDecoder.sampleRate(); | |
| } | |
| float AudioSource::duration() | |
| { | |
| if (m_duration >= 0.0f) | |
| { | |
| return m_duration; | |
| } | |
| if (m_isBuffered) | |
| { | |
| uint32_t ch = channels(); | |
| uint32_t sr = sampleRate(); | |
| if (ch == 0 || sr == 0) | |
| { | |
| return m_duration = 0.0f; | |
| } | |
| return m_duration = (float)bufferedSamples().size() / (float)(ch * sr); | |
| } | |
| // A reader's seek table corrects an MP3 header that miscounts its frames. | |
| auto reader = makeReader(0, 0); | |
| if (reader == nullptr || reader->sampleRate() == 0) | |
| { | |
| return m_duration = 0.0f; | |
| } | |
| return m_duration = (float)reader->lengthInFrames() / | |
| (float)reader->sampleRate(); | |
| } | |
| AudioFormat AudioSource::format() const | |
| { | |
| if (m_isBuffered) | |
| { | |
| return AudioFormat::buffered; | |
| } | |
| ma_decoder decoder; | |
| ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0); | |
| if (ma_decoder_init_memory( | |
| m_fileBytes.data(), | |
| m_fileBytes.size(), | |
| &config, | |
| &decoder) != MA_SUCCESS) | |
| { | |
| fprintf(stderr, "AudioSource::format - Failed to initialize decoder.\n"); | |
| return AudioFormat::unknown; | |
| } | |
| ma_encoding_format encodingFormat; | |
| AudioFormat format = AudioFormat::unknown; | |
| if (ma_decoder_get_encoding_format(&decoder, &encodingFormat) == MA_SUCCESS) | |
| { | |
| switch (encodingFormat) | |
| { | |
| case ma_encoding_format_mp3: | |
| format = AudioFormat::mp3; | |
| break; | |
| case ma_encoding_format_wav: | |
| format = AudioFormat::wav; | |
| break; | |
| case ma_encoding_format_vorbis: | |
| format = AudioFormat::vorbis; | |
| break; | |
| case ma_encoding_format_flac: | |
| format = AudioFormat::flac; | |
| break; | |
| default: | |
| format = AudioFormat::unknown; | |
| break; | |
| } | |
| } | |
| ma_decoder_uninit(&decoder); | |
| return format; | |
| } | |
| rcp<AudioReader> AudioSource::makeReader( | |
| uint32_t numChannels, | |
| uint32_t sampleRate) | |
| { | |
| rive::rcp<rive::AudioSource> rcSource = rive::rcp<rive::AudioSource>(this); | |
| rcSource->ref(); | |
| auto reader = rcp<AudioReader>(new AudioReader(rcSource)); | |
| if (!m_isBuffered) | |
| { | |
| // Native format, so the reader's own converter carries across a loop. | |
| ma_decoder_config config = ma_decoder_config_init(ma_format_f32, 0, 0); | |
| config.seekPointCount = kDecoderSeekPoints; | |
| if (ma_decoder_init_memory( | |
| m_fileBytes.data(), | |
| m_fileBytes.size(), | |
| &config, | |
| &reader->m_decoder) != MA_SUCCESS) | |
| { | |
| fprintf( | |
| stderr, | |
| "AudioSource::makeReader - Failed to initialize decoder.\n"); | |
| return nullptr; | |
| } | |
| reader->m_decoding = true; | |
| } | |
| return reader->init(numChannels, sampleRate) ? reader : nullptr; | |
| } | |
| #else | |
| rcp<AudioSource> AudioSource::MakeAudioSource(rive::Span<uint8_t> fileBytes) | |
| { | |
| return nullptr; | |
| } | |
| rcp<AudioSource> AudioSource::MakeAudioSource( | |
| rive::SimpleArray<uint8_t> fileBytes) | |
| { | |
| return nullptr; | |
| } | |
| AudioSource::AudioSource(rive::Span<uint8_t> fileBytes) {} | |
| AudioSource::AudioSource(rive::SimpleArray<uint8_t> fileBytes) {} | |
| AudioSource::AudioSource( | |
| rive::Span<float> samples, | |
| uint32_t numChannels, | |
| uint32_t sampleRate) | |
| {} | |
| uint32_t AudioSource::channels() { return 0; } | |
| uint32_t AudioSource::sampleRate() { return 0; } | |
| AudioFormat AudioSource::format() const { return AudioFormat::unknown; } | |
| const rive::Span<float> AudioSource::bufferedSamples() const | |
| { | |
| return rive::Span<float>(nullptr, 0); | |
| } | |
| #endif |