ALSAAudioDevice.cxx

Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG)
00003  *                         UNIVERSITAT POMPEU FABRA
00004  *
00005  *
00006  * This program is free software; you can redistribute it and/or modify
00007  * it under the terms of the GNU General Public License as published by
00008  * the Free Software Foundation; either version 2 of the License, or
00009  * (at your option) any later version.
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software
00018  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00019  *
00020  */
00021 
00022 #include "SndPcm.hxx"
00023 #include "AudioIO.hxx"
00024 #include "AudioIn.hxx"
00025 #include "AudioOut.hxx"
00026 #include "AudioDeviceList.hxx"
00027 #include "AudioDevice.hxx"
00028 #include <sstream>
00029 
00030 namespace CLAM {
00031 
00032         class ALSAAudioDevice: public AudioDevice
00033         {
00034         private:
00035                 int mNChannelsWritten;
00036                 bool mChannelsWritten[256];
00037 
00038                 int mNChannelsRead;
00039                 bool mChannelsRead[256];
00040                 
00041                 int mWriteBufSize;
00042                 Array<short> mWriteBuf;
00043 
00044                 int mReadBufSize;
00045                 Array<short> mReadBuf;
00046 
00047                 SndPcm* mSndpcm;
00048                 std::string mDevice;
00049 
00050                 int HighestChannelID();
00051 
00052         public:
00053                 ALSAAudioDevice(const std::string& name,const std::string& device);
00054                 ~ALSAAudioDevice();
00055                 
00056                 void Start(void) throw(Err);
00057                 void Stop(void) throw(Err);
00058                 void Read(Audio& audio,const int channelID);
00059                 void Write(const Audio& audio,const int channelID);
00060         };
00061 
00062         ALSAAudioDevice::ALSAAudioDevice(const std::string& name,const std::string& device): 
00063                 AudioDevice(name), 
00064                 mSndpcm(0)
00065         {
00066                 //printf("ALSAAudioDevice::ALSAAudioDevice\n");
00067 
00068                 int i;
00069                 
00070                 mNChannelsWritten = 0;
00071                 for (i=0;i<256;i++)
00072                         mChannelsWritten[i] = false;
00073 
00074                 mNChannelsRead = 0;
00075                 for (i=0;i<256;i++)
00076                         mChannelsRead[i] = false;
00077                         
00078                 mDevice = device;
00079         }
00080 
00081         int ALSAAudioDevice::HighestChannelID(void)
00082         {
00083                 int max_id = 0;
00084 
00085                 std::vector<AudioIn*>::const_iterator in_it;
00086                 for (in_it = mInputs.begin(); in_it != mInputs.end(); in_it++)
00087                 {
00088                         if ( (*in_it)->GetChannelID() > max_id)
00089                                 max_id = (*in_it)->GetChannelID();
00090                 }
00091 
00092                 std::vector<AudioOut*>::const_iterator out_it;
00093                 for (out_it = mOutputs.begin(); out_it != mOutputs.end(); out_it++)
00094                 {
00095                         if ( (*out_it)->GetChannelID() > max_id)
00096                                 max_id = (*out_it)->GetChannelID();
00097                 }
00098 
00099                 return max_id;
00100 
00101         }
00102 
00103         void ALSAAudioDevice::Start(void) throw(Err)
00104         {
00105                 int i;
00106                 bool needs_start = false;
00107 
00108                 mNReadChannels = mInputs.size();
00109                 mNWriteChannels = mOutputs.size();
00110                         
00111                 if (mForceNChannels)
00112                 {
00113                         int used_channels = HighestChannelID() + 1;
00114                         if (used_channels > mNChannels)
00115                                 throw Err("ALSAAudioDevice::Start(): more inputs or outputs than requested channels.");
00116                                 
00117                 }
00118                 else
00119                 {
00120                         if ( HighestChannelID() + 1 != mNChannels)
00121                         {
00122                                 if (mSndpcm) {
00123                                         mSndpcm->Stop();
00124                                         delete mSndpcm;
00125                                         mSndpcm = 0;
00126                                 }
00127                                 mNChannels = HighestChannelID() + 1;
00128                         }
00129                 }
00130 
00131                 if (mSndpcm==0)
00132                 {
00133                         try {
00134                                 mSndpcm = new ::SndPcm(SampleRate(),mNReadChannels,mNWriteChannels,Latency(),mDevice.c_str(),mDevice.c_str());
00135                         }
00136                         catch (SndPcmError &e) {
00137                                 Err ne("ALSAAudioDevice::Start(): Failed to create PCM device.");
00138                                 ne.Embed(e);
00139                                 throw(ne);
00140                         }
00141                         needs_start = true;
00142                 }
00143 
00144                 int bufSize = mSndpcm->latency * mNChannels;
00145                 mReadBuf.Resize(bufSize);
00146                 mWriteBuf.Resize(bufSize);
00147                 mReadBuf.SetSize(bufSize);
00148                 mWriteBuf.SetSize(bufSize);
00149 
00150                 for (i=0; i<bufSize; i++) {
00151                         mReadBuf[i] = 0;
00152                         mWriteBuf[i] = 0;
00153                 }
00154 
00155                 // the following settings will be set at the first Read/Write
00156                 // to the Audio buffer size that is passed.
00157                 mReadBufSize = 0; 
00158                 mWriteBufSize = 0;
00159 
00160                 if (needs_start)
00161                         mSndpcm->Start();
00162         }
00163 
00164         void ALSAAudioDevice::Stop(void) throw(Err)
00165         {
00166                 //printf("ALSAAudioDevice::Stop\n");
00167                 if (mSndpcm) {
00168                         mSndpcm->Stop();
00169                 }
00170         }
00171         ALSAAudioDevice::~ALSAAudioDevice()
00172         {
00173                 //printf("ALSAAudioDevice::~ALSAAudioDevice\n");
00174                 Stop();
00175                 if (mSndpcm) {
00176                         delete mSndpcm;
00177                 }
00178         }
00179 
00180         void ALSAAudioDevice::Read(Audio& audio,const int channelID)
00181         {
00182                 CLAM_DEBUG_ASSERT(channelID < mNChannels,
00183                                   "ALSAAudioDevice::Read(): Invalid Channel ID");
00184 
00185                 TData* ptrA = audio.GetBuffer().GetPtr();
00186                 short* ptrB = mReadBuf.GetPtr() + channelID;
00187                 int n;
00188 
00189                 if (mChannelsRead[channelID]) 
00190                         throw Err("ALSAAudioDevice::Read(): Tried to read "
00191                                   "twice from a channel in a single time frame!");
00192                 if (!mSndpcm)
00193                         throw Err("ALSAAudioDevice::Read(): Device not configured.");
00194                 
00195                 if (mReadBufSize==0) mReadBufSize=audio.GetSize();
00196                 else{
00197                         CLAM_ASSERT(mReadBufSize==audio.GetSize(),"ALSADevice: Inconsistent Audio size");
00198                 }
00199                 if (mReadBufSize>mSndpcm->latency) 
00200                         throw Err("You are trying to read audio in blocks bigger than the latency");
00201                 
00202                 if (mNChannelsRead == 0)
00203                 {
00204                         mSndpcm->Poll();
00205                         mSndpcm->ReadBuf(mReadBuf.GetPtr(),mReadBufSize);
00206                 }
00207 
00208                 n = mReadBufSize;
00209                 while (n--)
00210                 {
00211                         *ptrA++ = TData(*ptrB) / 32767.;
00212                         ptrB += mNChannels;
00213                 }
00214                 
00215                 mChannelsRead[channelID] = true;
00216                 mNChannelsRead++;
00217 
00218                 if (mNChannelsRead==mNReadChannels)
00219                 {
00220                         mNChannelsRead = 0;
00221                         for (int i=0;i<mNChannels;i++)
00222                                 mChannelsRead[i] = false;
00223                 }
00224         }
00225 
00226         void ALSAAudioDevice::Write(const Audio& audio,const int channelID)
00227         {
00228                 CLAM_DEBUG_ASSERT(channelID < mNChannels,
00229                                   "ALSAAudioDevice::Write(): Invalid Channel ID");
00230 
00231                 TData* ptrA = audio.GetBuffer().GetPtr();
00232                 short* ptrB = mWriteBuf.GetPtr() + channelID;
00233                 int i,n;
00234 
00235                 //printf("alsaauduiodevice write. audio size: %d mWriteBufSize: %d\n", audio.GetSize(), mWriteBufSize);
00236 
00237                 if (mWriteBufSize==0) mWriteBufSize=audio.GetSize();
00238                 else{
00239                         CLAM_ASSERT(mWriteBufSize==audio.GetSize(),"ALSADevice Write: Inconsistent Audio size");
00240                 }
00241                         
00242                 if (mWriteBufSize>mSndpcm->latency) 
00243                         throw Err("You are trying to write audio in blocks bigger than the latency");
00244 
00245 
00246                 if (mChannelsWritten[channelID])
00247                         throw Err("ALSAAudioDevice::Write(): Tried to write "
00248                                           "twice into a channel in a single time frame.");
00249                 if (!mSndpcm)
00250                         throw Err("ALSAAudioDevice::Write(): Device not configured.");
00251 
00252                 n = mWriteBufSize;
00253                 while (n--)
00254                 {
00255                         *ptrB = (short) (32767.*(*ptrA++));
00256                         ptrB += mNChannels;
00257                 }
00258 
00259                 mChannelsWritten[channelID] = true;
00260                 mNChannelsWritten++;
00261 
00262                 if (mNChannelsWritten==mNWriteChannels)
00263                 {
00264                         if (mNReadChannels==0) mSndpcm->Poll();
00265                         mSndpcm->WriteBuf(mWriteBuf.GetPtr(),mWriteBufSize);
00266                                                 
00267                         mNChannelsWritten = 0;
00268                         for (i=0;i<mNChannels;i++)
00269                                 mChannelsWritten[i] = false;
00270                 }
00271         }
00272 
00273 
00274         class ALSAAudioDeviceList: public AudioDeviceList
00275         {
00276                 static ALSAAudioDeviceList sDevices;
00277 
00278                 ALSAAudioDeviceList()
00279                         :AudioDeviceList(std::string("alsa"))
00280                 {
00281                         int card, dev;
00282                         snd_ctl_t *handle;
00283                         snd_ctl_card_info_t *info;
00284 
00285                         snd_ctl_card_info_alloca(&info);
00286                         card = -1;
00287                         if (snd_card_next(&card) < 0 || card < 0)
00288                                 return; // No cards found
00289                         while (card >= 0) {
00290                                 std::stringstream namestr;
00291                                 namestr << "hw:" << card;
00292                                 std::string name(namestr.str());
00293                                 if (snd_ctl_open(&handle, name.c_str(), 0) < 0)
00294                                         continue; // Card control open error!
00295                                 if (snd_ctl_card_info(handle, info) < 0) {
00296                                         snd_ctl_close(handle); // Card control read error!
00297                                         continue;
00298                                 }
00299                                 dev = -1;
00300                                 while (1) {
00301                                         snd_ctl_pcm_next_device(handle, &dev);
00302                                         if (dev < 0)
00303                                                 break;
00304                                         std::stringstream dnamestr;
00305                                         dnamestr << name << "," << dev;
00306                                         std::string dname(dnamestr.str());
00307                                         mAvailableDevices.push_back(dname.c_str());
00308 
00309                                         std::stringstream plug;
00310                                         plug << "plug" << dname;
00311                                         mAvailableDevices.push_back(plug.str());
00312                                 }
00313                                 snd_ctl_close(handle);
00314                                 if (snd_card_next(&card) < 0)
00315                                         break;
00316                         }
00317                         
00318                         AddMe();
00319                 }
00320         public:
00321 
00322                 std::string DefaultDevice(void)
00323                 {
00324                         return "plughw:0,0";
00325                 }
00326 
00327                 AudioDevice* Create(
00328                         const std::string& name,const std::string& device)
00329                 {
00330                         return new ALSAAudioDevice(name,device);
00331                 }
00332         };
00333 
00334         ALSAAudioDeviceList ALSAAudioDeviceList::sDevices;
00335 }
00336 
Generated by  doxygen 1.6.3