Skip to content
Permalink
master
Go to file
 
 
Cannot retrieve contributors at this time
executable file 311 lines (269 sloc) 8.82 KB
// Copyright 2012 Emilie Gillet.
//
// Author: Emilie Gillet (emilie.o.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.
#include <stm32f10x_conf.h>
#include <algorithm>
#include "stmlib/utils/dsp.h"
#include "stmlib/utils/ring_buffer.h"
#include "stmlib/system/system_clock.h"
#include "stmlib/system/uid.h"
#include "braids/drivers/adc.h"
#include "braids/drivers/dac.h"
#include "braids/drivers/debug_pin.h"
#include "braids/drivers/gate_input.h"
#include "braids/drivers/internal_adc.h"
#include "braids/drivers/system.h"
#include "braids/envelope.h"
#include "braids/macro_oscillator.h"
#include "braids/quantizer.h"
#include "braids/signature_waveshaper.h"
#include "braids/vco_jitter_source.h"
#include "braids/ui.h"
#include "braids/quantizer_scales.h"
// #define PROFILE_RENDER 1
using namespace braids;
using namespace std;
using namespace stmlib;
const size_t kNumBlocks = 4;
const size_t kBlockSize = 24;
MacroOscillator osc;
Envelope envelope;
Adc adc;
Dac dac;
DebugPin debug_pin;
GateInput gate_input;
InternalAdc internal_adc;
Quantizer quantizer;
SignatureWaveshaper ws;
System sys;
VcoJitterSource jitter_source;
Ui ui;
uint8_t current_scale = 0xff;
size_t current_sample;
volatile size_t playback_block;
volatile size_t render_block;
int16_t audio_samples[kNumBlocks][kBlockSize];
uint8_t sync_samples[kNumBlocks][kBlockSize];
bool trigger_detected_flag;
volatile bool trigger_flag;
uint16_t trigger_delay;
extern "C" {
void HardFault_Handler(void) { while (1); }
void MemManage_Handler(void) { while (1); }
void BusFault_Handler(void) { while (1); }
void UsageFault_Handler(void) { while (1); }
void NMI_Handler(void) { }
void SVC_Handler(void) { }
void DebugMon_Handler(void) { }
void PendSV_Handler(void) { }
}
extern "C" {
void SysTick_Handler() {
ui.Poll();
}
void TIM1_UP_IRQHandler(void) {
if (!(TIM1->SR & TIM_IT_Update)) {
return;
}
TIM1->SR = (uint16_t)~TIM_IT_Update;
dac.Write(-audio_samples[playback_block][current_sample] + 32768);
bool trigger_detected = gate_input.raised();
sync_samples[playback_block][current_sample] = trigger_detected;
trigger_detected_flag = trigger_detected_flag | trigger_detected;
current_sample = current_sample + 1;
if (current_sample >= kBlockSize) {
current_sample = 0;
playback_block = (playback_block + 1) % kNumBlocks;
}
bool adc_scan_cycle_complete = adc.PipelinedScan();
if (adc_scan_cycle_complete) {
ui.UpdateCv(adc.channel(0), adc.channel(1), adc.channel(2), adc.channel(3));
if (trigger_detected_flag) {
trigger_delay = settings.trig_delay()
? (1 << settings.trig_delay()) : 0;
++trigger_delay;
trigger_detected_flag = false;
}
if (trigger_delay) {
--trigger_delay;
if (trigger_delay == 0) {
trigger_flag = true;
}
}
}
}
}
void Init() {
sys.Init(F_CPU / 96000 - 1, true);
settings.Init();
ui.Init();
system_clock.Init();
adc.Init(false);
gate_input.Init();
#ifdef PROFILE_RENDER
debug_pin.Init();
#endif
dac.Init();
osc.Init();
quantizer.Init();
internal_adc.Init();
for (size_t i = 0; i < kNumBlocks; ++i) {
fill(&audio_samples[i][0], &audio_samples[i][kBlockSize], 0);
fill(&sync_samples[i][0], &sync_samples[i][kBlockSize], 0);
}
playback_block = kNumBlocks / 2;
render_block = 0;
current_sample = 0;
envelope.Init();
ws.Init(GetUniqueId(1));
jitter_source.Init();
sys.StartTimers();
}
const uint16_t bit_reduction_masks[] = {
0xc000,
0xe000,
0xf000,
0xf800,
0xff00,
0xfff0,
0xffff };
const uint16_t decimation_factors[] = { 24, 12, 6, 4, 3, 2, 1 };
void RenderBlock() {
static int16_t previous_pitch = 0;
static int16_t previous_shape = 0;
static uint16_t gain_lp;
#ifdef PROFILE_RENDER
debug_pin.High();
#endif
envelope.Update(
settings.GetValue(SETTING_AD_ATTACK) * 8,
settings.GetValue(SETTING_AD_DECAY) * 8);
uint32_t ad_value = envelope.Render();
if (ui.paques()) {
osc.set_shape(MACRO_OSC_SHAPE_QUESTION_MARK);
} else if (settings.meta_modulation()) {
int16_t shape = adc.channel(3);
shape -= settings.data().fm_cv_offset;
if (shape > previous_shape + 2 || shape < previous_shape - 2) {
previous_shape = shape;
} else {
shape = previous_shape;
}
shape = MACRO_OSC_SHAPE_LAST * shape >> 11;
shape += settings.shape();
if (shape >= MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META) {
shape = MACRO_OSC_SHAPE_LAST_ACCESSIBLE_FROM_META;
} else if (shape <= 0) {
shape = 0;
}
MacroOscillatorShape osc_shape = static_cast<MacroOscillatorShape>(shape);
osc.set_shape(osc_shape);
ui.set_meta_shape(osc_shape);
} else {
osc.set_shape(settings.shape());
}
// Set timbre and color: CV + internal modulation.
uint16_t parameters[2];
for (uint16_t i = 0; i < 2; ++i) {
int32_t value = settings.adc_to_parameter(i, adc.channel(i));
Setting ad_mod_setting = i == 0 ? SETTING_AD_TIMBRE : SETTING_AD_COLOR;
value += ad_value * settings.GetValue(ad_mod_setting) >> 5;
CONSTRAIN(value, 0, 32767);
parameters[i] = value;
}
osc.set_parameters(parameters[0], parameters[1]);
// Apply hysteresis to ADC reading to prevent a single bit error to move
// the quantized pitch up and down the quantization boundary.
int32_t pitch = quantizer.Process(
settings.adc_to_pitch(adc.channel(2)),
(60 + settings.quantizer_root()) << 7);
if (!settings.meta_modulation()) {
pitch += settings.adc_to_fm(adc.channel(3));
}
// Check if the pitch has changed to cause an auto-retrigger
int32_t pitch_delta = pitch - previous_pitch;
if (settings.data().auto_trig &&
(pitch_delta >= 0x40 || -pitch_delta >= 0x40)) {
trigger_detected_flag = true;
}
previous_pitch = pitch;
pitch += jitter_source.Render(settings.vco_drift());
pitch += internal_adc.value() >> 8;
pitch += ad_value * settings.GetValue(SETTING_AD_FM) >> 7;
if (pitch > 16383) {
pitch = 16383;
} else if (pitch < 0) {
pitch = 0;
}
if (settings.vco_flatten()) {
pitch = Interpolate88(lut_vco_detune, pitch << 2);
}
osc.set_pitch(pitch + settings.pitch_transposition());
if (trigger_flag) {
osc.Strike();
envelope.Trigger(ENV_SEGMENT_ATTACK);
ui.StepMarquee();
trigger_flag = false;
}
uint8_t* sync_buffer = sync_samples[render_block];
int16_t* render_buffer = audio_samples[render_block];
if (settings.GetValue(SETTING_AD_VCA) != 0
|| settings.GetValue(SETTING_AD_TIMBRE) != 0
|| settings.GetValue(SETTING_AD_COLOR) != 0
|| settings.GetValue(SETTING_AD_FM) != 0) {
memset(sync_buffer, 0, kBlockSize);
}
osc.Render(sync_buffer, render_buffer, kBlockSize);
// Copy to DAC buffer with sample rate and bit reduction applied.
int16_t sample = 0;
size_t decimation_factor = decimation_factors[settings.data().sample_rate];
uint16_t bit_mask = bit_reduction_masks[settings.data().resolution];
int32_t gain = settings.GetValue(SETTING_AD_VCA) ? ad_value : 65535;
uint16_t signature = settings.signature() * settings.signature() * 4095;
for (size_t i = 0; i < kBlockSize; ++i) {
if ((i % decimation_factor) == 0) {
sample = render_buffer[i] & bit_mask;
}
sample = sample * gain_lp >> 16;
gain_lp += (gain - gain_lp) >> 4;
int16_t warped = ws.Transform(sample);
render_buffer[i] = Mix(sample, warped, signature);
}
render_block = (render_block + 1) % kNumBlocks;
#ifdef PROFILE_RENDER
debug_pin.Low();
#endif
}
int main(void) {
Init();
while (1) {
if (current_scale != settings.GetValue(SETTING_QUANTIZER_SCALE)) {
current_scale = settings.GetValue(SETTING_QUANTIZER_SCALE);
quantizer.Configure(scales[current_scale]);
}
while (render_block != playback_block) {
RenderBlock();
}
ui.DoEvents();
}
}