ft2-clone

Fasttracker 2 clone
Log | Files | Refs | README | LICENSE

ft2_audio.c (26237B)


      1 // for finding memory leaks in debug mode with Visual Studio
      2 #if defined _DEBUG && defined _MSC_VER
      3 #include <crtdbg.h>
      4 #endif
      5 
      6 #include <stdio.h>
      7 #include <stdint.h>
      8 #include "ft2_header.h"
      9 #include "ft2_config.h"
     10 #include "scopes/ft2_scopes.h"
     11 #include "ft2_video.h"
     12 #include "ft2_gui.h"
     13 #include "ft2_midi.h"
     14 #include "ft2_wav_renderer.h"
     15 #include "ft2_tables.h"
     16 #include "ft2_structs.h"
     17 #include "ft2_audioselector.h"
     18 #include "mixer/ft2_mix.h"
     19 #include "mixer/ft2_silence_mix.h"
     20 
     21 // hide POSIX warnings
     22 #ifdef _MSC_VER
     23 #pragma warning(disable: 4996)
     24 #endif
     25 
     26 static int32_t smpShiftValue;
     27 static uint32_t oldAudioFreq, tickTimeLenInt;
     28 static uint64_t tickTimeLenFrac;
     29 static float fAudioNormalizeMul, fSqrtPanningTable[256+1];
     30 static voice_t voice[MAX_CHANNELS * 2];
     31 
     32 // globalized
     33 audio_t audio;
     34 pattSyncData_t *pattSyncEntry;
     35 chSyncData_t *chSyncEntry;
     36 chSync_t chSync;
     37 pattSync_t pattSync;
     38 volatile bool pattQueueClearing, chQueueClearing;
     39 
     40 void stopVoice(int32_t i)
     41 {
     42 	voice_t *v;
     43 
     44 	v = &voice[i];
     45 	memset(v, 0, sizeof (voice_t));
     46 	v->panning = 128;
     47 
     48 	// clear "fade out" voice too
     49 
     50 	v = &voice[MAX_CHANNELS + i];
     51 	memset(v, 0, sizeof (voice_t));
     52 	v->panning = 128;
     53 }
     54 
     55 bool setNewAudioSettings(void) // only call this from the main input/video thread
     56 {
     57 	pauseAudio();
     58 
     59 	if (!setupAudio(CONFIG_HIDE_ERRORS))
     60 	{
     61 		// set back old known working settings
     62 
     63 		config.audioFreq = audio.lastWorkingAudioFreq;
     64 		config.specialFlags &= ~(BITDEPTH_16 + BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_1024 + BUFFSIZE_2048);
     65 		config.specialFlags |= audio.lastWorkingAudioBits;
     66 
     67 		if (audio.lastWorkingAudioDeviceName != NULL)
     68 		{
     69 			if (audio.currOutputDevice != NULL)
     70 			{
     71 				free(audio.currOutputDevice);
     72 				audio.currOutputDevice = NULL;
     73 			}
     74 
     75 			audio.currOutputDevice = strdup(audio.lastWorkingAudioDeviceName);
     76 		}
     77 
     78 		// also update config audio radio buttons if we're on that screen at the moment
     79 		if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO)
     80 			setConfigAudioRadioButtonStates();
     81 
     82 		// if it didn't work to use the old settings again, then something is seriously wrong...
     83 		if (!setupAudio(CONFIG_HIDE_ERRORS))
     84 			okBox(0, "System message", "Couldn't find a working audio mode... You'll get no sound / replayer timer!", NULL);
     85 
     86 		resumeAudio();
     87 		return false;
     88 	}
     89 
     90 	resumeAudio();
     91 
     92 	setWavRenderFrequency(audio.freq);
     93 	setWavRenderBitDepth((config.specialFlags & BITDEPTH_32) ? 32 : 16);
     94 	return true;
     95 }
     96 
     97 // amp = 1..32, masterVol = 0..256
     98 void setAudioAmp(int16_t amp, int16_t masterVol, bool bitDepth32Flag)
     99 {
    100 	amp = CLAMP(amp, 1, 32);
    101 	masterVol = CLAMP(masterVol, 0, 256);
    102 
    103 	double dAmp = (amp * masterVol) / (32.0 * 256.0);
    104 	if (!bitDepth32Flag)
    105 		dAmp *= 32768.0;
    106 
    107 	fAudioNormalizeMul = (float)dAmp;
    108 }
    109 
    110 void decreaseMasterVol(void)
    111 {
    112 	if (config.masterVol >= 16)
    113 		config.masterVol -= 16;
    114 	else
    115 		config.masterVol = 0;
    116 
    117 	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
    118 
    119 	// if Config -> Audio is open, update master volume scrollbar
    120 	if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO)
    121 		drawScrollBar(SB_MASTERVOL_SCROLL);
    122 }
    123 
    124 void increaseMasterVol(void)
    125 {
    126 	if (config.masterVol < (256-16))
    127 		config.masterVol += 16;
    128 	else
    129 		config.masterVol = 256;
    130 
    131 	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
    132 
    133 	// if Config -> Audio is open, update master volume scrollbar
    134 	if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO)
    135 		drawScrollBar(SB_MASTERVOL_SCROLL);
    136 }
    137 
    138 void setNewAudioFreq(uint32_t freq) // for song-to-WAV rendering
    139 {
    140 	if (freq == 0)
    141 		return;
    142 
    143 	oldAudioFreq = audio.freq;
    144 	audio.freq = freq;
    145 
    146 	const bool mustRecalcTables = audio.freq != oldAudioFreq;
    147 	if (mustRecalcTables)
    148 		calcReplayerVars(audio.freq);
    149 }
    150 
    151 void setBackOldAudioFreq(void) // for song-to-WAV rendering
    152 {
    153 	const bool mustRecalcTables = audio.freq != oldAudioFreq;
    154 
    155 	audio.freq = oldAudioFreq;
    156 
    157 	if (mustRecalcTables)
    158 		calcReplayerVars(audio.freq);
    159 }
    160 
    161 void setMixerBPM(int32_t bpm)
    162 {
    163 	if (bpm < MIN_BPM || bpm > MAX_BPM)
    164 		return;
    165 
    166 	int32_t i = bpm - MIN_BPM;
    167 
    168 	audio.samplesPerTickInt = audio.samplesPerTickIntTab[i];
    169 	audio.samplesPerTickFrac = audio.samplesPerTickFracTab[i];
    170 	audio.fSamplesPerTickIntMul = (float)(1.0 / (double)audio.samplesPerTickInt);
    171 
    172 	// for audio/video sync timestamp
    173 	tickTimeLenInt = audio.tickTimeIntTab[i];
    174 	tickTimeLenFrac = audio.tickTimeFracTab[i];
    175 }
    176 
    177 void audioSetVolRamp(bool volRamp)
    178 {
    179 	lockMixerCallback();
    180 	audio.volumeRampingFlag = volRamp;
    181 	unlockMixerCallback();
    182 }
    183 
    184 void audioSetInterpolationType(uint8_t interpolationType)
    185 {
    186 	lockMixerCallback();
    187 	audio.interpolationType = interpolationType;
    188 
    189 	audio.sincInterpolation = false;
    190 
    191 	// set sinc LUT pointers
    192 	if (config.interpolation == INTERPOLATION_SINC8)
    193 	{
    194 		fSinc_1 = fSinc8_1;
    195 		fSinc_2 = fSinc8_2;
    196 		fSinc_3 = fSinc8_3;
    197 
    198 		audio.sincInterpolation = true;
    199 	}
    200 	else if (config.interpolation == INTERPOLATION_SINC16)
    201 	{
    202 		fSinc_1 = fSinc16_1;
    203 		fSinc_2 = fSinc16_2;
    204 		fSinc_3 = fSinc16_3;
    205 
    206 		audio.sincInterpolation = true;
    207 	}
    208 
    209 	unlockMixerCallback();
    210 }
    211 
    212 void calcPanningTable(void)
    213 {
    214 	// same formula as FT2's panning table (with 0.0 .. 1.0 scale)
    215 	for (int32_t i = 0; i <= 256; i++)
    216 		fSqrtPanningTable[i] = (float)sqrt(i / 256.0);
    217 }
    218 
    219 static void voiceUpdateVolumes(int32_t i, uint8_t status)
    220 {
    221 	voice_t *v = &voice[i];
    222 
    223 	v->fTargetVolumeL = v->fVolume * fSqrtPanningTable[256-v->panning];
    224 	v->fTargetVolumeR = v->fVolume * fSqrtPanningTable[    v->panning];
    225 
    226 	if (!audio.volumeRampingFlag)
    227 	{
    228 		// volume ramping is disabled, set volume directly
    229 		v->fCurrVolumeL = v->fTargetVolumeL;
    230 		v->fCurrVolumeR = v->fTargetVolumeR;
    231 		v->volumeRampLength = 0;
    232 		return;
    233 	}
    234 
    235 	// now we need to handle volume ramping
    236 
    237 	const bool voiceSampleTrigger = !!(status & IS_Trigger);
    238 
    239 	if (voiceSampleTrigger)
    240 	{
    241 		// sample is about to start, ramp out/in at the same time
    242 
    243 		if (v->fCurrVolumeL > 0.0f || v->fCurrVolumeR > 0.0f)
    244 		{
    245 			// setup fadeout voice
    246 
    247 			voice_t *f = &voice[MAX_CHANNELS+i];
    248 
    249 			*f = *v; // copy current voice to new fadeout-ramp voice
    250 
    251 			const float fVolumeLDiff = 0.0f - f->fCurrVolumeL;
    252 			const float fVolumeRDiff = 0.0f - f->fCurrVolumeR;
    253 
    254 			f->volumeRampLength = audio.quickVolRampSamples; // 5ms
    255 			f->fVolumeLDelta = fVolumeLDiff * audio.fQuickVolRampSamplesMul;
    256 			f->fVolumeRDelta = fVolumeRDiff * audio.fQuickVolRampSamplesMul;
    257 
    258 			f->isFadeOutVoice = true;
    259 		}
    260 
    261 		// make current voice fade in from zero when it starts
    262 		v->fCurrVolumeL = v->fCurrVolumeR = 0.0f;
    263 	}
    264 
    265 	if (!voiceSampleTrigger && v->fTargetVolumeL == v->fCurrVolumeL && v->fTargetVolumeR == v->fCurrVolumeR)
    266 	{
    267 		v->volumeRampLength = 0; // no ramp needed for now
    268 	}
    269 	else
    270 	{
    271 		const float fVolumeLDiff = v->fTargetVolumeL - v->fCurrVolumeL;
    272 		const float fVolumeRDiff = v->fTargetVolumeR - v->fCurrVolumeR;
    273 
    274 		float fRampLengthMul;
    275 		if (status & IS_QuickVol) // duration of 5ms
    276 		{
    277 			v->volumeRampLength = audio.quickVolRampSamples;
    278 			fRampLengthMul = audio.fQuickVolRampSamplesMul;
    279 		}
    280 		else // duration of a tick
    281 		{
    282 			v->volumeRampLength = audio.samplesPerTickInt;
    283 			fRampLengthMul = audio.fSamplesPerTickIntMul;
    284 		}
    285 
    286 		v->fVolumeLDelta = fVolumeLDiff * fRampLengthMul;
    287 		v->fVolumeRDelta = fVolumeRDiff * fRampLengthMul;
    288 	}
    289 }
    290 
    291 static void voiceTrigger(int32_t ch, sample_t *s, int32_t position)
    292 {
    293 	voice_t *v = &voice[ch];
    294 
    295 	int32_t length = s->length;
    296 	int32_t loopStart = s->loopStart;
    297 	int32_t loopLength = s->loopLength;
    298 	int32_t loopEnd = s->loopStart + s->loopLength;
    299 	uint8_t loopType = GET_LOOPTYPE(s->flags);
    300 	bool sample16Bit = !!(s->flags & SAMPLE_16BIT);
    301 
    302 	if (s->dataPtr == NULL || length < 1)
    303 	{
    304 		v->active = false; // shut down voice (illegal parameters)
    305 		return;
    306 	}
    307 
    308 	if (loopLength < 1) // disable loop if loopLength is below 1
    309 		loopType = 0;
    310 
    311 	if (sample16Bit)
    312 	{
    313 		v->base16 = (const int16_t *)s->dataPtr;
    314 		v->revBase16 = &v->base16[loopStart + loopEnd]; // for pingpong loops
    315 		v->leftEdgeTaps16 = s->leftEdgeTapSamples16 + MAX_LEFT_TAPS;
    316 	}
    317 	else
    318 	{
    319 		v->base8 = s->dataPtr;
    320 		v->revBase8 = &v->base8[loopStart + loopEnd]; // for pingpong loops
    321 		v->leftEdgeTaps8 = s->leftEdgeTapSamples8 + MAX_LEFT_TAPS;
    322 	}
    323 
    324 	v->hasLooped = false; // for cubic/sinc interpolation special case
    325 	v->samplingBackwards = false;
    326 	v->loopType = loopType;
    327 	v->sampleEnd = (loopType == LOOP_OFF) ? length : loopEnd;
    328 	v->loopStart = loopStart;
    329 	v->loopLength = loopLength;
    330 	v->position = position;
    331 	v->positionFrac = 0;
    332 
    333 	// if position overflows, shut down voice (f.ex. through 9xx command)
    334 	if (v->position >= v->sampleEnd)
    335 	{
    336 		v->active = false;
    337 		return;
    338 	}
    339 
    340 	v->mixFuncOffset = ((int32_t)sample16Bit * 18) + (audio.interpolationType * 3) + loopType;
    341 	v->active = true;
    342 }
    343 
    344 void resetRampVolumes(void)
    345 {
    346 	voice_t *v = voice;
    347 	for (int32_t i = 0; i < song.numChannels; i++, v++)
    348 	{
    349 		v->fCurrVolumeL = v->fTargetVolumeL;
    350 		v->fCurrVolumeR = v->fTargetVolumeR;
    351 		v->volumeRampLength = 0;
    352 	}
    353 }
    354 
    355 void updateVoices(void)
    356 {
    357 	channel_t *ch = channel;
    358 	voice_t *v = voice;
    359 
    360 	for (int32_t i = 0; i < song.numChannels; i++, ch++, v++)
    361 	{
    362 		const uint8_t status = ch->tmpStatus = ch->status; // (tmpStatus is used for audio/video sync queue)
    363 		if (status == 0)
    364 			continue;
    365 
    366 		ch->status = 0;
    367 
    368 		if (status & IS_Vol)
    369 		{
    370 			v->fVolume = ch->fFinalVol; // 0.0f .. 1.0f
    371 			v->scopeVolume = (uint8_t)((ch->fFinalVol * 255.0f) + 0.5f); // 0..255, rounded
    372 		}
    373 
    374 		if (status & IS_Pan)
    375 			v->panning = ch->finalPan;
    376 
    377 		if (status & (IS_Vol + IS_Pan))
    378 			voiceUpdateVolumes(i, status);
    379 
    380 		if (status & IS_Period)
    381 		{
    382 			const double dVoiceHz = dPeriod2Hz(ch->finalPeriod);
    383 
    384 			// set voice delta
    385 			v->delta = (int64_t)((dVoiceHz * audio.dHz2MixDeltaMul) + 0.5); // Hz -> fixed-point delta (rounded)
    386 			if (audio.sincInterpolation)
    387 			{
    388 				// decide which sinc LUT to use according to the resampling ratio
    389 				if (v->delta <= sincRatio1)
    390 					v->fSincLUT = fSinc_1;
    391 				else if (v->delta <= sincRatio2)
    392 					v->fSincLUT = fSinc_2;
    393 				else
    394 					v->fSincLUT = fSinc_3;
    395 			}
    396 		}
    397 
    398 		if (status & IS_Trigger)
    399 			voiceTrigger(i, ch->smpPtr, ch->smpStartPos);
    400 	}
    401 }
    402 
    403 static void sendSamples16BitStereo(void *stream, uint32_t sampleBlockLength)
    404 {
    405 	int16_t *streamPtr16 = (int16_t *)stream;
    406 	for (uint32_t i = 0; i < sampleBlockLength; i++)
    407 	{
    408 		int32_t L = (int32_t)(audio.fMixBufferL[i] * fAudioNormalizeMul);
    409 		int32_t R = (int32_t)(audio.fMixBufferR[i] * fAudioNormalizeMul);
    410 
    411 		CLAMP16(L);
    412 		CLAMP16(R);
    413 
    414 		*streamPtr16++ = (int16_t)L;
    415 		*streamPtr16++ = (int16_t)R;
    416 
    417 		// clear what we read from the mixing buffer
    418 		audio.fMixBufferL[i] = audio.fMixBufferR[i] = 0.0f;
    419 	}
    420 }
    421 
    422 static void sendSamples32BitFloatStereo(void *stream, uint32_t sampleBlockLength)
    423 {
    424 	float *fStreamPtr32 = (float *)stream;
    425 	for (uint32_t i = 0; i < sampleBlockLength; i++)
    426 	{
    427 		const float fL = audio.fMixBufferL[i] * fAudioNormalizeMul;
    428 		const float fR = audio.fMixBufferR[i] * fAudioNormalizeMul;
    429 
    430 		*fStreamPtr32++ = CLAMP(fL, -1.0f, 1.0f);
    431 		*fStreamPtr32++ = CLAMP(fR, -1.0f, 1.0f);
    432 
    433 		// clear what we read from the mixing buffer
    434 		audio.fMixBufferL[i] = audio.fMixBufferR[i] = 0.0f;
    435 	}
    436 }
    437 
    438 static void doChannelMixing(int32_t bufferPosition, int32_t samplesToMix)
    439 {
    440 	voice_t *v = voice; // normal voices
    441 	voice_t *r = &voice[MAX_CHANNELS]; // volume ramp fadeout-voices
    442 
    443 	const int32_t mixOffsetBias = 3 * NUM_INTERPOLATORS * 2; // 3 = loop types (off/fwd/bidi), 2 = bit depths (8-bit/16-bit)
    444 
    445 	for (int32_t i = 0; i < song.numChannels; i++, v++, r++)
    446 	{
    447 		if (v->active)
    448 		{
    449 			const bool volRampFlag = (v->volumeRampLength > 0);
    450 			if (!volRampFlag && v->fCurrVolumeL == 0.0f && v->fCurrVolumeR == 0.0f)
    451 				silenceMixRoutine(v, samplesToMix);
    452 			else
    453 				mixFuncTab[((int32_t)volRampFlag * mixOffsetBias) + v->mixFuncOffset](v, bufferPosition, samplesToMix);
    454 		}
    455 
    456 		if (r->active) // volume ramp fadeout-voice
    457 			mixFuncTab[mixOffsetBias + r->mixFuncOffset](r, bufferPosition, samplesToMix);
    458 	}
    459 }
    460 
    461 // used for song-to-WAV renderer
    462 void mixReplayerTickToBuffer(uint32_t samplesToMix, void *stream, uint8_t bitDepth)
    463 {
    464 	doChannelMixing(0, samplesToMix);
    465 
    466 	// normalize mix buffer and send to audio stream
    467 	if (bitDepth == 16)
    468 		sendSamples16BitStereo(stream, samplesToMix);
    469 	else
    470 		sendSamples32BitFloatStereo(stream, samplesToMix);
    471 }
    472 
    473 int32_t pattQueueReadSize(void)
    474 {
    475 	while (pattQueueClearing);
    476 
    477 	if (pattSync.writePos > pattSync.readPos)
    478 		return pattSync.writePos - pattSync.readPos;
    479 	else if (pattSync.writePos < pattSync.readPos)
    480 		return pattSync.writePos - pattSync.readPos + SYNC_QUEUE_LEN + 1;
    481 	else
    482 		return 0;
    483 }
    484 
    485 int32_t pattQueueWriteSize(void)
    486 {
    487 	int32_t size;
    488 
    489 	if (pattSync.writePos > pattSync.readPos)
    490 	{
    491 		size = pattSync.readPos - pattSync.writePos + SYNC_QUEUE_LEN;
    492 	}
    493 	else if (pattSync.writePos < pattSync.readPos)
    494 	{
    495 		pattQueueClearing = true;
    496 
    497 		/* Buffer is full, reset the read/write pos. This is actually really nasty since
    498 		** read/write are two different threads, but because of timestamp validation it
    499 		** shouldn't be that dangerous.
    500 		** It will also create a small visual stutter while the buffer is getting filled,
    501 		** though that is barely noticable on normal buffer sizes, and it takes a minute
    502 		** or two at max BPM between each time (when queue size is default, 4095)
    503 		*/
    504 		pattSync.data[0].timestamp = 0;
    505 		pattSync.readPos = 0;
    506 		pattSync.writePos = 0;
    507 
    508 		size = SYNC_QUEUE_LEN;
    509 
    510 		pattQueueClearing = false;
    511 	}
    512 	else
    513 	{
    514 		size = SYNC_QUEUE_LEN;
    515 	}
    516 
    517 	return size;
    518 }
    519 
    520 bool pattQueuePush(pattSyncData_t t)
    521 {
    522 	if (!pattQueueWriteSize())
    523 		return false;
    524 
    525 	assert(pattSync.writePos <= SYNC_QUEUE_LEN);
    526 	pattSync.data[pattSync.writePos] = t;
    527 	pattSync.writePos = (pattSync.writePos + 1) & SYNC_QUEUE_LEN;
    528 
    529 	return true;
    530 }
    531 
    532 bool pattQueuePop(void)
    533 {
    534 	if (!pattQueueReadSize())
    535 		return false;
    536 
    537 	pattSync.readPos = (pattSync.readPos + 1) & SYNC_QUEUE_LEN;
    538 	assert(pattSync.readPos <= SYNC_QUEUE_LEN);
    539 
    540 	return true;
    541 }
    542 
    543 pattSyncData_t *pattQueuePeek(void)
    544 {
    545 	if (!pattQueueReadSize())
    546 		return NULL;
    547 
    548 	assert(pattSync.readPos <= SYNC_QUEUE_LEN);
    549 	return &pattSync.data[pattSync.readPos];
    550 }
    551 
    552 uint64_t getPattQueueTimestamp(void)
    553 {
    554 	if (!pattQueueReadSize())
    555 		return 0;
    556 
    557 	assert(pattSync.readPos <= SYNC_QUEUE_LEN);
    558 	return pattSync.data[pattSync.readPos].timestamp;
    559 }
    560 
    561 int32_t chQueueReadSize(void)
    562 {
    563 	while (chQueueClearing);
    564 
    565 	if (chSync.writePos > chSync.readPos)
    566 		return chSync.writePos - chSync.readPos;
    567 	else if (chSync.writePos < chSync.readPos)
    568 		return chSync.writePos - chSync.readPos + SYNC_QUEUE_LEN + 1;
    569 	else
    570 		return 0;
    571 }
    572 
    573 int32_t chQueueWriteSize(void)
    574 {
    575 	int32_t size;
    576 
    577 	if (chSync.writePos > chSync.readPos)
    578 	{
    579 		size = chSync.readPos - chSync.writePos + SYNC_QUEUE_LEN;
    580 	}
    581 	else if (chSync.writePos < chSync.readPos)
    582 	{
    583 		chQueueClearing = true;
    584 
    585 		/* Buffer is full, reset the read/write pos. This is actually really nasty since
    586 		** read/write are two different threads, but because of timestamp validation it
    587 		** shouldn't be that dangerous.
    588 		** It will also create a small visual stutter while the buffer is getting filled,
    589 		** though that is barely noticable on normal buffer sizes, and it takes several
    590 		** minutes between each time (when queue size is default, 16384)
    591 		*/
    592 		chSync.data[0].timestamp = 0;
    593 		chSync.readPos = 0;
    594 		chSync.writePos = 0;
    595 
    596 		size = SYNC_QUEUE_LEN;
    597 
    598 		chQueueClearing = false;
    599 	}
    600 	else
    601 	{
    602 		size = SYNC_QUEUE_LEN;
    603 	}
    604 
    605 	return size;
    606 }
    607 
    608 bool chQueuePush(chSyncData_t t)
    609 {
    610 	if (!chQueueWriteSize())
    611 		return false;
    612 
    613 	assert(chSync.writePos <= SYNC_QUEUE_LEN);
    614 	chSync.data[chSync.writePos] = t;
    615 	chSync.writePos = (chSync.writePos + 1) & SYNC_QUEUE_LEN;
    616 
    617 	return true;
    618 }
    619 
    620 bool chQueuePop(void)
    621 {
    622 	if (!chQueueReadSize())
    623 		return false;
    624 
    625 	chSync.readPos = (chSync.readPos + 1) & SYNC_QUEUE_LEN;
    626 	assert(chSync.readPos <= SYNC_QUEUE_LEN);
    627 
    628 	return true;
    629 }
    630 
    631 chSyncData_t *chQueuePeek(void)
    632 {
    633 	if (!chQueueReadSize())
    634 		return NULL;
    635 
    636 	assert(chSync.readPos <= SYNC_QUEUE_LEN);
    637 	return &chSync.data[chSync.readPos];
    638 }
    639 
    640 uint64_t getChQueueTimestamp(void)
    641 {
    642 	if (!chQueueReadSize())
    643 		return 0;
    644 
    645 	assert(chSync.readPos <= SYNC_QUEUE_LEN);
    646 	return chSync.data[chSync.readPos].timestamp;
    647 }
    648 
    649 void lockAudio(void)
    650 {
    651 	if (audio.dev != 0)
    652 		SDL_LockAudioDevice(audio.dev);
    653 
    654 	audio.locked = true;
    655 }
    656 
    657 void unlockAudio(void)
    658 {
    659 	if (audio.dev != 0)
    660 		SDL_UnlockAudioDevice(audio.dev);
    661 
    662 	audio.locked = false;
    663 }
    664 
    665 void resetSyncQueues(void)
    666 {
    667 	pattSync.data[0].timestamp = 0;
    668 	pattSync.readPos = 0;
    669 	pattSync.writePos = 0;
    670 	
    671 	chSync.data[0].timestamp = 0;
    672 	chSync.writePos = 0;
    673 	chSync.readPos = 0;
    674 }
    675 
    676 void lockMixerCallback(void) // lock audio + clear voices/scopes (for short operations)
    677 {
    678 	if (!audio.locked)
    679 		lockAudio();
    680 
    681 	audio.resetSyncTickTimeFlag = true;
    682 
    683 	stopVoices(); // VERY important! prevents potential crashes by purging pointers
    684 
    685 	// scopes, mixer and replayer are guaranteed to not be active at this point
    686 
    687 	resetSyncQueues();
    688 }
    689 
    690 void unlockMixerCallback(void)
    691 {
    692 	stopVoices(); // VERY important! prevents potential crashes by purging pointers
    693 	
    694 	if (audio.locked)
    695 		unlockAudio();
    696 }
    697 
    698 void pauseAudio(void) // lock audio + clear voices/scopes + render silence (for long operations)
    699 {
    700 	if (audioPaused)
    701 	{
    702 		stopVoices(); // VERY important! prevents potential crashes by purging pointers
    703 		return;
    704 	}
    705 
    706 	if (audio.dev > 0)
    707 		SDL_PauseAudioDevice(audio.dev, true);
    708 
    709 	audio.resetSyncTickTimeFlag = true;
    710 
    711 	stopVoices(); // VERY important! prevents potential crashes by purging pointers
    712 
    713 	// scopes, mixer and replayer are guaranteed to not be active at this point
    714 
    715 	resetSyncQueues();
    716 	audioPaused = true;
    717 }
    718 
    719 void resumeAudio(void) // unlock audio
    720 {
    721 	if (!audioPaused)
    722 		return;
    723 
    724 	if (audio.dev > 0)
    725 		SDL_PauseAudioDevice(audio.dev, false);
    726 
    727 	audioPaused = false;
    728 }
    729 
    730 static void fillVisualsSyncBuffer(void)
    731 {
    732 	pattSyncData_t pattSyncData;
    733 	chSyncData_t chSyncData;
    734 
    735 	if (audio.resetSyncTickTimeFlag)
    736 	{
    737 		audio.resetSyncTickTimeFlag = false;
    738 
    739 		audio.tickTime64 = SDL_GetPerformanceCounter() + audio.audLatencyPerfValInt;
    740 		audio.tickTime64Frac = audio.audLatencyPerfValFrac;
    741 	}
    742 
    743 	if (songPlaying)
    744 	{
    745 		// push pattern variables to sync queue
    746 		pattSyncData.tick = song.curReplayerTick;
    747 		pattSyncData.row = song.curReplayerRow;
    748 		pattSyncData.pattNum = song.curReplayerPattNum;
    749 		pattSyncData.songPos = song.curReplayerSongPos;
    750 		pattSyncData.BPM = (uint8_t)song.BPM;
    751 		pattSyncData.speed = (uint8_t)song.speed;
    752 		pattSyncData.globalVolume = (uint8_t)song.globalVolume;
    753 		pattSyncData.timestamp = audio.tickTime64;
    754 		pattQueuePush(pattSyncData);
    755 	}
    756 
    757 	// push channel variables to sync queue
    758 
    759 	syncedChannel_t *c = chSyncData.channels;
    760 	channel_t *s = channel;
    761 	voice_t *v = voice;
    762 
    763 	for (int32_t i = 0; i < song.numChannels; i++, c++, s++, v++)
    764 	{
    765 		c->scopeVolume = v->scopeVolume;
    766 		c->period = s->finalPeriod;
    767 		c->instrNum = s->instrNum;
    768 		c->smpNum = s->smpNum;
    769 		c->status = s->tmpStatus;
    770 		c->smpStartPos = s->smpStartPos;
    771 
    772 		c->pianoNoteNum = 255; // no piano key
    773 		if (songPlaying && ui.instEditorShown && (c->status & IS_Period) && !s->keyOff)
    774 		{
    775 			const int32_t note = getPianoKey(s->finalPeriod, s->finetune, s->relativeNote);
    776 			if (note >= 0 && note <= 95)
    777 				c->pianoNoteNum = (uint8_t)note;
    778 		}
    779 	}
    780 
    781 	chSyncData.timestamp = audio.tickTime64;
    782 	chQueuePush(chSyncData);
    783 
    784 	audio.tickTime64 += tickTimeLenInt;
    785 
    786 	audio.tickTime64Frac += tickTimeLenFrac;
    787 	if (audio.tickTime64Frac >= TICK_TIME_FRAC_SCALE)
    788 	{
    789 		audio.tickTime64Frac &= TICK_TIME_FRAC_MASK;
    790 		audio.tickTime64++;
    791 	}
    792 }
    793 
    794 static void SDLCALL audioCallback(void *userdata, Uint8 *stream, int len)
    795 {
    796 	if (editor.wavIsRendering)
    797 		return;
    798 
    799 	len >>= smpShiftValue; // bytes -> samples
    800 	if (len <= 0)
    801 		return;
    802 
    803 	int32_t bufferPosition = 0;
    804 
    805 	uint32_t samplesLeft = len;
    806 	while (samplesLeft > 0)
    807 	{
    808 		if (audio.tickSampleCounter == 0) // new replayer tick
    809 		{
    810 			replayerBusy = true;
    811 			if (!musicPaused) // important, don't remove this check! (also used for safety)
    812 			{
    813 				if (audio.volumeRampingFlag)
    814 					resetRampVolumes();
    815 
    816 				tickReplayer();
    817 				updateVoices();
    818 				fillVisualsSyncBuffer();
    819 			}
    820 			replayerBusy = false;
    821 
    822 			audio.tickSampleCounter = audio.samplesPerTickInt;
    823 
    824 			audio.tickSampleCounterFrac += audio.samplesPerTickFrac;
    825 			if (audio.tickSampleCounterFrac >= BPM_FRAC_SCALE)
    826 			{
    827 				audio.tickSampleCounterFrac &= BPM_FRAC_MASK;
    828 				audio.tickSampleCounter++;
    829 			}
    830 		}
    831 
    832 		uint32_t samplesToMix = samplesLeft;
    833 		if (samplesToMix > audio.tickSampleCounter)
    834 			samplesToMix = audio.tickSampleCounter;
    835 
    836 		doChannelMixing(bufferPosition, samplesToMix);
    837 		bufferPosition += samplesToMix;
    838 		
    839 		audio.tickSampleCounter -= samplesToMix;
    840 		samplesLeft -= samplesToMix;
    841 	}
    842 
    843 	if (config.specialFlags & BITDEPTH_16)
    844 		sendSamples16BitStereo(stream, len);
    845 	else
    846 		sendSamples32BitFloatStereo(stream, len);
    847 
    848 	(void)userdata;
    849 }
    850 
    851 static bool setupAudioBuffers(void)
    852 {
    853 	const int32_t maxAudioFreq = MAX(MAX_AUDIO_FREQ, MAX_WAV_RENDER_FREQ);
    854 	int32_t maxSamplesPerTick = (int32_t)ceil(maxAudioFreq / (MIN_BPM / 2.5)) + 1;
    855 
    856 	audio.fMixBufferL = (float *)calloc(maxSamplesPerTick, sizeof (float));
    857 	audio.fMixBufferR = (float *)calloc(maxSamplesPerTick, sizeof (float));
    858 
    859 	if (audio.fMixBufferL == NULL || audio.fMixBufferR == NULL)
    860 		return false;
    861 
    862 	return true;
    863 }
    864 
    865 static void freeAudioBuffers(void)
    866 {
    867 	if (audio.fMixBufferL != NULL)
    868 	{
    869 		free(audio.fMixBufferL);
    870 		audio.fMixBufferL = NULL;
    871 	}
    872 
    873 	if (audio.fMixBufferR != NULL)
    874 	{
    875 		free(audio.fMixBufferR);
    876 		audio.fMixBufferR = NULL;
    877 	}
    878 }
    879 
    880 static void calcAudioLatencyVars(int32_t audioBufferSize, int32_t audioFreq)
    881 {
    882 	double dInt;
    883 
    884 	if (audioFreq == 0)
    885 		return;
    886 
    887 	const double dAudioLatencySecs = audioBufferSize / (double)audioFreq;
    888 
    889 	double dFrac = modf(dAudioLatencySecs * editor.dPerfFreq, &dInt);
    890 
    891 	audio.audLatencyPerfValInt = (uint32_t)dInt;
    892 	audio.audLatencyPerfValFrac = (uint64_t)((dFrac * TICK_TIME_FRAC_SCALE) + 0.5); // rounded
    893 }
    894 
    895 static void setLastWorkingAudioDevName(void)
    896 {
    897 	if (audio.lastWorkingAudioDeviceName != NULL)
    898 	{
    899 		free(audio.lastWorkingAudioDeviceName);
    900 		audio.lastWorkingAudioDeviceName = NULL;
    901 	}
    902 
    903 	if (audio.currOutputDevice != NULL)
    904 		audio.lastWorkingAudioDeviceName = strdup(audio.currOutputDevice);
    905 }
    906 
    907 bool setupAudio(bool showErrorMsg)
    908 {
    909 	SDL_AudioSpec want, have;
    910 
    911 	closeAudio();
    912 
    913 	if (config.audioFreq < MIN_AUDIO_FREQ || config.audioFreq > MAX_AUDIO_FREQ)
    914 		config.audioFreq = DEFAULT_AUDIO_FREQ;
    915 
    916 	// get audio buffer size from config special flags
    917 
    918 	uint16_t configAudioBufSize = 1024;
    919 	if (config.specialFlags & BUFFSIZE_512)
    920 		configAudioBufSize = 512;
    921 	else if (config.specialFlags & BUFFSIZE_2048)
    922 		configAudioBufSize = 2048;
    923 
    924 	audio.wantFreq = config.audioFreq;
    925 	audio.wantSamples = configAudioBufSize;
    926 
    927 	// set up audio device
    928 	memset(&want, 0, sizeof (want));
    929 	want.freq = config.audioFreq;
    930 	want.format = (config.specialFlags & BITDEPTH_32) ? AUDIO_F32 : AUDIO_S16;
    931 	want.channels = 2;
    932 	want.callback = audioCallback;
    933 	want.samples  = configAudioBufSize;
    934 
    935 	char *device = audio.currOutputDevice;
    936 	if (device != NULL && strcmp(device, DEFAULT_AUDIO_DEV_STR) == 0)
    937 		device = NULL; // force default device
    938 
    939 	audio.dev = SDL_OpenAudioDevice(device, 0, &want, &have, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
    940 	if (audio.dev == 0)
    941 	{
    942 		audio.dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
    943 		if (audio.currOutputDevice != NULL)
    944 		{
    945 			free(audio.currOutputDevice);
    946 			audio.currOutputDevice = NULL;
    947 		}
    948 		audio.currOutputDevice = strdup(DEFAULT_AUDIO_DEV_STR);
    949 
    950 		if (audio.dev == 0)
    951 		{
    952 			if (showErrorMsg)
    953 				showErrorMsgBox("Couldn't open audio device:\n\"%s\"\n\nDo you have an audio device enabled and plugged in?", SDL_GetError());
    954 
    955 			return false;
    956 		}
    957 	}
    958 
    959 	// test if the received audio format is compatible
    960 	if (have.format != AUDIO_S16 && have.format != AUDIO_F32)
    961 	{
    962 		if (showErrorMsg)
    963 			showErrorMsgBox("Couldn't open audio device:\nThis program only supports 16-bit or 32-bit float audio streams. Sorry!");
    964 
    965 		closeAudio();
    966 		return false;
    967 	}
    968 
    969 	// test if the received audio stream is compatible
    970 
    971 	if (have.channels != 2)
    972 	{
    973 		if (showErrorMsg)
    974 			showErrorMsgBox("Couldn't open audio device:\nThis program only supports stereo audio streams. Sorry!");
    975 
    976 		closeAudio();
    977 		return false;
    978 	}
    979 
    980 	/*
    981 	if (have.freq != 44100 && have.freq != 48000 && have.freq != 96000)
    982 	{
    983 		if (showErrorMsg)
    984 			showErrorMsgBox("Couldn't open audio device:\nThis program doesn't support an audio output rate of %dHz. Sorry!", have.freq);
    985 
    986 		closeAudio();
    987 		return false;
    988 	}
    989 	*/
    990 
    991 	if (!setupAudioBuffers())
    992 	{
    993 		if (showErrorMsg)
    994 			showErrorMsgBox("Not enough memory!");
    995 
    996 		closeAudio();
    997 		return false;
    998 	}
    999 
   1000 	// set new bit depth flag
   1001 
   1002 	int8_t newBitDepth = 16;
   1003 	config.specialFlags &= ~BITDEPTH_32;
   1004 	config.specialFlags |=  BITDEPTH_16;
   1005 
   1006 	if (have.format == AUDIO_F32)
   1007 	{
   1008 		newBitDepth = 24;
   1009 		config.specialFlags &= ~BITDEPTH_16;
   1010 		config.specialFlags |=  BITDEPTH_32;
   1011 	}
   1012 
   1013 	audio.haveFreq = have.freq;
   1014 	audio.haveSamples = have.samples;
   1015 	config.audioFreq = audio.freq = have.freq;
   1016 
   1017 	calcAudioLatencyVars(have.samples, have.freq);
   1018 	smpShiftValue = (newBitDepth == 16) ? 2 : 3;
   1019 
   1020 	// make a copy of the new known working audio settings
   1021 
   1022 	audio.lastWorkingAudioFreq = config.audioFreq;
   1023 	audio.lastWorkingAudioBits = config.specialFlags & (BITDEPTH_16 + BITDEPTH_32 + BUFFSIZE_512 + BUFFSIZE_1024 + BUFFSIZE_2048);
   1024 	setLastWorkingAudioDevName();
   1025 
   1026 	// update config audio radio buttons if we're on that screen at the moment
   1027 	if (ui.configScreenShown && editor.currConfigScreen == CONFIG_SCREEN_AUDIO)
   1028 		showConfigScreen();
   1029 
   1030 	updateWavRendererSettings();
   1031 	setAudioAmp(config.boostLevel, config.masterVol, !!(config.specialFlags & BITDEPTH_32));
   1032 
   1033 	// don't call stopVoices() in this routine
   1034 	for (int32_t i = 0; i < MAX_CHANNELS; i++)
   1035 		stopVoice(i);
   1036 
   1037 	stopAllScopes();
   1038 
   1039 	// zero tick sample counter so that it will instantly initiate a tick
   1040 	audio.tickSampleCounterFrac  = audio.tickSampleCounter = 0;
   1041 
   1042 	calcReplayerVars(audio.freq);
   1043 
   1044 	if (song.BPM == 0)
   1045 		song.BPM = 125;
   1046 
   1047 	setMixerBPM(song.BPM); // this is important
   1048 
   1049 	audio.resetSyncTickTimeFlag = true;
   1050 
   1051 	setWavRenderFrequency(audio.freq);
   1052 	setWavRenderBitDepth((config.specialFlags & BITDEPTH_32) ? 32 : 16);
   1053 
   1054 	return true;
   1055 }
   1056 
   1057 void closeAudio(void)
   1058 {
   1059 	if (audio.dev > 0)
   1060 	{
   1061 		SDL_PauseAudioDevice(audio.dev, true);
   1062 		SDL_CloseAudioDevice(audio.dev);
   1063 		audio.dev = 0;
   1064 	}
   1065 
   1066 	freeAudioBuffers();
   1067 }