]> git.saurik.com Git - wxWidgets.git/blobdiff - utils/wxMMedia2/lib/sndwin.cpp
makefile updates
[wxWidgets.git] / utils / wxMMedia2 / lib / sndwin.cpp
index b4527bd17f8e6a4676c54b5dd71b405f9ea24cdd..0d070fce806c899cc251c054202b6cadac30b9e7 100644 (file)
@@ -7,8 +7,9 @@
 // --------------------------------------------------------------------------
 #include <wx/wxprec.h>
 
 // --------------------------------------------------------------------------
 #include <wx/wxprec.h>
 
-#include <wx/msw/private.h>
+#include <wx/app.h>
 #include <wx/module.h>
 #include <wx/module.h>
+#include <wx/msw/private.h>
 #include <string.h>
 #include "sndbase.h"
 #include "sndwin.h"
 #include <string.h>
 #include "sndbase.h"
 #include "sndwin.h"
@@ -20,7 +21,7 @@
 typedef struct _wxSoundInternal wxSoundInternal;
 typedef struct _wxSoundInfoHeader wxSoundInfoHeader;
 
 typedef struct _wxSoundInternal wxSoundInternal;
 typedef struct _wxSoundInfoHeader wxSoundInfoHeader;
 
-extern char wxCanvasClassName[];
+extern const wxChar *wxCanvasClassName;
 
 wxList *wxSoundHandleList = NULL;
 
 
 wxList *wxSoundHandleList = NULL;
 
@@ -50,7 +51,7 @@ struct _wxSoundInfoHeader {
   wxSoundStreamWin *m_driver;
 };
 
   wxSoundStreamWin *m_driver;
 };
 
-#define WXSOUND_MAX_QUEUE 128
+#define WXSOUND_MAX_QUEUE 10
 
 wxSoundStreamWin::wxSoundStreamWin()
 {
 
 wxSoundStreamWin::wxSoundStreamWin()
 {
@@ -58,12 +59,22 @@ wxSoundStreamWin::wxSoundStreamWin()
 
   m_production_started = FALSE;
   m_internal = new wxSoundInternal;
 
   m_production_started = FALSE;
   m_internal = new wxSoundInternal;
+  if (!m_internal) {
+    m_snderror = wxSOUND_MEMERR;
+    m_internal = NULL;
+    return;
+  }
   m_snderror = wxSOUND_NOERR;
 
   // Setup defaults
   CreateSndWindow();
   SetSoundFormat(pcm);
 
   m_snderror = wxSOUND_NOERR;
 
   // Setup defaults
   CreateSndWindow();
   SetSoundFormat(pcm);
 
+  m_internal->m_input_enabled = FALSE;
+  m_internal->m_output_enabled = FALSE;
+
+  m_waiting_for = FALSE;
+
   if (!OpenDevice(wxSOUND_OUTPUT))
     return;
 
   if (!OpenDevice(wxSOUND_OUTPUT))
     return;
 
@@ -72,27 +83,42 @@ wxSoundStreamWin::wxSoundStreamWin()
 
 wxSoundStreamWin::~wxSoundStreamWin()
 {
 
 wxSoundStreamWin::~wxSoundStreamWin()
 {
-  if (m_production_started)
-    StopProduction();
-  DestroySndWindow();
+  if (m_internal) {
+    if (m_production_started)
+      StopProduction();
+    DestroySndWindow();
 
 
-  delete m_internal;
+    delete m_internal;
+  }
 }
 
 LRESULT APIENTRY _EXPORT _wxSoundHandlerWndProc(HWND hWnd, UINT message,
                  WPARAM wParam, LPARAM lParam)
 {
 }
 
 LRESULT APIENTRY _EXPORT _wxSoundHandlerWndProc(HWND hWnd, UINT message,
                  WPARAM wParam, LPARAM lParam)
 {
+  wxSoundStreamWin *sndwin;
+
+  sndwin = wxFindSoundFromHandle((WXHWND)hWnd);
+  if (!sndwin)
+    return (LRESULT)0;
+
   switch (message) {
   switch (message) {
-  case MM_WOM_DONE: {
-    wxFindSoundFromHandle((WXHWND)hWnd)->NotifyDoneBuffer(wParam);
+  case MM_WOM_DONE:
+    sndwin->NotifyDoneBuffer(wParam, wxSOUND_OUTPUT);
+    break;
+  case MM_WIM_DATA:
+    sndwin->NotifyDoneBuffer(wParam, wxSOUND_INPUT);
     break;
     break;
-  }
   default:
     break;
   }
   return (LRESULT)0;
 }
 
   default:
     break;
   }
   return (LRESULT)0;
 }
 
+// -----------------------------------------------------------------------
+// CreateSndWindow() creates an hidden window which will receive the sound
+// events
+// -----------------------------------------------------------------------
+
 void wxSoundStreamWin::CreateSndWindow()
 {
   FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc,
 void wxSoundStreamWin::CreateSndWindow()
 {
   FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc,
@@ -104,13 +130,18 @@ void wxSoundStreamWin::CreateSndWindow()
                                         wxGetInstance(), NULL);
 
   error = GetLastError();
                                         wxGetInstance(), NULL);
 
   error = GetLastError();
-  wxPrintf("%d\n", error);
 
   ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
 
 
   ::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
 
+  // Add this window to the sound handle list so we'll be able to redecode
+  // the "magic" number.
   wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this);
 }
 
   wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this);
 }
 
