CLAM-Development  1.4.0
ALSAAudioDevice.cxx
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2001-2004 MUSIC TECHNOLOGY GROUP (MTG)
3  * UNIVERSITAT POMPEU FABRA
4  *
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  *
20  */
21 
22 #include "SndPcm.hxx"
23 #include "AudioIO.hxx"
24 #include "AudioIn.hxx"
25 #include "AudioOut.hxx"
26 #include "AudioDeviceList.hxx"
27 #include "AudioDevice.hxx"
28 #include <sstream>
29 
30 namespace CLAM {
31 
32  class ALSAAudioDevice: public AudioDevice
33  {
34  private:
35  int mNChannelsWritten;
36  bool mChannelsWritten[256];
37 
38  int mNChannelsRead;
39  bool mChannelsRead[256];
40 
41  int mWriteBufSize;
42  Array<short> mWriteBuf;
43 
44  int mReadBufSize;
45  Array<short> mReadBuf;
46 
47  SndPcm* mSndpcm;
48  std::string mDevice;
49 
50  int HighestChannelID();
51 
52  public:
53  ALSAAudioDevice(const std::string& name,const std::string& device);
54  ~ALSAAudioDevice();
55 
56  void Start(void) throw(Err);
57  void Stop(void) throw(Err);
58  void Read(Audio& audio,const int channelID);
59  void Write(const Audio& audio,const int channelID);
60  };
61 
62  ALSAAudioDevice::ALSAAudioDevice(const std::string& name,const std::string& device):
63  AudioDevice(name),
64  mSndpcm(0)
65  {
66  //printf("ALSAAudioDevice::ALSAAudioDevice\n");
67 
68  int i;
69 
70  mNChannelsWritten = 0;
71  for (i=0;i<256;i++)
72  mChannelsWritten[i] = false;
73 
74  mNChannelsRead = 0;
75  for (i=0;i<256;i++)
76  mChannelsRead[i] = false;
77 
78  mDevice = device;
79  }
80 
81  int ALSAAudioDevice::HighestChannelID(void)
82  {
83  int max_id = 0;
84 
85  std::vector<AudioIn*>::const_iterator in_it;
86  for (in_it = mInputs.begin(); in_it != mInputs.end(); in_it++)
87  {
88  if ( (*in_it)->GetChannelID() > max_id)
89  max_id = (*in_it)->GetChannelID();
90  }
91 
92  std::vector<AudioOut*>::const_iterator out_it;
93  for (out_it = mOutputs.begin(); out_it != mOutputs.end(); out_it++)
94  {
95  if ( (*out_it)->GetChannelID() > max_id)
96  max_id = (*out_it)->GetChannelID();
97  }
98 
99  return max_id;
100 
101  }
102 
103  void ALSAAudioDevice::Start(void) throw(Err)
104  {
105  int i;
106  bool needs_start = false;
107 
108  mNReadChannels = mInputs.size();
109  mNWriteChannels = mOutputs.size();
110 
111  if (mForceNChannels)
112  {
113  int used_channels = HighestChannelID() + 1;
114  if (used_channels > mNChannels)
115  throw Err("ALSAAudioDevice::Start(): more inputs or outputs than requested channels.");
116 
117  }
118  else
119  {
120  if ( HighestChannelID() + 1 != mNChannels)
121  {
122  if (mSndpcm) {
123  mSndpcm->Stop();
124  delete mSndpcm;
125  mSndpcm = 0;
126  }
127  mNChannels = HighestChannelID() + 1;
128  }
129  }
130 
131  if (mSndpcm==0)
132  {
133  try {
134  mSndpcm = new ::SndPcm(SampleRate(),mNReadChannels,mNWriteChannels,Latency(),mDevice.c_str(),mDevice.c_str());
135  }
136  catch (SndPcmError &e) {
137  Err ne("ALSAAudioDevice::Start(): Failed to create PCM device.");
138  ne.Embed(e);
139  throw(ne);
140  }
141  needs_start = true;
142  }
143 
144  int bufSize = mSndpcm->latency * mNChannels;
145  mReadBuf.Resize(bufSize);
146  mWriteBuf.Resize(bufSize);
147  mReadBuf.SetSize(bufSize);
148  mWriteBuf.SetSize(bufSize);
149 
150  for (i=0; i<bufSize; i++) {
151  mReadBuf[i] = 0;
152  mWriteBuf[i] = 0;
153  }
154 
155  // the following settings will be set at the first Read/Write
156  // to the Audio buffer size that is passed.
157  mReadBufSize = 0;
158  mWriteBufSize = 0;
159 
160  if (needs_start)
161  mSndpcm->Start();
162  }
163 
164  void ALSAAudioDevice::Stop(void) throw(Err)
165  {
166  //printf("ALSAAudioDevice::Stop\n");
167  if (mSndpcm) {
168  mSndpcm->Stop();
169  }
170  }
171  ALSAAudioDevice::~ALSAAudioDevice()
172  {
173  //printf("ALSAAudioDevice::~ALSAAudioDevice\n");
174  Stop();
175  if (mSndpcm) {
176  delete mSndpcm;
177  }
178  }
179 
180  void ALSAAudioDevice::Read(Audio& audio,const int channelID)
181  {
182  CLAM_DEBUG_ASSERT(channelID < mNChannels,
183  "ALSAAudioDevice::Read(): Invalid Channel ID");
184 
185  TData* ptrA = audio.GetBuffer().GetPtr();
186  short* ptrB = mReadBuf.GetPtr() + channelID;
187  int n;
188 
189  if (mChannelsRead[channelID])
190  throw Err("ALSAAudioDevice::Read(): Tried to read "
191  "twice from a channel in a single time frame!");
192  if (!mSndpcm)
193  throw Err("ALSAAudioDevice::Read(): Device not configured.");
194 
195  if (mReadBufSize==0) mReadBufSize=audio.GetSize();
196  else{
197  CLAM_ASSERT(mReadBufSize==audio.GetSize(),"ALSADevice: Inconsistent Audio size");
198  }
199  if (mReadBufSize>mSndpcm->latency)
200  throw Err("You are trying to read audio in blocks bigger than the latency");
201 
202  if (mNChannelsRead == 0)
203  {
204  mSndpcm->Poll();
205  mSndpcm->ReadBuf(mReadBuf.GetPtr(),mReadBufSize);
206  }
207 
208  n = mReadBufSize;
209  while (n--)
210  {
211  *ptrA++ = TData(*ptrB) / 32767.;
212  ptrB += mNChannels;
213  }
214 
215  mChannelsRead[channelID] = true;
216  mNChannelsRead++;
217 
218  if (mNChannelsRead==mNReadChannels)
219  {
220  mNChannelsRead = 0;
221  for (int i=0;i<mNChannels;i++)
222  mChannelsRead[i] = false;
223  }
224  }
225 
226  void ALSAAudioDevice::Write(const Audio& audio,const int channelID)
227  {
228  CLAM_DEBUG_ASSERT(channelID < mNChannels,
229  "ALSAAudioDevice::Write(): Invalid Channel ID");
230 
231  TData* ptrA = audio.GetBuffer().GetPtr();
232  short* ptrB = mWriteBuf.GetPtr() + channelID;
233  int i,n;
234 
235  //printf("alsaauduiodevice write. audio size: %d mWriteBufSize: %d\n", audio.GetSize(), mWriteBufSize);
236 
237  if (mWriteBufSize==0) mWriteBufSize=audio.GetSize();
238  else{
239  CLAM_ASSERT(mWriteBufSize==audio.GetSize(),"ALSADevice Write: Inconsistent Audio size");
240  }
241 
242  if (mWriteBufSize>mSndpcm->latency)
243  throw Err("You are trying to write audio in blocks bigger than the latency");
244 
245 
246  if (mChannelsWritten[channelID])
247  throw Err("ALSAAudioDevice::Write(): Tried to write "
248  "twice into a channel in a single time frame.");
249  if (!mSndpcm)
250  throw Err("ALSAAudioDevice::Write(): Device not configured.");
251 
252  n = mWriteBufSize;
253  while (n--)
254  {
255  *ptrB = (short) (32767.*(*ptrA++));
256  ptrB += mNChannels;
257  }
258 
259  mChannelsWritten[channelID] = true;
260  mNChannelsWritten++;
261 
262  if (mNChannelsWritten==mNWriteChannels)
263  {
264  if (mNReadChannels==0) mSndpcm->Poll();
265  mSndpcm->WriteBuf(mWriteBuf.GetPtr(),mWriteBufSize);
266 
267  mNChannelsWritten = 0;
268  for (i=0;i<mNChannels;i++)
269  mChannelsWritten[i] = false;
270  }
271  }
272 
273 
274  class ALSAAudioDeviceList: public AudioDeviceList
275  {
276  static ALSAAudioDeviceList sDevices;
277 
278  ALSAAudioDeviceList()
279  :AudioDeviceList(std::string("alsa"))
280  {
281  int card, dev;
282  snd_ctl_t *handle;
283  snd_ctl_card_info_t *info;
284 
285  snd_ctl_card_info_alloca(&info);
286  card = -1;
287  if (snd_card_next(&card) < 0 || card < 0)
288  return; // No cards found
289  while (card >= 0) {
290  std::stringstream namestr;
291  namestr << "hw:" << card;
292  std::string name(namestr.str());
293  if (snd_ctl_open(&handle, name.c_str(), 0) < 0)
294  continue; // Card control open error!
295  if (snd_ctl_card_info(handle, info) < 0) {
296  snd_ctl_close(handle); // Card control read error!
297  continue;
298  }
299  dev = -1;
300  while (1) {
301  snd_ctl_pcm_next_device(handle, &dev);
302  if (dev < 0)
303  break;
304  std::stringstream dnamestr;
305  dnamestr << name << "," << dev;
306  std::string dname(dnamestr.str());
307  mAvailableDevices.push_back(dname.c_str());
308 
309  std::stringstream plug;
310  plug << "plug" << dname;
311  mAvailableDevices.push_back(plug.str());
312  }
313  snd_ctl_close(handle);
314  if (snd_card_next(&card) < 0)
315  break;
316  }
317 
318  AddMe();
319  }
320  public:
321 
322  std::string DefaultDevice(void)
323  {
324  return "plughw:0,0";
325  }
326 
327  AudioDevice* Create(
328  const std::string& name,const std::string& device)
329  {
330  return new ALSAAudioDevice(name,device);
331  }
332  };
333 
334  ALSAAudioDeviceList ALSAAudioDeviceList::sDevices;
335 }
336