void FeedbackDelayNetworkNode::process() { //First, check an d set properties. //We just do this inline because it's trivial. if(werePropertiesModified(this, Lav_FDN_DELAYS)) { setDelays(getProperty(Lav_FDN_DELAYS).getFloatArrayPtr()); } if(werePropertiesModified(this, Lav_FDN_MATRIX)) { setMatrix(getProperty(Lav_FDN_MATRIX).getFloatArrayPtr()); } if(werePropertiesModified(this, Lav_FDN_OUTPUT_GAINS)) { setOutputGains(getProperty(Lav_FDN_OUTPUT_GAINS).getFloatArrayPtr()); } if(werePropertiesModified(this, Lav_FDN_FILTER_TYPES, Lav_FDN_FILTER_FREQUENCIES)) { configureFilters( getProperty(Lav_FDN_FILTER_TYPES).getIntArrayPtr(), getProperty(Lav_FDN_FILTER_FREQUENCIES).getFloatArrayPtr()); } for(int i = 0 ; i < block_size; i++) { network->computeFrame(last_output); for(int j = 0; j < num_output_buffers; j++) { output_buffers[j][i] = last_output[j]*gains[j]; next_input[j] = input_buffers[j][i]; //Apply the filter. last_output[j] = filters[j]->tick(last_output[j]); } network->advance(next_input, last_output); } }
void FilteredDelayNode::process() { if(werePropertiesModified(this, Lav_FILTERED_DELAY_DELAY)) delayChanged(); if(werePropertiesModified(this, Lav_FILTERED_DELAY_INTERPOLATION_TIME)) recomputeDelta(); reconfigureBiquads(); float feedback = getProperty(Lav_FILTERED_DELAY_FEEDBACK).getFloatValue(); //optimize the common case of not having feedback. //the only difference between these blocks is in the advance line. if(feedback == 0.0f) { for(unsigned int output = 0; output < num_output_buffers; output++) { auto &line = *lines[output]; auto &filter=*biquads[output]; line.processBuffer(block_size, input_buffers[output], output_buffers[output]); for(int i=0; i < block_size; i++) output_buffers[output][i] = filter.tick(output_buffers[output][i]); } } else { for(unsigned int output = 0; output < num_output_buffers; output++) { auto &line = *lines[output]; auto &filter = *biquads[output]; for(unsigned int i = 0; i < block_size; i++) { output_buffers[output][i] = line.computeSample(); output_buffers[output][i] = filter.tick(output_buffers[output][i]); line.advance(input_buffers[output][i]+output_buffers[output][i]*feedback); } } } }
void DoppleringDelayNode::process() { if(werePropertiesModified(this, Lav_DELAY_DELAY)) delayChanged(); if(werePropertiesModified(this, Lav_DELAY_INTERPOLATION_TIME)) recomputeDelta(); for(int output = 0; output < num_output_buffers; output++) { auto &line = *lines[output]; for(int i = 0; i < block_size; i++) output_buffers[output][i] = line.tick(input_buffers[output][i]); } }
void BufferNode::process() { auto buff = getProperty(Lav_BUFFER_BUFFER).getBufferValue(); if(buff == nullptr) return; if(werePropertiesModified(this, Lav_BUFFER_POSITION)) player.setPosition(getProperty(Lav_BUFFER_POSITION).getDoubleValue()); if(werePropertiesModified(this, Lav_BUFFER_RATE)) player.setRate(getProperty(Lav_BUFFER_RATE).getDoubleValue()); if(werePropertiesModified(this, Lav_BUFFER_LOOPING)) player.setIsLooping(getProperty(Lav_BUFFER_LOOPING).getIntValue() != 0); int prevEndedCount = player.getEndedCount(); player.process(buff->getChannels(), &output_buffers[0]); getProperty(Lav_BUFFER_POSITION).setDoubleValue(player.getPosition()); for(int i = player.getEndedCount(); i > prevEndedCount; i--) { simulation->enqueueTask([=] () {(*end_callback)();}); } getProperty(Lav_BUFFER_ENDED_COUNT).setIntValue(player.getEndedCount()); }
void EnvironmentNode::willTick() { if(werePropertiesModified(this, Lav_ENVIRONMENT_OUTPUT_CHANNELS)) { int channels = getProperty(Lav_ENVIRONMENT_OUTPUT_CHANNELS).getIntValue(); output->resize(channels, channels); getOutputConnection(0)->reconfigure(0, channels); output->getOutputConnection(0)->reconfigure(0, channels); output->getInputConnection(0)->reconfigure(0, channels); } }
void AdditiveTriangleNode::process() { if(werePropertiesModified(this, Lav_TRIANGLE_HARMONICS)) oscillator.setHarmonics(getProperty(Lav_TRIANGLE_HARMONICS).getIntValue()); if(werePropertiesModified(this, Lav_OSCILLATOR_PHASE)) oscillator.setPhase(getProperty(Lav_OSCILLATOR_PHASE).getFloatValue()+oscillator.getPhase()); auto &freq = getProperty(Lav_OSCILLATOR_FREQUENCY); auto &freqMul = getProperty(Lav_OSCILLATOR_FREQUENCY_MULTIPLIER); if(freq.needsARate() || freqMul.needsARate()) { for(int i = 0; i < block_size; i++) { oscillator.setFrequency(freq.getFloatValue(i)*freqMul.getFloatValue(i)); output_buffers[0][i] = oscillator.tick(); } } else { oscillator.setFrequency(freq.getFloatValue()*freqMul.getFloatValue()); for(int i = 0; i < block_size; i++) { output_buffers[0][i] = oscillator.tick(); } } }
void BlitNode::process() { if(werePropertiesModified(this, Lav_OSCILLATOR_PHASE)) { oscillator.setPhase(oscillator.getPhase()+getProperty(Lav_OSCILLATOR_PHASE).getFloatValue()); } if(werePropertiesModified(this, Lav_BLIT_HARMONICS)) oscillator.setHarmonics(getProperty(Lav_BLIT_HARMONICS).getIntValue()); if(werePropertiesModified(this, Lav_OSCILLATOR_FREQUENCY)) oscillator.setFrequency(getProperty(Lav_OSCILLATOR_FREQUENCY).getFloatValue()); if(werePropertiesModified(this, Lav_BLIT_SHOULD_NORMALIZE)) oscillator.setShouldNormalize(getProperty(Lav_BLIT_SHOULD_NORMALIZE).getIntValue() == 1); auto &freq = getProperty(Lav_OSCILLATOR_FREQUENCY); auto &freqMul = getProperty(Lav_OSCILLATOR_FREQUENCY_MULTIPLIER); if(freq.needsARate()| freqMul.needsARate()) { for(int i = 0; i < block_size; i++) { oscillator.setFrequency(freq.getFloatValue(i)*freqMul.getFloatValue(i)); output_buffers[0][i] = (float)oscillator.tick(); } } else { oscillator.setFrequency(freq.getFloatValue()*freqMul.getFloatValue()); for(int i = 0; i < block_size; i++) output_buffers[0][i] = (float)oscillator.tick(); } }
void ThreeBandEqNode::process() { if(werePropertiesModified(this, Lav_THREE_BAND_EQ_LOWBAND_DBGAIN, Lav_THREE_BAND_EQ_LOWBAND_FREQUENCY, Lav_THREE_BAND_EQ_MIDBAND_DBGAIN, Lav_THREE_BAND_EQ_HIGHBAND_DBGAIN, Lav_THREE_BAND_EQ_HIGHBAND_FREQUENCY )) recompute(); for(int channel=0; channel < midband_peaks.getChannelCount(); channel++) scalarMultiplicationKernel(block_size, lowband_gain, input_buffers[channel], output_buffers[channel]); midband_peaks.process(block_size, &output_buffers[0], &output_buffers[0]); highband_shelves.process(block_size, &output_buffers[0], &output_buffers[0]); }
void ThreeBandEqNode::process() { if(werePropertiesModified(this, Lav_THREE_BAND_EQ_LOWBAND_DBGAIN, Lav_THREE_BAND_EQ_LOWBAND_FREQUENCY, Lav_THREE_BAND_EQ_MIDBAND_DBGAIN, Lav_THREE_BAND_EQ_HIGHBAND_DBGAIN, Lav_THREE_BAND_EQ_HIGHBAND_FREQUENCY )) recompute(); for(int channel=0; channel < channels; channel++) { auto &peak= *midband_peaks[channel]; auto &shelf = *highband_shelves[channel]; for(int i= 0; i < block_size; i++) { output_buffers[channel][i] = lowband_gain*peak.tick(shelf.tick(input_buffers[channel][i])); } } }
void SineNode::process() { if(werePropertiesModified(this, Lav_OSCILLATOR_PHASE)) oscillator.setPhase(oscillator.getPhase()+getProperty(Lav_OSCILLATOR_PHASE).getFloatValue()); auto &freq = getProperty(Lav_OSCILLATOR_FREQUENCY); auto &freqMul = getProperty(Lav_OSCILLATOR_FREQUENCY_MULTIPLIER); if(freq.needsARate() | freqMul.needsARate()) { for(int i=0; i < block_size; i++) { oscillator.setFrequency(freq.getFloatValue(i)*freqMul.getFloatValue(i)); output_buffers[0][i] = oscillator.tick(); } } else { oscillator.setFrequency(freq.getFloatValue()*freqMul.getFloatValue()); for(int i = 0; i < block_size; i++) { output_buffers[0][i] = (float)oscillator.tick(); } } }
void EnvironmentNode::willProcessParents() { if(werePropertiesModified(this, Lav_3D_POSITION, Lav_3D_ORIENTATION)) { //update the matrix. //Important: look at the glsl constructors. Glm copies them, and there is nonintuitive stuff here. const float* pos = getProperty(Lav_3D_POSITION).getFloat3Value(); const float* atup = getProperty(Lav_3D_ORIENTATION).getFloat6Value(); auto at = glm::vec3(atup[0], atup[1], atup[2]); auto up = glm::vec3(atup[3], atup[4], atup[5]); auto right = glm::cross(at, up); auto m = glm::mat4( right.x, up.x, -at.x, 0, right.y, up.y, -at.y, 0, right.z, up.z, -at.z, 0, 0, 0, 0, 1); //Above is a rotation matrix, which works presuming the player is at (0, 0). //Pass the translation through it, so that we can bake the translation in. auto posvec = m*glm::vec4(pos[0], pos[1], pos[2], 1.0f); //[column][row] because GLSL. m[3][0] = -posvec.x; m[3][1] = -posvec.y; m[3][2] = -posvec.z; environment_info.world_to_listener_transform = m; //this debug code left in case this is still all broken. /*printf("\n%f %f %f %f\n", m[0][0], m[1][0], m[2][0], m[3][0]); printf("%f %f %f %f\n", m[0][1], m[1][1], m[2][1], m[3][1]); printf("%f %f %f %f\n", m[0][2], m[1][2], m[2][2], m[3][2]); printf("%f %f %f %f\n\n", m[0][3], m[1][3], m[2][3], m[3][3]);*/ } //give the new environment to the sources. //this is a set of weak pointers. decltype(sources) needsRemoval; //for source cleanup below. for(auto i: sources) { auto tmp = i.lock(); if(tmp == nullptr) { needsRemoval.insert(i); continue; } tmp->update(environment_info); } //do cleanup of dead sources. for(auto i: needsRemoval) sources.erase(i); }
void PannerBankNode::willTick() { if(werePropertiesModified(this, Lav_PANNER_STRATEGY)) strategyChanged(); if(werePropertiesModified(this, Lav_PANNER_AZIMUTH, Lav_PANNER_BANK_SPREAD, Lav_PANNER_BANK_IS_CENTERED)) needsRepositioning(); }
void AllpassNode::process() { if(werePropertiesModified(this, Lav_ALLPASS_INTERPOLATION_TIME)) reconfigureInterpolationTime(); if(werePropertiesModified(this, Lav_ALLPASS_COEFFICIENT)) reconfigureCoefficient(); if(werePropertiesModified(this, Lav_ALLPASS_DELAY_SAMPLES)) reconfigureDelay(); bank.process(block_size, &input_buffers[0], &output_buffers[0]); }
bool werePropertiesModified(Node* node, int first, args... rest) { if(werePropertiesModified(node, first)) return true; else return werePropertiesModified(node, rest...); }
void LateReflectionsNode::process() { if(werePropertiesModified(this, Lav_LATE_REFLECTIONS_T60, Lav_LATE_REFLECTIONS_DENSITY, Lav_LATE_REFLECTIONS_HF_T60, Lav_LATE_REFLECTIONS_LF_T60, Lav_LATE_REFLECTIONS_HF_REFERENCE, Lav_LATE_REFLECTIONS_LF_REFERENCE )) recompute(); if(werePropertiesModified(this, Lav_LATE_REFLECTIONS_AMPLITUDE_MODULATION_FREQUENCY)) amplitudeModulationFrequencyChanged(); if(werePropertiesModified(this, Lav_LATE_REFLECTIONS_DELAY_MODULATION_FREQUENCY)) delayModulationFrequencyChanged(); if(werePropertiesModified(this, Lav_LATE_REFLECTIONS_ALLPASS_ENABLED)) allpassEnabledChanged(); if(werePropertiesModified(this, Lav_LATE_REFLECTIONS_ALLPASS_MODULATION_FREQUENCY)) allpassModulationFrequencyChanged(); normalizeOscillators(); float amplitudeModulationDepth = getProperty(Lav_LATE_REFLECTIONS_AMPLITUDE_MODULATION_DEPTH).getFloatValue(); float delayModulationDepth = getProperty(Lav_LATE_REFLECTIONS_DELAY_MODULATION_DEPTH).getFloatValue(); float allpassMinFreq=getProperty(Lav_LATE_REFLECTIONS_ALLPASS_MINFREQ).getFloatValue(); float allpassMaxFreq = getProperty(Lav_LATE_REFLECTIONS_ALLPASS_MAXFREQ).getFloatValue(); float allpassQ=getProperty(Lav_LATE_REFLECTIONS_ALLPASS_Q).getFloatValue(); bool allpassEnabled = getProperty(Lav_LATE_REFLECTIONS_ALLPASS_ENABLED).getIntValue() == 1; float allpassDelta= (allpassMaxFreq-allpassMinFreq)/2.0f; //we move delta upward and delta downward of this point. //consequently, we therefore range from the min to the max. float allpassModulationStart =allpassMinFreq+allpassDelta; for(int i= 0; i < block_size; i++) { //We modulate the delay lines first. for(int modulating = 0; modulating < 16; modulating++) { float delay = delays[modulating]; delay =delay+delay*delayModulationDepth*delay_modulators[modulating]->tick(); delay = std::min(delay, 1.0f); fdn.setDelay(modulating, delay); } //Prepare the allpasses, if enabled. if(allpassEnabled) { for(int modulating =0; modulating < order; modulating++) { allpasses[modulating]->configure(Lav_BIQUAD_TYPE_ALLPASS, allpassModulationStart+allpassDelta*allpass_modulators[modulating]->tick(), 0.0, allpassQ); } } //If disabled, the modulators are advanced later. //Get the fdn's output. fdn.computeFrame(output_frame); for(int j= 0; j < order; j++) output_buffers[j][i] = output_frame[j]; for(int j=0; j < order; j++) { //Through the highshelf, then the lowshelf. output_frame[j] = midshelves[j]->tick(highshelves[j]->tick(gains[j]*output_frame[j])); //and maybe through the allpass if(allpassEnabled) output_frame[j] = allpasses[j]->tick(output_frame[j]); } //Gains are baked into the fdn matrix. //bring in the inputs. for(int j = 0; j < order; j++) next_input_frame[j] = input_buffers[j][i]; fdn.advance(next_input_frame, output_frame); } //appluy the amplitude modulation, if it's needed. if(amplitudeModulationDepth!=0.0f) { for(int output = 0; output < num_output_buffers; output++) { float* output_buffer=output_buffers[output]; SinOsc& osc= *amplitude_modulators[output]; //get A sine wave. osc.fillBuffer(block_size, amplitude_modulation_buffer); //Implement 1.0-amplitudeModulationDepth/2+amplitudeModulationDepth*oscillatorValue. scalarMultiplicationKernel(block_size, amplitudeModulationDepth, amplitude_modulation_buffer, amplitude_modulation_buffer); scalarAdditionKernel(block_size, 1.0f-amplitudeModulationDepth/2.0f, amplitude_modulation_buffer, amplitude_modulation_buffer); //Apply the modulation. multiplicationKernel(block_size, amplitude_modulation_buffer, output_buffer, output_buffer); } } //Advance modulators for anything we aren't modulating: //We do this so that the same parameters always produce the same reverb, even after transitioning through multiple presets. //Without the following, the modulators for different stages can get out of phase with each other. if(allpassEnabled == false) { for(int i=0; i < order; i++)allpass_modulators[i]->skipSamples(block_size); } if(amplitudeModulationDepth == 0.0f) { for(int i = 0; i < 16; i++) { amplitude_modulators[i]->skipSamples(block_size); } } //Apply the pan reduction: for(int i = 0; i < order; i++) { auto &line = *pan_reducers[i]; for(int j = 0; j < block_size; j++) { output_buffers[i][j] = line.tick(output_buffers[i][j]); } } }
void MultipannerNode::willTick() { if(werePropertiesModified(this, Lav_PANNER_STRATEGY)) strategyChanged(); }
void BiquadNode::process() { if(werePropertiesModified(this, Lav_BIQUAD_FILTER_TYPE, Lav_BIQUAD_DBGAIN, Lav_BIQUAD_FREQUENCY, Lav_BIQUAD_Q)) reconfigure(); bank.process(block_size, &input_buffers[0], &output_buffers[0]); }
void HrtfNode::process() { if(werePropertiesModified(this, Lav_PANNER_APPLY_ITD)) applyIdtChanged(); bool applyingItd = getProperty(Lav_PANNER_APPLY_ITD).getIntValue() == 1; bool linearPhase = getProperty(Lav_PANNER_USE_LINEAR_PHASE).getIntValue() == 1; //Get the fft of the input. std::copy(input_buffers[0], input_buffers[0]+block_size, fft_workspace); kiss_fftr(fft, fft_workspace, input_fft); //calculating the hrir is expensive, do it only if needed. bool didRecompute = false; bool allowCrossfade = getProperty(Lav_PANNER_SHOULD_CROSSFADE).getIntValue(); float currentAzimuth = getProperty(Lav_PANNER_AZIMUTH).getFloatValue(); float currentElevation = getProperty(Lav_PANNER_ELEVATION).getFloatValue(); if(fabs(currentElevation-prev_elevation) > 0.5f || fabs(currentAzimuth-prev_azimuth) > 0.5f) { hrtf->computeCoefficientsStereo(currentElevation, currentAzimuth, left_response, right_response, linearPhase); if(allowCrossfade) { new_left_convolver->setResponse(response_length, left_response); new_right_convolver->setResponse(response_length, right_response); } else { left_convolver->setResponse(response_length, left_response); right_convolver->setResponse(response_length, right_response); } didRecompute=true; //note: putting these anywhere in the didnt-recompute path causes things to never move. prev_elevation = currentElevation; prev_azimuth = currentAzimuth; } //These happen regardless of if we are crossfading or recomputed. left_convolver->convolveFft(input_fft, output_buffers[0]); right_convolver->convolveFft(input_fft, output_buffers[1]); //If we crossfaded, we need to apply the following change. if(didRecompute && allowCrossfade) { //Shape the buffers as follows, enabling a simple add. for(int i=0; i < block_size; i++) { float w = 1.0f-i*crossfade_delta; output_buffers[0][i]*=w; output_buffers[1][i] *= w; } //Run the new convolver for the left channel and crossfade. //Then, repeat for the right. new_left_convolver->convolveFft(input_fft, crossfade_workspace); for(int i =0; i < block_size; i++) output_buffers[0][i] += crossfade_workspace[i]*i*crossfade_delta; new_right_convolver->convolveFft(input_fft, crossfade_workspace); for(int i=0; i < block_size; i++) output_buffers[1][i] += crossfade_workspace[i]*i*crossfade_delta; //Finally, swap them. std::swap(left_convolver, new_left_convolver); std::swap(right_convolver, new_right_convolver); } //break out early if we aren't doing interaural delay. if(applyingItd == false) return; //we compute the interaural delay and apply it to the lines. float interauralDelay = computeInterauralDelay(); if(interauralDelay > 0) { left_delay_line.setDelay(0.0); right_delay_line.setDelay(std::min(interauralDelay, max_interaural_delay)); } else { left_delay_line.setDelay(std::min(-interauralDelay, max_interaural_delay)); right_delay_line.setDelay(0.0); } //apply the delay lines. left_delay_line.processBuffer(block_size, output_buffers[0], output_buffers[0]); right_delay_line.processBuffer(block_size, output_buffers[1], output_buffers[1]); }