+// -----------------------------------------------------------------------
+// DestroySndWindow() destroys the hidden window
+// -----------------------------------------------------------------------
+
 void wxSoundStreamWin::DestroySndWindow()
 {
   if (m_internal->m_sndWin) {
 void wxSoundStreamWin::DestroySndWindow()
 {
   if (m_internal->m_sndWin) {
@@ -119,6 +150,17 @@ void wxSoundStreamWin::DestroySndWindow()
   }
 }
 
   }
 }
 
+// -------------------------------------------------------------------------
+// OpenDevice(int mode) initializes the windows driver for a "mode"
+// operation. mode is a bit mask: if the bit "wxSOUND_OUTPUT" is set,
+// the driver is opened for output operation, and if the bit "wxSOUND_INPUT"
+// is set, then the driver is opened for input operation. The two modes
+// aren't exclusive.
+// The initialization parameters (sample rate, ...) are taken from the
+// m_sndformat object.
+// At the end, OpenDevice() calls AllocHeaders() to initialize the Sound IO
+// queue.
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::OpenDevice(int mode)
 {
   wxSoundFormatPcm *pcm;
 bool wxSoundStreamWin::OpenDevice(int mode)
 {
   wxSoundFormatPcm *pcm;
@@ -133,12 +175,15 @@ bool wxSoundStreamWin::OpenDevice(int mode)
 
   wformat.wFormatTag      = WAVE_FORMAT_PCM;
   wformat.nChannels       = pcm->GetChannels();
 
   wformat.wFormatTag      = WAVE_FORMAT_PCM;
   wformat.nChannels       = pcm->GetChannels();
-  wformat.nBlockAlign     = pcm->GetBPS() / 8 * wformat.nChannels;
-  wformat.nAvgBytesPerSec = pcm->GetBytesFromTime(1);
+  wformat.nBlockAlign     = wformat.nChannels * pcm->GetBPS() / 8;
   wformat.nSamplesPerSec  = pcm->GetSampleRate();
   wformat.nSamplesPerSec  = pcm->GetSampleRate();
+  wformat.nAvgBytesPerSec = wformat.nSamplesPerSec * wformat.nBlockAlign;
   wformat.wBitsPerSample  = pcm->GetBPS();
   wformat.cbSize          = 0;
 
   wformat.wBitsPerSample  = pcm->GetBPS();
   wformat.cbSize          = 0;
 
+  // -----------------------------------
+  // Open the driver for Output operation
+  // -----------------------------------
   if (mode & wxSOUND_OUTPUT) {
     MMRESULT result;
 
   if (mode & wxSOUND_OUTPUT) {
     MMRESULT result;
 
@@ -157,6 +202,9 @@ bool wxSoundStreamWin::OpenDevice(int mode)
 
     m_internal->m_output_enabled = TRUE;
   }
 
     m_internal->m_output_enabled = TRUE;
   }
+  // -----------------------------------
+  // Open the driver for Input operation
+  // -----------------------------------
   if (mode & wxSOUND_INPUT) {
     MMRESULT result;
 
   if (mode & wxSOUND_INPUT) {
     MMRESULT result;
 
@@ -170,43 +218,65 @@ bool wxSoundStreamWin::OpenDevice(int mode)
       return FALSE;
     }
 
       return FALSE;
     }
 
-    m_input_frag_in   = WXSOUND_MAX_QUEUE-1;
-    m_current_frag_in = 0;
+    m_current_frag_in = WXSOUND_MAX_QUEUE-1;
+    m_input_frag_in   = 0;
 
     m_internal->m_input_enabled = TRUE;
   }
 
 
     m_internal->m_input_enabled = TRUE;
   }
 
