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/at_exit.h" | |
11 #include "base/basictypes.h" | 12 #include "base/basictypes.h" |
13 #include "base/bind.h" | |
12 #include "base/debug/trace_event.h" | 14 #include "base/debug/trace_event.h" |
13 #include "base/logging.h" | 15 #include "base/logging.h" |
14 #include "media/audio/audio_io.h" | 16 #include "media/audio/audio_io.h" |
15 #include "media/audio/audio_util.h" | 17 #include "media/audio/audio_util.h" |
16 #include "media/audio/win/audio_manager_win.h" | 18 #include "media/audio/win/audio_manager_win.h" |
17 | 19 |
18 // Number of times InitializeCriticalSectionAndSpinCount() spins | 20 base::Lock PCMWaveOutAudioOutputStream::g_lock_; |
19 // before going to sleep. | 21 volatile DWORD PCMWaveOutAudioOutputStream::g_ordinal_; |
20 const DWORD kSpinCount = 2000; | 22 scoped_ptr<std::map<PCMWaveOutAudioOutputStream*, DWORD> > |
23 PCMWaveOutAudioOutputStream::g_streams_map_; | |
tommi (sloooow) - chröme
2011/11/18 09:53:21
All these objects should belong to one singleton o
enal1
2011/11/19 00:59:19
Done.
| |
21 | 24 |
22 // Some general thoughts about the waveOut API which is badly documented : | 25 // Some general thoughts about the waveOut API which is badly documented : |
23 // - We use CALLBACK_FUNCTION mode in which XP secretly creates two threads | 26 // - We use CALLBACK_FUNCTION mode in which XP secretly creates two threads |
24 // named _MixerCallbackThread and _waveThread which have real-time priority. | 27 // named _MixerCallbackThread and _waveThread which have real-time priority. |
25 // The callbacks occur in _waveThread. | 28 // The callbacks occur in _waveThread. |
26 // - Windows does not provide a way to query if the device is playing or paused | 29 // - 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 | 30 // thus it forces you to maintain state, which naturally is not exactly |
28 // synchronized to the actual device state. | 31 // synchronized to the actual device state. |
29 // - Some functions, like waveOutReset cannot be called in the callback thread | 32 // - 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 | 33 // or called in any random state because they deadlock. This results in a |
31 // non- instantaneous Stop() method. waveOutPrepareHeader seems to be in the | 34 // non- instantaneous Stop() method. waveOutWrite() and waveOutPrepareHeader |
32 // same boat. | 35 // seem to be in the same boat. |
33 // - waveOutReset() will forcefully kill the _waveThread so it is important | 36 // - We have to use separate "feeder" thread that calls waveOutWrite() to feed |
34 // to make sure we are not executing inside the audio source's OnMoreData() | 37 // buffers to driver, cannot do it from the callback. |
35 // or that we take locks inside WaveCallback() or QueueNextPacket(). | 38 // - Stopping is tricky, see comment below. |
36 | |
37 // Sixty four MB is the maximum buffer size per AudioOutputStream. | |
38 static const uint32 kMaxOpenBufferSize = 1024 * 1024 * 64; | |
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 | 39 |
46 // See Also | 40 // See Also |
47 // http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-sp eaker-set-up/ | 41 // http://www.thx.com/consumer/home-entertainment/home-theater/surround-sound-sp eaker-set-up/ |
48 // http://en.wikipedia.org/wiki/Surround_sound | 42 // http://en.wikipedia.org/wiki/Surround_sound |
49 | 43 |
50 static const int kMaxChannelsToMask = 8; | 44 static const int kMaxChannelsToMask = 8; |
51 static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { | 45 static const unsigned int kChannelsToMask[kMaxChannelsToMask + 1] = { |
52 0, | 46 0, |
53 // 1 = Mono | 47 // 1 = Mono |
54 SPEAKER_FRONT_CENTER, | 48 SPEAKER_FRONT_CENTER, |
(...skipping 26 matching lines...) Expand all Loading... | |
81 | 75 |
82 PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( | 76 PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( |
83 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, | 77 AudioManagerWin* manager, const AudioParameters& params, int num_buffers, |
84 UINT device_id) | 78 UINT device_id) |
85 : state_(PCMA_BRAND_NEW), | 79 : state_(PCMA_BRAND_NEW), |
86 manager_(manager), | 80 manager_(manager), |
87 device_id_(device_id), | 81 device_id_(device_id), |
88 waveout_(NULL), | 82 waveout_(NULL), |
89 callback_(NULL), | 83 callback_(NULL), |
90 num_buffers_(num_buffers), | 84 num_buffers_(num_buffers), |
91 buffer_(NULL), | 85 buffers_(NULL), |
92 buffer_size_(params.GetPacketSize()), | 86 buffer_size_(params.GetPacketSize()), |
93 volume_(1), | 87 volume_(1), |
94 channels_(params.channels), | 88 channels_(params.channels), |
95 pending_bytes_(0) { | 89 pending_bytes_(0), |
96 ::InitializeCriticalSectionAndSpinCount(&lock_, kSpinCount); | 90 ordinal_(0) { |
91 InitializeGlobalsIfNecessary(); | |
97 | 92 |
98 format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; | 93 format_.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; |
99 format_.Format.nChannels = params.channels; | 94 format_.Format.nChannels = params.channels; |
100 format_.Format.nSamplesPerSec = params.sample_rate; | 95 format_.Format.nSamplesPerSec = params.sample_rate; |
101 format_.Format.wBitsPerSample = params.bits_per_sample; | 96 format_.Format.wBitsPerSample = params.bits_per_sample; |
102 format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); | 97 format_.Format.cbSize = sizeof(format_) - sizeof(WAVEFORMATEX); |
103 // The next are computed from above. | 98 // The next are computed from above. |
104 format_.Format.nBlockAlign = (format_.Format.nChannels * | 99 format_.Format.nBlockAlign = (format_.Format.nChannels * |
105 format_.Format.wBitsPerSample) / 8; | 100 format_.Format.wBitsPerSample) / 8; |
106 format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * | 101 format_.Format.nAvgBytesPerSec = format_.Format.nBlockAlign * |
107 format_.Format.nSamplesPerSec; | 102 format_.Format.nSamplesPerSec; |
108 if (params.channels > kMaxChannelsToMask) { | 103 if (params.channels > kMaxChannelsToMask) { |
109 format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; | 104 format_.dwChannelMask = kChannelsToMask[kMaxChannelsToMask]; |
110 } else { | 105 } else { |
111 format_.dwChannelMask = kChannelsToMask[params.channels]; | 106 format_.dwChannelMask = kChannelsToMask[params.channels]; |
112 } | 107 } |
113 format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; | 108 format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; |
114 format_.Samples.wValidBitsPerSample = params.bits_per_sample; | 109 format_.Samples.wValidBitsPerSample = params.bits_per_sample; |
115 } | 110 } |
116 | 111 |
117 PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { | 112 PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { |
118 DCHECK(NULL == waveout_); | 113 DCHECK(NULL == waveout_); |
119 ::DeleteCriticalSection(&lock_); | |
120 } | 114 } |
121 | 115 |
122 bool PCMWaveOutAudioOutputStream::Open() { | 116 bool PCMWaveOutAudioOutputStream::Open() { |
123 if (state_ != PCMA_BRAND_NEW) | 117 if (state_ != PCMA_BRAND_NEW) |
124 return false; | 118 return false; |
125 if (num_buffers_ < 2 || num_buffers_ > 5) | 119 if (num_buffers_ < 2 || num_buffers_ > 5) |
126 return false; | 120 return false; |
127 // Open the device. We'll be getting callback in WaveCallback function. | 121 // Open the device. We'll be getting callback in WaveCallback function. |
128 // They occur in a magic, time-critical thread that windows creates. | 122 // They occur in a magic, time-critical thread that windows creates. |
129 MMRESULT result = ::waveOutOpen(&waveout_, device_id_, | 123 MMRESULT result = ::waveOutOpen(&waveout_, device_id_, |
130 reinterpret_cast<LPCWAVEFORMATEX>(&format_), | 124 reinterpret_cast<LPCWAVEFORMATEX>(&format_), |
131 reinterpret_cast<DWORD_PTR>(WaveCallback), | 125 reinterpret_cast<DWORD_PTR>(WaveCallback), |
132 reinterpret_cast<DWORD_PTR>(this), | 126 reinterpret_cast<DWORD_PTR>(this), |
133 CALLBACK_FUNCTION); | 127 CALLBACK_FUNCTION); |
134 if (result != MMSYSERR_NOERROR) | 128 if (result != MMSYSERR_NOERROR) |
135 return false; | 129 return false; |
136 | 130 |
137 SetupBuffers(); | 131 SetupBuffers(); |
138 state_ = PCMA_READY; | 132 state_ = PCMA_READY; |
139 return true; | 133 return true; |
140 } | 134 } |
141 | 135 |
142 void PCMWaveOutAudioOutputStream::SetupBuffers() { | 136 void PCMWaveOutAudioOutputStream::SetupBuffers() { |
143 WAVEHDR* last = NULL; | 137 buffers_ = new char[CbBuffer() * num_buffers_]; |
144 WAVEHDR* first = NULL; | |
145 for (int ix = 0; ix != num_buffers_; ++ix) { | 138 for (int ix = 0; ix != num_buffers_; ++ix) { |
146 uint32 sz = sizeof(WAVEHDR) + buffer_size_; | 139 WAVEHDR *buffer = GetBuffer(ix); |
147 buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); | 140 buffer->lpData = reinterpret_cast<char*>(buffer) + sizeof(WAVEHDR); |
148 buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); | 141 buffer->dwBufferLength = buffer_size_; |
149 buffer_->dwBufferLength = buffer_size_; | 142 buffer->dwBytesRecorded = 0; |
150 buffer_->dwBytesRecorded = 0; | 143 buffer->dwUser = 0; |
151 buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); | 144 buffer->dwFlags = WHDR_DONE; |
152 buffer_->dwFlags = WHDR_DONE; | 145 buffer->dwLoops = 0; |
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 | 146 // 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 | 147 // 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. | 148 // the memory pages so the driver can use them without worries. |
160 ::waveOutPrepareHeader(waveout_, buffer_, sizeof(WAVEHDR)); | 149 ::waveOutPrepareHeader(waveout_, buffer, sizeof(WAVEHDR)); |
161 } | 150 } |
162 // Fix the first buffer to point to the last one. | |
163 first->dwUser = reinterpret_cast<DWORD_PTR>(last); | |
164 } | 151 } |
165 | 152 |
166 void PCMWaveOutAudioOutputStream::FreeBuffers() { | 153 void PCMWaveOutAudioOutputStream::FreeBuffers() { |
167 WAVEHDR* current = buffer_; | |
168 for (int ix = 0; ix != num_buffers_; ++ix) { | 154 for (int ix = 0; ix != num_buffers_; ++ix) { |
169 WAVEHDR* next = GetNextBuffer(current); | 155 ::waveOutUnprepareHeader(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); |
170 ::waveOutUnprepareHeader(waveout_, current, sizeof(WAVEHDR)); | |
171 delete[] reinterpret_cast<char*>(current); | |
172 current = next; | |
173 } | 156 } |
174 buffer_ = NULL; | 157 delete[] buffers_; |
tommi (sloooow) - chröme
2011/11/18 09:53:21
use scoped_array?
enal1
2011/11/19 00:59:19
Done.
| |
175 } | 158 } |
176 | 159 |
177 // Initially we ask the source to fill up both audio buffers. If we don't do | 160 // 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 | 161 // 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. | 162 // samples and that would leave too little time to react. |
180 void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { | 163 void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { |
181 if (state_ != PCMA_READY) | 164 if (state_ != PCMA_READY) |
182 return; | 165 return; |
183 callback_ = callback; | 166 callback_ = callback; |
184 state_ = PCMA_PLAYING; | 167 state_ = PCMA_PLAYING; |
168 | |
169 // Place "this" into map. Do it under global lock. | |
170 { | |
171 base::AutoLock auto_lock(g_lock_); | |
172 ordinal_ = g_ordinal_; | |
173 ++g_ordinal_; | |
174 DCHECK(g_streams_map_->find(this) == g_streams_map_->end()); | |
175 (*g_streams_map_)[this] = ordinal_; | |
176 } | |
177 | |
178 // Queue the buffers. | |
179 // TODO(enal): If there are more than 2, queue only first 2 and schedule | |
180 // remaining to be filled in the "feeder" thread. Non-trivial because we have | |
181 // to be sure that data is ready. Can be done by storing time of last | |
182 // OnMoreData() call and scheduling delayed task in FeedBuffer() if data is | |
183 // not ready yet. | |
185 pending_bytes_ = 0; | 184 pending_bytes_ = 0; |
186 WAVEHDR* buffer = buffer_; | |
187 for (int ix = 0; ix != num_buffers_; ++ix) { | 185 for (int ix = 0; ix != num_buffers_; ++ix) { |
186 WAVEHDR* buffer = GetBuffer(ix); | |
187 buffer->dwUser = ordinal_; | |
188 // 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, |
189 // so we wait for them here. | 189 // so we wait for them here. |
190 if (ix != 0) | 190 if (ix != 0) |
191 callback_->WaitTillDataReady(); | 191 callback_->WaitTillDataReady(); |
192 QueueNextPacket(buffer); // Read more data. | 192 QueueNextPacket(buffer); // Read more data. |
193 pending_bytes_ += buffer->dwBufferLength; | 193 pending_bytes_ += buffer->dwBufferLength; |
194 buffer = GetNextBuffer(buffer); | |
195 } | 194 } |
196 buffer = buffer_; | |
197 | 195 |
198 // From now on |pending_bytes_| would be accessed by callback thread. | 196 // From now on |pending_bytes_| would be accessed by callback thread. |
199 // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, | 197 // Most likely waveOutPause() or waveOutRestart() has its own memory barrier, |
200 // but issuing our own is safer. | 198 // but issuing our own is safer. |
201 MemoryBarrier(); | 199 MemoryBarrier(); |
202 | 200 |
203 MMRESULT result = ::waveOutPause(waveout_); | 201 MMRESULT result = ::waveOutPause(waveout_); |
204 if (result != MMSYSERR_NOERROR) { | 202 if (result != MMSYSERR_NOERROR) { |
205 HandleError(result); | 203 HandleError(result); |
206 return; | 204 return; |
207 } | 205 } |
208 | 206 |
209 // Send the buffers to the audio driver. Note that the device is paused | 207 // Send the buffers to the audio driver. Note that the device is paused |
210 // so we avoid entering the callback method while still here. | 208 // so we avoid entering the callback method while still here. |
211 for (int ix = 0; ix != num_buffers_; ++ix) { | 209 for (int ix = 0; ix != num_buffers_; ++ix) { |
212 result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); | 210 result = ::waveOutWrite(waveout_, GetBuffer(ix), sizeof(WAVEHDR)); |
213 if (result != MMSYSERR_NOERROR) { | 211 if (result != MMSYSERR_NOERROR) { |
214 HandleError(result); | 212 HandleError(result); |
215 break; | 213 break; |
216 } | 214 } |
217 buffer = GetNextBuffer(buffer); | |
218 } | 215 } |
219 result = ::waveOutRestart(waveout_); | 216 result = ::waveOutRestart(waveout_); |
220 if (result != MMSYSERR_NOERROR) { | 217 if (result != MMSYSERR_NOERROR) { |
221 HandleError(result); | 218 HandleError(result); |
222 return; | 219 return; |
223 } | 220 } |
224 } | 221 } |
225 | 222 |
226 // Stopping is tricky. First, no buffer should be locked by the audio driver | 223 // Stopping is tricky. We want to avoid stopping while feeder thread feeds |
227 // or else the waveOutReset() will deadlock and secondly, the callback should | 224 // buffer to driver, so |
228 // not be inside the AudioSource's OnMoreData because waveOutReset() forcefully | 225 // * Here: |
229 // kills the callback thread after releasing all buffers. | 226 // (a) get object lock |
tommi (sloooow) - chröme
2011/11/18 09:53:21
why do we need the object lock while we hold the o
enal1
2011/11/19 00:59:19
Got rid of other lock. Done.
| |
227 // (b) get global lock | |
228 // (c) remove object from map | |
229 // (d) release both locks | |
230 // ...do real stopping... | |
231 // * In feeder thread: | |
232 // (e) get global lock | |
233 // (f) if object is not in the map, exit | |
234 // (g) try getting object lock; if fail, exit | |
235 // (h) release global lock | |
236 // ...do real feeding... | |
237 // There is no way for object to be alive in (f) but stopped/deleted in (g). | |
238 // For object to be stopped between (f) and (g), Stop() should acquire global | |
239 // lock, but it is already acquired by feeder. So it is safe to touch object | |
240 // lock at (g)... | |
230 void PCMWaveOutAudioOutputStream::Stop() { | 241 void PCMWaveOutAudioOutputStream::Stop() { |
231 if (state_ != PCMA_PLAYING) | 242 if (state_ != PCMA_PLAYING) |
232 return; | 243 return; |
244 state_ = PCMA_STOPPING; | |
245 MMRESULT res = MMSYSERR_NOERROR; | |
246 { | |
247 base::AutoLock auto_lock(lock_); | |
233 | 248 |
234 // Enter into critical section and call ::waveOutReset(). The fact that we | 249 // Remove us from the map. |
235 // entered critical section means that callback is out of critical section and | 250 { |
236 // it is safe to reset. | 251 base::AutoLock auto_lock(g_lock_); |
237 ::EnterCriticalSection(&lock_); | 252 g_streams_map_->erase(this); |
238 MMRESULT res = ::waveOutReset(waveout_); | 253 } |
239 ::LeaveCriticalSection(&lock_); | 254 res = ::waveOutReset(waveout_); |
255 } | |
240 if (res != MMSYSERR_NOERROR) { | 256 if (res != MMSYSERR_NOERROR) { |
241 HandleError(res); | 257 HandleError(res); |
242 return; | 258 return; |
243 } | 259 } |
tommi (sloooow) - chröme
2011/11/18 09:53:21
it's ok to keep the empty line below this one imo.
enal1
2011/11/19 00:59:19
Done.
| |
244 | |
245 // Don't use callback after Stop(). | 260 // Don't use callback after Stop(). |
246 callback_ = NULL; | 261 callback_ = NULL; |
247 | |
248 state_ = PCMA_READY; | 262 state_ = PCMA_READY; |
249 } | 263 } |
250 | 264 |
251 // We can Close in any state except that trying to close a stream that is | 265 // 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. | 266 // playing Windows generates an error, which we propagate to the source. |
253 void PCMWaveOutAudioOutputStream::Close() { | 267 void PCMWaveOutAudioOutputStream::Close() { |
254 if (waveout_) { | 268 if (waveout_) { |
255 // waveOutClose generates a callback with WOM_CLOSE id in the same thread. | 269 // waveOutClose generates a callback with WOM_CLOSE id in the same thread. |
256 MMRESULT res = ::waveOutClose(waveout_); | 270 MMRESULT res = ::waveOutClose(waveout_); |
257 if (res != MMSYSERR_NOERROR) { | 271 if (res != MMSYSERR_NOERROR) { |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
309 format_.Format.wBitsPerSample >> 3, | 323 format_.Format.wBitsPerSample >> 3, |
310 volume_); | 324 volume_); |
311 } | 325 } |
312 } else { | 326 } else { |
313 HandleError(0); | 327 HandleError(0); |
314 return; | 328 return; |
315 } | 329 } |
316 buffer->dwFlags = WHDR_PREPARED; | 330 buffer->dwFlags = WHDR_PREPARED; |
317 } | 331 } |
318 | 332 |
333 void PCMWaveOutAudioOutputStream::FeedBuffer( | |
334 PCMWaveOutAudioOutputStream* stream, | |
335 WAVEHDR* buffer, | |
336 HWAVEOUT hwo, | |
337 DWORD ordinal) { | |
338 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::FeedBuffer"); | |
339 // Is stream still alive and playing? Check is tricky. | |
340 // See comment in PCMWaveOutAudioOutputStream::Stop() explaining what | |
341 // exactly happens. | |
tommi (sloooow) - chröme
2011/11/18 09:53:21
Are we doing this check to avoid touching an objec
enal1
2011/11/19 00:59:19
Yes, we had very complicated checking to avoid tou
| |
342 bool stopped = true; | |
343 { | |
344 base::AutoLock auto_lock(g_lock_); | |
345 std::map<PCMWaveOutAudioOutputStream*, DWORD>::iterator it = | |
346 g_streams_map_->find(stream); | |
347 stopped = (it == g_streams_map_->end() || | |
348 it->second != ordinal || | |
349 !stream->lock_.Try()); | |
tommi (sloooow) - chröme
2011/11/18 09:53:21
Maybe add a comment that here is where you acquire
enal1
2011/11/19 00:59:19
Code totally rewritten.
| |
350 } | |
351 if (stopped) | |
tommi (sloooow) - chröme
2011/11/18 09:53:21
what about checking the state_ variable for != PCM
enal1
2011/11/19 00:59:19
Again, could not do that because object could be d
| |
352 return; | |
353 | |
354 // Before we queue the next packet, we need to adjust the number of | |
355 // pending bytes since the last write to hardware. | |
356 stream->pending_bytes_ -= buffer->dwBufferLength; | |
357 | |
358 stream->QueueNextPacket(buffer); | |
359 | |
360 // Time to send the buffer to the audio driver. Since we are reusing | |
361 // the same buffers we can get away without calling waveOutPrepareHeader. | |
362 MMRESULT result = ::waveOutWrite(hwo, buffer, sizeof(WAVEHDR)); | |
363 if (result != MMSYSERR_NOERROR) | |
364 stream->HandleError(result); | |
365 | |
366 stream->pending_bytes_ += buffer->dwBufferLength; | |
367 stream->lock_.Release(); | |
368 } | |
369 | |
319 // Windows call us back in this function when some events happen. Most notably | 370 // Windows call us back in this function when some events happen. Most notably |
320 // when it is done playing a buffer. Since we use double buffering it is | 371 // when it is done playing a buffer. Cannot feed freed buffer back to the system |
321 // convenient to think of |buffer| as free and GetNextBuffer(buffer) as in | 372 // from here, have to schedule it for the separate therad. |
tommi (sloooow) - chröme
2011/11/18 09:53:21
therad -> thread
enal1
2011/11/19 00:59:19
Done.
| |
322 // use by the driver. | |
323 void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, | 373 void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, |
324 DWORD_PTR instance, | 374 DWORD_PTR instance, |
325 DWORD_PTR param1, DWORD_PTR) { | 375 DWORD_PTR param1, DWORD_PTR) { |
326 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::WaveCallback"); | 376 TRACE_EVENT0("audio", "PCMWaveOutAudioOutputStream::WaveCallback"); |
327 | 377 |
328 if (msg == WOM_DONE) { | 378 if (msg == WOM_DONE) { |
329 // WOM_DONE indicates that the driver is done with our buffer, we can | 379 // WOM_DONE indicates that the driver is done with our buffer, |
330 // either ask the source for more data or check if we need to stop playing. | 380 // if still playing ask "feedef" thread to buffer more data. |
331 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); | 381 WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); |
332 buffer->dwFlags = WHDR_DONE; | 382 buffer->dwFlags = WHDR_DONE; |
333 | 383 |
334 PCMWaveOutAudioOutputStream* stream = | 384 PCMWaveOutAudioOutputStream* stream = |
335 reinterpret_cast<PCMWaveOutAudioOutputStream*>(instance); | 385 reinterpret_cast<PCMWaveOutAudioOutputStream*>(instance); |
336 | 386 |
337 // Do real work only if main thread has not yet called waveOutReset(). | 387 if (stream->state_ == PCMA_PLAYING) { |
338 if (::TryEnterCriticalSection(&stream->lock_)) { | 388 stream->manager_->GetMessageLoop()->PostTask( |
339 // Before we queue the next packet, we need to adjust the number of | 389 FROM_HERE, |
340 // pending bytes since the last write to hardware. | 390 base::Bind(&PCMWaveOutAudioOutputStream::FeedBuffer, |
341 stream->pending_bytes_ -= buffer->dwBufferLength; | 391 stream, |
342 | 392 buffer, |
343 stream->QueueNextPacket(buffer); | 393 hwo, |
344 | 394 buffer->dwUser)); |
345 // Time to send the buffer to the audio driver. Since we are reusing | |
346 // the same buffers we can get away without calling waveOutPrepareHeader. | |
347 MMRESULT result = ::waveOutWrite(hwo, buffer, sizeof(WAVEHDR)); | |
348 if (result != MMSYSERR_NOERROR) | |
349 stream->HandleError(result); | |
350 | |
351 stream->pending_bytes_ += buffer->dwBufferLength; | |
352 ::LeaveCriticalSection(&stream->lock_); | |
353 } | 395 } |
354 } | 396 } |
355 } | 397 } |
398 // Initialize globals, if necessary. Use double-checked locking, | |
399 // it is safe on Windows as long as you are checking volatile variable. | |
400 // static | |
401 void PCMWaveOutAudioOutputStream::InitializeGlobalsIfNecessary() { | |
402 if (g_ordinal_ == 0) { | |
403 base::AutoLock auto_lock(g_lock_); | |
404 if (g_ordinal_ == 0) { | |
405 g_streams_map_.reset(new std::map<PCMWaveOutAudioOutputStream*, DWORD>); | |
406 base::AtExitManager::RegisterCallback( | |
407 &PCMWaveOutAudioOutputStream::CleanupGlobals, NULL); | |
408 // Volatile stores have release semantics, no separate memory | |
409 // barrier necessary. | |
410 g_ordinal_ = 1; | |
411 } | |
412 } | |
413 } | |
414 | |
415 // static | |
416 void PCMWaveOutAudioOutputStream::CleanupGlobals(void* not_used) { | |
417 g_streams_map_.reset(NULL); | |
418 } | |
OLD | NEW |