]>
Commit | Line | Data |
---|---|---|
e8482f24 GL |
1 | // -------------------------------------------------------------------------- |
2 | // Name: sndwin.cpp | |
3 | // Purpose: | |
4 | // Date: 08/11/1999 | |
5 | // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000 | |
6 | // CVSID: $Id$ | |
58b9c9ba | 7 | // wxWindows licence |
e8482f24 GL |
8 | // -------------------------------------------------------------------------- |
9 | #ifdef __GNUG__ | |
10 | #pragma implementation "sndwin.cpp" | |
11 | #endif | |
12 | ||
13 | #include "wx/wxprec.h" | |
14 | ||
15 | #ifdef __WINDOWS__ | |
16 | ||
17 | #ifndef WX_PRECOMP | |
18 | #include "wx/defs.h" | |
19 | #include "wx/app.h" | |
e8482f24 GL |
20 | #include "wx/string.h" |
21 | #endif | |
22 | ||
59917a74 JS |
23 | #include "wx/module.h" |
24 | #include "wx/msw/private.h" | |
25 | ||
e8482f24 GL |
26 | // ------------------------------------------------------------------------- |
27 | // MMedia headers | |
28 | // ------------------------------------------------------------------------- | |
29 | ||
30 | #include "wx/mmedia/sndbase.h" | |
31 | #include "wx/mmedia/sndwin.h" | |
32 | #include "wx/mmedia/sndpcm.h" | |
33 | ||
34 | // ------------------------------------------------------------------------- | |
35 | // System headers | |
36 | // ------------------------------------------------------------------------- | |
37 | ||
38 | #include <windows.h> | |
39 | #include <mmsystem.h> | |
40 | ||
41 | // ------------------------------------------------------------------------- | |
42 | // External definitions, forward, ... | |
43 | // ------------------------------------------------------------------------- | |
44 | ||
45 | typedef struct _wxSoundInternal wxSoundInternal; | |
46 | typedef struct _wxSoundInfoHeader wxSoundInfoHeader; | |
47 | ||
48 | extern const wxChar *wxCanvasClassName; | |
49 | ||
50 | wxList *wxSoundHandleList = NULL; | |
51 | ||
52 | static inline wxSoundStreamWin *wxFindSoundFromHandle(WXHWND hWnd) | |
53 | { | |
5e0dbc8d | 54 | wxObjectList::compatibility_iterator node = wxSoundHandleList->Find((long)hWnd); |
e8482f24 GL |
55 | if (!node) |
56 | return NULL; | |
2b3644c7 | 57 | return (wxSoundStreamWin *)node->GetData(); |
e8482f24 GL |
58 | } |
59 | ||
60 | struct _wxSoundInternal { | |
61 | HWND m_sndWin; | |
62 | HWAVEIN m_devin; | |
63 | HWAVEOUT m_devout; | |
64 | bool m_output_enabled, m_input_enabled; | |
65 | }; | |
66 | ||
67 | struct _wxSoundInfoHeader { | |
68 | HGLOBAL m_h_header, m_h_data; | |
69 | char *m_data; | |
70 | WAVEHDR *m_header; | |
71 | int m_mode; | |
72 | bool m_playing, m_recording; | |
73 | wxUint32 m_position, m_size; | |
74 | ||
75 | wxSoundStreamWin *m_driver; | |
76 | }; | |
77 | ||
78 | #define WXSOUND_MAX_QUEUE 10 | |
79 | ||
80 | wxSoundStreamWin::wxSoundStreamWin() | |
81 | { | |
82 | wxSoundFormatPcm pcm; | |
83 | ||
dea7e44a | 84 | m_production_started = false; |
e8482f24 GL |
85 | m_internal = new wxSoundInternal; |
86 | if (!m_internal) { | |
87 | m_snderror = wxSOUND_MEMERROR; | |
88 | m_internal = NULL; | |
89 | return; | |
90 | } | |
91 | m_snderror = wxSOUND_NOERROR; | |
92 | ||
93 | // Setup defaults | |
94 | CreateSndWindow(); | |
95 | SetSoundFormat(pcm); | |
96 | ||
dea7e44a WS |
97 | m_internal->m_input_enabled = false; |
98 | m_internal->m_output_enabled = false; | |
e8482f24 | 99 | |
dea7e44a | 100 | m_waiting_for = false; |
e8482f24 | 101 | |
8c6f3b9c GL |
102 | if (!OpenDevice(wxSOUND_OUTPUT)) { |
103 | m_snderror = wxSOUND_NOERROR; //next call to OpenDevice won't do this | |
104 | if (!OpenDevice(wxSOUND_INPUT)) | |
105 | return; | |
106 | } | |
e8482f24 GL |
107 | |
108 | CloseDevice(); | |
109 | } | |
110 | ||
111 | wxSoundStreamWin::~wxSoundStreamWin() | |
112 | { | |
113 | if (m_internal) { | |
114 | if (m_production_started) | |
115 | StopProduction(); | |
116 | DestroySndWindow(); | |
117 | ||
118 | delete m_internal; | |
119 | } | |
120 | } | |
121 | ||
122 | // ----------------------------------------------------------------------- | |
123 | // _wxSoundHandlerWndProc: Window callback to handle buffer completion | |
124 | // ----------------------------------------------------------------------- | |
15e8daec | 125 | LRESULT APIENTRY _EXPORT |
59917a74 JS |
126 | |
127 | _wxSoundHandlerWndProc(HWND hWnd, UINT message, | |
42c37dec | 128 | WPARAM wParam, LPARAM WXUNUSED(lParam)) |
e8482f24 GL |
129 | { |
130 | wxSoundStreamWin *sndwin; | |
131 | ||
132 | sndwin = wxFindSoundFromHandle((WXHWND)hWnd); | |
133 | if (!sndwin) | |
134 | return (LRESULT)0; | |
135 | ||
136 | switch (message) { | |
137 | case MM_WOM_DONE: | |
138 | sndwin->NotifyDoneBuffer(wParam, wxSOUND_OUTPUT); | |
139 | break; | |
140 | case MM_WIM_DATA: | |
141 | sndwin->NotifyDoneBuffer(wParam, wxSOUND_INPUT); | |
142 | break; | |
143 | default: | |
144 | break; | |
145 | } | |
146 | return (LRESULT)0; | |
147 | } | |
148 | ||
149 | // ----------------------------------------------------------------------- | |
150 | // CreateSndWindow() creates an hidden window which will receive the sound | |
151 | // events | |
152 | // ----------------------------------------------------------------------- | |
153 | ||
154 | void wxSoundStreamWin::CreateSndWindow() | |
155 | { | |
156 | FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc, | |
157 | wxGetInstance()); | |
15e8daec VS |
158 | // NB: class name must be kept in sync with wxCanvasClassName in |
159 | // src/msw/app.cpp! | |
160 | m_internal->m_sndWin = ::CreateWindow(wxT("wxWindowClass"), NULL, 0, | |
dea7e44a | 161 | 0, 0, 0, 0, NULL, (HMENU) NULL, |
e8482f24 GL |
162 | wxGetInstance(), NULL); |
163 | ||
42c37dec | 164 | GetLastError(); |
e8482f24 GL |
165 | |
166 | ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc); | |
167 | ||
168 | // Add this window to the sound handle list so we'll be able to redecode | |
169 | // the "magic" number. | |
170 | wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this); | |
171 | } | |
172 | ||
173 | // ----------------------------------------------------------------------- | |
174 | // DestroySndWindow() destroys the hidden window | |
175 | // ----------------------------------------------------------------------- | |
176 | ||
177 | void wxSoundStreamWin::DestroySndWindow() | |
178 | { | |
179 | if (m_internal->m_sndWin) { | |
180 | ::DestroyWindow(m_internal->m_sndWin); | |
181 | wxSoundHandleList->DeleteObject((wxObject *)this); | |
182 | } | |
183 | } | |
184 | ||
185 | // ------------------------------------------------------------------------- | |
186 | // OpenDevice(int mode) initializes the windows driver for a "mode" | |
187 | // operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set, | |
188 | // the driver is opened for output operation, and if the bit "wxSOUND_INPUT" | |
189 | // is set, then the driver is opened for input operation. The two modes | |
190 | // aren't exclusive. | |
191 | // The initialization parameters (sample rate, ...) are taken from the | |
192 | // m_sndformat object. | |
193 | // At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO | |
194 | // queue. | |
195 | // ------------------------------------------------------------------------- | |
196 | bool wxSoundStreamWin::OpenDevice(int mode) | |
197 | { | |
198 | wxSoundFormatPcm *pcm; | |
199 | WAVEFORMATEX wformat; | |
200 | ||
201 | if (!m_sndformat) { | |
202 | m_snderror = wxSOUND_INVFRMT; | |
dea7e44a | 203 | return false; |
e8482f24 GL |
204 | } |
205 | ||
206 | pcm = (wxSoundFormatPcm *)m_sndformat; | |
207 | ||
208 | wformat.wFormatTag = WAVE_FORMAT_PCM; | |
209 | wformat.nChannels = pcm->GetChannels(); | |
210 | wformat.nBlockAlign = wformat.nChannels * pcm->GetBPS() / 8; | |
211 | wformat.nSamplesPerSec = pcm->GetSampleRate(); | |
212 | wformat.nAvgBytesPerSec = wformat.nSamplesPerSec * wformat.nBlockAlign; | |
213 | wformat.wBitsPerSample = pcm->GetBPS(); | |
214 | wformat.cbSize = 0; | |
215 | ||
216 | // ----------------------------------- | |
217 | // Open the driver for Output operation | |
218 | // ----------------------------------- | |
219 | if (mode & wxSOUND_OUTPUT) { | |
220 | MMRESULT result; | |
221 | ||
222 | result = waveOutOpen(&m_internal->m_devout, | |
223 | WAVE_MAPPER, &wformat, | |
224 | (DWORD)m_internal->m_sndWin, 0, | |
225 | CALLBACK_WINDOW); | |
226 | ||
227 | if (result != MMSYSERR_NOERROR) { | |
228 | m_snderror = wxSOUND_INVDEV; | |
dea7e44a | 229 | return false; |
e8482f24 GL |
230 | } |
231 | ||
232 | m_output_frag_out = WXSOUND_MAX_QUEUE-1; | |
233 | m_current_frag_out = 0; | |
234 | ||
dea7e44a | 235 | m_internal->m_output_enabled = true; |
e8482f24 GL |
236 | } |
237 | // ----------------------------------- | |
238 | // Open the driver for Input operation | |
239 | // ----------------------------------- | |
240 | if (mode & wxSOUND_INPUT) { | |
241 | MMRESULT result; | |
242 | ||
243 | result = waveInOpen(&m_internal->m_devin, | |
244 | WAVE_MAPPER, &wformat, | |
245 | (DWORD)m_internal->m_sndWin, 0, | |
246 | CALLBACK_WINDOW); | |
247 | ||
248 | if (result != MMSYSERR_NOERROR) { | |
249 | m_snderror = wxSOUND_INVDEV; | |
dea7e44a | 250 | return false; |
e8482f24 GL |
251 | } |
252 | ||
253 | m_current_frag_in = WXSOUND_MAX_QUEUE-1; | |
254 | m_input_frag_in = 0; | |
255 | ||
dea7e44a | 256 | m_internal->m_input_enabled = true; |
e8482f24 GL |
257 | } |
258 | ||
259 | if (mode & wxSOUND_OUTPUT) { | |
260 | if (!AllocHeaders(wxSOUND_OUTPUT)) { | |
261 | CloseDevice(); | |
dea7e44a | 262 | return false; |
e8482f24 GL |
263 | } |
264 | } | |
265 | if (mode & wxSOUND_INPUT) { | |
266 | if (!AllocHeaders(wxSOUND_INPUT)) { | |
267 | CloseDevice(); | |
dea7e44a | 268 | return false; |
e8482f24 GL |
269 | } |
270 | } | |
271 | ||
dea7e44a | 272 | return true; |
e8482f24 GL |
273 | } |
274 | ||
275 | // ------------------------------------------------------------------------- | |
276 | // CloseDevice() closes the driver handles and frees memory allocated for | |
277 | // IO queues. | |
278 | // ------------------------------------------------------------------------- | |
279 | void wxSoundStreamWin::CloseDevice() | |
280 | { | |
281 | if (m_internal->m_output_enabled) { | |
282 | FreeHeaders(wxSOUND_OUTPUT); | |
dea7e44a | 283 | m_internal->m_output_enabled = false; |
e8482f24 GL |
284 | waveOutClose(m_internal->m_devout); |
285 | } | |
286 | ||
287 | if (m_internal->m_input_enabled) { | |
288 | FreeHeaders(wxSOUND_INPUT); | |
dea7e44a | 289 | m_internal->m_input_enabled = false; |
e8482f24 GL |
290 | waveInClose(m_internal->m_devin); |
291 | } | |
292 | } | |
293 | ||
294 | // ------------------------------------------------------------------------- | |
295 | // AllocHeader(int mode) | |
296 | // | |
297 | // mode has the same mean as in OpenDevice() except that here the two flags | |
298 | // must be exclusive. | |
299 | // AllocHeader() initializes an element of an operation (this can be input | |
300 | // or output). It means it allocates the sound header's memory block | |
301 | // and "prepares" it (It is needed by Windows). At the same time, it sets | |
302 | // private datas so we can the header's owner (See callback). | |
303 | // | |
304 | // It returns the new allocated block or NULL. | |
305 | // ------------------------------------------------------------------------- | |
306 | wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode) | |
307 | { | |
308 | wxSoundInfoHeader *info; | |
309 | WAVEHDR *header; | |
310 | ||
311 | // Some memory allocation | |
312 | info = new wxSoundInfoHeader; | |
313 | info->m_h_data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize()); | |
314 | info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR)); | |
315 | if (!info->m_h_data || !info->m_h_header) { | |
316 | delete info; | |
59917a74 | 317 | m_snderror = wxSOUND_MEMERROR; |
e8482f24 GL |
318 | return NULL; |
319 | } | |
320 | ||
321 | // Get the two pointers from the system | |
322 | info->m_data = (char *)GlobalLock(info->m_h_data); | |
323 | info->m_header = (WAVEHDR *)GlobalLock(info->m_h_header); | |
324 | // Set the header's mode | |
325 | info->m_mode = mode; | |
326 | // Set the parent of the header | |
327 | info->m_driver = this; | |
328 | // Clean it up | |
329 | ClearHeader(info); | |
330 | ||
331 | header = info->m_header; | |
332 | // Initialize Windows variables | |
333 | header->lpData = info->m_data; | |
334 | header->dwBufferLength = GetBestSize(); | |
335 | header->dwUser = (DWORD)info; | |
336 | header->dwFlags = WHDR_DONE; | |
337 | ||
338 | // "Prepare" the header | |
339 | if (mode == wxSOUND_INPUT) { | |
340 | MMRESULT result; | |
341 | ||
342 | result = waveInPrepareHeader(m_internal->m_devin, header, | |
343 | sizeof(WAVEHDR)); | |
344 | ||
345 | if (result != MMSYSERR_NOERROR) { | |
346 | // If something goes wrong, free everything. | |
347 | GlobalUnlock(info->m_data); | |
348 | GlobalUnlock(info->m_header); | |
349 | GlobalFree(info->m_h_data); | |
350 | GlobalFree(info->m_h_header); | |
351 | delete info; | |
352 | ||
353 | m_snderror = wxSOUND_IOERROR; | |
354 | return NULL; | |
355 | } | |
356 | } else if (mode == wxSOUND_OUTPUT) { | |
357 | MMRESULT result; | |
358 | ||
359 | result = waveOutPrepareHeader(m_internal->m_devout, header, | |
360 | sizeof(WAVEHDR)); | |
361 | ||
362 | if (result != MMSYSERR_NOERROR) { | |
363 | // If something goes wrong, free everything. | |
364 | GlobalUnlock(info->m_data); | |
365 | GlobalUnlock(info->m_header); | |
366 | GlobalFree(info->m_h_data); | |
367 | GlobalFree(info->m_h_header); | |
368 | delete info; | |
369 | ||
370 | m_snderror = wxSOUND_IOERROR; | |
371 | return NULL; | |
372 | } | |
373 | } | |
374 | return info; | |
375 | } | |
376 | ||
377 | // ------------------------------------------------------------------------- | |
378 | // AllocHeaders(int mode) | |
379 | // | |
380 | // "mode" has the same mean as for OpenDevice() except that the two flags must | |
381 | // be exclusive. | |
382 | // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation | |
383 | // queue. It uses AllocHeader() for each element. | |
384 | // | |
dea7e44a WS |
385 | // Once it has allocated all blocks, it returns true and if an error occured |
386 | // it returns false. | |
e8482f24 GL |
387 | // ------------------------------------------------------------------------- |
388 | bool wxSoundStreamWin::AllocHeaders(int mode) | |
389 | { | |
390 | int i; | |
391 | wxSoundInfoHeader **headers; | |
392 | ||
393 | if (mode == wxSOUND_OUTPUT) | |
394 | headers = m_headers_play = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE]; | |
395 | else | |
396 | headers = m_headers_rec = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE]; | |
397 | ||
398 | memset(headers, 0, WXSOUND_MAX_QUEUE*sizeof(wxSoundInfoHeader *)); | |
399 | ||
400 | for (i=0;i<WXSOUND_MAX_QUEUE;i++) { | |
401 | headers[i] = AllocHeader(mode); | |
402 | if (!headers[i]) { | |
403 | FreeHeaders(mode); | |
dea7e44a | 404 | return false; |
e8482f24 GL |
405 | } |
406 | } | |
dea7e44a | 407 | return true; |
e8482f24 GL |
408 | } |
409 | ||
410 | // ------------------------------------------------------------------------- | |
411 | // FreeHeader(int mode) | |
412 | // | |
413 | // "mode" has the same mean as for OpenDevice() except that the two flags must | |
414 | // be exclusive. | |
415 | // FreeHeader() frees a memory block and "unprepares" it. | |
416 | // ------------------------------------------------------------------------- | |
417 | void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode) | |
418 | { | |
419 | if (mode == wxSOUND_OUTPUT) | |
420 | waveOutUnprepareHeader(m_internal->m_devout, header->m_header, sizeof(WAVEHDR)); | |
421 | else | |
422 | waveInUnprepareHeader(m_internal->m_devin, header->m_header, sizeof(WAVEHDR)); | |
423 | ||
424 | GlobalUnlock(header->m_data); | |
425 | GlobalUnlock(header->m_header); | |
426 | GlobalFree(header->m_h_header); | |
427 | GlobalFree(header->m_h_data); | |
428 | delete header; | |
429 | } | |
430 | ||
431 | // ------------------------------------------------------------------------- | |
432 | // FreeHeaders(int mode) | |
433 | // | |
434 | // "mode" has the same mean as for OpenDevice() except that the two flags must | |
435 | // be exclusive. | |
436 | // FreeHeaders() frees all an operation queue once it has checked that | |
437 | // all buffers have been terminated. | |
438 | // ------------------------------------------------------------------------- | |
439 | void wxSoundStreamWin::FreeHeaders(int mode) | |
440 | { | |
441 | int i; | |
442 | wxSoundInfoHeader ***headers; | |
443 | ||
444 | if (mode == wxSOUND_OUTPUT) | |
445 | headers = &m_headers_play; | |
446 | else | |
447 | headers = &m_headers_rec; | |
448 | ||
449 | for (i=0;i<WXSOUND_MAX_QUEUE;i++) { | |
450 | if ((*headers)[i]) { | |
451 | // We wait for the end of the buffer | |
452 | WaitFor((*headers)[i]); | |
453 | // Then, we free the header | |
454 | FreeHeader((*headers)[i], mode); | |
455 | } | |
456 | } | |
457 | delete[] (*headers); | |
458 | (*headers) = NULL; | |
459 | } | |
460 | ||
461 | // ------------------------------------------------------------------------- | |
462 | // WaitFor(wxSoundInfoHeader *info) | |
463 | // | |
464 | // "info" is one element of an IO queue | |
465 | // WaitFor() checks whether the specified block has been terminated. | |
466 | // If it hasn't been terminated, it waits for its termination. | |
467 | // | |
468 | // NB: if it's a partially filled buffer it adds it to the Windows queue | |
469 | // ------------------------------------------------------------------------- | |
470 | void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info) | |
471 | { | |
472 | // If the buffer is finished, we return immediately | |
473 | if (!info->m_playing) { | |
474 | ||
475 | // We begun filling it: we must send it to the Windows queue | |
476 | if (info->m_position != 0) { | |
477 | memset(info->m_data + info->m_position, 0, info->m_size); | |
478 | AddToQueue(info); | |
479 | } | |
480 | } | |
481 | ||
482 | if (m_waiting_for) { | |
483 | // PROBLEM // | |
484 | return; | |
485 | } | |
dea7e44a | 486 | m_waiting_for = true; |
e8482f24 GL |
487 | // Else, we wait for its termination |
488 | while (info->m_playing || info->m_recording) | |
489 | wxYield(); | |
dea7e44a | 490 | m_waiting_for = false; |
e8482f24 GL |
491 | } |
492 | ||
493 | // ------------------------------------------------------------------------- | |
494 | // AddToQueue(wxSoundInfoHeader *info) | |
495 | // | |
496 | // For "info", see WaitFor() | |
497 | // AddToQueue() sends the IO queue element to the Windows queue. | |
498 | // | |
499 | // Warning: in the current implementation, it partially assume we send the | |
500 | // element in the right order. This is true in that implementation but if | |
501 | // you use it elsewhere, be careful: it may shuffle all your sound datas. | |
502 | // ------------------------------------------------------------------------- | |
503 | bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info) | |
504 | { | |
505 | MMRESULT result; | |
506 | ||
507 | if (info->m_mode == wxSOUND_INPUT) { | |
508 | // Increment the input fragment pointer | |
509 | result = waveInAddBuffer(m_internal->m_devin, | |
510 | info->m_header, sizeof(WAVEHDR)); | |
511 | if (result == MMSYSERR_NOERROR) | |
dea7e44a | 512 | info->m_recording = true; |
e8482f24 | 513 | else |
dea7e44a | 514 | return false; |
e8482f24 GL |
515 | } else if (info->m_mode == wxSOUND_OUTPUT) { |
516 | result = waveOutWrite(m_internal->m_devout, | |
517 | info->m_header, sizeof(WAVEHDR)); | |
518 | if (result == MMSYSERR_NOERROR) | |
dea7e44a | 519 | info->m_playing = true; |
e8482f24 | 520 | else |
dea7e44a | 521 | return false; |
e8482f24 | 522 | } |
dea7e44a | 523 | return true; |
e8482f24 GL |
524 | } |
525 | ||
526 | // ------------------------------------------------------------------------- | |
527 | // ClearHeader(wxSoundInfoHeader *info) | |
528 | // | |
529 | // ClearHeader() reinitializes the parameters of "info" to their default | |
530 | // value. | |
531 | // ------------------------------------------------------------------------- | |
532 | void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info) | |
533 | { | |
dea7e44a WS |
534 | info->m_playing = false; |
535 | info->m_recording = false; | |
e8482f24 GL |
536 | info->m_position = 0; |
537 | info->m_size = GetBestSize(); | |
538 | } | |
539 | ||
540 | // ------------------------------------------------------------------------- | |
541 | // wxSoundInfoHeader *NextFragmentOutput() | |
542 | // | |
543 | // NextFragmentOutput() looks for a free output block. It will always | |
544 | // return you a non-NULL pointer but it may waits for an empty buffer a long | |
545 | // time. | |
546 | // ------------------------------------------------------------------------- | |
547 | wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput() | |
548 | { | |
549 | if (m_headers_play[m_current_frag_out]->m_playing) { | |
550 | m_current_frag_out = (m_current_frag_out + 1) % WXSOUND_MAX_QUEUE; | |
551 | ||
552 | if (m_headers_play[m_current_frag_out]->m_playing) | |
553 | WaitFor(m_headers_play[m_current_frag_out]); | |
554 | } | |
555 | if (m_current_frag_out == m_output_frag_out) | |
dea7e44a | 556 | m_queue_filled = true; |
e8482f24 GL |
557 | return m_headers_play[m_current_frag_out]; |
558 | } | |
559 | ||
560 | // ------------------------------------------------------------------------- | |
561 | // The behaviour of Write is documented in the global documentation. | |
562 | // ------------------------------------------------------------------------- | |
563 | wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len) | |
564 | { | |
565 | m_lastcount = 0; | |
566 | if (!m_internal->m_output_enabled) { | |
567 | m_snderror = wxSOUND_NOTSTARTED; | |
568 | return *this; | |
569 | } | |
570 | ||
571 | ||
572 | while (len > 0) { | |
573 | wxSoundInfoHeader *header; | |
574 | wxUint32 to_copy; | |
575 | ||
576 | // Get a new output fragment | |
577 | header = NextFragmentOutput(); | |
578 | ||
579 | to_copy = (len > header->m_size) ? header->m_size : len; | |
580 | memcpy(header->m_data + header->m_position, buffer, to_copy); | |
581 | ||
582 | header->m_position += to_copy; | |
583 | header->m_size -= to_copy; | |
584 | buffer = (((const char *)buffer) + to_copy); | |
585 | len -= to_copy; | |
586 | m_lastcount += to_copy; | |
587 | ||
588 | // If the fragment is full, we send it to the Windows queue. | |
589 | if (header->m_size == 0) | |
590 | if (!AddToQueue(header)) { | |
591 | m_snderror = wxSOUND_IOERROR; | |
592 | return *this; | |
593 | } | |
594 | } | |
595 | return *this; | |
596 | } | |
597 | ||
598 | // ------------------------------------------------------------------------- | |
599 | // NextFragmentInput is not functional. | |
600 | // ------------------------------------------------------------------------- | |
601 | wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput() | |
602 | { | |
603 | wxSoundInfoHeader *header; | |
604 | ||
605 | // Queue pointer: reader | |
606 | m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE; | |
607 | ||
608 | header = m_headers_rec[m_current_frag_in]; | |
609 | // If the current buffer is in recording mode, we must wait for its | |
610 | // completion. | |
611 | if (header->m_recording) | |
612 | WaitFor(header); | |
613 | ||
614 | // We reached the writer position: the queue is full. | |
615 | if (m_current_frag_in == m_input_frag_in) | |
dea7e44a | 616 | m_queue_filled = true; |
e8482f24 GL |
617 | |
618 | return header; | |
619 | } | |
620 | ||
621 | // ------------------------------------------------------------------------- | |
622 | // The behaviour of Read is documented in the global documentation. | |
623 | // ------------------------------------------------------------------------- | |
624 | wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len) | |
625 | { | |
626 | wxSoundInfoHeader *header; | |
627 | wxUint32 to_copy; | |
628 | ||
629 | m_lastcount = 0; | |
630 | if (!m_internal->m_input_enabled) | |
631 | return *this; | |
632 | ||
633 | while (len > 0) { | |
634 | header = NextFragmentInput(); | |
635 | ||
636 | to_copy = (len > header->m_size) ? header->m_size : len; | |
637 | memcpy(buffer, header->m_data + header->m_position, to_copy); | |
638 | ||
639 | header->m_position += to_copy; | |
640 | header->m_size -= to_copy; | |
641 | buffer = (((char *)buffer) + to_copy); | |
642 | len -= to_copy; | |
643 | m_lastcount += to_copy; | |
644 | ||
645 | if (header->m_size == 0) { | |
646 | ClearHeader(header); | |
647 | if (!AddToQueue(header)) { | |
648 | m_snderror = wxSOUND_IOERROR; | |
649 | return *this; | |
650 | } | |
651 | } | |
652 | } | |
653 | return *this; | |
654 | } | |
655 | ||
656 | // ------------------------------------------------------------------------- | |
657 | // NotifyDoneBuffer(wxUint32 dev_handle) | |
658 | // | |
659 | // NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound | |
660 | // fragment finished. It reinitializes the parameters of the fragment and | |
661 | // sends an event to the clients. | |
662 | // ------------------------------------------------------------------------- | |
42c37dec | 663 | void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 WXUNUSED(dev_handle), int flag) |
e8482f24 GL |
664 | { |
665 | wxSoundInfoHeader *info; | |
666 | ||
667 | if (flag == wxSOUND_OUTPUT) { | |
668 | if (!m_internal->m_output_enabled) | |
669 | return; | |
670 | ||
671 | // Queue pointer: reader | |
672 | m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE; | |
673 | info = m_headers_play[m_output_frag_out]; | |
674 | // Clear header to tell the system the buffer is free now | |
675 | ClearHeader(info); | |
dea7e44a | 676 | m_queue_filled = false; |
e8482f24 GL |
677 | if (!m_waiting_for) |
678 | // Try to requeue a new buffer. | |
679 | OnSoundEvent(wxSOUND_OUTPUT); | |
680 | } else { | |
681 | if (!m_internal->m_input_enabled) | |
682 | return; | |
683 | ||
684 | // Recording completed | |
dea7e44a | 685 | m_headers_rec[m_input_frag_in]->m_recording = false; |
e8482f24 GL |
686 | // Queue pointer: writer |
687 | m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE; | |
688 | if (!m_waiting_for) | |
689 | OnSoundEvent(wxSOUND_INPUT); | |
dea7e44a | 690 | m_queue_filled = false; |
e8482f24 GL |
691 | } |
692 | } | |
693 | ||
694 | // ------------------------------------------------------------------------- | |
695 | // SetSoundFormat() | |
696 | // ------------------------------------------------------------------------- | |
2bbf230a | 697 | bool wxSoundStreamWin::SetSoundFormat(const wxSoundFormatBase& base) |
e8482f24 GL |
698 | { |
699 | // TODO: detect best format | |
700 | return wxSoundStream::SetSoundFormat(base); | |
701 | } | |
702 | ||
703 | // ------------------------------------------------------------------------- | |
704 | // StartProduction() | |
705 | // ------------------------------------------------------------------------- | |
706 | bool wxSoundStreamWin::StartProduction(int evt) | |
707 | { | |
708 | if (!m_internal) | |
dea7e44a | 709 | return false; |
e8482f24 GL |
710 | |
711 | if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) || | |
712 | (m_internal->m_input_enabled && (evt & wxSOUND_INPUT))) | |
713 | CloseDevice(); | |
714 | ||
715 | if (!OpenDevice(evt)) | |
dea7e44a | 716 | return false; |
e8482f24 | 717 | |
dea7e44a WS |
718 | m_production_started = true; |
719 | m_queue_filled = false; | |
e8482f24 GL |
720 | // Send a dummy event to start. |
721 | if (evt & wxSOUND_OUTPUT) | |
722 | OnSoundEvent(wxSOUND_OUTPUT); | |
723 | ||
724 | if (evt & wxSOUND_INPUT) { | |
725 | int i; | |
726 | for (i=0;i<WXSOUND_MAX_QUEUE;i++) | |
727 | AddToQueue(m_headers_rec[i]); | |
728 | ||
729 | waveInStart(m_internal->m_devin); | |
730 | } | |
731 | ||
dea7e44a | 732 | return true; |
e8482f24 GL |
733 | } |
734 | ||
735 | // ------------------------------------------------------------------------- | |
736 | // StopProduction() | |
737 | // ------------------------------------------------------------------------ | |
738 | bool wxSoundStreamWin::StopProduction() | |
739 | { | |
740 | if (!m_production_started) { | |
741 | m_snderror = wxSOUND_NOTSTARTED; | |
dea7e44a | 742 | return false; |
e8482f24 GL |
743 | } |
744 | ||
745 | m_snderror = wxSOUND_NOERROR; | |
dea7e44a | 746 | m_production_started = false; |
e8482f24 | 747 | CloseDevice(); |
dea7e44a | 748 | return true; |
e8482f24 GL |
749 | } |
750 | ||
751 | // ------------------------------------------------------------------------- | |
752 | // QueueFilled() | |
753 | // ------------------------------------------------------------------------- | |
754 | bool wxSoundStreamWin::QueueFilled() const | |
755 | { | |
756 | return (!m_production_started || m_queue_filled); | |
757 | } | |
758 | ||
759 | ||
760 | // -------------------------------------------------------------------------- | |
761 | // wxSoundWinModule | |
762 | // -------------------------------------------------------------------------- | |
763 | ||
15e8daec | 764 | class wxSoundWinModule : public wxModule { |
e8482f24 GL |
765 | DECLARE_DYNAMIC_CLASS(wxSoundWinModule) |
766 | public: | |
767 | bool OnInit(); | |
768 | void OnExit(); | |
769 | }; | |
770 | ||
771 | IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule, wxModule) | |
772 | ||
773 | bool wxSoundWinModule::OnInit() { | |
774 | wxSoundHandleList = new wxList(wxKEY_INTEGER); | |
dea7e44a | 775 | return true; |
e8482f24 GL |
776 | } |
777 | ||
778 | void wxSoundWinModule::OnExit() { | |
779 | delete wxSoundHandleList; | |
780 | } | |
781 | ||
782 | #endif | |
783 | // __WINDOWS__ |