-  if (!AllocHeaders(mode)) {
-    CloseDevice();
-    return FALSE;
+  if (mode & wxSOUND_OUTPUT) {
+    if (!AllocHeaders(wxSOUND_OUTPUT)) {
+      CloseDevice();
+      return FALSE;
+    }
   }
   }
+  if (mode & wxSOUND_INPUT) {
+    if (!AllocHeaders(wxSOUND_INPUT)) {
+      CloseDevice();
+      return FALSE;
+    }
+  }
+
   return TRUE;
 }
 
   return TRUE;
 }
 
-
+// -------------------------------------------------------------------------
+// CloseDevice() closes the driver handles and frees memory allocated for
+// IO queues.
+// -------------------------------------------------------------------------
 void wxSoundStreamWin::CloseDevice()
 {
   if (m_internal->m_output_enabled) {
     FreeHeaders(wxSOUND_OUTPUT);
 void wxSoundStreamWin::CloseDevice()
 {
   if (m_internal->m_output_enabled) {
     FreeHeaders(wxSOUND_OUTPUT);
-    waveOutReset(m_internal->m_devout);
+    m_internal->m_output_enabled = FALSE;
     waveOutClose(m_internal->m_devout);
   }
 
   if (m_internal->m_input_enabled) {
     FreeHeaders(wxSOUND_INPUT);
     waveOutClose(m_internal->m_devout);
   }
 
   if (m_internal->m_input_enabled) {
     FreeHeaders(wxSOUND_INPUT);
-    waveInReset(m_internal->m_devin);
+    m_internal->m_input_enabled  = FALSE;
     waveInClose(m_internal->m_devin);
   }
     waveInClose(m_internal->m_devin);
   }
-
-  m_internal->m_output_enabled = FALSE;
-  m_internal->m_input_enabled  = FALSE;
 }
 
 }
 
+// -------------------------------------------------------------------------
+// AllocHeader(int mode)
+//
+// mode has the same mean as in OpenDevice() except that here the two flags
+// must be exclusive.
+// AllocHeader() initializes an element of an operation (this can be input
+// or output). It means it allocates the sound header's memory block 
+// and "prepares" it (It is needed by Windows). At the same time, it sets
+// private datas so we can the header's owner (See callback).
+//
+// It returns the new allocated block or NULL.
+// -------------------------------------------------------------------------
 wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
 {
   wxSoundInfoHeader *info;
   WAVEHDR *header;
 
 wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
 {
   wxSoundInfoHeader *info;
   WAVEHDR *header;
 
+  // Some memory allocation
   info = new wxSoundInfoHeader;
   info->m_h_data   = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize());
   info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
   info = new wxSoundInfoHeader;
   info->m_h_data   = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize());
   info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
@@ -216,19 +286,24 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
     return NULL;
   }
 
     return NULL;
   }
 
+  // Get the two pointers from the system
   info->m_data      = (char *)GlobalLock(info->m_h_data);
   info->m_header    = (WAVEHDR *)GlobalLock(info->m_h_header);
   info->m_data      = (char *)GlobalLock(info->m_h_data);
   info->m_header    = (WAVEHDR *)GlobalLock(info->m_h_header);
+  // Set the header's mode
   info->m_mode      = mode;
   info->m_mode      = mode;
+  // Set the parent of the header
   info->m_driver    = this;
   info->m_driver    = this;
+  // Clean it up
   ClearHeader(info);
 
   header            = info->m_header;
   ClearHeader(info);
 
   header            = info->m_header;
-
+  // Initialize Windows variables
   header->lpData         = info->m_data;
   header->dwBufferLength = GetBestSize();
   header->dwUser         = (DWORD)info;
   header->dwFlags        = WHDR_DONE;
 
   header->lpData         = info->m_data;
   header->dwBufferLength = GetBestSize();
   header->dwUser         = (DWORD)info;
   header->dwFlags        = WHDR_DONE;
 
