Files
-
blinds / hardware_design / pcb / blinds_v60.brd
-
blinds / hardware_design / pcb / blinds_v60.sch
-
braids / hardware_design / pcb / braids_v50.brd
-
braids / hardware_design / pcb / braids_v50.sch
-
branches / hardware_design / pcb / branches_v40.brd
-
branches / hardware_design / pcb / branches_v40.sch
-
clouds / hardware_design / pcb / clouds_v30.brd
-
clouds / hardware_design / pcb / clouds_v30.sch
-
ears / hardware_design / panel / ears_panel_v30.brd
-
ears / hardware_design / panel / ears_panel_v30.sch
-
ears / hardware_design / pcb / ears_v40.brd
-
ears / hardware_design / pcb / ears_v40.sch
-
edges / hardware_design / pcb / edges_expander_v01.brd
-
edges / hardware_design / pcb / edges_expander_v01.sch
-
edges / hardware_design / pcb / edges_v20.brd
-
edges / hardware_design / pcb / edges_v20.sch
-
elements / hardware_design / pcb / elements_v02.brd
-
elements / hardware_design / pcb / elements_v02.sch
-
frames / hardware_design / pcb / frames_v03.brd
-
frames / hardware_design / pcb / frames_v03.sch
-
grids / hardware_design / pcb / grids_v02.brd
-
grids / hardware_design / pcb / grids_v02.sch
-
kinks / hardware_design / pcb / kinks_v41.brd
-
kinks / hardware_design / pcb / kinks_v41.sch
-
links / hardware_design / pcb / links_v40.brd
-
links / hardware_design / pcb / links_v40.sch
-
marbles / hardware_design / pcb / marbles_v70.brd
-
marbles / hardware_design / pcb / marbles_v70.sch
-
peaks / hardware_design / pcb / peaks_v30.brd
-
peaks / hardware_design / pcb / peaks_v30.sch
-
plaits / hardware_design / pcb / plaits_v50.brd
-
plaits / hardware_design / pcb / plaits_v50.sch
-
rings / hardware_design / pcb / rings_v30.brd
-
rings / hardware_design / pcb / rings_v30.sch
-
ripples / hardware_design / pcb / ripples_v40.brd
-
ripples / hardware_design / pcb / ripples_v40.sch
-
shades / hardware_design / pcb / shades_v30.brd
-
shades / hardware_design / pcb / shades_v30.sch
-
shelves / hardware_design / pcb / shelves_expander_v10.brd
-
shelves / hardware_design / pcb / shelves_expander_v10.sch
-
shelves / hardware_design / pcb / shelves_v05.brd
-
shelves / hardware_design / pcb / shelves_v05.sch
-
stages / hardware_design / pcb / stages_v70.brd
-
stages / hardware_design / pcb / stages_v70.sch
-
streams / hardware_design / pcb / streams_v02_bargraph.brd
-
streams / hardware_design / pcb / streams_v02_bargraph.sch
-
streams / hardware_design / pcb / streams_v05.brd
-
streams / hardware_design / pcb / streams_v05.sch
-
tides / hardware_design / pcb / tides_v40.brd
-
tides / hardware_design / pcb / tides_v40.sch
-
veils / hardware_design / pcb / veils_v40.brd
-
veils / hardware_design / pcb / veils_v40.sch
-
volts / hardware_design / pcb / volts_v01.brd
-
volts / hardware_design / pcb / volts_v01.sch
-
warps / hardware_design / pcb / warps_v30.brd
-
warps / hardware_design / pcb / warps_v30.sch
-
yarns / hardware_design / pcb / yarns_v03.brd
-
yarns / hardware_design / pcb / yarns_v03.sch
Last update 6 years 1 month
by
Olivier Gillet
multi.cc// Copyright 2013 Olivier Gillet. // // Author: Olivier Gillet (ol.gillet@gmail.com) // // 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. // // See http://creativecommons.org/licenses/MIT/ for more information. // // ----------------------------------------------------------------------------- // // Multi. #include "yarns/multi.h" #include <algorithm> #include "stmlib/algorithms/voice_allocator.h" #include "yarns/just_intonation_processor.h" #include "yarns/midi_handler.h" #include "yarns/settings.h" namespace yarns { using namespace std; using namespace stmlib; const uint8_t clock_divisions[] = { 96, 48, 32, 24, 16, 12, 8, 6, 4, 3, 2, 1 }; void Multi::Init() { just_intonation_processor.Init(); fill( &settings_.custom_pitch_table[0], &settings_.custom_pitch_table[12], 0); for (uint8_t i = 0; i < kNumParts; ++i) { part_[i].Init(); part_[i].set_custom_pitch_table(settings_.custom_pitch_table); } for (uint8_t i = 0; i < kNumVoices; ++i) { voice_[i].Init(); } running_ = false; latched_ = false; recording_ = false; // Put the multi in a usable state. Even if these settings will later be // overriden with some data retrieved from Flash (presets). settings_.clock_tempo = 120; settings_.clock_swing = 0; settings_.clock_input_division = 1; settings_.clock_output_division = 7; settings_.clock_bar_duration = 4; settings_.clock_override = 0; MidiSettings* midi = part_[0].mutable_midi_settings(); midi->channel = 0; midi->min_note = 0; midi->max_note = 127; midi->min_velocity = 0; midi->max_velocity = 127; midi->out_mode = MIDI_OUT_MODE_GENERATED_EVENTS; VoicingSettings* voicing = part_[0].mutable_voicing_settings(); voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO; voicing->legato_mode = false; voicing->portamento = 0; voicing->pitch_bend_range = 2; voicing->vibrato_range = 1; voicing->modulation_rate = 50; voicing->trigger_duration = 2; voicing->aux_cv = 1; voicing->aux_cv_2 = 6; voicing->tuning_transpose = 0; voicing->tuning_fine = 0; voicing->tuning_root = 0; voicing->tuning_system = 0; voicing->audio_mode = 0; SequencerSettings* seq = part_[0].mutable_sequencer_settings(); seq->clock_division = 7; seq->gate_length = 3; seq->arp_range = 0; seq->arp_direction = 0; seq->arp_pattern = 0; fill( &seq->step[0], &seq->step[kNumSteps], SequencerStep(SEQUENCER_STEP_REST, 0)); seq->num_steps = 0; // A test sequence... // seq->num_steps = 4; // seq->step[0].data[0] = 48; // seq->step[0].data[1] = 0x7f; // seq->step[1].data[0] = 72; // seq->step[1].data[1] = 0x7f; // seq->step[2].data[0] = 60; // seq->step[2].data[1] = 0x7f; // seq->step[3].data[0] = 72; // seq->step[3].data[1] = 0x7f; // voicing->audio_mode = 1; // To hear it... num_active_parts_ = 1; part_[0].AllocateVoices(&voice_[0], 1, false); settings_.layout = LAYOUT_MONO; } void Multi::Clock() { if (!running_) { return; } uint16_t output_division = clock_divisions[settings_.clock_output_division]; uint16_t input_division = settings_.clock_input_division; // Logic equation for computing a clock output with a 50% duty cycle. if (output_division > 1) { if (clock_output_prescaler_ == 0 && clock_input_prescaler_ == 0) { clock_pulse_counter_ = 0xffff; } if (clock_output_prescaler_ == (output_division >> 1) && clock_input_prescaler_ == (input_division >> 1)) { clock_pulse_counter_ = 0; } } else { if (input_division > 1) { clock_pulse_counter_ = \ clock_input_prescaler_ <= (input_division - 1) >> 1 ? 0xffff : 0; } else { // Because no division is used, neither on the output nor on the input, // we don't have a sufficient fast time base to derive a 50% duty cycle // output. Instead, we output 5ms pulses. clock_pulse_counter_ = 40; } } if (!clock_input_prescaler_) { midi_handler.OnClock(); if (song_pointer_) { ClockSong(); } else { for (uint8_t i = 0; i < num_active_parts_; ++i) { part_[i].Clock(); } } ++bar_position_; if (bar_position_ >= settings_.clock_bar_duration * 24) { bar_position_ = 0; } if (bar_position_ == 0) { reset_pulse_counter_ = 81; } if (settings_.clock_bar_duration > kMaxBarDuration) { bar_position_ = 1; } ++clock_output_prescaler_; if (clock_output_prescaler_ >= output_division) { clock_output_prescaler_ = 0; } } ++clock_input_prescaler_; if (clock_input_prescaler_ >= settings_.clock_input_division) { clock_input_prescaler_ = 0; } if (stop_count_down_) { --stop_count_down_; if (!stop_count_down_ && started_by_keyboard_ && internal_clock()) { Stop(); } } } void Multi::Start(bool started_by_keyboard) { if (running_ || recording_) { return; } if (internal_clock()) { internal_clock_ticks_ = 0; internal_clock_.Start(settings_.clock_tempo, settings_.clock_swing); } midi_handler.OnStart(); started_by_keyboard_ = started_by_keyboard; running_ = true; latched_ = false; clock_input_prescaler_ = 0; clock_output_prescaler_ = 0; stop_count_down_ = 0; bar_position_ = 0xffff; for (uint8_t i = 0; i < num_active_parts_; ++i) { part_[i].Start(started_by_keyboard); } song_pointer_ = NULL; } void Multi::Stop() { if (!running()) { return; } for (uint8_t i = 0; i < num_active_parts_; ++i) { part_[i].Stop(); } midi_handler.OnStop(); clock_pulse_counter_ = 0; reset_pulse_counter_ = 0; stop_count_down_ = 0; running_ = false; latched_ = false; started_by_keyboard_ = false; song_pointer_ = NULL; } void Multi::Refresh() { if (clock_pulse_counter_) { --clock_pulse_counter_; } if (reset_pulse_counter_) { --reset_pulse_counter_; } for (uint8_t i = 0; i < kNumVoices; ++i) { voice_[i].Refresh(); } } void Multi::Set(uint8_t address, uint8_t value) { uint8_t* bytes; bytes = static_cast<uint8_t*>(static_cast<void*>(&settings_)); uint8_t previous_value = bytes[address]; bytes[address] = value; if (value != previous_value) { if (address == MULTI_LAYOUT) { ChangeLayout( static_cast<Layout>(previous_value), static_cast<Layout>(value)); } else if (address == MULTI_CLOCK_TEMPO) { internal_clock_.set_tempo(settings_.clock_tempo); } else if (address == MULTI_CLOCK_SWING) { internal_clock_.set_swing(settings_.clock_swing); } } } void Multi::GetCvGate(uint16_t* cv, bool* gate) { switch (settings_.layout) { case LAYOUT_MONO: case LAYOUT_DUAL_POLYCHAINED: cv[0] = voice_[0].note_dac_code(); cv[1] = voice_[0].velocity_dac_code(); cv[2] = voice_[0].aux_cv_dac_code(); cv[3] = voice_[0].aux_cv_dac_code_2(); gate[0] = voice_[0].gate(); gate[1] = voice_[0].trigger(); gate[2] = clock(); gate[3] = reset_or_playing_flag(); break; case LAYOUT_DUAL_MONO: cv[0] = voice_[0].note_dac_code(); cv[1] = voice_[1].note_dac_code(); cv[2] = voice_[0].aux_cv_dac_code(); cv[3] = voice_[1].aux_cv_dac_code(); gate[0] = voice_[0].gate(); gate[1] = voice_[1].gate(); gate[2] = clock(); gate[3] = reset_or_playing_flag(); break; case LAYOUT_DUAL_POLY: case LAYOUT_QUAD_POLYCHAINED: cv[0] = voice_[0].note_dac_code(); cv[1] = voice_[1].note_dac_code(); cv[2] = voice_[0].aux_cv_dac_code(); cv[3] = voice_[1].aux_cv_dac_code_2(); gate[0] = voice_[0].gate(); gate[1] = voice_[1].gate(); gate[2] = clock(); gate[3] = reset_or_playing_flag(); break; case LAYOUT_QUAD_MONO: case LAYOUT_QUAD_POLY: case LAYOUT_OCTAL_POLYCHAINED: case LAYOUT_THREE_ONE: cv[0] = voice_[0].note_dac_code(); cv[1] = voice_[1].note_dac_code(); cv[2] = voice_[2].note_dac_code(); cv[3] = voice_[3].note_dac_code(); gate[0] = voice_[0].gate(); gate[1] = voice_[1].gate(); if (settings_.clock_override) { gate[2] = clock(); gate[3] = reset_or_playing_flag(); } else { gate[2] = voice_[2].gate(); gate[3] = voice_[3].gate(); } break; case LAYOUT_QUAD_TRIGGERS: cv[0] = voice_[0].trigger_dac_code(); cv[1] = voice_[1].trigger_dac_code(); cv[2] = voice_[2].trigger_dac_code(); cv[3] = voice_[3].trigger_dac_code(); gate[0] = voice_[0].trigger() && ~voice_[1].gate(); gate[1] = voice_[0].trigger() && voice_[1].gate(); gate[2] = clock(); gate[3] = reset_or_playing_flag(); break; case LAYOUT_QUAD_VOLTAGES: cv[0] = voice_[0].aux_cv_dac_code(); cv[1] = voice_[1].aux_cv_dac_code(); cv[2] = voice_[2].aux_cv_dac_code(); cv[3] = voice_[3].aux_cv_dac_code(); gate[0] = voice_[0].gate(); gate[1] = voice_[1].gate(); if (settings_.clock_override) { gate[2] = clock(); gate[3] = reset_or_playing_flag(); } else { gate[2] = voice_[2].gate(); gate[3] = voice_[3].gate(); } break; } } bool Multi::GetAudioSource(uint8_t* audio_source) { bool has_audio_source = false; switch (settings_.layout) { case LAYOUT_MONO: case LAYOUT_DUAL_POLYCHAINED: audio_source[0] = 0xff; audio_source[1] = 0xff; audio_source[2] = 0xff; audio_source[3] = voice_[0].audio_mode() ? 0 : 0xff; has_audio_source = voice_[0].audio_mode(); break; case LAYOUT_DUAL_MONO: case LAYOUT_DUAL_POLY: case LAYOUT_QUAD_POLYCHAINED: audio_source[0] = 0xff; audio_source[1] = 0xff; audio_source[2] = voice_[0].audio_mode() ? 0 : 0xff; audio_source[3] = voice_[1].audio_mode() ? 1 : 0xff; has_audio_source = voice_[0].audio_mode() || voice_[1].audio_mode(); break; case LAYOUT_QUAD_MONO: case LAYOUT_QUAD_POLY: case LAYOUT_OCTAL_POLYCHAINED: case LAYOUT_THREE_ONE: audio_source[0] = voice_[0].audio_mode() ? 0 : 0xff; audio_source[1] = voice_[1].audio_mode() ? 1 : 0xff; audio_source[2] = voice_[2].audio_mode() ? 2 : 0xff; audio_source[3] = voice_[3].audio_mode() ? 3 : 0xff; has_audio_source = voice_[0].audio_mode() || voice_[1].audio_mode() || \ voice_[2].audio_mode() || voice_[3].audio_mode(); break; case LAYOUT_QUAD_TRIGGERS: case LAYOUT_QUAD_VOLTAGES: audio_source[0] = 0xff; audio_source[1] = 0xff; audio_source[2] = 0xff; audio_source[3] = 0xff; has_audio_source = false; break; } return has_audio_source; } void Multi::GetLedsBrightness(uint8_t* brightness) { if (layout_configurator_.learning()) { fill(&brightness[0], &brightness[kNumVoices], 0); for (uint8_t i = 0; i < layout_configurator_.num_notes(); ++i) { brightness[i] = 255; } return; } switch (settings_.layout) { case LAYOUT_MONO: case LAYOUT_DUAL_POLYCHAINED: brightness[0] = voice_[0].gate() ? 255 : 0; brightness[1] = voice_[0].velocity() << 1; brightness[2] = voice_[0].aux_cv(); brightness[3] = voice_[0].aux_cv_2(); break; case LAYOUT_DUAL_MONO: brightness[0] = voice_[0].gate() ? 255 : 0; brightness[1] = voice_[1].gate() ? 255 : 0; brightness[2] = voice_[0].aux_cv(); brightness[3] = voice_[1].aux_cv(); break; case LAYOUT_DUAL_POLY: case LAYOUT_QUAD_POLYCHAINED: brightness[0] = voice_[0].gate() ? 255 : 0; brightness[1] = voice_[1].gate() ? 255 : 0; brightness[2] = voice_[0].aux_cv(); brightness[3] = voice_[1].aux_cv_2(); break; case LAYOUT_QUAD_MONO: case LAYOUT_QUAD_POLY: case LAYOUT_OCTAL_POLYCHAINED: case LAYOUT_QUAD_TRIGGERS: case LAYOUT_THREE_ONE: brightness[0] = voice_[0].gate() ? (voice_[0].velocity() << 1) : 0; brightness[1] = voice_[1].gate() ? (voice_[1].velocity() << 1) : 0; brightness[2] = voice_[2].gate() ? (voice_[2].velocity() << 1) : 0; brightness[3] = voice_[3].gate() ? (voice_[3].velocity() << 1) : 0; break; case LAYOUT_QUAD_VOLTAGES: brightness[0] = voice_[0].aux_cv(); brightness[1] = voice_[1].aux_cv(); brightness[2] = voice_[2].aux_cv(); brightness[3] = voice_[3].aux_cv(); break; } } void Multi::UpdateLayout() { // Reset and close all parts and voices. for (uint8_t i = 0; i < kNumParts; ++i) { part_[i].Reset(); } for (uint8_t i = 0; i < kNumVoices; ++i) { voice_[i].NoteOff(); } switch (settings_.layout) { case LAYOUT_MONO: case LAYOUT_DUAL_MONO: case LAYOUT_QUAD_MONO: { uint8_t num_parts = settings_.layout == LAYOUT_MONO ? 1 : \ (settings_.layout == LAYOUT_DUAL_MONO ? 2 : 4); for (uint8_t i = 0; i < num_parts; ++i) { part_[i].AllocateVoices(&voice_[i], 1, false); } num_active_parts_ = num_parts; } break; case LAYOUT_DUAL_POLY: case LAYOUT_QUAD_POLY: case LAYOUT_DUAL_POLYCHAINED: case LAYOUT_QUAD_POLYCHAINED: case LAYOUT_OCTAL_POLYCHAINED: { uint8_t num_voices = settings_.layout == LAYOUT_DUAL_POLY || \ settings_.layout == LAYOUT_QUAD_POLYCHAINED ? 2 : \ (settings_.layout == LAYOUT_DUAL_POLYCHAINED ? 1 : 4); part_[0].AllocateVoices( &voice_[0], num_voices, settings_.layout >= LAYOUT_DUAL_POLYCHAINED); num_active_parts_ = 1; } break; case LAYOUT_QUAD_TRIGGERS: case LAYOUT_QUAD_VOLTAGES: { for (uint8_t i = 0; i < 4; ++i) { part_[i].AllocateVoices(&voice_[i], 1, false); } num_active_parts_ = 4; } break; case LAYOUT_THREE_ONE: { part_[0].AllocateVoices(&voice_[0], 3, false); part_[1].AllocateVoices(&voice_[3], 1, false); num_active_parts_ = 2; } break; default: break; } } void Multi::ChangeLayout(Layout old_layout, Layout new_layout) { // Reset and close all parts and voices. for (uint8_t i = 0; i < kNumParts; ++i) { part_[i].Reset(); } for (uint8_t i = 0; i < kNumVoices; ++i) { voice_[i].NoteOff(); } switch (new_layout) { case LAYOUT_MONO: case LAYOUT_DUAL_MONO: case LAYOUT_QUAD_MONO: { uint8_t num_parts = new_layout == LAYOUT_MONO ? 1 : \ (new_layout == LAYOUT_DUAL_MONO ? 2 : 4); for (uint8_t i = 0; i < num_parts; ++i) { MidiSettings* midi = part_[i].mutable_midi_settings(); if (old_layout == LAYOUT_QUAD_TRIGGERS) { midi->min_note = 0; midi->max_note = 127; } midi->min_velocity = 0; midi->max_velocity = 127; VoicingSettings* voicing = part_[i].mutable_voicing_settings(); voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO; voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; } // Duplicate uninitialized voices. for (uint8_t i = 1; i < num_parts; ++i) { uint8_t destination = i; uint8_t source = i % num_active_parts_; if (destination != source) { memcpy( part_[destination].mutable_midi_settings(), part_[source].mutable_midi_settings(), sizeof(MidiSettings)); memcpy( part_[destination].mutable_voicing_settings(), part_[source].mutable_voicing_settings(), sizeof(VoicingSettings)); memcpy( part_[destination].mutable_sequencer_settings(), part_[source].mutable_sequencer_settings(), sizeof(SequencerSettings)); } } for (uint8_t i = 0; i < num_parts; ++i) { part_[i].AllocateVoices(&voice_[i], 1, false); } num_active_parts_ = num_parts; } break; case LAYOUT_DUAL_POLY: case LAYOUT_QUAD_POLY: case LAYOUT_DUAL_POLYCHAINED: case LAYOUT_QUAD_POLYCHAINED: case LAYOUT_OCTAL_POLYCHAINED: { uint8_t num_voices = settings_.layout == LAYOUT_DUAL_POLY || \ settings_.layout == LAYOUT_QUAD_POLYCHAINED ? 2 : \ (settings_.layout == LAYOUT_DUAL_POLYCHAINED ? 1 : 4); MidiSettings* midi = part_[0].mutable_midi_settings(); if (old_layout == LAYOUT_QUAD_TRIGGERS) { midi->min_note = 0; midi->max_note = 127; } midi->min_velocity = 0; midi->max_velocity = 127; VoicingSettings* voicing = part_[0].mutable_voicing_settings(); voicing->allocation_mode = VOICE_ALLOCATION_MODE_POLY; voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; voicing->portamento = 0; voicing->legato_mode = 0; part_[0].AllocateVoices( &voice_[0], num_voices, new_layout >= LAYOUT_DUAL_POLYCHAINED); num_active_parts_ = 1; } break; case LAYOUT_QUAD_TRIGGERS: { for (uint8_t i = 0; i < 4; ++i) { MidiSettings* midi = part_[i].mutable_midi_settings(); if (old_layout != LAYOUT_QUAD_TRIGGERS) { midi->min_note = 36 + i * 2; midi->max_note = 36 + i * 2; } midi->min_velocity = 0; midi->max_velocity = 127; midi->channel = part_[0].mutable_midi_settings()->channel; midi->out_mode = part_[0].mutable_midi_settings()->out_mode; VoicingSettings* voicing = part_[i].mutable_voicing_settings(); voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO; voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; voicing->portamento = 0; voicing->legato_mode = 0; } // Duplicate sequencer settings. for (uint8_t i = 1; i < 4; ++i) { uint8_t destination = i; uint8_t source = i % num_active_parts_; if (destination != source) { memcpy( part_[destination].mutable_sequencer_settings(), part_[source].mutable_sequencer_settings(), sizeof(SequencerSettings)); } } for (uint8_t i = 0; i < 4; ++i) { part_[i].AllocateVoices(&voice_[i], 1, false); } num_active_parts_ = 4; } break; case LAYOUT_THREE_ONE: { MidiSettings* midi = part_[0].mutable_midi_settings(); if (old_layout == LAYOUT_QUAD_TRIGGERS) { midi->min_note = 0; midi->max_note = 127; } midi->min_velocity = 0; midi->max_velocity = 127; VoicingSettings* voicing = part_[0].mutable_voicing_settings(); voicing->allocation_mode = VOICE_ALLOCATION_MODE_POLY; voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; voicing->portamento = 0; voicing->legato_mode = 0; part_[0].AllocateVoices(&voice_[0], 3, false); midi = part_[1].mutable_midi_settings(); if (old_layout == LAYOUT_QUAD_TRIGGERS) { midi->min_note = 0; midi->max_note = 127; } midi->min_velocity = 0; midi->max_velocity = 127; voicing = part_[1].mutable_voicing_settings(); voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO; voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; voicing->portamento = 0; voicing->legato_mode = 0; part_[1].AllocateVoices(&voice_[3], 1, false); num_active_parts_ = 2; } break; case LAYOUT_QUAD_VOLTAGES: { uint8_t num_parts = 4; for (uint8_t i = 0; i < num_parts; ++i) { MidiSettings* midi = part_[i].mutable_midi_settings(); if (old_layout == LAYOUT_QUAD_TRIGGERS) { midi->min_note = 0; midi->max_note = 127; } midi->min_velocity = 0; midi->max_velocity = 127; VoicingSettings* voicing = part_[i].mutable_voicing_settings(); voicing->allocation_mode = VOICE_ALLOCATION_MODE_MONO; voicing->allocation_priority = NOTE_STACK_PRIORITY_LAST; } // Duplicate uninitialized voices. for (uint8_t i = 1; i < num_parts; ++i) { uint8_t destination = i; uint8_t source = i % num_active_parts_; if (destination != source) { memcpy( part_[destination].mutable_midi_settings(), part_[source].mutable_midi_settings(), sizeof(MidiSettings)); memcpy( part_[destination].mutable_voicing_settings(), part_[source].mutable_voicing_settings(), sizeof(VoicingSettings)); memcpy( part_[destination].mutable_sequencer_settings(), part_[source].mutable_sequencer_settings(), sizeof(SequencerSettings)); } } for (uint8_t i = 0; i < num_parts; ++i) { part_[i].AllocateVoices(&voice_[i], 1, false); } num_active_parts_ = num_parts; } break; default: break; } for (uint8_t i = 0; i < num_active_parts_; ++i) { part_[i].set_siblings(num_active_parts_ > 1); } } void Multi::Touch() { Stop(); internal_clock_.set_tempo(settings_.clock_tempo); UpdateLayout(); for (uint8_t i = 0; i < kNumParts; ++i) { part_[i].Touch(); } } const uint8_t song[] = { #include "song/song.h" 255, }; void Multi::StartSong() { Set(MULTI_LAYOUT, LAYOUT_QUAD_MONO); part_[0].mutable_voicing_settings()->audio_mode = 0x83; part_[1].mutable_voicing_settings()->audio_mode = 0x83; part_[2].mutable_voicing_settings()->audio_mode = 0x84; part_[3].mutable_voicing_settings()->audio_mode = 0x86; UpdateLayout(); settings_.clock_tempo = 140; Stop(); Start(false); song_pointer_ = &song[0]; song_clock_ = 0; song_delta_ = 0; } void Multi::ClockSong() { while (song_clock_ >= song_delta_) { if (*song_pointer_ == 255) { song_pointer_ = &song[0]; } if (*song_pointer_ == 254) { song_delta_ += 6; } else { uint8_t part = *song_pointer_ >> 6; uint8_t note = *song_pointer_ & 0x3f; if (note == 0) { part_[part].AllNotesOff(); } else { part_[part].NoteOn(0, note + 24, 100); } song_clock_ = 0; song_delta_ = 0; } ++song_pointer_; } ++song_clock_; } bool Multi::ControlChange(uint8_t channel, uint8_t controller, uint8_t value) { bool thru = true; if (channel + 1 == settings_.remote_control_channel) { yarns::settings.SetFromCC(0xff, controller, value); if (num_active_parts_ >= 4 && \ (controller == 0x78 || controller == 0x79 || controller == 0x7b)) { // Do not continue to avoid treating these messages as "all sound off", // "reset all controllers" and "all notes off" CC. return true; } } for (uint8_t i = 0; i < num_active_parts_; ++i) { if (part_[i].accepts(channel) && \ channel + 1 != settings_.remote_control_channel) { thru = part_[i].ControlChange(channel, controller, value) && thru; yarns::settings.SetFromCC(i, controller, value); } } return thru; } /* extern */ Multi multi; } // namespace yarns