/* Experimental audio sampling support.** There may be several bad practices here, as I don't really** have the proper knowledge on this stuff.**** Some functions like sin() may be different depending on** math library implementation, but we don't use pt2_math.c** replacements for speed reasons.*/ // for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER#include <crtdbg.h>#endif #include <stdio.h>#include <stdint.h>#include <stdbool.h>#include "pt2_textout.h"#include "pt2_sampler.h"#include "pt2_visuals.h"#include "pt2_helpers.h"#include "pt2_bmp.h"#include "pt2_audio.h"#include "pt2_tables.h"#include "pt2_config.h"#include "pt2_sampling.h"#include "pt2_math.h" // PT2_PI#include "pt2_replayer.h" enum{ SAMPLE_LEFT = 0, SAMPLE_RIGHT = 1, SAMPLE_MIX = 2}; // this may change after opening the audio input device#define SAMPLING_BUFFER_SIZE 1024 // after several tests, these values yields a good trade-off between quality and compute time#define SINC_TAPS 64#define SINC_TAPS_BITS 6 /* log2(SINC_TAPS) */#define SINC_PHASES 4096#define MID_TAP ((SINC_TAPS/2)*SINC_PHASES) #define SAMPLE_PREVIEW_WITDH 194#define SAMPLE_PREVIEW_HEIGHT 38#define MAX_INPUT_DEVICES 99#define VISIBLE_LIST_ENTRIES 4 static volatile bool callbackBusy, displayingBuffer, samplingEnded;static bool audioDevOpen;static char *audioInputDevs[MAX_INPUT_DEVICES];static uint8_t samplingNote = 33, samplingFinetune = 4; // period 124, max safe period for PAL Paulastatic int16_t displayBuffer[SAMPLING_BUFFER_SIZE];static int32_t samplingMode = SAMPLE_MIX, inputFrequency, roundedOutputFrequency;static int32_t numAudioInputDevs, audioInputDevListOffset, selectedDev;static int32_t bytesSampled, maxSamplingLength, inputBufferSize;static double dOutputFrequency, *dSincTable, *dKaiserTable, *dSamplingBuffer, *dSamplingBufferOrig;static SDL_AudioDeviceID recordDev; /*** ----------------------------------------------------------------------------------** Sinc code taken from the OpenMPT project (has a similar BSD license), and modified** ----------------------------------------------------------------------------------*/ static double Izero(double y) // Compute Bessel function Izero(y) using a series approximation{ double s = 1.0, ds = 1.0, d = 0.0; const double epsilon = 1E-9; // 8bb: 1E-7 -> 1E-9 for added precision (still fast to calculate) do { d = d + 2.0; ds = ds * (y * y) / (d * d); s = s + ds; } while (ds > epsilon * s); return s;} bool initKaiserTable(void) // called once on tracker init{ dKaiserTable = (double *)malloc(SINC_TAPS * SINC_PHASES * sizeof (double)); if (dKaiserTable == NULL) { showErrorMsgBox("Out of memory!"); return false; } const double beta = 9.6377; const double izeroBeta = Izero(beta); for (int32_t i = 0; i < SINC_TAPS*SINC_PHASES; i++) { double fkaiser; int32_t ix = (SINC_TAPS-1) - (i & (SINC_TAPS-1)); ix = (ix * SINC_PHASES) + (i >> SINC_TAPS_BITS); if (ix == MID_TAP) { fkaiser = 1.0; } else { const double x = (ix - MID_TAP) * (1.0 / SINC_PHASES); const double xMul = 1.0 / ((SINC_TAPS/2) * (SINC_TAPS/2)); fkaiser = Izero(beta * sqrt(1.0 - x * x * xMul)) / izeroBeta; } dKaiserTable[i] = fkaiser; } return true;} void freeKaiserTable(void){ if (dKaiserTable != NULL) { free(dKaiserTable); dKaiserTable = NULL; }} // calculated after completion of sampling (before downsampling)static bool initSincTable(double cutoff){ dSincTable = (double *)malloc(SINC_TAPS * SINC_PHASES * sizeof (double)); if (dSincTable == NULL) return false; if (cutoff > 1.0) cutoff = 1.0; const double kPi = PT2_PI * cutoff; for (int32_t i = 0; i < SINC_TAPS*SINC_PHASES; i++) { double fsinc; int32_t ix = (SINC_TAPS-1) - (i & (SINC_TAPS-1)); ix = (ix * SINC_PHASES) + (i >> SINC_TAPS_BITS); if (ix == MID_TAP) { fsinc = 1.0; } else { const double x = (ix - MID_TAP) * (1.0 / SINC_PHASES); const double xPi = x * kPi; fsinc = (sin(xPi) / xPi) * dKaiserTable[i]; } dSincTable[i] = fsinc * cutoff; } return true;} static void freeSincTable(void){ if (dSincTable != NULL) { free(dSincTable); dSincTable = NULL; }} static double sinc(const double *dSmpData, const double dPhase){ const int32_t phase = (int32_t)(dPhase * SINC_PHASES); const double *dSincLUT = &dSincTable[phase << SINC_TAPS_BITS]; double dSmp = 0.0; for (int32_t i = 0; i < SINC_TAPS; i++) dSmp += dSmpData[i] * dSincLUT[i]; return dSmp;} /*** ----------------------------------------------------------------------------------** ----------------------------------------------------------------------------------*/ static void listAudioDevices(void); static void updateOutputFrequency(void){ if (samplingNote > 35) samplingNote = 35; int32_t period = periodTable[((samplingFinetune & 0xF) * 37) + samplingNote]; if (period < 113) // also happens in our "set period" Paula function period = 113; dOutputFrequency = (double)PAULA_PAL_CLK / period; roundedOutputFrequency = (int32_t)(dOutputFrequency + 0.5);} static void SDLCALL samplingCallback(void *userdata, Uint8 *stream, int len){ callbackBusy = true; USED(callbackBusy); if (!displayingBuffer) { if (len > SAMPLING_BUFFER_SIZE) len = SAMPLING_BUFFER_SIZE; const int16_t *L = (int16_t *)stream; const int16_t *R = ((int16_t *)stream) + 1; int16_t *dst16 = displayBuffer; if (samplingMode == SAMPLE_LEFT) { for (int32_t i = 0; i < len; i++) dst16[i] = L[i << 1]; } else if (samplingMode == SAMPLE_RIGHT) { for (int32_t i = 0; i < len; i++) dst16[i] = R[i << 1]; } else { for (int32_t i = 0; i < len; i++) dst16[i] = (L[i << 1] + R[i << 1]) >> 1; } } if (audio.isSampling) { if (bytesSampled+len > maxSamplingLength) len = maxSamplingLength - bytesSampled; if (len > inputBufferSize) len = inputBufferSize; const int16_t *L = (int16_t *)stream; const int16_t *R = ((int16_t *)stream) + 1; double *dSmp = &dSamplingBuffer[bytesSampled]; if (samplingMode == SAMPLE_LEFT) { for (int32_t i = 0; i < len; i++) dSmp[i] = L[i << 1] * (1.0 / 32768.0); } else if (samplingMode == SAMPLE_RIGHT) { for (int32_t i = 0; i < len; i++) dSmp[i] = R[i << 1] * (1.0 / 32768.0); } else { for (int32_t i = 0; i < len; i++) dSmp[i] = (L[i << 1] + R[i << 1]) * (1.0 / (32768.0 * 2.0)); } bytesSampled += len; if (bytesSampled >= maxSamplingLength) { audio.isSampling = true; samplingEnded = true; } } callbackBusy = false; USED(userdata);} static void stopInputAudio(void){ if (recordDev > 0) { SDL_CloseAudioDevice(recordDev); recordDev = 0; } callbackBusy = false;} static void startInputAudio(void){ SDL_AudioSpec want, have; if (recordDev > 0) stopInputAudio(); if (numAudioInputDevs == 0 || selectedDev >= numAudioInputDevs) { audioDevOpen = false; return; } assert(roundedOutputFrequency > 0); memset(&want, 0, sizeof (SDL_AudioSpec)); want.freq = config.audioInputFrequency; want.format = AUDIO_S16; want.channels = 2; want.callback = samplingCallback; want.userdata = NULL; want.samples = SAMPLING_BUFFER_SIZE; recordDev = SDL_OpenAudioDevice(audioInputDevs[selectedDev], true, &want, &have, 0); audioDevOpen = (recordDev != 0); inputFrequency = have.freq; inputBufferSize = have.samples; SDL_PauseAudioDevice(recordDev, false);} static void selectAudioDevice(int32_t dev){ if (dev < 0) return; if (numAudioInputDevs == 0) { listAudioDevices(); return; } if (dev >= numAudioInputDevs) return; listAudioDevices(); stopInputAudio(); selectedDev = dev; listAudioDevices(); startInputAudio(); changeStatusText(ui.statusMessage);} void renderSampleMonitor(void){ blit32(120, 44, 200, 55, sampleMonitorBMP); memset(displayBuffer, 0, sizeof (displayBuffer));} void freeAudioDeviceList(void){ for (int32_t i = 0; i < numAudioInputDevs; i++) { if (audioInputDevs[i] != NULL) { free(audioInputDevs[i]); audioInputDevs[i] = NULL; } }} static void scanAudioDevices(void){ freeAudioDeviceList(); numAudioInputDevs = SDL_GetNumAudioDevices(true); if (numAudioInputDevs > MAX_INPUT_DEVICES) numAudioInputDevs = MAX_INPUT_DEVICES; for (int32_t i = 0; i < numAudioInputDevs; i++) { const char *deviceName = SDL_GetAudioDeviceName(i, true); if (deviceName == NULL) { numAudioInputDevs--; // hide device continue; } const uint32_t stringLen = (uint32_t)strlen(deviceName); audioInputDevs[i] = (char *)malloc(stringLen + 2); if (audioInputDevs[i] == NULL) break; if (stringLen > 0) strcpy(audioInputDevs[i], deviceName); audioInputDevs[i][stringLen+1] = '\0'; // UTF-8 needs double null termination (XXX: citation needed) } audioInputDevListOffset = 0; // reset scroll position if (selectedDev >= numAudioInputDevs) selectedDev = 0;} static void listAudioDevices(void){ fillRect(3, 219, 163, 33, PAL_BACKGRD); if (numAudioInputDevs == 0) { textOut(16, 219+13, "NO DEVICES FOUND!", video.palette[PAL_QADSCP]); return; } for (int32_t i = 0; i < VISIBLE_LIST_ENTRIES; i++) { const int32_t dev = audioInputDevListOffset+i; if (audioInputDevListOffset+i >= numAudioInputDevs) break; if (dev == selectedDev) fillRect(4, 219+1+(i*(FONT_CHAR_H+3)), 161, 8, video.palette[PAL_GENBKG2]); if (audioInputDevs[dev] != NULL) textOutTightN(2+2, 219+2+(i*(FONT_CHAR_H+3)), audioInputDevs[dev], 23, video.palette[PAL_QADSCP]); }} static void drawSamplingNote(void){ assert(samplingNote < 36); const char *str = config.accidental ? noteNames2[2+samplingNote]: noteNames1[2+samplingNote]; textOutBg(262, 230, str, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);} static void drawSamplingFinetune(void){ textOutBg(254, 219, ftuneStrTab[samplingFinetune & 0xF], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]);} static void drawSamplingFrequency(void){ char str[16]; sprintf(str, "%05dHZ", roundedOutputFrequency); const int32_t maxSafeFrequency = (int32_t)(PAL_PAULA_MAX_SAFE_HZ + 0.5); // rounded textOutBg(262, 208, str, roundedOutputFrequency <= maxSafeFrequency ? video.palette[PAL_GENTXT] : 0x8C0F0F, video.palette[PAL_GENBKG]);} static void drawSamplingModeCross(void){ // clear old crosses fillRect(4, 208, 6, 5, video.palette[PAL_GENBKG]); fillRect(51, 208, 6, 5, video.palette[PAL_GENBKG]); fillRect(105, 208, 6, 5, video.palette[PAL_GENBKG]); int16_t x; if (samplingMode == SAMPLE_LEFT) x = 3; else if (samplingMode == SAMPLE_RIGHT) x = 50; else x = 104; charOut(x, 208, 'X', video.palette[PAL_GENTXT]);} static void showCurrSample(void){ updateCurrSample(); // reset sampler screen attributes sampler.loopStartPos = 0; sampler.loopEndPos = 0; editor.markStartOfs = -1; editor.markEndOfs = -1; editor.samplePos = 0; hideSprite(SPRITE_LOOP_PIN_LEFT); hideSprite(SPRITE_LOOP_PIN_RIGHT); renderSampleData();} void renderSamplingBox(void){ changeStatusText("PLEASE WAIT ..."); flipFrame(); hpc_ResetCounters(&video.vblankHpc); editor.sampleZero = false; editor.blockMarkFlag = false; // remove all open screens (except sampler) if (ui.diskOpScreenShown || ui.posEdScreenShown || ui.editOpScreenShown) { ui.diskOpScreenShown = false; ui.posEdScreenShown = false; ui.editOpScreenShown = false; displayMainScreen(); } setStatusMessage("ALL RIGHT", DO_CARRY); blit32(0, 203, 320, 52, samplingBoxBMP); updateOutputFrequency(); drawSamplingNote(); drawSamplingFinetune(); drawSamplingFrequency();Lines 1–500 of 1022. Load more
/* Experimental audio sampling support. ** There may be several bad practices here, as I don't really ** have the proper knowledge on this stuff. ** ** Some functions like sin() may be different depending on ** math library implementation, but we don't use pt2_math.c ** replacements for speed reasons. */ // for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdio.h> #include <stdint.h> #include <stdbool.h> #include "pt2_textout.h" #include "pt2_sampler.h" #include "pt2_visuals.h" #include "pt2_helpers.h" #include "pt2_bmp.h" #include "pt2_audio.h" #include "pt2_tables.h" #include "pt2_config.h" #include "pt2_sampling.h" #include "pt2_math.h" // PT2_PI #include "pt2_replayer.h" enum { SAMPLE_LEFT = 0, SAMPLE_RIGHT = 1, SAMPLE_MIX = 2 }; // this may change after opening the audio input device #define SAMPLING_BUFFER_SIZE 1024 // after several tests, these values yields a good trade-off between quality and compute time #define SINC_TAPS 64 #define SINC_TAPS_BITS 6 /* log2(SINC_TAPS) */ #define SINC_PHASES 4096 #define MID_TAP ((SINC_TAPS/2)*SINC_PHASES) #define SAMPLE_PREVIEW_WITDH 194 #define SAMPLE_PREVIEW_HEIGHT 38 #define MAX_INPUT_DEVICES 99 #define VISIBLE_LIST_ENTRIES 4 static volatile bool callbackBusy, displayingBuffer, samplingEnded; static bool audioDevOpen; static char *audioInputDevs[MAX_INPUT_DEVICES]; static uint8_t samplingNote = 33, samplingFinetune = 4; // period 124, max safe period for PAL Paula static int16_t displayBuffer[SAMPLING_BUFFER_SIZE]; static int32_t samplingMode = SAMPLE_MIX, inputFrequency, roundedOutputFrequency; static int32_t numAudioInputDevs, audioInputDevListOffset, selectedDev; static int32_t bytesSampled, maxSamplingLength, inputBufferSize; static double dOutputFrequency, *dSincTable, *dKaiserTable, *dSamplingBuffer, *dSamplingBufferOrig; static SDL_AudioDeviceID recordDev; /* ** ---------------------------------------------------------------------------------- ** Sinc code taken from the OpenMPT project (has a similar BSD license), and modified ** ---------------------------------------------------------------------------------- */ static double Izero(double y) // Compute Bessel function Izero(y) using a series approximation { double s = 1.0, ds = 1.0, d = 0.0; const double epsilon = 1E-9; // 8bb: 1E-7 -> 1E-9 for added precision (still fast to calculate) do { d = d + 2.0; ds = ds * (y * y) / (d * d); s = s + ds; } while (ds > epsilon * s); return s; } bool initKaiserTable(void) // called once on tracker init { dKaiserTable = (double *)malloc(SINC_TAPS * SINC_PHASES * sizeof (double)); if (dKaiserTable == NULL) { showErrorMsgBox("Out of memory!"); return false; } const double beta = 9.6377; const double izeroBeta = Izero(beta); for (int32_t i = 0; i < SINC_TAPS*SINC_PHASES; i++) { double fkaiser; int32_t ix = (SINC_TAPS-1) - (i & (SINC_TAPS-1)); ix = (ix * SINC_PHASES) + (i >> SINC_TAPS_BITS); if (ix == MID_TAP) { fkaiser = 1.0; } else { const double x = (ix - MID_TAP) * (1.0 / SINC_PHASES); const double xMul = 1.0 / ((SINC_TAPS/2) * (SINC_TAPS/2)); fkaiser = Izero(beta * sqrt(1.0 - x * x * xMul)) / izeroBeta; } dKaiserTable[i] = fkaiser; } return true; } void freeKaiserTable(void) { if (dKaiserTable != NULL) { free(dKaiserTable); dKaiserTable = NULL; } } // calculated after completion of sampling (before downsampling) static bool initSincTable(double cutoff) { dSincTable = (double *)malloc(SINC_TAPS * SINC_PHASES * sizeof (double)); if (dSincTable == NULL) return false; if (cutoff > 1.0) cutoff = 1.0; const double kPi = PT2_PI * cutoff; for (int32_t i = 0; i < SINC_TAPS*SINC_PHASES; i++) { double fsinc; int32_t ix = (SINC_TAPS-1) - (i & (SINC_TAPS-1)); ix = (ix * SINC_PHASES) + (i >> SINC_TAPS_BITS); if (ix == MID_TAP) { fsinc = 1.0; } else { const double x = (ix - MID_TAP) * (1.0 / SINC_PHASES); const double xPi = x * kPi; fsinc = (sin(xPi) / xPi) * dKaiserTable[i]; } dSincTable[i] = fsinc * cutoff; } return true; } static void freeSincTable(void) { if (dSincTable != NULL) { free(dSincTable); dSincTable = NULL; } } static double sinc(const double *dSmpData, const double dPhase) { const int32_t phase = (int32_t)(dPhase * SINC_PHASES); const double *dSincLUT = &dSincTable[phase << SINC_TAPS_BITS]; double dSmp = 0.0; for (int32_t i = 0; i < SINC_TAPS; i++) dSmp += dSmpData[i] * dSincLUT[i]; return dSmp; } /* ** ---------------------------------------------------------------------------------- ** ---------------------------------------------------------------------------------- */ static void listAudioDevices(void); static void updateOutputFrequency(void) { if (samplingNote > 35) samplingNote = 35; int32_t period = periodTable[((samplingFinetune & 0xF) * 37) + samplingNote]; if (period < 113) // also happens in our "set period" Paula function period = 113; dOutputFrequency = (double)PAULA_PAL_CLK / period; roundedOutputFrequency = (int32_t)(dOutputFrequency + 0.5); } static void SDLCALL samplingCallback(void *userdata, Uint8 *stream, int len) { callbackBusy = true; USED(callbackBusy); if (!displayingBuffer) { if (len > SAMPLING_BUFFER_SIZE) len = SAMPLING_BUFFER_SIZE; const int16_t *L = (int16_t *)stream; const int16_t *R = ((int16_t *)stream) + 1; int16_t *dst16 = displayBuffer; if (samplingMode == SAMPLE_LEFT) { for (int32_t i = 0; i < len; i++) dst16[i] = L[i << 1]; } else if (samplingMode == SAMPLE_RIGHT) { for (int32_t i = 0; i < len; i++) dst16[i] = R[i << 1]; } else { for (int32_t i = 0; i < len; i++) dst16[i] = (L[i << 1] + R[i << 1]) >> 1; } } if (audio.isSampling) { if (bytesSampled+len > maxSamplingLength) len = maxSamplingLength - bytesSampled; if (len > inputBufferSize) len = inputBufferSize; const int16_t *L = (int16_t *)stream; const int16_t *R = ((int16_t *)stream) + 1; double *dSmp = &dSamplingBuffer[bytesSampled]; if (samplingMode == SAMPLE_LEFT) { for (int32_t i = 0; i < len; i++) dSmp[i] = L[i << 1] * (1.0 / 32768.0); } else if (samplingMode == SAMPLE_RIGHT) { for (int32_t i = 0; i < len; i++) dSmp[i] = R[i << 1] * (1.0 / 32768.0); } else { for (int32_t i = 0; i < len; i++) dSmp[i] = (L[i << 1] + R[i << 1]) * (1.0 / (32768.0 * 2.0)); } bytesSampled += len; if (bytesSampled >= maxSamplingLength) { audio.isSampling = true; samplingEnded = true; } } callbackBusy = false; USED(userdata); } static void stopInputAudio(void) { if (recordDev > 0) { SDL_CloseAudioDevice(recordDev); recordDev = 0; } callbackBusy = false; } static void startInputAudio(void) { SDL_AudioSpec want, have; if (recordDev > 0) stopInputAudio(); if (numAudioInputDevs == 0 || selectedDev >= numAudioInputDevs) { audioDevOpen = false; return; } assert(roundedOutputFrequency > 0); memset(&want, 0, sizeof (SDL_AudioSpec)); want.freq = config.audioInputFrequency; want.format = AUDIO_S16; want.channels = 2; want.callback = samplingCallback; want.userdata = NULL; want.samples = SAMPLING_BUFFER_SIZE; recordDev = SDL_OpenAudioDevice(audioInputDevs[selectedDev], true, &want, &have, 0); audioDevOpen = (recordDev != 0); inputFrequency = have.freq; inputBufferSize = have.samples; SDL_PauseAudioDevice(recordDev, false); } static void selectAudioDevice(int32_t dev) { if (dev < 0) return; if (numAudioInputDevs == 0) { listAudioDevices(); return; } if (dev >= numAudioInputDevs) return; listAudioDevices(); stopInputAudio(); selectedDev = dev; listAudioDevices(); startInputAudio(); changeStatusText(ui.statusMessage); } void renderSampleMonitor(void) { blit32(120, 44, 200, 55, sampleMonitorBMP); memset(displayBuffer, 0, sizeof (displayBuffer)); } void freeAudioDeviceList(void) { for (int32_t i = 0; i < numAudioInputDevs; i++) { if (audioInputDevs[i] != NULL) { free(audioInputDevs[i]); audioInputDevs[i] = NULL; } } } static void scanAudioDevices(void) { freeAudioDeviceList(); numAudioInputDevs = SDL_GetNumAudioDevices(true); if (numAudioInputDevs > MAX_INPUT_DEVICES) numAudioInputDevs = MAX_INPUT_DEVICES; for (int32_t i = 0; i < numAudioInputDevs; i++) { const char *deviceName = SDL_GetAudioDeviceName(i, true); if (deviceName == NULL) { numAudioInputDevs--; // hide device continue; } const uint32_t stringLen = (uint32_t)strlen(deviceName); audioInputDevs[i] = (char *)malloc(stringLen + 2); if (audioInputDevs[i] == NULL) break; if (stringLen > 0) strcpy(audioInputDevs[i], deviceName); audioInputDevs[i][stringLen+1] = '\0'; // UTF-8 needs double null termination (XXX: citation needed) } audioInputDevListOffset = 0; // reset scroll position if (selectedDev >= numAudioInputDevs) selectedDev = 0; } static void listAudioDevices(void) { fillRect(3, 219, 163, 33, PAL_BACKGRD); if (numAudioInputDevs == 0) { textOut(16, 219+13, "NO DEVICES FOUND!", video.palette[PAL_QADSCP]); return; } for (int32_t i = 0; i < VISIBLE_LIST_ENTRIES; i++) { const int32_t dev = audioInputDevListOffset+i; if (audioInputDevListOffset+i >= numAudioInputDevs) break; if (dev == selectedDev) fillRect(4, 219+1+(i*(FONT_CHAR_H+3)), 161, 8, video.palette[PAL_GENBKG2]); if (audioInputDevs[dev] != NULL) textOutTightN(2+2, 219+2+(i*(FONT_CHAR_H+3)), audioInputDevs[dev], 23, video.palette[PAL_QADSCP]); } } static void drawSamplingNote(void) { assert(samplingNote < 36); const char *str = config.accidental ? noteNames2[2+samplingNote]: noteNames1[2+samplingNote]; textOutBg(262, 230, str, video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]); } static void drawSamplingFinetune(void) { textOutBg(254, 219, ftuneStrTab[samplingFinetune & 0xF], video.palette[PAL_GENTXT], video.palette[PAL_GENBKG]); } static void drawSamplingFrequency(void) { char str[16]; sprintf(str, "%05dHZ", roundedOutputFrequency); const int32_t maxSafeFrequency = (int32_t)(PAL_PAULA_MAX_SAFE_HZ + 0.5); // rounded textOutBg(262, 208, str, roundedOutputFrequency <= maxSafeFrequency ? video.palette[PAL_GENTXT] : 0x8C0F0F, video.palette[PAL_GENBKG]); } static void drawSamplingModeCross(void) { // clear old crosses fillRect(4, 208, 6, 5, video.palette[PAL_GENBKG]); fillRect(51, 208, 6, 5, video.palette[PAL_GENBKG]); fillRect(105, 208, 6, 5, video.palette[PAL_GENBKG]); int16_t x; if (samplingMode == SAMPLE_LEFT) x = 3; else if (samplingMode == SAMPLE_RIGHT) x = 50; else x = 104; charOut(x, 208, 'X', video.palette[PAL_GENTXT]); } static void showCurrSample(void) { updateCurrSample(); // reset sampler screen attributes sampler.loopStartPos = 0; sampler.loopEndPos = 0; editor.markStartOfs = -1; editor.markEndOfs = -1; editor.samplePos = 0; hideSprite(SPRITE_LOOP_PIN_LEFT); hideSprite(SPRITE_LOOP_PIN_RIGHT); renderSampleData(); } void renderSamplingBox(void) { changeStatusText("PLEASE WAIT ..."); flipFrame(); hpc_ResetCounters(&video.vblankHpc); editor.sampleZero = false; editor.blockMarkFlag = false; // remove all open screens (except sampler) if (ui.diskOpScreenShown || ui.posEdScreenShown || ui.editOpScreenShown) { ui.diskOpScreenShown = false; ui.posEdScreenShown = false; ui.editOpScreenShown = false; displayMainScreen(); } setStatusMessage("ALL RIGHT", DO_CARRY); blit32(0, 203, 320, 52, samplingBoxBMP); updateOutputFrequency(); drawSamplingNote(); drawSamplingFinetune(); drawSamplingFrequency();