// --------------------------------------------------------------------------
#include <wx/wxprec.h>
-#include <wx/msw/private.h>
+#include <wx/app.h>
#include <wx/module.h>
+#include <wx/msw/private.h>
#include <string.h>
#include "sndbase.h"
#include "sndwin.h"
typedef struct _wxSoundInternal wxSoundInternal;
typedef struct _wxSoundInfoHeader wxSoundInfoHeader;
-extern char wxCanvasClassName[];
+extern const wxChar *wxCanvasClassName;
wxList *wxSoundHandleList = NULL;
wxSoundStreamWin *m_driver;
};
-#define WXSOUND_MAX_QUEUE 128
+#define WXSOUND_MAX_QUEUE 10
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;
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,
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) {
}
}
+// -------------------------------------------------------------------------
+// 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;
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;
m_internal->m_output_enabled = TRUE;
}
+ // -----------------------------------
+ // Open the driver for Input operation
+ // -----------------------------------
if (mode & wxSOUND_INPUT) {
MMRESULT result;
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));
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;
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);
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);
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;
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)
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;
for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
if ((*headers)[i]) {
+ // We wait for the end of the buffer
WaitFor((*headers)[i]);
+ // Then, we free the header
FreeHeader((*headers)[i], mode);
}
}
(*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)
{
- 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;
-
+ }
+ 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)
return TRUE;
}
+// -------------------------------------------------------------------------
+// ClearHeader(wxSoundInfoHeader *info)
+//
+// ClearHeader() reinitializes the parameters of "info" to their default
+// value.
+// -------------------------------------------------------------------------
void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
{
info->m_playing = FALSE;
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) {
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;
wxSoundInfoHeader *header;
wxUint32 to_copy;
+ // Get a new output fragment
header = NextFragmentOutput();
to_copy = (len > header->m_size) ? header->m_size : 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;
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;
return header;
}
+// -------------------------------------------------------------------------
+// The behaviour of Read is documented in the global documentation.
+// -------------------------------------------------------------------------
wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
{
wxSoundInfoHeader *header;
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();
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]);
+
+ waveInStart(m_internal->m_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);