mirror of
https://github.com/Pecusx/libretro-atari800.git
synced 2026-05-21 21:33:56 +02:00
554 lines
11 KiB
C
554 lines
11 KiB
C
/*
|
|
* sound.c - Win32 port specific code
|
|
*
|
|
* Copyright (C) 2000 Krzysztof Nikiel
|
|
* Copyright (C) 2000-2003 Atari800 development team (see DOC/CREDITS)
|
|
*
|
|
* This file is part of the Atari800 emulator project which emulates
|
|
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
|
|
*
|
|
* Atari800 is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Atari800 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with Atari800; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef SOUND
|
|
|
|
#include <windows.h>
|
|
#ifdef DIRECTX
|
|
# define DIRECTSOUND_VERSION 0x0500
|
|
# include <dsound.h>
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "sound.h"
|
|
#include "main.h"
|
|
#include "pokeysnd.h"
|
|
#include "atari.h"
|
|
#include "log.h"
|
|
|
|
#define MIXBUFSIZE 0x1000
|
|
#define WAVSHIFT 9
|
|
#define WAVSIZE (1 << WAVSHIFT)
|
|
int buffers = 0;
|
|
|
|
enum {SOUND_NONE, SOUND_DX, SOUND_WAV};
|
|
|
|
static int usesound = TRUE;
|
|
static int issound = SOUND_NONE;
|
|
static int dsprate = 44100;
|
|
static int snddelay = 40; /* delay in milliseconds */
|
|
static int snddelaywav = 100;
|
|
static int bit16 = FALSE;
|
|
|
|
static HANDLE event;
|
|
static HWAVEOUT wout;
|
|
static WAVEHDR *waves;
|
|
|
|
#ifdef DIRECTX
|
|
static int wavonly = FALSE;
|
|
static DWORD sbufsize = 0;
|
|
static int samples = 0; /* #samples to be in play buffer */
|
|
static UBYTE mixbuf[MIXBUFSIZE];
|
|
static DWORD bufpos = 0;
|
|
|
|
static LPDIRECTSOUND lpDS = NULL;
|
|
static LPDIRECTSOUNDBUFFER pDSB = NULL;
|
|
|
|
static int initsound_dx(void)
|
|
{
|
|
DWORD i;
|
|
int err;
|
|
DSBUFFERDESC dsBD =
|
|
{0};
|
|
WAVEFORMATEX wfx;
|
|
DSBCAPS bc;
|
|
|
|
LPVOID pMem1, pMem2;
|
|
DWORD dwSize1, dwSize2;
|
|
|
|
if (issound != SOUND_NONE)
|
|
return 0;
|
|
|
|
if ((err = DirectSoundCreate(NULL, &lpDS, NULL)) < 0)
|
|
return err;
|
|
if ((err = IDirectSound_SetCooperativeLevel(lpDS, hWndMain,
|
|
DSSCL_EXCLUSIVE)) < 0)
|
|
goto end;
|
|
|
|
dsBD.dwSize = sizeof(dsBD);
|
|
dsBD.dwFlags = DSBCAPS_GETCURRENTPOSITION2;
|
|
dsBD.dwBufferBytes = 0x4000;
|
|
dsBD.lpwfxFormat = &wfx;
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
#ifdef STEREO_SOUND
|
|
wfx.nChannels = POKEYSND_stereo_enabled ? 2 : 1;
|
|
#else
|
|
wfx.nChannels = 1;
|
|
#endif /* STEREO_SOUND */
|
|
wfx.nSamplesPerSec = dsprate;
|
|
wfx.nAvgBytesPerSec = dsprate * wfx.nChannels * (bit16 ? 2 : 1);
|
|
wfx.nBlockAlign = wfx.nChannels * (bit16 ? 2 : 1);
|
|
wfx.wBitsPerSample = bit16 ? 16 : 8;
|
|
wfx.cbSize = 0;
|
|
|
|
if ((err = IDirectSound_CreateSoundBuffer(lpDS, &dsBD, &pDSB, NULL)) < 0)
|
|
goto end;
|
|
|
|
bc.dwSize = sizeof(bc);
|
|
IDirectSoundBuffer_GetCaps(pDSB, &bc);
|
|
sbufsize = bc.dwBufferBytes;
|
|
|
|
if ((err = IDirectSoundBuffer_Lock(pDSB, 0, sbufsize,
|
|
&pMem1, &dwSize1, &pMem2, &dwSize2,
|
|
DSBLOCK_FROMWRITECURSOR)) < 0)
|
|
goto end;
|
|
|
|
for (i = 0; i < dwSize1 >> 1; i++)
|
|
*((short *) pMem1 + i) = 0;
|
|
for (i = 0; i < dwSize2 >> 1; i++)
|
|
*((short *) pMem2 + i) = 0;
|
|
IDirectSoundBuffer_Unlock(pDSB, pMem1, dwSize1, pMem2, dwSize2);
|
|
|
|
IDirectSoundBuffer_Play(pDSB, 0, 0, DSBPLAY_LOOPING);
|
|
|
|
POKEYSND_Init(POKEYSND_FREQ_17_EXACT, (UWORD) dsprate, (UBYTE) wfx.nChannels, (bit16 ? POKEYSND_BIT16 : 0));
|
|
|
|
samples = dsprate * snddelay / 1000;
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition(pDSB, 0, &bufpos);
|
|
|
|
issound = SOUND_DX;
|
|
return 0;
|
|
|
|
end:
|
|
MessageBox(hWndMain, "DirectSound Init FAILED", myname, MB_OK);
|
|
Sound_Exit();
|
|
return err;
|
|
}
|
|
|
|
static void uninitsound_dx(void)
|
|
{
|
|
if (issound != SOUND_DX)
|
|
return;
|
|
|
|
if (lpDS)
|
|
{
|
|
if (pDSB)
|
|
{
|
|
IDirectSoundBuffer_Stop(pDSB);
|
|
IDirectSoundBuffer_Release(pDSB);
|
|
pDSB = NULL;
|
|
}
|
|
IDirectSound_Release(lpDS);
|
|
lpDS = NULL;
|
|
}
|
|
issound = SOUND_NONE;
|
|
}
|
|
|
|
static void sound_update_dx(void)
|
|
{
|
|
DWORD wc;
|
|
int d1;
|
|
LPVOID pMem1, pMem2;
|
|
DWORD dwSize1, dwSize2;
|
|
signed short *p1s;
|
|
signed short *p2s;
|
|
UBYTE *p1b;
|
|
UBYTE *p2b;
|
|
signed short *pbufs;
|
|
UBYTE *pbufb;
|
|
int s1, s2;
|
|
int err;
|
|
int i;
|
|
int samplesize = bit16 ? 2 : 1;
|
|
|
|
#ifdef STEREO_SOUND
|
|
if (POKEYSND_stereo_enabled) samplesize *= 2;
|
|
#endif
|
|
|
|
if (issound != SOUND_DX)
|
|
return;
|
|
|
|
IDirectSoundBuffer_GetCurrentPosition(pDSB, 0, &wc);
|
|
|
|
d1 = (wc - bufpos);
|
|
if ((DWORD) abs(d1) > (sbufsize >> 1)) {
|
|
if (d1 < 0)
|
|
d1 += sbufsize;
|
|
else
|
|
d1 -= sbufsize;
|
|
}
|
|
if (d1 < (-samples * samplesize)) // there is more than necessary bytes filled?
|
|
return;
|
|
|
|
d1 = (samples * samplesize) + d1; // bytes to fill
|
|
d1 = (d1 / samplesize) * samplesize; //round to a sample pair
|
|
|
|
if (d1 > (sizeof(mixbuf) / sizeof(mixbuf[0])))
|
|
{
|
|
d1 = (sizeof(mixbuf) / sizeof(mixbuf[0]));
|
|
}
|
|
|
|
if ((err = IDirectSoundBuffer_Lock(pDSB,
|
|
bufpos,
|
|
d1,
|
|
&pMem1, &dwSize1,
|
|
&pMem2, &dwSize2,
|
|
0)) < 0)
|
|
{
|
|
if (err == DSERR_BUFFERLOST)
|
|
Sound_Continue();
|
|
return;
|
|
}
|
|
|
|
s1 = dwSize1;
|
|
s2 = dwSize2;
|
|
p1s = (signed short *)pMem1;
|
|
p2s = (signed short *)pMem2;
|
|
p1b = (UBYTE *)pMem1;
|
|
p2b = (UBYTE *)pMem2;
|
|
bufpos += (s1 + s2);
|
|
if (bufpos >= sbufsize)
|
|
bufpos -= sbufsize;
|
|
|
|
if (bit16)
|
|
{
|
|
s1 /= 2;
|
|
s2 /= 2;
|
|
}
|
|
i = s1 + s2;
|
|
|
|
POKEYSND_Process(mixbuf, i);
|
|
|
|
pbufs = (signed short *)mixbuf;
|
|
pbufb = (UBYTE *)mixbuf;
|
|
if (s1)
|
|
{
|
|
if (bit16)
|
|
for (; s1; s1--)
|
|
*p1s++ = *pbufs++;
|
|
else
|
|
for (; s1; s1--)
|
|
*p1b++ = *pbufb++;
|
|
}
|
|
if (s2)
|
|
{
|
|
if (bit16)
|
|
for (; s2; s2--)
|
|
*p2s++ = *pbufs++;
|
|
else
|
|
for (; s2; s2--)
|
|
*p2b++ = *pbufb++;
|
|
}
|
|
|
|
IDirectSoundBuffer_Unlock(pDSB, pMem1, dwSize1, pMem2, dwSize2);
|
|
|
|
return;
|
|
}
|
|
#endif /* DIRECTX */
|
|
|
|
static void uninitsound_wav(void)
|
|
{
|
|
int i;
|
|
MMRESULT err;
|
|
|
|
if (issound != SOUND_WAV)
|
|
return;
|
|
|
|
l0:
|
|
for (i = 0; i < buffers; i++)
|
|
{
|
|
if (!(waves[i].dwFlags & WHDR_DONE))
|
|
{
|
|
WaitForSingleObject(event, 5000);
|
|
ResetEvent(event);
|
|
goto l0;
|
|
}
|
|
}
|
|
|
|
waveOutReset (wout);
|
|
|
|
for (i = 0; i < buffers; i++)
|
|
{
|
|
err = waveOutUnprepareHeader(wout, &waves[i], sizeof (waves[i]));
|
|
if (err != MMSYSERR_NOERROR)
|
|
{
|
|
fprintf(stderr, "warning: cannot unprepare wave header (%x)\n", err);
|
|
}
|
|
free(waves[i].lpData);
|
|
}
|
|
free(waves);
|
|
|
|
waveOutClose(wout);
|
|
CloseHandle(event);
|
|
|
|
issound = SOUND_NONE;
|
|
}
|
|
|
|
static int initsound_wav(void)
|
|
{
|
|
int i;
|
|
WAVEFORMATEX wfx;
|
|
MMRESULT err;
|
|
|
|
event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
|
|
memset(&wfx, 0, sizeof(wfx));
|
|
|
|
wfx.wFormatTag = WAVE_FORMAT_PCM;
|
|
#ifdef STEREO_SOUND
|
|
wfx.nChannels = POKEYSND_stereo_enabled ? 2 : 1;
|
|
#else
|
|
wfx.nChannels = 1;
|
|
#endif /* STEREO_SOUND */
|
|
wfx.nSamplesPerSec = dsprate;
|
|
wfx.nAvgBytesPerSec = dsprate * wfx.nChannels * (bit16 ? 2 : 1);
|
|
wfx.nBlockAlign = wfx.nChannels * (bit16 ? 2 : 1);
|
|
wfx.wBitsPerSample = bit16 ? 16 : 8;
|
|
wfx.cbSize = 0;
|
|
|
|
err = waveOutOpen(&wout, WAVE_MAPPER, &wfx, (int)event, 0, CALLBACK_EVENT);
|
|
if (err == WAVERR_BADFORMAT)
|
|
{
|
|
Log_print("wave output parameters unsupported\n");
|
|
exit(1);
|
|
}
|
|
if (err != MMSYSERR_NOERROR)
|
|
{
|
|
Log_print("cannot open wave output (%x)\n", err);
|
|
exit(1);
|
|
}
|
|
|
|
buffers = ((wfx.nAvgBytesPerSec * snddelaywav / 1000) >> WAVSHIFT) + 1;
|
|
waves = (WAVEHDR *)malloc(buffers * sizeof(*waves));
|
|
for (i = 0; i < buffers; i++)
|
|
{
|
|
memset(&waves[i], 0, sizeof (waves[i]));
|
|
if (!(waves[i].lpData = (LPSTR)malloc(WAVSIZE)))
|
|
{
|
|
Log_print("could not get wave buffer memory\n");
|
|
exit(1);
|
|
}
|
|
waves[i].dwBufferLength = WAVSIZE;
|
|
err = waveOutPrepareHeader(wout, &waves[i], sizeof(waves[i]));
|
|
if (err != MMSYSERR_NOERROR)
|
|
{
|
|
Log_print("cannot prepare wave header (%x)\n", err);
|
|
exit(1);
|
|
}
|
|
waves[i].dwFlags |= WHDR_DONE;
|
|
}
|
|
|
|
POKEYSND_Init(POKEYSND_FREQ_17_EXACT, (UWORD) dsprate, (UBYTE) wfx.nChannels, (bit16 ? POKEYSND_BIT16 : 0));
|
|
|
|
issound = SOUND_WAV;
|
|
return 0;
|
|
}
|
|
|
|
int Sound_Initialise(int *argc, char *argv[])
|
|
{
|
|
int i, j;
|
|
int help = FALSE;
|
|
|
|
if (issound != SOUND_NONE)
|
|
return TRUE;
|
|
|
|
for (i = j = 1; i < *argc; i++)
|
|
{
|
|
int i_a = (i + 1 < *argc); /* is argument available? */
|
|
int a_m = FALSE; /* error, argument missing! */
|
|
|
|
if (strcmp(argv[i], "-sound") == 0)
|
|
usesound = TRUE;
|
|
else if (strcmp(argv[i], "-nosound") == 0)
|
|
usesound = FALSE;
|
|
else if (strcmp(argv[i], "-audio16") == 0)
|
|
bit16 = TRUE;
|
|
else if (strcmp(argv[i], "-dsprate") == 0)
|
|
{
|
|
if (i_a)
|
|
sscanf(argv[++i], "%d", &dsprate);
|
|
else a_m = TRUE;
|
|
}
|
|
else if (strcmp(argv[i], "-snddelay") == 0)
|
|
{
|
|
if (i_a)
|
|
{
|
|
sscanf(argv[++i], "%d", &snddelay);
|
|
snddelaywav = snddelay;
|
|
}
|
|
else a_m = TRUE;
|
|
}
|
|
else if (strcmp(argv[i], "-quality") == 0) {
|
|
if (i_a) {
|
|
int quality;
|
|
sscanf(argv[++i], "%d", &quality);
|
|
if (quality > 1) {
|
|
POKEYSND_SetMzQuality(quality - 1);
|
|
POKEYSND_enable_new_pokey = 1;
|
|
}
|
|
else
|
|
POKEYSND_enable_new_pokey = 0;
|
|
}
|
|
else a_m = TRUE;
|
|
}
|
|
#ifdef DIRECTX
|
|
else if (strcmp(argv[i], "-wavonly") == 0)
|
|
wavonly = TRUE;
|
|
#endif
|
|
else
|
|
{
|
|
if (strcmp(argv[i], "-help") == 0)
|
|
{
|
|
Log_print("\t-sound Enable sound\n"
|
|
"\t-nosound Disable sound\n"
|
|
#ifdef DIRECTX
|
|
"\t-wavonly Disable direct sound\n"
|
|
#endif
|
|
"\t-dsprate <rate> Set dsp rate\n"
|
|
"\t-snddelay <ms> Set sound delay\n"
|
|
"\t-audio16 Use 16 bit mixing\n"
|
|
"\t-quality <level> Set sound quality"
|
|
);
|
|
help = TRUE;
|
|
}
|
|
argv[j++] = argv[i];
|
|
}
|
|
|
|
if (a_m) {
|
|
Log_print("Missing argument for '%s'", argv[i]);
|
|
usesound = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
*argc = j;
|
|
|
|
if (help || !usesound) {
|
|
usesound = FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef DIRECTX
|
|
if (!wavonly)
|
|
{
|
|
i = initsound_dx();
|
|
if (!i)
|
|
return TRUE;
|
|
}
|
|
#endif
|
|
initsound_wav();
|
|
return TRUE;
|
|
}
|
|
|
|
void Sound_Reinit(void)
|
|
{
|
|
if (usesound)
|
|
{
|
|
Sound_Exit();
|
|
#ifdef DIRECTX
|
|
if (!wavonly)
|
|
{
|
|
int i = initsound_dx();
|
|
if (!i)
|
|
return;
|
|
}
|
|
#endif
|
|
initsound_wav();
|
|
}
|
|
return;
|
|
}
|
|
|
|
void Sound_Exit(void)
|
|
{
|
|
#ifdef DIRECTX
|
|
if (issound == SOUND_DX)
|
|
uninitsound_dx();
|
|
#endif
|
|
if (issound == SOUND_WAV)
|
|
uninitsound_wav();
|
|
|
|
issound = SOUND_NONE;
|
|
}
|
|
|
|
static WAVEHDR *getwave(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < buffers; i++)
|
|
{
|
|
if (waves[i].dwFlags & WHDR_DONE)
|
|
{
|
|
waves[i].dwFlags &= ~WHDR_DONE;
|
|
return &waves[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Sound_Update(void)
|
|
{
|
|
MMRESULT err;
|
|
WAVEHDR *wh;
|
|
|
|
switch (issound)
|
|
{
|
|
case SOUND_WAV:
|
|
while ((wh = getwave()))
|
|
{
|
|
POKEYSND_Process(wh->lpData, wh->dwBufferLength >> (bit16 ? 1 : 0));
|
|
err = waveOutWrite(wout, wh, sizeof(*wh));
|
|
if (err != MMSYSERR_NOERROR)
|
|
{
|
|
Log_print("cannot write wave output (%x)\n", err);
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
#ifdef DIRECTX
|
|
case SOUND_DX:
|
|
sound_update_dx();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void Sound_Pause(void)
|
|
{
|
|
#ifdef DIRECTX
|
|
if (issound != SOUND_DX)
|
|
return;
|
|
if (!pDSB)
|
|
return;
|
|
IDirectSoundBuffer_Stop(pDSB);
|
|
#endif
|
|
}
|
|
|
|
void Sound_Continue(void)
|
|
{
|
|
#ifdef DIRECTX
|
|
if (issound != SOUND_DX)
|
|
return;
|
|
if (!pDSB)
|
|
return;
|
|
IDirectSoundBuffer_Restore(pDSB);
|
|
IDirectSoundBuffer_Play(pDSB, 0, 0, DSBPLAY_LOOPING);
|
|
IDirectSoundBuffer_GetCurrentPosition(pDSB, 0, &bufpos);
|
|
#endif
|
|
}
|
|
|
|
#endif /* SOUND */
|