Files

copied
Last update 6 years 1 month by Olivier Gillet
Filesedges
..
bootloader
hardware_design
resources
__init__.py
adc_acquisition.cc
adc_acquisition.h
audio_buffer.cc
audio_buffer.h
digital_oscillator.cc
digital_oscillator.h
edges.cc
hardware_config.h
makefile
midi.h
midi_handler.cc
midi_handler.h
note_stack.h
resources.cc
resources.h
settings.cc
settings.h
storage.h
timer_oscillator.cc
timer_oscillator.h
ui.cc
ui.h
voice_allocator.cc
voice_allocator.h
edges.cc
// Copyright 2012 Olivier Gillet. // // Author: Olivier Gillet (olivier@mutable-instruments.net) // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. #include "avrlibx/system/init.h" #include "avrlibx/system/time.h" #include "avrlibx/system/timer.h" #include "avrlibx/utils/op.h" #include "edges/adc_acquisition.h" #include "edges/audio_buffer.h" #include "edges/digital_oscillator.h" #include "edges/hardware_config.h" #include "edges/midi.h" #include "edges/midi_handler.h" #include "edges/settings.h" #include "edges/timer_oscillator.h" #include "edges/ui.h" using namespace avrlibx; using namespace edges; MidiIO midi_io; MidiBuffer midi_in_buffer; midi::MidiStreamParser<MidiHandler> midi_parser; GateInputs gate_inputs; DACSS dac_ss; AudioDACInterface audio_dac; ADCSS adc_ss; ADCInterface adc; AudioRateTimer audio_rate_timer; SyncSwitch sync_switch; TimerOscillator channel_0; TimerOscillator channel_1; TimerOscillator channel_2; TimerOscillator channel_3; DigitalOscillator channel_4; // Channel 1 overflow. If sync is enabled, reset the phase of the channel 2. ISR(TCD0_OVF_vect) { Channel2Timer::Restart(); } volatile uint16_t audio_interrupt_count = 0; uint8_t channel_remapping[] = { 0, 1, 4, 5, 2, 3, 6, 7}; // Audio rate interrupt (48.048kHz). ISR(TCE0_OVF_vect) { // Shove a sample to the DAC. uint16_t sample = audio_buffer.ImmediateRead(); audio_dac.Strobe(); Word sample_12bits; sample_12bits.value = sample | 0x3000; audio_dac.Overwrite(sample_12bits.bytes[1]); audio_dac.Overwrite(sample_12bits.bytes[0]); // Step through the ADC pipeline. int8_t result = adc_acquisition.Cycle(); if (result != -1) { uint16_t value = adc_acquisition.channel(result); #ifdef OCTAL_ADC value = 4095 - value; result = channel_remapping[result]; #endif // OCTAL_ADC ui.set_cv(result, value); } // When a conversion is finished, update the corresponding channel // Note that the UI can take over the value read from the DAC, for example // When programming an arpeggio switch (result) { case 0: channel_1.UpdatePitch<Channel1Timer>( midi_handler.shift_pitch(0, settings.dac_to_pitch(0, ui.cv(0))) + settings.dac_to_fm(0, ui.cv(4)), settings.pw(0)); channel_0.SubFollow<Channel0Timer>(channel_1); break; case 1: channel_2.UpdatePitch<Channel2Timer>( midi_handler.shift_pitch(1, settings.dac_to_pitch(1, ui.cv(1))) + settings.dac_to_fm(1, ui.cv(5)), settings.pw(1)); break; case 2: channel_3.UpdatePitch<Channel3Timer>( midi_handler.shift_pitch(2, settings.dac_to_pitch(2, ui.cv(2))) + settings.dac_to_fm(2, ui.cv(6)), settings.pw(2)); break; case 3: { channel_4.UpdatePitch( midi_handler.shift_pitch(3, settings.dac_to_pitch(3, ui.cv(3))) + settings.dac_to_fm(3, ui.cv(7)), settings.shape(3)); #ifndef OCTAL_ADC uint8_t cv_controlled_pw_8bit = U16ShiftRight4(ui.cv(3)); channel_1.set_cv_pw(cv_controlled_pw_8bit); channel_2.set_cv_pw(cv_controlled_pw_8bit); channel_3.set_cv_pw(cv_controlled_pw_8bit); #endif // #ifndef OCTAL_ADC } break; #ifdef OCTAL_ADC case 4: channel_1.set_cv_pw(U16ShiftRight4(ui.cv(4))); break; case 5: channel_2.set_cv_pw(U16ShiftRight4(ui.cv(5))); break; case 6: channel_3.set_cv_pw(U16ShiftRight4(ui.cv(6))); break; case 7: channel_4.set_cv_pw(U16ShiftRight4(ui.cv(7))); break; #endif // #ifdef OCTAL_ADC default: { // Otherwise, scan the gate. uint8_t gate = ~gate_inputs.value(); bool g_1 = (gate & 1) || midi_handler.gate(0) || settings.arpeggio(0); channel_0.Gate<Channel0>(g_1); channel_1.Gate<Channel1>(g_1); bool g_2 = (gate & 2) || midi_handler.gate(1) || settings.arpeggio(1); channel_2.Gate<Channel2>(g_2); bool g_3 = (gate & 4) || midi_handler.gate(2) || settings.arpeggio(2); channel_3.Gate<Channel3>(g_3); bool g_4 = (gate & 8) || midi_handler.gate(3) || settings.arpeggio(3); channel_4.Gate(g_4); // Read some MIDI bytes, but process them later. if (midi_io.readable()) { midi_in_buffer.NonBlockingWrite(midi_io.ImmediateRead()); } } break; } ++audio_interrupt_count; } inline void Init() { SysInit(); sync_switch.set_direction(INPUT); sync_switch.set_mode(PORT_MODE_PULL_UP); gate_inputs.set_direction(INPUT); gate_inputs.set_mode(PORT_MODE_PULL_UP); ui.Init(); // Reset to factory default if any switch is pressed during boot. settings.Init(!(Switches::Read() & 8)); midi_io.Init(); midi_handler.Init(); audio_dac.Init(); audio_dac.Strobe(); audio_dac.Overwrite(0x1f); audio_dac.Overwrite(0xff); channel_0.Init<Channel0, Channel0Timer>(); channel_1.Init<Channel1, Channel1Timer>(); channel_2.Init<Channel2, Channel2Timer>(); channel_3.Init<Channel3, Channel3Timer>(); channel_4.Init(); adc_acquisition.Init(); audio_rate_timer.set_prescaler(TIMER_PRESCALER_CLK); audio_rate_timer.set_period(665); // 48kHz audio_rate_timer.set_mode(TIMER_MODE_NORMAL); audio_rate_timer.EnableOverflowInterrupt(2); } int main(void) { Init(); while (1) { // Render audio. channel_4.Render(); // Scan MIDI in. while (midi_in_buffer.readable()) { midi_parser.PushByte(midi_in_buffer.ImmediateRead()); } // Do some UI stuff. if (audio_interrupt_count >= 48) { audio_interrupt_count -= 48; uint8_t gate = ~gate_inputs.value(); // Detect new notes and forward to UI subsystem for calibration functions. uint8_t mask = 1; for (uint8_t channel = 0; channel < kNumChannels; ++channel) { if ((gate & mask) && !(ui.gate() & mask)) { settings.StepArpeggio(channel); } mask <<= 1; } ui.set_gate(gate); ui.Poll(); } // Check the status of the sync switch if (sync_switch.value()) { Channel1Timer::EnableOverflowInterrupt(3); } else { Channel1Timer::DisableOverflowInterrupt(); } } }
Report a bug