+  // "Prepare" the header
   if (mode == wxSOUND_INPUT) {
     MMRESULT result;
 
   if (mode == wxSOUND_INPUT) {
     MMRESULT result;
 
@@ -236,6 +311,7 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
                                   sizeof(WAVEHDR));
 
     if (result != MMSYSERR_NOERROR) {
                                   sizeof(WAVEHDR));
 
     if (result != MMSYSERR_NOERROR) {
+      // If something goes wrong, free everything.
       GlobalUnlock(info->m_data);
       GlobalUnlock(info->m_header);
       GlobalFree(info->m_h_data);
       GlobalUnlock(info->m_data);
       GlobalUnlock(info->m_header);
       GlobalFree(info->m_h_data);
@@ -252,6 +328,7 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
                                    sizeof(WAVEHDR));
 
     if (result != MMSYSERR_NOERROR) {
                                    sizeof(WAVEHDR));
 
     if (result != MMSYSERR_NOERROR) {
+      // If something goes wrong, free everything.
       GlobalUnlock(info->m_data);
       GlobalUnlock(info->m_header);
       GlobalFree(info->m_h_data);
       GlobalUnlock(info->m_data);
       GlobalUnlock(info->m_header);
       GlobalFree(info->m_h_data);
@@ -265,6 +342,17 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
   return info;
 }
 
   return info;
 }
 
+// -------------------------------------------------------------------------
+// AllocHeaders(int mode)
+//
+// "mode" has the same mean as for OpenDevice() except that the two flags must
+// be exclusive.
+// AllocHeaders() allocates WXSOUND_MAX_QUEUE (= 128) blocks for an operation
+// queue. It uses AllocHeader() for each element.
+//
+// Once it has allocated all blocks, it returns TRUE and if an error occured
+// it returns FALSE.
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::AllocHeaders(int mode)
 {
   int i;
 bool wxSoundStreamWin::AllocHeaders(int mode)
 {
   int i;
@@ -287,6 +375,13 @@ bool wxSoundStreamWin::AllocHeaders(int mode)
   return TRUE;
 }
 
   return TRUE;
 }
 
+// -------------------------------------------------------------------------
+// FreeHeader(int mode)
+//
+// "mode" has the same mean as for OpenDevice() except that the two flags must
+// be exclusive.
+// FreeHeader() frees a memory block and "unprepares" it.
+// -------------------------------------------------------------------------
 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
 {
   if (mode == wxSOUND_OUTPUT)
 void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
 {
   if (mode == wxSOUND_OUTPUT)
@@ -301,6 +396,14 @@ void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
   delete header;
 }
 
   delete header;
 }
 
+// -------------------------------------------------------------------------
+// FreeHeaders(int mode)
+//
+// "mode" has the same mean as for OpenDevice() except that the two flags must
+// be exclusive.
+// FreeHeaders() frees all an operation queue once it has checked that
+// all buffers have been terminated.
+// -------------------------------------------------------------------------
 void wxSoundStreamWin::FreeHeaders(int mode)
 {
   int i;
 void wxSoundStreamWin::FreeHeaders(int mode)
 {
   int i;
@@ -313,7 +416,9 @@ void wxSoundStreamWin::FreeHeaders(int mode)
 
   for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
     if ((*headers)[i]) {
 
   for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
     if ((*headers)[i]) {
+      // We wait for the end of the buffer
       WaitFor((*headers)[i]);
       WaitFor((*headers)[i]);
+      // Then, we free the header
       FreeHeader((*headers)[i], mode);
     }
   }
       FreeHeader((*headers)[i], mode);
     }
   }
@@ -321,26 +426,54 @@ void wxSoundStreamWin::FreeHeaders(int mode)
   (*headers) = NULL;
 }
 
   (*headers) = NULL;
 }
 
+// -------------------------------------------------------------------------
+// WaitFor(wxSoundInfoHeader *info)
+//
+// "info" is one element of an IO queue
+// WaitFor() checks whether the specified block has been terminated.
+// If it hasn't been terminated, it waits for its termination.
+//
+// NB: if it's a partially filled buffer it adds it to the Windows queue
+// -------------------------------------------------------------------------
 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
 {
 void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
 {
-  if (info->m_position != 0) {
-    memset(info->m_data + info->m_position, 0, info->m_size);
-    AddToQueue(info);
+  // If the buffer is finished, we return immediately
+  if (!info->m_playing) {
+
+    // We begun filling it: we must send it to the Windows queue
+    if (info->m_position != 0) {
+      memset(info->m_data + info->m_position, 0, info->m_size);
+      AddToQueue(info);
+    }
   }
 
   }
 
-  if (!info->m_playing && !info->m_recording)
+  if (m_waiting_for) {
+    // PROBLEM //
     return;
     return;
-
+  }
+  m_waiting_for = TRUE;
+  // Else, we wait for its termination
   while (info->m_playing || info->m_recording)
     wxYield();
   while (info->m_playing || info->m_recording)
     wxYield();
+  m_waiting_for = FALSE;
 }
 
 }
 
