Bochs/bochs/iodev/soundwin.cc
Stanislav Shwartsman 5873b26a82 Speed up compilation process.
bochs.h already not include iodev.h which reduces compilation dependences for almost all cpu and fpu files, now cpu files will not be recompiled if iodev includes was changed
2004-06-19 15:20:15 +00:00

522 lines
14 KiB
C++

/////////////////////////////////////////////////////////////////////////
// $Id: soundwin.cc,v 1.14 2004-06-19 15:20:14 sshwarts Exp $
/////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2001 MandrakeSoft S.A.
//
// MandrakeSoft S.A.
// 43, rue d'Aboukir
// 75002 Paris - France
// http://www.linux-mandrake.com/
// http://www.mandrakesoft.com/
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
// This file (SOUNDWIN.CC) written and donated by Josef Drexler
// Define BX_PLUGGABLE in files that can be compiled into plugins. For
// platforms that require a special tag on exported symbols, BX_PLUGGABLE
// is used to know when we are exporting symbols and when we are importing.
#define BX_PLUGGABLE
#include "iodev.h"
#if defined(WIN32) && BX_SUPPORT_SB16
#define LOG_THIS bx_devices.pluginSB16Device->
bx_sound_windows_c::bx_sound_windows_c(bx_sb16_c *sb16)
:bx_sound_output_c(sb16)
{
this->sb16 = sb16;
MidiOpen = 0;
WaveOpen = 0;
ismidiready = 1;
iswaveready = 1;
// size is the total size of the midi header and buffer and the
// BX_SOUND_WINDOWS_NBUF wave header and buffers, all aligned
// on a 16-byte boundary
#define ALIGN(size) ( (size + 15) & ~15 )
#define size ALIGN(sizeof(MIDIHDR)) \
+ ALIGN(sizeof(WAVEHDR)) \
+ ALIGN(BX_SOUND_WINDOWS_MAXSYSEXLEN) * BX_SOUND_WINDOWS_NBUF \
+ ALIGN(BX_SOUND_OUTPUT_WAVEPACKETSIZE) * BX_SOUND_WINDOWS_NBUF
DataHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, size);
DataPointer = (Bit8u*) GlobalLock(DataHandle);
if (DataPointer == NULL)
BX_PANIC(("GlobalLock returned NULL-pointer"));
#define NEWBUFFER(size) &(DataPointer[offset]); offset += ALIGN(size)
int offset = 0;
MidiHeader = (LPMIDIHDR) NEWBUFFER(sizeof(MIDIHDR));
MidiData = (LPSTR) NEWBUFFER(BX_SOUND_WINDOWS_MAXSYSEXLEN);
for (int bufnum=0; bufnum<BX_SOUND_WINDOWS_NBUF; bufnum++)
{
WaveHeader[bufnum] = (LPWAVEHDR) NEWBUFFER(sizeof(WAVEHDR));
WaveData[bufnum] = (LPSTR) NEWBUFFER(BX_SOUND_OUTPUT_WAVEPACKETSIZE);
}
if (offset > size)
BX_PANIC(("Allocated memory was too small!"));
#undef size
#undef ALIGN
#undef NEWBUFFER
}
bx_sound_windows_c::~bx_sound_windows_c()
{
GlobalUnlock(DataHandle);
GlobalFree(DataHandle);
}
int bx_sound_windows_c::waveready()
{
if (iswaveready == 0)
checkwaveready();
if (iswaveready == 1)
return BX_SOUND_OUTPUT_OK;
else
return BX_SOUND_OUTPUT_ERR;
}
int bx_sound_windows_c::midiready()
{
if (ismidiready == 0)
checkmidiready();
if (ismidiready == 1)
return BX_SOUND_OUTPUT_OK;
else
return BX_SOUND_OUTPUT_ERR;
}
int bx_sound_windows_c::openmidioutput(char *device)
{
// could make the output device selectable,
// but currently only the midi mapper is supported
UNUSED(device);
UINT deviceid = (UINT) MIDIMAPPER;
MidiOpen = 0;
UINT ret = midiOutOpen( &MidiOut, deviceid, 0, 0, CALLBACK_NULL);
if (ret == 0)
MidiOpen = 1;
WRITELOG( MIDILOG(4), "midiOutOpen() = %d, MidiOpen: %d", ret, MidiOpen);
return (MidiOpen == 1) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR;
}
int bx_sound_windows_c::sendmidicommand(int delta, int command, int length, Bit8u data[])
{
UINT ret;
if (MidiOpen != 1)
return BX_SOUND_OUTPUT_ERR;
if ( (command == 0xf0) || (command == 0xf7) || (length > 3) )
{
WRITELOG( WAVELOG(5), "SYSEX started, length %d", length);
ismidiready = 0; // until the buffer is done
memcpy(MidiData, data, length);
MidiHeader->lpData = MidiData;
MidiHeader->dwBufferLength = BX_SOUND_WINDOWS_MAXSYSEXLEN;
MidiHeader->dwBytesRecorded = 0;
MidiHeader->dwUser = 0;
MidiHeader->dwFlags = 0;
ret = midiOutPrepareHeader(MidiOut, MidiHeader, sizeof(*MidiHeader));
if (ret != 0)
WRITELOG( MIDILOG(2), "midiOutPrepareHeader() = %d", ret);
ret = midiOutLongMsg(MidiOut, MidiHeader, sizeof(*MidiHeader));
if (ret != 0)
WRITELOG( MIDILOG(2), "midiOutLongMsg() = %d", ret);
}
else
{
DWORD msg = command;
for (int i = 0; i<length; i++)
msg |= (data[i] << (8 * (i + 1) ) );
ret = midiOutShortMsg(MidiOut, msg);
WRITELOG( MIDILOG(4), "midiOutShortMsg(%x) = %d", msg, ret);
}
return (ret == 0) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR;
}
int bx_sound_windows_c::closemidioutput()
{
UINT ret;
if (MidiOpen != 1)
return BX_SOUND_OUTPUT_ERR;
ret = midiOutReset(MidiOut);
if (ismidiready == 0)
checkmidiready(); // to clear any pending SYSEX
ret = midiOutClose(MidiOut);
WRITELOG( MIDILOG(4), "midiOutClose() = %d", ret);
MidiOpen = 0;
return (ret == 0) ? BX_SOUND_OUTPUT_OK : BX_SOUND_OUTPUT_ERR;
}
int bx_sound_windows_c::openwaveoutput(char *device)
{
// could make the output device selectable,
// but currently only the midi mapper is supported
UNUSED(device);
WRITELOG( WAVELOG(4), "openwaveoutput(%s)", device);
#ifdef usewaveOut
WaveDevice = (UINT) WAVEMAPPER;
for (int i=0; i<BX_SOUND_WINDOWS_NBUF; i++)
WaveHeader[i]->dwFlags = WHDR_DONE;
head = 0;
tailfull = 0;
tailplay = 0;
needreopen = 0;
#endif
return BX_SOUND_OUTPUT_OK;
}
int bx_sound_windows_c::playnextbuffer()
{
UINT ret;
PCMWAVEFORMAT waveformat;
int bufnum;
// if the format is different, we have to reopen the device,
// so reset it first
if (needreopen != 0)
if (WaveOpen != 0)
ret = waveOutReset( WaveOut );
// clean up the buffers and mark if output is ready
checkwaveready();
// do we have to play anything?
if (tailplay == head)
return BX_SOUND_OUTPUT_OK;
// if the format is different, we have to close and reopen the device
// or, just open the device if it's not open yet
if ( (needreopen != 0) || (WaveOpen == 0) )
{
if (WaveOpen != 0)
{
ret = waveOutClose( WaveOut );
WaveOpen = 0;
}
// try three times to find a suitable format
for (int tries = 0; tries < 3; tries++)
{
int frequency = WaveInfo.frequency;
int stereo = WaveInfo.stereo;
int bits = WaveInfo.bits;
int format = WaveInfo.format;
int bps = (bits / 8) * (stereo + 1);
waveformat.wf.wFormatTag = WAVE_FORMAT_PCM;
waveformat.wf.nChannels = stereo + 1;
waveformat.wf.nSamplesPerSec = frequency;
waveformat.wf.nAvgBytesPerSec = frequency * bps;
waveformat.wf.nBlockAlign = bps;
waveformat.wBitsPerSample = bits;
ret = waveOutOpen( &(WaveOut), WaveDevice, (LPWAVEFORMATEX)&(waveformat.wf), 0, 0, CALLBACK_NULL);
if (ret != 0)
{
char errormsg[4*MAXERRORLENGTH+1];
waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
WRITELOG( WAVELOG(5), "waveOutOpen: %s", errormsg);
switch (tries)
{
case 0: // maybe try a different frequency
if (frequency < 15600)
frequency = 11025;
else if (frequency < 31200)
frequency = 22050;
else
frequency = 44100;
WRITELOG( WAVELOG(4), "Couldn't open wave device (error %d), trying frequency %d", ret, frequency);
break;
case 1: // or something else
frequency = 11025;
stereo = 0;
bits = 8;
bps = 1;
WRITELOG( WAVELOG(4), "Couldn't open wave device again (error %d), trying 11KHz, mono, 8bit", ret);
break;
case 2: // nope, doesn't work
WRITELOG( WAVELOG(2), "Couldn't open wave device (error %d)!", ret);
return BX_SOUND_OUTPUT_ERR;
}
WRITELOG( WAVELOG(5), "The format was: wFormatTag=%d, nChannels=%d, nSamplesPerSec=%d,",
waveformat.wf.wFormatTag, waveformat.wf.nChannels, waveformat.wf.nSamplesPerSec);
WRITELOG( WAVELOG(5), " nAvgBytesPerSec=%d, nBlockAlign=%d, wBitsPerSample=%d",
waveformat.wf.nAvgBytesPerSec, waveformat.wf.nBlockAlign, waveformat.wBitsPerSample);
}
else
{
WaveOpen = 1;
needreopen = 0;
break;
}
}
}
for (bufnum=tailplay; bufnum != head;
bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK, tailplay=bufnum)
{
WRITELOG( WAVELOG(5), "Playing buffer %d", bufnum);
// prepare the wave header
WaveHeader[bufnum]->lpData = WaveData[bufnum];
WaveHeader[bufnum]->dwBufferLength = length[bufnum];
WaveHeader[bufnum]->dwBytesRecorded = length[bufnum];
WaveHeader[bufnum]->dwUser = 0;
WaveHeader[bufnum]->dwFlags = 0;
WaveHeader[bufnum]->dwLoops = 1;
ret = waveOutPrepareHeader(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
if (ret != 0)
{
WRITELOG( WAVELOG(2), "waveOutPrepareHeader = %d", ret);
return BX_SOUND_OUTPUT_ERR;
}
ret = waveOutWrite(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
if (ret != 0)
{
char errormsg[4*MAXERRORLENGTH+1];
waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
WRITELOG( WAVELOG(5), "waveOutWrite: %s", errormsg);
}
}
return BX_SOUND_OUTPUT_OK;
}
int bx_sound_windows_c::startwaveplayback(int frequency, int bits, int stereo, int format)
{
// UINT ret;
WRITELOG( WAVELOG(4), "startwaveplayback(%d, %d, %d, %x)", frequency, bits, stereo, format);
#ifdef usewaveOut
// check if any of the properties have changed
if ( (WaveInfo.frequency != frequency) ||
(WaveInfo.bits != bits) ||
(WaveInfo.stereo != stereo) ||
(WaveInfo.format != format) )
{
needreopen = 1;
// store the current settings to be used by sendwavepacket()
WaveInfo.frequency = frequency;
WaveInfo.bits = bits;
WaveInfo.stereo = stereo;
WaveInfo.format = format;
}
#endif
#ifdef usesndPlaySnd
int bps = (bits / 8) * (stereo + 1);
LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData;
memcpy(header->RIFF, "RIFF", 4);
memcpy(header->TYPE, "WAVE", 4);
memcpy(header->chnk, "fmt ", 4);
header->chnklen = 16;
header->waveformat.wf.wFormatTag = WAVE_FORMAT_PCM;
header->waveformat.wf.nChannels = stereo + 1;
header->waveformat.wf.nSamplesPerSec = frequency;
header->waveformat.wf.nAvgBytesPerSec = frequency * bps;
header->waveformat.wf.nBlockAlign = bps;
header->waveformat.wBitsPerSample = bits;
memcpy(header->chnk2, "data", 4);
#endif
return BX_SOUND_OUTPUT_OK;
}
int bx_sound_windows_c::sendwavepacket(int length, Bit8u data[])
{
// UINT ret;
int bufnum;
WRITELOG( WAVELOG(4), "sendwavepacket(%d, %p)", length, data);
#ifdef usewaveOut
bufnum = head;
memcpy(WaveData[bufnum], data, length);
this->length[bufnum] = length;
// select next buffer to write to
bufnum++;
bufnum &= BX_SOUND_WINDOWS_NMASK;
if ( ( (bufnum + 1) & BX_SOUND_WINDOWS_NMASK) == tailfull )
{ // this should not actually happen!
WRITELOG( WAVELOG(2), "Output buffer overflow! Not played. Iswaveready was %d", iswaveready);
iswaveready = 0; // stop the output for a while
return BX_SOUND_OUTPUT_ERR;
}
head = bufnum;
// check if more buffers are available, otherwise stall the emulator
if ( ( (bufnum + 2) & BX_SOUND_WINDOWS_NMASK) == tailfull )
{
WRITELOG( WAVELOG(5), "Buffer status: Head %d, TailFull %d, TailPlay %d. Stall.",
head, tailfull, tailplay);
iswaveready = 0;
}
playnextbuffer();
#endif
#ifdef usesndPlaySnd
LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData;
header->length = length + 36;
header->chnk2len = length;
memcpy( &(header->data), data, length);
FILE *test = fopen("test", "a");
fwrite(WaveData, 1, length + 44, test);
fclose(test);
ret = sndPlaySoundA( (LPCSTR) header, SND_SYNC | SND_MEMORY );
if (ret != 0)
{
WRITELOG( WAVELOG(3), "sndPlaySoundA: %d", ret);
}
#endif
return BX_SOUND_OUTPUT_OK;
}
int bx_sound_windows_c::stopwaveplayback()
{
WRITELOG( WAVELOG(4), "stopwaveplayback()");
#ifdef usewaveOut
// this is handled by checkwaveready() when closing
#endif
#ifdef usesndPlaySnd
sndPlaySoundA( NULL, SND_ASYNC | SND_MEMORY );
WaveOpen = 0;
#endif
return BX_SOUND_OUTPUT_OK;
}
int bx_sound_windows_c::closewaveoutput()
{
// int bufnum;
WRITELOG( WAVELOG(4), "closewaveoutput");
#ifdef usewaveOut
if (WaveOpen == 1)
{
waveOutReset(WaveOut);
// let checkwaveready() clean up the buffers
checkwaveready();
waveOutClose(WaveOut);
head = 0;
tailfull = 0;
tailplay = 0;
needreopen = 0;
}
#endif
return BX_SOUND_OUTPUT_OK;
}
void bx_sound_windows_c::checkmidiready()
{
UINT ret;
if ( (MidiHeader->dwFlags & WHDR_DONE) != 0)
{
WRITELOG( MIDILOG(5), "SYSEX message done, midi ready again.");
ret = midiOutUnprepareHeader( MidiOut, MidiHeader, sizeof(*MidiHeader));
ismidiready = 1;
}
}
void bx_sound_windows_c::checkwaveready()
{
int bufnum;
UINT ret;
// clean up all finished buffers and mark them as available
for (bufnum=tailfull;
(bufnum != tailplay) &&
( (WaveHeader[bufnum]->dwFlags & WHDR_DONE) != 0);
bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK)
{
WRITELOG( WAVELOG(5), "Buffer %d done.", bufnum);
ret = waveOutUnprepareHeader(WaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
}
tailfull = bufnum;
// enable gathering data if a buffer is available
if ( ( (head + 2) & BX_SOUND_WINDOWS_NMASK) != tailfull )
{
WRITELOG( WAVELOG(5), "Buffer status: Head %d, TailFull %d, TailPlay %d. Ready.",
head, tailfull, tailplay);
iswaveready = 1;
}
}
#endif // defined(WIN32)