/* 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 */ #include "audiosynth/AudioMath.h" #include "audiosynth/AudioSampler_generalSynth.h" #include "audiosynth/AudioSamplerParameters_generalSynth.h" #include "audiosynth/AudioSamplerParameters_multitrack.h" #include static float getMaxDecayTime(FilterChain filterChain) { float maxDecayTime = 0.0f; for (unsigned int filterIndex = 0; filterIndex < filterChain.signalFilterCount; filterIndex++) { maxDecayTime = fmaxf(maxDecayTime, call_virtual(getDecayTime, filterChain.signalFilters[filterIndex])); } return maxDecayTime; } float AudioSampler_generalSynth_getLength(compat_type(AudioSamplerParameters *) parameters) { float totalLength = 0.0f; if (StemObject_isExactClass(parameters, &AudioSamplerParameters_multitrack_class)) { AudioSamplerParameters_multitrack * params = parameters; for (unsigned int trackIndex = 0; trackIndex < params->trackCount; trackIndex++) { totalLength = fmax(totalLength, AudioSampler_generalSynth_getLength(params->tracks[trackIndex].parameters) + params->tracks[trackIndex].offset); } } else if (StemObject_isExactClass(parameters, &AudioSamplerParameters_generalSynth_class)) { AudioSamplerParameters_generalSynth * params = parameters; float soundLength = 1.0f; switch (params->timingMode) { case SAMPLER_TIMING_SPECIFY_LENGTH: soundLength = params->length; break; case SAMPLER_TIMING_AMPLITUDE_LENGTH: soundLength = call_virtual(getLength, params->amplitudeEnvelope); break; case SAMPLER_TIMING_FREQUENCY_LENGTH: soundLength = call_virtual(getLength, params->frequencyCurve); break; case SAMPLER_TIMING_WAVE_LENGTH: soundLength = call_virtual(getLength, params->waveSampler); break; } totalLength = soundLength + getMaxDecayTime(params->filterChain); } return totalLength; } PCMAudio * AudioSampler_generalSynth_createPCMAudio(compat_type(AudioSamplerParameters *) parameters, float amplitude, unsigned int bytesPerSample, unsigned int channelCount, unsigned int sampleRate) { PCMAudio * result = NULL; if (StemObject_isExactClass(parameters, &AudioSamplerParameters_multitrack_class)) { AudioSamplerParameters_multitrack params = *(AudioSamplerParameters_multitrack *) parameters; float totalLength = 0.0f; for (unsigned int trackIndex = 0; trackIndex < params.trackCount; trackIndex++) { totalLength = fmax(totalLength, AudioSampler_generalSynth_getLength(params.tracks[trackIndex].parameters) + params.tracks[trackIndex].offset); } AudioFrameIndex frameCount = totalLength * sampleRate; result = PCMAudio_create(bytesPerSample, channelCount, sampleRate, frameCount, calloc(frameCount, bytesPerSample * channelCount), false); for (unsigned int trackIndex = 0; trackIndex < params.trackCount; trackIndex++) { if (params.tracks[trackIndex].solo) { float channelMultipliers[channelCount]; for (unsigned int channelIndex = 0; channelIndex < channelCount; channelIndex++) { channelMultipliers[channelIndex] = params.tracks[trackIndex].blend * amplitude; } AudioSampler_generalSynth_renderIntoMix(params.tracks[trackIndex].parameters, channelMultipliers, result, params.tracks[trackIndex].offset * sampleRate); return result; } } for (unsigned int trackIndex = 0; trackIndex < params.trackCount; trackIndex++) { if (!params.tracks[trackIndex].mute) { float channelMultipliers[channelCount]; for (unsigned int channelIndex = 0; channelIndex < channelCount; channelIndex++) { channelMultipliers[channelIndex] = params.tracks[trackIndex].blend * amplitude; } AudioSampler_generalSynth_renderIntoMix(params.tracks[trackIndex].parameters, channelMultipliers, result, params.tracks[trackIndex].offset * sampleRate); } } return result; } else if (StemObject_isExactClass(parameters, &AudioSamplerParameters_generalSynth_class)) { AudioSamplerParameters_generalSynth params = *(AudioSamplerParameters_generalSynth *) parameters; float soundLength = 1.0f; switch (params.timingMode) { case SAMPLER_TIMING_SPECIFY_LENGTH: soundLength = params.length; break; case SAMPLER_TIMING_AMPLITUDE_LENGTH: // TODO: This needs to be with controllers applied soundLength = call_virtual(getLength, params.amplitudeEnvelope); break; case SAMPLER_TIMING_FREQUENCY_LENGTH: // TODO: This needs to be with controllers applied soundLength = call_virtual(getLength, params.frequencyCurve); break; case SAMPLER_TIMING_WAVE_LENGTH: // TODO: This needs to be with frequency and controllers applied soundLength = call_virtual(getLength, params.waveSampler); break; } float decayTime = getMaxDecayTime(params.filterChain); AudioFrameIndex frameCount = soundLength * sampleRate; AudioFrameIndex decayFrameCount = decayTime * sampleRate; PCMAudio * audio = PCMAudio_create(4, 1, sampleRate, frameCount + decayFrameCount, malloc(4 * 1 * (frameCount + decayFrameCount)), false); float * samples = audio->samples; double ftime = 0.0; double timePerFrame = 1.0 / sampleRate; double amplitudeTimePerFrame = timePerFrame; if (!params.amplitudeTimeAbsolute && params.timingMode != SAMPLER_TIMING_AMPLITUDE_LENGTH) { amplitudeTimePerFrame /= soundLength; } float frequencyTimePerFrame = timePerFrame; if (!params.frequencyTimeAbsolute && params.timingMode != SAMPLER_TIMING_FREQUENCY_LENGTH) { frequencyTimePerFrame /= soundLength; } double amplitudeTime = 0.0f; double frequencyTime = 0.0f; double phase = 0.0f; double phaseDeltaPerFrame = 1.0 / sampleRate; float (* waveSampleFunction)(WaveSampler *, SamplerObject_state *, float, float, float, float) = params.waveSampler->vtable->sample; float (* amplitudeSampleFunction)(AmplitudeEnvelope *, SamplerObject_state *, float, float, float, float) = params.amplitudeEnvelope->vtable->sample; float (* frequencySampleFunction)(FrequencyCurve *, SamplerObject_state *, float, float, float, float) = params.frequencyCurve->vtable->sample; SamplerObject_state * waveState = call_virtual(initState, params.waveSampler); SamplerObject_state * amplitudeState = call_virtual(initState, params.amplitudeEnvelope); SamplerObject_state * frequencyState = call_virtual(initState, params.frequencyCurve); SignalFilter * activeFilters[params.filterChain.signalFilterCount]; unsigned int activeFilterCount = 0; for (unsigned int filterIndex = 0; filterIndex < params.filterChain.signalFilterCount; filterIndex++) { if (params.filterChain.signalFilters[filterIndex]->solo) { activeFilters[activeFilterCount++] = params.filterChain.signalFilters[filterIndex]; } } if (activeFilterCount == 0) { for (unsigned int filterIndex = 0; filterIndex < params.filterChain.signalFilterCount; filterIndex++) { if (!params.filterChain.signalFilters[filterIndex]->mute && params.filterChain.signalFilters[filterIndex]->blend != 0.0f) { activeFilters[activeFilterCount++] = params.filterChain.signalFilters[filterIndex]; } } } SamplerObject_state * activeFilterStates[params.filterChain.signalFilterCount]; for (unsigned int filterIndex = 0; filterIndex < activeFilterCount; filterIndex++) { activeFilterStates[filterIndex] = call_virtual(initState, activeFilters[filterIndex]); } SynthPropertyController * activeControllers[params.controllerList.controllerCount]; unsigned int activeControllerCount = 0; for (unsigned int controllerIndex = 0; controllerIndex < params.controllerList.controllerCount; controllerIndex++) { if (params.controllerList.controllers[controllerIndex]->solo) { activeControllers[activeControllerCount++] = params.controllerList.controllers[controllerIndex]; } } if (activeControllerCount == 0) { for (unsigned int controllerIndex = 0; controllerIndex < params.controllerList.controllerCount; controllerIndex++) { if (!params.controllerList.controllers[controllerIndex]->mute) { activeControllers[activeControllerCount++] = params.controllerList.controllers[controllerIndex]; } } } SamplerObject * controllerTargetObjects[activeControllerCount]; SamplerObject_state * controllerTargetStates[activeControllerCount]; unsigned int controllerOffset = 0; for (unsigned int controllerIndex = 0; controllerIndex < activeControllerCount; controllerIndex++) { activeControllers[controllerIndex] = activeControllers[controllerIndex + controllerOffset]; SamplerObject * object = NULL; SamplerObject_state * state = NULL; switch (activeControllers[controllerIndex]->propertyIdentifier.rootType) { case SYNTH_PROPERTY_ROOT_UNDEFINED: break; case SYNTH_PROPERTY_ROOT_AMPLITUDE_ENVELOPE: object = (SamplerObject *) params.amplitudeEnvelope; state = amplitudeState; break; case SYNTH_PROPERTY_ROOT_FREQUENCY_CURVE: object = (SamplerObject *) params.frequencyCurve; state = frequencyState; break; case SYNTH_PROPERTY_ROOT_WAVE_SAMPLER: object = (SamplerObject *) params.waveSampler; state = waveState; break; case SYNTH_PROPERTY_ROOT_SIGNAL_FILTER: if (activeControllers[controllerIndex]->propertyIdentifier.objectIndex < params.filterChain.signalFilterCount) { for (unsigned int filterIndex = 0; filterIndex < activeFilterCount; filterIndex++) { if (activeFilters[filterIndex] == params.filterChain.signalFilters[activeControllers[controllerIndex]->propertyIdentifier.objectIndex]) { object = (SamplerObject *) params.filterChain.signalFilters[activeControllers[controllerIndex]->propertyIdentifier.objectIndex]; state = activeFilterStates[filterIndex]; break; } } } break; } if (state == NULL || object == NULL) { controllerOffset++; controllerIndex--; activeControllerCount--; } else { controllerTargetObjects[controllerIndex] = object; controllerTargetStates[controllerIndex] = state; } } double lastPhase = 0.0; for (AudioFrameIndex frameIndex = 0; frameIndex < frameCount; frameIndex++) { for (unsigned int controllerIndex = 0; controllerIndex < activeControllerCount; controllerIndex++) { call_virtual(apply, activeControllers[controllerIndex], controllerTargetObjects[controllerIndex], controllerTargetStates[controllerIndex], frequencyTime, ftime); } float amplitudeSample = amplitudeSampleFunction(params.amplitudeEnvelope, amplitudeState, amplitudeTime, amplitudeTimePerFrame, ftime, timePerFrame); float waveSample = waveSampleFunction(params.waveSampler, waveState, phase, phase - lastPhase, ftime, timePerFrame) * amplitudeSample; for (unsigned int filterIndex = 0; filterIndex < activeFilterCount; filterIndex++) { float filteredSample = call_virtual(transformSample, activeFilters[filterIndex], activeFilterStates[filterIndex], waveSample, ftime, timePerFrame); waveSample = waveSample * (1.0f - activeFilters[filterIndex]->blend) + filteredSample * activeFilters[filterIndex]->blend; } samples[frameIndex] = waveSample * amplitude; lastPhase = phase; float frequencySample = frequencySampleFunction(params.frequencyCurve, frequencyState, frequencyTime, frequencyTimePerFrame, ftime, timePerFrame); double phaseDelta = 13.75 * pow(2.0, frequencySample) * phaseDeltaPerFrame; phase += fmin(1.0, phaseDelta); ftime += timePerFrame; frequencyTime += frequencyTimePerFrame; amplitudeTime += amplitudeTimePerFrame; } for (AudioFrameIndex frameIndex = 0; frameIndex < decayFrameCount; frameIndex++) { float sample = 0.0f; for (unsigned int filterIndex = 0; filterIndex < activeFilterCount; filterIndex++) { float filteredSample = call_virtual(transformSample, activeFilters[filterIndex], activeFilterStates[filterIndex], sample, ftime, timePerFrame); sample = sample * (1.0f - activeFilters[filterIndex]->blend) + filteredSample * activeFilters[filterIndex]->blend; } samples[frameCount + frameIndex] = sample; ftime += timePerFrame; } for (unsigned int filterIndex = 0; filterIndex < activeFilterCount; filterIndex++) { call_virtual(disposeState, activeFilters[filterIndex], activeFilterStates[filterIndex]); } call_virtual(disposeState, params.waveSampler, waveState); call_virtual(disposeState, params.amplitudeEnvelope, amplitudeState); call_virtual(disposeState, params.frequencyCurve, frequencyState); if (bytesPerSample == audio->bytesPerSample && channelCount == audio->channelCount) { return audio; } result = PCMAudio_create(bytesPerSample, channelCount, sampleRate, audio->frameCount, malloc(bytesPerSample * channelCount * audio->frameCount), false); convertAudioSamples(audio->samples, audio->frameCount, audio->channelCount, audio->sampleRate, audio->bytesPerSample, result->samples, result->frameCount, result->channelCount, result->sampleRate, result->bytesPerSample, NULL, NULL, NULL); PCMAudio_dispose(audio); } return result; } void AudioSampler_generalSynth_renderIntoMix(compat_type(AudioSamplerParameters *) parameters, float * channelMultipliers, PCMAudio * ioMixedAudio, AudioFrameIndex frameOffset) { if (frameOffset > ioMixedAudio->frameCount) { return; } PCMAudio * audio = AudioSampler_generalSynth_createPCMAudio(parameters, 1.0f, 4, 1, ioMixedAudio->sampleRate); mixAudioSamples(audio->samples, audio->channelCount, audio->sampleRate, audio->bytesPerSample, ioMixedAudio->samples + frameOffset * ioMixedAudio->bytesPerSample * ioMixedAudio->channelCount, ioMixedAudio->channelCount, ioMixedAudio->sampleRate, ioMixedAudio->bytesPerSample, channelMultipliers, audio->frameCount, ioMixedAudio->frameCount - frameOffset, NULL, NULL, NULL); PCMAudio_dispose(audio); }