diff options
Diffstat (limited to 'src/player/SoundNode.cpp')
-rw-r--r-- | src/player/SoundNode.cpp | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/src/player/SoundNode.cpp b/src/player/SoundNode.cpp new file mode 100644 index 0000000..3bb089c --- /dev/null +++ b/src/player/SoundNode.cpp @@ -0,0 +1,354 @@ +// +// 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 +// +#include "SoundNode.h" +#include "Player.h" +#include "TypeDefinition.h" +#include "Canvas.h" + +#include "../base/Exception.h" +#include "../base/Logger.h" +#include "../base/ScopeTimer.h" +#include "../base/XMLHelper.h" +#include "../base/ObjectCounter.h" + +#include "../audio/AudioEngine.h" + +#include "../video/AsyncVideoDecoder.h" + +#include <iostream> +#include <sstream> + +#ifndef _WIN32 +#include <unistd.h> +#endif + +using namespace boost; +using namespace std; + +namespace avg { + +void SoundNode::registerType() +{ + TypeDefinition def = TypeDefinition("sound", "areanode", + ExportedObject::buildObject<SoundNode>) + .addArg(Arg<UTF8String>("href", "", false, offsetof(SoundNode, m_href))) + .addArg(Arg<bool>("loop", false, false, offsetof(SoundNode, m_bLoop))) + .addArg(Arg<float>("volume", 1.0, false, offsetof(SoundNode, m_Volume))) + ; + TypeRegistry::get()->registerType(def); +} + +SoundNode::SoundNode(const ArgList& args) + : m_Filename(""), + m_pEOFCallback(0), + m_SeekBeforeCanRenderTime(0), + m_pDecoder(0), + m_Volume(1.0), + m_State(Unloaded), + m_AudioID(-1) +{ + args.setMembers(this); + m_Filename = m_href; + initFilename(m_Filename); + m_pDecoder = new AsyncVideoDecoder(8); + + ObjectCounter::get()->incRef(&typeid(*this)); +} + +SoundNode::~SoundNode() +{ + if (m_pDecoder) { + delete m_pDecoder; + m_pDecoder = 0; + } + if (m_pEOFCallback) { + Py_DECREF(m_pEOFCallback); + } + ObjectCounter::get()->decRef(&typeid(*this)); +} + +long long SoundNode::getDuration() const +{ + exceptionIfUnloaded("getDuration"); + return (long long)(m_pDecoder->getVideoInfo().m_Duration*1000); +} + +std::string SoundNode::getAudioCodec() const +{ + exceptionIfUnloaded("getAudioCodec"); + return m_pDecoder->getVideoInfo().m_sACodec; +} + +int SoundNode::getAudioSampleRate() const +{ + exceptionIfUnloaded("getAudioSampleRate"); + return m_pDecoder->getVideoInfo().m_SampleRate; +} + +int SoundNode::getNumAudioChannels() const +{ + exceptionIfUnloaded("getNumAudioChannels"); + return m_pDecoder->getVideoInfo().m_NumAudioChannels; +} + +long long SoundNode::getCurTime() const +{ + exceptionIfUnloaded("getCurTime"); + return (long long)(m_pDecoder->getCurTime()*1000); +} + +void SoundNode::seekToTime(long long Time) +{ + exceptionIfUnloaded("seekToTime"); + seek(Time); +} + +bool SoundNode::getLoop() const +{ + return m_bLoop; +} + +void SoundNode::setEOFCallback(PyObject * pEOFCallback) +{ + if (m_pEOFCallback) { + Py_DECREF(m_pEOFCallback); + } + if (pEOFCallback == Py_None) { + m_pEOFCallback = 0; + } else { + avgDeprecationWarning("1.8", "SoundNode.setEOFCallback()", + "Node.subscribe(END_OF_FILE)"); + Py_INCREF(pEOFCallback); + m_pEOFCallback = pEOFCallback; + } +} + +void SoundNode::connectDisplay() +{ + if (!AudioEngine::get()) { + throw Exception(AVG_ERR_UNSUPPORTED, + "Sound nodes can only be created if audio is not disabled."); + } + checkReload(); + AreaNode::connectDisplay(); + long long curTime = Player::get()->getFrameTime(); + if (m_State != Unloaded) { + startDecoding(); + m_StartTime = curTime; + m_PauseTime = 0; + } + if (m_State == Paused) { + m_PauseStartTime = curTime; + } +} + +void SoundNode::connect(CanvasPtr pCanvas) +{ + checkReload(); + AreaNode::connect(pCanvas); + pCanvas->registerFrameEndListener(this); +} + +void SoundNode::disconnect(bool bKill) +{ + changeSoundState(Unloaded); + getCanvas()->unregisterFrameEndListener(this); + if (bKill) { + setEOFCallback(Py_None); + } + AreaNode::disconnect(bKill); +} + +void SoundNode::play() +{ + changeSoundState(Playing); +} + +void SoundNode::stop() +{ + changeSoundState(Unloaded); +} + +void SoundNode::pause() +{ + changeSoundState(Paused); +} + +const UTF8String& SoundNode::getHRef() const +{ + return m_href; +} + +void SoundNode::setHRef(const UTF8String& href) +{ + m_href = href; + checkReload(); +} + +float SoundNode::getVolume() +{ + return m_Volume; +} + +void SoundNode::setVolume(float volume) +{ + if (volume < 0) { + volume = 0; + } + m_Volume = volume; + if (m_AudioID != -1) { + AudioEngine::get()->setSourceVolume(m_AudioID, volume); + } +} + +void SoundNode::checkReload() +{ + string fileName (m_href); + if (m_href != "") { + initFilename(fileName); + if (fileName != m_Filename && m_State != Unloaded) { + changeSoundState(Unloaded); + m_Filename = fileName; + changeSoundState(Paused); + } else { + m_Filename = fileName; + } + } else { + changeSoundState(Unloaded); + m_Filename = ""; + } +} + +void SoundNode::onFrameEnd() +{ + if (m_State == Playing) { + m_pDecoder->updateAudioStatus(); + } + if (m_State == Playing && m_pDecoder->isEOF()) { + NodePtr pTempThis = getSharedThis(); + onEOF(); + } +} + +void SoundNode::changeSoundState(SoundState newSoundState) +{ + if (newSoundState == m_State) { + return; + } + if (m_State == Unloaded) { + open(); + } + if (newSoundState == Unloaded) { + close(); + } + if (getState() == NS_CANRENDER) { + long long curTime = Player::get()->getFrameTime(); + if (m_State == Unloaded) { + startDecoding(); + m_StartTime = curTime; + m_PauseTime = 0; + } + if (newSoundState == Paused) { + m_PauseStartTime = curTime; + AudioEngine::get()->pauseSource(m_AudioID); + } else if (newSoundState == Playing && m_State == Paused) { + m_PauseTime += curTime-m_PauseStartTime; + AudioEngine::get()->playSource(m_AudioID); + } + } + m_State = newSoundState; +} + +void SoundNode::seek(long long destTime) +{ + if (getState() == NS_CANRENDER) { + AudioEngine::get()->notifySeek(m_AudioID); + m_pDecoder->seek(float(destTime)/1000); + m_StartTime = Player::get()->getFrameTime() - destTime; + m_PauseTime = 0; + m_PauseStartTime = Player::get()->getFrameTime(); + } else { + // If we get a seek command before decoding has really started, we need to defer + // the actual seek until the decoder is ready. + m_SeekBeforeCanRenderTime = destTime; + } +} + +void SoundNode::open() +{ + m_pDecoder->open(m_Filename, false, true); + VideoInfo videoInfo = m_pDecoder->getVideoInfo(); + if (!videoInfo.m_bHasAudio) { + throw Exception(AVG_ERR_VIDEO_GENERAL, + string("SoundNode: Opening "+m_Filename + +" failed. No audio stream found.")); + } +} + +void SoundNode::startDecoding() +{ + AudioEngine* pEngine = AudioEngine::get(); + m_pDecoder->startDecoding(false, pEngine->getParams()); + m_AudioID = pEngine->addSource(*m_pDecoder->getAudioMsgQ(), + *m_pDecoder->getAudioStatusQ()); + pEngine->setSourceVolume(m_AudioID, m_Volume); + if (m_SeekBeforeCanRenderTime != 0) { + seek(m_SeekBeforeCanRenderTime); + m_SeekBeforeCanRenderTime = 0; + } +} + +void SoundNode::close() +{ + if (m_AudioID != -1) { + AudioEngine::get()->removeSource(m_AudioID); + m_AudioID = -1; + } + m_pDecoder->close(); +} + +void SoundNode::exceptionIfUnloaded(const std::string& sFuncName) const +{ + if (m_State == Unloaded) { + throw Exception(AVG_ERR_VIDEO_GENERAL, + string("SoundNode.")+sFuncName+" failed: sound not loaded."); + } +} + +void SoundNode::onEOF() +{ + seek(0); + if (!m_bLoop) { + changeSoundState(Paused); + } + if (m_pEOFCallback) { + PyObject * arglist = Py_BuildValue("()"); + PyObject * result = PyEval_CallObject(m_pEOFCallback, arglist); + Py_DECREF(arglist); + if (!result) { + throw py::error_already_set(); + } + Py_DECREF(result); + } + notifySubscribers("END_OF_FILE"); +} + +} |