File: AudioDevice.cpp

package info (click to toggle)
camstream 0.27%2Bdfsg-4
  • links: PTS
  • area: main
  • in suites: squeeze
  • size: 12,368 kB
  • ctags: 5,393
  • sloc: cpp: 17,031; sh: 8,154; asm: 455; ansic: 440; makefile: 343
file content (297 lines) | stat: -rw-r--r-- 7,930 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
/*  audiodevs: Abstraction layer for audio hardware & samples
    Copyright (C) 2003-2004 Nemosoft Unv.

    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 of the License, 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    For questions, remarks, patches, etc. for this program, the author can be
    reached at camstream@smcc.demon.nl.
*/


/**
  \class CAudioDevice

  \brief Base class for audio capturing/playback from and to an audio card

  This class is meant as a basis for OS/hardware specific audio card 'drivers'.
  Examples are ALSA & OSS for Linux, or Window's support for audio devices.

  \par General design

  ... AudioReader/Writer ...
  ... image ...

  \par How to implement

  In order to make this class useful, you must subclass it and implement
  8 pure virtual functions. 6 Of them are directly related to the sound supprt;
  they are \ref Init(), \ref Exit(),
  \ref StartCapture(), \ref StopCapture(), \ref StartPlayback() and
  \ref StopPlayback().

  \par
  2 Other functions are \ref GetPlaybackPointer() and \ref ShowMixerControls().
  They are described below.

  \par
  Finally, since CAudioDevice is derived from QThread, the subclass must
  implement the run() function; this should be written as a tight loop
  where reading and writing to the actual device handle is done.


  ...timeline diagram


*/


/**

*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "AudioDevice.h"

CAudioDevice::CAudioDevice()
	: m_CaptureBuffer(1000000), m_PlaybackBuffer(1000000)
{
   qDebug("CAudioDevice::CAudioDevice()");
   m_Validated = false;
   m_OpenCount = 0;
   m_CaptureCount = m_PlaybackCount = 0;
   m_CurrentSoundAttr.SetPreset(SoundAttributes::Speech);

   connect(&m_CaptureBuffer,  SIGNAL(ReaderAttached()), this, SLOT(EnableCapture()));
   connect(&m_CaptureBuffer,  SIGNAL(ReaderDetached()), this, SLOT(DisableCapture()));
   connect(&m_PlaybackBuffer, SIGNAL(WriterAttached()), this, SLOT(EnablePlayback()));
   connect(&m_PlaybackBuffer, SIGNAL(WriterDetached()), this, SLOT(DisablePlayback()));
}

CAudioDevice::~CAudioDevice()
{
   qDebug("CAudioDevice::~CAudioDevice()");
}



// public

QString CAudioDevice::GetName() const
{
   return m_ShortName;
}

QString CAudioDevice::GetLongName() const
{
   return m_LongName;
}

QString CAudioDevice::GetNodeName() const
{
   return m_NodeName;
}

bool CAudioDevice::IsValid() const
{
   return m_Validated;
}

bool CAudioDevice::Open(Mode m)
{
   if (m_OpenCount++ > 0)
     return true;
   m_Mode = m;
   if (!Init())
     return false;
   emit Opened();
   return true;
}

void CAudioDevice::Close()
{
   if (m_OpenCount == 0) {
     qWarning("CAudioDevice::Close() Count already at 0.");
     return;
   }

   m_OpenCount--;
   if (m_OpenCount == 0) {
     if (m_CaptureCount > 0) {
       StopCapture();
       m_CaptureCount = 0;
     }
     if (m_PlaybackCount > 0) {
       StopPlayback();
       m_PlaybackCount = 0;
     }
     emit Closed();
     Exit();
   }
}

bool CAudioDevice::IsOpen() const
{
   return (m_OpenCount > 0);
}


/**
  \brief Change parameters of capture/playback
  \param attr Struct with new settings
  \return A boolean indicating success or not

  This function will set the card to different hardware parameters for
  capture or playback (or both). It returns true when it was succesful
  in doing so.

  If the new sound attributes are the same as the current ones,
  the function immediately returns true and does not emit a signal.

  In case no stream is being captured or played back, nothing really
  happens; it just sets the initial parameters, emits the
  SoundAttributesChanged signal and returns true.

  Otherwise, the following happens:
  - capture and playback are stopped
  - we wait until all buffers of all readers are drained
  - the new attributes are set
  - capture or playback is restarted
  - if that fails, we reset the old attributes and try again
  - if that fails too, the driver is left in an idle state

  Changes to the sound attributes are emit()ted between stopping and
  starting the audio stream(s). It is possible multiple emits are
  generated with no audio data in between, for example when the (hardware) driver
  rejects the new attributes. Because of this, the receiver always knows the
  exact format of the audio stream when reading from its CAudioRingBufferReader.

*/
bool CAudioDevice::SetSoundAttributes(const SoundAttributes &attr)
{
   SoundAttributes old_attr = m_CurrentSoundAttr;
   bool OK = true;

   if (attr == m_CurrentSoundAttr)
     return true;

   if (m_CaptureCount > 0 || m_PlaybackCount > 0) {
     StopCapture();
     StopPlayback();
     m_CurrentSoundAttr = attr;
     // Emit now, even if we didn't succeed yet
     emit SoundAttributesChanged(m_CurrentSoundAttr);
     if (m_CaptureCount > 0)
       OK = OK && (StartCapture() >= 0);
     if (m_PlaybackCount > 0)
       OK = OK && (StartPlayback() >= 0);
     if (!OK) {
       bool Second = true;
       m_CurrentSoundAttr = old_attr; // restore attributes
       if (m_CaptureCount > 0 && StartCapture() < 0)  { // okay, now we are in serious trouble
         qWarning("CAudioDevice::SetSoundAttributes() Failed to restore old sound parameters!");
         Exit();
         m_CaptureCount = 0;
         Second = false;
       }
       if (m_PlaybackCount > 0 && StartPlayback() < 0) {
         qWarning("CAudioDevice::SetSoundAttributes() Failed to restore old sound parameters!");
         Exit();
         m_PlaybackCount = 0;
         Second = false;
       }
       if (Second)
       {
         emit SoundAttributesChanged(m_CurrentSoundAttr);
       }
     }
   }
   else
   {
     // Just store; we'll need it when capturing starts
     m_CurrentSoundAttr = attr;
     emit SoundAttributesChanged(m_CurrentSoundAttr);
   }
   return OK; // For now
}

