/* Copyright (c) 2021 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ // sfxr source code license: /* Copyright (c) 2007 Tomas Pettersson Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // bfxr source code ported from ActionScript to C by Alex Diener. Original copyright notice from SfxrSynth.as in the bfxr project: /* SfxrSynth Copyright 2010 Thomas Vian Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #include "audiosynth/AudioMath.h" #include "audiosynth/sfxrSynth.h" #include "utilities/IOUtilities.h" #include #include #include #include #include #include #define rnd(n) (rand()%(n+1)) #define PI 3.14159265f float frnd(float range) { return (float)rnd(10000)/10000*range; } void sfxr_ResetParams(struct sfxrParams * params) { params->wave_type = 0; params->p_base_freq = 0.3f; params->p_freq_limit = 0.0f; params->p_freq_ramp = 0.0f; params->p_freq_dramp = 0.0f; params->p_duty = 0.0f; params->p_duty_ramp = 0.0f; params->p_vib_strength = 0.0f; params->p_vib_speed = 0.0f; params->p_vib_delay = 0.0f; params->p_env_attack = 0.0f; params->p_env_sustain = 0.3f; params->p_env_decay = 0.4f; params->p_env_punch = 0.0f; params->p_lpf_resonance = 0.0f; params->p_lpf_freq = 1.0f; params->p_lpf_ramp = 0.0f; params->p_hpf_freq = 0.0f; params->p_hpf_ramp = 0.0f; params->p_pha_offset = 0.0f; params->p_pha_ramp = 0.0f; params->p_repeat_speed = 0.0f; params->p_arp_speed = 0.0f; params->p_arp_mod = 0.0f; params->master_vol = 0.05f; params->sound_vol = 0.5f; params->p_compression = 0.0f; params->p_harmonics = 0.0f; params->p_harmonics_falloff = 0.0f; params->p_arp_repeat = 0.0f; params->p_arp_speed_2 = 0.0f; params->p_arp_mod_2 = 0.0f; params->p_bitcrush = 0.0f; params->p_bitcrush_ramp = 0.0f; } void sfxr_ResetSample(struct sfxrSynthState * state, struct sfxrParams params, bool restart) { if (!restart) { state->phase = 0; } state->fperiod = 100.0 / (params.p_base_freq * params.p_base_freq + 0.001); state->period = (int) state->fperiod; state->fmaxperiod = 100.0 / (params.p_freq_limit * params.p_freq_limit + 0.001); state->fslide = 1.0 - pow((double) params.p_freq_ramp, 3.0) * 0.01; state->fdslide = -pow((double) params.p_freq_dramp, 3.0) * 0.000001; state->square_duty = 0.5f - params.p_duty * 0.5f; state->square_slide = -params.p_duty_ramp * 0.00005f; if (params.p_arp_mod >= 0.0f) { state->arp_mod= 1.0 - pow((double) params.p_arp_mod, 2.0) * 0.9; } else { state->arp_mod = 1.0 + pow((double) params.p_arp_mod, 2.0) * 10.0; } state->arp_time = 0; state->arp_limit = (int) (powf(1.0f - params.p_arp_speed, 2.0f) * 20000 + 32); if (params.p_arp_speed == 1.0f) { state->arp_limit = 0; } if (!restart) { // reset filter state->fltp = 0.0f; state->fltdp = 0.0f; state->fltw = powf(params.p_lpf_freq, 3.0f) * 0.1f; state->fltw_d = 1.0f + params.p_lpf_ramp * 0.0001f; state->fltdmp = 5.0f / (1.0f + powf(params.p_lpf_resonance, 2.0f) * 20.0f) * (0.01f + state->fltw); if (state->fltdmp > 0.8f) { state->fltdmp = 0.8f; } state->fltphp = 0.0f; state->flthp = powf(params.p_hpf_freq, 2.0f) * 0.1f; state->flthp_d = 1.0f + params.p_hpf_ramp * 0.0003f; // reset vibrato state->vib_phase = 0.0f; state->vib_speed = powf(params.p_vib_speed, 2.0f) * 0.01f; state->vib_amp = params.p_vib_strength * 0.5f; // reset envelope state->env_vol = 0.0f; state->env_stage = 0; state->env_time = 0; state->env_length[0] = (int) (params.p_env_attack * params.p_env_attack * 100000.0f); if (state->env_length[0] == 0) { state->env_length[0] = 1; } state->env_length[1] = (int) (params.p_env_sustain * params.p_env_sustain * 100000.0f); if (state->env_length[1] == 0) { state->env_length[1] = 1; } state->env_length[2] = (int) (params.p_env_decay * params.p_env_decay * 100000.0f); if (state->env_length[2] == 0) { state->env_length[2] = 1; } state->fphase = powf(params.p_pha_offset, 2.0f) * 1020.0f; if (params.p_pha_offset < 0.0f) { state->fphase = -state->fphase; } state->fdphase = powf(params.p_pha_ramp, 2.0f) * 1.0f; if (params.p_pha_ramp < 0.0f) { state->fdphase = -state->fdphase; } state->iphase = abs((int) state->fphase); state->ipp = 0; for (int i = 0; i < 1024; i++) { state->phaser_buffer[i] = 0.0f; } for (int i = 0; i < 32; i++) { state->noise_buffer[i] = frnd(2.0f) - 1.0f; } state->rep_time = 0; state->rep_limit = (int) (powf(1.0f - params.p_repeat_speed, 2.0f) * 20000 + 32); if (params.p_repeat_speed == 0.0f) { state->rep_limit = 0; } } } void sfxr_SynthSample(struct sfxrSynthState * state, struct sfxrParams params, int length, float* buffer) { struct sfxrSynthState stateLocal = *state; for (int i = 0; i < length; i++) { if (!stateLocal.playing_sample) { break; } stateLocal.rep_time++; if (stateLocal.rep_limit != 0 && stateLocal.rep_time >= stateLocal.rep_limit) { stateLocal.rep_time = 0; sfxr_ResetSample(&stateLocal, params, true); } // frequency envelopes/arpeggios stateLocal.arp_time++; if (stateLocal.arp_limit != 0 && stateLocal.arp_time >= stateLocal.arp_limit) { stateLocal.arp_limit = 0; stateLocal.fperiod *= stateLocal.arp_mod; } stateLocal.fslide += stateLocal.fdslide; stateLocal.fperiod *= stateLocal.fslide; if (stateLocal.fperiod > stateLocal.fmaxperiod) { stateLocal.fperiod = stateLocal.fmaxperiod; if (params.p_freq_limit > 0.0f) { stateLocal.playing_sample = false; } } float rfperiod = stateLocal.fperiod; if (stateLocal.vib_amp > 0.0f) { stateLocal.vib_phase += stateLocal.vib_speed; rfperiod = stateLocal.fperiod * ( 1.0 + sin(stateLocal.vib_phase) * stateLocal.vib_amp); } stateLocal.period = (int) rfperiod; if (stateLocal.period < 8) { stateLocal.period = 8; } stateLocal.square_duty += stateLocal.square_slide; if (stateLocal.square_duty < 0.0f) { stateLocal.square_duty = 0.0f; } if (stateLocal.square_duty > 0.5f) { stateLocal.square_duty = 0.5f; } // volume envelope stateLocal.env_time++; if (stateLocal.env_time > stateLocal.env_length[stateLocal.env_stage]) { stateLocal.env_time = 0; stateLocal.env_stage++; if (stateLocal.env_stage == 3) { stateLocal.playing_sample = false; } } switch (stateLocal.env_stage) { case 0: stateLocal.env_vol = (float) stateLocal.env_time / stateLocal.env_length[0]; break; case 1: stateLocal.env_vol = 1.0f + pow(1.0f - (float) stateLocal.env_time / stateLocal.env_length[1], 1.0f) * 2.0f * params.p_env_punch; break; case 2: stateLocal.env_vol = 1.0f - (float) stateLocal.env_time / stateLocal.env_length[2]; break; } // phaser step stateLocal.fphase += stateLocal.fdphase; stateLocal.iphase = abs((int) stateLocal.fphase); if (stateLocal.iphase > 1023) { stateLocal.iphase = 1023; } if (stateLocal.flthp_d != 0.0f) { stateLocal.flthp *= stateLocal.flthp_d; if (stateLocal.flthp < 0.00001f) { stateLocal.flthp = 0.00001f; } if (stateLocal.flthp > 0.1f) { stateLocal.flthp = 0.1f; } } float ssample = 0.0f; for (int si = 0; si < 8; si++) { // 8x supersampling float sample = 0.0f; stateLocal.phase++; if (stateLocal.phase >= stateLocal.period) { stateLocal.phase %= stateLocal.period; if (params.wave_type == 3) { for (int i = 0; i < 32; i++) { stateLocal.noise_buffer[i] = frnd(2.0f) - 1.0f; } } } // base waveform float fp = (float) stateLocal.phase / stateLocal.period; switch (params.wave_type) { case 0: // square if (fp < stateLocal.square_duty) { sample = 0.5f; } else { sample = -0.5f; } break; case 1: // sawtooth sample = 1.0f - fp * 2; break; case 2: // sine sample = sinf(fp * 2 * PI); break; case 3: // noise sample = stateLocal.noise_buffer[stateLocal.phase * 32 / stateLocal.period]; break; case 4: // triangle (flawed implementation that actually produces a different kind of wave) if (fp <= 0.25f) { sample = 4.0f * fp; } else if (fp <= 0.5f) { sample = 1.0f - 4.0f * (fp - 0.25f); } else if (fp >= 0.75f) { sample = 4.0f * (fp - 1.0f); } break; } // lp filter float pp = stateLocal.fltp; stateLocal.fltw *= stateLocal.fltw_d; if (stateLocal.fltw < 0.0f) { stateLocal.fltw = 0.0f; } if (stateLocal.fltw > 0.1f) { stateLocal.fltw = 0.1f; } if (params.p_lpf_freq != 1.0f) { stateLocal.fltdp += (sample - stateLocal.fltp) * stateLocal.fltw; stateLocal.fltdp -= stateLocal.fltdp * stateLocal.fltdmp; } else { stateLocal.fltp = sample; stateLocal.fltdp = 0.0f; } stateLocal.fltp += stateLocal.fltdp; // hp filter stateLocal.fltphp += stateLocal.fltp - pp; stateLocal.fltphp -= stateLocal.fltphp * stateLocal.flthp; sample = stateLocal.fltphp; // phaser stateLocal.phaser_buffer[stateLocal.ipp & 1023] = sample; sample += stateLocal.phaser_buffer[(stateLocal.ipp - stateLocal.iphase + 1024) & 1023]; stateLocal.ipp = (stateLocal.ipp + 1) & 1023; // final accumulation and envelope application ssample += sample * stateLocal.env_vol; } ssample = ssample / 8 * params.master_vol; ssample *= 2.0f * params.sound_vol; if (buffer != NULL) { if (ssample > 1.0f) { ssample = 1.0f; } if (ssample <- 1.0f) { ssample = -1.0f; } *buffer++ = ssample; } } *state = stateLocal; } static float bfxrRandom(pcg_state * pcgState) { return pcg32_ufrand(pcgState, 1.0f); } static struct bfxrPinkNumberState bfxr_PinkNumber_Init(pcg_state * pcgState) { struct bfxrPinkNumberState pinkNumber; pinkNumber.pcgState = pcgState; pinkNumber.max_key = 0x1f; // Five bits set pinkNumber.range = 128; pinkNumber.key = 0; for (int i = 0; i < 5; i++) pinkNumber.white_values[i] = bfxrRandom(pcgState) * (pinkNumber.range/5); return pinkNumber; } static double bfxr_PinkNumber_GetNextValue(struct bfxrPinkNumberState * state) { int last_key = state->key; unsigned int sum; state->key++; if (state->key > state->max_key) state->key = 0; // Exclusive-Or previous value with current value. This gives // a list of bits that have changed. int diff = last_key ^ state->key; sum = 0; for (int i = 0; i < 5; i++) { // If bit changed get new random number for corresponding // white_value if (diff & (1 << i)) state->white_values[i] = bfxrRandom(state->pcgState) * (state->range/5); sum += state->white_values[i]; } return sum/64.0-1.0; } /* wave_type "waveType" sound_vol "masterVolume" p_env_attack "attackTime" p_env_sustain "sustainTime" p_env_punch "sustainPunch" p_env_decay "decayTime" p_compression "compressionAmount" p_base_freq "startFrequency" p_freq_limit "minFrequency" p_freq_ramp "slide" p_freq_dramp "deltaSlide" p_vib_strength "vibratoDepth" p_vib_speed "vibratoSpeed" p_harmonics "overtones" p_harmonics_falloff "overtoneFalloff" p_arp_repeat "changeRepeat" p_arp_mod "changeAmount" p_arp_speed "changeSpeed" p_arp_mod_2 "changeAmount2" p_arp_speed_2 "changeSpeed2" p_duty "squareDuty" p_duty_ramp "dutySweep" p_repeat_speed "repeatSpeed" p_pha_offset "flangerOffset" p_pha_ramp "flangerSweep" p_lpf_ramp "lpFilterCutoffSweep" p_lpf_freq "lpFilterCutoff" p_lpf_resonance "lpFilterResonance" p_hpf_freq "hpFilterCutoff" p_hpf_ramp "hpFilterCutoffSweep" p_bitcrush "bitCrush" p_bitcrush_ramp "bitCrushSweep" */ #define MIN_LENGTH 0.18 //#define LoResNoisePeriod 8 void bfxr_reset(struct bfxrSynthState * ioState, struct sfxrParams params, bool totalReset) { pcg32_seed(&ioState->pcgState, 0, 0); ioState->period = 100.0 / (params.p_base_freq * params.p_base_freq + 0.001); ioState->maxPeriod = 100.0 / (params.p_freq_limit * params.p_freq_limit + 0.001); ioState->slide = 1.0 - params.p_freq_ramp * params.p_freq_ramp * params.p_freq_ramp * 0.01; ioState->deltaSlide = -params.p_freq_dramp * params.p_freq_dramp * params.p_freq_dramp * 0.000001; if (params.wave_type == 0) { ioState->squareDuty = 0.5 - params.p_duty * 0.5; ioState->dutySweep = -params.p_duty_ramp * 0.00005; } ioState->changePeriod = (((1-params.p_arp_repeat)+0.1)/1.1) * 20000 + 32; ioState->changePeriodTime = 0; if (params.p_arp_mod > 0.0) ioState->changeAmount = 1.0 - params.p_arp_mod * params.p_arp_mod * 0.9; else ioState->changeAmount = 1.0 + params.p_arp_mod * params.p_arp_mod * 10.0; ioState->changeTime = 0; ioState->changeReached=false; if(params.p_arp_speed == 1.0) ioState->changeLimit = 0; else ioState->changeLimit = (1.0 - params.p_arp_speed) * (1.0 - params.p_arp_speed) * 20000 + 32; if (params.p_arp_mod_2 > 0.0) ioState->changeAmount2 = 1.0 - params.p_arp_mod_2 * params.p_arp_mod_2 * 0.9; else ioState->changeAmount2 = 1.0 + params.p_arp_mod_2 * params.p_arp_mod_2 * 10.0; ioState->changeTime2 = 0; ioState->changeReached2=false; if(params.p_arp_speed_2 == 1.0) ioState->changeLimit2 = 0; else ioState->changeLimit2 = (1.0 - params.p_arp_speed_2) * (1.0 - params.p_arp_speed_2) * 20000 + 32; ioState->changeLimit*=(1-params.p_arp_repeat+0.1)/1.1; ioState->changeLimit2*=(1-params.p_arp_repeat+0.1)/1.1; if(totalReset) { ioState->masterVolume = params.sound_vol * params.sound_vol; ioState->waveType = params.wave_type; if (params.p_env_sustain < 0.01) params.p_env_sustain = 0.01; double totalTime = params.p_env_attack + params.p_env_sustain + params.p_env_decay; if (totalTime < MIN_LENGTH ) { double multiplier = MIN_LENGTH / totalTime; params.p_env_attack *= multiplier; params.p_env_sustain *= multiplier; params.p_env_decay *= multiplier; } ioState->sustainPunch = params.p_env_punch; ioState->phase = 0; ioState->minFreqency = params.p_freq_limit; ioState->muted=false; ioState->overtones = params.p_harmonics*10; ioState->overtoneFalloff = params.p_harmonics_falloff; ioState->bitcrush_freq = 1 - powf(params.p_bitcrush,1.0/3.0); ioState->bitcrush_freq_sweep = -params.p_bitcrush_ramp* 0.000015; ioState->bitcrush_phase=0; ioState->bitcrush_last=0; ioState->compression_factor = 1/(1+4*params.p_compression); ioState->filters = params.p_lpf_freq != 1.0 || params.p_hpf_freq != 0.0; ioState->lpFilterPos = 0.0; ioState->lpFilterDeltaPos = 0.0; ioState->lpFilterCutoff = params.p_lpf_freq * params.p_lpf_freq * params.p_lpf_freq * 0.1; ioState->lpFilterDeltaCutoff = 1.0 + params.p_lpf_ramp * 0.0001; ioState->lpFilterDamping = 5.0 / (1.0 + params.p_lpf_resonance * params.p_lpf_resonance * 20.0) * (0.01 + ioState->lpFilterCutoff); if (ioState->lpFilterDamping > 0.8) ioState->lpFilterDamping = 0.8; ioState->lpFilterDamping = 1.0 - ioState->lpFilterDamping; ioState->lpFilterOn = params.p_lpf_freq != 1.0; ioState->hpFilterPos = 0.0; ioState->hpFilterCutoff = params.p_hpf_freq * params.p_hpf_freq * 0.1; ioState->hpFilterDeltaCutoff = 1.0 + params.p_hpf_ramp * 0.0003; ioState->vibratoPhase = 0.0; ioState->vibratoSpeed = params.p_vib_speed * params.p_vib_speed * 0.01; ioState->vibratoAmplitude = params.p_vib_strength * 0.5; ioState->envelopeVolume = 0.0; ioState->envelopeStage = 0; ioState->envelopeTime = 0; ioState->envelopeLength0 = params.p_env_attack * params.p_env_attack * 100000.0; ioState->envelopeLength1 = params.p_env_sustain * params.p_env_sustain * 100000.0; ioState->envelopeLength2 = params.p_env_decay * params.p_env_decay * 100000.0 + 10; ioState->envelopeLength = ioState->envelopeLength0; ioState->envelopeOverLength0 = 1.0 / ioState->envelopeLength0; ioState->envelopeOverLength1 = 1.0 / ioState->envelopeLength1; ioState->envelopeOverLength2 = 1.0 / ioState->envelopeLength2; ioState->flanger = params.p_pha_offset != 0.0 || params.p_pha_ramp != 0.0; ioState->flangerOffset = params.p_pha_offset * params.p_pha_offset * 1020.0; if(params.p_pha_offset < 0.0) ioState->flangerOffset = -ioState->flangerOffset; ioState->flangerDeltaOffset = params.p_pha_ramp * params.p_pha_ramp * params.p_pha_ramp * 0.2; ioState->flangerPos = 0; ioState->pinkNumber = bfxr_PinkNumber_Init(&ioState->pcgState); ioState->oneBitNoiseState = 1 << 14; ioState->oneBitNoise = 0; ioState->buzzState = 1 << 14; ioState->buzz = 0; for(unsigned int i = 0; i < 1024; i++) ioState->flangerBuffer[i] = 0.0; for(unsigned int i = 0; i < 32; i++) ioState->noiseBuffer[i] = bfxrRandom(&ioState->pcgState) * 2.0 - 1.0; for(unsigned int i = 0; i < 32; i++) ioState->pinkNoiseBuffer[i] = bfxr_PinkNumber_GetNextValue(&ioState->pinkNumber); //for(unsigned int i = 0; i < 32; i++) ioState->loResNoiseBuffer[i] = ((i%LoResNoisePeriod)==0) ? bfxrRandom(&ioState->pcgState)*2.0-1.0 : ioState->loResNoiseBuffer[i-1]; ioState->repeatTime = 0; if (params.p_repeat_speed == 0.0) ioState->repeatLimit = 0; else ioState->repeatLimit = (int) ((1.0-params.p_repeat_speed) * (1.0-params.p_repeat_speed) * 20000) + 32; } if (ioState->waveType==9 || ioState->waveType==11){ double sf = params.p_base_freq; double mf = params.p_freq_limit; double startFrequency_min = 0.0f; double startFrequency_max = 1.0f; double startFrequency_mid = (startFrequency_max+startFrequency_min)/2; double minFrequency_min = 0.0f; double minFrequency_max = 1.0f; double minFrequency_mid = (minFrequency_max+minFrequency_min)/2; double delta_start = (sf-startFrequency_min)/(startFrequency_max-startFrequency_min); double delta_min = (mf-minFrequency_min)/(minFrequency_max-minFrequency_min); sf = startFrequency_mid+delta_start/2; mf = minFrequency_mid+delta_min/2; ioState->period = 100.0 / (sf*sf + 0.001); ioState->maxPeriod = 100.0 / (mf*mf + 0.001); } } bool bfxr_synthWave(struct bfxrSynthState * ioState, struct sfxrParams params, unsigned int length, float * buffer) { struct bfxrSynthState state = *ioState; state.finished = false; for(unsigned int i = 0; i < length; i++) { if (state.finished) { *ioState = state; return true; } // Repeats every _repeatLimit times, partially resetting the sound parameters if(state.repeatLimit != 0) { if(++state.repeatTime >= state.repeatLimit) { state.repeatTime = 0; bfxr_reset(&state, params, false); } } state.changePeriodTime++; if (state.changePeriodTime>=state.changePeriod) { state.changeTime=0; state.changeTime2=0; state.changePeriodTime=0; if (state.changeReached) { state.period /= state.changeAmount; state.changeReached=false; } if (state.changeReached2) { state.period /= state.changeAmount2; state.changeReached2=false; } } // If _changeLimit is reached, shifts the pitch if(!state.changeReached) { if(++state.changeTime >= state.changeLimit) { state.changeReached = true; state.period *= state.changeAmount; } } // If _changeLimit is reached, shifts the pitch if(!state.changeReached2) { if(++state.changeTime2 >= state.changeLimit2) { state.period *= state.changeAmount2; state.changeReached2=true; } } // Acccelerate and apply slide state.slide += state.deltaSlide; state.period *= state.slide; // Checks for frequency getting too low, and stops the sound if a minFrequency was set if(state.period > state.maxPeriod) { state.period = state.maxPeriod; if(state.minFreqency > 0.0) { state.muted = true; } } state.periodTemp = state.period; // Applies the vibrato effect if(state.vibratoAmplitude > 0.0) { state.vibratoPhase += state.vibratoSpeed; state.periodTemp = state.period * (1.0 + sinf(state.vibratoPhase) * state.vibratoAmplitude); } state.periodTemp = (int) state.periodTemp; if(state.periodTemp < 8) state.periodTemp = 8; // Sweeps the square duty if (state.waveType == 0) { state.squareDuty += state.dutySweep; if(state.squareDuty < 0.0) state.squareDuty = 0.0; else if (state.squareDuty > 0.5) state.squareDuty = 0.5; } // Moves through the different stages of the volume envelope if(++state.envelopeTime > state.envelopeLength) { state.envelopeTime = 0; switch(++state.envelopeStage) { case 1: state.envelopeLength = state.envelopeLength1; break; case 2: state.envelopeLength = state.envelopeLength2; break; } } // Sets the volume based on the position in the envelope switch(state.envelopeStage) { case 0: state.envelopeVolume = state.envelopeTime * state.envelopeOverLength0; break; case 1: state.envelopeVolume = 1.0 + (1.0 - state.envelopeTime * state.envelopeOverLength1) * 2.0 * state.sustainPunch; break; case 2: state.envelopeVolume = 1.0 - state.envelopeTime * state.envelopeOverLength2; break; case 3: state.envelopeVolume = 0.0; state.finished = true; break; } // Moves the flanger offset if (state.flanger) { state.flangerOffset += state.flangerDeltaOffset; state.flangerInt = (int) state.flangerOffset; if(state.flangerInt < 0) state.flangerInt = -state.flangerInt; else if (state.flangerInt > 1023) state.flangerInt = 1023; } // Moves the high-pass filter cutoff if(state.filters && state.hpFilterDeltaCutoff != 0.0) { state.hpFilterCutoff *= state.hpFilterDeltaCutoff; if(state.hpFilterCutoff < 0.00001) state.hpFilterCutoff = 0.00001; else if(state.hpFilterCutoff > 0.1) state.hpFilterCutoff = 0.1; } float superSample = 0.0; for(int j = 0; j < 8; j++) { // Cycles through the period state.phase++; if(state.phase >= state.periodTemp) { state.phase = state.phase - state.periodTemp; // Generates new random noise for this period if(state.waveType == 3) { for(unsigned int n = 0; n < 32; n++) state.noiseBuffer[n] = bfxrRandom(&ioState->pcgState) * 2.0 - 1.0; } else if (state.waveType == 5) { for(unsigned int n = 0; n < 32; n++) state.pinkNoiseBuffer[n] = bfxr_PinkNumber_GetNextValue(&state.pinkNumber); } //else if (state.waveType == 6) //{ // Vestigial code; waveType 6 is tan. loResNoiseBuffer is never accessed. //for(unsigned int n = 0; n < 32; n++) state.loResNoiseBuffer[n] = ((n%LoResNoisePeriod)==0) ? bfxrRandom(&ioState->pcgState)*2.0-1.0 : state.loResNoiseBuffer[n-1]; //} else if (state.waveType == 9) { // Bitnoise // Based on SN76489 periodic "white" noise // http://www.smspower.org/Development/SN76489?sid=ae16503f2fb18070f3f40f2af56807f1#NoiseChannel // This one matches the behaviour of the SN76489 in the BBC Micro. int feedBit = (state.oneBitNoiseState >> 1 & 1) ^ (state.oneBitNoiseState & 1); state.oneBitNoiseState = state.oneBitNoiseState >> 1 | (feedBit << 14); state.oneBitNoise = (~state.oneBitNoiseState & 1) - 0.5; } else if (state.waveType == 11) { // Based on SN76489 periodic "white" noise // http://www.smspower.org/Development/SN76489?sid=ae16503f2fb18070f3f40f2af56807f1#NoiseChannel // This one doesn't match the behaviour of anything real, but it made a nice sound, so I kept it. int fb = (state.buzzState >> 3 & 1) ^ (state.buzzState & 1); state.buzzState = state.buzzState >> 1 | (fb << 14); state.buzz = (~state.buzzState & 1) - 0.5; } } double sample=0; double overtonestrength=1; for (int k=0;k<=state.overtones;k++) { double tempphase = fmodf((state.phase*(k+1)), state.periodTemp); // Gets the sample from the oscillator unsigned int wtype = state.waveType; if (wtype==10){ wtype = ((int) state.phase/4) %10; } switch(wtype) { case 0: // Square wave { sample += overtonestrength*(((tempphase / state.periodTemp) < state.squareDuty) ? 0.5 : -0.5); break; } case 1: // Saw wave { sample += overtonestrength*(1.0 - (tempphase / state.periodTemp) * 2.0); break; } case 2: // Sine wave (fast and accurate approx) { state.pos = tempphase / state.periodTemp; state.pos = state.pos > 0.5 ? (state.pos - 1.0) * 6.28318531 : state.pos * 6.28318531; double tempsample = state.pos < 0 ? 1.27323954 * state.pos + .405284735 * state.pos * state.pos : 1.27323954 * state.pos - 0.405284735 * state.pos * state.pos; sample += overtonestrength*(tempsample < 0 ? .225 * (tempsample *-tempsample - tempsample) + tempsample : .225 * (tempsample * tempsample - tempsample) + tempsample); break; } case 3: // Noise { sample += overtonestrength*(state.noiseBuffer[(unsigned int) (tempphase * 32 / (int) state.periodTemp)%32]); break; } case 4: // Triangle Wave { sample += overtonestrength*(fabs(1-(tempphase / state.periodTemp)*2)-1); break; } case 5: // Pink Noise { sample += overtonestrength*(state.pinkNoiseBuffer[(unsigned int) (tempphase * 32 / (int) state.periodTemp)%32]); break; } case 6: // tan { //detuned sample += tanf(M_PI*tempphase/state.periodTemp)*overtonestrength; break; } case 7: // Whistle { // Sin wave code state.pos = tempphase / state.periodTemp; state.pos = state.pos > 0.5 ? (state.pos - 1.0) * 6.28318531 : state.pos * 6.28318531; double tempsample = state.pos < 0 ? 1.27323954 * state.pos + .405284735 * state.pos * state.pos : 1.27323954 * state.pos - 0.405284735 * state.pos * state.pos; double value = 0.75*(tempsample < 0 ? .225 * (tempsample *-tempsample - tempsample) + tempsample : .225 * (tempsample * tempsample - tempsample) + tempsample); //then whistle (essentially an overtone with frequencyx20 and amplitude0.25 state.pos = fmodf((tempphase*20), state.periodTemp) / state.periodTemp; state.pos = state.pos > 0.5 ? (state.pos - 1.0) * 6.28318531 : state.pos * 6.28318531; tempsample = state.pos < 0 ? 1.27323954 * state.pos + .405284735 * state.pos * state.pos : 1.27323954 * state.pos - 0.405284735 * state.pos * state.pos; value += 0.25*(tempsample < 0 ? .225 * (tempsample *-tempsample - tempsample) + tempsample : .225 * (tempsample * tempsample - tempsample) + tempsample); sample += overtonestrength*value;//main wave break; } case 8: // Breaker { double amp = tempphase/state.periodTemp; sample += overtonestrength*(fabs(1-amp*amp*2)-1); break; } case 9: // Bitnoise (1-bit periodic "white" noise) { sample += overtonestrength*state.oneBitNoise; break; } case 11: //new2 { sample += overtonestrength*state.buzz; break; } } overtonestrength*=(1-state.overtoneFalloff); } // Applies the low and high pass filters if (state.filters) { state.lpFilterOldPos = state.lpFilterPos; state.lpFilterCutoff *= state.lpFilterDeltaCutoff; if(state.lpFilterCutoff < 0.0) state.lpFilterCutoff = 0.0; else if(state.lpFilterCutoff > 0.1) state.lpFilterCutoff = 0.1; if(state.lpFilterOn) { state.lpFilterDeltaPos += (sample - state.lpFilterPos) * state.lpFilterCutoff; state.lpFilterDeltaPos *= state.lpFilterDamping; } else { state.lpFilterPos = sample; state.lpFilterDeltaPos = 0.0; } state.lpFilterPos += state.lpFilterDeltaPos; state.hpFilterPos += state.lpFilterPos - state.lpFilterOldPos; state.hpFilterPos *= 1.0 - state.hpFilterCutoff; sample = state.hpFilterPos; } // Applies the flanger effect if (state.flanger) { state.flangerBuffer[state.flangerPos&1023] = sample; sample += state.flangerBuffer[(state.flangerPos - state.flangerInt + 1024) & 1023]; state.flangerPos = (state.flangerPos + 1) & 1023; } superSample += sample; } // Clipping if too loud if(superSample > 8.0) superSample = 8.0; else if(superSample < -8.0) superSample = -8.0; // Averages out the super samples and applies volumes superSample = state.masterVolume * state.envelopeVolume * superSample * 0.125; //BIT CRUSH state.bitcrush_phase+=state.bitcrush_freq; if (state.bitcrush_phase>1) { state.bitcrush_phase=0; state.bitcrush_last=superSample; } state.bitcrush_freq = fmax(fmin(state.bitcrush_freq+state.bitcrush_freq_sweep,1),0); superSample=state.bitcrush_last; //compressor if (superSample>0) { superSample = pow(superSample,state.compression_factor); } else { superSample = -pow(-superSample,state.compression_factor); } if (state.muted) { superSample = 0; } if (buffer != NULL) { *buffer++ = superSample; } } *ioState = state; return false; } #define skipDelimiter(delimiter) \ if (filePosition >= fileLength || fileContents[filePosition] != delimiter) { \ return false; \ } \ filePosition++; #define readInt(variable) { \ size_t startIndex = filePosition; \ while (filePosition < fileLength && \ ((fileContents[filePosition] >= '0' && fileContents[filePosition] <= '9') || \ fileContents[filePosition] == '-')) { \ filePosition++; \ } \ if (filePosition == startIndex) { \ variable = 0; \ } else { \ variable = atoi((const char *) fileContents + startIndex); \ } \ } #define readFloat(variable) { \ size_t startIndex = filePosition; \ while (filePosition < fileLength && \ ((fileContents[filePosition] >= '0' && fileContents[filePosition] <= '9') || \ fileContents[filePosition] == '.' || \ fileContents[filePosition] == '-' || \ fileContents[filePosition] == 'N' || \ fileContents[filePosition] == 'a')) { \ filePosition++; \ } \ if (filePosition == startIndex) { \ variable = 0.0f; \ } else { \ variable = atof((const char *) fileContents + startIndex); \ } \ } #define readNextFloat(delimiter, variable) \ skipDelimiter(delimiter); \ readFloat(variable); #define readBoolean(variable) { \ size_t startIndex = filePosition; \ while (filePosition < fileLength && \ fileContents[filePosition] >= 'a' && fileContents[filePosition] <= 'z') { \ filePosition++; \ } \ variable = (filePosition - startIndex == 4 && !memcmp(fileContents + startIndex, "true", filePosition - startIndex)); \ } #define readNextBoolean(delimiter, variable) \ skipDelimiter(delimiter); \ readBoolean(variable); #define skipString(string) {\ size_t stringLength = strlen(string); \ for (size_t charIndex = 0; charIndex < stringLength; charIndex++) { \ skipDelimiter(string[charIndex]); \ } \ } static bool readBFXRSynthData(unsigned char * fileContents, size_t fileLength, size_t * ioFilePosition, struct sfxrParams * outParams) { size_t filePosition = *ioFilePosition; float waveTypeFloat; readFloat(waveTypeFloat); if (isnan(waveTypeFloat)) { outParams->wave_type = 0; } else { outParams->wave_type = roundf(waveTypeFloat); } readNextFloat(',', outParams->sound_vol); readNextFloat(',', outParams->p_env_attack); readNextFloat(',', outParams->p_env_sustain); readNextFloat(',', outParams->p_env_punch); readNextFloat(',', outParams->p_env_decay); readNextFloat(',', outParams->p_compression); readNextFloat(',', outParams->p_base_freq); readNextFloat(',', outParams->p_freq_limit); readNextFloat(',', outParams->p_freq_ramp); readNextFloat(',', outParams->p_freq_dramp); readNextFloat(',', outParams->p_vib_strength); readNextFloat(',', outParams->p_vib_speed); readNextFloat(',', outParams->p_harmonics); readNextFloat(',', outParams->p_harmonics_falloff); readNextFloat(',', outParams->p_arp_repeat); readNextFloat(',', outParams->p_arp_mod); readNextFloat(',', outParams->p_arp_speed); readNextFloat(',', outParams->p_arp_mod_2); readNextFloat(',', outParams->p_arp_speed_2); readNextFloat(',', outParams->p_duty); readNextFloat(',', outParams->p_duty_ramp); readNextFloat(',', outParams->p_repeat_speed); readNextFloat(',', outParams->p_pha_offset); readNextFloat(',', outParams->p_pha_ramp); readNextFloat(',', outParams->p_lpf_freq); readNextFloat(',', outParams->p_lpf_ramp); readNextFloat(',', outParams->p_lpf_resonance); readNextFloat(',', outParams->p_hpf_freq); readNextFloat(',', outParams->p_hpf_ramp); readNextFloat(',', outParams->p_bitcrush); readNextFloat(',', outParams->p_bitcrush_ramp); skipDelimiter(','); skipString("masterVolume"); outParams->master_vol = 0.2f; *ioFilePosition = filePosition; return true; } static bool readBFXRTrackData(unsigned char * fileContents, size_t fileLength, size_t * ioFilePosition, struct bfxrMixerTrackData * outTrackData) { size_t filePosition = *ioFilePosition; readFloat(outTrackData->id); skipDelimiter('|'); sfxr_ResetParams(&outTrackData->synthData); if (fileContents[filePosition] != '|') { if (!readBFXRSynthData(fileContents, fileLength, &filePosition, &outTrackData->synthData)) { return false; } } readNextFloat('|', outTrackData->onset); readNextFloat('|', outTrackData->volume); readNextBoolean('|', outTrackData->reverse); *ioFilePosition = filePosition; return true; } bool readBFXRSoundFile(const char * filePath, struct bfxrTrackList * outTrackList) { FILE * file = fopen(filePath, "rb"); if (file == NULL) { #ifdef DEBUG fprintf(stderr, "Error: Couldn't open \"%s\" for reading (errno = %d)\n", filePath, errno); #endif return false; } fseek(file, 0, SEEK_END); size_t fileLength = ftell(file); fseek(file, 0, SEEK_SET); unsigned char * fileContents = malloc(fileLength + 1); fread(fileContents, 1, fileLength, file); fclose(file); fileContents[fileLength] = 0; if (fileLength < 2) { free(fileContents); return false; } size_t filePosition = 0; uint16_t length = fileContents[0] << 8 | fileContents[1]; if (fileLength == (size_t) length + 2) { filePosition = 2; } bool containsMixerData = false; for (unsigned int charIndex = filePosition; charIndex < fileLength; charIndex++) { if (fileContents[charIndex] == '|' || fileContents[charIndex] == '>') { containsMixerData = true; break; } } unsigned int trackCount = 0; struct bfxrMixerTrackData * tracks = NULL; if (containsMixerData) { readInt(outTrackList->id); if (filePosition >= fileLength || fileContents[filePosition] != '>') { free(fileContents); return false; } filePosition++; readFloat(outTrackList->volume); while (filePosition < fileLength) { if (fileContents[filePosition] != '>') { free(fileContents); free(tracks); return false; } filePosition++; struct bfxrMixerTrackData track; if (!readBFXRTrackData(fileContents, fileLength, &filePosition, &track)) { free(fileContents); free(tracks); return false; } tracks = realloc(tracks, (trackCount + 1) * sizeof(*tracks)); tracks[trackCount++] = track; } } else { struct bfxrMixerTrackData track; if (!readBFXRSynthData(fileContents, fileLength, &filePosition, &track.synthData)) { free(fileContents); return false; } track.id = 0; track.onset = 0.0f; track.volume = 1.0f; track.reverse = false; tracks = malloc(sizeof(*tracks)); *tracks = track; trackCount = 1; outTrackList->id = -1; outTrackList->volume = 1.0f; } free(fileContents); outTrackList->trackCount = trackCount; outTrackList->tracks = tracks; return true; } #define writeFloat(variable) { \ char formattedNumber[38]; \ unsigned int length = formatDecimalString(variable, formattedNumber, sizeof(formattedNumber), 0, DECIMAL_PRECISION_MAX); \ fwrite(formattedNumber, 1, length, file); \ } #define writeNextFloat(delimiter, variable) \ fputc(delimiter, file); \ writeFloat(variable); #define writeBoolean(variable) \ if (variable) { \ fwrite("true", 1, 4, file); \ } else { \ fwrite("false", 1, 5, file); \ } #define writeNextBoolean(delimiter, variable) \ fputc(delimiter, file); \ writeBoolean(variable); static void writeBFXRSynthData(FILE * file, struct sfxrParams * params) { writeFloat(params->wave_type); writeNextFloat(',', params->sound_vol); writeNextFloat(',', params->p_env_attack); writeNextFloat(',', params->p_env_sustain); writeNextFloat(',', params->p_env_punch); writeNextFloat(',', params->p_env_decay); writeNextFloat(',', params->p_compression); writeNextFloat(',', params->p_base_freq); writeNextFloat(',', params->p_freq_limit); writeNextFloat(',', params->p_freq_ramp); writeNextFloat(',', params->p_freq_dramp); writeNextFloat(',', params->p_vib_strength); writeNextFloat(',', params->p_vib_speed); writeNextFloat(',', params->p_harmonics); writeNextFloat(',', params->p_harmonics_falloff); writeNextFloat(',', params->p_arp_repeat); writeNextFloat(',', params->p_arp_mod); writeNextFloat(',', params->p_arp_speed); writeNextFloat(',', params->p_arp_mod_2); writeNextFloat(',', params->p_arp_speed_2); writeNextFloat(',', params->p_duty); writeNextFloat(',', params->p_duty_ramp); writeNextFloat(',', params->p_repeat_speed); writeNextFloat(',', params->p_pha_offset); writeNextFloat(',', params->p_pha_ramp); writeNextFloat(',', params->p_lpf_freq); writeNextFloat(',', params->p_lpf_ramp); writeNextFloat(',', params->p_lpf_resonance); writeNextFloat(',', params->p_hpf_freq); writeNextFloat(',', params->p_hpf_ramp); writeNextFloat(',', params->p_bitcrush); writeNextFloat(',', params->p_bitcrush_ramp); fwrite(",masterVolume", 1, 13, file); } static void writeBFXRTrackData(FILE * file, struct bfxrMixerTrackData * trackData) { writeFloat(trackData->id); fputc('|', file); writeBFXRSynthData(file, &trackData->synthData); writeNextFloat('|', trackData->onset); writeNextFloat('|', trackData->volume); writeNextBoolean('|', trackData->reverse); } bool writeBFXRSoundFile(const char * filePath, struct bfxrTrackList * trackList) { FILE * file = fopen(filePath, "wb"); if (file == NULL) { #ifdef DEBUG fprintf(stderr, "Error: Couldn't open \"%s\" for writing\n", filePath); #endif return false; } if (trackList->trackCount == 1) { writeBFXRSynthData(file, &trackList->tracks[0].synthData); } else { writeFloat(trackList->id); writeNextFloat('>', trackList->volume); for (unsigned int trackIndex = 0; trackIndex < trackList->trackCount; trackIndex++) { fputc('>', file); writeBFXRTrackData(file, &trackList->tracks[trackIndex]); } } fclose(file); return true; } bool readSfxrXSoundFile(const char * filePath, struct sfxrParams * outParams) { FILE * file = fopen(filePath, "rb"); if (file == NULL) { #ifdef DEBUG fprintf(stderr, "Error: Couldn't open \"%s\" for reading\n", filePath); #endif return false; } int32_t version = 0; fread(&version, 1, 4, file); if (version < 100 || version > 102) { #ifdef DEBUG fprintf(stderr, "Error: sfs file \"%s\" has unrecognized version number %d; only 100, 101, and 102 are valid\n", filePath, version); #endif fclose(file); return false; } sfxr_ResetParams(outParams); fread(&outParams->wave_type, 1, 4, file); outParams->sound_vol = 1.5f; if (version == 102) { fread(&outParams->sound_vol, 1, 4, file); } fread(&outParams->p_base_freq, 1, 4, file); fread(&outParams->p_freq_limit, 1, 4, file); fread(&outParams->p_freq_ramp, 1, 4, file); if (version >= 101) { fread(&outParams->p_freq_dramp, 1, 4, file); } fread(&outParams->p_duty, 1, 4, file); fread(&outParams->p_duty_ramp, 1, 4, file); fread(&outParams->p_vib_strength, 1, 4, file); fread(&outParams->p_vib_speed, 1, 4, file); fread(&outParams->p_vib_delay, 1, 4, file); fread(&outParams->p_env_attack, 1, 4, file); fread(&outParams->p_env_sustain, 1, 4, file); fread(&outParams->p_env_decay, 1, 4, file); fread(&outParams->p_env_punch, 1, 4, file); fseek(file, 4, SEEK_CUR); fread(&outParams->p_lpf_resonance, 1, 4, file); fread(&outParams->p_lpf_freq, 1, 4, file); fread(&outParams->p_lpf_ramp, 1, 4, file); fread(&outParams->p_hpf_freq, 1, 4, file); fread(&outParams->p_hpf_ramp, 1, 4, file); fread(&outParams->p_pha_offset, 1, 4, file); fread(&outParams->p_pha_ramp, 1, 4, file); fread(&outParams->p_repeat_speed, 1, 4, file); if (version >= 101) { fread(&outParams->p_arp_speed, 1, 4, file); fread(&outParams->p_arp_mod, 1, 4, file); } fclose(file); return true; } bool writeSfxrXSoundFile(const char * filePath, struct sfxrParams * params) { FILE * file = fopen(filePath, "wb"); if (file == NULL) { #ifdef DEBUG fprintf(stderr, "Error: Couldn't open \"%s\" for writing\n", filePath); #endif return false; } int32_t version = 102; fwrite(&version, 1, 4, file); fwrite(¶ms->wave_type, 1, 4, file); fwrite(¶ms->sound_vol, 1, 4, file); fwrite(¶ms->p_base_freq, 1, 4, file); fwrite(¶ms->p_freq_limit, 1, 4, file); fwrite(¶ms->p_freq_ramp, 1, 4, file); fwrite(¶ms->p_freq_dramp, 1, 4, file); fwrite(¶ms->p_duty, 1, 4, file); fwrite(¶ms->p_duty_ramp, 1, 4, file); fwrite(¶ms->p_vib_strength, 1, 4, file); fwrite(¶ms->p_vib_speed, 1, 4, file); fwrite(¶ms->p_vib_delay, 1, 4, file); fwrite(¶ms->p_env_attack, 1, 4, file); fwrite(¶ms->p_env_sustain, 1, 4, file); fwrite(¶ms->p_env_decay, 1, 4, file); fwrite(¶ms->p_env_punch, 1, 4, file); uint8_t zeroes[] = {0, 0, 0, 0}; fwrite(zeroes, 1, 4, file); fwrite(¶ms->p_lpf_resonance, 1, 4, file); fwrite(¶ms->p_lpf_freq, 1, 4, file); fwrite(¶ms->p_lpf_ramp, 1, 4, file); fwrite(¶ms->p_hpf_freq, 1, 4, file); fwrite(¶ms->p_hpf_ramp, 1, 4, file); fwrite(¶ms->p_pha_offset, 1, 4, file); fwrite(¶ms->p_pha_ramp, 1, 4, file); fwrite(¶ms->p_repeat_speed, 1, 4, file); fwrite(¶ms->p_arp_speed, 1, 4, file); fwrite(¶ms->p_arp_mod, 1, 4, file); fclose(file); return true; } #define SYNTH_FRAME_COUNT 512 size_t getSFXRSoundFrameCount(struct sfxrParams * params) { size_t frameCount = 0; struct sfxrSynthState state; sfxr_ResetSample(&state, *params, false); state.playing_sample = true; while (state.playing_sample) { sfxr_SynthSample(&state, *params, SYNTH_FRAME_COUNT, NULL); frameCount += SYNTH_FRAME_COUNT; } return frameCount; } size_t getBFXRSoundFrameCount(struct sfxrParams * params) { size_t frameCount = 0; struct bfxrSynthState bfxrState; bfxr_reset(&bfxrState, *params, true); bfxrState.finished = false; while (!bfxrState.finished) { bfxr_synthWave(&bfxrState, *params, SYNTH_FRAME_COUNT, NULL); frameCount += SYNTH_FRAME_COUNT; } return frameCount; } PCMAudio * createSFXRSoundOutputFromParams(struct sfxrParams * params) { size_t frameCount = getSFXRSoundFrameCount(params); float * samples = calloc(frameCount, sizeof(*samples)); struct sfxrSynthState state; sfxr_ResetSample(&state, *params, false); state.playing_sample = true; sfxr_SynthSample(&state, *params, frameCount, samples); return PCMAudio_create(4, 1, SFXR_SAMPLE_RATE, frameCount, samples, false); } PCMAudio * createBFXRSoundOutputFromParams(struct sfxrParams * params) { size_t frameCount = getBFXRSoundFrameCount(params); float * samples = calloc(frameCount, sizeof(*samples)); struct bfxrSynthState bfxrState; bfxr_reset(&bfxrState, *params, true); bfxr_synthWave(&bfxrState, *params, frameCount, samples); return PCMAudio_create(4, 1, SFXR_SAMPLE_RATE, frameCount, samples, false); } PCMAudio * createBFXRSoundOutputFromTrackList(struct bfxrTrackList * trackList) { PCMAudio * trackAudio[trackList->trackCount]; unsigned int maxLength = 0; for (unsigned int trackIndex = 0; trackIndex < trackList->trackCount; trackIndex++) { if (trackList->tracks[trackIndex].id == -1) { continue; } trackAudio[trackIndex] = createBFXRSoundOutputFromParams(&trackList->tracks[trackIndex].synthData); if (trackList->tracks[trackIndex].reverse) { reverseAudioSamples(trackAudio[trackIndex]->samples, trackAudio[trackIndex]->channelCount, trackAudio[trackIndex]->bytesPerSample, trackAudio[trackIndex]->frameCount); } if (trackList->tracks[trackIndex].volume != 1.0f) { amplifyAudioSamples(trackAudio[trackIndex]->samples, trackAudio[trackIndex]->channelCount, trackAudio[trackIndex]->bytesPerSample, trackAudio[trackIndex]->frameCount, trackList->tracks[trackIndex].volume); } unsigned int onsetLength = (unsigned int) (trackList->tracks[trackIndex].onset * SFXR_SAMPLE_RATE / 2); if (trackAudio[trackIndex]->frameCount + onsetLength > maxLength) { maxLength = trackAudio[trackIndex]->frameCount + onsetLength; } } PCMAudio * mixedAudio = PCMAudio_create(4, 1, SFXR_SAMPLE_RATE, maxLength, calloc(maxLength, 4), false); for (unsigned int trackIndex = 0; trackIndex < trackList->trackCount; trackIndex++) { if (trackList->tracks[trackIndex].id == -1) { continue; } unsigned int onsetLength = (unsigned int) (trackList->tracks[trackIndex].onset * SFXR_SAMPLE_RATE / 2); float multiplier = 1.0f; mixAudioSamples(trackAudio[trackIndex]->samples, trackAudio[trackIndex]->channelCount, trackAudio[trackIndex]->sampleRate, trackAudio[trackIndex]->bytesPerSample, mixedAudio->samples + (onsetLength * mixedAudio->channelCount * mixedAudio->bytesPerSample), mixedAudio->channelCount, mixedAudio->sampleRate, mixedAudio->bytesPerSample, &multiplier, trackAudio[trackIndex]->frameCount, maxLength - onsetLength, NULL, NULL, NULL); PCMAudio_dispose(trackAudio[trackIndex]); } return mixedAudio; } void freeBFXRTrackList(struct bfxrTrackList * trackList) { free(trackList->tracks); }