OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/audio/win/waveout_output_win.h" | 5 #include "media/audio/win/waveout_output_win.h" |
6 | 6 |
7 #include <windows.h> | 7 #include <windows.h> |
8 #include <mmsystem.h> | 8 #include <mmsystem.h> |
9 #pragma comment(lib, "winmm.lib") | 9 #pragma comment(lib, "winmm.lib") |
10 | 10 |
11 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
12 #include "base/debug/trace_event.h" | 12 #include "base/debug/trace_event.h" |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "media/audio/audio_io.h" | 14 #include "media/audio/audio_io.h" |
15 #include "media/audio/audio_util.h" | 15 #include "media/audio/audio_util.h" |
16 #include "media/audio/win/audio_manager_win.h" | 16 #include "media/audio/win/audio_manager_win.h" |
17 | 17 |
| 18 // Number of times InitializeCriticalSectionAndSpinCount() spins |
| 19 // before going to sleep. |
| 20 const DWORD kSpinCount = 2000; |
| 21 |
18 // Some general thoughts about the waveOut API which is badly documented : | 22 // Some general thoughts about the waveOut API which is badly documented : |
19 // - We use CALLBACK_EVENT mode in which XP signals events such as buffer | 23 // - We use CALLBACK_FUNCTION mode in which XP secretly creates two threads |
20 // releases. | 24 // named _MixerCallbackThread and _waveThread which have real-time priority. |
21 // - We use RegisterWaitForSingleObject() so one of threads in thread pool | 25 // The callbacks occur in _waveThread. |
22 // automatically calls our callback that feeds more data to Windows. | |
23 // - Windows does not provide a way to query if the device is playing or paused | 26 // - Windows does not provide a way to query if the device is playing or paused |
24 // thus it forces you to maintain state, which naturally is not exactly | 27 // thus it forces you to maintain state, which naturally is not exactly |
25 // synchronized to the actual device state. | 28 // synchronized to the actual device state. |
| 29 // - Some functions, like waveOutReset cannot be called in the callback thread |
| 30 // or called in any random state because they deadlock. This results in a |
| 31 // non- instantaneous Stop() method. waveOutPrepareHeader seems to be in the |
| 32 // same boat. |
| 33 // - waveOutReset() will forcefully kill the _waveThread so it is important |
| 34 // to make sure we are not executing inside the audio source's OnMoreData() |
| 35 // or that we take locks inside WaveCallback() or QueueNextPacket(). |
26 | 36 |
27 // Sixty four MB is the maximum buffer size per AudioOutputStream. | 37 // Sixty four MB is the maximum buffer size per AudioOutputStream. |
28 static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64; | 38 static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64; |
29 | 39 |
| 40 // Our sound buffers are allocated once and kept in a linked list using the |
| 41 // the WAVEHDR::dwUser variable. The last buffer points to the first buffer. |
| 42 static WAVEHDR* GetNextBuffer(WAVEHDR* current) { |
| 43 return reinterpret_cast<WAVEHDR*>(current->dwUser); |
| 44 } |
| 45 |
30 // See Also | 46 // See Also |
31 // http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-sp
eaker-set-up/ | 47 // http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-sp
eaker-set-up/ |
32 // http://en.wikipedia.org/wiki/Surround_sound | 48 // http://en.wikipedia.org/wiki/Surround_sound |
33 | 49 |
34 static const int kMaxChannelsToMask = 8; | 50 static const int kMaxChannelsToMask = 8; |
35 static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { | 51 static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { |
36 0, | 52 0, |
37 // 1 = Mono | 53 // 1 = Mono |
38 SPEAKER_FRONT_CENTER, | 54 SPEAKER_FRONT_CENTER, |
39 // 2 = Stereo | 55 // 2 = Stereo |
(...skipping 16 matching lines...) Expand all Loading... |
56 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | | 72 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | |
57 SPEAKER_BACK_CENTER, | 73 SPEAKER_BACK_CENTER, |
58 // 8 = 7.1 | 74 // 8 = 7.1 |
59 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | | 75 SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | |
60 SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | | 76 SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | |
61 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | | 77 SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | |
62 SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT | 78 SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT |
63 // TODO(fbarchard): Add additional masks for 7.2 and beyond. | 79 // TODO(fbarchard): Add additional masks for 7.2 and beyond. |
64 }; | 80 }; |
65 | 81 |
66 inline size_t PCMWaveOutAudioOutputStream::BufferSize() const { | |
67 // Round size of buffer up to the nearest 16 bytes. | |
68 return (sizeof(WAVEHDR) + buffer_size_ + 15u) & static_cast<size_t>(~15); | |
69 } | |
70 | |
71 inline WAVEHDR* PCMWaveOutAudioOutputStream::GetBuffer(int n) const { | |
72 DCHECK_GE(n, 0); | |
73 DCHECK_LT(n, num_buffers_); | |
74 return reinterpret_cast<WAVEHDR*>(&buffers_[n * BufferSize()]); | |
75 } | |
76 | |
77 | |
78 PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( | 82 PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( |
79 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, | 83 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, |
80 UINT device_id) | 84 UINT device_id) |
81 : state_(PCMA_BRAND_NEW), | 85 : state_(PCMA_BRAND_NEW), |
82 manager_(manager), | 86 manager_(manager), |
83 device_id_(device_id), | 87 device_id_(device_id), |
84 waveout_(NULL), | 88 waveout_(NULL), |
85 callback_(NULL), | 89 callback_(NULL), |
86 num_buffers_(num_buffers), | 90 num_buffers_(num_buffers), |
| 91 buffer_(NULL), |
87 buffer_size_(params.GetPacketSize()), | 92 buffer_size_(params.GetPacketSize()), |
88 volume_(1), | 93 volume_(1), |
89 channels_(params.channels), | 94 channels_(params.channels), |
90 pending_bytes_(0) { | 95 pending_bytes_(0) { |
| 96 ::InitializeCriticalSectionAndSpinCount(&lock_, kSpinCount); |
| 97 |
91 format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | 98 format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; |
92 format_.Format.nChannels = params.channels; | 99 format_.Format.nChannels = params.channels; |
93 format_.Format.nSamplesPerSec = params.sample_rate; | 100 format_.Format.nSamplesPerSec = params.sample_rate; |
94 format_.Format.wBitsPerSample = params.bits_per_sample; | 101 format_.Format.wBitsPerSample = params.bits_per_sample; |
95 format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); | 102 format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); |
96 // The next are computed from above. | 103 // The next are computed from above. |
97 format_.Format.nBlockAlign = (format_.Format.nChannels * | 104 format_.Format.nBlockAlign = (format_.Format.nChannels * |
98 format_.Format.wBitsPerSample) / 8; | 105 format_.Format.wBitsPerSample) / 8; |
99 format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * | 106 format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * |
100 format_.Format.nSamplesPerSec; | 107 format_.Format.nSamplesPerSec; |
101 if (params.channels > kMaxChannelsToMask) { | 108 if (params.channels > kMaxChannelsToMask) { |
102 format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; | 109 format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; |
103 } else { | 110 } else { |
104 format_.dwChannelMask = kChannelsToMask[params.channels]; | 111 format_.dwChannelMask = kChannelsToMask[params.channels]; |
105 } | 112 } |
106 format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; | 113 format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; |
107 format_.Samples.wValidBitsPerSample = params.bits_per_sample; | 114 format_.Samples.wValidBitsPerSample = params.bits_per_sample; |
108 } | 115 } |
109 | 116 |
110 PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { | 117 PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { |
111 DCHECK(NULL == waveout_); | 118 DCHECK(NULL == waveout_); |
| 119 ::DeleteCriticalSection(&lock_); |
112 } | 120 } |
113 | 121 |
114 bool PCMWaveOutAudioOutputStream::Open() { | 122 bool PCMWaveOutAudioOutputStream::Open() { |
115 if (state_ != PCMA_BRAND_NEW) | 123 if (state_ != PCMA_BRAND_NEW) |
116 return false; | 124 return false; |
117 if (BufferSize() * num_buffers_ > kMaxOpenBufferSize) | |
118 return false; | |
119 if (num_buffers_ < 2 || num_buffers_ > 5) | 125 if (num_buffers_ < 2 || num_buffers_ > 5) |
120 return false; | 126 return false; |
121 | 127 // Open the device. We'll be getting callback in WaveCallback function. |
122 // Create buffer event. | 128 // They occur in a magic, time-critical thread that windows creates. |
123 buffer_event_.Set(::CreateEvent(NULL, // Security attributes | 129 MMRESULT result = ::waveOutOpen(&waveout_, device_id_, |
124 FALSE, // It will auto-reset | 130 reinterpret_cast<LPCWAVEFORMATEX>(&format_), |
125 FALSE, // Initial state | 131 reinterpret_cast<DWORD_PTR>(WaveCallback), |
126 NULL // No name | 132 reinterpret_cast<DWORD_PTR>(this), |
127 )); | 133 CALLBACK_FUNCTION); |
128 if (!buffer_event_.Get()) { | |
129 return false; | |
130 } | |
131 | |
132 // Open the device. | |
133 // We'll be getting buffer_event_ events when it's time to refill the buffer. | |
134 MMRESULT result = ::waveOutOpen( | |
135 &waveout_, | |
136 device_id_, | |
137 reinterpret_cast<LPCWAVEFORMATEX>(&format_), | |
138 reinterpret_cast<DWORD_PTR>(buffer_event_.Get()), | |
139 NULL, | |
140 CALLBACK_EVENT); | |
141 if (result != MMSYSERR_NOERROR) | 134 if (result != MMSYSERR_NOERROR) |
142 return false; | 135 return false; |
143 | 136 |
144 SetupBuffers(); | 137 SetupBuffers(); |
145 state_ = PCMA_READY; | 138 state_ = PCMA_READY; |
146 return true; | 139 return true; |
147 } | 140 } |
148 | 141 |
149 void PCMWaveOutAudioOutputStream::SetupBuffers() { | 142 void PCMWaveOutAudioOutputStream::SetupBuffers() { |
150 buffers_.reset(new char[BufferSize() * num_buffers_]); | 143 WAVEHDR* last = NULL; |
| 144 WAVEHDR* first = NULL; |
151 for (int ix = 0; ix != num_buffers_; ++ix) { | 145 for (int ix = 0; ix != num_buffers_; ++ix) { |
152 WAVEHDR* buffer = GetBuffer(ix); | 146 uint32 sz = sizeof(WAVEHDR) + buffer_size_; |
153 buffer->lpData = reinterpret_cast<char*>(buffer) + sizeof(WAVEHDR); | 147 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); |
154 buffer->dwBufferLength = buffer_size_; | 148 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); |
155 buffer->dwBytesRecorded = 0; | 149 buffer_->dwBufferLength = buffer_size_; |
156 buffer->dwFlags = WHDR_DONE; | 150 buffer_->dwBytesRecorded = 0; |
157 buffer->dwLoops = 0; | 151 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); |
| 152 buffer_->dwFlags = WHDR_DONE; |
| 153 buffer_->dwLoops = 0; |
| 154 if (ix == 0) |
| 155 first = buffer_; |
| 156 last = buffer_; |
158 // Tell windows sound drivers about our buffers. Not documented what | 157 // Tell windows sound drivers about our buffers. Not documented what |
159 // this does but we can guess that causes the OS to keep a reference to | 158 // this does but we can guess that causes the OS to keep a reference to |
160 // the memory pages so the driver can use them without worries. | 159 // the memory pages so the driver can use them without worries. |
161 ::waveOutPrepareHeader(waveout_, buffer, sizeof(WAVEHDR)); | 160 ::waveOutPrepareHeader(waveout_, buffer_, sizeof(WAVEHDR)); |
162 } | 161 } |
| 162 // Fix the first buffer to point to the last one. |
| 163 first->dwUser = reinterpret_cast<DWORD_PTR>(last); |
163 } | 164 } |
164 | 165 |
165 void PCMWaveOutAudioOutputStream::FreeBuffers() { | 166 void PCMWaveOutAudioOutputStream::FreeBuffers() { |
| 167 WAVEHDR* current = buffer_; |
166 for (int ix = 0; ix != num_buffers_; ++ix) { | 168 for (int ix = 0; ix != num_buffers_; ++ix) { |
167 ::waveOutUnprepareHeader(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); | 169 WAVEHDR* next = GetNextBuffer(current); |
| 170 ::waveOutUnprepareHeader(waveout_, current, sizeof(WAVEHDR)); |
| 171 delete[] reinterpret_cast<char*>(current); |
| 172 current = next; |
168 } | 173 } |
169 buffers_.reset(NULL); | 174 buffer_ = NULL; |
170 } | 175 } |
171 | 176 |
172 // Initially we ask the source to fill up all audio buffers. If we don't do | 177 // Initially we ask the source to fill up both audio buffers. If we don't do |
173 // this then we would always get the driver callback when it is about to run | 178 // this then we would always get the driver callback when it is about to run |
174 // samples and that would leave too little time to react. | 179 // samples and that would leave too little time to react. |
175 void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { | 180 void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { |
176 if (state_ != PCMA_READY) | 181 if (state_ != PCMA_READY) |
177 return; | 182 return; |
178 callback_ = callback; | 183 callback_ = callback; |
179 | |
180 // Start watching for buffer events. | |
181 { | |
182 HANDLE waiting_handle = NULL; | |
183 ::RegisterWaitForSingleObject(&waiting_handle, | |
184 buffer_event_.Get(), | |
185 &BufferCallback, | |
186 static_cast<void*>(this), | |
187 INFINITE, | |
188 WT_EXECUTEDEFAULT); | |
189 if (!waiting_handle) { | |
190 HandleError(MMSYSERR_ERROR); | |
191 return; | |
192 } | |
193 waiting_handle_.Set(waiting_handle); | |
194 } | |
195 | |
196 state_ = PCMA_PLAYING; | 184 state_ = PCMA_PLAYING; |
197 | |
198 // Queue the buffers. | |
199 pending_bytes_ = 0; | 185 pending_bytes_ = 0; |
| 186 WAVEHDR* buffer = buffer_; |
200 for (int ix = 0; ix != num_buffers_; ++ix) { | 187 for (int ix = 0; ix != num_buffers_; ++ix) { |
201 WAVEHDR* buffer = GetBuffer(ix); | |
202 // Caller waits for 1st packet to become available, but not for others, | 188 // Caller waits for 1st packet to become available, but not for others, |
203 // so we wait for them here. | 189 // so we wait for them here. |
204 if (ix != 0) | 190 if (ix != 0) |
205 callback_->WaitTillDataReady(); | 191 callback_->WaitTillDataReady(); |
206 QueueNextPacket(buffer); // Read more data. | 192 QueueNextPacket(buffer); // Read more data. |
207 pending_bytes_ += buffer->dwBufferLength; | 193 pending_bytes_ += buffer->dwBufferLength; |
| 194 buffer = GetNextBuffer(buffer); |
208 } | 195 } |
| 196 buffer = buffer_; |
209 | 197 |
210 // From now on |pending_bytes_| would be accessed by callback thread. | 198 // From now on |pending_bytes_| would be accessed by callback thread. |
211 // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, | 199 // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, |
212 // but issuing our own is safer. | 200 // but issuing our own is safer. |
213 MemoryBarrier(); | 201 MemoryBarrier(); |
214 | 202 |
215 MMRESULT result = ::waveOutPause(waveout_); | 203 MMRESULT result = ::waveOutPause(waveout_); |
216 if (result != MMSYSERR_NOERROR) { | 204 if (result != MMSYSERR_NOERROR) { |
217 HandleError(result); | 205 HandleError(result); |
218 return; | 206 return; |
219 } | 207 } |
220 | 208 |
221 // Send the buffers to the audio driver. Note that the device is paused | 209 // Send the buffers to the audio driver. Note that the device is paused |
222 // so we avoid entering the callback method while still here. | 210 // so we avoid entering the callback method while still here. |
223 for (int ix = 0; ix != num_buffers_; ++ix) { | 211 for (int ix = 0; ix != num_buffers_; ++ix) { |
224 result = ::waveOutWrite(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); | 212 result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); |
225 if (result != MMSYSERR_NOERROR) { | 213 if (result != MMSYSERR_NOERROR) { |
226 HandleError(result); | 214 HandleError(result); |
227 break; | 215 break; |
228 } | 216 } |
| 217 buffer = GetNextBuffer(buffer); |
229 } | 218 } |
230 result = ::waveOutRestart(waveout_); | 219 result = ::waveOutRestart(waveout_); |
231 if (result != MMSYSERR_NOERROR) { | 220 if (result != MMSYSERR_NOERROR) { |
232 HandleError(result); | 221 HandleError(result); |
233 return; | 222 return; |
234 } | 223 } |
235 } | 224 } |
236 | 225 |
237 // Stopping is tricky if we want it be fast. | 226 // Stopping is tricky. First, no buffer should be locked by the audio driver |
238 // For now just do it synchronously and avoid all the complexities. | 227 // or else the waveOutReset() will deadlock and secondly, the callback should |
239 // TODO(enal): if we want faster Stop() we can create singleton that keeps track | 228 // not be inside the AudioSource's OnMoreData because waveOutReset() forcefully |
240 // of all currently playing streams. Then you don't have to wait | 229 // kills the callback thread after releasing all buffers. |
241 // till all callbacks are completed. Of course access to singleton | |
242 // should be under its own lock, and checking the liveness and | |
243 // acquiring the lock on stream should be done atomically. | |
244 void PCMWaveOutAudioOutputStream::Stop() { | 230 void PCMWaveOutAudioOutputStream::Stop() { |
245 if (state_ != PCMA_PLAYING) | 231 if (state_ != PCMA_PLAYING) |
246 return; | 232 return; |
247 state_ = PCMA_STOPPING; | |
248 MemoryBarrier(); | |
249 | 233 |
250 // Stop playback. | 234 // Enter into critical section and call ::waveOutReset(). The fact that we |
| 235 // entered critical section means that callback is out of critical section and |
| 236 // it is safe to reset. |
| 237 ::EnterCriticalSection(&lock_); |
251 MMRESULT res = ::waveOutReset(waveout_); | 238 MMRESULT res = ::waveOutReset(waveout_); |
| 239 ::LeaveCriticalSection(&lock_); |
252 if (res != MMSYSERR_NOERROR) { | 240 if (res != MMSYSERR_NOERROR) { |
253 state_ = PCMA_PLAYING; | |
254 HandleError(res); | 241 HandleError(res); |
255 return; | 242 return; |
256 } | 243 } |
257 | 244 |
258 // Stop watching for buffer event, wait till all the callbacks are complete. | |
259 BOOL unregister = UnregisterWaitEx(waiting_handle_.Take(), | |
260 INVALID_HANDLE_VALUE); | |
261 if (!unregister) { | |
262 state_ = PCMA_PLAYING; | |
263 HandleError(MMSYSERR_ERROR); | |
264 return; | |
265 } | |
266 | |
267 // waveOutReset() leaves buffers in the unpredictable state, causing | |
268 // problems if we want to release or reuse them. Fix the states. | |
269 for (int ix = 0; ix != num_buffers_; ++ix) { | |
270 GetBuffer(ix)->dwFlags = WHDR_PREPARED; | |
271 } | |
272 | |
273 // Don't use callback after Stop(). | 245 // Don't use callback after Stop(). |
274 callback_ = NULL; | 246 callback_ = NULL; |
275 | 247 |
276 state_ = PCMA_READY; | 248 state_ = PCMA_READY; |
277 } | 249 } |
278 | 250 |
279 // We can Close in any state except that trying to close a stream that is | 251 // We can Close in any state except that trying to close a stream that is |
280 // playing Windows generates an error, which we propagate to the source. | 252 // playing Windows generates an error, which we propagate to the source. |
281 void PCMWaveOutAudioOutputStream::Close() { | 253 void PCMWaveOutAudioOutputStream::Close() { |
282 Stop(); // Just to be sure. No-op if not playing. | |
283 if (waveout_) { | 254 if (waveout_) { |
| 255 // waveOutClose generates a callback with WOM_CLOSE id in the same thread. |
284 MMRESULT res = ::waveOutClose(waveout_); | 256 MMRESULT res = ::waveOutClose(waveout_); |
285 if (res != MMSYSERR_NOERROR) { | 257 if (res != MMSYSERR_NOERROR) { |
286 HandleError(res); | 258 HandleError(res); |
287 return; | 259 return; |
288 } | 260 } |
289 state_ = PCMA_CLOSED; | 261 state_ = PCMA_CLOSED; |
290 waveout_ = NULL; | 262 waveout_ = NULL; |
291 FreeBuffers(); | 263 FreeBuffers(); |
292 } | 264 } |
293 // Tell the audio manager that we have been released. This can result in | 265 // Tell the audio manager that we have been released. This can result in |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 format_.Format.wBitsPerSample >> 3, | 309 format_.Format.wBitsPerSample >> 3, |
338 volume_); | 310 volume_); |
339 } | 311 } |
340 } else { | 312 } else { |
341 HandleError(0); | 313 HandleError(0); |
342 return; | 314 return; |
343 } | 315 } |
344 buffer->dwFlags = WHDR_PREPARED; | 316 buffer->dwFlags = WHDR_PREPARED; |
345 } | 317 } |
346 | 318 |
347 // One of the threads in our thread pool asynchronously calls this function when | 319 // Windows call us back in this function when some events happen. Most notably |
348 // buffer_event_ is signalled. Search through all the buffers looking for freed | 320 // when it is done playing a buffer. Since we use double buffering it is |
349 // ones, fills them with data, and "feed" the Windows. | 321 // convenient to think of |buffer| as free and GetNextBuffer(buffer) as in |
350 // Note: by searching through all the buffers we guarantee that we fill all the | 322 // use by the driver. |
351 // buffers, even when "event loss" happens, i.e. if Windows signals event | 323 void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, |
352 // when it did not flip into unsignaled state from the previous signal. | 324 DWORD_PTR instance, |
353 void NTAPI PCMWaveOutAudioOutputStream::BufferCallback(PVOID lpParameter, | 325 DWORD_PTR param1, DWORD_PTR) { |
354 BOOLEAN) { | 326 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::WaveCallback"); |
355 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::BufferCallback"); | |
356 | 327 |
357 PCMWaveOutAudioOutputStream* stream = | 328 if (msg == WOM_DONE) { |
358 reinterpret_cast<PCMWaveOutAudioOutputStream*>(lpParameter); | 329 // WOM_DONE indicates that the driver is done with our buffer, we can |
| 330 // either ask the source for more data or check if we need to stop playing. |
| 331 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); |
| 332 buffer->dwFlags = WHDR_DONE; |
359 | 333 |
360 // Lock the stream so 2 callbacks do not interfere with each other. | 334 PCMWaveOutAudioOutputStream* stream = |
361 // Two callbacks can be called simultaneously by 2 different threads in the | 335 reinterpret_cast<PCMWaveOutAudioOutputStream*>(instance); |
362 // thread pool if one of the callbacks is slow, or system is very busy and | |
363 // one of scheduled callbacks is not called on time. | |
364 base::AutoLock auto_lock(stream->lock_); | |
365 if (stream->state_ != PCMA_PLAYING) | |
366 return; | |
367 | 336 |
368 for (int ix = 0; ix != stream->num_buffers_; ++ix) { | 337 // Do real work only if main thread has not yet called waveOutReset(). |
369 WAVEHDR* buffer = stream->GetBuffer(ix); | 338 if (::TryEnterCriticalSection(&stream->lock_)) { |
370 if (buffer->dwFlags & WHDR_DONE) { | |
371 // Before we queue the next packet, we need to adjust the number of | 339 // Before we queue the next packet, we need to adjust the number of |
372 // pending bytes since the last write to hardware. | 340 // pending bytes since the last write to hardware. |
373 stream->pending_bytes_ -= buffer->dwBufferLength; | 341 stream->pending_bytes_ -= buffer->dwBufferLength; |
| 342 |
374 stream->QueueNextPacket(buffer); | 343 stream->QueueNextPacket(buffer); |
375 | 344 |
376 // QueueNextPacket() can take a long time, especially if several of them | |
377 // were called back-to-back. Check if we are stopping now. | |
378 if (stream->state_ != PCMA_PLAYING) | |
379 return; | |
380 | |
381 // Time to send the buffer to the audio driver. Since we are reusing | 345 // Time to send the buffer to the audio driver. Since we are reusing |
382 // the same buffers we can get away without calling waveOutPrepareHeader. | 346 // the same buffers we can get away without calling waveOutPrepareHeader. |
383 MMRESULT result = ::waveOutWrite(stream->waveout_, | 347 MMRESULT result = ::waveOutWrite(hwo, buffer, sizeof(WAVEHDR)); |
384 buffer, | |
385 sizeof(WAVEHDR)); | |
386 if (result != MMSYSERR_NOERROR) | 348 if (result != MMSYSERR_NOERROR) |
387 stream->HandleError(result); | 349 stream->HandleError(result); |
| 350 |
388 stream->pending_bytes_ += buffer->dwBufferLength; | 351 stream->pending_bytes_ += buffer->dwBufferLength; |
| 352 ::LeaveCriticalSection(&stream->lock_); |
389 } | 353 } |
390 } | 354 } |
391 } | 355 } |
OLD | NEW |