SoundAttributes CAudioDevice::GetSoundAttributes() const
{
   return m_CurrentSoundAttr;
}

/*
  \brief Create thread-safe AudioReader buffer
  \return A CAudioRingBufferReader object

  This function creates and returns an CAudioRingBufferReader; this
  is the only way to gain access to the captured data.

  You can create as many readers as you like; each reader works
  independantly (but shares the same buffer). Creating a reader
  automatically enables capturing, and deleting the last reader
  automatically stops capturing.

  You must keep the returned pointer.

*/
CAudioRingBufferReader *CAudioDevice::CreateReader()
{
   return new CAudioRingBufferReader(&m_CaptureBuffer, &m_CurrentSoundAttr);
}

CRingBufferWriter *CAudioDevice::CreateWriter()
{
   return new CRingBufferWriter(&m_PlaybackBuffer);
}


// private slots

void CAudioDevice::EnableCapture()
{
   qDebug("<> CAudioDevice::EnableCapture()");
   if (m_CaptureCount++ == 0) // first enable
     StartCapture();
}

void CAudioDevice::DisableCapture()
{
   qDebug("<> CAudioDevice::DisableCapture()");
   if (m_CaptureCount > 0)
   {
     if (--m_CaptureCount == 0) // last disable
       StopCapture();
   }
}

void CAudioDevice::EnablePlayback()
{
   qDebug("<> CAudioDevice::EnablePlayback()");
   if (m_PlaybackCount++ == 0)
     StartPlayback();
}

void CAudioDevice::DisablePlayback()
{
   qDebug("<> CAudioDevice::DisablePlayback()");
   if (m_PlaybackCount > 0)
   {
     if (--m_PlaybackCount == 0)
       StopPlayback();
   }
}