]> git.saurik.com Git - wxWidgets.git/blob - utils/wxMMedia2/lib/sndwin.cpp
dd459ddfb3835f9306435d5a73f0ab1a5c265cb5
[wxWidgets.git] / utils / wxMMedia2 / lib / sndwin.cpp
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 #include <wx/wxprec.h>
9
10 #include <wx/app.h>
11 #include <wx/module.h>
12 #include <wx/msw/private.h>
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
24 extern const wxChar *wxCanvasClassName;
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
54 #define WXSOUND_MAX_QUEUE 10
55
56 wxSoundStreamWin::wxSoundStreamWin()
57 {
58 wxSoundFormatPcm pcm;
59
60 m_production_started = FALSE;
61 m_internal = new wxSoundInternal;
62 if (!m_internal) {
63 m_snderror = wxSOUND_MEMERROR;
64 m_internal = NULL;
65 return;
66 }
67 m_snderror = wxSOUND_NOERROR;
68
69 // Setup defaults
70 CreateSndWindow();
71 SetSoundFormat(pcm);
72
73 m_internal->m_input_enabled = FALSE;
74 m_internal->m_output_enabled = FALSE;
75
76 m_waiting_for = FALSE;
77
78 if (!OpenDevice(wxSOUND_OUTPUT))
79 return;
80
81 CloseDevice();
82 }
83
84 wxSoundStreamWin::~wxSoundStreamWin()
85 {
86 if (m_internal) {
87 if (m_production_started)
88 StopProduction();
89 DestroySndWindow();
90
91 delete m_internal;
92 }
93 }
94
95 // -----------------------------------------------------------------------
96 // _wxSoundHandlerWndProc: Window callback to handle buffer completion
97 // -----------------------------------------------------------------------
98 LRESULT APIENTRY _EXPORT _wxSoundHandlerWndProc(HWND hWnd, UINT message,
99 WPARAM wParam, LPARAM lParam)
100 {
101 wxSoundStreamWin *sndwin;
102
103 sndwin = wxFindSoundFromHandle((WXHWND)hWnd);
104 if (!sndwin)
105 return (LRESULT)0;
106
107 switch (message) {
108 case MM_WOM_DONE:
109 sndwin->NotifyDoneBuffer(wParam, wxSOUND_OUTPUT);
110 break;
111 case MM_WIM_DATA:
112 sndwin->NotifyDoneBuffer(wParam, wxSOUND_INPUT);
113 break;
114 default:
115 break;
116 }
117 return (LRESULT)0;
118 }
119
120 // -----------------------------------------------------------------------
121 // CreateSndWindow() creates an hidden window which will receive the sound
122 // events
123 // -----------------------------------------------------------------------
124
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();
136
137 ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
138
139 // Add this window to the sound handle list so we'll be able to redecode
140 // the "magic" number.
141 wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this);
142 }
143
144 // -----------------------------------------------------------------------
145 // DestroySndWindow() destroys the hidden window
146 // -----------------------------------------------------------------------
147
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
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 // -------------------------------------------------------------------------
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();
181 wformat.nBlockAlign = wformat.nChannels * pcm->GetBPS() / 8;
182 wformat.nSamplesPerSec = pcm->GetSampleRate();
183 wformat.nAvgBytesPerSec = wformat.nSamplesPerSec * wformat.nBlockAlign;
184 wformat.wBitsPerSample = pcm->GetBPS();
185 wformat.cbSize = 0;
186
187 // -----------------------------------
188 // Open the driver for Output operation
189 // -----------------------------------
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 }
208 // -----------------------------------
209 // Open the driver for Input operation
210 // -----------------------------------
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
224 m_current_frag_in = WXSOUND_MAX_QUEUE-1;
225 m_input_frag_in = 0;
226
227 m_internal->m_input_enabled = TRUE;
228 }
229
230 if (mode & wxSOUND_OUTPUT) {
231 if (!AllocHeaders(wxSOUND_OUTPUT)) {
232 CloseDevice();
233 return FALSE;
234 }
235 }
236 if (mode & wxSOUND_INPUT) {
237 if (!AllocHeaders(wxSOUND_INPUT)) {
238 CloseDevice();
239 return FALSE;
240 }
241 }
242
243 return TRUE;
244 }
245
246 // -------------------------------------------------------------------------
247 // CloseDevice() closes the driver handles and frees memory allocated for
248 // IO queues.
249 // -------------------------------------------------------------------------
250 void wxSoundStreamWin::CloseDevice()
251 {
252 if (m_internal->m_output_enabled) {
253 FreeHeaders(wxSOUND_OUTPUT);
254 m_internal->m_output_enabled = FALSE;
255 waveOutClose(m_internal->m_devout);
256 }
257
258 if (m_internal->m_input_enabled) {
259 FreeHeaders(wxSOUND_INPUT);
260 m_internal->m_input_enabled = FALSE;
261 waveInClose(m_internal->m_devin);
262 }
263 }
264
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 // -------------------------------------------------------------------------
277 wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
278 {
279 wxSoundInfoHeader *info;
280 WAVEHDR *header;
281
282 // Some memory allocation
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
292 // Get the two pointers from the system
293 info->m_data = (char *)GlobalLock(info->m_h_data);
294 info->m_header = (WAVEHDR *)GlobalLock(info->m_h_header);
295 // Set the header's mode
296 info->m_mode = mode;
297 // Set the parent of the header
298 info->m_driver = this;
299 // Clean it up
300 ClearHeader(info);
301
302 header = info->m_header;
303 // Initialize Windows variables
304 header->lpData = info->m_data;
305 header->dwBufferLength = GetBestSize();
306 header->dwUser = (DWORD)info;
307 header->dwFlags = WHDR_DONE;
308
309 // "Prepare" the header
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) {
317 // If something goes wrong, free everything.
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
324 m_snderror = wxSOUND_IOERROR;
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) {
334 // If something goes wrong, free everything.
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
341 m_snderror = wxSOUND_IOERROR;
342 return NULL;
343 }
344 }
345 return info;
346 }
347
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 // -------------------------------------------------------------------------
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
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 // -------------------------------------------------------------------------
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
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 // -------------------------------------------------------------------------
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]) {
422 // We wait for the end of the buffer
423 WaitFor((*headers)[i]);
424 // Then, we free the header
425 FreeHeader((*headers)[i], mode);
426 }
427 }
428 delete[] (*headers);
429 (*headers) = NULL;
430 }
431
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 // -------------------------------------------------------------------------
441 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
442 {
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 }
451 }
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;
462 }
463
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 // -------------------------------------------------------------------------
474 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info)
475 {
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)
490 info->m_playing = TRUE;
491 else
492 return FALSE;
493 }
494 return TRUE;
495 }
496
497 // -------------------------------------------------------------------------
498 // ClearHeader(wxSoundInfoHeader *info)
499 //
500 // ClearHeader() reinitializes the parameters of "info" to their default
501 // value.
502 // -------------------------------------------------------------------------
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
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 // -------------------------------------------------------------------------
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
531 // -------------------------------------------------------------------------
532 // The behaviour of Write is documented in the global documentation.
533 // -------------------------------------------------------------------------
534 wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
535 {
536 m_lastcount = 0;
537 if (!m_internal->m_output_enabled) {
538 m_snderror = wxSOUND_NOTSTARTED;
539 return *this;
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;
567 }
568
569 // -------------------------------------------------------------------------
570 // NextFragmentInput is not functional.
571 // -------------------------------------------------------------------------
572 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
573 {
574 wxSoundInfoHeader *header;
575
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;
590 }
591
592 // -------------------------------------------------------------------------
593 // The behaviour of Read is documented in the global documentation.
594 // -------------------------------------------------------------------------
595 wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
596 {
597 wxSoundInfoHeader *header;
598 wxUint32 to_copy;
599
600 m_lastcount = 0;
601 if (!m_internal->m_input_enabled)
602 return *this;
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 }
623 }
624 return *this;
625 }
626
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 // -------------------------------------------------------------------------
634 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle, int flag)
635 {
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 }
663 }
664
665 // -------------------------------------------------------------------------
666 // SetSoundFormat()
667 // -------------------------------------------------------------------------
668 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
669 {
670 // TODO: detect best format
671 return wxSoundStream::SetSoundFormat(base);
672 }
673
674 // -------------------------------------------------------------------------
675 // StartProduction()
676 // -------------------------------------------------------------------------
677 bool wxSoundStreamWin::StartProduction(int evt)
678 {
679 if (!m_internal)
680 return FALSE;
681
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)
693 OnSoundEvent(wxSOUND_OUTPUT);
694
695 if (evt & wxSOUND_INPUT) {
696 int i;
697 for (i=0;i<WXSOUND_MAX_QUEUE;i++)
698 AddToQueue(m_headers_rec[i]);
699
700 waveInStart(m_internal->m_devin);
701 }
702
703 return TRUE;
704 }
705
706 // -------------------------------------------------------------------------
707 // StopProduction()
708 // ------------------------------------------------------------------------
709 bool wxSoundStreamWin::StopProduction()
710 {
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;
720 }
721
722 // -------------------------------------------------------------------------
723 // QueueFilled()
724 // -------------------------------------------------------------------------
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 }