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