CLAM-Development
1.1
|
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