void SimpleDelay::process() { MOPO_ASSERT(inputMatchesBufferSize(kAudio)); MOPO_ASSERT(inputMatchesBufferSize(kFeedback)); MOPO_ASSERT(inputMatchesBufferSize(kSampleDelay)); mopo_float* dest = output()->buffer; const mopo_float* audio = input(kAudio)->source->buffer; const mopo_float* feedback = input(kFeedback)->source->buffer; if (feedback[0] == 0.0 && feedback[buffer_size_ - 1] == 0.0) { memcpy(dest, audio, sizeof(mopo_float) * buffer_size_); memory_->pushBlock(audio, buffer_size_); return; } const mopo_float* period = input(kSampleDelay)->source->buffer; int i = 0; if (input(kReset)->source->triggered) { int trigger_offset = input(kReset)->source->trigger_offset; for (; i < trigger_offset; ++i) tick(i, dest, audio, period, feedback); int clear_samples = std::min(MAX_CLEAR_SAMPLES, ((int)period[i]) + 1); memory_->pushZero(clear_samples); } for (int i = 0; i < buffer_size_; ++i) tick(i, dest, audio, period, feedback); }
void StateVariableFilter::computePassCoefficients(mopo_float blend, mopo_float cutoff, mopo_float resonance, bool db24) { MOPO_ASSERT(resonance > 0.0); MOPO_ASSERT(cutoff > 0.0); if (db24) resonance = sqrt(resonance); mopo_float g = tan(PI * utils::min(cutoff / sample_rate_, 0.5)); mopo_float k = 1.0 / resonance; mopo_float low_pass_amount = sqrt(utils::clamp(1.0 - blend, 0.0, 1.0)); mopo_float band_pass_amount = sqrt(utils::clamp(1.0 - fabs(blend - 1.0), 0.0, 1.0)); mopo_float high_pass_amount = sqrt(utils::clamp(blend - 1.0, 0.0, 1.0)); target_m0_ = 0.0; target_m1_ = band_pass_amount; target_m2_ = low_pass_amount; target_m0_ += high_pass_amount; target_m1_ += -k * high_pass_amount; target_m2_ += -high_pass_amount; a1_ = 1.0 / (1.0 + g * (g + k)); a2_ = g * a1_; a3_ = g * a2_; }
ValueDetailsLookup::ValueDetailsLookup() { int num_parameters = sizeof(parameter_list) / sizeof(ValueDetails); for (int i = 0; i < num_parameters; ++i) { details_lookup_[parameter_list[i].name] = parameter_list[i]; MOPO_ASSERT(parameter_list[i].default_value <= parameter_list[i].max); MOPO_ASSERT(parameter_list[i].default_value >= parameter_list[i].min); } }
void Processor::plug(const Output* source, unsigned int input_index) { MOPO_ASSERT(input_index < inputs_.size()); MOPO_ASSERT(source); inputs_[input_index]->source = source; if (router_) router_->connect(this, source, input_index); }
void VoiceHandler::noteOn(mopo_float note, mopo_float velocity, int sample, int channel) { MOPO_ASSERT(sample >= 0 && sample < buffer_size_); MOPO_ASSERT(channel >= 0 && channel < NUM_MIDI_CHANNELS); Voice* voice = grabVoice(); pressed_notes_.push_front(note); if (last_played_note_ < 0) last_played_note_ = note; voice->activate(note, velocity, last_played_note_, pressed_notes_.size(), sample, channel); active_voices_.push_back(voice); last_played_note_ = note; }
void StateVariableFilter::computeShelfCoefficients(Shelves choice, mopo_float cutoff, mopo_float gain) { MOPO_ASSERT(gain >= 0.0); MOPO_ASSERT(cutoff > 0.0); gain = sqrt(gain); mopo_float g = tan(PI * utils::min(cutoff / sample_rate_, 0.5)); mopo_float k = 1.0; switch(choice) { case kLowShelf: { g /= sqrt(gain); target_m0_ = 1.0; target_m1_ = k * (gain - 1.0); target_m2_ = gain * gain - 1.0; break; } case kBandShelf: { k /= gain; target_m0_ = 1.0; target_m1_ = k * (gain * gain - 1.0); target_m2_ = 0.0; break; } case kHighShelf: { g *= sqrt(gain); target_m0_ = gain * gain; target_m1_ = k * gain * (1.0 - gain); target_m2_ = 1.0 - gain * gain; break; } default: { target_m0_ = 0.0; target_m1_ = 0.0; target_m2_ = 0.0; } } a1_ = 1.0 / (1.0 + g * (g + k)); a2_ = g * a1_; a3_ = g * a2_; if (last_shelf_ != choice) { reset(); last_shelf_ = choice; } }
void HelmEngine::connectModulation(ModulationConnection* connection) { Processor::Output* source = getModulationSource(connection->source); MOPO_ASSERT(source != 0); Processor* destination = getModulationDestination(connection->destination, source->owner->isPolyphonic()); MOPO_ASSERT(destination != 0); connection->modulation_scale.plug(source, 0); connection->modulation_scale.plug(&connection->amount, 1); connection->modulation_scale.setControlRate(source->owner->isControlRate()); destination->plugNext(&connection->modulation_scale); source->owner->router()->addProcessor(&connection->modulation_scale); mod_connections_.insert(connection); }
void LinearSlope::process() { MOPO_ASSERT(inputMatchesBufferSize(kTarget)); MOPO_ASSERT(inputMatchesBufferSize(kRunSeconds)); int i = 0; if (input(kTriggerJump)->source->triggered) { int trigger_offset = input(kTriggerJump)->source->trigger_offset; for (; i < trigger_offset; ++i) tick(i); last_value_ = input(kTarget)->at(i); } for (; i < buffer_size_; ++i) tick(i); }
void Oscillator::process() { MOPO_ASSERT(inputMatchesBufferSize(kFrequency)); MOPO_ASSERT(inputMatchesBufferSize(kPhase)); preprocess(); int i = 0; if (input(kReset)->source->triggered && input(kReset)->source->trigger_value == kVoiceReset) { int trigger_offset = input(kReset)->source->trigger_offset; for (; i < trigger_offset; ++i) tick(i); offset_ = 0.0; } for (; i < buffer_size_; ++i) tick(i); }
void SynthBase::updateMemoryOutput(int samples, const mopo::mopo_float* left, const mopo::mopo_float* right) { mopo::mopo_float last_played = std::max(engine_.getLastActiveNote(), OUTPUT_WINDOW_MIN_NOTE); int output_inc = engine_.getSampleRate() / mopo::MEMORY_SAMPLE_RATE; if (last_played && last_played_note_ != last_played) { last_played_note_ = last_played; mopo::mopo_float frequency = mopo::utils::midiNoteToFrequency(last_played_note_); mopo::mopo_float period = engine_.getSampleRate() / frequency; int window_length = output_inc * mopo::MEMORY_RESOLUTION; memory_reset_period_ = period; while (memory_reset_period_ < window_length) memory_reset_period_ += memory_reset_period_; memory_reset_period_ = std::min(memory_reset_period_, 2.0 * window_length); memory_index_ = 0; } for (; memory_input_offset_ < samples; memory_input_offset_ += output_inc) { int input_index = mopo::utils::iclamp(memory_input_offset_, 0, samples); memory_index_ = mopo::utils::iclamp(memory_index_, 0, 2 * mopo::MEMORY_RESOLUTION - 1); MOPO_ASSERT(input_index >= 0); MOPO_ASSERT(input_index < samples); MOPO_ASSERT(memory_index_ >= 0); MOPO_ASSERT(memory_index_ < 2 * mopo::MEMORY_RESOLUTION); output_memory_write_[memory_index_++] = left[input_index] + right[input_index]; if (memory_index_ * output_inc >= memory_reset_period_) { memory_input_offset_ += memory_reset_period_ - memory_index_ * output_inc; memory_index_ = 0; memcpy(output_memory_, output_memory_write_, 2 * mopo::MEMORY_RESOLUTION * sizeof(float)); } } memory_input_offset_ -= samples; }
void PortamentoSlope::process() { MOPO_ASSERT(inputMatchesBufferSize(kTarget)); processTriggers(); int state = static_cast<int>(input(kPortamentoType)->at(0)); mopo_float run_seconds = input(kRunSeconds)->at(0); if (state == kPortamentoOff || utils::closeToZero(run_seconds)) { processBypass(0); return; } mopo_float increment = 0.4 / (sample_rate_ * input(kRunSeconds)->at(0)); mopo_float decay = 0.07 / (sample_rate_ * input(kRunSeconds)->at(0)); const mopo_float* targets = input(kTarget)->source->buffer; int i = 0; int note_number = static_cast<int>(input(kNoteNumber)->source->trigger_value); if (state == kPortamentoAuto && note_number <= 1 && input(kTriggerJump)->source->triggered) { int trigger_offset = input(kTriggerJump)->source->trigger_offset; for (; i < trigger_offset; ++i) tick(i, targets[i], increment, decay); last_value_ = input(kTarget)->at(trigger_offset); } else if (input(kTriggerStart)->source->triggered) { int trigger_offset = input(kTriggerStart)->source->trigger_offset; for (; i < trigger_offset; ++i) tick(i, targets[i], increment, decay); last_value_ = input(kTriggerStart)->source->trigger_value; } if (last_value_ == input(kTarget)->at(0) && last_value_ == input(kTarget)->at(buffer_size_ - 1)) { processBypass(i); } else { for (; i < buffer_size_; ++i) tick(i, targets[i], increment, decay); } }
void SynthBase::processAudio(AudioSampleBuffer* buffer, int channels, int samples, int offset) { if (engine_.getBufferSize() != samples) engine_.setBufferSize(samples); engine_.process(); const mopo::mopo_float* engine_output_left = engine_.output(0)->buffer; const mopo::mopo_float* engine_output_right = engine_.output(1)->buffer; for (int channel = 0; channel < channels; ++channel) { float* channelData = buffer->getWritePointer(channel, offset); const mopo::mopo_float* synth_output = (channel % 2) ? engine_output_right : engine_output_left; #pragma clang loop vectorize(enable) interleave(enable) for (int i = 0; i < samples; ++i) { channelData[i] = synth_output[i]; MOPO_ASSERT(std::isfinite(synth_output[i])); } } updateMemoryOutput(samples, engine_output_left, engine_output_right); }
void Cursynth::saveConfiguration() { confirmPathExists(getConfigPath()); // Store all the MIDI learn data into JSON. cJSON* root = cJSON_CreateObject(); std::map<int, std::string>::iterator iter = midi_learn_.begin(); for (; iter != midi_learn_.end(); ++iter) { cJSON* midi = cJSON_CreateNumber(iter->first); cJSON_AddItemToObject(root, iter->second.c_str(), midi); } // Write the configuration JSON to the configuration file. char* json = cJSON_Print(root); std::ofstream save_file; save_file.open(getConfigFile().c_str()); MOPO_ASSERT(save_file.is_open()); save_file << json; save_file.close(); free(json); cJSON_Delete(root); }
void StateVariableFilter::process() { MOPO_ASSERT(inputMatchesBufferSize(kAudio)); const mopo_float* audio_buffer = input(kAudio)->source->buffer; mopo_float* dest = output()->buffer; if (input(kOn)->at(0) == 0.0) { processAllPass(audio_buffer, dest); return; } Styles style = static_cast<Styles>(static_cast<int>(input(kStyle)->at(0))); bool db24 = style == k24dB; mopo_float cutoff = utils::clamp(input(kCutoff)->at(0), MIN_CUTTOFF, sample_rate_); mopo_float resonance = utils::clamp(input(kResonance)->at(0), MIN_RESONANCE, MAX_RESONANCE); target_drive_ = input(kDrive)->at(0); if (style == kShelf) { Shelves shelf_choice = static_cast<Shelves>(static_cast<int>(input(kShelfChoice)->at(0))); computeShelfCoefficients(shelf_choice, cutoff, input(kGain)->at(0)); } else { mopo_float blend = input(kPassBlend)->at(0); computePassCoefficients(blend, cutoff, resonance, db24); } if (style != last_style_) { reset(); last_style_ = style; } if (db24) process24db(audio_buffer, dest); else process12db(audio_buffer, dest); }
Voice* VoiceHandler::grabVoice() { Voice* voice = 0; // First check free voices. if (free_voices_.size() && (!legato_ || pressed_notes_.size() == 0 || active_voices_.size() < polyphony_)) { voice = free_voices_.front(); free_voices_.pop_front(); return voice; } // Next check released voices. std::list<Voice*>::iterator iter = active_voices_.begin(); for (; iter != active_voices_.end(); ++iter) { voice = *iter; if (voice->key_state() == Voice::kReleased) { active_voices_.erase(iter); return voice; } } // Then check sustained voices. iter = active_voices_.begin(); for (; iter != active_voices_.end(); ++iter) { voice = *iter; if (voice->key_state() == Voice::kSustained) { active_voices_.erase(iter); return voice; } } // If all are active just grab the oldest voice. MOPO_ASSERT(active_voices_.size()); voice = active_voices_.front(); active_voices_.pop_front(); return voice; }
void HelmStandaloneEditor::getNextAudioBlock(const AudioSourceChannelInfo& buffer) { ScopedLock lock(critical_section_); int num_samples = buffer.buffer->getNumSamples(); int synth_samples = std::min(num_samples, mopo::MAX_BUFFER_SIZE); MOPO_ASSERT(num_samples % synth_samples == 0); for (int b = 0; b < num_samples; b += synth_samples) { synth_.process(); const mopo::mopo_float* synth_output_left = synth_.output(0)->buffer; const mopo::mopo_float* synth_output_right = synth_.output(1)->buffer; for (int channel = 0; channel < mopo::NUM_CHANNELS; ++channel) { float* channelData = buffer.buffer->getWritePointer(channel); const mopo::mopo_float* synth_output = (channel % 2) ? synth_output_right : synth_output_left; for (int i = 0; i < synth_samples; ++i) channelData[b + i] = synth_output[i]; } for (int i = 0; i < synth_samples; ++i) output_memory_->push(synth_output_left[i] + synth_output_right[i]); } }
Processor::Output* Processor::output(unsigned int index) const { MOPO_ASSERT(index < outputs_.size()); return outputs_[index]; }
Processor::Input* Processor::input(unsigned int index) const { MOPO_ASSERT(index < inputs_.size()); return inputs_[index]; }
Arpeggiator::Arpeggiator(NoteHandler* note_handler) : Processor(kNumInputs, 1), note_handler_(note_handler), sustain_(false), phase_(1.0), note_index_(-1), current_octave_(0), octave_up_(true), last_played_note_(0) { MOPO_ASSERT(note_handler); }
Processor::Output* VoiceHandler::registerOutput(Output* output, int index) { MOPO_ASSERT(false); return output; }