#include "engine/audio/audio_mixer.h" #include #include "base/log.h" #include "base/task_runner.h" #include "base/thread_pool.h" #include "engine/audio/audio_bus.h" #if defined(__ANDROID__) #include "engine/audio/audio_sink_oboe.h" #elif defined(__linux__) #include "engine/audio/audio_sink_alsa.h" #endif using namespace base; namespace eng { AudioMixer::AudioMixer() : main_thread_task_runner_(TaskRunner::GetThreadLocalTaskRunner()), #if defined(__ANDROID__) audio_sink_{std::make_unique(this)} { #elif defined(__linux__) audio_sink_{std::make_unique(this)} { #endif bool res = audio_sink_->Initialize(); CHECK(res) << "Failed to initialize audio sink."; } AudioMixer::~AudioMixer() = default; uint64_t AudioMixer::CreateResource() { uint64_t resource_id = ++last_resource_id_; resources_[resource_id] = std::make_shared(); return resource_id; } void AudioMixer::DestroyResource(uint64_t resource_id) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; if (it->second->active) { it->second->restart_cb = nullptr; it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); } resources_.erase(it); } void AudioMixer::Play(uint64_t resource_id, std::shared_ptr audio_bus, float amplitude, bool reset_pos) { if (!audio_enabled_) return; auto it = resources_.find(resource_id); if (it == resources_.end()) return; if (it->second->active) { if (reset_pos) it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); if (it->second->flags.load(std::memory_order_relaxed) & kStopped) it->second->restart_cb = [&, resource_id, audio_bus, amplitude, reset_pos]() -> void { Play(resource_id, audio_bus, amplitude, reset_pos); }; return; } if (reset_pos) { it->second->src_index = 0; it->second->accumulator = 0; audio_bus->ResetStream(); } else if (it->second->src_index >= audio_bus->samples_per_channel()) { return; } it->second->active = true; it->second->flags.fetch_and(~kStopped, std::memory_order_relaxed); it->second->audio_bus = audio_bus; if (amplitude >= 0) it->second->amplitude = amplitude; std::lock_guard scoped_lock(lock_); play_list_[0].push_back(it->second); } void AudioMixer::Stop(uint64_t resource_id) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; if (it->second->active) { it->second->restart_cb = nullptr; it->second->flags.fetch_or(kStopped, std::memory_order_relaxed); } } void AudioMixer::SetLoop(uint64_t resource_id, bool loop) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; if (loop) it->second->flags.fetch_or(kLoop, std::memory_order_relaxed); else it->second->flags.fetch_and(~kLoop, std::memory_order_relaxed); } void AudioMixer::SetSimulateStereo(uint64_t resource_id, bool simulate) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; if (simulate) it->second->flags.fetch_or(kSimulateStereo, std::memory_order_relaxed); else it->second->flags.fetch_and(~kSimulateStereo, std::memory_order_relaxed); } void AudioMixer::SetResampleStep(uint64_t resource_id, size_t step) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; it->second->step.store(step + 100, std::memory_order_relaxed); } void AudioMixer::SetMaxAmplitude(uint64_t resource_id, float max_amplitude) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; it->second->max_amplitude.store(max_amplitude, std::memory_order_relaxed); } void AudioMixer::SetAmplitudeInc(uint64_t resource_id, float amplitude_inc) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; it->second->amplitude_inc.store(amplitude_inc, std::memory_order_relaxed); } void AudioMixer::SetEndCallback(uint64_t resource_id, base::Closure cb) { auto it = resources_.find(resource_id); if (it == resources_.end()) return; it->second->end_cb = std::move(cb); } void AudioMixer::Suspend() { audio_sink_->Suspend(); } void AudioMixer::Resume() { audio_sink_->Resume(); } size_t AudioMixer::GetHardwareSampleRate() { return audio_sink_->GetHardwareSampleRate(); } void AudioMixer::RenderAudio(float* output_buffer, size_t num_frames) { { std::unique_lock scoped_lock(lock_, std::try_to_lock); if (scoped_lock) play_list_[1].splice(play_list_[1].end(), play_list_[0]); } memset(output_buffer, 0, sizeof(float) * num_frames * kChannelCount); for (auto it = play_list_[1].begin(); it != play_list_[1].end();) { auto audio_bus = it->get()->audio_bus.get(); unsigned flags = it->get()->flags.load(std::memory_order_relaxed); bool marked_for_removal = false; if (flags & kStopped) { marked_for_removal = true; } else { const float* src[2] = {audio_bus->GetChannelData(0), audio_bus->GetChannelData(1)}; if (!src[1]) src[1] = src[0]; // mono. size_t num_samples = audio_bus->samples_per_channel(); size_t src_index = it->get()->src_index; size_t step = it->get()->step.load(std::memory_order_relaxed); size_t accumulator = it->get()->accumulator; float amplitude = it->get()->amplitude; float amplitude_inc = it->get()->amplitude_inc.load(std::memory_order_relaxed); float max_amplitude = it->get()->max_amplitude.load(std::memory_order_relaxed); size_t channel_offset = (flags & kSimulateStereo) ? audio_bus->sample_rate() / 10 : 0; DCHECK(num_samples > 0); for (size_t i = 0; i < num_frames * kChannelCount;) { if (src_index < num_samples) { // Mix the 1st channel. output_buffer[i++] += src[0][src_index] * amplitude; // Mix the 2nd channel. Offset the source index for stereo simulation. size_t ind = channel_offset + src_index; if (ind < num_samples) output_buffer[i++] += src[1][ind] * amplitude; else if (flags & kLoop) output_buffer[i++] += src[1][ind % num_samples] * amplitude; else i++; // Apply amplitude modification. amplitude += amplitude_inc; if (amplitude <= 0) { marked_for_removal = true; break; } else if (amplitude > max_amplitude) { amplitude = max_amplitude; } // Advance source index. Apply basic resampling for variations. accumulator += step; src_index += accumulator / 100; accumulator %= 100; } else { if (audio_bus->EndOfStream()) { src_index %= num_samples; marked_for_removal = !(flags & kLoop); break; } if (!it->get()->streaming_in_progress.load( std::memory_order_acquire)) { src_index %= num_samples; it->get()->streaming_in_progress.store(true, std::memory_order_relaxed); // Swap buffers and start streaming in background. audio_bus->SwapBuffers(); src[0] = audio_bus->GetChannelData(0); src[1] = audio_bus->GetChannelData(1); if (!src[1]) src[1] = src[0]; // mono. num_samples = audio_bus->samples_per_channel(); ThreadPool::Get().PostTask( HERE, std::bind(&AudioMixer::DoStream, this, *it, flags & kLoop), true); } else { DLOG << "Mixer buffer underrun!"; } } } it->get()->src_index = src_index; it->get()->accumulator = accumulator; it->get()->amplitude = amplitude; } if (marked_for_removal) { end_list_.push_back(*it); it = play_list_[1].erase(it); } else { ++it; } } for (auto it = end_list_.begin(); it != end_list_.end();) { if (!it->get()->streaming_in_progress.load(std::memory_order_relaxed)) { main_thread_task_runner_->PostTask( HERE, std::bind(&AudioMixer::EndCallback, this, *it)); it = end_list_.erase(it); } else { ++it; } } } void AudioMixer::DoStream(std::shared_ptr resource, bool loop) { resource->audio_bus->Stream(loop); resource->streaming_in_progress.store(false, std::memory_order_release); } void AudioMixer::EndCallback(std::shared_ptr resource) { resource->active = false; if (resource->end_cb) resource->end_cb(); if (resource->restart_cb) { resource->restart_cb(); resource->restart_cb = nullptr; } } } // namespace eng