X-Git-Url: https://git.saurik.com/wxWidgets.git/blobdiff_plain/0662cd3286d6da0be81ef063633fec13c5cf741b..954e4e9883693afe0b5e4e411c20d5cd23731f0f:/utils/wxMMedia2/lib/sndwin.cpp?ds=inline diff --git a/utils/wxMMedia2/lib/sndwin.cpp b/utils/wxMMedia2/lib/sndwin.cpp index b4527bd17f..0d070fce80 100644 --- a/utils/wxMMedia2/lib/sndwin.cpp +++ b/utils/wxMMedia2/lib/sndwin.cpp @@ -7,8 +7,9 @@ // -------------------------------------------------------------------------- #include -#include +#include #include +#include #include #include "sndbase.h" #include "sndwin.h" @@ -20,7 +21,7 @@ typedef struct _wxSoundInternal wxSoundInternal; typedef struct _wxSoundInfoHeader wxSoundInfoHeader; -extern char wxCanvasClassName[]; +extern const wxChar *wxCanvasClassName; wxList *wxSoundHandleList = NULL; @@ -50,7 +51,7 @@ struct _wxSoundInfoHeader { wxSoundStreamWin *m_driver; }; -#define WXSOUND_MAX_QUEUE 128 +#define WXSOUND_MAX_QUEUE 10 wxSoundStreamWin::wxSoundStreamWin() { @@ -58,12 +59,22 @@ wxSoundStreamWin::wxSoundStreamWin() 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_internal->m_input_enabled = FALSE; + m_internal->m_output_enabled = FALSE; + + m_waiting_for = FALSE; + if (!OpenDevice(wxSOUND_OUTPUT)) return; @@ -72,27 +83,42 @@ 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) { + wxSoundStreamWin *sndwin; + + sndwin = wxFindSoundFromHandle((WXHWND)hWnd); + if (!sndwin) + return (LRESULT)0; + 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; - } default: break; } return (LRESULT)0; } +// ----------------------------------------------------------------------- +// CreateSndWindow() creates an hidden window which will receive the sound +// events +// ----------------------------------------------------------------------- + void wxSoundStreamWin::CreateSndWindow() { FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc, @@ -104,13 +130,18 @@ void wxSoundStreamWin::CreateSndWindow() wxGetInstance(), NULL); error = GetLastError(); - wxPrintf("%d\n", error); ::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); } +// ----------------------------------------------------------------------- +// DestroySndWindow() destroys the hidden window +// ----------------------------------------------------------------------- + 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; @@ -133,12 +175,15 @@ bool wxSoundStreamWin::OpenDevice(int mode) 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.nAvgBytesPerSec = wformat.nSamplesPerSec * wformat.nBlockAlign; wformat.wBitsPerSample = pcm->GetBPS(); wformat.cbSize = 0; + // ----------------------------------- + // Open the driver for Output operation + // ----------------------------------- if (mode & wxSOUND_OUTPUT) { MMRESULT result; @@ -157,6 +202,9 @@ bool wxSoundStreamWin::OpenDevice(int mode) m_internal->m_output_enabled = TRUE; } + // ----------------------------------- + // Open the driver for Input operation + // ----------------------------------- if (mode & wxSOUND_INPUT) { MMRESULT result; @@ -170,43 +218,65 @@ bool wxSoundStreamWin::OpenDevice(int mode) 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; } - 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; } - +// ------------------------------------------------------------------------- +// CloseDevice() closes the driver handles and frees memory allocated for +// IO queues. +// ------------------------------------------------------------------------- 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); - waveInReset(m_internal->m_devin); + m_internal->m_input_enabled = FALSE; 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; + // 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)); @@ -216,19 +286,24 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode) 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); + // Set the header's mode info->m_mode = mode; + // Set the parent of the header info->m_driver = this; + // Clean it up 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; + // "Prepare" the header if (mode == wxSOUND_INPUT) { MMRESULT result; @@ -236,6 +311,7 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode) 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); @@ -252,6 +328,7 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode) 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); @@ -265,6 +342,17 @@ wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode) 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; @@ -287,6 +375,13 @@ bool wxSoundStreamWin::AllocHeaders(int mode) 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) @@ -301,6 +396,14 @@ void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode) 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; @@ -313,7 +416,9 @@ void wxSoundStreamWin::FreeHeaders(int mode) for (i=0;im_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; - + } + m_waiting_for = TRUE; + // Else, we wait for its termination 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) { - 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) @@ -358,6 +491,12 @@ bool wxSoundStreamWin::AddToQueue(wxSoundInfoHeader *info) return TRUE; } +// ------------------------------------------------------------------------- +// ClearHeader(wxSoundInfoHeader *info) +// +// ClearHeader() reinitializes the parameters of "info" to their default +// value. +// ------------------------------------------------------------------------- void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info) { info->m_playing = FALSE; @@ -366,6 +505,13 @@ void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info) 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) { @@ -379,6 +525,9 @@ wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput() 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; @@ -389,6 +538,7 @@ wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len) wxSoundInfoHeader *header; wxUint32 to_copy; + // Get a new output fragment 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; + // If the fragment is full, we send it to the Windows queue. 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; } +// ------------------------------------------------------------------------- +// NextFragmentInput is not functional. +// ------------------------------------------------------------------------- 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]; - WaitFor(header); + if (header->m_recording) + WaitFor(header); if (m_current_frag_in == m_input_frag_in) m_queue_filled = TRUE; @@ -423,6 +579,9 @@ wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput() return header; } +// ------------------------------------------------------------------------- +// The behaviour of Read is documented in the global documentation. +// ------------------------------------------------------------------------- wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len) { wxSoundInfoHeader *header; @@ -455,30 +614,53 @@ wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len) 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; - 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; - OnSoundEvent(wxSOUND_OUTPUT); + if (!m_waiting_for) + OnSoundEvent(wxSOUND_OUTPUT); } 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; - OnSoundEvent(wxSOUND_INPUT); + if (!m_waiting_for) + OnSoundEvent(wxSOUND_INPUT); m_queue_filled = FALSE; } } +// ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base) { return wxSoundStream::SetSoundFormat(base); } +// ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- 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(); @@ -490,24 +672,33 @@ bool wxSoundStreamWin::StartProduction(int evt) 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;im_devin); } return TRUE; } +// ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- bool wxSoundStreamWin::StopProduction() { + if (!m_production_started) + return FALSE; + m_production_started = FALSE; CloseDevice(); return TRUE; } +// ------------------------------------------------------------------------- +// ------------------------------------------------------------------------- bool wxSoundStreamWin::QueueFilled() const { return (!m_production_started || m_queue_filled);