wxWidgets/utils/wxMMedia2/lib/sndwin.cpp

538 lines
13 KiB
C++
Raw Normal View History

// --------------------------------------------------------------------------
// Name: sndwin.cpp
// Purpose:
// Date: 08/11/1999
// Author: Guilhem Lavaux <lavaux@easynet.fr> (C) 1999
// CVSID: $Id$
// --------------------------------------------------------------------------
#include <wx/wxprec.h>
#include <wx/msw/private.h>
#include <wx/module.h>
#include <string.h>
#include "sndbase.h"
#include "sndwin.h"
#include "sndpcm.h"
#include <windows.h>
#include <mmsystem.h>
typedef struct _wxSoundInternal wxSoundInternal;
typedef struct _wxSoundInfoHeader wxSoundInfoHeader;
extern char wxCanvasClassName[];
wxList *wxSoundHandleList = NULL;
static inline wxSoundStreamWin *wxFindSoundFromHandle(WXHWND hWnd)
{
wxNode *node = wxSoundHandleList->Find((long)hWnd);
if (!node)
return NULL;
return (wxSoundStreamWin *)node->Data();
}
struct _wxSoundInternal {
HWND m_sndWin;
HWAVEIN m_devin;
HWAVEOUT m_devout;
bool m_output_enabled, m_input_enabled;
};
struct _wxSoundInfoHeader {
HGLOBAL m_h_header, m_h_data;
char *m_data;
WAVEHDR *m_header;
int m_mode;
bool m_playing, m_recording;
wxUint32 m_position, m_size;
wxSoundStreamWin *m_driver;
};
#define WXSOUND_MAX_QUEUE 128
wxSoundStreamWin::wxSoundStreamWin()
{
wxSoundFormatPcm pcm;
m_production_started = FALSE;
m_internal = new wxSoundInternal;
m_snderror = wxSOUND_NOERR;
// Setup defaults
CreateSndWindow();
SetSoundFormat(pcm);
if (!OpenDevice(wxSOUND_OUTPUT))
return;
CloseDevice();
}
wxSoundStreamWin::~wxSoundStreamWin()
{
if (m_production_started)
StopProduction();
DestroySndWindow();
delete m_internal;
}
LRESULT APIENTRY _EXPORT _wxSoundHandlerWndProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
switch (message) {
case MM_WOM_DONE: {
wxFindSoundFromHandle((WXHWND)hWnd)->NotifyDoneBuffer(wParam);
break;
}
default:
break;
}
return (LRESULT)0;
}
void wxSoundStreamWin::CreateSndWindow()
{
FARPROC proc = MakeProcInstance((FARPROC)_wxSoundHandlerWndProc,
wxGetInstance());
int error;
m_internal->m_sndWin = ::CreateWindow(wxCanvasClassName, NULL, 0,
0, 0, 0, 0, NULL, (HMENU) NULL,
wxGetInstance(), NULL);
error = GetLastError();
wxPrintf("%d\n", error);
::SetWindowLong(m_internal->m_sndWin, GWL_WNDPROC, (LONG)proc);
wxSoundHandleList->Append((long)m_internal->m_sndWin, (wxObject *)this);
}
void wxSoundStreamWin::DestroySndWindow()
{
if (m_internal->m_sndWin) {
::DestroyWindow(m_internal->m_sndWin);
wxSoundHandleList->DeleteObject((wxObject *)this);
}
}
bool wxSoundStreamWin::OpenDevice(int mode)
{
wxSoundFormatPcm *pcm;
WAVEFORMATEX wformat;
if (!m_sndformat) {
m_snderror = wxSOUND_INVFRMT;
return FALSE;
}
pcm = (wxSoundFormatPcm *)m_sndformat;
wformat.wFormatTag = WAVE_FORMAT_PCM;
wformat.nChannels = pcm->GetChannels();
wformat.nBlockAlign = pcm->GetBPS() / 8 * wformat.nChannels;
wformat.nAvgBytesPerSec = pcm->GetBytesFromTime(1);
wformat.nSamplesPerSec = pcm->GetSampleRate();
wformat.wBitsPerSample = pcm->GetBPS();
wformat.cbSize = 0;
if (mode & wxSOUND_OUTPUT) {
MMRESULT result;
result = waveOutOpen(&m_internal->m_devout,
WAVE_MAPPER, &wformat,
(DWORD)m_internal->m_sndWin, 0,
CALLBACK_WINDOW);
if (result != MMSYSERR_NOERROR) {
m_snderror = wxSOUND_INVDEV;
return FALSE;
}
m_output_frag_out = WXSOUND_MAX_QUEUE-1;
m_current_frag_out = 0;
m_internal->m_output_enabled = TRUE;
}
if (mode & wxSOUND_INPUT) {
MMRESULT result;
result = waveInOpen(&m_internal->m_devin,
WAVE_MAPPER, &wformat,
(DWORD)m_internal->m_sndWin, 0,
CALLBACK_WINDOW);
if (result != MMSYSERR_NOERROR) {
m_snderror = wxSOUND_INVDEV;
return FALSE;
}
m_input_frag_in = WXSOUND_MAX_QUEUE-1;
m_current_frag_in = 0;
m_internal->m_input_enabled = TRUE;
}
if (!AllocHeaders(mode)) {
CloseDevice();
return FALSE;
}
return TRUE;
}
void wxSoundStreamWin::CloseDevice()
{
if (m_internal->m_output_enabled) {
FreeHeaders(wxSOUND_OUTPUT);
waveOutReset(m_internal->m_devout);
waveOutClose(m_internal->m_devout);
}
if (m_internal->m_input_enabled) {
FreeHeaders(wxSOUND_INPUT);
waveInReset(m_internal->m_devin);
waveInClose(m_internal->m_devin);
}
m_internal->m_output_enabled = FALSE;
m_internal->m_input_enabled = FALSE;
}
wxSoundInfoHeader *wxSoundStreamWin::AllocHeader(int mode)
{
wxSoundInfoHeader *info;
WAVEHDR *header;
info = new wxSoundInfoHeader;
info->m_h_data = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, GetBestSize());
info->m_h_header = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
if (!info->m_h_data || !info->m_h_header) {
delete info;
m_snderror = wxSOUND_MEMERR;
return NULL;
}
info->m_data = (char *)GlobalLock(info->m_h_data);
info->m_header = (WAVEHDR *)GlobalLock(info->m_h_header);
info->m_mode = mode;
info->m_driver = this;
ClearHeader(info);
header = info->m_header;
header->lpData = info->m_data;
header->dwBufferLength = GetBestSize();
header->dwUser = (DWORD)info;
header->dwFlags = WHDR_DONE;
if (mode == wxSOUND_INPUT) {
MMRESULT result;
result = waveInPrepareHeader(m_internal->m_devin, header,
sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR) {
GlobalUnlock(info->m_data);
GlobalUnlock(info->m_header);
GlobalFree(info->m_h_data);
GlobalFree(info->m_h_header);
delete info;
m_snderror = wxSOUND_IOERR;
return NULL;
}
} else if (mode == wxSOUND_OUTPUT) {
MMRESULT result;
result = waveOutPrepareHeader(m_internal->m_devout, header,
sizeof(WAVEHDR));
if (result != MMSYSERR_NOERROR) {
GlobalUnlock(info->m_data);
GlobalUnlock(info->m_header);
GlobalFree(info->m_h_data);
GlobalFree(info->m_h_header);
delete info;
m_snderror = wxSOUND_IOERR;
return NULL;
}
}
return info;
}
bool wxSoundStreamWin::AllocHeaders(int mode)
{
int i;
wxSoundInfoHeader **headers;
if (mode == wxSOUND_OUTPUT)
headers = m_headers_play = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
else
headers = m_headers_rec = new wxSoundInfoHeader *[WXSOUND_MAX_QUEUE];
memset(headers, 0, WXSOUND_MAX_QUEUE*sizeof(wxSoundInfoHeader *));
for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
headers[i] = AllocHeader(mode);
if (!headers[i]) {
FreeHeaders(mode);
return FALSE;
}
}
return TRUE;
}
void wxSoundStreamWin::FreeHeader(wxSoundInfoHeader *header, int mode)
{
if (mode == wxSOUND_OUTPUT)
waveOutUnprepareHeader(m_internal->m_devout, header->m_header, sizeof(WAVEHDR));
else
waveInUnprepareHeader(m_internal->m_devin, header->m_header, sizeof(WAVEHDR));
GlobalUnlock(header->m_data);
GlobalUnlock(header->m_header);
GlobalFree(header->m_h_header);
GlobalFree(header->m_h_data);
delete header;
}
void wxSoundStreamWin::FreeHeaders(int mode)
{
int i;
wxSoundInfoHeader ***headers;
if (mode == wxSOUND_OUTPUT)
headers = &m_headers_play;
else
headers = &m_headers_rec;
for (i=0;i<WXSOUND_MAX_QUEUE;i++) {
if ((*headers)[i]) {
WaitFor((*headers)[i]);
FreeHeader((*headers)[i], mode);
}
}
delete[] (*headers);
(*headers) = NULL;
}
void wxSoundStreamWin::WaitFor(wxSoundInfoHeader *info)
{
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)
return;
while (info->m_playing || info->m_recording)
wxYield();
}
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;
result = waveInAddBuffer(m_internal->m_devin,
info->m_header, sizeof(WAVEHDR));
if (result == MMSYSERR_NOERROR)
info->m_recording = TRUE;
else
return FALSE;
} else if (info->m_mode == wxSOUND_OUTPUT) {
result = waveOutWrite(m_internal->m_devout,
info->m_header, sizeof(WAVEHDR));
if (result == MMSYSERR_NOERROR)
info->m_playing = TRUE;
else
return FALSE;
}
return TRUE;
}
void wxSoundStreamWin::ClearHeader(wxSoundInfoHeader *info)
{
info->m_playing = FALSE;
info->m_recording = FALSE;
info->m_position = 0;
info->m_size = GetBestSize();
}
wxSoundInfoHeader *wxSoundStreamWin::NextFragmentOutput()
{
if (m_headers_play[m_current_frag_out]->m_playing) {
m_current_frag_out = (m_current_frag_out + 1) % WXSOUND_MAX_QUEUE;
if (m_headers_play[m_current_frag_out]->m_playing)
WaitFor(m_headers_play[m_current_frag_out]);
}
if (m_current_frag_out == m_output_frag_out)
m_queue_filled = TRUE;
return m_headers_play[m_current_frag_out];
}
wxSoundStream& wxSoundStreamWin::Write(const void *buffer, wxUint32 len)
{
m_lastcount = 0;
if (!m_internal->m_output_enabled)
return *this;
while (len > 0) {
wxSoundInfoHeader *header;
wxUint32 to_copy;
header = NextFragmentOutput();
to_copy = (len > header->m_size) ? header->m_size : len;
memcpy(header->m_data + header->m_position, buffer, to_copy);
header->m_position += to_copy;
header->m_size -= to_copy;
buffer = (((const char *)buffer) + to_copy);
len -= to_copy;
m_lastcount += to_copy;
if (header->m_size == 0)
if (!AddToQueue(header)) {
m_snderror = wxSOUND_IOERR;
return *this;
}
}
return *this;
}
wxSoundInfoHeader *wxSoundStreamWin::NextFragmentInput()
{
wxSoundInfoHeader *header;
// TODO //
header = m_headers_rec[m_current_frag_in];
WaitFor(header);
if (m_current_frag_in == m_input_frag_in)
m_queue_filled = TRUE;
return header;
}
wxSoundStream& wxSoundStreamWin::Read(void *buffer, wxUint32 len)
{
wxSoundInfoHeader *header;
wxUint32 to_copy;
m_lastcount = 0;
if (!m_internal->m_input_enabled)
return *this;
while (len > 0) {
header = NextFragmentInput();
to_copy = (len > header->m_size) ? header->m_size : len;
memcpy(buffer, header->m_data + header->m_position, to_copy);
header->m_position += to_copy;
header->m_size -= to_copy;
buffer = (((char *)buffer) + to_copy);
len -= to_copy;
m_lastcount += to_copy;
if (header->m_size == 0) {
ClearHeader(header);
if (!AddToQueue(header)) {
m_snderror = wxSOUND_IOERR;
return *this;
}
}
}
return *this;
}
void wxSoundStreamWin::NotifyDoneBuffer(wxUint32 dev_handle)
{
wxSoundInfoHeader *info;
if (dev_handle == (wxUint32)m_internal->m_devout) {
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);
} else {
m_input_frag_in = (m_input_frag_in + 1) % WXSOUND_MAX_QUEUE;
OnSoundEvent(wxSOUND_INPUT);
m_queue_filled = FALSE;
}
}
bool wxSoundStreamWin::SetSoundFormat(wxSoundFormatBase& base)
{
return wxSoundStream::SetSoundFormat(base);
}
bool wxSoundStreamWin::StartProduction(int evt)
{
if ((m_internal->m_output_enabled && (evt & wxSOUND_OUTPUT)) ||
(m_internal->m_input_enabled && (evt & wxSOUND_INPUT)))
CloseDevice();
if (!OpenDevice(evt))
return FALSE;
m_production_started = TRUE;
m_queue_filled = FALSE;
// Send a dummy event to start.
if (evt & wxSOUND_OUTPUT)
OnSoundEvent(evt);
if (evt & wxSOUND_INPUT) {
int i;
for (i=0;i<WXSOUND_MAX_QUEUE;i++)
AddToQueue(m_headers_rec[i]);
}
return TRUE;
}
bool wxSoundStreamWin::StopProduction()
{
m_production_started = FALSE;
CloseDevice();
return TRUE;
}
bool wxSoundStreamWin::QueueFilled() const
{
return (!m_production_started || m_queue_filled);
}
// --------------------------------------------------------------------------
// wxSoundWinModule
// --------------------------------------------------------------------------
class WXDLLEXPORT wxSoundWinModule : public wxModule {
DECLARE_DYNAMIC_CLASS(wxSoundWinModule)
public:
bool OnInit();
void OnExit();
};
IMPLEMENT_DYNAMIC_CLASS(wxSoundWinModule, wxModule)
bool wxSoundWinModule::OnInit() {
wxSoundHandleList = new wxList(wxKEY_INTEGER);
return TRUE;
}
void wxSoundWinModule::OnExit() {
delete wxSoundHandleList;
}