+// -------------------------------------------------------------------------
+// AddToQueue(wxSoundInfoHeader *info)
+//
+// For "info", see WaitFor()
+// AddToQueue() sends the IO queue element to the Windows queue.
+//
+// Warning: in the current implementation, it partially assume we send the
+// element in the right order. This is true in that implementation but if
+// you use it elsewhere, be careful: it may shuffle all your sound datas.
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info)
 {
   MMRESULT result;
 
   if (info->m_mode == wxSOUND_INPUT) {
 bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info)
 {
   MMRESULT result;
 
   if (info->m_mode == wxSOUND_INPUT) {
-    m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE;
+    // Increment the input fragment pointer
     result = waveInAddBuffer(m_internal->m_devin,
                              info->m_header, sizeof(WAVEHDR));
     if (result == MMSYSERR_NOERROR)
     result = waveInAddBuffer(m_internal->m_devin,
                              info->m_header, sizeof(WAVEHDR));
     if (result == MMSYSERR_NOERROR)
@@ -358,6 +491,12 @@ bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info)
   return TRUE;
 }
 
   return TRUE;
 }
 
+// -------------------------------------------------------------------------
+// ClearHeader(wxSoundInfoHeader *info)
+//
+// ClearHeader() reinitializes the parameters of "info" to their default
+// value.
+// -------------------------------------------------------------------------
 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
 {
   info->m_playing   = FALSE;
 void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
 {
   info->m_playing   = FALSE;
@@ -366,6 +505,13 @@ void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
   info->m_size      = GetBestSize();
 }
 
   info->m_size      = GetBestSize();
 }
 
