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 3 months
by
Olivier Gillet
digital_oscillator.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/>. // // ----------------------------------------------------------------------------- // // Digital oscillator generated from a timer. #include "edges/digital_oscillator.h" #include "avrlibx/utils/op.h" #include "edges/audio_buffer.h" #include "edges/resources.h" namespace edges { static const uint8_t kMaxZone = 7; static const int16_t kOctave = 12 * 128; static const int16_t kPitchTableStart = 116 * 128; using namespace avrlibx; #define UPDATE_PHASE \ phase = U24Add(phase, phase_increment); #define BEGIN_SAMPLE_LOOP \ uint24_t phase; \ uint24_t phase_increment; \ phase_increment.integral = phase_increment_.integral; \ phase_increment.fractional = phase_increment_.fractional; \ phase.integral = phase_.integral; \ phase.fractional = phase_.fractional; \ uint8_t size = kAudioBlockSize; \ while (size--) { #define END_SAMPLE_LOOP \ } \ phase_.integral = phase.integral; \ phase_.fractional = phase.fractional; void DigitalOscillator::ComputePhaseIncrement() { int16_t ref_pitch = pitch_ - kPitchTableStart; uint8_t num_shifts = shape_ >= OSC_PITCHED_NOISE ? 0 : 1; while (ref_pitch < 0) { ref_pitch += kOctave; ++num_shifts; } uint24_t increment; uint16_t pitch_lookup_index_integral = U16ShiftRight4(ref_pitch); uint8_t pitch_lookup_index_fractional = U8ShiftLeft4(ref_pitch); uint16_t increment16 = ResourcesManager::Lookup<uint16_t, uint16_t>( lut_res_oscillator_increments, pitch_lookup_index_integral); uint16_t increment16_next = ResourcesManager::Lookup<uint16_t, uint16_t>( lut_res_oscillator_increments, pitch_lookup_index_integral + 1); increment.integral = increment16 + U16U8MulShift8( increment16_next - increment16, pitch_lookup_index_fractional); increment.fractional = 0; while (num_shifts--) { increment = U24ShiftRight(increment); } note_ = U15ShiftRight7(pitch_); if (note_ < 12) { note_ = 12; } phase_increment_ = increment; } void DigitalOscillator::Render() { if (gate_) { ComputePhaseIncrement(); } while (audio_buffer.writable() >= kAudioBlockSize) { if (!gate_) { RenderSilence(); } else { RenderFn fn; ResourcesManager::Load(fn_table_, shape_, &fn); (this->*fn)(); } } } static inline uint8_t InterpolateSample( const prog_uint8_t* table, uint16_t phase) { uint8_t result; uint8_t work; asm( "movw r30, %A2" "\n\t" // copy base address to r30:r31 "mov %1, %A3" "\n\t" // duplicate phase increment "add %1, %A3" "\n\t" // duplicate "adc r30, %B3" "\n\t" // duplicate "adc r31, r1" "\n\t" // duplicate "add r30, %B3" "\n\t" // duplicate "adc r31, r1" "\n\t" // duplicate "lpm %0, z+" "\n\t" // load sample[n] "lpm r1, z+" "\n\t" // load sample[n+1] "mul %1, r1" "\n\t" // multiply second sample by phaseL "movw r30, r0" "\n\t" // result to accumulator "com %1" "\n\t" // 255 - phaseL -> phaseL "mul %1, %0" "\n\t" // multiply first sample by phaseL "add r30, r0" "\n\t" // accumulate L "adc r31, r1" "\n\t" // accumulate H "eor r1, r1" "\n\t" // reset r1 after multiplication "mov %0, r31" "\n\t" // use sum H as output : "=r" (result), "=r" (work) : "r" (table), "r" (phase) : "r30", "r31" ); return result; } static inline uint16_t InterpolateSample16( const prog_uint8_t* table, uint16_t phase) { uint16_t result; uint8_t work; asm( "movw r30, %A2" "\n\t" // copy base address to r30:r31 "mov %1, %A3" "\n\t" // duplicate phase increment "add %1, %A3" "\n\t" // duplicate "adc r30, %B3" "\n\t" // duplicate "adc r31, r1" "\n\t" // duplicate "add r30, %B3" "\n\t" // duplicate "adc r31, r1" "\n\t" // duplicate "lpm %0, z+" "\n\t" // load sample[n] "lpm r1, z+" "\n\t" // load sample[n+1] "mul %1, r1" "\n\t" // multiply second sample by phaseL "movw r30, r0" "\n\t" // result to accumulator "com %1" "\n\t" // 255 - phaseL -> phaseL "mul %1, %0" "\n\t" // multiply first sample by phaseL "add r30, r0" "\n\t" // accumulate L "adc r31, r1" "\n\t" // accumulate H "eor r1, r1" "\n\t" // reset r1 after multiplication "movw %0, r30" "\n\t" // use sum H as output : "=r" (result), "=r" (work) : "r" (table), "r" (phase) : "r30", "r31" ); return result; } static inline uint16_t InterpolateTwoTables( const prog_uint8_t* table_a, const prog_uint8_t* table_b, uint16_t phase, uint8_t gain_a, uint8_t gain_b) { uint16_t result = 0; result += U8U8Mul(InterpolateSample(table_a, phase), gain_a); result += U8U8Mul(InterpolateSample(table_b, phase), gain_b); return result; } void DigitalOscillator::RenderSilence() { uint8_t size = kAudioBlockSize; while (size--) { audio_buffer.Overwrite(2048); } } void DigitalOscillator::RenderSine() { uint16_t aux_phase_increment = pgm_read_word( lut_res_bitcrusher_increments + cv_pw_); BEGIN_SAMPLE_LOOP UPDATE_PHASE aux_phase_ += aux_phase_increment; if (aux_phase_ < aux_phase_increment || !aux_phase_increment) { sample_ = InterpolateSample16( wav_res_bandlimited_triangle_6, phase.integral); } audio_buffer.Overwrite(sample_ >> 4); END_SAMPLE_LOOP } void DigitalOscillator::RenderBandlimitedTriangle() { uint8_t balance_index = U8Swap4(note_ - 12); uint8_t gain_2 = balance_index & 0xf0; uint8_t gain_1 = ~gain_2; uint8_t wave_index = balance_index & 0xf; uint8_t base_resource_id = (shape_ == OSC_NES_TRIANGLE) ? WAV_RES_BANDLIMITED_NES_TRIANGLE_0 : WAV_RES_BANDLIMITED_TRIANGLE_0; const prog_uint8_t* wave_1 = waveform_table[base_resource_id + wave_index]; wave_index = U8AddClip(wave_index, 1, kMaxZone); const prog_uint8_t* wave_2 = waveform_table[base_resource_id + wave_index]; BEGIN_SAMPLE_LOOP UPDATE_PHASE uint16_t sample = InterpolateTwoTables( wave_1, wave_2, phase.integral, gain_1, gain_2); audio_buffer.Overwrite(sample >> 4); END_SAMPLE_LOOP } void DigitalOscillator::RenderNoiseNES() { uint16_t rng_state = rng_state_; uint16_t sample = sample_; BEGIN_SAMPLE_LOOP phase = U24Add(phase, phase_increment); if (phase.integral < phase_increment.integral) { uint8_t tap = rng_state >> 1; if (shape_ == OSC_NES_NOISE_SHORT) { tap >>= 5; } uint8_t random_bit = (rng_state ^ tap) & 1; rng_state >>= 1; if (random_bit) { rng_state |= 0x4000; sample = 0x0300; } else { sample = 0x0cff; } } audio_buffer.Overwrite(sample); END_SAMPLE_LOOP rng_state_ = rng_state; sample_ = sample; } void DigitalOscillator::RenderNoise() { uint16_t rng_state = rng_state_; uint16_t sample = sample_; BEGIN_SAMPLE_LOOP phase = U24Add(phase, phase_increment); if (phase.integral < phase_increment.integral) { rng_state = (rng_state >> 1) ^ (-(rng_state & 1) & 0xb400); sample = rng_state & 0x0fff; sample = 512 + ((sample * 3) >> 2); } audio_buffer.Overwrite(sample); END_SAMPLE_LOOP rng_state_ = rng_state; sample_ = sample; } /* static */ const DigitalOscillator::RenderFn DigitalOscillator::fn_table_[] PROGMEM = { &DigitalOscillator::RenderBandlimitedTriangle, &DigitalOscillator::RenderBandlimitedTriangle, &DigitalOscillator::RenderNoise, &DigitalOscillator::RenderNoiseNES, &DigitalOscillator::RenderNoiseNES, &DigitalOscillator::RenderSine, }; } // namespace shruthi