diff options
Diffstat (limited to 'media-tv/xbmc/files/xbmc-10.1~beta1-jack.patch.bkp')
-rw-r--r-- | media-tv/xbmc/files/xbmc-10.1~beta1-jack.patch.bkp | 1738 |
1 files changed, 1738 insertions, 0 deletions
diff --git a/media-tv/xbmc/files/xbmc-10.1~beta1-jack.patch.bkp b/media-tv/xbmc/files/xbmc-10.1~beta1-jack.patch.bkp new file mode 100644 index 0000000..23933a9 --- /dev/null +++ b/media-tv/xbmc/files/xbmc-10.1~beta1-jack.patch.bkp @@ -0,0 +1,1738 @@ +Index: xbmc/cores/AudioRenderers/jackblockingaudioio.hpp +=================================================================== +--- xbmc/cores/AudioRenderers/jackblockingaudioio.hpp (revision 0) ++++ xbmc/cores/AudioRenderers/jackblockingaudioio.hpp (revision 1806) +@@ -0,0 +1,176 @@ ++//C++ Classes that wrap JACK ++//Copyright 2007 Alex Norman ++// ++//This file is part of JACKC++. ++// ++//JACKC++ is free software: you can redistribute it and/or modify ++//it under the terms of the GNU General Public License as published by ++//the Free Software Foundation, either version 3 of the License, or ++//(at your option) any later version. ++// ++//JACKC++ 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 General Public License for more details. ++// ++//You should have received a copy of the GNU General Public License ++//along with JACKC++. If not, see <http://www.gnu.org/licenses/>. ++ ++#ifndef JACK_BLOCKING_AUDIO_IO_HPP ++#define JACK_BLOCKING_AUDIO_IO_HPP ++ ++#include "jackaudioio.hpp" ++#include "jackringbuffer.hpp" ++ ++namespace JackCpp { ++ ++/** ++@class BlockingAudioIO ++ ++@brief This is an class that provides a blocking read/write interface for Jack. ++ ++This class has read/write methods that allow users to write audio to and read ++audio from a Jack client. ++ ++@author Alex Norman ++ ++*/ ++ class BlockingAudioIO : public AudioIO { ++ public: ++ /** ++ @brief The Constructor ++ \param name string indicating the name of the jack client to create ++ \param inChans an unsigned integer indicating the number of default input ports to create ++ \param outChans an unsigned integer indicating the number of default output ports to create ++ \param inBufSize the size of the buffer that the jack callback fills for us to read with read ++ \param outBufSize the size of the buffer that we write to and the jack callback reads from ++ \param startServer a boolean indicating whether to start a jack server if one isn't already running ++ \sa AudioIO::AudioIO ++ */ ++ BlockingAudioIO(std::string name, ++ unsigned int inChans = 2, unsigned int outChans = 2, ++ unsigned int inBufSize = 0, unsigned int outBufSize = 0, ++#ifdef __APPLE__ ++ bool startServer = false) ++#else ++ bool startServer = true) ++#endif ++ throw(std::runtime_error); ++ virtual ~BlockingAudioIO(); ++ ++ /** ++ @brief Write to an output buffer ++ ++ Writes val to the output[channel] (if it exists). If ++ output[channel] does not exist is silently fails otherwise it ++ sleeps until it can write output[channel] ++ ++ \param channel the output chanel to write to ++ \param val the value to write to the channel ++ \sa tryWrite(unsigned int channel, jack_default_audio_sample_t val) ++ */ ++ void write(unsigned int channel, jack_default_audio_sample_t val); ++ ++ /** ++ @brief Try to write to an output buffer ++ ++ Trys to write to output[channel] (if it exists). If it succeeds it ++ returns true. If it fails, because the buffer is full, it returns ++ false. ++ ++ \param channel the output chanel to write to ++ \param val the value to write to the channel ++ \return true if it can write, false if it cannot ++ \sa write(unsigned int channel, jack_default_audio_sample_t val) ++ */ ++ bool tryWrite(unsigned int channel, jack_default_audio_sample_t val); ++ ++ /** ++ @brief Read from an input buffer. ++ ++ Reads from input[channel] if it exists. If there is no input to ++ read it sleeps until there is. ++ ++ \param channel the input chanel to read from ++ \return the value read from the input channel [will be zero if the channel does not exist] ++ \sa tryRead(unsigned int channel, jack_default_audio_sample_t &val) ++ */ ++ jack_default_audio_sample_t read(unsigned int channel); ++ ++ /** ++ @brief Try to read from to an input buffer ++ ++ Trys to read from input[channel] (if it exists). If it succeeds it ++ returns true. If it fails, because the buffer is empty, it returns ++ false. ++ ++ \param channel the output chanel to read from ++ \param val the value to read into (basically a return value) ++ \return true if it can read, false if it cannot ++ \sa read(unsigned int channel) ++ */ ++ bool tryRead(unsigned int channel, jack_default_audio_sample_t &val); ++ ++ //XXX reserve exists but is basically useless as you cannot ++ //add ports while the client is active ++ ///This method is useless at the moment. ++ virtual void reserveOutPorts(unsigned int num) ++ throw(std::runtime_error); ++ ///This method is useless at the moment. ++ virtual void reserveInPorts(unsigned int num) ++ throw(std::runtime_error); ++ ++ /** ++ @brief Add an input port to our client ++ ++ Unlike AudioIO, this currently cannot be called while the client is running. ++ ++ \param name string the name of the port to add ++ \return the number of total input ports ++ \sa AudioIO::addInPort(std::string name) ++ */ ++ virtual unsigned int addInPort(std::string name) ++ throw(std::runtime_error); ++ /** ++ @brief Add an output port to our client ++ ++ Unlike AudioIO, this currently cannot be called while the client is running. ++ ++ \param name string the name of the port to add ++ \return the number of total output ports ++ \sa AudioIO::addOutPort(std::string name) ++ */ ++ virtual unsigned int addOutPort(std::string name) ++ throw(std::runtime_error); ++ ++ protected: ++ /** ++ @brief This is the callback that processes our buffers. ++ ++ This method takes the buffers we write to with "write" and writes ++ them out to the Jack bus. It also takes audio from the Jack bus ++ and uses that to fill the input buffers that we read from. ++ ++ \param nframes the number frames to process ++ \param inBufs a vector of audio buffers ++ \param outBufs a vector of audio buffers ++ \return the actual number of frames processed ++ */ ++ virtual int audioCallback(jack_nframes_t nframes, ++ std::vector<jack_default_audio_sample_t *> inBufs, ++ std::vector<jack_default_audio_sample_t *> outBufs); ++ private: ++ std::vector<RingBuffer<jack_default_audio_sample_t> *> mUserOutBuff; ++ std::vector<RingBuffer<jack_default_audio_sample_t> *> mUserInBuff; ++ ++ //this is the size of the ring buffers that we alloc ++ const unsigned int mOutputBufferMaxSize; ++ const unsigned int mInputBufferMaxSize; ++ //this is the amount of free space we leave in the ring buffers ++ //this can decrease so that we'll have more latency but fewer glitches ++ unsigned int mOutputBufferFreeSize; ++ unsigned int mInputBufferFreeSize; ++ }; ++} ++#endif ++ +Index: xbmc/cores/AudioRenderers/Makefile.in +=================================================================== +--- xbmc/cores/AudioRenderers/Makefile.in (revision 1805) ++++ xbmc/cores/AudioRenderers/Makefile.in (revision 1806) +@@ -4,11 +4,17 @@ + + ifeq ($(findstring osx,$(ARCH)), osx) + SRCS = \ ++ jackaudioio.cpp \ ++ jackblockingaudioio.cpp \ ++ JackDirectSound.cpp \ + NullDirectSound.cpp \ + AudioRendererFactory.cpp \ + CoreAudioRenderer.cpp + else + SRCS = \ ++ jackaudioio.cpp \ ++ jackblockingaudioio.cpp \ ++ JackDirectSound.cpp \ + NullDirectSound.cpp \ + AudioRendererFactory.cpp \ + ALSADirectSound.cpp \ +Index: xbmc/cores/AudioRenderers/JackDirectSound.cpp +=================================================================== +--- xbmc/cores/AudioRenderers/JackDirectSound.cpp (revision 0) ++++ xbmc/cores/AudioRenderers/JackDirectSound.cpp (revision 1806) +@@ -0,0 +1,243 @@ ++/* ++ * Copyright (C) 2005-2008 Team XBMC ++ * http://www.xbmc.org ++ * ++ * This Program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This Program 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 General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with XBMC; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#include "JackDirectSound.h" ++#include "AudioContext.h" ++#include "Application.h" ++#include "utils/log.h" ++#include "utils/TimeUtils.h" ++ ++#define BUFFER CHUNKLEN * 20 ++#define CHUNKLEN 512 ++ ++ ++void CJackDirectSound::DoWork() ++{ ++ ++} ++ ++////////////////////////////////////////////////////////////////////// ++// Construction/Destruction ++////////////////////////////////////////////////////////////////////// ++//*********************************************************************************************** ++CJackDirectSound::CJackDirectSound() ++{ ++ jackBuffer = 0; ++} ++bool CJackDirectSound::Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, const char* strAudioCodec, bool bIsMusic, bool bPassthrough) ++{ ++ CLog::Log(LOGERROR,"Jack.Initialize() - Channels: %i - SampleRate: %i - SampleBit: %i - Resample %s - Codec %s - IsMusic %s - IsPassthrough %s", iChannels, uiSamplesPerSec, uiBitsPerSample, bResample ? "true" : "false", strAudioCodec, bIsMusic ? "true" : "false", bPassthrough ? "true" : "false"); ++ if (iChannels == 0) ++ iChannels = 2; ++ ++ bool bAudioOnAllSpeakers(false); ++ g_audioContext.SetupSpeakerConfig(iChannels, bAudioOnAllSpeakers, bIsMusic); ++ g_audioContext.SetActiveDevice(CAudioContext::DIRECTSOUND_DEVICE); ++ ++ ++ jackBuffer = new JackCpp::BlockingAudioIO("XBMC.Jack", iChannels, iChannels); ++ jackBuffer->start(); ++ for(int i = 0; i < iChannels; i++){ ++ jackBuffer->connectToPhysical(i,i); ++ } ++ m_uiChannels = iChannels; ++ ++ //g_application.m_guiDialogKaiToast.QueueNotification("Failed to initialize audio device", "Check your audiosettings" ++ ++ m_timePerPacket = 1.0f / (float)(iChannels*(uiBitsPerSample/8) * uiSamplesPerSec); ++ m_packetsSent = 0; ++ m_paused = 0; ++ m_lastUpdate = CTimeUtils::GetTimeMS(); ++ return true; ++} ++ ++//*********************************************************************************************** ++CJackDirectSound::~CJackDirectSound() ++{ ++ Deinitialize(); ++} ++ ++ ++//*********************************************************************************************** ++bool CJackDirectSound::Deinitialize() ++{ ++ CLog::Log(LOGERROR,"Jack.Deinitialize"); ++ if (jackBuffer) { ++ for(int i = 0; i < m_uiChannels; i++){ ++ jackBuffer->disconnectOutPort(i); ++ } ++ jackBuffer->close(); ++ //TODO: Cannot delete jackBuffer, otherwise will crash. ++ //delete jackBuffer; ++ } ++ jackBuffer = 0; ++ ++ g_audioContext.SetActiveDevice(CAudioContext::DEFAULT_DEVICE); ++ return true; ++} ++ ++void CJackDirectSound::Flush() ++{ ++ m_lastUpdate = CTimeUtils::GetTimeMS(); ++ m_packetsSent = 0; ++ Pause(); ++} ++ ++//*********************************************************************************************** ++bool CJackDirectSound::Pause() ++{ ++ m_paused = true; ++ return true; ++} ++ ++//*********************************************************************************************** ++bool CJackDirectSound::Resume() ++{ ++ m_paused = false; ++ return true; ++} ++ ++//*********************************************************************************************** ++bool CJackDirectSound::Stop() ++{ ++ Flush(); ++ return true; ++} ++ ++//*********************************************************************************************** ++long CJackDirectSound::GetCurrentVolume() const ++{ ++ return m_nCurrentVolume; ++} ++ ++//*********************************************************************************************** ++void CJackDirectSound::Mute(bool bMute) ++{ ++} ++ ++//*********************************************************************************************** ++bool CJackDirectSound::SetCurrentVolume(long nVolume) ++{ ++ m_nCurrentVolume = nVolume; ++ return true; ++} ++ ++ ++//*********************************************************************************************** ++unsigned int CJackDirectSound::GetSpace() ++{ ++ Update(); ++ ++ if(BUFFER > m_packetsSent) ++ return (int)BUFFER - m_packetsSent; ++ else ++ return 0; ++} ++ ++//*********************************************************************************************** ++unsigned int CJackDirectSound::AddPackets(const void* data, unsigned int len) ++{ ++ if (m_paused) ++ return 0; ++ Update(); ++ ++ int add = ( len / GetChunkLen() ) * GetChunkLen(); ++ m_packetsSent += add; ++ ++ CLog::Log(LOGERROR,"Jack.AddPackets() len=%d, add=%d", len, add); ++ ++ if (jackBuffer){ ++ short* pSamples = (short*)data; ++ for (int i=0; i< add/sizeof(short)/m_uiChannels; i++){ ++ for(unsigned int j = 0; j < m_uiChannels; j++){ ++ jackBuffer->write(j, (float) pSamples[i*m_uiChannels + j] / 32768.0); ++ } ++ } ++ } ++ ++ return add; ++} ++ ++//*********************************************************************************************** ++float CJackDirectSound::GetDelay() ++{ ++ Update(); ++ ++ return m_timePerPacket * (float)m_packetsSent + 0.4; ++} ++ ++float CJackDirectSound::GetCacheTime() ++{ ++ return GetDelay(); ++} ++ ++//*********************************************************************************************** ++unsigned int CJackDirectSound::GetChunkLen() ++{ ++ return (int)CHUNKLEN; ++} ++//*********************************************************************************************** ++int CJackDirectSound::SetPlaySpeed(int iSpeed) ++{ ++ return 0; ++} ++ ++void CJackDirectSound::RegisterAudioCallback(IAudioCallback *pCallback) ++{ ++} ++ ++void CJackDirectSound::UnRegisterAudioCallback() ++{ ++} ++ ++void CJackDirectSound::WaitCompletion() ++{ ++ while(m_packetsSent > 0) ++ Update(); ++} ++ ++void CJackDirectSound::SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers) ++{ ++ return ; ++} ++ ++void CJackDirectSound::Update() ++{ ++ long currentTime = CTimeUtils::GetTimeMS(); ++ long deltaTime = (currentTime - m_lastUpdate); ++ ++ if (m_paused) ++ { ++ m_lastUpdate += deltaTime; ++ return; ++ } ++ ++ double d = (double)deltaTime / 1000.0f; ++ ++ if (currentTime != m_lastUpdate) ++ { ++ double i = (d / (double)m_timePerPacket); ++ m_packetsSent -= (long)i; ++ if (m_packetsSent < 0) ++ m_packetsSent = 0; ++ m_lastUpdate = currentTime; ++ } ++} +Index: xbmc/cores/AudioRenderers/AudioRendererFactory.cpp +=================================================================== +--- xbmc/cores/AudioRenderers/AudioRendererFactory.cpp (revision 1805) ++++ xbmc/cores/AudioRenderers/AudioRendererFactory.cpp (revision 1806) +@@ -24,6 +24,7 @@ + #include "GUISettings.h" + #include "log.h" + #include "NullDirectSound.h" ++#include "JackDirectSound.h" + + #ifdef HAS_PULSEAUDIO + #include "PulseAudioDirectSound.h" +@@ -132,6 +133,10 @@ + + device = deviceString; + ++//For Jack ++ audioSink = new CJackDirectSound(); ++ ReturnOnValidInitialize(); ++ + /* First pass creation */ + #ifdef HAS_PULSEAUDIO + audioSink = new CPulseAudioDirectSound(); +Index: xbmc/cores/AudioRenderers/JackDirectSound.h +=================================================================== +--- xbmc/cores/AudioRenderers/JackDirectSound.h (revision 0) ++++ xbmc/cores/AudioRenderers/JackDirectSound.h (revision 1806) +@@ -0,0 +1,79 @@ ++/* ++ * Copyright (C) 2005-2008 Team XBMC ++ * http://www.xbmc.org ++ * ++ * This Program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2, or (at your option) ++ * any later version. ++ * ++ * This Program 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 General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with XBMC; see the file COPYING. If not, write to ++ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ++ * http://www.gnu.org/copyleft/gpl.html ++ * ++ */ ++ ++#ifndef __JACK_DIRECT_SOUND_H__ ++#define __JACK_DIRECT_SOUND_H__ ++ ++#if _MSC_VER > 1000 ++#pragma once ++#endif // _MSC_VER > 1000 ++ ++#include "IAudioRenderer.h" ++#include "IAudioCallback.h" ++ ++#include "jackblockingaudioio.hpp" ++ ++extern void RegisterAudioCallback(IAudioCallback* pCallback); ++extern void UnRegisterAudioCallback(); ++ ++class CJackDirectSound : public IAudioRenderer ++{ ++public: ++ virtual void UnRegisterAudioCallback(); ++ virtual void RegisterAudioCallback(IAudioCallback* pCallback); ++ virtual unsigned int GetChunkLen(); ++ virtual float GetDelay(); ++ virtual float GetCacheTime(); ++ CJackDirectSound(); ++ virtual bool Initialize(IAudioCallback* pCallback, const CStdString& device, int iChannels, unsigned int uiSamplesPerSec, unsigned int uiBitsPerSample, bool bResample, const char* strAudioCodec = "", bool bIsMusic=false, bool bPassthrough = false); ++ virtual ~CJackDirectSound(); ++ ++ virtual unsigned int AddPackets(const void* data, unsigned int len); ++ virtual unsigned int GetSpace(); ++ virtual bool Deinitialize(); ++ virtual bool Pause(); ++ virtual bool Stop(); ++ virtual bool Resume(); ++ ++ virtual long GetCurrentVolume() const; ++ virtual void Mute(bool bMute); ++ virtual bool SetCurrentVolume(long nVolume); ++ virtual int SetPlaySpeed(int iSpeed); ++ virtual void WaitCompletion(); ++ virtual void DoWork(); ++ virtual void SwitchChannels(int iAudioStream, bool bAudioOnAllSpeakers); ++ ++ virtual void Flush(); ++private: ++ long m_nCurrentVolume; ++ ++ float m_timePerPacket; ++ int m_packetsSent; ++ bool m_paused; ++ long m_lastUpdate; ++ ++ int m_uiChannels; ++ JackCpp::BlockingAudioIO* jackBuffer; ++ ++ void Update(); ++}; ++ ++#endif +Index: xbmc/cores/AudioRenderers/jackaudioio.cpp +=================================================================== +--- xbmc/cores/AudioRenderers/jackaudioio.cpp (revision 0) ++++ xbmc/cores/AudioRenderers/jackaudioio.cpp (revision 1806) +@@ -0,0 +1,475 @@ ++//C++ Classes that wrap JACK ++//Copyright 2007 Alex Norman ++// ++//This file is part of JACKC++. ++// ++//JACKC++ is free software: you can redistribute it and/or modify ++//it under the terms of the GNU General Public License as published by ++//the Free Software Foundation, either version 3 of the License, or ++//(at your option) any later version. ++// ++//JACKC++ 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 General Public License for more details. ++// ++//You should have received a copy of the GNU General Public License ++//along with JACKC++. If not, see <http://www.gnu.org/licenses/>. ++ ++#include "jackaudioio.hpp" ++#include <iostream> ++#include <errno.h> ++#include <sstream> ++#include <algorithm> ++ ++template <typename T> ++std::string ToString(T aValue){ ++ std::stringstream ss; ++ ss << aValue; ++ return ss.str(); ++} ++ ++/* callback for jack's error messages */ ++static void error_callback (const char *msg) { ++ std::cerr << "Jack:" << msg << std::endl; ++ std::cerr.flush(); ++} ++ ++static void shutdown_callback (void *arg) { ++ return ((JackCpp::AudioIO *)arg)->jackShutdownCallback(); ++} ++ ++void JackCpp::AudioIO::jackShutdownCallback(){ ++ std::cerr << std::endl << "jack has shutdown" << std::endl; ++} ++ ++int JackCpp::AudioIO::jackProcessCallback(jack_nframes_t nframes, void *arg){ ++ JackCpp::AudioIO* callbackjackobject = (AudioIO * )arg; ++ return callbackjackobject->jackToClassAudioCallback(nframes); ++} ++ ++int JackCpp::AudioIO::jackToClassAudioCallback(jack_nframes_t nframes){ ++ //read in commands ++ while(mCmdBuffer.getReadSpace() > 0){ ++ cmd_t cmd; ++ mCmdBuffer.read(cmd); ++ switch(cmd){ ++ case add_in_port: ++ //we will have tested that we have this capacity, so we resize the buffer ++ //to include the new port ++ mJackInBuf.resize(mJackInBuf.size() + 1); ++ mNumInputPorts++; ++ break; ++ case add_out_port: ++ //we will have tested that we have this capacity, so we resize the buffer ++ //to include the new port ++ mJackOutBuf.resize(mJackOutBuf.size() + 1); ++ mNumOutputPorts++; ++ break; ++ } ++ } ++ ++ //get the input and output buffers ++ for(unsigned int i = 0; i < mNumInputPorts; i++) ++ mJackInBuf[i] = (jack_default_audio_sample_t *) jack_port_get_buffer ( mInputPorts[i], nframes); ++ for(unsigned int i = 0; i < mNumOutputPorts; i++) ++ mJackOutBuf[i] = (jack_default_audio_sample_t *) jack_port_get_buffer ( mOutputPorts[i], nframes); ++ ++ return audioCallback(nframes, mJackInBuf, mJackOutBuf); ++} ++ ++JackCpp::AudioIO::AudioIO(std::string name, unsigned int inPorts, unsigned int outPorts, bool startServer) ++ throw(std::runtime_error) : mCmdBuffer(256,true) ++{ ++ jack_options_t jack_open_options = JackNullOption; ++ ++ if(startServer == false) ++ jack_open_options = JackNoStartServer; ++ ++ mJackState = notActive; ++ ++ //set the error callback ++ jack_set_error_function (error_callback); ++ ++ /* try to become a client of the JACK server */ ++ if ((mJackClient = jack_client_open (name.c_str(), jack_open_options, NULL)) == 0) { ++ throw std::runtime_error("cannot create client jack server not running?"); ++ } ++#ifdef __APPLE__ ++ else { ++ // because the mac version of jack is being totally LAME ++ sleep(2); ++ } ++#endif ++ ++ //set the shutdown callback ++ jack_on_shutdown (mJackClient, shutdown_callback, this); ++ ++ //allocate ports ++ if (inPorts > 0){ ++ for(unsigned int i = 0; i < inPorts; i++){ ++ std::string portname = "input"; ++ portname.append(ToString(i)); ++ mInputPorts.push_back( ++ jack_port_register (mJackClient, portname.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); ++ mPortNames.push_back(portname); ++ } ++ //reserve the data for the jack callback buffers ++ for(unsigned int i = 0; i < mInputPorts.size(); i++) ++ mJackInBuf.push_back(NULL); ++ } ++ if (outPorts > 0){ ++ for(unsigned int i = 0; i < outPorts; i++){ ++ std::string portname = "output"; ++ portname.append(ToString(i)); ++ mOutputPorts.push_back( ++ jack_port_register (mJackClient, portname.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); ++ mPortNames.push_back(portname); ++ } ++ //reserve the data for the jack callback buffers ++ for(unsigned int i = 0; i < mOutputPorts.size(); i++) ++ mJackOutBuf.push_back(NULL); ++ } ++ ++ //set up the callback ++ if(0 != jack_set_process_callback (mJackClient, JackCpp::AudioIO::jackProcessCallback, this)) ++ throw std::runtime_error("cannot register process callback"); ++} ++ ++ ++JackCpp::AudioIO::~AudioIO(){ ++ //make sure to deactiveate the client if we need to ++ switch(mJackState){ ++ case active: ++ stop(); ++ close(); ++ break; ++ case notActive: ++ close(); ++ break; ++ default: ++ break; ++ //do nothing ++ } ++} ++ ++bool JackCpp::AudioIO::portExists(std::string name){ ++ //see if the port name exists ++ std::vector<std::string>::iterator it; ++ it = std::find(mPortNames.begin(),mPortNames.end(), name); ++ if (it != mPortNames.end()) ++ return true; ++ else ++ return false; ++} ++ ++void JackCpp::AudioIO::reserveOutPorts(unsigned int num) ++ throw(std::runtime_error) ++{ ++ if(getState() == active) ++ throw std::runtime_error("reserving ports while the client is running is not supported yet."); ++ mOutputPorts.reserve(num); ++ mJackOutBuf.reserve(num); ++} ++ ++void JackCpp::AudioIO::reserveInPorts(unsigned int num) ++ throw(std::runtime_error) ++{ ++ if(getState() == active) ++ throw std::runtime_error("reserving ports while the client is running is not supported yet."); ++ mInputPorts.reserve(num); ++ mJackInBuf.reserve(num); ++} ++ ++unsigned int JackCpp::AudioIO::inPorts(){ ++ return mInputPorts.size(); ++} ++ ++unsigned int JackCpp::AudioIO::outPorts(){ ++ return mOutputPorts.size(); ++} ++ ++unsigned int JackCpp::AudioIO::addInPort(std::string name) ++ throw(std::runtime_error) ++{ ++ if (mJackState == active && mInputPorts.size() == mInputPorts.capacity()) ++ throw std::runtime_error("trying to add input ports while the client is running and there are not reserved ports"); ++ ++ if(portExists(name)){ ++ std::string ret_string("cannot register new inport: "); ++ ret_string.append(name); ++ ret_string.append(" port already exists with that name"); ++ throw std::runtime_error(ret_string); ++ } ++ ++ //allocate the item in the vector ++ jack_port_t * newPort = jack_port_register (mJackClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); ++ if(newPort == NULL){ ++ std::string ret_string("cannot register new inport: "); ++ ret_string.append(name); ++ throw std::runtime_error(ret_string); ++ } ++ mInputPorts.push_back(newPort); ++ mPortNames.push_back(name); ++ ++ //if we're active then send a command indicating this change ++ if (mJackState == active) { ++ //loop while there isn't space to write ++ while(mCmdBuffer.getWriteSpace() == 0); ++ mCmdBuffer.write(add_in_port); ++ } else ++ mJackInBuf.push_back(NULL); ++ ++ return mInputPorts.size() - 1; ++} ++ ++//add an output port, if we are active then deactivate and reactivate after ++//maybe we can do this more intelligently in the future? ++unsigned int JackCpp::AudioIO::addOutPort(std::string name) ++ throw(std::runtime_error) ++{ ++ if (mJackState == active && mOutputPorts.size() == mOutputPorts.capacity()) ++ throw std::runtime_error("trying to add output ports while the client is running and there are not reserved ports"); ++ ++ if(portExists(name)){ ++ std::string ret_string("cannot register new outport: "); ++ ret_string.append(name); ++ ret_string.append(" port already exists with that name"); ++ throw std::runtime_error(ret_string); ++ } ++ ++ //allocate the item in the vector ++ jack_port_t * newPort = jack_port_register (mJackClient, name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); ++ if(newPort == NULL){ ++ std::string ret_string("cannot register new outport: "); ++ ret_string.append(name); ++ throw std::runtime_error(ret_string); ++ } ++ mOutputPorts.push_back(newPort); ++ mPortNames.push_back(name); ++ ++ //if we're active then send a command indicating this change ++ if (mJackState == active) { ++ //loop while there isn't space to write ++ while(mCmdBuffer.getWriteSpace() == 0); ++ mCmdBuffer.write(add_out_port); ++ } else ++ mJackOutBuf.push_back(NULL); ++ ++ return mOutputPorts.size() - 1; ++} ++ ++void JackCpp::AudioIO::connectTo(unsigned int index, std::string destPortName) ++ throw(std::range_error, std::runtime_error) ++{ ++ int connect_ret; ++ if (mJackState != active) ++ throw std::runtime_error("client must be active before connecting ports"); ++ if(index < mOutputPorts.size()){ ++ connect_ret = jack_connect(mJackClient, jack_port_name(mOutputPorts[index]), destPortName.c_str()); ++ if(connect_ret != 0 && connect_ret != EEXIST){ ++ std::string ret_string("cannot connect source: "); ++ ret_string.append(jack_port_name(mOutputPorts[index])); ++ ret_string.append(" to dest: "); ++ ret_string.append(destPortName); ++ ret_string.append(" does dest exist?"); ++ throw std::range_error(ret_string); ++ } ++ } else ++ throw std::range_error("outport index out of range"); ++} ++ ++void JackCpp::AudioIO::connectFrom(unsigned int index, std::string sourcePortName) ++ throw(std::range_error, std::runtime_error) ++{ ++ int connect_ret; ++ if (mJackState != active) ++ throw std::runtime_error("client must be active before connecting ports"); ++ if(index < mInputPorts.size()){ ++ connect_ret = jack_connect(mJackClient, sourcePortName.c_str(), jack_port_name(mInputPorts[index])); ++ if(connect_ret != 0 && connect_ret != EEXIST){ ++ std::string ret_string("cannot connect source: "); ++ ret_string.append(sourcePortName); ++ ret_string.append(" to dest: "); ++ ret_string.append(jack_port_name(mInputPorts[index])); ++ ret_string.append(" does source exist?"); ++ throw std::range_error(ret_string); ++ } ++ } else ++ throw std::range_error("inport index out of range"); ++} ++ ++//XXX should the "free" free the names that these ports point too as well? ++void JackCpp::AudioIO::connectToPhysical(unsigned int index, unsigned physical_index) ++ throw(std::range_error, std::runtime_error) ++{ ++ const char **ports; ++ if (mJackState != active) ++ throw std::runtime_error("client must be active before connecting ports"); ++ if (index > mOutputPorts.size()) ++ throw std::range_error("outport index out of range"); ++ ports = jack_get_ports (mJackClient, NULL, NULL, JackPortIsPhysical|JackPortIsInput); ++ if(ports == NULL){ ++ throw std::range_error("no physical inports to connect to"); ++ } ++ //make sure the port exists ++ for(unsigned int i = 0; i <= physical_index; i++){ ++ if(ports[i] == NULL){ ++ free(ports); ++ throw std::range_error("physical inport index out of range"); ++ } ++ } ++ connectTo(index, ports[physical_index]); ++ free(ports); ++} ++ ++//XXX should the "free" free the names that these ports point too as well? ++void JackCpp::AudioIO::connectFromPhysical(unsigned int index, unsigned physical_index) ++ throw(std::range_error, std::runtime_error) ++{ ++ const char **ports; ++ if (mJackState != active) ++ throw std::runtime_error("client must be active before connecting ports"); ++ if (index > mInputPorts.size()) ++ throw std::range_error("inport index out of range"); ++ ports = jack_get_ports (mJackClient, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); ++ if(ports == NULL){ ++ throw std::range_error("no physical outports to connect to"); ++ } ++ //make sure the port exists ++ for(unsigned int i = 0; i <= physical_index; i++){ ++ if(ports[i] == NULL){ ++ free(ports); ++ throw std::range_error("physical outport index out of range"); ++ } ++ } ++ connectFrom(index, ports[physical_index]); ++ free(ports); ++} ++ ++void JackCpp::AudioIO::disconnectInPort(unsigned int index) ++ throw(std::range_error, std::runtime_error) ++{ ++ if (mJackState != active) ++ throw std::runtime_error("client must be active before disconnecting ports"); ++ if(index < mInputPorts.size()){ ++ jack_port_disconnect(mJackClient, mInputPorts[index]); ++ } else ++ throw std::range_error("inport index out of range"); ++} ++ ++void JackCpp::AudioIO::disconnectOutPort(unsigned int index) ++ throw(std::range_error, std::runtime_error) ++{ ++ if (mJackState != active) ++ throw std::runtime_error("client must be active before disconnecting ports"); ++ if(index < mOutputPorts.size()){ ++ jack_port_disconnect(mJackClient, mOutputPorts[index]); ++ } else ++ throw std::range_error("outport index out of range"); ++} ++ ++unsigned int JackCpp::AudioIO::numConnectionsInPort(unsigned int index) ++ throw(std::range_error) ++{ ++ if(index < mInputPorts.size()) ++ return jack_port_connected(mInputPorts[index]); ++ else ++ throw std::range_error("inport index out of range"); ++} ++ ++unsigned int JackCpp::AudioIO::numConnectionsOutPort(unsigned int index) ++ throw(std::range_error) ++{ ++ if(index < mOutputPorts.size()) ++ return jack_port_connected(mOutputPorts[index]); ++ else ++ throw std::range_error("outport index out of range"); ++} ++ ++unsigned int JackCpp::AudioIO::numPhysicalDestinationPorts(){ ++ const char **ports; ++ unsigned int cnt = 0; ++ ports = jack_get_ports (mJackClient, NULL, NULL, JackPortIsPhysical|JackPortIsInput); ++ if (ports != NULL){ ++ while(ports[cnt] != NULL) ++ cnt++; ++ free(ports); ++ return cnt; ++ } else ++ return 0; ++} ++ ++unsigned int JackCpp::AudioIO::numPhysicalSourcePorts(){ ++ const char **ports; ++ unsigned int cnt = 0; ++ //XXX is this really correct? we should get the naming right... ++ ports = jack_get_ports (mJackClient, NULL, NULL, JackPortIsPhysical|JackPortIsOutput); ++ if (ports != NULL){ ++ while(ports[cnt] != NULL) ++ cnt++; ++ free(ports); ++ return cnt; ++ } else ++ return 0; ++} ++ ++std::string JackCpp::AudioIO::getInputPortName(unsigned int index) ++ throw(std::range_error) ++{ ++ if(index < mInputPorts.size()) ++ return std::string(jack_port_name(mInputPorts[index])); ++ else ++ throw std::range_error("inport index out of range"); ++ ++} ++std::string JackCpp::AudioIO::getOutputPortName(unsigned int index) ++ throw(std::range_error) ++{ ++ if(index < mOutputPorts.size()) ++ return std::string(jack_port_name(mOutputPorts[index])); ++ else ++ throw std::range_error("outport index out of range"); ++} ++ ++void JackCpp::AudioIO::start() ++ throw(std::runtime_error) ++{ ++ //update these so that the callback can use them ++ if(mJackState != active){ ++ mNumOutputPorts = mOutputPorts.size(); ++ mNumInputPorts = mInputPorts.size(); ++ } ++ if (jack_activate(mJackClient) != 0) ++ throw std::runtime_error("cannot activate the client"); ++ mJackState = active; ++} ++ ++void JackCpp::AudioIO::stop() ++ throw(std::runtime_error) ++{ ++ if (jack_deactivate(mJackClient) != 0) ++ throw std::runtime_error("cannot deactivate the client"); ++ mJackState = notActive; ++} ++ ++void JackCpp::AudioIO::close() ++ throw(std::runtime_error) ++{ ++ if (jack_client_close(mJackClient) != 0) ++ throw std::runtime_error("cannot close the client"); ++ mJackState = closed; ++} ++ ++float JackCpp::AudioIO::getCpuLoad(){ ++ return jack_cpu_load(mJackClient); ++} ++ ++jack_nframes_t JackCpp::AudioIO::getSampleRate(){ ++ return jack_get_sample_rate(mJackClient); ++} ++ ++jack_nframes_t JackCpp::AudioIO::getBufferSize(){ ++ return jack_get_buffer_size(mJackClient); ++} ++ +Index: xbmc/cores/AudioRenderers/jackaudioio.hpp +=================================================================== +--- xbmc/cores/AudioRenderers/jackaudioio.hpp (revision 0) ++++ xbmc/cores/AudioRenderers/jackaudioio.hpp (revision 1806) +@@ -0,0 +1,299 @@ ++//C++ Classes that wrap JACK ++//Copyright 2007 Alex Norman ++// ++//This file is part of JACKC++. ++// ++//JACKC++ is free software: you can redistribute it and/or modify ++//it under the terms of the GNU General Public License as published by ++//the Free Software Foundation, either version 3 of the License, or ++//(at your option) any later version. ++// ++//JACKC++ 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 General Public License for more details. ++// ++//You should have received a copy of the GNU General Public License ++//along with JACKC++. If not, see <http://www.gnu.org/licenses/>. ++ ++#ifndef JACK_AUDIO_IO_H ++#define JACK_AUDIO_IO_H ++ ++extern "C" { ++#include <jack/jack.h> ++#include <jack/types.h> ++#include <jack/ringbuffer.h> ++} ++#include <string> ++#include <vector> ++#include <stdexcept> ++#include "jackringbuffer.hpp" ++ ++namespace JackCpp { ++ ++/** ++@class AudioIO ++ ++@brief This is an abstract class that provides a jack callback method. ++ ++This abstract class wraps the Jack Audio IO functionality needed for basic ++audio input and output. In order to create your own jack client you simply ++make your own class that inherits from this class and overloads audioCallback. ++In that method you can get audio in from jack and write it out to jack. ++ ++@author Alex Norman ++ ++*/ ++ ++ class AudioIO { ++ public: ++ ///An enum indicating the state of our jack client ++ enum jack_state_t {notActive,active,closed}; ++ ///A typedef so so that we don't always have to write std::vector<jack_default_audio_sample_t *> ++ typedef std::vector<jack_default_audio_sample_t *> audioBufVector; ++ private: ++ //commands ++ enum cmd_t {add_in_port, add_out_port}; ++ RingBuffer<cmd_t> mCmdBuffer; ++ /* the client */ ++ jack_client_t *mJackClient; ++ // an vector of i/o ports ++ std::vector<jack_port_t *> mOutputPorts; ++ std::vector<jack_port_t *> mInputPorts; ++ ++ //these are only accessed by the callback [once it is activated] ++ //they will usually be equal mOutputPorts.size() etc, except when ++ //a new port is added, before the callback ++ unsigned int mNumOutputPorts; ++ unsigned int mNumInputPorts; ++ ++ //these items are used for grabbing data for the jack callback ++ audioBufVector mJackInBuf; ++ audioBufVector mJackOutBuf; ++ //this stores the state of this jack process [active,notActive,closed] ++ jack_state_t mJackState; ++ //this prepares the input/output buffers to be passed ++ //to the callback function that a user writes ++ //XXX should this be virtual? ++ inline int jackToClassAudioCallback(jack_nframes_t nframes); ++ std::vector<std::string> mPortNames; ++ protected: ++ /** ++ @brief The method that the user must overload in order to actually process jack data. ++ \param nframes the number frames to process ++ \param inBufs a vector of audio buffers ++ \param outBufs a vector of audio buffers ++ \return 0 on success, non zero on error, which will cause jack to remove the client from the process graph ++ */ ++ virtual int audioCallback(jack_nframes_t nframes, ++ audioBufVector inBufs, ++ audioBufVector outBufs) = 0; ++ ++ public: ++ /** ++ @brief The Constructor ++ \param name string indicating the name of the jack client to create ++ \param inPorts an unsigned integer indicating the number of default input ports to create ++ \param outPorts an unsigned integer indicating the number of default output ports to create ++ \param startServer a boolean indicating whether to start a jack server if one isn't already running ++ \sa audioCallback ++ */ ++ AudioIO(std::string name, ++ unsigned int inPorts = 0, ++ unsigned int outPorts = 2, ++#ifdef __APPLE__ ++ bool startServer = false) ++#else ++ bool startServer = true) ++#endif ++ throw(std::runtime_error); ++ ++ ///The Destructor ++ virtual ~AudioIO(); ++ ++ /** ++ @brief The callback that jack actually gets [static]. ++ ++ This is a static method that the C jack callback calls, users ++ should not need to override this method. ++ ++ \param nframes the number frames to process ++ \param arg a pointer to our AudioIO object ++ \return the actual number of frames processed ++ \sa audioCallback ++ */ ++ static int jackProcessCallback(jack_nframes_t nframes, void *arg); ++ ++ ///See if a port with the name "name" exists for our client ++ bool portExists(std::string name); ++ ++ /** ++ @brief Reserve output ports ++ ++ This method must be called before the client is started. This ++ reserves a number of ports so that they can be safely created while ++ the client is running. The number indicates the maximum number of ports ++ that can be created while the client is running. This number includes ++ those ports that a have already been created, so if you've already created ++ x ports and you reserve y ports total, you can only create y - x ports while ++ the client is running. ++ \param num an integer indicating the number of output ports to reserve ++ */ ++ virtual void reserveOutPorts(unsigned int num) ++ throw(std::runtime_error); ++ /** ++ @brief Reserve input ports ++ ++ This method must be called before the client is started. This ++ reserves a number of ports so that they can be safely created while ++ the client is running. The number indicates the maximum number of ports ++ that can be created while the client is running. This number includes ++ those ports that a have already been created, so if you've already created ++ x ports and you reserve y ports total, you can only create y - x ports while ++ the client is running. ++ \param num an integer indicating the number of input ports to reserve ++ */ ++ virtual void reserveInPorts(unsigned int num) ++ throw(std::runtime_error); ++ ++ ///Start the jack client. ++ void start() ++ throw(std::runtime_error); ++ ///Stop the jack client. ++ void stop() ++ throw(std::runtime_error); ++ ///Close the jack client. ++ void close() ++ throw(std::runtime_error); ++ ++ ///Get the number of jack input ports ++ unsigned int inPorts(); ++ ///Get the number of jack output ports ++ unsigned int outPorts(); ++ ++ /** ++ @brief Add a jack input port to our client ++ \param name string the name of the port to add ++ \return the number of total input ports ++ */ ++ virtual unsigned int addInPort(std::string name) ++ throw(std::runtime_error); ++ /** ++ @brief Add a jack output port to our client ++ \param name string the name of the port to add ++ \return the number of total output ports ++ */ ++ virtual unsigned int addOutPort(std::string name) ++ throw(std::runtime_error); ++ ++ /** ++ @brief Connect our output to a jack client's source port. ++ \param index the index of our output port to connect from. ++ \param sourcePortName the client:port name to connect to ++ */ ++ void connectTo(unsigned int index, std::string sourcePortName) ++ throw(std::range_error, std::runtime_error); ++ /** ++ @brief Connect our input to a jack client's destination port. ++ \param index the index of our input port to connect to ++ \param destPortName the client:port name to connect from ++ */ ++ void connectFrom(unsigned int index, std::string destPortName) ++ throw(std::range_error, std::runtime_error); ++ /** ++ @brief Connect our output port to a physical output port ++ \param index the index of our output port to connect from ++ \param physical_index the physical output port index to connect to ++ */ ++ void connectToPhysical(unsigned int index, unsigned physical_index) ++ throw(std::range_error, std::runtime_error); ++ /** ++ @brief Connect our input port to a physical input port ++ \param index the index of our input port to connect to ++ \param physical_index the physical input port index to connect from ++ */ ++ void connectFromPhysical(unsigned int index, unsigned physical_index) ++ throw(std::range_error, std::runtime_error); ++ ///Disconnect input port from all connections ++ void disconnectInPort(unsigned int index) ++ throw(std::range_error, std::runtime_error); ++ ///Disconnect output port from all connections ++ void disconnectOutPort(unsigned int index) ++ throw(std::range_error, std::runtime_error); ++ ++ ///Get the number of connections to our input port ++ unsigned int numConnectionsInPort(unsigned int index) ++ throw(std::range_error); ++ ///Get the number of connections to our output port ++ unsigned int numConnectionsOutPort(unsigned int index) ++ throw(std::range_error); ++ ++ /** ++ @brief Get the number of physical audio input ports ++ These are ports that can send audio to your client ++ */ ++ unsigned int numPhysicalSourcePorts(); ++ /** ++ @brief Get the number of physical audio output ports ++ These are ports that your client can send audio to ++ */ ++ unsigned int numPhysicalDestinationPorts(); ++ ++ ///Get the name of our client's input port ++ std::string getInputPortName(unsigned int index) ++ throw(std::range_error); ++ ///Get the name of our client's output port ++ std::string getOutputPortName(unsigned int index) ++ throw(std::range_error); ++ ++ /** ++ @brief This method is called when Jack shuts down. ++ Override if you want to do something when jack shuts down. ++ */ ++ virtual void jackShutdownCallback(); ++ /** ++ @brief The current CPU load estimated by JACK ++ ++ This is a running average of the time it takes to execute a full ++ process cycle for all clients as a percentage of the real time ++ available per cycle determined by the buffer size and sample rate. ++ */ ++ float getCpuLoad(); ++ ///Get the sample rate ++ jack_nframes_t getSampleRate(); ++ ///Get the jack buffer size ++ jack_nframes_t getBufferSize(); ++ ///Check to see if the client is running in real time mode ++ bool isRealTime(){return jack_is_realtime(mJackClient);} ++ /** ++ @brief Get the name of our client ++ ++ This might not be exactly the same as the name we provided to the ++ constructor ++ ++ \return a string indicating the name of our client. ++ */ ++ std::string getName(){return std::string(jack_get_client_name(mJackClient));} ++ ///Get the state of our Jack client. ++ jack_state_t getState(){return mJackState;} ++ ++ /** ++ @brief Get an estimate of the current time in frames ++ ++ This is a running counter, no significance should be attached to ++ its value, but it can be compared to a previously returned value. ++ \return an estimate of the current time in frames. ++ */ ++ jack_nframes_t getFrameTime(){return jack_frame_time(mJackClient);} ++ ++ /** ++ @brief Get the time in frames since the JACK server began the current process cycle ++ ++ \return the time in frames that has passed since the JACK server began the current process cycle ++ */ ++ jack_nframes_t getFramesSinceCycleStart(){return jack_frames_since_cycle_start(mJackClient);} ++ }; ++ ++} ++ ++#endif +Index: xbmc/cores/AudioRenderers/jackringbuffer.hpp +=================================================================== +--- xbmc/cores/AudioRenderers/jackringbuffer.hpp (revision 0) ++++ xbmc/cores/AudioRenderers/jackringbuffer.hpp (revision 1806) +@@ -0,0 +1,205 @@ ++//C++ Classes that wrap JACK ++//Copyright 2007 Alex Norman ++// ++//This file is part of JACKC++. ++// ++//JACKC++ is free software: you can redistribute it and/or modify ++//it under the terms of the GNU General Public License as published by ++//the Free Software Foundation, either version 3 of the License, or ++//(at your option) any later version. ++// ++//JACKC++ 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 General Public License for more details. ++// ++//You should have received a copy of the GNU General Public License ++//along with JACKC++. If not, see <http://www.gnu.org/licenses/>. ++ ++#ifndef JACK_RING_BUFFER_CLASS_H ++#define JACK_RING_BUFFER_CLASS_H ++ ++#ifndef NULL ++#define NULL 0 ++#endif ++ ++extern "C" { ++#include <jack/ringbuffer.h> ++} ++#include <string.h> ++ ++namespace JackCpp { ++ ++template<typename Type> ++ ++/** ++@class RingBuffer ++ ++@brief This template class wraps the Jack lock-free ringbuffer. ++ ++This class creates a lock-free ring buffer that accepts a specific data type. ++ ++The key attribute of a ringbuffer is that it can be safely accessed by two ++threads simultaneously -- one reading from the buffer and the other writing to ++it -- without using any synchronization or mutual exclusion primitives. For ++this to work correctly, there can only be a single reader and a single writer ++thread. Their identities cannot be interchanged. ++ ++@author Alex Norman ++ ++*/ ++ class RingBuffer { ++ private: ++ jack_ringbuffer_t *mRingBufferPtr; ++ size_t mLength; ++ public: ++ /** ++ @brief The Constructor ++ \param size the number of items that the ring buffer should be able to hold ++ \param mlock a boolean indicating whether or not the ring buffer should be locked in memory ++ */ ++ RingBuffer(size_t size, bool mlock = false){ ++ mLength = size; ++ mRingBufferPtr = jack_ringbuffer_create(mLength * sizeof(Type)); ++ ++ //should we lock the memory for the ring buffer? ++ if(mlock) ++ jack_ringbuffer_mlock(mRingBufferPtr); ++ } ++ ///The Destructor ++ ~RingBuffer(){ ++ if(mRingBufferPtr != NULL) ++ jack_ringbuffer_free(mRingBufferPtr); ++ } ++ ++ ///Get the total length of the ring buffer ++ size_t length(){ ++ return mLength; ++ } ++ ++ ///Get the number of items that can be read at this time ++ size_t getReadSpace(){ ++ return jack_ringbuffer_read_space(mRingBufferPtr) / sizeof(Type); ++ } ++ ++ ///Get the number of items that can be written at this time ++ size_t getWriteSpace(){ ++ return jack_ringbuffer_write_space(mRingBufferPtr) / sizeof(Type); ++ } ++ ++ /** ++ @brief Read into dest ++ ++ Read from the buffer into a variable. ++ ++ \param dest an item to be read into ++ */ ++ void read(Type &dest){ ++ if(getReadSpace() <= 0){ ++ //throw error!!!! ++ return; ++ } ++ jack_ringbuffer_read(mRingBufferPtr, (char *)&dest, sizeof(Type)); ++ } ++ ++ /** ++ @brief Read into an array ++ ++ Read from the buffer into an array. ++ ++ \param dest an array to be read into ++ \param cnt the number of elements to read into this array ++ */ ++ void read(Type *dest, unsigned cnt){ ++ jack_ringbuffer_data_t readVec[2]; ++ unsigned int read_size = sizeof(Type) * cnt; ++ if(getReadSpace() <= 0){ ++ //throw error!!!! ++ return; ++ } ++ ++ //get the readvector ++ jack_ringbuffer_get_read_vector(mRingBufferPtr, readVec); ++ ++ //if the first vector has enough data then just read from there ++ if(readVec[0].len >= read_size){ ++ memcpy(dest, readVec[0].buf, read_size); ++ } else { ++ //if the first vector is zero length then read from the second ++ if(readVec[0].len == 0){ ++ memcpy(dest, readVec[1].buf, read_size); ++ } else { ++ //this gets tricky ++ char * byterep = (char *)dest; ++ //first read the data out of the first vector ++ memcpy(byterep, readVec[0].buf, readVec[0].len); ++ //then read the rest out of the second ++ memcpy(byterep + readVec[0].len, readVec[1].buf, read_size - readVec[0].len); ++ } ++ } ++ //advance the read pointer ++ jack_ringbuffer_read_advance(mRingBufferPtr, read_size); ++ } ++ ++ /** ++ @brief Write into the ring buffer. ++ ++ \param src the value to write ++ */ ++ void write(Type src){ ++ if(getWriteSpace() <= 0){ ++ //throw error!!!! ++ return; ++ } ++ jack_ringbuffer_write(mRingBufferPtr, (char *)&src, sizeof(Type)); ++ } ++ ++ /** ++ @brief Write an array of values into the ring buffer. ++ ++ \param src an array of values to write ++ \param cnt the number of items from the array to write into our buffer ++ */ ++ void write(Type *src, unsigned int cnt){ ++ jack_ringbuffer_data_t writeVec[2]; ++ unsigned int write_size = sizeof(Type) * cnt; ++ if(cnt > getWriteSpace()){ ++ //throw error!!!! ++ return; ++ } ++ ++ //get the write vector ++ jack_ringbuffer_get_write_vector(mRingBufferPtr, writeVec); ++ //if there is enough room in the first vector then just write there ++ if(writeVec[0].len >= write_size){ ++ memcpy(writeVec[0].buf,src,write_size); ++ } else { ++ //if there is no room in the first vector then write into the second ++ if(writeVec[0].len == 0){ ++ memcpy(writeVec[1].buf,src,write_size); ++ } else { ++ //this is more tricky, we have to split the data up ++ char * byterep = (char *)src; ++ //copy the first chunck ++ memcpy(writeVec[0].buf, byterep, writeVec[0].len); ++ //copy the second chunck ++ memcpy(writeVec[1].buf, byterep + writeVec[0].len, write_size - writeVec[0].len); ++ } ++ } ++ jack_ringbuffer_write_advance(mRingBufferPtr, write_size); ++ } ++ ++ /** ++ @brief Reset ++ ++ This is not threadsafe. This resets the read and write pointers, ++ effectively making the ring buffer empty. ++ */ ++ void reset(){ ++ jack_ringbuffer_reset(mRingBufferPtr); ++ } ++ }; ++ ++} ++ ++#endif +Index: xbmc/cores/AudioRenderers/jackblockingaudioio.cpp +=================================================================== +--- xbmc/cores/AudioRenderers/jackblockingaudioio.cpp (revision 0) ++++ xbmc/cores/AudioRenderers/jackblockingaudioio.cpp (revision 1806) +@@ -0,0 +1,181 @@ ++//C++ Classes that wrap JACK ++//Copyright 2007 Alex Norman ++// ++//This file is part of JACKC++. ++// ++//JACKC++ is free software: you can redistribute it and/or modify ++//it under the terms of the GNU General Public License as published by ++//the Free Software Foundation, either version 3 of the License, or ++//(at your option) any later version. ++// ++//JACKC++ 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 General Public License for more details. ++// ++//You should have received a copy of the GNU General Public License ++//along with JACKC++. If not, see <http://www.gnu.org/licenses/>. ++ ++#include "jackblockingaudioio.hpp" ++#include <unistd.h> ++#define MIN(x,y) ((x) < (y) ? (x) : (y)) ++ ++#if 0 ++#include <iostream> ++using std::cerr; ++using std::cout; ++using std::endl; ++#endif ++ ++//allocate input and output buffers ++JackCpp::BlockingAudioIO::BlockingAudioIO(std::string name, ++ unsigned int inChans, unsigned int outChans, ++ unsigned int inBufSize, unsigned int outBufSize, ++ bool startServer) throw(std::runtime_error): ++ AudioIO(name, inChans, outChans, startServer), ++ mOutputBufferMaxSize((unsigned int)getSampleRate()), ++ mInputBufferMaxSize((unsigned int)getSampleRate()) ++{ ++ if(inBufSize < 2 * getBufferSize()) ++ inBufSize = 2 * getBufferSize(); ++ else if (inBufSize > mInputBufferMaxSize) ++ inBufSize = mInputBufferMaxSize; ++ if(outBufSize < 2 * getBufferSize()) ++ outBufSize = 2 * getBufferSize(); ++ else if (outBufSize > mOutputBufferMaxSize) ++ outBufSize = mOutputBufferMaxSize; ++ ++ //set the amount of the ring buffer that we leave free ++ mOutputBufferFreeSize = mOutputBufferMaxSize - outBufSize; ++ mInputBufferFreeSize = mInputBufferMaxSize - inBufSize; ++ ++ //create input and output buffers, give them extra space to work with and memory lock them ++ for(unsigned int i = 0; i < outChans; i++) ++ mUserOutBuff.push_back(new RingBuffer<jack_default_audio_sample_t>(mOutputBufferMaxSize, true)); ++ for(unsigned int i = 0; i < inChans; i++) ++ mUserInBuff.push_back(new RingBuffer<jack_default_audio_sample_t>(mInputBufferMaxSize, true)); ++} ++ ++//clean up the buffers we allocated ++JackCpp::BlockingAudioIO::~BlockingAudioIO(){ ++ stop(); ++ for(std::vector<RingBuffer<jack_default_audio_sample_t> *>::iterator it = mUserOutBuff.begin(); ++ it != mUserOutBuff.end(); it++) ++ delete *it; ++ for(std::vector<RingBuffer<jack_default_audio_sample_t> *>::iterator it = mUserInBuff.begin(); ++ it != mUserInBuff.end(); it++) ++ delete *it; ++} ++ ++//wait until we can write, then write ++void JackCpp::BlockingAudioIO::write(unsigned int channel, jack_default_audio_sample_t val){ ++ if (channel >= outPorts()) ++ return; ++ while(mUserOutBuff[channel]->getWriteSpace() <= mOutputBufferFreeSize) ++ usleep(10); ++ mUserOutBuff[channel]->write(val); ++} ++ ++//we we can write then write, otherwise return false ++bool JackCpp::BlockingAudioIO::tryWrite(unsigned int channel, jack_default_audio_sample_t val){ ++ if (channel < outPorts() && mUserOutBuff[channel]->getWriteSpace() > mOutputBufferFreeSize){ ++ mUserOutBuff[channel]->write(val); ++ return true; ++ } ++ return false; ++} ++ ++//wait until we can read, then return the value ++jack_default_audio_sample_t JackCpp::BlockingAudioIO::read(unsigned int channel){ ++ jack_default_audio_sample_t val; ++ if (channel >= inPorts()) ++ return 0; ++ while(mUserInBuff[channel]->getReadSpace() == 0) ++ usleep(10); ++ mUserInBuff[channel]->read(val); ++ return val; ++} ++ ++//if we cannot read then return false, otherwise, read and return true ++bool JackCpp::BlockingAudioIO::tryRead(unsigned int channel, jack_default_audio_sample_t &val){ ++ if (channel >= inPorts() || mUserInBuff[channel]->getReadSpace() == 0) ++ return false; ++ mUserInBuff[channel]->read(val); ++ return true; ++} ++ ++void JackCpp::BlockingAudioIO::reserveOutPorts(unsigned int num) ++ throw(std::runtime_error) ++{ ++ AudioIO::reserveOutPorts(num); ++ mUserOutBuff.reserve(num); ++} ++ ++void JackCpp::BlockingAudioIO::reserveInPorts(unsigned int num) ++ throw(std::runtime_error) ++{ ++ AudioIO::reserveInPorts(num); ++ mUserInBuff.reserve(num); ++} ++ ++unsigned int JackCpp::BlockingAudioIO::addInPort(std::string name) ++ throw(std::runtime_error) ++{ ++ unsigned int ret; ++ if(getState() == AudioIO::active) ++ throw std::runtime_error("JackCpp::BlockingAudioIO::addInPort not allowed while the client is active"); ++ ret = AudioIO::addInPort(name); ++ mUserInBuff.push_back(new RingBuffer<jack_default_audio_sample_t>(mInputBufferMaxSize, true)); ++ return ret; ++} ++ ++unsigned int JackCpp::BlockingAudioIO::addOutPort(std::string name) ++ throw(std::runtime_error) ++{ ++ unsigned int ret; ++ if(getState() == AudioIO::active) ++ throw std::runtime_error("JackCpp::BlockingAudioIO::addOutPort not allowed while the client is active"); ++ ret = AudioIO::addOutPort(name); ++ mUserOutBuff.push_back(new RingBuffer<jack_default_audio_sample_t>(mOutputBufferMaxSize, true)); ++ return ret; ++} ++ ++//read the jack input buffers into the user input buffers ++//write the user output buffers into the jack output buffers ++int JackCpp::BlockingAudioIO::audioCallback(jack_nframes_t nframes, ++ audioBufVector inBufs, ++ audioBufVector outBufs){ ++ ++ //only try to write as much as we have space to write ++ unsigned int numToWrite = MIN(mUserOutBuff[0]->getReadSpace(), nframes); ++ unsigned int numToRead = MIN(mUserInBuff[0]->getWriteSpace(), nframes); ++ ++ //make sure we leave the amount of free space we require ++ if(mUserInBuff[0]->getWriteSpace() - numToRead < mInputBufferFreeSize) ++ numToRead = mUserInBuff[0]->getWriteSpace() - mInputBufferFreeSize; ++ ++ //if (numToWrite < nframes) ++ //cerr << "oops" << endl; ++ ++ //read get inputs ++ for(unsigned int i = 0; i < inPorts(); i++){ ++ for(unsigned int j = 0; j < numToRead; j++) ++ mUserInBuff[i]->write(inBufs[i][j]); ++ } ++ ++ //write output ++ for(unsigned int i = 0; i < outPorts(); i++){ ++ for(unsigned int j = 0; j < numToWrite; j++){ ++ jack_default_audio_sample_t val; ++ mUserOutBuff[i]->read(val); ++ outBufs[i][j] = val; ++ } ++ //write zeros for the rest ++ for(unsigned int j = numToWrite; j < nframes; j++) ++ outBufs[i][j] = 0.0; ++ //if(numToWrite < nframes) ++ //cerr << "oops" << endl; ++ } ++ return 0; ++} ++ |