3
* Copyright 2004--2010, Google Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
8
* 1. Redistributions of source code must retain the above copyright notice,
9
* this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
13
* 3. The name of the author may not be used to endorse or promote products
14
* derived from this software without specific prior written permission.
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
#include "talk/sound/alsasoundsystem.h"
30
#include "talk/base/common.h"
31
#include "talk/base/logging.h"
32
#include "talk/base/scoped_ptr.h"
33
#include "talk/base/stringutils.h"
34
#include "talk/base/timeutils.h"
35
#include "talk/base/worker.h"
36
#include "talk/sound/sounddevicelocator.h"
37
#include "talk/sound/soundinputstreaminterface.h"
38
#include "talk/sound/soundoutputstreaminterface.h"
42
// Lookup table from the cricket format enum in soundsysteminterface.h to
44
static const snd_pcm_format_t kCricketFormatToAlsaFormatTable[] = {
45
// The order here must match the order in soundsysteminterface.h
46
SND_PCM_FORMAT_S16_LE,
49
// Lookup table for the size of a single sample of a given format.
50
static const size_t kCricketFormatToSampleSizeTable[] = {
51
// The order here must match the order in soundsysteminterface.h
55
// Minimum latency we allow, in microseconds. This is more or less arbitrary,
56
// but it has to be at least large enough to be able to buffer data during a
57
// missed context switch, and the typical Linux scheduling quantum is 10ms.
58
static const int kMinimumLatencyUsecs = 20 * 1000;
60
// The latency we'll use for kNoLatencyRequirements (chosen arbitrarily).
61
static const int kDefaultLatencyUsecs = kMinimumLatencyUsecs * 2;
63
// We translate newlines in ALSA device descriptions to hyphens.
64
static const char kAlsaDescriptionSearch[] = "\n";
65
static const char kAlsaDescriptionReplace[] = " - ";
67
class AlsaDeviceLocator : public SoundDeviceLocator {
69
AlsaDeviceLocator(const std::string &name,
70
const std::string &device_name)
71
: SoundDeviceLocator(name, device_name) {
72
// The ALSA descriptions have newlines in them, which won't show up in
73
// a drop-down box. Replace them with hyphens.
74
talk_base::replace_substrs(kAlsaDescriptionSearch,
75
sizeof(kAlsaDescriptionSearch) - 1,
76
kAlsaDescriptionReplace,
77
sizeof(kAlsaDescriptionReplace) - 1,
81
virtual SoundDeviceLocator *Copy() const {
82
return new AlsaDeviceLocator(*this);
86
// Accesses ALSA functions through our late-binding symbol table instead of
87
// directly. This way we don't have to link to libasound, which means our binary
88
// will load faster and we can run on strange systems that may not have
91
LATESYM_GET(AlsaSymbolTable, &alsa_->symbol_table_, sym)
93
// Functionality that is common to both AlsaInputStream and AlsaOutputStream.
96
AlsaStream(AlsaSoundSystem *alsa,
104
frame_size_(frame_size),
105
wait_timeout_ms_(wait_timeout_ms),
114
// Waits for the stream to be ready to accept/return more data, and returns
115
// how much can be written/read, or 0 if we need to Wait() again.
116
snd_pcm_uframes_t Wait() {
117
snd_pcm_sframes_t frames;
118
// Ideally we would not use snd_pcm_wait() and instead hook snd_pcm_poll_*
119
// into PhysicalSocketServer, but PhysicalSocketServer is nasty enough
120
// already and the current clients of SoundSystemInterface do not run
121
// anything else on their worker threads, so snd_pcm_wait() is good enough.
122
frames = LATE(snd_pcm_avail_update)(handle_);
124
LOG(LS_ERROR) << "snd_pcm_avail_update(): " << GetError(frames);
127
} else if (frames > 0) {
128
// Already ready, so no need to wait.
131
// Else no space/data available, so must wait.
132
int ready = LATE(snd_pcm_wait)(handle_, wait_timeout_ms_);
134
LOG(LS_ERROR) << "snd_pcm_wait(): " << GetError(ready);
137
} else if (ready == 0) {
138
// Timeout, so nothing can be written/read right now.
139
// We set the timeout to twice the requested latency, so continuous
140
// timeouts are indicative of a problem, so log as a warning.
141
LOG(LS_WARNING) << "Timeout while waiting on stream";
144
// Else ready > 0 (i.e., 1), so it's ready. Get count.
145
frames = LATE(snd_pcm_avail_update)(handle_);
147
LOG(LS_ERROR) << "snd_pcm_avail_update(): " << GetError(frames);
150
} else if (frames == 0) {
151
// wait() said we were ready, so this ought to have been positive. Has
152
// been observed to happen in practice though.
153
LOG(LS_WARNING) << "Spurious wake-up";
158
int CurrentDelayUsecs() {
159
if (!(flags_ & SoundSystemInterface::FLAG_REPORT_LATENCY)) {
163
snd_pcm_sframes_t delay;
164
int err = LATE(snd_pcm_delay)(handle_, &delay);
166
LOG(LS_ERROR) << "snd_pcm_delay(): " << GetError(err);
168
// We'd rather continue playout/capture with an incorrect delay than stop
169
// it altogether, so return a valid value.
172
// The delay is in frames. Convert to microseconds.
173
return delay * talk_base::kNumMicrosecsPerSec / freq_;
176
// Used to recover from certain recoverable errors, principally buffer overrun
177
// or underrun (identified as EPIPE). Without calling this the stream stays
178
// in the error state forever.
179
bool Recover(int error) {
181
err = LATE(snd_pcm_recover)(handle_,
183
// Silent; i.e., no logging on stderr.
186
// Docs say snd_pcm_recover returns the original error if it is not one
187
// of the recoverable ones, so this log message will probably contain the
189
LOG(LS_ERROR) << "Unable to recover from \"" << GetError(error) << "\": "
193
if (error == -EPIPE && // Buffer underrun/overrun.
194
LATE(snd_pcm_stream)(handle_) == SND_PCM_STREAM_CAPTURE) {
195
// For capture streams we also have to repeat the explicit start() to get
196
// data flowing again.
197
err = LATE(snd_pcm_start)(handle_);
199
LOG(LS_ERROR) << "snd_pcm_start(): " << GetError(err);
209
err = LATE(snd_pcm_drop)(handle_);
211
LOG(LS_ERROR) << "snd_pcm_drop(): " << GetError(err);
214
err = LATE(snd_pcm_close)(handle_);
216
LOG(LS_ERROR) << "snd_pcm_close(): " << GetError(err);
224
AlsaSoundSystem *alsa() {
228
snd_pcm_t *handle() {
232
const char *GetError(int err) {
233
return alsa_->GetError(err);
236
size_t frame_size() {
241
AlsaSoundSystem *alsa_;
244
int wait_timeout_ms_;
248
DISALLOW_COPY_AND_ASSIGN(AlsaStream);
251
// Redefine for the next two classes.
254
LATESYM_GET(AlsaSymbolTable, &stream_.alsa()->symbol_table_, sym)
256
// Implementation of an input stream. See soundinputstreaminterface.h regarding
258
class AlsaInputStream :
259
public SoundInputStreamInterface,
260
private talk_base::Worker {
262
AlsaInputStream(AlsaSoundSystem *alsa,
268
: stream_(alsa, handle, frame_size, wait_timeout_ms, flags, freq),
272
virtual ~AlsaInputStream() {
273
bool success = StopReading();
274
// We need that to live.
278
virtual bool StartReading() {
282
virtual bool StopReading() {
286
virtual bool GetVolume(int *volume) {
287
// TODO: Implement this.
291
virtual bool SetVolume(int volume) {
292
// TODO: Implement this.
296
virtual bool Close() {
297
return StopReading() && stream_.Close();
300
virtual int LatencyUsecs() {
301
return stream_.CurrentDelayUsecs();
305
// Inherited from Worker.
306
virtual void OnStart() {
310
// Inherited from Worker.
311
virtual void OnHaveWork() {
312
// Block waiting for data.
313
snd_pcm_uframes_t avail = stream_.Wait();
315
// Data is available.
316
size_t size = avail * stream_.frame_size();
317
if (size > buffer_size_) {
318
// Must increase buffer size.
319
buffer_.reset(new char[size]);
322
// Read all the data.
323
snd_pcm_sframes_t read = LATE(snd_pcm_readi)(stream_.handle(),
327
LOG(LS_ERROR) << "snd_pcm_readi(): " << GetError(read);
328
stream_.Recover(read);
329
} else if (read == 0) {
330
// Docs say this shouldn't happen.
332
LOG(LS_ERROR) << "No data?";
334
// Got data. Pass it off to the app.
335
SignalSamplesRead(buffer_.get(),
336
read * stream_.frame_size(),
340
// Check for more data with no delay, after any pending messages are
345
// Inherited from Worker.
346
virtual void OnStop() {
350
const char *GetError(int err) {
351
return stream_.GetError(err);
355
talk_base::scoped_array<char> buffer_;
358
DISALLOW_COPY_AND_ASSIGN(AlsaInputStream);
361
// Implementation of an output stream. See soundoutputstreaminterface.h
362
// regarding thread-safety.
363
class AlsaOutputStream :
364
public SoundOutputStreamInterface,
365
private talk_base::Worker {
367
AlsaOutputStream(AlsaSoundSystem *alsa,
373
: stream_(alsa, handle, frame_size, wait_timeout_ms, flags, freq) {
376
virtual ~AlsaOutputStream() {
377
bool success = DisableBufferMonitoring();
378
// We need that to live.
382
virtual bool EnableBufferMonitoring() {
386
virtual bool DisableBufferMonitoring() {
390
virtual bool WriteSamples(const void *sample_data,
392
if (size % stream_.frame_size() != 0) {
393
// No client of SoundSystemInterface does this, so let's not support it.
394
// (If we wanted to support it, we'd basically just buffer the fractional
395
// frame until we get more data.)
397
LOG(LS_ERROR) << "Writes with fractional frames are not supported";
400
snd_pcm_uframes_t frames = size / stream_.frame_size();
401
snd_pcm_sframes_t written = LATE(snd_pcm_writei)(stream_.handle(),
405
LOG(LS_ERROR) << "snd_pcm_writei(): " << GetError(written);
406
stream_.Recover(written);
408
} else if (static_cast<snd_pcm_uframes_t>(written) < frames) {
409
// Shouldn't happen. Drop the rest of the data.
410
LOG(LS_ERROR) << "Stream wrote only " << written << " of " << frames
417
virtual bool GetVolume(int *volume) {
418
// TODO: Implement this.
422
virtual bool SetVolume(int volume) {
423
// TODO: Implement this.
427
virtual bool Close() {
428
return DisableBufferMonitoring() && stream_.Close();
431
virtual int LatencyUsecs() {
432
return stream_.CurrentDelayUsecs();
436
// Inherited from Worker.
437
virtual void OnStart() {
441
// Inherited from Worker.
442
virtual void OnHaveWork() {
443
snd_pcm_uframes_t avail = stream_.Wait();
445
size_t space = avail * stream_.frame_size();
446
SignalBufferSpace(space, this);
451
// Inherited from Worker.
452
virtual void OnStop() {
456
const char *GetError(int err) {
457
return stream_.GetError(err);
462
DISALLOW_COPY_AND_ASSIGN(AlsaOutputStream);
465
// Redefine for the main class.
467
#define LATE(sym) LATESYM_GET(AlsaSymbolTable, &symbol_table_, sym)
469
AlsaSoundSystem::AlsaSoundSystem() : initialized_(false) {}
471
AlsaSoundSystem::~AlsaSoundSystem() {
472
// Not really necessary, because Terminate() doesn't really do anything.
476
bool AlsaSoundSystem::Init() {
477
if (IsInitialized()) {
482
if (!symbol_table_.Load()) {
483
// Very odd for a Linux machine to not have a working libasound ...
484
LOG(LS_ERROR) << "Failed to load symbol table";
493
void AlsaSoundSystem::Terminate() {
494
if (!IsInitialized()) {
498
initialized_ = false;
500
// We do not unload the symbol table because we may need it again soon if
501
// Init() is called again.
504
bool AlsaSoundSystem::EnumeratePlaybackDevices(
505
SoundDeviceLocatorList *devices) {
506
return EnumerateDevices(devices, false);
509
bool AlsaSoundSystem::EnumerateCaptureDevices(
510
SoundDeviceLocatorList *devices) {
511
return EnumerateDevices(devices, true);
514
bool AlsaSoundSystem::GetDefaultPlaybackDevice(SoundDeviceLocator **device) {
515
return GetDefaultDevice(device);
518
bool AlsaSoundSystem::GetDefaultCaptureDevice(SoundDeviceLocator **device) {
519
return GetDefaultDevice(device);
522
SoundOutputStreamInterface *AlsaSoundSystem::OpenPlaybackDevice(
523
const SoundDeviceLocator *device,
524
const OpenParams ¶ms) {
525
return OpenDevice<SoundOutputStreamInterface>(
528
SND_PCM_STREAM_PLAYBACK,
529
&AlsaSoundSystem::StartOutputStream);
532
SoundInputStreamInterface *AlsaSoundSystem::OpenCaptureDevice(
533
const SoundDeviceLocator *device,
534
const OpenParams ¶ms) {
535
return OpenDevice<SoundInputStreamInterface>(
538
SND_PCM_STREAM_CAPTURE,
539
&AlsaSoundSystem::StartInputStream);
542
const char *AlsaSoundSystem::GetName() const {
546
bool AlsaSoundSystem::EnumerateDevices(
547
SoundDeviceLocatorList *devices,
548
bool capture_not_playback) {
549
ClearSoundDeviceLocatorList(devices);
551
if (!IsInitialized()) {
555
const char *type = capture_not_playback ? "Input" : "Output";
556
// dmix and dsnoop are only for playback and capture, respectively, but ALSA
557
// stupidly includes them in both lists.
558
const char *ignore_prefix = capture_not_playback ? "dmix:" : "dsnoop:";
559
// (ALSA lists many more "devices" of questionable interest, but we show them
560
// just in case the weird devices may actually be desirable for some
562
const char *ignore_default = "default";
563
const char *ignore_null = "null";
564
const char *ignore_pulse = "pulse";
565
// The 'pulse' entry has a habit of mysteriously disappearing when you query
566
// a second time. Remove it from our list. (GIPS lib did the same thing.)
570
err = LATE(snd_device_name_hint)(-1, // All cards
571
"pcm", // Only PCM devices
574
LOG(LS_ERROR) << "snd_device_name_hint(): " << GetError(err);
578
for (void **list = hints; *list != NULL; ++list) {
579
char *actual_type = LATE(snd_device_name_get_hint)(*list, "IOID");
580
if (actual_type) { // NULL means it's both.
581
bool wrong_type = (strcmp(actual_type, type) != 0);
584
// Wrong type of device (i.e., input vs. output).
589
char *name = LATE(snd_device_name_get_hint)(*list, "NAME");
591
LOG(LS_ERROR) << "Device has no name???";
596
// Now check if we actually want to show this device.
597
if (strcmp(name, ignore_default) != 0 &&
598
strcmp(name, ignore_null) != 0 &&
599
strcmp(name, ignore_pulse) != 0 &&
600
!talk_base::starts_with(name, ignore_prefix)) {
603
char *desc = LATE(snd_device_name_get_hint)(*list, "DESC");
605
// Virtual devices don't necessarily have descriptions. Use their names
606
// instead (not pretty!).
610
AlsaDeviceLocator *device = new AlsaDeviceLocator(desc, name);
612
devices->push_back(device);
622
err = LATE(snd_device_name_free_hint)(hints);
624
LOG(LS_ERROR) << "snd_device_name_free_hint(): " << GetError(err);
625
// Continue and return true anyways, since we did get the whole list.
631
bool AlsaSoundSystem::GetDefaultDevice(SoundDeviceLocator **device) {
632
if (!IsInitialized()) {
635
*device = new AlsaDeviceLocator("Default device", "default");
639
inline size_t AlsaSoundSystem::FrameSize(const OpenParams ¶ms) {
640
ASSERT(params.format < ARRAY_SIZE(kCricketFormatToSampleSizeTable));
641
return kCricketFormatToSampleSizeTable[params.format] * params.channels;
644
template <typename StreamInterface>
645
StreamInterface *AlsaSoundSystem::OpenDevice(
646
const SoundDeviceLocator *device,
647
const OpenParams ¶ms,
648
snd_pcm_stream_t type,
649
StreamInterface *(AlsaSoundSystem::*start_fn)(
656
if (!IsInitialized()) {
660
StreamInterface *stream;
663
const char *dev = static_cast<const AlsaDeviceLocator *>(device)->
664
device_name().c_str();
666
snd_pcm_t *handle = NULL;
667
err = LATE(snd_pcm_open)(
674
LOG(LS_ERROR) << "snd_pcm_open(" << dev << "): " << GetError(err);
677
LOG(LS_VERBOSE) << "Opening " << dev;
678
ASSERT(handle); // If open succeeded, handle ought to be valid
680
// Compute requested latency in microseconds.
682
if (params.latency == kNoLatencyRequirements) {
683
latency = kDefaultLatencyUsecs;
685
// kLowLatency is 0, so we treat it the same as a request for zero latency.
686
// Compute what the user asked for.
687
latency = talk_base::kNumMicrosecsPerSec *
691
// And this is what we'll actually use.
692
latency = talk_base::_max(latency, kMinimumLatencyUsecs);
695
ASSERT(params.format < ARRAY_SIZE(kCricketFormatToAlsaFormatTable));
697
err = LATE(snd_pcm_set_params)(handle,
698
kCricketFormatToAlsaFormatTable[params.format],
699
// SoundSystemInterface only supports
700
// interleaved audio.
701
SND_PCM_ACCESS_RW_INTERLEAVED,
704
1, // Allow ALSA to resample.
707
LOG(LS_ERROR) << "snd_pcm_set_params(): " << GetError(err);
711
err = LATE(snd_pcm_prepare)(handle);
713
LOG(LS_ERROR) << "snd_pcm_prepare(): " << GetError(err);
717
stream = (this->*start_fn)(
720
// We set the wait time to twice the requested latency, so that wait
721
// timeouts should be rare.
722
2 * latency / talk_base::kNumMicrosecsPerMillisec,
728
// Else fall through.
731
err = LATE(snd_pcm_close)(handle);
733
LOG(LS_ERROR) << "snd_pcm_close(): " << GetError(err);
738
SoundOutputStreamInterface *AlsaSoundSystem::StartOutputStream(
744
// Nothing to do here but instantiate the stream.
745
return new AlsaOutputStream(
746
this, handle, frame_size, wait_timeout_ms, flags, freq);
749
SoundInputStreamInterface *AlsaSoundSystem::StartInputStream(
755
// Output streams start automatically once enough data has been written, but
756
// input streams must be started manually or else snd_pcm_wait() will never
759
err = LATE(snd_pcm_start)(handle);
761
LOG(LS_ERROR) << "snd_pcm_start(): " << GetError(err);
764
return new AlsaInputStream(
765
this, handle, frame_size, wait_timeout_ms, flags, freq);
768
inline const char *AlsaSoundSystem::GetError(int err) {
769
return LATE(snd_strerror)(err);
772
} // namespace cricket