mirror of
https://github.com/mauiaaron/apple2.git
synced 2024-06-26 00:29:27 +00:00
Remove hardcoded sample rate and buffer sizes
- Audio backend now specifies the sample rate and min/ideal buffer size for mono and stereo audio - Increase maximum speaker amplitude
This commit is contained in:
parent
d5b7b5263c
commit
a9307aa827
|
@ -173,7 +173,7 @@ void sound_ay_init( CAY8910 *_this )
|
|||
#define HZ_COMMON_DENOMINATOR 25
|
||||
#endif
|
||||
|
||||
void sound_init( CAY8910 *_this, const char *device )
|
||||
static void sound_init( CAY8910 *_this, const char *device, unsigned long nSampleRate )
|
||||
{
|
||||
// static int first_init = 1;
|
||||
// int f, ret;
|
||||
|
@ -228,7 +228,7 @@ void sound_init( CAY8910 *_this, const char *device )
|
|||
|
||||
// sound_generator_freq =
|
||||
// settings_current.sound_hifi ? HIFI_FREQ : settings_current.sound_freq;
|
||||
sound_generator_freq = SPKR_SAMPLE_RATE;
|
||||
sound_generator_freq = nSampleRate;
|
||||
sound_generator_framesiz = sound_generator_freq / (int)hz;
|
||||
|
||||
#if 0
|
||||
|
@ -1018,21 +1018,21 @@ void AY8910Update(int chip, int16_t** buffer, int nNumSamples)
|
|||
sound_frame(&g_AY8910[chip]);
|
||||
}
|
||||
|
||||
void AY8910_InitAll(int nClock, int nSampleRate)
|
||||
void AY8910_InitAll(int nClock, unsigned long nSampleRate)
|
||||
{
|
||||
for (unsigned int i=0; i<MAX_8910; i++)
|
||||
{
|
||||
sound_init(&g_AY8910[i], NULL); // Inits mainly static members (except ay_tick_incr)
|
||||
sound_init(&g_AY8910[i], NULL, nSampleRate); // Inits mainly static members (except ay_tick_incr)
|
||||
sound_ay_init(&g_AY8910[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void AY8910_InitClock(int nClock)
|
||||
void AY8910_InitClock(int nClock, unsigned long nSampleRate)
|
||||
{
|
||||
SetCLK( (double)nClock );
|
||||
for (unsigned int i=0; i<MAX_8910; i++)
|
||||
{
|
||||
sound_init(&g_AY8910[i], NULL); // ay_tick_incr is dependent on AY_CLK
|
||||
sound_init(&g_AY8910[i], NULL, nSampleRate); // ay_tick_incr is dependent on AY_CLK
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ void _AYWriteReg(int chip, int r, int v);
|
|||
void AY8910_reset(int chip);
|
||||
void AY8910Update(int chip, int16_t** buffer, int nNumSamples);
|
||||
|
||||
void AY8910_InitAll(int nClock, int nSampleRate);
|
||||
void AY8910_InitClock(int nClock);
|
||||
void AY8910_InitAll(int nClock, unsigned long nSampleRate);
|
||||
void AY8910_InitClock(int nClock, unsigned long nSampleRate);
|
||||
uint8_t* AY8910_GetRegsPtr(unsigned int uChip);
|
||||
|
||||
void AY8910UpdateSetCycles();
|
||||
|
@ -49,7 +49,7 @@ struct CAY8910;
|
|||
void CAY8910_init(struct CAY8910 *_this);
|
||||
|
||||
void sound_ay_init(struct CAY8910 *_this);
|
||||
void sound_init(struct CAY8910 *_this, const char *device);
|
||||
//void sound_init(struct CAY8910 *_this, const char *device);
|
||||
void sound_ay_write(struct CAY8910 *_this, int reg, int val, libspectrum_dword now);
|
||||
void sound_ay_reset(struct CAY8910 *_this);
|
||||
void sound_frame(struct CAY8910 *_this);
|
||||
|
|
|
@ -171,7 +171,7 @@ typedef struct
|
|||
|
||||
|
||||
// Support 2 MB's, each with 2x SY6522/AY8910 pairs.
|
||||
static SY6522_AY8910 g_MB[NUM_AY8910];
|
||||
static SY6522_AY8910 g_MB[NUM_AY8910] = { 0 };
|
||||
|
||||
// Timer vars
|
||||
static unsigned long g_n6522TimerPeriod = 0;
|
||||
|
@ -190,7 +190,7 @@ static bool g_bStopPhoneme = false;
|
|||
static bool g_bVotraxPhoneme = false;
|
||||
|
||||
#ifdef APPLE2IX
|
||||
static const unsigned long SAMPLE_RATE = SPKR_SAMPLE_RATE;
|
||||
static unsigned long SAMPLE_RATE = 0;
|
||||
#else
|
||||
static const unsigned long SAMPLE_RATE = 44100; // Use a base freq so that DirectX (or sound h/w) doesn't have to up/down-sample
|
||||
#endif
|
||||
|
@ -225,10 +225,9 @@ static uint8_t g_nPhasorMode = 0; // 0=Mockingboard emulation, 1=Phasor native
|
|||
|
||||
#ifdef APPLE2IX
|
||||
#define MB_CHANNELS 2
|
||||
#define MB_BUF_SIZE MAX_SAMPLES * sizeof(int16_t) * MB_CHANNELS
|
||||
static const unsigned short g_nMB_NumChannels = MB_CHANNELS;
|
||||
|
||||
static const unsigned long g_dwDSBufferSize = MB_BUF_SIZE;
|
||||
static unsigned long MB_BUF_SIZE = 0;
|
||||
static unsigned short g_nMB_NumChannels = MB_CHANNELS;
|
||||
static unsigned long g_dwDSBufferSize = 0;
|
||||
#else
|
||||
static const unsigned short g_nMB_NumChannels = 2;
|
||||
|
||||
|
@ -239,7 +238,7 @@ static const int16_t nWaveDataMin = (int16_t)0x8000;
|
|||
static const int16_t nWaveDataMax = (int16_t)0x7FFF;
|
||||
|
||||
#ifdef APPLE2IX
|
||||
static short g_nMixBuffer[MB_BUF_SIZE / sizeof(short)];
|
||||
static short *g_nMixBuffer = NULL;
|
||||
#else
|
||||
static short g_nMixBuffer[g_dwDSBufferSize / sizeof(short)];
|
||||
#endif
|
||||
|
@ -1132,12 +1131,14 @@ static void* SSI263Thread(void *lpParameter)
|
|||
static unsigned long SSI263Thread(void *lpParameter)
|
||||
#endif
|
||||
{
|
||||
const unsigned long nsecWait = NANOSECONDS_PER_SECOND / audio_backend->systemSettings.sampleRateHz;
|
||||
const struct timespec wait = { .tv_sec=0, .tv_nsec=nsecWait };
|
||||
|
||||
while(1)
|
||||
{
|
||||
#ifdef APPLE2IX
|
||||
int err =0;
|
||||
|
||||
static struct timespec wait = { .tv_sec = 0, .tv_nsec=45351 }; // 22050Hz
|
||||
pthread_mutex_lock(&mockingboard_mutex);
|
||||
err = pthread_cond_timedwait(&mockingboard_cond, &mockingboard_mutex, &wait);
|
||||
if (err && (err != ETIMEDOUT))
|
||||
|
@ -1371,7 +1372,7 @@ static bool MB_DSInit()
|
|||
if(!audio_isAvailable)
|
||||
return false;
|
||||
|
||||
int hr = audio_createSoundBuffer(&MockingboardVoice, g_dwDSBufferSize, SAMPLE_RATE, 2);
|
||||
int hr = audio_createSoundBuffer(&MockingboardVoice, 2);
|
||||
LOG("MB_DSInit: DSGetSoundBuffer(), hr=0x%08X\n", (unsigned int)hr);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
|
@ -1379,6 +1380,11 @@ static bool MB_DSInit()
|
|||
return false;
|
||||
}
|
||||
|
||||
SAMPLE_RATE = audio_backend->systemSettings.sampleRateHz;
|
||||
MB_BUF_SIZE = audio_backend->systemSettings.stereoBufferSizeSamples * audio_backend->systemSettings.bytesPerSample * MB_CHANNELS;
|
||||
g_dwDSBufferSize = MB_BUF_SIZE;
|
||||
g_nMixBuffer = malloc(MB_BUF_SIZE / audio_backend->systemSettings.bytesPerSample);
|
||||
|
||||
#ifndef APPLE2IX
|
||||
bool bRes = DSZeroVoiceBuffer(&MockingboardVoice, (char*)"MB", g_dwDSBufferSize);
|
||||
LOG("MB_DSInit: DSZeroVoiceBuffer(), res=%d\n", bRes ? 1 : 0);
|
||||
|
@ -1463,10 +1469,14 @@ static bool MB_DSInit()
|
|||
bPause = false;
|
||||
}
|
||||
|
||||
unsigned int nPhonemeByteLength = g_nPhonemeInfo[nPhoneme].nLength * sizeof(int16_t);
|
||||
unsigned int nPhonemeByteLength = g_nPhonemeInfo[nPhoneme].nLength * audio_backend->systemSettings.bytesPerSample;
|
||||
if (nPhonemeByteLength > audio_backend->systemSettings.monoBufferSizeSamples) {
|
||||
RELEASE_ERRLOG("!!!!!!!!!!!!!!!!!!!!! phoneme length > buffer size !!!!!!!!!!!!!!!!!!!!!");
|
||||
#warning ^^^^^^^^^^ require vigilence here around this change ... we used to be able to specify the exact buffer size ...
|
||||
}
|
||||
|
||||
// NB. DSBCAPS_LOCSOFTWARE required for
|
||||
hr = audio_createSoundBuffer(&SSI263Voice[i], nPhonemeByteLength, 22050, 1);
|
||||
hr = audio_createSoundBuffer(&SSI263Voice[i], 1);
|
||||
LOG("MB_DSInit: (%02d) DSGetSoundBuffer(), hr=0x%08X\n", i, (unsigned int)hr);
|
||||
if(FAILED(hr))
|
||||
{
|
||||
|
@ -1628,6 +1638,7 @@ static void MB_DSUninit()
|
|||
}
|
||||
|
||||
//
|
||||
FREE(g_nMixBuffer);
|
||||
|
||||
#ifndef APPLE2IX
|
||||
if(g_hSSI263Event[0])
|
||||
|
@ -1666,6 +1677,14 @@ void MB_Initialize()
|
|||
{
|
||||
memset(&g_MB,0,sizeof(g_MB));
|
||||
|
||||
g_bMBAvailable = MB_DSInit();
|
||||
if (!g_bMBAvailable) {
|
||||
MockingboardVoice->bMute = true;
|
||||
g_SoundcardType = CT_Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int i;
|
||||
for(i=0; i<NUM_VOICES; i++)
|
||||
{
|
||||
|
@ -1684,7 +1703,6 @@ void MB_Initialize()
|
|||
|
||||
//
|
||||
|
||||
g_bMBAvailable = MB_DSInit();
|
||||
LOG("MB_Initialize: MB_DSInit(), g_bMBAvailable=%d\n", g_bMBAvailable);
|
||||
|
||||
MB_Reset();
|
||||
|
@ -1697,7 +1715,7 @@ void MB_Initialize()
|
|||
// NB. Called when /cycles_persec_target/ changes
|
||||
void MB_Reinitialize()
|
||||
{
|
||||
AY8910_InitClock((int)cycles_persec_target);
|
||||
AY8910_InitClock((int)cycles_persec_target, SAMPLE_RATE);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -1936,7 +1954,7 @@ static uint8_t PhasorIO(uint16_t PC, uint16_t nAddr, uint8_t bWrite, uint8_t nVa
|
|||
|
||||
double fCLK = (nAddr & 4) ? CLK_6502*2 : CLK_6502;
|
||||
|
||||
AY8910_InitClock((int)fCLK);
|
||||
AY8910_InitClock((int)fCLK, SAMPLE_RATE);
|
||||
|
||||
return MemReadFloatingBus();
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ typedef struct ALVoices {
|
|||
} ALVoices;
|
||||
|
||||
static ALVoices *voices = NULL;
|
||||
static AudioBackend_s openal_audio_backend = { 0 };
|
||||
static AudioBackend_s openal_audio_backend = { { 0 } };
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// AudioBuffer_s processing routines
|
||||
|
@ -348,7 +348,7 @@ static void _openal_destroyVoice(ALVoice *voice) {
|
|||
FREE(voice);
|
||||
}
|
||||
|
||||
static ALVoice *_openal_createVoice(const AudioParams_s *params) {
|
||||
static ALVoice *_openal_createVoice(unsigned long numChannels) {
|
||||
ALVoice *voice = NULL;
|
||||
|
||||
do {
|
||||
|
@ -405,17 +405,20 @@ static ALVoice *_openal_createVoice(const AudioParams_s *params) {
|
|||
break;
|
||||
}
|
||||
|
||||
voice->rate = (ALuint)params->nSamplesPerSec;
|
||||
voice->rate = openal_audio_backend.systemSettings.sampleRateHz;
|
||||
|
||||
// Emulator supports only mono and stereo
|
||||
if (params->nChannels == 2) {
|
||||
if (numChannels == 2) {
|
||||
voice->format = AL_FORMAT_STEREO16;
|
||||
} else {
|
||||
voice->format = AL_FORMAT_MONO16;
|
||||
}
|
||||
|
||||
/* Allocate enough space for the temp buffer, given the format */
|
||||
voice->buffersize = (ALsizei)params->dwBufferBytes;
|
||||
assert(numChannels == 1 || numChannels == 2);
|
||||
unsigned long maxSamples = openal_audio_backend.systemSettings.monoBufferSizeSamples * numChannels;
|
||||
voice->buffersize = maxSamples * openal_audio_backend.systemSettings.bytesPerSample;
|
||||
|
||||
voice->data = malloc(voice->buffersize);
|
||||
if (voice->data == NULL) {
|
||||
ERRLOG("OOPS, Error allocating %d bytes", voice->buffersize);
|
||||
|
@ -440,7 +443,7 @@ static ALVoice *_openal_createVoice(const AudioParams_s *params) {
|
|||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static long openal_destroySoundBuffer(INOUT AudioBuffer_s **soundbuf_struct) {
|
||||
static long openal_destroySoundBuffer(const struct AudioContext_s *sound_system, INOUT AudioBuffer_s **soundbuf_struct) {
|
||||
if (!*soundbuf_struct) {
|
||||
// already dealloced
|
||||
return 0;
|
||||
|
@ -463,7 +466,7 @@ static long openal_destroySoundBuffer(INOUT AudioBuffer_s **soundbuf_struct) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long openal_createSoundBuffer(const AudioParams_s *params, INOUT AudioBuffer_s **soundbuf_struct, const AudioContext_s *audio_context) {
|
||||
static long openal_createSoundBuffer(const AudioContext_s *audio_context, unsigned long numChannels, INOUT AudioBuffer_s **soundbuf_struct) {
|
||||
LOG("openal_createSoundBuffer ...");
|
||||
assert(*soundbuf_struct == NULL);
|
||||
|
||||
|
@ -474,7 +477,7 @@ static long openal_createSoundBuffer(const AudioParams_s *params, INOUT AudioBuf
|
|||
ALCcontext *ctx = (ALCcontext*)(audio_context->_internal);
|
||||
assert(ctx != NULL);
|
||||
|
||||
if ((voice = _openal_createVoice(params)) == NULL) {
|
||||
if ((voice = _openal_createVoice(numChannels)) == NULL) {
|
||||
ERRLOG("OOPS, Cannot create new voice");
|
||||
break;
|
||||
}
|
||||
|
@ -507,7 +510,7 @@ static long openal_createSoundBuffer(const AudioParams_s *params, INOUT AudioBuf
|
|||
} while(0);
|
||||
|
||||
if (*soundbuf_struct) {
|
||||
openal_destroySoundBuffer(soundbuf_struct);
|
||||
openal_destroySoundBuffer(audio_context, soundbuf_struct);
|
||||
} else if (voice) {
|
||||
_openal_destroyVoice(voice);
|
||||
}
|
||||
|
@ -538,6 +541,12 @@ static long openal_systemSetup(INOUT AudioContext_s **audio_context) {
|
|||
long result = -1;
|
||||
ALCcontext *ctx = NULL;
|
||||
|
||||
// 2015/06/29 these values seem to work well on Linux desktop ... no other OpenAL platform has been tested
|
||||
openal_audio_backend.systemSettings.sampleRateHz = 22050;
|
||||
openal_audio_backend.systemSettings.bytesPerSample = 2;
|
||||
openal_audio_backend.systemSettings.monoBufferSizeSamples = (8*1024);
|
||||
openal_audio_backend.systemSettings.stereoBufferSizeSamples = openal_audio_backend.systemSettings.monoBufferSizeSamples;
|
||||
|
||||
do {
|
||||
|
||||
if ((ctx = InitAL()) == NULL) {
|
||||
|
|
|
@ -24,18 +24,12 @@ AudioBackend_s *audio_backend = NULL;
|
|||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
long audio_createSoundBuffer(INOUT AudioBuffer_s **pVoice, unsigned long dwBufferSize, unsigned long nSampleRate, int nChannels) {
|
||||
AudioParams_s params = { 0 };
|
||||
long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer, unsigned long numChannels) {
|
||||
|
||||
params.nChannels = nChannels;
|
||||
params.nSamplesPerSec = nSampleRate;
|
||||
params.wBitsPerSample = 16;
|
||||
params.nBlockAlign = (params.nChannels == 1) ? 2 : 4;
|
||||
params.nAvgBytesPerSec = params.nBlockAlign * params.nSamplesPerSec;
|
||||
params.dwBufferBytes = dwBufferSize;
|
||||
AudioSettings_s *settings = &audio_backend->systemSettings;
|
||||
|
||||
if (*pVoice) {
|
||||
audio_destroySoundBuffer(pVoice);
|
||||
if (*audioBuffer) {
|
||||
audio_destroySoundBuffer(audioBuffer);
|
||||
}
|
||||
|
||||
long err = 0;
|
||||
|
@ -45,7 +39,7 @@ long audio_createSoundBuffer(INOUT AudioBuffer_s **pVoice, unsigned long dwBuffe
|
|||
err = -1;
|
||||
break;
|
||||
}
|
||||
err = audioContext->CreateSoundBuffer(¶ms, pVoice, audioContext);
|
||||
err = audioContext->CreateSoundBuffer(audioContext, numChannels, audioBuffer);
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
|
@ -56,7 +50,7 @@ long audio_createSoundBuffer(INOUT AudioBuffer_s **pVoice, unsigned long dwBuffe
|
|||
|
||||
void audio_destroySoundBuffer(INOUT AudioBuffer_s **audioBuffer) {
|
||||
if (audioContext) {
|
||||
audioContext->DestroySoundBuffer(audioBuffer);
|
||||
audioContext->DestroySoundBuffer(audioContext, audioBuffer);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#ifndef _SOUNDCORE_H_
|
||||
#define _SOUNDCORE_H_
|
||||
|
||||
#define MAX_SAMPLES (8*1024)
|
||||
#define AUDIO_STATUS_PLAYING 0x00000001
|
||||
#define AUDIO_STATUS_NOTPLAYING 0x08000000
|
||||
|
||||
|
@ -52,7 +51,7 @@ typedef struct AudioBuffer_s {
|
|||
/*
|
||||
* Creates a sound buffer object.
|
||||
*/
|
||||
long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer, unsigned long bufferSize, unsigned long sampleRate, int numChannels);
|
||||
long audio_createSoundBuffer(INOUT AudioBuffer_s **audioBuffer, unsigned long numChannels);
|
||||
|
||||
/*
|
||||
* Destroy and nullify sound buffer object.
|
||||
|
@ -82,28 +81,44 @@ void audio_resume(void);
|
|||
/*
|
||||
* Is the audio subsystem available?
|
||||
*/
|
||||
extern bool audio_isAvailable;
|
||||
extern READONLY bool audio_isAvailable;
|
||||
|
||||
typedef struct AudioSettings_s {
|
||||
|
||||
/*
|
||||
* Native device sample rate
|
||||
*/
|
||||
READONLY unsigned long sampleRateHz;
|
||||
|
||||
/*
|
||||
* Native device bytes-per-sample (currently assuming 16bit == 2bytes)
|
||||
*/
|
||||
READONLY unsigned long bytesPerSample;
|
||||
|
||||
/*
|
||||
* Native mono min/ideal buffer size in samples
|
||||
*/
|
||||
READONLY unsigned long monoBufferSizeSamples;
|
||||
|
||||
/*
|
||||
* Native stereo min/ideal buffer size in samples
|
||||
*/
|
||||
READONLY unsigned long stereoBufferSizeSamples;
|
||||
} AudioSettings_s;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Private audio backend APIs
|
||||
|
||||
typedef struct AudioParams_s {
|
||||
uint16_t nChannels;
|
||||
unsigned long nSamplesPerSec;
|
||||
unsigned long nAvgBytesPerSec;
|
||||
uint16_t nBlockAlign;
|
||||
uint16_t wBitsPerSample;
|
||||
unsigned long dwBufferBytes;
|
||||
} AudioParams_s;
|
||||
|
||||
typedef struct AudioContext_s {
|
||||
PRIVATE void *_internal;
|
||||
PRIVATE long (*CreateSoundBuffer)(const AudioParams_s *params, INOUT AudioBuffer_s **buffer, const struct AudioContext_s *sound_system);
|
||||
PRIVATE long (*DestroySoundBuffer)(INOUT AudioBuffer_s **buffer);
|
||||
PRIVATE long (*CreateSoundBuffer)(const struct AudioContext_s *sound_system, unsigned long numChannels, INOUT AudioBuffer_s **buffer);
|
||||
PRIVATE long (*DestroySoundBuffer)(const struct AudioContext_s *sound_system, INOUT AudioBuffer_s **buffer);
|
||||
} AudioContext_s;
|
||||
|
||||
typedef struct AudioBackend_s {
|
||||
|
||||
AudioSettings_s systemSettings;
|
||||
|
||||
// basic backend functionality controlled by soundcore
|
||||
PRIVATE long (*setup)(INOUT AudioContext_s **audio_context);
|
||||
PRIVATE long (*shutdown)(INOUT AudioContext_s **audio_context);
|
||||
|
@ -114,6 +129,6 @@ typedef struct AudioBackend_s {
|
|||
} AudioBackend_s;
|
||||
|
||||
// Audio backend registered at CTOR time
|
||||
PRIVATE extern AudioBackend_s *audio_backend;
|
||||
extern AudioBackend_s *audio_backend;
|
||||
|
||||
#endif /* whole file */
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
/* Apple //e speaker support. Source inspired/derived from AppleWin.
|
||||
*
|
||||
* - ~46 //e cycles per PC sample (played back at 22.050kHz) -- (CLK_6502/SPKR_SAMPLE_RATE)
|
||||
* - ~23 //e cycles per PC sample (played back at 44.100kHz)
|
||||
*
|
||||
* The soundcard output drives how much 6502 emulation is done in real-time. If the soundcard buffer is running out of
|
||||
* sample-data, then more 6502 cycles need to be executed to top-up the buffer, and vice-versa.
|
||||
|
@ -22,24 +22,26 @@
|
|||
|
||||
#include "common.h"
|
||||
|
||||
#define DEBUG_SPEAKER (!defined(NDEBUG) && 0) // enable to print timing stats
|
||||
#define DEBUG_SPEAKER 0
|
||||
#if DEBUG_SPEAKER
|
||||
# define SPEAKER_LOG(...) LOG(__VA_ARGS__)
|
||||
#else
|
||||
# define SPEAKER_LOG(...)
|
||||
#endif
|
||||
|
||||
#define MAX_REMAINDER_BUFFER (((CLK_6502_INT*(unsigned int)CPU_SCALE_FASTEST)/SPKR_SAMPLE_RATE)+1)
|
||||
static unsigned long bufferTotalSize = 0;
|
||||
static unsigned long bufferSizeIdealMin = 0;
|
||||
static unsigned long bufferSizeIdealMax = 0;
|
||||
static unsigned long sampleRateHz = 0;
|
||||
|
||||
#define SOUNDCORE_BUFFER_SIZE (MAX_SAMPLES*sizeof(int16_t)*1/*mono*/)
|
||||
#define QUARTER_SIZE (SOUNDCORE_BUFFER_SIZE/4)
|
||||
#define IDEAL_MIN (SOUNDCORE_BUFFER_SIZE/4) // minimum goldilocks zone for samples-in-play
|
||||
#define IDEAL_MAX (SOUNDCORE_BUFFER_SIZE/2) // maximum goldilocks-zone for samples-in-play
|
||||
|
||||
static int16_t samples_buffer[SPKR_SAMPLE_RATE * sizeof(int16_t)] = { 0 }; // holds max 1 second of samples
|
||||
static int16_t remainder_buffer[MAX_REMAINDER_BUFFER * sizeof(int16_t)] = { 0 }; // holds enough to create one sample (averaged)
|
||||
static bool speaker_isAvailable = false;
|
||||
|
||||
static int16_t *samples_buffer = NULL; // holds max 1 second of samples
|
||||
static int16_t *remainder_buffer = NULL; // holds enough to create one sample (averaged)
|
||||
static unsigned int samples_buffer_idx = 0;
|
||||
static unsigned int remainder_buffer_size = 0;
|
||||
static unsigned long remainder_buffer_size_max = 0;
|
||||
static unsigned int remainder_buffer_idx = 0;
|
||||
|
||||
static int16_t speaker_amplitude = SPKR_DATA_INIT;
|
||||
|
@ -77,14 +79,14 @@ static void _speaker_init_timing(void) {
|
|||
// 46.28 //e cycles for 22.05kHz sample rate
|
||||
|
||||
// AppleWin NOTE : use integer value: Better for MJ Mahon's RT.SYNTH.DSK (integer multiples of 1.023MHz Clk)
|
||||
cycles_per_sample = (unsigned int)(cycles_persec_target / (double)SPKR_SAMPLE_RATE);
|
||||
cycles_per_sample = (unsigned int)(cycles_persec_target / (double)sampleRateHz);
|
||||
|
||||
unsigned int last_remainder_buffer_size = remainder_buffer_size;
|
||||
remainder_buffer_size = (unsigned int)cycles_per_sample;
|
||||
if ((double)remainder_buffer_size != cycles_per_sample) {
|
||||
++remainder_buffer_size;
|
||||
}
|
||||
assert(remainder_buffer_size <= MAX_REMAINDER_BUFFER);
|
||||
assert(remainder_buffer_size <= remainder_buffer_size_max);
|
||||
|
||||
if (last_remainder_buffer_size == remainder_buffer_size) {
|
||||
// no change ... insure seamless remainder_buffer
|
||||
|
@ -148,7 +150,7 @@ static void _speaker_update(/*bool toggled*/) {
|
|||
|
||||
sample_mean /= (int)remainder_buffer_size;
|
||||
|
||||
if (samples_buffer_idx < SPKR_SAMPLE_RATE) {
|
||||
if (samples_buffer_idx < sampleRateHz) {
|
||||
samples_buffer[samples_buffer_idx++] = (int16_t)sample_mean;
|
||||
}
|
||||
}
|
||||
|
@ -159,7 +161,7 @@ static void _speaker_update(/*bool toggled*/) {
|
|||
const unsigned long long cycles_remainder = (unsigned long long)((double)cycles_diff - (double)num_samples * cycles_per_sample);
|
||||
|
||||
// populate samples_buffer with whole samples
|
||||
while (num_samples && (samples_buffer_idx < SPKR_SAMPLE_RATE)) {
|
||||
while (num_samples && (samples_buffer_idx < sampleRateHz)) {
|
||||
samples_buffer[samples_buffer_idx++] = speaker_data;
|
||||
if (speaker_going_silent && speaker_data) {
|
||||
speaker_data -= speaker_silent_step;
|
||||
|
@ -182,8 +184,8 @@ static void _speaker_update(/*bool toggled*/) {
|
|||
#endif
|
||||
}
|
||||
|
||||
if (UNLIKELY(samples_buffer_idx >= SPKR_SAMPLE_RATE)) {
|
||||
assert(samples_buffer_idx == SPKR_SAMPLE_RATE && "should be at exactly the end, no further");
|
||||
if (UNLIKELY(samples_buffer_idx >= sampleRateHz)) {
|
||||
assert(samples_buffer_idx == sampleRateHz && "should be at exactly the end, no further");
|
||||
ERRLOG("OOPS, overflow in speaker samples buffer");
|
||||
}
|
||||
}
|
||||
|
@ -207,13 +209,13 @@ static void _submit_samples_buffer_fullspeed(void) {
|
|||
if (err) {
|
||||
return;
|
||||
}
|
||||
assert(bytes_queued <= SOUNDCORE_BUFFER_SIZE);
|
||||
assert(bytes_queued <= bufferTotalSize);
|
||||
|
||||
if (bytes_queued >= IDEAL_MAX) {
|
||||
if (bytes_queued >= bufferSizeIdealMax) {
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int num_samples_pad = (IDEAL_MAX - bytes_queued) / sizeof(int16_t);
|
||||
unsigned int num_samples_pad = (bufferSizeIdealMax - bytes_queued) / sizeof(int16_t);
|
||||
if (num_samples_pad == 0) {
|
||||
return;
|
||||
}
|
||||
|
@ -245,15 +247,15 @@ static unsigned int _submit_samples_buffer(const unsigned int num_samples) {
|
|||
if (err) {
|
||||
return num_samples;
|
||||
}
|
||||
assert(bytes_queued <= SOUNDCORE_BUFFER_SIZE);
|
||||
assert(bytes_queued <= bufferTotalSize);
|
||||
|
||||
//
|
||||
// calculate CPU cycles feedback adjustment to prevent system audio buffer under/overflow
|
||||
//
|
||||
|
||||
if (bytes_queued < IDEAL_MIN) {
|
||||
if (bytes_queued < bufferSizeIdealMin) {
|
||||
samples_adjustment_counter += SOUNDCORE_ERROR_INC; // need moar data
|
||||
} else if (bytes_queued > IDEAL_MAX) {
|
||||
} else if (bytes_queued > bufferSizeIdealMax) {
|
||||
samples_adjustment_counter -= SOUNDCORE_ERROR_INC; // need less data
|
||||
} else {
|
||||
samples_adjustment_counter = 0; // Acceptable amount of data in buffer
|
||||
|
@ -267,13 +269,13 @@ static unsigned int _submit_samples_buffer(const unsigned int num_samples) {
|
|||
|
||||
cycles_speaker_feedback = (int)(samples_adjustment_counter * cycles_per_sample);
|
||||
|
||||
//SPEAKER_LOG("feedback:%d samples_adjustment_counter:%d", cycles_speaker_feedback, samples_adjustment_counter);
|
||||
//SPEAKER_LOG("feedback:%d samples_adjustment_counter:%d bytes_queued:%lu", cycles_speaker_feedback, samples_adjustment_counter, bytes_queued);
|
||||
|
||||
//
|
||||
// copy samples to audio system backend
|
||||
//
|
||||
|
||||
const unsigned int bytes_free = SOUNDCORE_BUFFER_SIZE - bytes_queued;
|
||||
const unsigned int bytes_free = bufferTotalSize - bytes_queued;
|
||||
unsigned int num_samples_to_use = num_samples;
|
||||
|
||||
if (num_samples_to_use * sizeof(int16_t) > bytes_free) {
|
||||
|
@ -303,24 +305,64 @@ static unsigned int _submit_samples_buffer(const unsigned int num_samples) {
|
|||
// speaker public API functions
|
||||
|
||||
void speaker_destroy(void) {
|
||||
speaker_isAvailable = false;
|
||||
audio_destroySoundBuffer(&speakerBuffer);
|
||||
FREE(samples_buffer);
|
||||
FREE(remainder_buffer);
|
||||
}
|
||||
|
||||
void speaker_init(void) {
|
||||
long err = audio_createSoundBuffer(&speakerBuffer, SOUNDCORE_BUFFER_SIZE, SPKR_SAMPLE_RATE, 1);
|
||||
if (!err) {
|
||||
long err = 0;
|
||||
speaker_isAvailable = false;
|
||||
do {
|
||||
err = audio_createSoundBuffer(&speakerBuffer, 1);
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
|
||||
bufferTotalSize = audio_backend->systemSettings.monoBufferSizeSamples * audio_backend->systemSettings.bytesPerSample;
|
||||
bufferSizeIdealMin = bufferTotalSize/4;
|
||||
bufferSizeIdealMax = bufferTotalSize/2;
|
||||
sampleRateHz = audio_backend->systemSettings.sampleRateHz;
|
||||
|
||||
remainder_buffer_size_max = ((CLK_6502_INT*(unsigned long)CPU_SCALE_FASTEST)/sampleRateHz)+1;
|
||||
|
||||
samples_buffer = malloc(sampleRateHz * sizeof(int16_t));
|
||||
if (!samples_buffer) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
remainder_buffer = malloc(remainder_buffer_size_max * sizeof(int16_t));
|
||||
if (!remainder_buffer) {
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
_speaker_init_timing();
|
||||
|
||||
speaker_isAvailable = true;
|
||||
} while (0);
|
||||
|
||||
if (err) {
|
||||
LOG("Error creating speaker subsystem, regular sound will be disabled!");
|
||||
if (samples_buffer) {
|
||||
FREE(samples_buffer);
|
||||
}
|
||||
if (remainder_buffer) {
|
||||
FREE(remainder_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void speaker_reset(void) {
|
||||
if (audio_isAvailable) {
|
||||
if (speaker_isAvailable) {
|
||||
_speaker_init_timing();
|
||||
}
|
||||
}
|
||||
|
||||
void speaker_flush(void) {
|
||||
if (!audio_isAvailable) {
|
||||
if (!speaker_isAvailable) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -345,7 +387,7 @@ void speaker_flush(void) {
|
|||
speaker_recently_active = false;
|
||||
speaker_going_silent = false;
|
||||
speaker_data = 0;
|
||||
} else if ((speaker_data == speaker_amplitude) && (cycles_count_total - cycles_quiet_time > cycles_diff)) {
|
||||
} else if ((speaker_data != 0) && (cycles_count_total - cycles_quiet_time > cycles_diff)) {
|
||||
// After 0.1sec of //e cycles time start reducing samples to zero (if they aren't there already). This
|
||||
// process attempts to mask the extraneous clicks when freezing/restarting emulation (GUI access) and
|
||||
// glitching from the audio system backend
|
||||
|
@ -377,7 +419,7 @@ bool speaker_is_active(void) {
|
|||
}
|
||||
|
||||
void speaker_set_volume(int16_t amplitude) {
|
||||
speaker_amplitude = (amplitude/4);
|
||||
speaker_amplitude = (amplitude/2);
|
||||
}
|
||||
|
||||
double speaker_cycles_per_sample(void) {
|
||||
|
@ -405,7 +447,7 @@ GLUE_C_READ(speaker_toggle)
|
|||
is_fullspeed = false;
|
||||
}
|
||||
|
||||
if (audio_isAvailable) {
|
||||
if (speaker_isAvailable) {
|
||||
_speaker_update(/*toggled:true*/);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#ifndef _SPEAKER_H_
|
||||
#define _SPEAKER_H_
|
||||
|
||||
#define SPKR_SAMPLE_RATE 22050
|
||||
#define SPKR_DATA_INIT 0x4000
|
||||
|
||||
void speaker_init(void);
|
||||
|
@ -24,7 +23,7 @@ bool speaker_is_active(void);
|
|||
|
||||
/*
|
||||
* returns the machine cycles per sample
|
||||
* - for emulator running at normal speed: CLK_6502 / SPKR_SAMPLE_RATE == ~46
|
||||
* - for example, emulator running at normal speed: CLK_6502 / 44.1kHz == ~23
|
||||
*/
|
||||
double speaker_cycles_per_sample(void);
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@
|
|||
#define INOUT
|
||||
#define PRIVATE
|
||||
#define PUBLIC
|
||||
#define READONLY
|
||||
|
||||
#define CTOR_PRIORITY_FIRST 101
|
||||
#define CTOR_PRIORITY_EARLY 111
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "common.h"
|
||||
|
||||
#if !defined(NANOSECONDS_PER_SECOND)
|
||||
#define NANOSECONDS_PER_SECOND 1000000000
|
||||
#define NANOSECONDS_PER_SECOND 1000000000UL
|
||||
#endif
|
||||
|
||||
// timing values cribbed from AppleWin ... reference: Sather's _Understanding the Apple IIe_
|
||||
|
|
Loading…
Reference in New Issue
Block a user