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 18 void SndioAudioInputStream::OnMoveCallback(void *arg, int delta) 19 { 20 SndioAudioInputStream* self = static_cast<SndioAudioInputStream*>(arg); 21 22 self->hw_delay += delta; 23 } 24 25 void *SndioAudioInputStream::ThreadEntry(void *arg) { 26 SndioAudioInputStream* self = static_cast<SndioAudioInputStream*>(arg); 27 28 self->ThreadLoop(); 29 return NULL; 30 } 31 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 41 SndioAudioInputStream::~SndioAudioInputStream() { 42 if (state != kClosed) 43 Close(); 44 } 45 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 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 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 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 141 double SndioAudioInputStream::GetMaxVolume() { 142 // Not supported 143 return 0.0; 144 } 145 146 void SndioAudioInputStream::SetVolume(double volume) { 147 // Not supported. Do nothing. 148 } 149 150 double SndioAudioInputStream::GetVolume() { 151 // Not supported. 152 return 0.0; 153 } 154 155 bool SndioAudioInputStream::IsMuted() { 156 // Not supported. 157 return false; 158 } 159 160 void SndioAudioInputStream::SetOutputDeviceForAec( 161 const std::string& output_device_id) { 162 // Not supported. 163 } 164 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