1 // -------------------------------------------------------------------------- 
   5 // Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999, 2000 
   7 // -------------------------------------------------------------------------- 
   9 #pragma implementation "sndwin.cpp" 
  12 #include "wx/wxprec.h" 
  19     #include "wx/string.h" 
  22 #include "wx/module.h" 
  23 #include "wx/msw/private.h" 
  25 // ------------------------------------------------------------------------- 
  27 // ------------------------------------------------------------------------- 
  29 #include "wx/mmedia/sndbase.h" 
  30 #include "wx/mmedia/sndwin.h" 
  31 #include "wx/mmedia/sndpcm.h" 
  33 // ------------------------------------------------------------------------- 
  35 // ------------------------------------------------------------------------- 
  40 // ------------------------------------------------------------------------- 
  41 // External definitions, forward, ... 
  42 // ------------------------------------------------------------------------- 
  44 typedef struct _wxSoundInternal wxSoundInternal
; 
  45 typedef struct _wxSoundInfoHeader wxSoundInfoHeader
; 
  47 extern const wxChar 
*wxCanvasClassName
; 
  49 wxList 
*wxSoundHandleList 
= NULL
; 
  51 static inline wxSoundStreamWin 
*wxFindSoundFromHandle(WXHWND hWnd
) 
  53   wxNode 