+// -------------------------------------------------------------------------
+// wxSoundInfoHeader *NextFragmentOutput()
+//
+// NextFragmentOutput() looks for a free output block. It will always
+// return you a non-NULL pointer but it may waits for an empty buffer a long
+// time.
+// -------------------------------------------------------------------------
 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
 {
   if (m_headers_play[m_current_frag_out]->m_playing) {
 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
 {
   if (m_headers_play[m_current_frag_out]->m_playing) {
@@ -379,6 +525,9 @@ wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
   return m_headers_play[m_current_frag_out];
 }
 
   return m_headers_play[m_current_frag_out];
 }
 
+// -------------------------------------------------------------------------
+// The behaviour of Write is documented in the global documentation.
+// -------------------------------------------------------------------------
 wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
 {
   m_lastcount = 0;
 wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
 {
   m_lastcount = 0;
@@ -389,6 +538,7 @@ wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
     wxSoundInfoHeader *header;
     wxUint32 to_copy;
 
     wxSoundInfoHeader *header;
     wxUint32 to_copy;
 
+    // Get a new output fragment
     header              = NextFragmentOutput();
 
     to_copy             = (len > header->m_size) ? header->m_size : len;
     header              = NextFragmentOutput();
 
     to_copy             = (len > header->m_size) ? header->m_size : len;
@@ -400,6 +550,7 @@ wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
     len                -= to_copy;
     m_lastcount        += to_copy;
     
     len                -= to_copy;
     m_lastcount        += to_copy;
     
+    // If the fragment is full, we send it to the Windows queue.
     if (header->m_size == 0)
       if (!AddToQueue(header)) {
         m_snderror = wxSOUND_IOERR;
     if (header->m_size == 0)
       if (!AddToQueue(header)) {
         m_snderror = wxSOUND_IOERR;
@@ -409,13 +560,18 @@ wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
   return *this;
 }
 
   return *this;
 }
 
+// -------------------------------------------------------------------------
+// NextFragmentInput is not functional.
+// -------------------------------------------------------------------------
 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
 {
   wxSoundInfoHeader *header;
 
 wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
 {
   wxSoundInfoHeader *header;
 
-  // TODO //
+  m_current_frag_in = (m_current_frag_in + 1) % WXSOUND_MAX_QUEUE;
+
   header = m_headers_rec[m_current_frag_in];
   header = m_headers_rec[m_current_frag_in];
-  WaitFor(header);
+  if (header->m_recording)
+    WaitFor(header);
 
   if (m_current_frag_in == m_input_frag_in)
     m_queue_filled = TRUE;
 
   if (m_current_frag_in == m_input_frag_in)
     m_queue_filled = TRUE;
@@ -423,6 +579,9 @@ wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
   return header;
 }
 
   return header;
 }
 
+// -------------------------------------------------------------------------
+// The behaviour of Read is documented in the global documentation.
+// -------------------------------------------------------------------------
 wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
 {
   wxSoundInfoHeader *header;
 wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
 {
   wxSoundInfoHeader *header;
@@ -455,30 +614,53 @@ wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
   return *this;
 }
 
   return *this;
 }
 
-void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle)
+// -------------------------------------------------------------------------
+// NotifyDoneBuffer(wxUint32 dev_handle)
+//
+// NotifyDoneBuffer() is called by wxSoundHandlerProc each time a sound
+// fragment finished. It reinitializes the parameters of the fragment and
+// sends an event to the clients.
+// -------------------------------------------------------------------------
+void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle, int flag)
 {
   wxSoundInfoHeader *info;
 
 {
   wxSoundInfoHeader *info;
 
-  if (dev_handle == (wxUint32)m_internal->m_devout) {
+  if (flag == wxSOUND_OUTPUT) {
+    if (!m_internal->m_output_enabled)
+      return;
+
     m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE;
     info = m_headers_play[m_output_frag_out];
     ClearHeader(info);
     m_queue_filled = FALSE;
     m_output_frag_out = (m_output_frag_out + 1) % WXSOUND_MAX_QUEUE;
     info = m_headers_play[m_output_frag_out];
     ClearHeader(info);
     m_queue_filled = FALSE;
-    OnSoundEvent(wxSOUND_OUTPUT);
+    if (!m_waiting_for)
+      OnSoundEvent(wxSOUND_OUTPUT);
   } else {
   } else {
+    if (!m_internal->m_input_enabled)
+      return;
+
+    m_headers_rec[m_input_frag_in]->m_recording = FALSE;
     m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE;
     m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE;
-    OnSoundEvent(wxSOUND_INPUT);
+    if (!m_waiting_for)
+      OnSoundEvent(wxSOUND_INPUT);
     m_queue_filled = FALSE;
   }
 }
 
     m_queue_filled = FALSE;
   }
 }
 
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
 {
   return wxSoundStream::SetSoundFormat(base);
 }
 
 bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
 {
   return wxSoundStream::SetSoundFormat(base);
 }
 
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::StartProduction(int evt)
 {
 bool wxSoundStreamWin::StartProduction(int evt)
 {
+  if (!m_internal)
+    return FALSE;
+
   if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) ||
       (m_internal->m_input_enabled && (evt & wxSOUND_INPUT)))
     CloseDevice();
   if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) ||
       (m_internal->m_input_enabled && (evt & wxSOUND_INPUT)))
     CloseDevice();
@@ -490,24 +672,33 @@ bool wxSoundStreamWin::StartProduction(int evt)
   m_queue_filled = FALSE;
   // Send a dummy event to start.
   if (evt & wxSOUND_OUTPUT)
   m_queue_filled = FALSE;
   // Send a dummy event to start.
   if (evt & wxSOUND_OUTPUT)
-    OnSoundEvent(evt);
+    OnSoundEvent(wxSOUND_OUTPUT);
 
   if (evt & wxSOUND_INPUT) {
     int i;
     for (i=0;i<WXSOUND_MAX_QUEUE;i++)
       AddToQueue(m_headers_rec[i]);
 
   if (evt & wxSOUND_INPUT) {
     int i;
     for (i=0;i<WXSOUND_MAX_QUEUE;i++)
       AddToQueue(m_headers_rec[i]);
+
+    waveInStart(m_internal->m_devin);
   }
 
   return TRUE;
 }
 
   }
 
   return TRUE;
 }
 
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::StopProduction()
 {
 bool wxSoundStreamWin::StopProduction()
 {
+  if (!m_production_started)
+    return FALSE;
+
   m_production_started = FALSE;
   CloseDevice();
   return TRUE;
 }
 
   m_production_started = FALSE;
   CloseDevice();
   return TRUE;
 }
 
+// -------------------------------------------------------------------------
+// -------------------------------------------------------------------------
 bool wxSoundStreamWin::QueueFilled() const
 {
   return (!m_production_started || m_queue_filled);
 bool wxSoundStreamWin::QueueFilled() const
 {
   return (!m_production_started || m_queue_filled);