Implemented support for the new mixer thread in the lowlevel sound module 'win'.

- removed disabled code for the sndPlaySound() function.
- removed now obsolete buffer ring for wave output.
- The new mothod waveout() is driven by the mixer thread. For the best results
  you need to disable the 'realtime' sychronization and to find out a usable
  IPS vaöue. The mixer also polls data from the speaker beep generator and
  the OPL3 FM generator.
- TODO: Code cleanup in soundwin.cc, implementation in other sound modules.
This commit is contained in:
Volker Ruppert 2015-02-06 15:57:26 +00:00
parent 1e52f0cbe0
commit d27972cc42
2 changed files with 83 additions and 286 deletions

View File

@ -42,7 +42,6 @@ bx_sound_windows_c::bx_sound_windows_c()
WaveInOpen = 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
@ -67,11 +66,7 @@ bx_sound_windows_c::bx_sound_windows_c()
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_SOUNDLOW_WAVEPACKETSIZE+64);
}
WaveOutHdr = (LPWAVEHDR) NEWBUFFER(sizeof(WAVEHDR));
WaveInHdr = (LPWAVEHDR) NEWBUFFER(sizeof(WAVEHDR));
WaveInData = (LPSTR) NEWBUFFER(BX_SOUNDLOW_WAVEPACKETSIZE+64);
@ -91,16 +86,6 @@ bx_sound_windows_c::~bx_sound_windows_c()
GlobalFree(DataHandle);
}
int bx_sound_windows_c::waveready()
{
if (iswaveready == 0)
checkwaveready();
if (iswaveready == 1)
return BX_SOUNDLOW_OK;
else
return BX_SOUNDLOW_ERR;
}
int bx_sound_windows_c::midiready()
{
if (ismidiready == 0)
@ -202,74 +187,48 @@ int bx_sound_windows_c::openwaveoutput(const char *wavedev)
BX_DEBUG(("openwaveoutput(%s)", wavedev));
#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
set_pcm_params(real_pcm_param);
pcm_callback_id = register_wave_callback(this, pcm_callback);
BX_INIT_MUTEX(mixer_mutex);
start_mixer_thread();
return BX_SOUNDLOW_OK;
}
int bx_sound_windows_c::playnextbuffer()
int bx_sound_windows_c::set_pcm_params(bx_pcm_param_t param)
{
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 (WaveOutOpen != 0)
ret = waveOutReset(hWaveOut);
BX_DEBUG(("set_pcm_params(): %u, %u, %u, %02x", param.samplerate, param.bits,
param.channels, param.format));
if (WaveOutOpen != 0) {
ret = waveOutReset(hWaveOut);
ret = waveOutClose(hWaveOut);
WaveOutOpen = 0;
}
// clean up the buffers and mark if output is ready
checkwaveready();
// try three times to find a suitable format
for (int tries = 0; tries < 3; tries++) {
int frequency = real_pcm_param.samplerate;
bx_bool stereo = real_pcm_param.channels == 2;
int bits = real_pcm_param.bits;
int bps = (bits / 8) * (stereo + 1);
// do we have to play anything?
if (tailplay == head)
return BX_SOUNDLOW_OK;
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;
// 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) || (WaveOutOpen == 0))
{
if (WaveOutOpen != 0)
{
ret = waveOutClose(hWaveOut);
WaveOutOpen = 0;
}
// try three times to find a suitable format
for (int tries = 0; tries < 3; tries++)
{
int frequency = WaveInfo[0].frequency;
bx_bool stereo = WaveInfo[0].stereo;
int bits = WaveInfo[0].bits;
// int format = WaveInfo[0].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(&(hWaveOut), WaveDevice, (LPWAVEFORMATEX)&(waveformat.wf), 0, 0, CALLBACK_NULL);
if (ret != 0)
{
char errormsg[4*MAXERRORLENGTH+1];
waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
BX_DEBUG(("waveOutOpen: %s", errormsg));
switch (tries) {
ret = waveOutOpen(&(hWaveOut), WaveDevice, (LPWAVEFORMATEX)&(waveformat.wf), 0, 0, CALLBACK_NULL);
if (ret != 0) {
char errormsg[4*MAXERRORLENGTH+1];
waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
BX_DEBUG(("waveOutOpen: %s", errormsg));
switch (tries) {
case 0: // maybe try a different frequency
if (frequency < 15600)
frequency = 11025;
@ -293,180 +252,46 @@ int bx_sound_windows_c::playnextbuffer()
case 2: // nope, doesn't work
BX_ERROR(("Couldn't open wave output device (error = %d)!", ret));
return BX_SOUNDLOW_ERR;
}
BX_DEBUG(("The format was: wFormatTag=%d, nChannels=%d, nSamplesPerSec=%d,",
waveformat.wf.wFormatTag, waveformat.wf.nChannels, waveformat.wf.nSamplesPerSec));
BX_DEBUG((" nAvgBytesPerSec=%d, nBlockAlign=%d, wBitsPerSample=%d",
waveformat.wf.nAvgBytesPerSec, waveformat.wf.nBlockAlign, waveformat.wBitsPerSample));
}
else
{
WaveOutOpen = 1;
needreopen = 0;
break;
}
}
}
for (bufnum=tailplay; bufnum != head;
bufnum++, bufnum &= BX_SOUND_WINDOWS_NMASK, tailplay=bufnum)
{
BX_DEBUG(("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(hWaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
if (ret != 0)
{
BX_ERROR(("waveOutPrepareHeader(): error = %d", ret));
return BX_SOUNDLOW_ERR;
}
ret = waveOutWrite(hWaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
if (ret != 0)
{
char errormsg[4*MAXERRORLENGTH+1];
waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
BX_ERROR(("waveOutWrite(): %s", errormsg));
BX_DEBUG(("The format was: wFormatTag=%d, nChannels=%d, nSamplesPerSec=%d,",
waveformat.wf.wFormatTag, waveformat.wf.nChannels, waveformat.wf.nSamplesPerSec));
BX_DEBUG((" nAvgBytesPerSec=%d, nBlockAlign=%d, wBitsPerSample=%d",
waveformat.wf.nAvgBytesPerSec, waveformat.wf.nBlockAlign, waveformat.wBitsPerSample));
} else {
WaveOutOpen = 1;
break;
}
}
return BX_SOUNDLOW_OK;
}
int bx_sound_windows_c::set_pcm_params(bx_pcm_param_t param)
int bx_sound_windows_c::waveout(int length, Bit8u data[])
{
BX_DEBUG(("set_pcm_params(): %u, %u, %u, %02x", param.samplerate, param.bits,
param.channels, param.format));
#ifdef usewaveOut
// check if any of the properties have changed
if ((WaveInfo[0].frequency != param.samplerate) ||
(WaveInfo[0].bits != param.bits) ||
(WaveInfo[0].stereo != (param.channels == 2)) ||
(WaveInfo[0].format != param.format))
{
needreopen = 1;
// store the current settings to be used by sendwavepacket()
WaveInfo[0].frequency = param.samplerate;
WaveInfo[0].bits = param.bits;
WaveInfo[0].stereo = (param.channels == 2);
WaveInfo[0].format = param.format;
}
#endif
#ifdef usesndPlaySnd
int bps = (param.bits / 8) * param.channels;
LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData[0];
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 = param.channels;
header->waveformat.wf.nSamplesPerSec = param.samplerate;
header->waveformat.wf.nAvgBytesPerSec = param.samplerate * bps;
header->waveformat.wf.nBlockAlign = bps;
header->waveformat.wBitsPerSample = param.bits;
memcpy(header->chnk2, "data", 4);
#endif
return BX_SOUNDLOW_OK;
}
int bx_sound_windows_c::sendwavepacket(int length, Bit8u data[], bx_pcm_param_t *src_param)
{
int len2;
#ifdef usewaveOut
int bufnum;
#endif
#ifdef usesndPlaySnd
UINT ret;
#endif
BX_DEBUG(("sendwavepacket(%d, %p)", length, data));
// prepare the wave header
WaveOutHdr->lpData = (LPSTR)data;
WaveOutHdr->dwBufferLength = length;
WaveOutHdr->dwBytesRecorded = length;
WaveOutHdr->dwUser = 0;
WaveOutHdr->dwFlags = 0;
WaveOutHdr->dwLoops = 1;
if (memcmp(src_param, &emu_pcm_param, sizeof(bx_pcm_param_t)) != 0) {
emu_pcm_param = *src_param;
cvt_mult = (src_param->bits == 8) ? 2 : 1;
if (src_param->channels == 1) cvt_mult <<= 1;
if (src_param->samplerate != real_pcm_param.samplerate) {
real_pcm_param.samplerate = src_param->samplerate;
set_pcm_params(real_pcm_param);
}
}
len2 = length * cvt_mult;
#ifdef usewaveOut
bufnum = head;
convert_pcm_data(data, length, (Bit8u*)WaveData[bufnum], len2, src_param);
this->length[bufnum] = len2;
// 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!
BX_ERROR(("Output buffer overflow! Not played. Iswaveready was %d", iswaveready));
iswaveready = 0; // stop the output for a while
ret = waveOutPrepareHeader(hWaveOut, WaveOutHdr, sizeof(*WaveOutHdr));
if (ret != 0) {
BX_ERROR(("waveOutPrepareHeader(): error = %d", ret));
return BX_SOUNDLOW_ERR;
}
head = bufnum;
// check if more buffers are available, otherwise stall the emulator
if (((bufnum + 2) & BX_SOUND_WINDOWS_NMASK) == tailfull)
{
BX_DEBUG(("Buffer status: Head %d, TailFull %d, TailPlay %d. Stall.",
head, tailfull, tailplay));
iswaveready = 0;
ret = waveOutWrite(hWaveOut, WaveOutHdr, sizeof(*WaveOutHdr));
if (ret != 0) {
char errormsg[4*MAXERRORLENGTH+1];
waveOutGetErrorTextA(ret, errormsg, 4*MAXERRORLENGTH+1);
BX_ERROR(("waveOutWrite(): %s", errormsg));
}
playnextbuffer();
#endif
#ifdef usesndPlaySnd
LPWAVEFILEHEADER header = (LPWAVEFILEHEADER) WaveData[0];
header->length = len2 + 36;
header->chnk2len = len2;
convert_wavedata(data, length, (Bit8u*)&(header->data), len2, src_param);
ret = sndPlaySoundA((LPCSTR) header, SND_SYNC | SND_MEMORY);
if (ret != 0)
{
BX_DEBUG(("sndPlaySoundA: %d", ret));
}
#endif
return BX_SOUNDLOW_OK;
}
int bx_sound_windows_c::stopwaveplayback()
{
BX_DEBUG(("stopwaveplayback()"));
#ifdef usewaveOut
// this is handled by checkwaveready() when closing
#endif
#ifdef usesndPlaySnd
sndPlaySoundA(NULL, SND_ASYNC | SND_MEMORY);
WaveOpen = 0;
#endif
Sleep(100);
return BX_SOUNDLOW_OK;
}
@ -475,22 +300,10 @@ int bx_sound_windows_c::closewaveoutput()
{
BX_DEBUG(("closewaveoutput"));
#ifdef usewaveOut
if (WaveOutOpen == 1)
{
if (WaveOutOpen == 1) {
waveOutReset(hWaveOut);
// let checkwaveready() clean up the buffers
checkwaveready();
waveOutClose(hWaveOut);
head = 0;
tailfull = 0;
tailplay = 0;
needreopen = 0;
}
#endif
return BX_SOUNDLOW_OK;
}
@ -505,30 +318,6 @@ void bx_sound_windows_c::checkmidiready()
}
}
void bx_sound_windows_c::checkwaveready()
{
int bufnum;
// 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)
{
BX_DEBUG(("Buffer %d done.", bufnum));
waveOutUnprepareHeader(hWaveOut, WaveHeader[bufnum], sizeof(*WaveHeader[bufnum]));
}
tailfull = bufnum;
// enable gathering data if a buffer is available
if (((head + 2) & BX_SOUND_WINDOWS_NMASK) != tailfull)
{
BX_DEBUG(("Buffer status: Head %d, TailFull %d, TailPlay %d. Ready.",
head, tailfull, tailplay));
iswaveready = 1;
}
}
int bx_sound_windows_c::openwaveinput(const char *wavedev, sound_record_handler_t rh)
{
UNUSED(wavedev);
@ -664,4 +453,24 @@ void bx_sound_windows_c::record_timer(void)
record_handler(this, record_packet_size);
}
int bx_sound_windows_c::register_wave_callback(void *arg, get_wave_cb_t wd_cb)
{
if (cb_count < BX_MAX_WAVE_CALLBACKS) {
get_wave[cb_count].device = arg;
get_wave[cb_count].cb = wd_cb;
return cb_count++;
}
return -1;
}
void bx_sound_windows_c::unregister_wave_callback(int callback_id)
{
BX_LOCK(mixer_mutex);
if ((callback_id >= 0) && (callback_id < BX_MAX_WAVE_CALLBACKS)) {
get_wave[callback_id].device = NULL;
get_wave[callback_id].cb = NULL;
}
BX_UNLOCK(mixer_mutex);
}
#endif // defined(WIN32)

View File

@ -25,10 +25,6 @@
#include <mmsystem.h>
// uncomment one of the following two #defines
//#define usesndPlaySnd
#define usewaveOut
#define BX_SOUND_WINDOWS_MAXSYSEXLEN 256 // maximum supported length of a sysex message
#define BX_SOUND_WINDOWS_NBUF 64 // number of buffers for the output, must be power of 2 and >= 4
@ -171,17 +167,14 @@ public:
virtual int get_type() {return BX_SOUNDLOW_WIN;}
virtual int waveready();
virtual int midiready();
virtual int openmidioutput(const char *mididev);
virtual int sendmidicommand(int delta, int command, int length, Bit8u data[]);
virtual int closemidioutput();
virtual int openmidioutput(const char *mididev);
virtual int midiready();
virtual int sendmidicommand(int delta, int command, int length, Bit8u data[]);
virtual int closemidioutput();
virtual int openwaveoutput(const char *wavedev);
virtual int set_pcm_params(bx_pcm_param_t param);
virtual int sendwavepacket(int length, Bit8u data[], bx_pcm_param_t *src_param);
virtual int stopwaveplayback();
virtual int waveout(int length, Bit8u data[]);
virtual int closewaveoutput();
virtual int openwaveinput(const char *wavedev, sound_record_handler_t rh);
@ -193,6 +186,8 @@ public:
static void record_timer_handler(void *);
void record_timer(void);
virtual int register_wave_callback(void *, get_wave_cb_t wd_cb);
virtual void unregister_wave_callback(int callback_id);
private:
struct bx_sound_waveinfo_struct {
int frequency;
@ -214,26 +209,19 @@ private:
HANDLE DataHandle; // returned by GlobalAlloc()
Bit8u *DataPointer; // returned by GlobalLock()
LPWAVEHDR WaveHeader[BX_SOUND_WINDOWS_NBUF];
LPSTR WaveData[BX_SOUND_WINDOWS_NBUF];
LPWAVEHDR WaveOutHdr;
LPWAVEHDR WaveInHdr;
LPSTR WaveInData;
bx_bool recording;
int length[BX_SOUND_WINDOWS_NBUF]; // length of the data in the buffer
int needreopen; // if the format has changed
int head,tailfull,tailplay; // These are for three states of the buffers: empty, full, played
bx_sound_waveinfo_struct WaveInfo[2]; // format for the next buffer to be played
int iswaveready;
// and the midi buffer for the SYSEX messages
LPMIDIHDR MidiHeader;
LPSTR MidiData;
int ismidiready;
int playnextbuffer();
int recordnextpacket();
void checkmidiready();
void checkwaveready();
};
#endif // defined(WIN32)