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