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