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