summaryrefslogtreecommitdiff
path: root/src/audio/AudioEngine.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/audio/AudioEngine.cpp')
-rw-r--r--src/audio/AudioEngine.cpp295
1 files changed, 295 insertions, 0 deletions
diff --git a/src/audio/AudioEngine.cpp b/src/audio/AudioEngine.cpp
new file mode 100644
index 0000000..aad1b9f
--- /dev/null
+++ b/src/audio/AudioEngine.cpp
@@ -0,0 +1,295 @@
+//
+// libavg - Media Playback Engine.
+// Copyright (C) 2003-2014 Ulrich von Zadow
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+//
+// Current versions can be found at www.libavg.de
+//
+// Original author of this file is Nick Hebner (hebnern@gmail.com).
+//
+
+#include "AudioEngine.h"
+
+#include "Dynamics.h"
+
+#include "../base/Exception.h"
+#include "../base/Logger.h"
+
+#include <iostream>
+
+using namespace std;
+using namespace boost;
+
+namespace avg {
+
+AudioEngine* AudioEngine::s_pInstance = 0;
+
+AudioEngine* AudioEngine::get()
+{
+ return s_pInstance;
+}
+
+AudioEngine::AudioEngine()
+ : m_pTempBuffer(),
+ m_pMixBuffer(0),
+ m_pLimiter(0),
+ m_bEnabled(true),
+ m_Volume(1)
+{
+ AVG_ASSERT(s_pInstance == 0);
+ if (SDL_InitSubSystem(SDL_INIT_AUDIO) == -1) {
+ AVG_LOG_ERROR("Can't init SDL audio subsystem.");
+ exit(-1);
+ }
+ s_pInstance = this;
+}
+
+AudioEngine::~AudioEngine()
+{
+ if (m_pMixBuffer) {
+ delete[] m_pMixBuffer;
+ }
+ SDL_QuitSubSystem(SDL_INIT_AUDIO);
+ m_AudioSources.clear();
+}
+
+int AudioEngine::getChannels()
+{
+ return m_AP.m_Channels;
+}
+
+int AudioEngine::getSampleRate()
+{
+ return m_AP.m_SampleRate;
+}
+
+const AudioParams * AudioEngine::getParams()
+{
+ if (isEnabled()) {
+ return &m_AP;
+ } else {
+ return 0;
+ }
+}
+
+void AudioEngine::init(const AudioParams& ap, float volume)
+{
+ m_Volume = volume;
+ m_AP = ap;
+ Dynamics<float, 2>* pLimiter = new Dynamics<float, 2>(float(m_AP.m_SampleRate));
+ pLimiter->setThreshold(0.f); // in dB
+ pLimiter->setAttackTime(0.f); // in seconds
+ pLimiter->setReleaseTime(0.05f); // in seconds
+ pLimiter->setRmsTime(0.f); // in seconds
+ pLimiter->setRatio(std::numeric_limits<float>::infinity());
+ pLimiter->setMakeupGain(0.f); // in dB
+ m_pLimiter = pLimiter;
+
+ SDL_AudioSpec desired;
+ desired.freq = m_AP.m_SampleRate;
+ desired.format = AUDIO_S16SYS;
+ desired.channels = m_AP.m_Channels;
+ desired.silence = 0;
+ desired.samples = m_AP.m_OutputBufferSamples;
+ desired.callback = audioCallback;
+ desired.userdata = this;
+
+ int err = SDL_OpenAudio(&desired, 0);
+ if (err < 0) {
+ static bool bWarned = false;
+ if (!bWarned) {
+ AVG_TRACE(Logger::category::CONFIG, Logger::severity::WARNING,
+ "Can't open audio: " << SDL_GetError());
+ bWarned = true;
+ }
+ }
+}
+
+void AudioEngine::teardown()
+{
+ {
+ lock_guard lock(m_Mutex);
+ SDL_PauseAudio(1);
+ }
+ // Optimized away - takes too long.
+// SDL_CloseAudio();
+
+ m_AudioSources.clear();
+ if (m_pLimiter) {
+ delete m_pLimiter;
+ m_pLimiter = 0;
+ }
+}
+
+void AudioEngine::setAudioEnabled(bool bEnabled)
+{
+ SDL_LockAudio();
+ lock_guard lock(m_Mutex);
+ AVG_ASSERT(m_AudioSources.empty());
+ m_bEnabled = bEnabled;
+ if (m_bEnabled) {
+ play();
+ } else {
+ pause();
+ }
+ SDL_UnlockAudio();
+}
+
+void AudioEngine::play()
+{
+ SDL_PauseAudio(0);
+}
+
+void AudioEngine::pause()
+{
+ SDL_PauseAudio(1);
+}
+
+int AudioEngine::addSource(AudioMsgQueue& dataQ, AudioMsgQueue& statusQ)
+{
+ SDL_LockAudio();
+ lock_guard lock(m_Mutex);
+ static int nextID = -1;
+ nextID++;
+ AudioSourcePtr pSrc(new AudioSource(dataQ, statusQ, m_AP.m_SampleRate));
+ m_AudioSources[nextID] = pSrc;
+ SDL_UnlockAudio();
+ return nextID;
+}
+
+void AudioEngine::removeSource(int id)
+{
+ SDL_LockAudio();
+ lock_guard lock(m_Mutex);
+ int numErased = m_AudioSources.erase(id);
+ AVG_ASSERT(numErased == 1);
+ SDL_UnlockAudio();
+}
+
+void AudioEngine::pauseSource(int id)
+{
+ lock_guard lock(m_Mutex);
+ AudioSourceMap::iterator itSource = m_AudioSources.find(id);
+ AVG_ASSERT(itSource != m_AudioSources.end());
+ AudioSourcePtr pSource = itSource->second;
+ pSource->pause();
+}
+
+void AudioEngine::playSource(int id)
+{
+ lock_guard lock(m_Mutex);
+ AudioSourceMap::iterator itSource = m_AudioSources.find(id);
+ AVG_ASSERT(itSource != m_AudioSources.end());
+ AudioSourcePtr pSource = itSource->second;
+ pSource->play();
+}
+
+void AudioEngine::notifySeek(int id)
+{
+ lock_guard lock(m_Mutex);
+ AudioSourceMap::iterator itSource = m_AudioSources.find(id);
+ AVG_ASSERT(itSource != m_AudioSources.end());
+ AudioSourcePtr pSource = itSource->second;
+ pSource->notifySeek();
+}
+
+void AudioEngine::setSourceVolume(int id, float volume)
+{
+ lock_guard lock(m_Mutex);
+ AudioSourceMap::iterator itSource = m_AudioSources.find(id);
+ AVG_ASSERT(itSource != m_AudioSources.end());
+ AudioSourcePtr pSource = itSource->second;
+ pSource->setVolume(volume);
+}
+
+void AudioEngine::setVolume(float volume)
+{
+ SDL_LockAudio();
+ lock_guard lock(m_Mutex);
+ m_Volume = volume;
+ SDL_UnlockAudio();
+}
+
+float AudioEngine::getVolume() const
+{
+ return m_Volume;
+}
+
+bool AudioEngine::isEnabled() const
+{
+ return m_bEnabled;
+}
+
+void AudioEngine::mixAudio(Uint8 *pDestBuffer, int destBufferLen)
+{
+ int numFrames = destBufferLen/(2*getChannels()); // 16 bit samples.
+
+ if (m_AudioSources.size() == 0) {
+ return;
+ }
+ if (!m_pTempBuffer || m_pTempBuffer->getNumFrames() < numFrames) {
+ if (m_pTempBuffer) {
+ delete[] m_pMixBuffer;
+ }
+ m_pTempBuffer = AudioBufferPtr(new AudioBuffer(numFrames, m_AP));
+ m_pMixBuffer = new float[getChannels()*numFrames];
+ }
+
+ for (int i = 0; i < getChannels()*numFrames; ++i) {
+ m_pMixBuffer[i]=0;
+ }
+ {
+ lock_guard lock(m_Mutex);
+ AudioSourceMap::iterator it;
+ for (it = m_AudioSources.begin(); it != m_AudioSources.end(); it++) {
+ m_pTempBuffer->clear();
+ it->second->fillAudioBuffer(m_pTempBuffer);
+ addBuffers(m_pMixBuffer, m_pTempBuffer);
+ }
+ }
+ calcVolume(m_pMixBuffer, numFrames*getChannels(), getVolume());
+ for (int i = 0; i < numFrames; ++i) {
+ m_pLimiter->process(m_pMixBuffer+i*getChannels());
+ for (int j = 0; j < getChannels(); ++j) {
+ ((short*)pDestBuffer)[i*2+j]=short(m_pMixBuffer[i*2+j]*32768);
+ }
+ }
+}
+
+void AudioEngine::audioCallback(void *userData, Uint8 *audioBuffer, int audioBufferLen)
+{
+ AudioEngine *pThis = (AudioEngine*)userData;
+ pThis->mixAudio(audioBuffer, audioBufferLen);
+}
+
+void AudioEngine::addBuffers(float *pDest, AudioBufferPtr pSrc)
+{
+ int numFrames = pSrc->getNumFrames();
+ short * pData = pSrc->getData();
+ for(int i = 0; i < numFrames*getChannels(); ++i) {
+ pDest[i] += pData[i]/32768.0f;
+ }
+}
+
+void AudioEngine::calcVolume(float *pBuffer, int numSamples, float volume)
+{
+ // TODO: We need a VolumeFader class that keeps state.
+ for(int i = 0; i < numSamples; ++i) {
+ pBuffer[i] *= volume;
+ }
+}
+
+}