ref: df4476ab77a8e48cc15f8316a914e734f6ce4434
dir: /src/ft2_sampling.c/
// for finding memory leaks in debug mode with Visual Studio #if defined _DEBUG && defined _MSC_VER #include <crtdbg.h> #endif #include <stdint.h> #include <stdbool.h> #include "ft2_config.h" #include "ft2_gui.h" #include "ft2_mouse.h" #include "ft2_sample_ed.h" #include "ft2_video.h" #include "ft2_sampling.h" #include "ft2_structs.h" #define STEREO_SAMPLE_HEIGHT (SAMPLE_AREA_HEIGHT/2) #define SAMPLE_L_CENTER (SAMPLE_AREA_Y_CENTER - (STEREO_SAMPLE_HEIGHT/2)) #define SAMPLE_R_CENTER (SAMPLE_AREA_Y_CENTER + (STEREO_SAMPLE_HEIGHT/2)) #define MONO_SAMPLE_HEIGHT SAMPLE_AREA_HEIGHT #define SAMPLE_CENTER SAMPLE_AREA_Y_CENTER // this number may change when the audio input device is opened (must be 2^n) #define SAMPLING_BUFFER_SIZE 2048 // (must be 2^n) #if defined(_WIN32) || defined(__APPLE__) #define PREVIEW_SAMPLES 2048 #else #define PREVIEW_SAMPLES 8192 #endif static bool sampleInStereo; static volatile bool drawSamplingBufferFlag, outOfMemoryFlag, noMoreRoomFlag; static int16_t previewBufL[2][PREVIEW_SAMPLES], previewBufR[2][PREVIEW_SAMPLES]; static int32_t samplesSampled, samplingBufferSize, currPreviewBufNum, oldPreviewSmpPos, currSampleLen; static uint32_t samplingRate; static sample_t *smpL, *smpR; static SDL_AudioDeviceID recordDev; static void SDLCALL stereoSamplingCallback(void *userdata, Uint8 *stream, int len) { const int32_t samples = len >> 2; if (instr[editor.curInstr] == NULL || samples < 0 || samples > samplingBufferSize) return; if (!reallocateSmpData(smpL, smpL->length + samples, true) || !reallocateSmpData(smpR, smpR->length + samples, true)) { drawSamplingBufferFlag = false; outOfMemoryFlag = true; return; } const int16_t *src16 = (int16_t *)stream; int16_t *dst16_L = (int16_t *)smpL->dataPtr + smpL->length; int16_t *dst16_R = (int16_t *)smpR->dataPtr + smpR->length; for (int32_t i = 0; i < samples; i++) { dst16_L[i] = *src16++; dst16_R[i] = *src16++; } smpL->length += samples; smpR->length += samples; if (smpL->length > MAX_SAMPLE_LEN) // length overflow { smpL->length -= samples; smpR->length -= samples; noMoreRoomFlag = true; return; } currSampleLen = smpL->length; // if we have gathared enough samples, fill the current display buffer samplesSampled += samples; if (samplesSampled >= PREVIEW_SAMPLES) { samplesSampled -= PREVIEW_SAMPLES; if (oldPreviewSmpPos > 0) { int16_t *dstL = previewBufL[currPreviewBufNum^1]; int16_t *dstR = previewBufR[currPreviewBufNum^1]; const int16_t *srcL = (int16_t *)smpL->dataPtr + oldPreviewSmpPos; const int16_t *srcR = (int16_t *)smpR->dataPtr + oldPreviewSmpPos; memcpy(dstL, srcL, PREVIEW_SAMPLES * sizeof (int16_t)); memcpy(dstR, srcR, PREVIEW_SAMPLES * sizeof (int16_t)); drawSamplingBufferFlag = true; } oldPreviewSmpPos = smpL->length - PREVIEW_SAMPLES; } (void)userdata; } static void SDLCALL monoSamplingCallback(void *userdata, Uint8 *stream, int len) { const int32_t samples = len >> 1; if (instr[editor.curInstr] == NULL || samples < 0 || samples > samplingBufferSize) return; if (!reallocateSmpData(smpL, smpL->length + samples, true)) { drawSamplingBufferFlag = false; outOfMemoryFlag = true; return; } const int16_t *src16 = (int16_t *)stream; int16_t *dst16 = (int16_t *)smpL->dataPtr + smpL->length; memcpy(dst16, src16, samples * sizeof (int16_t)); smpL->length += samples; if (smpL->length > MAX_SAMPLE_LEN) // length overflow { smpL->length -= samples; noMoreRoomFlag = true; return; } // if we have gathared enough samples, fill the current display buffer samplesSampled += samples; if (samplesSampled >= PREVIEW_SAMPLES) { samplesSampled -= PREVIEW_SAMPLES; if (oldPreviewSmpPos > 0) { int16_t *dst = previewBufL[currPreviewBufNum^1]; const int16_t *src = (int16_t *)smpL->dataPtr + oldPreviewSmpPos; memcpy(dst, src, PREVIEW_SAMPLES * sizeof (int16_t)); drawSamplingBufferFlag = true; } oldPreviewSmpPos = smpL->length - PREVIEW_SAMPLES; } currSampleLen = smpL->length; (void)userdata; } void stopSampling(void) { resumeAudio(); mouseAnimOff(); SDL_CloseAudioDevice(recordDev); if (smpL != NULL) fixSample(smpL); if (smpR != NULL) fixSample(smpR); editor.samplingAudioFlag = false; updateSampleEditorSample(); editor.updateCurInstr = true; } static void getMinMax16(const int16_t *p, uint32_t position, uint32_t scanLen, int16_t *min16, int16_t *max16) { int16_t minVal = 32767; int16_t maxVal = -32768; assert(position+scanLen <= PREVIEW_SAMPLES); const int16_t *ptr16 = (const int16_t *)p + position; for (uint32_t i = 0; i < scanLen; i++) { const int16_t smp16 = ptr16[i]; if (smp16 < minVal) minVal = smp16; if (smp16 > maxVal) maxVal = smp16; } *min16 = minVal; *max16 = maxVal; } static int32_t scr2BufPos(int32_t x) { const double dXScaleMul = PREVIEW_SAMPLES / (double)SAMPLE_AREA_WIDTH; return (int32_t)(x * dXScaleMul); } static void drawSamplingPreview(void) { int16_t min, max; // clear sample data area memset(&video.frameBuffer[174 * SCREEN_W], 0, SAMPLE_AREA_WIDTH * SAMPLE_AREA_HEIGHT * sizeof (int32_t)); if (sampleInStereo) // stereo sampling { const int16_t *smpDataL = previewBufL[currPreviewBufNum]; const int16_t *smpDataR = previewBufR[currPreviewBufNum]; int32_t oldMinL = SAMPLE_L_CENTER; int32_t oldMaxL = SAMPLE_L_CENTER; int32_t oldMinR = SAMPLE_R_CENTER; int32_t oldMaxR = SAMPLE_R_CENTER; hLine(0, SAMPLE_L_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line hLine(0, SAMPLE_R_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++) { int32_t smpIdx = scr2BufPos(x+0); int32_t smpNum = scr2BufPos(x+1) - smpIdx; if (smpIdx+smpNum > PREVIEW_SAMPLES) smpNum = PREVIEW_SAMPLES-smpIdx; if (smpNum > 0) { // left channel getMinMax16(smpDataL, smpIdx, smpNum, &min, &max); min = SAMPLE_L_CENTER - ((min * STEREO_SAMPLE_HEIGHT) >> 16); max = SAMPLE_L_CENTER - ((max * STEREO_SAMPLE_HEIGHT) >> 16); if (x != 0) { if (min > oldMaxL) sampleLine(x-1, x, oldMaxL, min); if (max < oldMinL) sampleLine(x-1, x, oldMinL, max); } sampleLine(x, x, max, min); oldMinL = min; oldMaxL = max; // right channel getMinMax16(smpDataR, smpIdx, smpNum, &min, &max); min = SAMPLE_R_CENTER - ((min * STEREO_SAMPLE_HEIGHT) >> 16); max = SAMPLE_R_CENTER - ((max * STEREO_SAMPLE_HEIGHT) >> 16); if (x != 0) { if (min > oldMaxR) sampleLine(x-1, x, oldMaxR, min); if (max < oldMinR) sampleLine(x-1, x, oldMinR, max); } sampleLine(x, x, max, min); oldMinR = min; oldMaxR = max; } } } else // mono sampling { const int16_t *smpData = previewBufL[currPreviewBufNum]; int32_t oldMin = SAMPLE_CENTER; int32_t oldMax = SAMPLE_CENTER; hLine(0, SAMPLE_CENTER, SAMPLE_AREA_WIDTH, PAL_DESKTOP); // draw center line for (int16_t x = 0; x < SAMPLE_AREA_WIDTH; x++) { int32_t smpIdx = scr2BufPos(x+0); int32_t smpNum = scr2BufPos(x+1) - smpIdx; if (smpIdx+smpNum > PREVIEW_SAMPLES) smpNum = PREVIEW_SAMPLES-smpIdx; if (smpNum > 0) { getMinMax16(smpData, smpIdx, smpNum, &min, &max); min = SAMPLE_CENTER - ((min * MONO_SAMPLE_HEIGHT) >> 16); max = SAMPLE_CENTER - ((max * MONO_SAMPLE_HEIGHT) >> 16); if (x != 0) { if (min > oldMax) sampleLine(x-1, x, oldMax, min); if (max < oldMin) sampleLine(x-1, x, oldMin, max); } sampleLine(x, x, max, min); oldMin = min; oldMax = max; } } } } void handleSamplingUpdates(void) { if (outOfMemoryFlag) { outOfMemoryFlag = false; stopSampling(); okBox(0, "System message", "Not enough memory!"); return; } if (noMoreRoomFlag) { noMoreRoomFlag = false; stopSampling(); okBox(0, "System message", "Not more room in sample!"); return; } if (drawSamplingBufferFlag) { drawSamplingBufferFlag = false; drawSamplingPreview(); currPreviewBufNum ^= 1; } // clear and draw new sample length number fillRect(536, 362, 56, 10, PAL_DESKTOP); hexOut(536, 362, PAL_FORGRND, currSampleLen, 8); } void startSampling(void) { #if SDL_MAJOR_VERSION == 2 && SDL_MINOR_VERSION == 0 && SDL_PATCHLEVEL < 5 okBox(0, "System message", "This program needs to be compiled with SDL 2.0.5 or later to support audio sampling."); return; #else SDL_AudioSpec want, have; if (editor.samplingAudioFlag || editor.curInstr == 0) return; int16_t result = okBox(9, "System request", "Stereo sampling will use the next sample slot. While ongoing, press any key to stop."); if (result == 0 || result == 3) return; sampleInStereo = (result == 2); mouseAnimOn(); switch (config.audioInputFreq) { case INPUT_FREQ_44KHZ: samplingRate = 44100; break; default: case INPUT_FREQ_48KHZ: samplingRate = 48000; break; case INPUT_FREQ_96KHZ: samplingRate = 96000; break; } memset(&want, 0, sizeof (SDL_AudioSpec)); want.freq = samplingRate; want.format = AUDIO_S16; want.channels = sampleInStereo ? 2 : 1; want.callback = sampleInStereo ? stereoSamplingCallback : monoSamplingCallback; want.samples = SAMPLING_BUFFER_SIZE; recordDev = SDL_OpenAudioDevice(audio.currInputDevice, true, &want, &have, 0); if (recordDev == 0) { okBox(0, "System message", "Couldn't open the input device! Try adjusting the input rate at the config screen."); return; } samplingRate = have.freq; samplingBufferSize = have.samples; pauseAudio(); if (instr[editor.curInstr] == NULL && !allocateInstr(editor.curInstr)) { stopSampling(); okBox(0, "System message", "Not enough memory!"); return; } if (sampleInStereo && editor.curSmp+1 >= MAX_SMP_PER_INST) { stopSampling(); okBox(0, "System message", "Error: No free sample slot for the right channel!"); return; } smpL = &instr[editor.curInstr]->smp[editor.curSmp]; freeSample(editor.curInstr, editor.curSmp); // also sets pan to 128 and vol to 64 tuneSample(smpL, samplingRate, audio.linearPeriodsFlag); smpL->flags |= SAMPLE_16BIT; if (sampleInStereo) { smpR = &instr[editor.curInstr]->smp[editor.curSmp+1]; freeSample(editor.curInstr, editor.curSmp+1); // also sets pan to 128 and vol to 64 tuneSample(smpR, samplingRate, audio.linearPeriodsFlag); smpR->flags |= SAMPLE_16BIT; strcpy(smpL->name, "Left sample"); smpL->panning = 0; strcpy(smpR->name, "Right sample"); smpR->panning = 255; } else { strcpy(smpL->name, "Mono sample"); } memset(previewBufL, 0, sizeof (previewBufL)); memset(previewBufR, 0, sizeof (previewBufR)); currPreviewBufNum = 0; samplesSampled = currSampleLen = oldPreviewSmpPos = 0; noMoreRoomFlag = outOfMemoryFlag = drawSamplingBufferFlag = false; updateSampleEditorSample(); updateSampleEditor(); setSongModifiedFlag(); editor.samplingAudioFlag = true; SDL_PauseAudioDevice(recordDev, false); #endif }