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