ALSAAudioDevice.cxx
Go to the documentation of this file.00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
00156
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
00167 if (mSndpcm) {
00168 mSndpcm->Stop();
00169 }
00170 }
00171 ALSAAudioDevice::~ALSAAudioDevice()
00172 {
00173
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
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;
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;
00295 if (snd_ctl_card_info(handle, info) < 0) {
00296 snd_ctl_close(handle);
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