*node 
= wxSoundHandleList
->Find((long)hWnd
); 
  56   return (wxSoundStreamWin 
*)node
->Data(); 
  59 struct _wxSoundInternal 
{ 
  63   bool m_output_enabled
, m_input_enabled
; 
  66 struct _wxSoundInfoHeader 
{ 
  67   HGLOBAL m_h_header
, m_h_data
; 
  71   bool m_playing
, m_recording
; 
  72   wxUint32 m_position
, m_size
; 
  74   wxSoundStreamWin 
*m_driver
; 
  77 #define WXSOUND_MAX_QUEUE 10 
  79 wxSoundStreamWin::wxSoundStreamWin() 
  83   m_production_started 
= FALSE
; 
  84   m_internal 
= new wxSoundInternal
; 
  86     m_snderror 
= wxSOUND_MEMERROR
; 
  90   m_snderror 
= wxSOUND_NOERROR
; 
  96   m_internal
->m_input_enabled 
= FALSE
; 
  97   m_internal
->m_output_enabled 
= FALSE
; 
  99   m_waiting_for 
= FALSE
; 
 101   if (!OpenDevice(wxSOUND_OUTPUT
)) { 
 102     m_snderror 
= wxSOUND_NOERROR
; //next call to OpenDevice won't do this 
 103     if (!OpenDevice(wxSOUND_INPUT
)) 
 110 wxSoundStreamWin::~wxSoundStreamWin() 
 113     if (m_production_started
) 
 121 // ----------------------------------------------------------------------- 
 122 // _wxSoundHandlerWndProc: Window callback to handle buffer completion 
 123 // ----------------------------------------------------------------------- 
 124 LRESULT APIENTRY _EXPORT 
 
 126  _wxSoundHandlerWndProc(HWND hWnd
, UINT message
, 
 127                  WPARAM wParam
, LPARAM 
WXUNUSED(lParam
)) 
 129   wxSoundStreamWin 
*sndwin
; 
 131   sndwin 
= wxFindSoundFromHandle((WXHWND
)hWnd
); 
 137     sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_OUTPUT
); 
 140     sndwin
->NotifyDoneBuffer(wParam
, wxSOUND_INPUT
); 
 148 // ----------------------------------------------------------------------- 
 149 // CreateSndWindow() creates an hidden window which will receive the sound 
 151 // ----------------------------------------------------------------------- 
 153 void wxSoundStreamWin::CreateSndWindow() 
 155   FARPROC proc 
= MakeProcInstance((FARPROC
)_wxSoundHandlerWndProc
, 
 157   // NB: class name must be kept in sync with wxCanvasClassName in  
 159   m_internal
->m_sndWin 
= ::CreateWindow(wxT("wxWindowClass"), NULL
, 0, 
 160                                         0, 0, 0, 0, NULL
, (HMENU
) NULL
, 
 161                                         wxGetInstance(), NULL
); 
 165   ::SetWindowLong(m_internal
->m_sndWin
, GWL_WNDPROC
, (LONG
)proc
); 
 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); 
 172 // ----------------------------------------------------------------------- 
 173 // DestroySndWindow() destroys the hidden window 
 174 // ----------------------------------------------------------------------- 
 176 void wxSoundStreamWin::DestroySndWindow() 
 178   if (m_internal
->m_sndWin
) { 
 179     ::DestroyWindow(m_internal
->m_sndWin
); 
 180     wxSoundHandleList
->DeleteObject((wxObject 
*)this); 
 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 
 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 
 194 // ------------------------------------------------------------------------- 
 195 bool wxSoundStreamWin::OpenDevice(int mode
) 
 197   wxSoundFormatPcm 
*pcm
; 
 198   WAVEFORMATEX wformat
; 
 201     m_snderror 
= wxSOUND_INVFRMT
; 
 205   pcm 
= (wxSoundFormatPcm 
*)m_sndformat
; 
 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(); 
 215   // ----------------------------------- 
 216   // Open the driver for Output operation 
 217   // ----------------------------------- 
 218   if (mode 
& wxSOUND_OUTPUT
) { 
 221     result 
= waveOutOpen(&m_internal
->m_devout
, 
 222                          WAVE_MAPPER
, &wformat
, 
 223                          (DWORD
)m_internal
->m_sndWin
, 0, 
 226     if (result 
!= MMSYSERR_NOERROR
) { 
 227       m_snderror 
= wxSOUND_INVDEV
; 
 231     m_output_frag_out  
= WXSOUND_MAX_QUEUE
-1; 
 232     m_current_frag_out 
= 0; 
 234     m_internal
->m_output_enabled 
= TRUE
; 
 236   // ----------------------------------- 
 237   // Open the driver for Input operation 
 238   // ----------------------------------- 
 239   if (mode 
& wxSOUND_INPUT
) { 
 242     result 
= waveInOpen(&m_internal
->m_devin
, 
 243                         WAVE_MAPPER
, &wformat
, 
 244                         (DWORD
)m_internal
->m_sndWin
, 0, 
 247     if (result 
!= MMSYSERR_NOERROR
) { 
 248       m_snderror 
= wxSOUND_INVDEV
; 
 252     m_current_frag_in 
= WXSOUND_MAX_QUEUE
-1; 
 255     m_internal
->m_input_enabled 
= TRUE
; 
 258   if (mode 
& wxSOUND_OUTPUT
) { 
 259     if (!AllocHeaders(wxSOUND_OUTPUT
)) { 
 264   if (mode 
& wxSOUND_INPUT
) { 
 265     if (!AllocHeaders(wxSOUND_INPUT
)) { 
 274 // ------------------------------------------------------------------------- 
 275 // CloseDevice() closes the driver handles and frees memory allocated for 
 277 // ------------------------------------------------------------------------- 
 278 void wxSoundStreamWin::CloseDevice() 
 280   if (m_internal
->m_output_enabled
) { 
 281     FreeHeaders(wxSOUND_OUTPUT
); 
 282     m_internal
->m_output_enabled 
= FALSE
; 
 283     waveOutClose(m_internal
->m_devout
); 
 286   if (m_internal
->m_input_enabled
) { 
 287     FreeHeaders(wxSOUND_INPUT
); 
 288     m_internal
->m_input_enabled  
= FALSE
; 
 289     waveInClose(m_internal
->m_devin
); 
 293 // ------------------------------------------------------------------------- 
 294 // AllocHeader(int mode) 
 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). 
 303 // It returns the new allocated block or NULL. 
 304 // ------------------------------------------------------------------------- 
 305 wxSoundInfoHeader 
*wxSoundStreamWin::AllocHeader(int mode
) 
 307   wxSoundInfoHeader 
*info
; 
 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
) { 
 316     m_snderror 
= wxSOUND_MEMERROR
; 
 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 
 325   // Set the parent of the header 
 326   info
->m_driver    
= this; 
 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
; 
 337   // "Prepare" the header 
 338   if (mode 
== wxSOUND_INPUT
) { 
 341     result  
= waveInPrepareHeader(m_internal
->m_devin
, header
, 
 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
); 
 352       m_snderror 
= wxSOUND_IOERROR
; 
 355   } else if (mode 
== wxSOUND_OUTPUT
) { 
 358     result  
= waveOutPrepareHeader(m_internal
->m_devout
, header
, 
 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
); 
 369       m_snderror 
= wxSOUND_IOERROR
; 
 376 // ------------------------------------------------------------------------- 
 377 // AllocHeaders(int mode) 
 379 // "mode" has the same mean as for OpenDevice() except that the two flags must 
 381 // AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation 
 382 // queue. It uses AllocHeader() for each element. 
 384 // Once it has allocated all blocks, it returns TRUE and if an error occured 
 386 // ------------------------------------------------------------------------- 
 387 bool wxSoundStreamWin::AllocHeaders(int mode
) 
 390   wxSoundInfoHeader 
**headers
; 
 392   if (mode 
== wxSOUND_OUTPUT
) 
 393     headers 
= m_headers_play 
= new wxSoundInfoHeader 
*[WXSOUND_MAX_QUEUE
]; 
 395     headers 
= m_headers_rec 
= new wxSoundInfoHeader 
*[WXSOUND_MAX_QUEUE
]; 
 397   memset(headers
, 0, WXSOUND_MAX_QUEUE
*sizeof(wxSoundInfoHeader 
*)); 
 399   for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) { 
 400     headers
[i
] = AllocHeader(mode
); 
 409 // ------------------------------------------------------------------------- 
 410 // FreeHeader(int mode) 
 412 // "mode" has the same mean as for OpenDevice() except that the two flags must 
 414 // FreeHeader() frees a memory block and "unprepares" it. 
 415 // ------------------------------------------------------------------------- 
 416 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader 
*header
, int mode
) 
 418   if (mode 
== wxSOUND_OUTPUT
) 
 419     waveOutUnprepareHeader(m_internal
->m_devout
, header
->m_header
, sizeof(WAVEHDR
)); 
 421     waveInUnprepareHeader(m_internal
->m_devin
, header
->m_header
, sizeof(WAVEHDR
)); 
 423   GlobalUnlock(header
->m_data
); 
 424   GlobalUnlock(header
->m_header
); 
 425   GlobalFree(header
->m_h_header
); 
 426   GlobalFree(header
->m_h_data
); 
 430 // ------------------------------------------------------------------------- 
 431 // FreeHeaders(int mode) 
 433 // "mode" has the same mean as for OpenDevice() except that the two flags must 
 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
) 
 441   wxSoundInfoHeader 
***headers
; 
 443   if (mode 
== wxSOUND_OUTPUT
) 
 444     headers 
= &m_headers_play
; 
 446     headers 
= &m_headers_rec
; 
 448   for (i
=0;i
<WXSOUND_MAX_QUEUE
;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
); 
 460 // ------------------------------------------------------------------------- 
 461 // WaitFor(wxSoundInfoHeader *info) 
 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. 
 467 // NB: if it's a partially filled buffer it adds it to the Windows queue 
 468 // ------------------------------------------------------------------------- 
 469 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader 
*info
) 
 471     // If the buffer is finished, we return immediately 
 472     if (!info
->m_playing
) { 
 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
); 
 485     m_waiting_for 
= TRUE
; 
 486     // Else, we wait for its termination 
 487     while (info
->m_playing 
|| info
->m_recording
) 
 489     m_waiting_for 
= FALSE
; 
 492 // ------------------------------------------------------------------------- 
 493 // AddToQueue(wxSoundInfoHeader *info) 
 495 // For "info", see WaitFor() 
 496 // AddToQueue() sends the IO queue element to the Windows queue. 
 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
) 
 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
) 
 511             info
->m_recording 
= TRUE
; 
 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
) 
 518       info
->m_playing 
= TRUE
; 
 525 // ------------------------------------------------------------------------- 
 526 // ClearHeader(wxSoundInfoHeader *info) 
 528 // ClearHeader() reinitializes the parameters of "info" to their default 
 530 // ------------------------------------------------------------------------- 
 531 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader 
*info
) 
 533   info
->m_playing   
= FALSE
; 
 534   info
->m_recording 
= FALSE
; 
 535   info
->m_position  
= 0; 
 536   info
->m_size      
= GetBestSize(); 
 539 // ------------------------------------------------------------------------- 
 540 // wxSoundInfoHeader *NextFragmentOutput() 
 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 
 545 // ------------------------------------------------------------------------- 
 546 wxSoundInfoHeader 
*wxSoundStreamWin::NextFragmentOutput() 
 548   if (m_headers_play
[m_current_frag_out
]->m_playing
) { 
 549     m_current_frag_out 
= (m_current_frag_out 
+ 1) % WXSOUND_MAX_QUEUE
; 
 551     if (m_headers_play
[m_current_frag_out
]->m_playing
) 
 552       WaitFor(m_headers_play
[m_current_frag_out
]); 
 554   if (m_current_frag_out 
== m_output_frag_out
) 
 555     m_queue_filled 
= TRUE
; 
 556   return m_headers_play
[m_current_frag_out
]; 
 559 // ------------------------------------------------------------------------- 
 560 // The behaviour of Write is documented in the global documentation. 
 561 // ------------------------------------------------------------------------- 
 562 wxSoundStream
& wxSoundStreamWin::Write(const void *buffer
, wxUint32 len
) 
 565     if (!m_internal
->m_output_enabled
) { 
 566         m_snderror 
= wxSOUND_NOTSTARTED
; 
 572         wxSoundInfoHeader 
*header
; 
 575         // Get a new output fragment 
 576         header              
= NextFragmentOutput(); 
 578         to_copy             
= (len 
> header
->m_size
) ? header
->m_size 
: len
; 
 579         memcpy(header
->m_data 
+ header
->m_position
, buffer
, to_copy
); 
 581         header
->m_position 
+= to_copy
; 
 582         header
->m_size     
-= to_copy
; 
 583         buffer              
= (((const char *)buffer
) + to_copy
); 
 585         m_lastcount        
+= to_copy
; 
 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
; 
 597 // ------------------------------------------------------------------------- 
 598 // NextFragmentInput is not functional. 
 599 // ------------------------------------------------------------------------- 
 600 wxSoundInfoHeader 
*wxSoundStreamWin::NextFragmentInput() 
 602     wxSoundInfoHeader 
*header
; 
 604     // Queue pointer: reader 
 605     m_current_frag_in 
= (m_current_frag_in 
+ 1) % WXSOUND_MAX_QUEUE
; 
 607     header 
= m_headers_rec
[m_current_frag_in
]; 
 608     // If the current buffer is in recording mode, we must wait for its 
 610     if (header
->m_recording
) 
 613     // We reached the writer position: the queue is full. 
 614     if (m_current_frag_in 
== m_input_frag_in
) 
 615         m_queue_filled 
= TRUE
; 
 620 // ------------------------------------------------------------------------- 
 621 // The behaviour of Read is documented in the global documentation. 
 622 // ------------------------------------------------------------------------- 
 623 wxSoundStream
& wxSoundStreamWin::Read(void *buffer
, wxUint32 len
) 
 625     wxSoundInfoHeader 
*header
; 
 629     if (!m_internal
->m_input_enabled
) 
 633         header 
= NextFragmentInput(); 
 635         to_copy             
= (len 
> header
->m_size
) ? header
->m_size 
: len
; 
 636         memcpy(buffer
, header
->m_data 
+ header
->m_position
, to_copy
); 
 638         header
->m_position 
+= to_copy
; 
 639         header
->m_size     
-= to_copy
; 
 640         buffer              
= (((char *)buffer
) + to_copy
); 
 642         m_lastcount        
+= to_copy
; 
 644         if (header
->m_size 
== 0) { 
 646             if (!AddToQueue(header
)) { 
 647                 m_snderror 
= wxSOUND_IOERROR
; 
 655 // ------------------------------------------------------------------------- 
 656 // NotifyDoneBuffer(wxUint32 dev_handle) 
 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 // ------------------------------------------------------------------------- 
 662 void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 
WXUNUSED(dev_handle
), int flag
) 
 664     wxSoundInfoHeader 
*info
; 
 666     if (flag 
== wxSOUND_OUTPUT
) { 
 667         if (!m_internal
->m_output_enabled
) 
 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 
 675         m_queue_filled 
= FALSE
; 
 677             // Try to requeue a new buffer. 
 678             OnSoundEvent(wxSOUND_OUTPUT
); 
 680         if (!m_internal
->m_input_enabled
) 
 683         // Recording completed 
 684         m_headers_rec
[m_input_frag_in
]->m_recording 
= FALSE
; 
 685         // Queue pointer: writer 
 686         m_input_frag_in 
= (m_input_frag_in 
+ 1) % WXSOUND_MAX_QUEUE
; 
 688             OnSoundEvent(wxSOUND_INPUT
); 
 689         m_queue_filled 
= FALSE
; 
 693 // ------------------------------------------------------------------------- 
 695 // ------------------------------------------------------------------------- 
 696 bool wxSoundStreamWin::SetSoundFormat(const wxSoundFormatBase
& base
) 
 698   // TODO: detect best format 
 699   return wxSoundStream::SetSoundFormat(base
); 
 702 // ------------------------------------------------------------------------- 
 704 // ------------------------------------------------------------------------- 
 705 bool wxSoundStreamWin::StartProduction(int evt
) 
 710   if ((m_internal
->m_output_enabled 
&& (evt 
& wxSOUND_OUTPUT
)) || 
 711       (m_internal
->m_input_enabled 
&& (evt 
& wxSOUND_INPUT
))) 
 714   if (!OpenDevice(evt
)) 
 717   m_production_started 
= TRUE
; 
 718   m_queue_filled 
= FALSE
; 
 719   // Send a dummy event to start. 
 720   if (evt 
& wxSOUND_OUTPUT
) 
 721     OnSoundEvent(wxSOUND_OUTPUT
); 
 723   if (evt 
& wxSOUND_INPUT
) { 
 725     for (i
=0;i
<WXSOUND_MAX_QUEUE
;i
++) 
 726       AddToQueue(m_headers_rec
[i
]); 
 728     waveInStart(m_internal
->m_devin
); 
 734 // ------------------------------------------------------------------------- 
 736 // ------------------------------------------------------------------------ 
 737 bool wxSoundStreamWin::StopProduction() 
 739     if (!m_production_started
) { 
 740         m_snderror 
= wxSOUND_NOTSTARTED
; 
 744     m_snderror 
= wxSOUND_NOERROR
; 
 745     m_production_started 
= FALSE
; 
 750 // ------------------------------------------------------------------------- 
 752 // ------------------------------------------------------------------------- 
 753 bool wxSoundStreamWin::QueueFilled() const 
 755   return (!m_production_started 
|| m_queue_filled
); 
 759 // -------------------------------------------------------------------------- 
 761 // -------------------------------------------------------------------------- 
 763 class wxSoundWinModule 
: public wxModule 
{ 
 764    DECLARE_DYNAMIC_CLASS(wxSoundWinModule
) 
 770 IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule
, wxModule
) 
 772 bool wxSoundWinModule::OnInit() { 
 773   wxSoundHandleList 
= new wxList(wxKEY_INTEGER
); 
 777 void wxSoundWinModule::OnExit() { 
 778   delete wxSoundHandleList
;