1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/bind.h"
6 #include "base/logging.h"
7 #include "base/macros.h"
8 #include "base/message_loop/message_pump.h"
9 #include "media/base/audio_timestamp_helper.h"
10 #include "media/audio/openbsd/audio_manager_openbsd.h"
11 #include "media/audio/audio_manager.h"
12 #include "media/audio/sndio/sndio_input.h"
13
14 namespace media {
15
16 static const SampleFormat kSampleFormat = kSampleFormatS16;
17
OnMoveCallback(void * arg,int delta)18 void SndioAudioInputStream::OnMoveCallback(void *arg, int delta)
19 {
20 SndioAudioInputStream* self = static_cast<SndioAudioInputStream*>(arg);
21
22 self->hw_delay += delta;
23 }
24
ThreadEntry(void * arg)25 void *SndioAudioInputStream::ThreadEntry(void *arg) {
26 SndioAudioInputStream* self = static_cast<SndioAudioInputStream*>(arg);
27
28 self->ThreadLoop();
29 return NULL;
30 }
31
SndioAudioInputStream(AudioManagerBase * manager,const std::string & device_name,const AudioParameters & params)32 SndioAudioInputStream::SndioAudioInputStream(AudioManagerBase* manager,
33 const std::string& device_name,
34 const AudioParameters& params)
35 : manager(manager),
36 params(params),
37 audio_bus(AudioBus::Create(params)),
38 state(kClosed) {
39 }
40
~SndioAudioInputStream()41 SndioAudioInputStream::~SndioAudioInputStream() {
42 if (state != kClosed)
43 Close();
44 }
45
Open()46 bool SndioAudioInputStream::Open() {
47 struct sio_par par;
48 int sig;
49
50 if (state != kClosed)
51 return false;
52
53 if (params.format() != AudioParameters::AUDIO_PCM_LINEAR &&
54 params.format() != AudioParameters::AUDIO_PCM_LOW_LATENCY) {
55 LOG(WARNING) << "Unsupported audio format.";
56 return false;
57 }
58
59 sio_initpar(&par);
60 par.rate = params.sample_rate();
61 par.rchan = params.channels();
62 par.bits = SampleFormatToBitsPerChannel(kSampleFormat);
63 par.bps = par.bits / 8;
64 par.sig = sig = par.bits != 8 ? 1 : 0;
65 par.le = SIO_LE_NATIVE;
66 par.appbufsz = params.frames_per_buffer();
67
68 hdl = sio_open(SIO_DEVANY, SIO_REC, 0);
69
70 if (hdl == NULL) {
71 LOG(ERROR) << "Couldn't open audio device.";
72 return false;
73 }
74
75 if (!sio_setpar(hdl, &par) || !sio_getpar(hdl, &par)) {
76 LOG(ERROR) << "Couldn't set audio parameters.";
77 goto bad_close;
78 }
79
80 if (par.rate != (unsigned int)params.sample_rate() ||
81 par.rchan != (unsigned int)params.channels() ||
82 par.bits != (unsigned int)SampleFormatToBitsPerChannel(kSampleFormat) ||
83 par.sig != (unsigned int)sig ||
84 (par.bps > 1 && par.le != SIO_LE_NATIVE) ||
85 (par.bits != par.bps * 8)) {
86 LOG(ERROR) << "Unsupported audio parameters.";
87 goto bad_close;
88 }
89 state = kStopped;
90 buffer = new char[audio_bus->frames() * params.GetBytesPerFrame(kSampleFormat)];
91 sio_onmove(hdl, &OnMoveCallback, this);
92 return true;
93 bad_close:
94 sio_close(hdl);
95 return false;
96 }
97
Start(AudioInputCallback * cb)98 void SndioAudioInputStream::Start(AudioInputCallback* cb) {
99
100 StartAgc();
101
102 state = kRunning;
103 hw_delay = 0;
104 callback = cb;
105 sio_start(hdl);
106 if (pthread_create(&thread, NULL, &ThreadEntry, this) != 0) {
107 LOG(ERROR) << "Failed to create real-time thread for recording.";
108 sio_stop(hdl);
109 state = kStopped;
110 }
111 }
112
Stop()113 void SndioAudioInputStream::Stop() {
114
115 if (state == kStopped)
116 return;
117
118 state = kStopWait;
119 pthread_join(thread, NULL);
120 sio_stop(hdl);
121 state = kStopped;
122
123 StopAgc();
124 }
125
Close()126 void SndioAudioInputStream::Close() {
127
128 if (state == kClosed)
129 return;
130
131 if (state == kRunning)
132 Stop();
133
134 state = kClosed;
135 delete [] buffer;
136 sio_close(hdl);
137
138 manager->ReleaseInputStream(this);
139 }
140
GetMaxVolume()141 double SndioAudioInputStream::GetMaxVolume() {
142 // Not supported
143 return 0.0;
144 }
145
SetVolume(double volume)146 void SndioAudioInputStream::SetVolume(double volume) {
147 // Not supported. Do nothing.
148 }
149
GetVolume()150 double SndioAudioInputStream::GetVolume() {
151 // Not supported.
152 return 0.0;
153 }
154
IsMuted()155 bool SndioAudioInputStream::IsMuted() {
156 // Not supported.
157 return false;
158 }
159
SetOutputDeviceForAec(const std::string & output_device_id)160 void SndioAudioInputStream::SetOutputDeviceForAec(
161 const std::string& output_device_id) {
162 // Not supported.
163 }
164
ThreadLoop(void)165 void SndioAudioInputStream::ThreadLoop(void) {
166 size_t todo, n;
167 char *data;
168 unsigned int nframes;
169 double normalized_volume = 0.0;
170
171 nframes = audio_bus->frames();
172
173 while (state == kRunning && !sio_eof(hdl)) {
174
175 GetAgcVolume(&normalized_volume);
176
177 // read one block
178 todo = nframes * params.GetBytesPerFrame(kSampleFormat);
179 data = buffer;
180 while (todo > 0) {
181 n = sio_read(hdl, data, todo);
182 if (n == 0)
183 return; // unrecoverable I/O error
184 todo -= n;
185 data += n;
186 }
187 hw_delay -= nframes;
188
189 // convert frames count to TimeDelta
190 const base::TimeDelta delay = AudioTimestampHelper::FramesToTime(hw_delay,
191 params.sample_rate());
192
193 // push into bus
194 audio_bus->FromInterleaved(buffer, nframes, SampleFormatToBytesPerChannel(kSampleFormat));
195
196 // invoke callback
197 callback->OnData(audio_bus.get(), base::TimeTicks::Now() - delay, 1.);
198 }
199 }
200
201 } // namespace media
202