blob: c63bb693c4effbc009a07bdf08283f5c4e3df6f1 [file] [log] [blame]
#ifdef WITH_RIVE_AUDIO
#include "rive/math/simd.hpp"
#ifdef __APPLE__
#include <TargetConditionals.h>
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_MACCATALYST || TARGET_OS_IPHONE
// Don't define MINIAUDIO_IMPLEMENTATION ON iOS
#elif TARGET_OS_MAC
#define MINIAUDIO_IMPLEMENTATION
#else
#error "Unknown Apple platform"
#endif
#else
#define MINIAUDIO_IMPLEMENTATION
#endif
#include "miniaudio.h"
#include "rive/audio/audio_engine.hpp"
#include "rive/audio/audio_sound.hpp"
#include "rive/audio/audio_source.hpp"
#include <algorithm>
using namespace rive;
void AudioEngine::SoundCompleted(void* pUserData, ma_sound* pSound)
{
AudioSound* audioSound = (AudioSound*)pUserData;
auto engine = audioSound->m_engine;
engine->soundCompleted(ref_rcp(audioSound));
}
void AudioEngine::soundCompleted(rcp<AudioSound> sound)
{
std::unique_lock<std::mutex> lock(m_mutex);
m_completedSounds.push_back(sound);
auto next = sound->m_nextPlaying;
auto prev = sound->m_prevPlaying;
if (next != nullptr)
{
next->m_prevPlaying = prev;
}
if (prev != nullptr)
{
prev->m_nextPlaying = next;
}
if (m_playingSoundsHead == sound)
{
m_playingSoundsHead = next;
}
sound->m_nextPlaying = nullptr;
sound->m_prevPlaying = nullptr;
}
#ifdef WITH_RIVE_AUDIO_TOOLS
namespace rive
{
class LevelsNode
{
public:
ma_node_base base;
AudioEngine* engine;
static void measureLevels(ma_node* pNode,
const float** ppFramesIn,
ma_uint32* pFrameCountIn,
float** ppFramesOut,
ma_uint32* pFrameCountOut)
{
const float* frames = ppFramesIn[0];
ma_uint32 frameCount = pFrameCountIn[0];
static_cast<LevelsNode*>(pNode)->engine->measureLevels(frames, (uint32_t)frameCount);
}
};
} // namespace rive
void AudioEngine::measureLevels(const float* frames, uint32_t frameCount)
{
uint32_t channelCount = channels();
for (uint32_t i = 0; i < frameCount; i++)
{
for (uint32_t c = 0; c < channelCount; c++)
{
float sample = *frames++;
m_levels[c] = std::max(m_levels[c], sample);
}
}
}
static ma_node_vtable measure_levels_vtable = {LevelsNode::measureLevels,
nullptr,
1,
1,
MA_NODE_FLAG_PASSTHROUGH};
void AudioEngine::initLevelMonitor()
{
if (m_levelMonitor == nullptr)
{
m_levelMonitor = new LevelsNode();
m_levelMonitor->engine = this;
ma_node_config nodeConfig = ma_node_config_init();
nodeConfig.vtable = &measure_levels_vtable;
uint32_t channelCount = channels();
nodeConfig.pInputChannels = &channelCount;
nodeConfig.pOutputChannels = &channelCount;
m_levels.resize(channelCount);
auto graph = ma_engine_get_node_graph(m_engine);
if (ma_node_init(graph, &nodeConfig, nullptr, &m_levelMonitor->base) != MA_SUCCESS)
{
delete m_levelMonitor;
m_levelMonitor = nullptr;
return;
}
if (ma_node_attach_output_bus(&m_levelMonitor->base,
0,
ma_node_graph_get_endpoint(graph),
0) != MA_SUCCESS)
{
ma_node_uninit(&m_levelMonitor->base, nullptr);
delete m_levelMonitor;
m_levelMonitor = nullptr;
return;
}
}
}
void AudioEngine::levels(Span<float> levels)
{
int size = std::min((int)m_levels.size(), (int)levels.size());
for (int i = 0; i < size; i++)
{
levels[i] = m_levels[i];
m_levels[i] = 0.0f;
}
}
float AudioEngine::level(uint32_t channel)
{
if (channel < m_levels.size())
{
float value = m_levels[channel];
m_levels[channel] = 0.0f;
return value;
}
return 0.0f;
}
#endif
rcp<AudioEngine> AudioEngine::Make(uint32_t numChannels, uint32_t sampleRate)
{
ma_engine_config engineConfig = ma_engine_config_init();
engineConfig.channels = numChannels;
engineConfig.sampleRate = sampleRate;
#ifdef EXTERNAL_RIVE_AUDIO_ENGINE
engineConfig.noDevice = MA_TRUE;
#endif
ma_engine* engine = new ma_engine();
if (ma_engine_init(&engineConfig, engine) != MA_SUCCESS)
{
fprintf(stderr, "AudioEngine::Make - failed to init engine\n");
delete engine;
return nullptr;
}
return rcp<AudioEngine>(new AudioEngine(engine));
}
uint32_t AudioEngine::channels() const { return ma_engine_get_channels(m_engine); }
uint32_t AudioEngine::sampleRate() const { return ma_engine_get_sample_rate(m_engine); }
AudioEngine::AudioEngine(ma_engine* engine) :
m_device(ma_engine_get_device(engine)), m_engine(engine)
{}
rcp<AudioSound> AudioEngine::play(rcp<AudioSource> source,
uint64_t startTime,
uint64_t endTime,
uint64_t soundStartTime)
{
std::unique_lock<std::mutex> lock(m_mutex);
// We have to dispose completed sounds out of the completed callback. So we
// do it on next play or at destruct.
for (auto sound : m_completedSounds)
{
sound->dispose();
}
m_completedSounds.clear();
rcp<AudioSound> audioSound = rcp<AudioSound>(new AudioSound(this));
if (source->isBuffered())
{
rive::Span<float> samples = source->bufferedSamples();
ma_audio_buffer_config config =
ma_audio_buffer_config_init(ma_format_f32,
source->channels(),
samples.size() / source->channels(),
(const void*)samples.data(),
nullptr);
if (ma_audio_buffer_init(&config, audioSound->buffer()) != MA_SUCCESS)
{
fprintf(stderr, "AudioSource::play - Failed to initialize audio buffer.\n");
return nullptr;
}
if (ma_sound_init_from_data_source(m_engine,
audioSound->buffer(),
MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION,
nullptr,
audioSound->sound()) != MA_SUCCESS)
{
return nullptr;
}
}
else
{
ma_decoder_config config = ma_decoder_config_init(ma_format_f32, channels(), sampleRate());
auto sourceBytes = source->bytes();
if (ma_decoder_init_memory(sourceBytes.data(),
sourceBytes.size(),
&config,
audioSound->decoder()) != MA_SUCCESS)
{
fprintf(stderr, "AudioSource::play - Failed to initialize decoder.\n");
return nullptr;
}
if (ma_sound_init_from_data_source(m_engine,
&audioSound->m_decoder,
MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION,
nullptr,
audioSound->sound()) != MA_SUCCESS)
{
return nullptr;
}
}
if (soundStartTime != 0)
{
audioSound->seek(soundStartTime);
}
ma_sound_set_end_callback(audioSound->sound(), SoundCompleted, audioSound.get());
if (startTime != 0)
{
ma_sound_set_start_time_in_pcm_frames(audioSound->sound(), startTime);
}
if (endTime != 0)
{
ma_sound_set_stop_time_in_pcm_frames(audioSound->sound(), endTime);
}
#ifdef WITH_RIVE_AUDIO_TOOLS
if (m_levelMonitor != nullptr)
{
ma_node_attach_output_bus(audioSound->sound(), 0, m_levelMonitor, 0);
}
#endif
if (ma_sound_start(audioSound->sound()) != MA_SUCCESS)
{
fprintf(stderr, "AudioSource::play - failed to start sound\n");
return nullptr;
}
if (m_playingSoundsHead != nullptr)
{
m_playingSoundsHead->m_prevPlaying = audioSound;
}
audioSound->m_nextPlaying = m_playingSoundsHead;
m_playingSoundsHead = audioSound;
return audioSound;
}
AudioEngine::~AudioEngine()
{
auto sound = m_playingSoundsHead;
while (sound != nullptr)
{
sound->dispose();
auto next = sound->m_nextPlaying;
sound->m_nextPlaying = nullptr;
sound->m_prevPlaying = nullptr;
sound = next;
}
for (auto sound : m_completedSounds)
{
sound->dispose();
}
m_completedSounds.clear();
ma_engine_uninit(m_engine);
delete m_engine;
#ifdef WITH_RIVE_AUDIO_TOOLS
if (m_levelMonitor != nullptr)
{
ma_node_uninit(&m_levelMonitor->base, nullptr);
delete m_levelMonitor;
}
#endif
}
uint64_t AudioEngine::timeInFrames()
{
return (uint64_t)ma_engine_get_time_in_pcm_frames(m_engine);
}
rcp<AudioEngine> AudioEngine::RuntimeEngine()
{
static rcp<AudioEngine> engine = AudioEngine::Make(defaultNumChannels, defaultSampleRate);
return engine;
}
#ifdef EXTERNAL_RIVE_AUDIO_ENGINE
bool AudioEngine::readAudioFrames(float* frames, uint64_t numFrames, uint64_t* framesRead)
{
return ma_engine_read_pcm_frames(m_engine,
(void*)frames,
(ma_uint64)numFrames,
(ma_uint64*)framesRead) == MA_SUCCESS;
}
bool AudioEngine::sumAudioFrames(float* frames, uint64_t numFrames)
{
size_t numChannels = (size_t)channels();
size_t count = (size_t)numFrames * numChannels;
if (m_readFrames.size() < count)
{
m_readFrames.resize(count);
}
ma_uint64 framesRead = 0;
if (ma_engine_read_pcm_frames(m_engine,
(void*)m_readFrames.data(),
(ma_uint64)numFrames,
&framesRead) != MA_SUCCESS)
{
return false;
}
count = framesRead * numChannels;
const size_t alignedCount = count - count % 4;
float* src = m_readFrames.data();
float* dst = frames;
float* srcEnd = src + alignedCount;
while (src != srcEnd)
{
float4 sum = simd::load4f(src) + simd::load4f(dst);
simd::store(dst, sum);
src += 4;
dst += 4;
}
for (size_t i = alignedCount; i < count; i++)
{
frames[i] += m_readFrames[i];
}
return true;
}
#endif
#endif