OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2010 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 "media/audio/win/wavein_input_win.h" |
| 6 |
| 7 #include <windows.h> |
| 8 #include <mmsystem.h> |
| 9 #pragma comment(lib, "winmm.lib") |
| 10 |
| 11 #include "base/basictypes.h" |
| 12 #include "base/logging.h" |
| 13 #include "media/audio/audio_io.h" |
| 14 #include "media/audio/audio_util.h" |
| 15 #include "media/audio/win/audio_manager_win.h" |
| 16 |
| 17 namespace { |
| 18 |
| 19 // Our sound buffers are allocated once and kept in a linked list using the |
| 20 // the WAVEHDR::dwUser variable. The last buffer points to the first buffer. |
| 21 WAVEHDR* GetNextBuffer(WAVEHDR* current) { |
| 22 return reinterpret_cast<WAVEHDR*>(current->dwUser); |
| 23 } |
| 24 |
| 25 } // namespace |
| 26 |
| 27 PCMWaveInAudioInputStream::PCMWaveInAudioInputStream( |
| 28 AudioManagerWin* manager, int channels, int sampling_rate, int num_buffers, |
| 29 char bits_per_sample, uint32 samples_per_packet, UINT device_id) |
| 30 : state_(kStateEmpty), |
| 31 manager_(manager), |
| 32 device_id_(device_id), |
| 33 wavein_(NULL), |
| 34 callback_(NULL), |
| 35 num_buffers_(num_buffers), |
| 36 buffer_(NULL), |
| 37 channels_(channels) { |
| 38 format_.wFormatTag = WAVE_FORMAT_PCM; |
| 39 format_.nChannels = channels > 2 ? 2 : channels; |
| 40 format_.nSamplesPerSec = sampling_rate; |
| 41 format_.wBitsPerSample = bits_per_sample; |
| 42 format_.cbSize = 0; |
| 43 format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8; |
| 44 format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec; |
| 45 buffer_size_ = samples_per_packet * format_.nBlockAlign; |
| 46 // If we don't have a packet size we use 100ms. |
| 47 if (!buffer_size_) |
| 48 buffer_size_ = format_.nAvgBytesPerSec / 10; |
| 49 // The event is auto-reset. |
| 50 stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); |
| 51 } |
| 52 |
| 53 PCMWaveInAudioInputStream::~PCMWaveInAudioInputStream() { |
| 54 DCHECK(NULL == wavein_); |
| 55 } |
| 56 |
| 57 bool PCMWaveInAudioInputStream::Open() { |
| 58 if (state_ != kStateEmpty) |
| 59 return false; |
| 60 if (num_buffers_ < 2 || num_buffers_ > 10) |
| 61 return false; |
| 62 MMRESULT result = ::waveInOpen(&wavein_, device_id_, &format_, |
| 63 reinterpret_cast<DWORD_PTR>(WaveCallback), |
| 64 reinterpret_cast<DWORD_PTR>(this), |
| 65 CALLBACK_FUNCTION); |
| 66 if (result != MMSYSERR_NOERROR) |
| 67 return false; |
| 68 |
| 69 SetupBuffers(); |
| 70 state_ = kStateReady; |
| 71 return true; |
| 72 } |
| 73 |
| 74 void PCMWaveInAudioInputStream::SetupBuffers() { |
| 75 WAVEHDR* last = NULL; |
| 76 WAVEHDR* first = NULL; |
| 77 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 78 uint32 sz = sizeof(WAVEHDR) + buffer_size_; |
| 79 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); |
| 80 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); |
| 81 buffer_->dwBufferLength = buffer_size_; |
| 82 buffer_->dwBytesRecorded = 0; |
| 83 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); |
| 84 buffer_->dwFlags = WHDR_DONE; |
| 85 buffer_->dwLoops = 0; |
| 86 if (ix == 0) |
| 87 first = buffer_; |
| 88 last = buffer_; |
| 89 ::waveInPrepareHeader(wavein_, buffer_, sizeof(WAVEHDR)); |
| 90 } |
| 91 // Fix the first buffer to point to the last one. |
| 92 first->dwUser = reinterpret_cast<DWORD_PTR>(last); |
| 93 } |
| 94 |
| 95 void PCMWaveInAudioInputStream::FreeBuffers() { |
| 96 WAVEHDR* current = buffer_; |
| 97 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 98 WAVEHDR* next = GetNextBuffer(current); |
| 99 if (current->dwFlags & WHDR_PREPARED) |
| 100 ::waveInUnprepareHeader(wavein_, current, sizeof(WAVEHDR)); |
| 101 delete[] reinterpret_cast<char*>(current); |
| 102 current = next; |
| 103 } |
| 104 buffer_ = NULL; |
| 105 } |
| 106 |
| 107 void PCMWaveInAudioInputStream::Start(AudioInputCallback* callback) { |
| 108 if (state_ != kStateReady) |
| 109 return; |
| 110 |
| 111 callback_ = callback; |
| 112 state_ = kStateRecording; |
| 113 |
| 114 WAVEHDR* buffer = buffer_; |
| 115 for (int ix = 0; ix != num_buffers_; ++ix) { |
| 116 QueueNextPacket(buffer); |
| 117 buffer = GetNextBuffer(buffer); |
| 118 } |
| 119 buffer = buffer_; |
| 120 |
| 121 MMRESULT result = ::waveInStart(wavein_); |
| 122 if (result != MMSYSERR_NOERROR) { |
| 123 HandleError(result); |
| 124 state_ = kStateReady; |
| 125 } |
| 126 } |
| 127 |
| 128 // Stopping is tricky. First, no buffer should be locked by the audio driver |
| 129 // or else the waveInReset() will deadlock and secondly, the callback should |
| 130 // not be inside the AudioInputCallback's OnData because waveInReset() |
| 131 // forcefully kills the callback thread. |
| 132 void PCMWaveInAudioInputStream::Stop() { |
| 133 if (state_ != kStateRecording) |
| 134 return; |
| 135 state_ = kStateStopping; |
| 136 // Wait for the callback to finish, it will signal us when ready to be reset. |
| 137 if (WAIT_OBJECT_0 != ::WaitForSingleObject(stopped_event_, INFINITE)) { |
| 138 HandleError(::GetLastError()); |
| 139 return; |
| 140 } |
| 141 state_ = kStateStopped; |
| 142 MMRESULT res = ::waveInReset(wavein_); |
| 143 if (res != MMSYSERR_NOERROR) { |
| 144 state_ = kStateRecording; |
| 145 HandleError(res); |
| 146 return; |
| 147 } |
| 148 state_ = kStateReady; |
| 149 } |
| 150 |
| 151 // We can Close in any state except that when trying to close a stream that is |
| 152 // recording Windows generates an error, which we propagate to the source. |
| 153 void PCMWaveInAudioInputStream::Close() { |
| 154 if (wavein_) { |
| 155 // waveInClose generates a callback with WIM_CLOSE id in the same thread. |
| 156 MMRESULT res = ::waveInClose(wavein_); |
| 157 if (res != MMSYSERR_NOERROR) { |
| 158 HandleError(res); |
| 159 return; |
| 160 } |
| 161 state_ = kStateClosed; |
| 162 wavein_ = NULL; |
| 163 FreeBuffers(); |
| 164 } |
| 165 // Tell the audio manager that we have been released. This can result in |
| 166 // the manager destroying us in-place so this needs to be the last thing |
| 167 // we do on this function. |
| 168 manager_->ReleaseInputStream(this); |
| 169 } |
| 170 |
| 171 void PCMWaveInAudioInputStream::HandleError(MMRESULT error) { |
| 172 DLOG(WARNING) << "PCMWaveInAudio error " << error; |
| 173 callback_->OnError(this, error); |
| 174 } |
| 175 |
| 176 void PCMWaveInAudioInputStream::QueueNextPacket(WAVEHDR *buffer) { |
| 177 MMRESULT res = ::waveInAddBuffer(wavein_, buffer, sizeof(WAVEHDR)); |
| 178 if (res != MMSYSERR_NOERROR) |
| 179 HandleError(res); |
| 180 } |
| 181 |
| 182 // Windows calls us back in this function when some events happen. Most notably |
| 183 // when it has an audio buffer with recorded data. |
| 184 void PCMWaveInAudioInputStream::WaveCallback(HWAVEIN hwi, UINT msg, |
| 185 DWORD_PTR instance, |
| 186 DWORD_PTR param1, DWORD_PTR) { |
| 187 PCMWaveInAudioInputStream* obj = |
| 188 reinterpret_cast<PCMWaveInAudioInputStream*>(instance); |
| 189 |
| 190 if (msg == WIM_DATA) { |
| 191 // WIM_DONE indicates that the driver is done with our buffer. We pass it |
| 192 // to the callback and check if we need to stop playing. |
| 193 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); |
| 194 obj->callback_->OnData(obj, reinterpret_cast<const uint8*>(buffer->lpData), |
| 195 buffer->dwBytesRecorded); |
| 196 |
| 197 if (obj->state_ == kStateStopping) { |
| 198 // The main thread has called Stop() and is waiting to issue waveOutReset |
| 199 // which will kill this thread. We should not enter AudioSourceCallback |
| 200 // code anymore. |
| 201 ::SetEvent(obj->stopped_event_); |
| 202 } else if (obj->state_ == kStateStopped) { |
| 203 // Not sure if ever hit this but just in case. |
| 204 } else { |
| 205 // Queue the finished buffer back with the audio driver. Since we are |
| 206 // reusing the same buffers we can get away without calling |
| 207 // waveInPrepareHeader. |
| 208 obj->QueueNextPacket(buffer); |
| 209 } |
| 210 } else if (msg == WIM_CLOSE) { |
| 211 // We can be closed before calling Start, so it is possible to have a |
| 212 // null callback at this point. |
| 213 if (obj->callback_) |
| 214 obj->callback_->OnClose(obj); |
| 215 } |
| 216 } |
OLD | NEW |