Browse Source

add AudibleInstruments.Marbles/Plaits/Stages

pull/1639/head
bsp2 6 years ago
parent
commit
e7392d5655
100 changed files with 19395 additions and 38 deletions
  1. +0
    -12
      .gitmodules
  2. +12
    -0
      plugins/community/repos/AudibleInstruments/CHANGELOG.md
  3. +0
    -26
      plugins/community/repos/AudibleInstruments/README.md
  4. +0
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/__init__.py
  5. +0
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/bootloader/__init__.py
  6. +317
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/bootloader/bootloader.cc
  7. +47
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/bootloader/makefile
  8. +87
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/clock_self_patching_detector.h
  9. +102
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/cv_reader.cc
  10. +129
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/cv_reader.h
  11. +213
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/cv_reader_channel.h
  12. +162
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/adc.cc
  13. +81
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/adc.h
  14. +122
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/clock_inputs.cc
  15. +75
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/clock_inputs.h
  16. +160
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/dac.cc
  17. +82
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/dac.h
  18. +76
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/debug_pin.h
  19. +94
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/debug_port.h
  20. +86
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/gate_outputs.h
  21. +86
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/leds.cc
  22. +75
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/leds.h
  23. +62
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/rng.h
  24. +79
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/switches.cc
  25. +87
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/switches.h
  26. +48
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/system.cc
  27. +50
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/system.h
  28. BIN
      plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/Marbles.xlsx
  29. +2483
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/panel/marbles_v70.ai
  30. BIN
      plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/panel/marbles_v70.dwg
  31. BIN
      plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/pcb/marbles_v70.brd
  32. BIN
      plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/pcb/marbles_v70.sch
  33. +109
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/io_buffer.h
  34. +57
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/makefile
  35. +447
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/marbles.cc
  36. +82
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/note_filter.h
  37. +40
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp.h
  38. +107
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_divider.h
  39. +312
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_extractor.cc
  40. +132
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_extractor.h
  41. +63
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_generator.h
  42. +132
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/slave_ramp.h
  43. +115
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/discrete_distribution_quantizer.cc
  44. +75
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/discrete_distribution_quantizer.h
  45. +182
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/distributions.h
  46. +88
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/lag_processor.cc
  47. +61
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/lag_processor.h
  48. +145
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/output_channel.cc
  49. +139
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/output_channel.h
  50. +138
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/quantizer.cc
  51. +122
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/quantizer.h
  52. +65
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/random_generator.h
  53. +228
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/random_sequence.h
  54. +82
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/random_stream.h
  55. +414
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/t_generator.cc
  56. +206
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/t_generator.h
  57. +184
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/x_y_generator.cc
  58. +123
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/random/x_y_generator.h
  59. +4767
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/resources.cc
  60. +226
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/resources.h
  61. +0
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/resources/__init__.py
  62. +109
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/resources/lookup_tables.py
  63. +79
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/resources/resources.py
  64. +149
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/scale_recorder.h
  65. +246
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/settings.cc
  66. +152
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/settings.h
  67. +227
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/test/fixtures.h
  68. +50
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/test/makefile
  69. +721
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/test/marbles_test.cc
  70. +84
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/test/ramp_checker.h
  71. +533
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ui.cc
  72. +146
    -0
      plugins/community/repos/AudibleInstruments/eurorack/marbles/ui.h
  73. +0
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/__init__.py
  74. +267
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/bootloader/bootloader.cc
  75. +46
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/bootloader/makefile
  76. +132
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/audio_dac.cc
  77. +73
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/audio_dac.h
  78. +193
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/cv_adc.cc
  79. +74
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/cv_adc.h
  80. +72
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/debug_pin.h
  81. +62
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/debug_port.cc
  82. +67
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/debug_port.h
  83. +111
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/firmware_update_adc.cc
  84. +60
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/firmware_update_adc.h
  85. +114
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/leds.cc
  86. +72
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/leds.h
  87. +87
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/normalization_probe.h
  88. +111
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/pots_adc.cc
  89. +71
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/pots_adc.h
  90. +72
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/switches.cc
  91. +82
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/switches.h
  92. +195
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/analog_bass_drum.h
  93. +201
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/analog_snare_drum.h
  94. +259
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/hi_hat.h
  95. +249
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/synthetic_bass_drum.h
  96. +198
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/synthetic_snare_drum.h
  97. +55
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/dsp.h
  98. +152
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/engine/additive_engine.cc
  99. +72
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/engine/additive_engine.h
  100. +96
    -0
      plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/engine/bass_drum_engine.cc

+ 0
- 12
.gitmodules View File

@@ -1,12 +0,0 @@
[submodule "ext/nanovg"]
path = dep/nanovg
url = https://github.com/memononen/nanovg.git
[submodule "ext/nanosvg"]
path = dep/nanosvg
url = https://github.com/memononen/nanosvg.git
[submodule "ext/osdialog"]
path = dep/osdialog
url = https://github.com/AndrewBelt/osdialog.git
[submodule "ext/oui-blendish"]
path = dep/oui-blendish
url = https://github.com/AndrewBelt/oui-blendish.git

+ 12
- 0
plugins/community/repos/AudibleInstruments/CHANGELOG.md View File

@@ -0,0 +1,12 @@

### 0.6.3 (2018-10-10)

- Added Segment Generator

### 0.6.2 (2018-10-09)

- Added Random Sampler from Audible Instruments Preview

### 0.6.1 (2018-09-12)

- Added Macro Oscillator 2 from Audible Instruments Preview

+ 0
- 26
plugins/community/repos/AudibleInstruments/README.md View File

@@ -16,28 +16,20 @@ After checking out AudibleInstruments in the `plugins/` directory, get external

### Macro Oscillator
Based on [Braids](https://mutable-instruments.net/modules/braids), [Manual](https://mutable-instruments.net/modules/braids/manual/)

![Macro Oscillator](https://vcvrack.com/images/AudibleInstruments/macro%20oscillator.png)
- Sync input doesn't work
- More settings could be supported

### Modal Synthesizer
Based on [Elements](https://mutable-instruments.net/modules/elements), [Manual](https://mutable-instruments.net/modules/elements/manual/)

![Modal Synthesizer](https://vcvrack.com/images/AudibleInstruments/modal%20synthesizer.png)

### Tidal Modulator
Based on [Tides](https://mutable-instruments.net/modules/tides), [Manual](https://mutable-instruments.net/modules/tides/manual/)

![Tidal Modulator](https://vcvrack.com/images/AudibleInstruments/tidal%20modulator.png)

### Wavetable Oscillator
Based on [Sheep](https://mutable-instruments.net/modules/tides/firmware/) (Tides alternative firmware)

### Texture Synthesizer
Based on [Clouds](https://mutable-instruments.net/modules/clouds), [Manual](https://mutable-instruments.net/modules/clouds/manual/)

![Texture Synthesizer](https://vcvrack.com/images/AudibleInstruments/texture%20synthesizer.png)
- edit buttons and lights
- freeze button
- right-click context menus to replace menu diving
@@ -45,46 +37,30 @@ Based on [Clouds](https://mutable-instruments.net/modules/clouds), [Manual](http
### Meta Modulator
Based on [Warps](https://mutable-instruments.net/modules/warps), [Manual](https://mutable-instruments.net/modules/warps/manual/)

![Meta Modulator](https://vcvrack.com/images/AudibleInstruments/meta%20modulator.png)

### Resonator
Based on [Rings](https://mutable-instruments.net/modules/rings), [Manual](https://mutable-instruments.net/modules/rings/manual/)

![Resonator](https://vcvrack.com/images/AudibleInstruments/resonator.png)

### Keyframer/Mixer
Based on [Frames](https://mutable-instruments.net/modules/frames), [Manual](https://mutable-instruments.net/modules/frames/manual/)

### Multiples
Based on [Links](https://mutable-instruments.net/modules/links), [Manual](https://mutable-instruments.net/modules/links/manual/)

![Multiples](https://vcvrack.com/images/AudibleInstruments/multiples.png)

### Utilities
Based on [Kinks](https://mutable-instruments.net/modules/kinks), [Manual](https://mutable-instruments.net/modules/kinks/manual/)

![Utilities](https://vcvrack.com/images/AudibleInstruments/utilities.png)

### Mixer
Based on [Shades](https://mutable-instruments.net/modules/shades), [Manual](https://mutable-instruments.net/modules/shades/manual/)

![Mixer](https://vcvrack.com/images/AudibleInstruments/mixer.png)

### Bernoulli Gate
Based on [Branches](https://mutable-instruments.net/modules/branches), [Manual](https://mutable-instruments.net/modules/branches/manual/)

![Bernoulli Gate](https://vcvrack.com/images/AudibleInstruments/bernoulli%20gate.png)

### Quad VC-polarizer
Based on [Blinds](https://mutable-instruments.net/modules/blinds), [Manual](https://mutable-instruments.net/modules/blinds/manual/)

![Quad VC-polarizer](https://vcvrack.com/images/AudibleInstruments/quad%20VC-polarizer.png)

### Quad VCA
Based on [Veils](https://mutable-instruments.net/modules/veils), [Manual](https://mutable-instruments.net/modules/veils/manual/)

![Quad VCA](https://vcvrack.com/images/AudibleInstruments/quad%20VCA.png)


## Not yet ported

@@ -113,9 +89,7 @@ Based on [Veils](https://mutable-instruments.net/modules/veils), [Manual](https:

### [Edges](https://mutable-instruments.net/modules/edges)
[Manual](https://mutable-instruments.net/modules/edges/manual/)
- GPL, will not port

### [Grids](https://mutable-instruments.net/modules/grids)
[Manual](https://mutable-instruments.net/modules/grids/manual/)
- GPL, will not port


+ 0
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/__init__.py View File


+ 0
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/bootloader/__init__.py View File


+ 317
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/bootloader/bootloader.cc View File

@@ -0,0 +1,317 @@
// Copyright 2014 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.

#include "stmlib/system/bootloader_utils.h"
#include "stmlib/system/system_clock.h"

#include "marbles/drivers/adc.h"
#include "marbles/drivers/dac.h"
#include "marbles/drivers/leds.h"
#include "marbles/drivers/switches.h"
#include "marbles/drivers/system.h"

#include "stm_audio_bootloader/qpsk/packet_decoder.h"
#include "stm_audio_bootloader/qpsk/demodulator.h"

#include <cstring>

using namespace marbles;
using namespace stmlib;
using namespace stm_audio_bootloader;

const double kSampleRate = 48000.0;
const double kModulationRate = 6000.0;
const double kBitRate = 12000.0;

const uint32_t kStartAddress = 0x08008000;

Adc adc;
Dac dac;
Leds leds;
Switches switches;
PacketDecoder decoder;
Demodulator demodulator;

int __errno;

void UpdateLeds();
volatile bool switch_released = false;

// Default interrupt handlers.
extern "C" {

void NMI_Handler() { }
void HardFault_Handler() { while (1); }
void MemManage_Handler() { while (1); }
void BusFault_Handler() { while (1); }
void UsageFault_Handler() { while (1); }
void SVC_Handler() { }
void DebugMon_Handler() { }
void PendSV_Handler() { }

void SysTick_Handler() {
IWDG_ReloadCounter();
system_clock.Tick();
switches.Debounce();
if (switches.released(SWITCH_T_DEJA_VU)) {
switch_released = true;
}
UpdateLeds();
}

}

enum UiState {
UI_STATE_WAITING,
UI_STATE_RECEIVING,
UI_STATE_ERROR,
UI_STATE_WRITING
};

volatile UiState ui_state;
volatile int32_t peak;

void UpdateLeds() {
leds.Clear();
switch (ui_state) {
case UI_STATE_WAITING:
leds.set(
LED_T_DEJA_VU,
system_clock.milliseconds() & 128 ? LED_COLOR_GREEN : 0);
leds.set(
LED_X_DEJA_VU,
system_clock.milliseconds() & 128 ? 0 : LED_COLOR_GREEN);
break;

case UI_STATE_RECEIVING:
leds.set(
LED_T_DEJA_VU,
system_clock.milliseconds() & 32 ? LED_COLOR_GREEN : 0);
leds.set(
LED_X_DEJA_VU,
system_clock.milliseconds() & 32 ? 0 : LED_COLOR_GREEN);
break;

case UI_STATE_ERROR:
{
bool on = system_clock.milliseconds() & 256;
for (int i = 0; i < LED_LAST; ++i) {
leds.set(Led(i), on ? LED_COLOR_RED : 0);
}
}
break;

case UI_STATE_WRITING:
{
for (int i = 0; i < LED_LAST; ++i) {
leds.set(Led(i), LED_COLOR_GREEN);
}
}
break;
}
if (ui_state != UI_STATE_WRITING) {
uint8_t pwm = system_clock.milliseconds() & 15;
if (peak < 8192) {
leds.set(
LED_T_RANGE,
(peak >> 9) > pwm ? LED_COLOR_GREEN : 0);
} else if (peak < 16384) {
leds.set(
LED_T_RANGE,
((peak - 8192) >> 9) >= pwm ? LED_COLOR_YELLOW : LED_COLOR_GREEN);
} else if (peak < 16384 + 8192) {
leds.set(
LED_T_RANGE,
((peak - 16384 - 8192) >> 9) >= pwm ?
LED_COLOR_RED : LED_COLOR_YELLOW);
} else {
leds.set(LED_T_RANGE, LED_COLOR_RED);
}
}
leds.Write();
}

int32_t dc_offset = 0;
int32_t gain_pot = 16;
size_t discard_samples = 8000;

IOBuffer::Block block;

IOBuffer::Slice FillBuffer(size_t size) {
adc.Convert();
if (!discard_samples) {
// Scan gain pot.
gain_pot = (adc.value(ADC_GROUP_POT) + 4095 * gain_pot) >> 12;
int32_t gain = ((gain_pot >> 1) * gain_pot >> 21) + 128;
// Extract sample. Note: there's a DC offset :/
int32_t sample = 32768 - static_cast<int32_t>(adc.value(ADC_GROUP_CV));
dc_offset += (sample - (dc_offset >> 15));
sample = (sample - (dc_offset >> 15)) * gain >> 8; // 0.5x to 4x
CONSTRAIN(sample, -32768, 32767);
// Update peak-meter
int32_t rect = sample > 0 ? sample : -sample;
peak = rect > peak ? rect : (rect + 32767 * peak) >> 15;

// Write to DAC for monitoring
block.cv_output[0][0] = 32767 - sample;
block.cv_output[1][0] = 32767 - sample;
block.cv_output[2][0] = 32767 - sample;
block.cv_output[3][0] = 32767 - sample;
demodulator.PushSample(2048 + (sample >> 4));
} else {
--discard_samples;
}
IOBuffer::Slice s;
s.block = &block;
s.frame_index = 0;
return s;
}

static size_t current_address;
static uint16_t packet_index;
static uint32_t kSectorBaseAddress[] = {
0x08000000,
0x08004000,
0x08008000,
0x0800C000,
0x08010000,
0x08020000,
0x08040000,
0x08060000,
0x08080000,
0x080A0000,
0x080C0000,
0x080E0000
};
const uint32_t kBlockSize = 16384;
const uint16_t kPacketsPerBlock = ::kBlockSize / kPacketSize;
uint8_t rx_buffer[::kBlockSize];

void ProgramPage(const uint8_t* data, size_t size) {
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR |
FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
for (int32_t i = 0; i < 12; ++i) {
if (current_address == kSectorBaseAddress[i]) {
FLASH_EraseSector(i * 8, VoltageRange_3);
}
}
const uint32_t* words = static_cast<const uint32_t*>(
static_cast<const void*>(data));
for (size_t written = 0; written < size; written += 4) {
FLASH_ProgramWord(current_address, *words++);
current_address += 4;
}
}

void InitializeReception() {
decoder.Init(20000);
demodulator.Init(
kModulationRate / kSampleRate * 4294967296.0,
kSampleRate / kModulationRate,
2.0 * kSampleRate / kBitRate);
demodulator.SyncCarrier(true);
decoder.Reset();
current_address = kStartAddress;
packet_index = 0;
ui_state = UI_STATE_WAITING;
}

void Init() {
System sys;
switches.Init();
sys.Init(false);
system_clock.Init();
adc.Init(true);
dac.Init(48000, 1);
leds.Init();
sys.StartTimers();
dac.Start(&FillBuffer);
}

int main(void) {
Init();
InitializeReception();

bool exit_updater = !switches.pressed_immediate(SWITCH_T_DEJA_VU);
while (!exit_updater) {
bool error = false;

if (demodulator.state() == DEMODULATOR_STATE_OVERFLOW) {
error = true;
} else {
demodulator.ProcessAtLeast(32);
}

while (demodulator.available() && !error && !exit_updater) {
uint8_t symbol = demodulator.NextSymbol();
PacketDecoderState state = decoder.ProcessSymbol(symbol);
switch (state) {
case PACKET_DECODER_STATE_OK:
{
ui_state = UI_STATE_RECEIVING;
memcpy(
rx_buffer + (packet_index % kPacketsPerBlock) * kPacketSize,
decoder.packet_data(),
kPacketSize);
++packet_index;
if ((packet_index % kPacketsPerBlock) == 0) {
ui_state = UI_STATE_WRITING;
ProgramPage(rx_buffer, ::kBlockSize);
decoder.Reset();
demodulator.SyncCarrier(false);
} else {
decoder.Reset();
demodulator.SyncDecision();
}
}
break;
case PACKET_DECODER_STATE_ERROR_SYNC:
case PACKET_DECODER_STATE_ERROR_CRC:
error = true;
break;
case PACKET_DECODER_STATE_END_OF_TRANSMISSION:
exit_updater = true;
break;
default:
break;
}
}

if (error) {
ui_state = UI_STATE_ERROR;
switch_released = false;
while (!switch_released); // Polled in ISR
InitializeReception();
}
}
adc.DeInit();
Uninitialize();
JumpTo(kStartAddress);
while (1) { }
}

+ 47
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/bootloader/makefile View File

@@ -0,0 +1,47 @@
# Copyright 2014 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.

# System specifications
F_CRYSTAL = 8000000L
F_CPU = 168000000L
SYSCLOCK = SYSCLK_FREQ_168MHz
FAMILY = f4xx
# USB = enabled

# Preferred upload command
UPLOAD_COMMAND = upload_jtag_erase_first

# Packages to build
TARGET = marbles_bootloader
PACKAGES = marbles/bootloader \
marbles/drivers \
stm_audio_bootloader/qpsk \
stmlib/dsp \
stmlib/utils \
stmlib/system
RESOURCES = marbles/resources

TOOLCHAIN_PATH ?= /usr/local/arm-4.8.3/

include stmlib/makefile.inc

+ 87
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/clock_self_patching_detector.h View File

@@ -0,0 +1,87 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Class for detecting if the t1 or t2 gate outputs are patched into the X
// clock input. This is done by comparing the number of synchronous transitions
// on the gate ouputs and the clock input. A small margin of error is allowed
// because of acquisition delays.

#ifndef MARBLES_CLOCK_SELF_PATCHING_DETECTOR_H_
#define MARBLES_CLOCK_SELF_PATCHING_DETECTOR_H_

#include "stmlib/stmlib.h"

#include "marbles/io_buffer.h"

namespace marbles {

class ClockSelfPatchingDetector {
public:
ClockSelfPatchingDetector() { }
~ClockSelfPatchingDetector() { }
void Init(size_t index) {
index_ = index;
error_streak_ = 0;
match_length_ = 0;
synchronous_transitions_ = 0;
}
size_t Process(IOBuffer::Block* block, size_t size) {
for (size_t i = 0; i < size; ++i) {
if (block->input[1][i] & stmlib::GATE_FLAG_RISING) {
if (match_length_ >= 12) {
++synchronous_transitions_;
}
error_streak_ = 0;
match_length_ = 0;
}
bool output_gate = block->gate_output[index_][i];
bool input_gate = block->input[1][i] & stmlib::GATE_FLAG_HIGH;
if (output_gate != input_gate) {
++error_streak_;
if (error_streak_ >= 6) {
synchronous_transitions_ = 0;
}
} else {
++match_length_;
}
}
return synchronous_transitions_;
}
private:
size_t index_;
size_t error_streak_;
size_t match_length_;
size_t synchronous_transitions_;
DISALLOW_COPY_AND_ASSIGN(ClockSelfPatchingDetector);
};

} // namespace marbles

#endif // MARBLES_CLOCK_SELF_PATCHING_DETECTOR_H_

+ 102
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/cv_reader.cc View File

@@ -0,0 +1,102 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// CV reader.

#include "marbles/cv_reader.h"

#include <algorithm>

#include "stmlib/dsp/dsp.h"

namespace marbles {
using namespace std;
using namespace stmlib;

/* static */
const CvReaderChannel::Settings CvReader::channel_settings_[] = {
// cv_lp | pot_scale | pot_offset | pot_lp | min | max | hysteresis
// ADC_CHANNEL_DEJA_VU_AMOUNT,
{ 0.05f, 1.0f, 0.0f, 0.01f, 0.0f, 1.0f, 0.00f },
// ADC_CHANNEL_X_SPREAD_2 / ADC_CHANNEL_DEJA_VU_LENGTH,
{ 0.05f, 1.0f, 0.0f, 0.01f, 0.0f, 1.0f, 0.00f },
// ADC_CHANNEL_T_RATE,
{ 0.2f, 120.0f, -60.0f, 0.01f, -120.0f, 120.0f, 0.001f },
// ADC_CHANNEL_T_BIAS,
{ 0.05f, 1.05f, -0.025f, 0.01f, 0.0f, 1.0f, 0.00f },
// ADC_CHANNEL_T_JITTER,
{ 0.05f, 1.0f, 0.0f, 0.01f, 0.0f, 1.0f, 0.00f },
// ADC_CHANNEL_X_SPREAD,
{ 0.1f, 1.0f, 0.0f, 0.01f, 0.0f, 1.0f, 0.01f },
// ADC_CHANNEL_X_BIAS,
{ 0.1f, 1.0f, 0.0f, 0.01f, 0.0f, 1.0f, 0.02f },
// ADC_CHANNEL_X_STEPS,
{ 0.05f, 1.0f, 0.0f, 0.01f, 0.0f, 1.0f, 0.02f },
};

void CvReader::Init(CalibrationData* calibration_data) {
calibration_data_ = calibration_data;
adc_.Init(false);
for (int i = 0; i < ADC_CHANNEL_LAST; ++i) {
channel_[i].Init(
&calibration_data_->adc_scale[i],
&calibration_data_->adc_offset[i],
channel_settings_[i]);
}
fill(&attenuverter_[0], &attenuverter_[ADC_CHANNEL_LAST], 1.0f);

// Set virtual attenuverter to 12 o'clock to ignore the non-existing
// CV input for DEJA VU length.
attenuverter_[ADC_CHANNEL_DEJA_VU_LENGTH] = 0.5f;

// Set virtual attenuverter to a little more than 100% to
// compensate for op-amp clipping and get full parameter swing.
attenuverter_[ADC_CHANNEL_DEJA_VU_AMOUNT] = 1.01f;
attenuverter_[ADC_CHANNEL_T_BIAS] = 1.01f;
attenuverter_[ADC_CHANNEL_T_JITTER] = 1.01f;
attenuverter_[ADC_CHANNEL_X_SPREAD] = 1.01f;
attenuverter_[ADC_CHANNEL_X_BIAS] = 1.01f;
attenuverter_[ADC_CHANNEL_X_STEPS] = 1.01f;
}

void CvReader::Copy(uint16_t* output) {
const uint16_t* adc_values = adc_.values();
copy(&adc_values[0], &adc_values[ADC_CHANNEL_LAST * 2], output);
adc_.Convert();
}

void CvReader::Process(const uint16_t* raw_values, float* output) {
for (int i = 0; i < ADC_CHANNEL_LAST; ++i) {
output[i] = channel_[i].Process(
static_cast<float>(raw_values[ADC_GROUP_POT + i]) / 65536.0f,
static_cast<float>(raw_values[ADC_GROUP_CV + i]) / 65536.0f,
attenuverter_[i]);
}
}

} // namespace marbles

+ 129
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/cv_reader.h View File

@@ -0,0 +1,129 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// CV reader.

#ifndef MARBLES_CV_READER_H_
#define MARBLES_CV_READER_H_

#include "stmlib/stmlib.h"

#include "marbles/drivers/adc.h"

#include "marbles/cv_reader_channel.h"
#include "marbles/settings.h"

namespace marbles {

class CvReader {
public:
CvReader() { }
~CvReader() { }
void Init(CalibrationData* calibration_data);

inline bool ready_for_calibration() const {
return true;
}
inline void CalibrateRateC1() {
cv_c1_[0] = channel_[ADC_CHANNEL_T_RATE].unscaled_cv_lp();
}
inline void CalibrateRateC3() {
cv_c3_[0] = channel_[ADC_CHANNEL_T_RATE].unscaled_cv_lp();
}

inline void CalibrateSpreadC1() {
cv_c1_[1] = 0.5f * channel_[ADC_CHANNEL_X_SPREAD].unscaled_cv_lp() + \
0.5f * channel_[ADC_CHANNEL_X_SPREAD_2].unscaled_cv_lp();
}
inline bool CalibrateSpreadC3() {
cv_c3_[1] = 0.5f * channel_[ADC_CHANNEL_X_SPREAD].unscaled_cv_lp() + \
0.5f * channel_[ADC_CHANNEL_X_SPREAD_2].unscaled_cv_lp();
for (int i = 0; i < 2; ++i) {
float c3 = cv_c3_[i]; // 0.2
float c1 = cv_c1_[i]; // 0.4
float delta = c3 - c1;
float target_scale = i == 0 ? 24.0f : 0.4f;
float target_offset = i == 0 ? 12.0f : 0.2f;
if (delta > -0.3f && delta < -0.1f) {
int channel = i == 0 ? ADC_CHANNEL_T_RATE : ADC_CHANNEL_X_SPREAD;
calibration_data_->adc_scale[channel] = target_scale / (c3 - c1);
calibration_data_->adc_offset[channel] = target_offset - \
calibration_data_->adc_scale[channel] * c1;
} else {
return false;
}
}
calibration_data_->adc_scale[ADC_CHANNEL_X_SPREAD_2] = calibration_data_->adc_scale[ADC_CHANNEL_X_SPREAD];
calibration_data_->adc_offset[ADC_CHANNEL_X_SPREAD_2] = calibration_data_->adc_offset[ADC_CHANNEL_X_SPREAD];
return true;
}

inline void CalibrateOffsets() {
for (size_t i = 0; i < ADC_CHANNEL_LAST; ++i) {
if (i != ADC_CHANNEL_T_RATE && i != ADC_CHANNEL_X_SPREAD) {
calibration_data_->adc_offset[i] = \
2.0f * channel_[i].unscaled_cv_lp();
}
}
}
inline uint8_t adc_value(int index) const {
return adc_.value(index) >> 8;
}
void Copy(uint16_t* output);
void Process(const uint16_t* values, float* output);
inline const CvReaderChannel& channel(size_t index) {
return channel_[index];
}
inline CvReaderChannel* mutable_channel(size_t index) {
return &channel_[index];
}
inline void set_attenuverter(int index, float value) {
attenuverter_[index] = value;
}
private:
Adc adc_;
CalibrationData* calibration_data_;
float cv_c1_[2];
float cv_c3_[2];
CvReaderChannel channel_[ADC_CHANNEL_LAST];
float attenuverter_[ADC_CHANNEL_LAST];
static const CvReaderChannel::Settings channel_settings_[ADC_CHANNEL_LAST];
DISALLOW_COPY_AND_ASSIGN(CvReader);
};

} // namespace marbles

#endif // MARBLES_CV_READER_H_

+ 213
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/cv_reader_channel.h View File

@@ -0,0 +1,213 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// CV reader channel.

#ifndef MARBLES_CV_READER_CHANNEL_H_
#define MARBLES_CV_READER_CHANNEL_H_

#include "stmlib/stmlib.h"
#include "stmlib/dsp/dsp.h"

namespace marbles {

enum PotState {
POT_STATE_TRACKING,
POT_STATE_LOCKED,
POT_STATE_CATCHING_UP
};

class HysteresisFilter {
public:
HysteresisFilter() { }
~HysteresisFilter() { }
void Init(float threshold) {
value_ = 0.0f;
threshold_ = threshold;
}
inline float Process(float value) {
float error = value - value_;
if (error > threshold_) {
value_ = value - threshold_;
} else if (error < -threshold_) {
value_ = value + threshold_;
}
return value_;
}
private:
float value_;
float threshold_;
DISALLOW_COPY_AND_ASSIGN(HysteresisFilter);
};

class CvReaderChannel {
public:
CvReaderChannel() { }
~CvReaderChannel() { }

// Because of the large number of initialization parameters, they are
// passed in one single struct.
struct Settings {
float cv_lp;
float pot_scale;
float pot_offset;
float pot_lp;
float min;
float max;
float hysteresis;
};

void Init(float* cv_scale, float* cv_offset, const Settings& settings) {
cv_scale_ = cv_scale;
cv_offset_ = cv_offset;
cv_lp_ = settings.cv_lp;
pot_scale_ = settings.pot_scale + 2.0f * settings.hysteresis;
pot_offset_ = settings.pot_offset - settings.hysteresis;
pot_lp_ = settings.pot_lp;
min_ = settings.min;
max_ = settings.max;
raw_cv_value_ = 0.0f;
cv_value_ = 0.0f;
pot_value_ = 0.0f;
stored_pot_value_ = 0.0f;
attenuverter_value_ = 0.0f;
previous_pot_value_ = 0.0f;

pot_state_ = POT_STATE_TRACKING;
hystereis_filter_.Init(settings.hysteresis);
}
inline float Process(float pot, float cv) {
return Process(pot, cv, 1.0f);
}
inline float Process(float pot, float cv, float attenuverter) {
cv *= *cv_scale_;
cv += *cv_offset_;

attenuverter -= 0.5f;
attenuverter = attenuverter * attenuverter * attenuverter * 8.0f;
ONE_POLE(attenuverter_value_, attenuverter, pot_lp_);

raw_cv_value_ = cv;
ONE_POLE(cv_value_, cv, cv_lp_);
ONE_POLE(pot_value_, pot, pot_lp_);
switch (pot_state_) {
case POT_STATE_TRACKING:
stored_pot_value_ = pot_value_;
previous_pot_value_ = pot_value_;
break;
case POT_STATE_LOCKED:
break;
case POT_STATE_CATCHING_UP:
{
if (fabs(pot_value_ - previous_pot_value_) > 0.01f) {
float delta = pot_value_ - previous_pot_value_;

float skew_ratio = delta > 0.0f
? (1.001f - stored_pot_value_) / (1.001f - previous_pot_value_)
: (0.001f + stored_pot_value_) / (0.001f + previous_pot_value_);
CONSTRAIN(skew_ratio, 0.1f, 10.0f);
stored_pot_value_ += skew_ratio * delta;
CONSTRAIN(stored_pot_value_, 0.0f, 1.0f);
if (fabs(stored_pot_value_ - pot_value_) < 0.01f) {
pot_state_ = POT_STATE_TRACKING;
}

previous_pot_value_ = pot_value_;
}
}
break;
};

float value = hystereis_filter_.Process(
cv_value_ * attenuverter_value_ + this->pot());
CONSTRAIN(value, min_, max_);
return value;
}

inline float cv() const { return cv_value_; }
inline float scaled_raw_cv() const { return raw_cv_value_; }
inline float unscaled_cv_lp() const {
return (cv_value_ - *cv_offset_) / (*cv_scale_);
}
inline float pot() const {
return stored_pot_value_ * pot_scale_ + pot_offset_;
}
inline float unscaled_pot() const { return pot_value_; }

inline void LockPot() {
pot_state_ = POT_STATE_LOCKED;
}

inline void UnlockPot() {
if (pot_state_ == POT_STATE_LOCKED) {
previous_pot_value_ = pot_value_;
pot_state_ = POT_STATE_CATCHING_UP;
}
}

private:
float* cv_scale_;
float* cv_offset_;
float raw_cv_value_;
float cv_lp_;
float pot_scale_;
float pot_offset_;
float pot_lp_;
float attenuverter_lp_;
float min_;
float max_;

PotState pot_state_;

float cv_value_;
float pot_value_; // Value after low-pass filtering.
float previous_pot_value_;
float stored_pot_value_; // The actual parameter value.
float attenuverter_value_;
HysteresisFilter hystereis_filter_;

DISALLOW_COPY_AND_ASSIGN(CvReaderChannel);
};


} // namespace marbles

#endif // MARBLES_CV_READER_CHANNEL_H_

+ 162
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/adc.cc View File

@@ -0,0 +1,162 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for ADC. ADC1 is used for the 8 pots ; ADC2 for the 8 CV inputs.

#include "marbles/drivers/adc.h"

#include <stm32f4xx_conf.h>

namespace marbles {
void Adc::Init(bool single_channel) {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);
DMA_InitTypeDef dma_init;
ADC_CommonInitTypeDef adc_common_init;
ADC_InitTypeDef adc_init;
GPIO_InitTypeDef gpio_init;
// Initialize A0..A7 (ADC0..ADC7)
gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
gpio_init.GPIO_Pin |= GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Mode = GPIO_Mode_AN;
GPIO_Init(GPIOA, &gpio_init);

// Initialize B0..B1 (ADC8..ADC9)
gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Mode = GPIO_Mode_AN;
GPIO_Init(GPIOB, &gpio_init);

// Initialize C0..C5 (ADC10..ADC11)
gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
gpio_init.GPIO_Pin |= GPIO_Pin_4 | GPIO_Pin_5;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Mode = GPIO_Mode_AN;
GPIO_Init(GPIOC, &gpio_init);

// Use DMA to automatically copy ADC data register to values_ buffer.
dma_init.DMA_Channel = DMA_Channel_0;
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
dma_init.DMA_Memory0BaseAddr = (uint32_t)&values_[ADC_GROUP_POT];
dma_init.DMA_DIR = DMA_DIR_PeripheralToMemory;
dma_init.DMA_BufferSize = single_channel ? 1 : ADC_CHANNEL_LAST;
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_Priority = DMA_Priority_High;
dma_init.DMA_FIFOMode = DMA_FIFOMode_Disable;
dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA2_Stream0, &dma_init);
DMA_Cmd(DMA2_Stream0, ENABLE);
dma_init.DMA_Channel = DMA_Channel_1;
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&ADC2->DR;
dma_init.DMA_Memory0BaseAddr = (uint32_t)&values_[ADC_GROUP_CV];
DMA_Init(DMA2_Stream2, &dma_init);
DMA_Cmd(DMA2_Stream2, ENABLE);
adc_common_init.ADC_Mode = ADC_Mode_Independent;
adc_common_init.ADC_Prescaler = ADC_Prescaler_Div8;
adc_common_init.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
adc_common_init.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_20Cycles;
ADC_CommonInit(&adc_common_init);
adc_init.ADC_Resolution = ADC_Resolution_12b;
adc_init.ADC_ScanConvMode = ENABLE;
adc_init.ADC_ContinuousConvMode = DISABLE;
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
adc_init.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None;
adc_init.ADC_DataAlign = ADC_DataAlign_Left;
adc_init.ADC_NbrOfConversion = single_channel ? 1 : ADC_CHANNEL_LAST;
ADC_Init(ADC1, &adc_init);
ADC_Init(ADC2, &adc_init);
// 168M / 2 / 8 / (8 x (144 + 20)) = 8.001kHz.
if (single_channel) {
ADC_RegularChannelConfig(ADC1, ADC_Channel_12, 1, ADC_SampleTime_144Cycles);
} else {
ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 1, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 2, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_12,3, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2,4, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_15,5, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_10,6, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 7, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC1, ADC_Channel_8, 8, ADC_SampleTime_144Cycles);
}
if (single_channel) {
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 1, ADC_SampleTime_144Cycles);
} else {
ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 1, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_0, 2, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_3, 3, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_1, 4, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_4, 5, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_7, 6, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_14,7, ADC_SampleTime_144Cycles);
ADC_RegularChannelConfig(ADC2, ADC_Channel_6, 8, ADC_SampleTime_144Cycles);
}
ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
ADC_DMARequestAfterLastTransferCmd(ADC2, ENABLE);
ADC_Cmd(ADC1, ENABLE);
ADC_Cmd(ADC2, ENABLE);
ADC_DMACmd(ADC1, ENABLE);
ADC_DMACmd(ADC2, ENABLE);
Convert();
}

void Adc::DeInit() {
DMA_Cmd(DMA2_Stream0, DISABLE);
DMA_Cmd(DMA2_Stream2, DISABLE);
ADC_DMARequestAfterLastTransferCmd(ADC1, DISABLE);
ADC_DMARequestAfterLastTransferCmd(ADC2, DISABLE);
ADC_Cmd(ADC1, DISABLE);
ADC_Cmd(ADC2, DISABLE);
ADC_DMACmd(ADC1, DISABLE);
ADC_DMACmd(ADC2, DISABLE);
ADC_DeInit();
}

void Adc::Convert() {
ADC_SoftwareStartConv(ADC1);
ADC_SoftwareStartConv(ADC2);
}

} // namespace marbles

+ 81
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/adc.h View File

@@ -0,0 +1,81 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for ADC. ADC1 is used for the 8 pots ; ADC2 for the 8 CV inputs.

#ifndef MARBLES_DRIVERS_ADC_H_
#define MARBLES_DRIVERS_ADC_H_

#include "stmlib/stmlib.h"

namespace marbles {

enum AdcParameter {
ADC_CHANNEL_DEJA_VU_AMOUNT,
ADC_CHANNEL_DEJA_VU_LENGTH,
ADC_CHANNEL_X_SPREAD_2 = ADC_CHANNEL_DEJA_VU_LENGTH,
ADC_CHANNEL_T_RATE,
ADC_CHANNEL_T_BIAS,
ADC_CHANNEL_T_JITTER,
ADC_CHANNEL_X_SPREAD,
ADC_CHANNEL_X_BIAS,
ADC_CHANNEL_X_STEPS,
ADC_CHANNEL_LAST
};

enum AdcGroup {
ADC_GROUP_POT = 0,
ADC_GROUP_CV = ADC_CHANNEL_LAST
};

class Adc {
public:
Adc() { }
~Adc() { }
void Init(bool single_channel);
void DeInit();
void Convert();
inline float float_value(int channel) const {
return static_cast<float>(values_[channel]) / 65536.0f;
}
inline uint16_t value(int channel) const {
return values_[channel];
}
inline const uint16_t* values() const {
return &values_[0];
}
private:
uint16_t values_[ADC_CHANNEL_LAST * 2];
DISALLOW_COPY_AND_ASSIGN(Adc);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_ADC_H_

+ 122
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/clock_inputs.cc View File

@@ -0,0 +1,122 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the two clock inputs and their normalization probe.

#include "marbles/drivers/clock_inputs.h"

#include <stm32f4xx_conf.h>

namespace marbles {

using namespace std;
using namespace stmlib;

struct ClockInputDefinition {
GPIO_TypeDef* gpio;
uint16_t pin;
};

const ClockInputDefinition clock_input_definition[] = {
{ GPIOC, GPIO_Pin_9 }, // CLOCK_INPUT_T,
{ GPIOA, GPIO_Pin_8 }, // CLOCK_INPUT_X,
};

void ClockInputs::Init() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
// Initialize probe.
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOC, &gpio_init);
// Initialize inputs.
gpio_init.GPIO_Mode = GPIO_Mode_IN;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;

for (int i = 0; i < CLOCK_INPUT_LAST; ++i) {
gpio_init.GPIO_Pin = clock_input_definition[i].pin;
GPIO_Init(clock_input_definition[i].gpio, &gpio_init);
previous_flags_[i] = 0;
normalization_mismatch_count_[i] = 0;
normalized_[i] = false;
}

normalization_probe_state_ = 0;
normalization_decision_count_ = 0;
}

void ClockInputs::ReadNormalization(IOBuffer::Block* block) {
++normalization_decision_count_;
if (normalization_decision_count_ >= kProbeSequenceDuration) {
normalization_decision_count_ = 0;
for (int i = 0; i < CLOCK_INPUT_LAST; ++i) {
normalized_[i] = \
normalization_mismatch_count_[i] < kProbeSequenceDuration / 8;
normalization_mismatch_count_[i] = 0;
}
}
int expected_value = normalization_probe_state_ >> 31;
for (int i = 0; i < CLOCK_INPUT_LAST; ++i) {
int read_value = previous_flags_[i] & GATE_FLAG_HIGH;
normalization_mismatch_count_[i] += read_value ^ expected_value;
block->input_patched[i] = !normalized_[i];
}

normalization_probe_state_ = 1103515245 * normalization_probe_state_ + 12345;
if (normalization_probe_state_ >> 31) {
GPIOC->BSRRL = GPIO_Pin_8;
} else {
GPIOC->BSRRH = GPIO_Pin_8;
}
}

void ClockInputs::Read(const IOBuffer::Slice& slice, size_t size) {
for (int i = 0; i < CLOCK_INPUT_LAST; ++i) {
previous_flags_[i] = ExtractGateFlags(
previous_flags_[i],
!(clock_input_definition[i].gpio->IDR & clock_input_definition[i].pin));
slice.block->input[i][slice.frame_index] = previous_flags_[i];
}
// Extend gate input data to the next samples.
for (size_t j = 1; j < size; ++j) {
for (int i = 0; i < CLOCK_INPUT_LAST; ++i) {
slice.block->input[i][slice.frame_index + j] = \
previous_flags_[i] & GATE_FLAG_HIGH;
}
}
}

} // namespace marbles

+ 75
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/clock_inputs.h View File

@@ -0,0 +1,75 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the two clock inputs and their normalization probe.

#ifndef MARBLES_DRIVERS_CLOCK_INPUTS_H_
#define MARBLES_DRIVERS_CLOCK_INPUTS_H_

#include "stmlib/stmlib.h"
#include "marbles/io_buffer.h"

namespace marbles {

enum ClockInput {
CLOCK_INPUT_T,
CLOCK_INPUT_X,
CLOCK_INPUT_LAST
};

class ClockInputs {
public:
ClockInputs() { }
~ClockInputs() { }
void Init();
void Read(const IOBuffer::Slice& slice, size_t size);
void ReadNormalization(IOBuffer::Block* block);
bool is_normalized(ClockInput input) {
return normalized_[input];
}
bool value(ClockInput input) {
return previous_flags_[input] & stmlib::GATE_FLAG_HIGH;
}

private:
static const int kProbeSequenceDuration = 64;

stmlib::GateFlags previous_flags_[CLOCK_INPUT_LAST];

uint32_t normalization_probe_state_;
bool normalized_[CLOCK_INPUT_LAST];
int normalization_mismatch_count_[CLOCK_INPUT_LAST];
int normalization_decision_count_;
DISALLOW_COPY_AND_ASSIGN(ClockInputs);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_CLOCK_INPUTS_H_

+ 160
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/dac.cc View File

@@ -0,0 +1,160 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for DAC.

#include "marbles/drivers/dac.h"

#include <algorithm>

namespace marbles {

/* static */
Dac* Dac::instance_;

void Dac::Init(int sample_rate, size_t block_size) {
instance_ = this;
block_size_ = block_size;
callback_ = NULL;
InitializeGPIO();
InitializeAudioInterface(sample_rate);
InitializeDMA(block_size);
}

void Dac::InitializeGPIO() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

// Initialize SS pin.
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_25MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_15;
GPIO_Init(GPIOA, &gpio_init);
// Initialize MOSI and SCK pins.
gpio_init.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_12;
GPIO_Init(GPIOC, &gpio_init);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource10, GPIO_AF_SPI3);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource12, GPIO_AF_SPI3);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_SPI3);
}

void Dac::InitializeAudioInterface(int sample_rate) {
RCC_I2SCLKConfig(RCC_I2S2CLKSource_PLLI2S);
RCC_PLLI2SCmd(DISABLE);
// Best results for multiples of 32kHz.
RCC_PLLI2SConfig(258, 3);
RCC_PLLI2SCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLI2SRDY) == RESET);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI3, ENABLE);
SPI_I2S_DeInit(SPI3);
I2S_InitTypeDef i2s_init;
i2s_init.I2S_Mode = I2S_Mode_MasterTx;
i2s_init.I2S_Standard = I2S_Standard_PCMShort;
i2s_init.I2S_DataFormat = I2S_DataFormat_32b;
i2s_init.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
i2s_init.I2S_AudioFreq = sample_rate * kNumDacChannels >> 1;
i2s_init.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(SPI3, &i2s_init);
I2S_Cmd(SPI3, ENABLE);
}

void Dac::InitializeDMA(size_t block_size) {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1, ENABLE);

DMA_Cmd(DMA1_Stream5, DISABLE);
DMA_DeInit(DMA1_Stream5);

DMA_InitTypeDef dma_init;
dma_init.DMA_Channel = DMA_Channel_0;
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&(SPI3->DR);
dma_init.DMA_Memory0BaseAddr = (uint32_t)(&tx_dma_buffer_[0]);
dma_init.DMA_DIR = DMA_DIR_MemoryToPeripheral;
dma_init.DMA_BufferSize = 2 * block_size * kNumDacChannels * 2;
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_Priority = DMA_Priority_High;
dma_init.DMA_FIFOMode = DMA_FIFOMode_Disable;
dma_init.DMA_FIFOThreshold = DMA_FIFOThreshold_1QuarterFull;
dma_init.DMA_MemoryBurst = DMA_MemoryBurst_Single;
dma_init.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA1_Stream5, &dma_init);

// Enable the interrupts.
DMA_ITConfig(DMA1_Stream5, DMA_IT_TC | DMA_IT_HT, ENABLE);
// Enable the IRQ.
NVIC_EnableIRQ(DMA1_Stream5_IRQn);

// Start DMA from/to codec.
SPI_I2S_DMACmd(SPI3, SPI_I2S_DMAReq_Tx, ENABLE);
}

void Dac::Start(FillBufferCallback callback) {
callback_ = callback;
DMA_Cmd(DMA1_Stream5, ENABLE);
}

void Dac::Stop() {
DMA_Cmd(DMA1_Stream5, DISABLE);
}

void Dac::Fill(size_t offset) {
// Fill the buffer.
IOBuffer::Slice slice = (*callback_)(block_size_);
uint16_t* p = &tx_dma_buffer_[offset * block_size_ * kNumDacChannels * 2];
for (size_t i = 0; i < block_size_; ++i) {
for (size_t j = 0; j < kNumDacChannels; ++j) {
uint16_t sample = slice.block->cv_output[j][slice.frame_index + i];
*p++ = 0x1000 | (j << 9) | (sample >> 8);
*p++ = sample << 8;
}
}
}

} // namespace marbles

extern "C" {

void DMA1_Stream5_IRQHandler(void) {
uint32_t flags = DMA1->HISR;
DMA1->HIFCR = DMA_FLAG_TCIF5 | DMA_FLAG_HTIF5;
if (flags & DMA_FLAG_TCIF5) {
marbles::Dac::GetInstance()->Fill(1);
} else if (flags & DMA_FLAG_HTIF5) {
marbles::Dac::GetInstance()->Fill(0);
}
}
}

+ 82
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/dac.h View File

@@ -0,0 +1,82 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for quad SPI DAC.

#ifndef MARBLES_DRIVERS_DAC_H_
#define MARBLES_DRIVERS_DAC_H_

#include "stmlib/stmlib.h"

#include <stm32f4xx_conf.h>

#include "marbles/io_buffer.h"

namespace marbles {

enum DacChannel {
DAC_CHANNEL_X_1,
DAC_CHANNEL_X_2,
DAC_CHANNEL_X_3,
DAC_CHANNEL_Y,
DAC_CHANNEL_LAST
};

const size_t kMaxDacBlockSize = 8;
const size_t kNumDacChannels = 4;

class Dac {
public:
Dac() { }
~Dac() { }
typedef IOBuffer::Slice (*FillBufferCallback)(size_t size);
void Init(int sample_rate, size_t block_size);
void Start(FillBufferCallback callback);
void Stop();
void Fill(size_t offset);
static Dac* GetInstance() { return instance_; }
private:
void InitializeGPIO();
void InitializeAudioInterface(int sample_rate);
void InitializeDMA(size_t block_size);
static Dac* instance_;
size_t block_size_;
FillBufferCallback callback_;
// There are 8 16-bit words per frame.
uint16_t tx_dma_buffer_[2 * kMaxDacBlockSize * kNumDacChannels * 2];

DISALLOW_COPY_AND_ASSIGN(Dac);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_DAC_H_

+ 76
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/debug_pin.h View File

@@ -0,0 +1,76 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the debug (timing) pin.

#ifndef MARBLES_DRIVERS_DEBUG_PIN_H_
#define MARBLES_DRIVERS_DEBUG_PIN_H_

#include "stmlib/stmlib.h"

#ifndef TEST
#include <stm32f4xx_conf.h>
#endif

namespace marbles {

class DebugPin {
public:
DebugPin() { }
~DebugPin() { }
#ifdef TEST
static void Init() { }
static void High() { }
static void Low() { }
#else
static void Init() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_9;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio_init);
}
static inline void High() {
GPIOA->BSRRL = GPIO_Pin_9;
}
static inline void Low() {
GPIOA->BSRRH = GPIO_Pin_9;
}
#endif
private:
DISALLOW_COPY_AND_ASSIGN(DebugPin);
};

#define TIC DebugPin::High();
#define TOC DebugPin::Low();

} // namespace marbles

#endif // MARBLES_DRIVERS_DEBUG_PIN_H_

+ 94
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/debug_port.h View File

@@ -0,0 +1,94 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// UART driver for conversing with the factory testing program.

#ifndef MARBLES_DRIVERS_DEBUG_PORT_H_
#define MARBLES_DRIVERS_DEBUG_PORT_H_

#include "stmlib/stmlib.h"

#include <stm32f4xx_conf.h>

namespace marbles {

class DebugPort {
public:
DebugPort() { }
~DebugPort() { }
void Init() {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// Initialize TX and RX pins.
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
// Initialize USART.
USART_InitTypeDef usart_init;
usart_init.USART_BaudRate = 9600;
usart_init.USART_WordLength = USART_WordLength_8b;
usart_init.USART_StopBits = USART_StopBits_1;
usart_init.USART_Parity = USART_Parity_No;
usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &usart_init);
USART_Cmd(USART1, ENABLE);
}
bool writable() {
return USART1->SR & USART_FLAG_TXE;
}
bool readable() {
return USART1->SR & USART_FLAG_RXNE;
}
void Write(uint8_t byte) {
USART1->DR = byte;
}
uint8_t Read() {
return USART1->DR;
}
private:
DISALLOW_COPY_AND_ASSIGN(DebugPort);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_DEBUG_PORT_H_

+ 86
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/gate_outputs.h View File

@@ -0,0 +1,86 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the two gate outputs.

#ifndef MARBLES_DRIVERS_GATE_OUTPUTS_H_
#define MARBLES_DRIVERS_GATE_OUTPUTS_H_

#include "stmlib/stmlib.h"

#include <stm32f4xx_conf.h>

#include "marbles/io_buffer.h"

namespace marbles {

class GateOutputs {
public:
GateOutputs() { }
~GateOutputs() { }
void Init() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);

GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
GPIO_Init(GPIOA, &gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_11;
GPIO_Init(GPIOC, &gpio_init);
}
inline void Write(IOBuffer::Slice s) {
if (s.block->gate_output[0][s.frame_index]) {
GPIOC->BSRRL = GPIO_Pin_11;
} else {
GPIOC->BSRRH = GPIO_Pin_11;
}
if (s.block->gate_output[1][s.frame_index]) {
GPIOA->BSRRL = GPIO_Pin_11;
} else {
GPIOA->BSRRH = GPIO_Pin_11;
}
if (s.block->gate_output[2][s.frame_index]) {
GPIOA->BSRRL = GPIO_Pin_12;
} else {
GPIOA->BSRRH = GPIO_Pin_12;
}
}

private:
DISALLOW_COPY_AND_ASSIGN(GateOutputs);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_GATE_OUTPUTS_H_

+ 86
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/leds.cc View File

@@ -0,0 +1,86 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for all the LEDs.

#include "marbles/drivers/leds.h"

#include <algorithm>

#include <stm32f4xx_conf.h>

namespace marbles {

using namespace std;

struct LedDefinition {
GPIO_TypeDef* gpio;
uint16_t pin[3]; // pins for R, G, B - assumed to be on the same GPIO
};

const LedDefinition led_definition[] = {
{ GPIOB, { 0, GPIO_Pin_10, 0 } }, // LED_T_DEJA_VU
{ GPIOB, { GPIO_Pin_13, GPIO_Pin_12, 0 } }, // LED_T_MODEL
{ GPIOC, { GPIO_Pin_7, GPIO_Pin_6, 0 } }, // LED_T_RANGE
{ GPIOB, { 0, GPIO_Pin_9, 0 } }, // LED_X_DEJA_VU
{ GPIOB, { GPIO_Pin_8, GPIO_Pin_7, 0 } }, // LED_X_CONTROL_MODE,
{ GPIOB, { GPIO_Pin_4, GPIO_Pin_5, 0 } }, // LED_X_RANGE
{ GPIOB, { 0, GPIO_Pin_3, 0 } } // LED_X_EXT
};

void Leds::Init() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
for (int i = 0; i < LED_LAST; ++i) {
LedDefinition d = led_definition[i];
gpio_init.GPIO_Pin = d.pin[0] | d.pin[1] | d.pin[2];
GPIO_Init(d.gpio, &gpio_init);
}
Clear();
}

void Leds::Clear() {
fill(&colors_[0], &colors_[LED_LAST], LED_COLOR_OFF);
}

void Leds::Write() {
for (int i = 0; i < LED_LAST; ++i) {
LedDefinition d = led_definition[i];
GPIO_WriteBit(d.gpio, d.pin[0], static_cast<BitAction>(
(colors_[i] & 0x800000) >> 23));
GPIO_WriteBit(d.gpio, d.pin[1], static_cast<BitAction>(
(colors_[i] & 0x008000) >> 15));
}
}

} // namespace marbles

+ 75
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/leds.h View File

@@ -0,0 +1,75 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for all the LEDs.

#ifndef MARBLES_DRIVERS_LEDS_H_
#define MARBLES_DRIVERS_LEDS_H_

#include "stmlib/stmlib.h"

namespace marbles {
enum Led {
LED_T_DEJA_VU,
LED_T_MODEL,
LED_T_RANGE,
LED_X_DEJA_VU,
LED_X_CONTROL_MODE,
LED_X_RANGE,
LED_X_EXT,
LED_LAST
};

enum LedColor {
LED_COLOR_OFF = 0,
LED_COLOR_RED = 0xff0000,
LED_COLOR_GREEN = 0x00ff00,
LED_COLOR_YELLOW = 0xffff00,
};

class Leds {
public:
Leds() { }
~Leds() { }
void Init();
void Write();
void Clear();
void set(Led led, uint32_t color) {
colors_[led] = color;
}

private:
uint32_t colors_[LED_LAST];
DISALLOW_COPY_AND_ASSIGN(Leds);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_LEDS_H_

+ 62
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/rng.h View File

@@ -0,0 +1,62 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for built-in random number generator.

#ifndef MARBLES_DRIVERS_RNG_H_
#define MARBLES_DRIVERS_RNG_H_

#include "stmlib/stmlib.h"

#include <stm32f4xx_conf.h>

namespace marbles {

class Rng {
public:
Rng() { }
~Rng() { }
void Init() {
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE);
RNG_Cmd(ENABLE);
}
inline bool readable() {
return RNG->SR & RNG_FLAG_DRDY;
}
inline uint32_t data() {
return RNG->DR;
}

private:
DISALLOW_COPY_AND_ASSIGN(Rng);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_RNG_H_

+ 79
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/switches.cc View File

@@ -0,0 +1,79 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the front panel switch.

#include "marbles/drivers/switches.h"

#include <algorithm>

namespace marbles {

using namespace std;

struct SwitchDefinition {
GPIO_TypeDef* gpio;
uint16_t pin;
};

const SwitchDefinition switch_definitions[] = {
{ GPIOB, GPIO_Pin_11 }, // SWITCH_T_DEJA_VU,
{ GPIOB, GPIO_Pin_14 }, // SWITCH_T_MODE
{ GPIOB, GPIO_Pin_15 }, // SWITCH_T_RANGE
{ GPIOC, GPIO_Pin_15 }, // SWITCH_X_DEJA_VU,
{ GPIOC, GPIO_Pin_13 }, // SWITCH_X_MODE,
{ GPIOB, GPIO_Pin_6 }, // SWITCH_X_RANGE
{ GPIOD, GPIO_Pin_2 } // SWITCH_X_EXT
};

void Switches::Init() {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_IN;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_UP;
for (int i = 0; i < SWITCH_LAST; ++i) {
SwitchDefinition definition = switch_definitions[i];
gpio_init.GPIO_Pin = definition.pin;
GPIO_Init(definition.gpio, &gpio_init);
}
fill(&switch_state_[0], &switch_state_[SWITCH_LAST], 0xff);
}

void Switches::Debounce() {
for (int i = 0; i < SWITCH_LAST; ++i) {
SwitchDefinition definition = switch_definitions[i];
switch_state_[i] = (switch_state_[i] << 1) | \
GPIO_ReadInputDataBit(definition.gpio, definition.pin);
}
}

} // namespace marbles

+ 87
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/switches.h View File

@@ -0,0 +1,87 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the 6 front panel switches.

#ifndef MARBLES_DRIVERS_SWITCHES_H_
#define MARBLES_DRIVERS_SWITCHES_H_

#include "stmlib/stmlib.h"

#include <stm32f4xx_conf.h>

namespace marbles {
enum Switch {
SWITCH_T_DEJA_VU,
SWITCH_T_MODEL,
SWITCH_T_RANGE,
SWITCH_X_DEJA_VU,
SWITCH_X_MODE,
SWITCH_X_RANGE,
SWITCH_X_EXT,
SWITCH_LAST
};

class Switches {
public:
Switches() { }
~Switches() { }
void Init();
void Debounce();
inline bool released(Switch s) const {
return switch_state_[s] == 0x7f;
}
inline bool just_pressed(Switch s) const {
return switch_state_[s] == 0x80;
}

inline bool pressed(Switch s) const {
return switch_state_[s] == 0x00;
}
inline bool pressed_immediate(Switch s) const {
if (s == SWITCH_T_DEJA_VU) {
return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11);
} else if (s == SWITCH_X_MODE) {
return !GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13);
} else {
return false;
}
}
private:
uint8_t switch_state_[SWITCH_LAST];
DISALLOW_COPY_AND_ASSIGN(Switches);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_SWITCHES_H_

+ 48
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/system.cc View File

@@ -0,0 +1,48 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// System level initialization.

#include "marbles/drivers/system.h"

#include <stm32f4xx_conf.h>

namespace marbles {

void System::Init(bool application) {
if (application) {
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x8000);
}
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_32);
}

void System::StartTimers() {
SysTick_Config(F_CPU / 1000);
IWDG_Enable();
}

} // namespace marbles

+ 50
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/drivers/system.h View File

@@ -0,0 +1,50 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// System-level initialization.

#ifndef MARBLES_DRIVERS_SYSTEM_H_
#define MARBLES_DRIVERS_SYSTEM_H_

#include "stmlib/stmlib.h"

namespace marbles {

class System {
public:
System() { }
~System() { }
void Init(bool application);
void StartTimers();
private:
DISALLOW_COPY_AND_ASSIGN(System);
};

} // namespace marbles

#endif // MARBLES_DRIVERS_SYSTEM_H_

BIN
plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/Marbles.xlsx View File


+ 2483
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/panel/marbles_v70.ai
File diff suppressed because it is too large
View File


BIN
plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/panel/marbles_v70.dwg View File


BIN
plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/pcb/marbles_v70.brd View File


BIN
plugins/community/repos/AudibleInstruments/eurorack/marbles/hardware_design/pcb/marbles_v70.sch View File


+ 109
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/io_buffer.h View File

@@ -0,0 +1,109 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// I/O Buffering.

#ifndef MARBLES_IO_BUFFER_H_
#define MARBLES_IO_BUFFER_H_

#include "stmlib/stmlib.h"
#include "stmlib/utils/gate_flags.h"

#include <algorithm>

namespace marbles {

const size_t kNumBlocks = 2;
const size_t kBlockSize = 5;

const size_t kNumInputs = 2;
const size_t kNumCvOutputs = 4;
const size_t kNumGateOutputs = 3;
const size_t kNumParameters = 8;

class IOBuffer {
public:
struct Block {
uint16_t adc_value[kNumParameters * 2];
bool input_patched[kNumInputs];
stmlib::GateFlags input[kNumInputs][kBlockSize];
uint16_t cv_output[kNumCvOutputs][kBlockSize];
bool gate_output[kNumGateOutputs][kBlockSize + 2];
};
struct Slice {
Block* block;
size_t frame_index;
};

typedef void ProcessFn(Block* block, size_t size);

IOBuffer() { }
~IOBuffer() { }
void Init() {
io_block_ = 0;
render_block_ = kNumBlocks / 2;
io_frame_ = 0;
}
inline void Process(ProcessFn* fn) {
while (render_block_ != io_block_) {
(*fn)(&block_[render_block_], kBlockSize);
render_block_ = (render_block_ + 1) % kNumBlocks;
}
}
inline Slice NextSlice(size_t size) {
Slice s;
s.block = &block_[io_block_];
s.frame_index = io_frame_;
io_frame_ += size;
if (io_frame_ >= kBlockSize) {
io_frame_ -= kBlockSize;
io_block_ = (io_block_ + 1) % kNumBlocks;
}
return s;
}
inline bool new_block() const {
return io_frame_ == 0;
}
private:
Block block_[kNumBlocks];
size_t io_frame_;
volatile size_t io_block_;
volatile size_t render_block_;
DISALLOW_COPY_AND_ASSIGN(IOBuffer);
};

} // namespace marbles

#endif // MARBLES_IO_BUFFER_H_

+ 57
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/makefile View File

@@ -0,0 +1,57 @@
# Copyright 2015 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.

# System specifications
F_CRYSTAL = 8000000L
F_CPU = 168000000L
SYSCLOCK = SYSCLK_FREQ_168MHz
FAMILY = f4xx
# USB = enabled

APPLICATION_LARGE = TRUE
BOOTLOADER = marbles_bootloader

# Prefered upload command
UPLOAD_COMMAND = upload_combo_jtag_erase_first

# Packages to build
TARGET = marbles
PACKAGES = marbles \
marbles/drivers \
marbles/ramp \
marbles/random \
stmlib/dsp \
stmlib/utils \
stmlib/system
RESOURCES = marbles/resources

TOOLCHAIN_PATH ?= /usr/local/arm-4.8.3/

include stmlib/makefile.inc

# Rule for building the firmware update file
wav: $(TARGET_BIN)
python stm_audio_bootloader/qpsk/encoder.py \
-t stm32f4 -s 48000 -b 12000 -c 6000 -p 256 \
$(TARGET_BIN)

+ 447
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/marbles.cc View File

@@ -0,0 +1,447 @@
// Copyright 2015 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.

#include <stm32f4xx_conf.h>

#include "marbles/drivers/clock_inputs.h"
#include "marbles/drivers/dac.h"
#include "marbles/drivers/debug_pin.h"
#include "marbles/drivers/debug_port.h"
#include "marbles/drivers/gate_outputs.h"
#include "marbles/drivers/rng.h"
#include "marbles/drivers/system.h"

#include "marbles/ramp/ramp_extractor.h"
#include "marbles/random/random_generator.h"
#include "marbles/random/random_stream.h"
#include "marbles/random/t_generator.h"
#include "marbles/random/x_y_generator.h"

#include "marbles/clock_self_patching_detector.h"
#include "marbles/cv_reader.h"
#include "marbles/io_buffer.h"
#include "marbles/note_filter.h"
#include "marbles/resources.h"
#include "marbles/scale_recorder.h"
#include "marbles/settings.h"
#include "marbles/ui.h"

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/hysteresis_quantizer.h"
#include "stmlib/dsp/units.h"

#define PROFILE_INTERRUPT 0
#define PROFILE_RENDER 0

using namespace marbles;
using namespace std;
using namespace stmlib;

const bool test_adc_noise = false;

const int kSampleRate = 32000;
const int kGateDelay = 2;

ClockInputs clock_inputs;
ClockSelfPatchingDetector self_patching_detector[kNumGateOutputs];
CvReader cv_reader;
Dac dac;
DebugPort debug_port;
GateOutputs gate_outputs;
HysteresisQuantizer deja_vu_length_quantizer;
IOBuffer io_buffer;
NoteFilter note_filter;
Rng rng;
ScaleRecorder scale_recorder;
Settings settings;
Ui ui;

RandomGenerator random_generator;
RandomStream random_stream;
TGenerator t_generator;
XYGenerator xy_generator;

// Default interrupt handlers.
extern "C" {

int __errno;

void NMI_Handler() { }
void HardFault_Handler() { while (1); }
void MemManage_Handler() { while (1); }
void BusFault_Handler() { while (1); }
void UsageFault_Handler() { while (1); }
void SVC_Handler() { }
void DebugMon_Handler() { }
void PendSV_Handler() { }

void SysTick_Handler() {
IWDG_ReloadCounter();
ui.Poll();
if (settings.freshly_baked()) {
if (debug_port.readable()) {
uint8_t command = debug_port.Read();
uint8_t response = ui.HandleFactoryTestingRequest(command);
debug_port.Write(response);
}
}
}

}

IOBuffer::Slice FillBuffer(size_t size) {
if (PROFILE_INTERRUPT) {
TIC;
}
IOBuffer::Slice s = io_buffer.NextSlice(size);
gate_outputs.Write(s);
clock_inputs.Read(s, size);

if (io_buffer.new_block()) {
cv_reader.Copy(&s.block->adc_value[0]);
clock_inputs.ReadNormalization(s.block);
}

if (rng.readable()) {
random_stream.Write(rng.data());
}

if (PROFILE_INTERRUPT) {
TOC;
}
return s;
}

inline uint16_t DacCode(int index, float voltage) {
CONSTRAIN(voltage, -5.0f, 5.0f);
const float scale = settings.calibration_data().dac_scale[index];
const float offset = settings.calibration_data().dac_offset[index];
return ClipU16(static_cast<int32_t>(voltage * scale + offset));
}

void ProcessTest(IOBuffer::Block* block, size_t size) {
float parameters[kNumParameters];
static float phase;
cv_reader.Process(&block->adc_value[0], parameters);
for (size_t i = 0; i < size; ++i) {
phase += 100.0f / static_cast<float>(kSampleRate);
if (phase >= 1.0f) {
phase -= 1.0f;
}
block->cv_output[0][i] = DacCode(
0, 4.0 * Interpolate(lut_sine, phase, 256.0f));
block->cv_output[1][i] = DacCode(
1, -8.0f * phase + 4.0f);
block->cv_output[2][i] = DacCode(
2, (phase < 0.5f ? phase : 1.0f - phase) * 16.0f - 4.0f);
block->cv_output[3][i] = DacCode(
3, phase < 0.5f ? -4.0f : 4.0f);

for (int j = 0; j < 4; ++j) {
uint16_t dac_code = ui.output_test_forced_dac_code(j);
if (dac_code) {
block->cv_output[j][i] = dac_code;
}
}
block->gate_output[0][i] = block->input_patched[0]
? block->input[0][i]
: phase < 0.2f;
block->gate_output[1][i] = phase < 0.5f;
block->gate_output[2][i] = block->input_patched[1]
? block->input[1][i]
: phase < 0.8f;
}
}

Ratio y_divider_ratios[] = {
{ 1, 64 },
{ 1, 48 },
{ 1, 32 },
{ 1, 24 },
{ 1, 16 },
{ 1, 12 },
{ 1, 8 },
{ 1, 6 },
{ 1, 4 },
{ 1, 3 },
{ 1, 2 },
{ 1, 1 },
};

int loop_length[] = {
1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
5, 5, 5, 5,
6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
7, 7,
8, 8, 8, 8, 8, 8, 8, 8, 8,
10, 10, 10,
12, 12, 12, 12, 12, 12, 12,
14,
16
};
float parameters[kNumParameters];
float ramp_buffer[kBlockSize * 4];
bool gates[kBlockSize * 2];
float voltages[kBlockSize * 4];
Ramps ramps;
GroupSettings x, y;
bool gate_delay_tail[kNumGateOutputs][kGateDelay];

float SineOscillator(float voltage) {
static float phase = 0.0f;
CONSTRAIN(voltage, -5.0f, 5.0f);
float frequency = stmlib::SemitonesToRatio(voltage * 12.0f) * 220.0f / kSampleRate;
phase += frequency;
if (phase >= 1.0f) {
phase -= 1.0f;
}
return 5.0f * Interpolate(lut_sine, phase, 256.0f);
}

void Process(IOBuffer::Block* block, size_t size) {
if (PROFILE_RENDER) {
TIC;
}

// Filter CV values (3.5%)
cv_reader.Process(&block->adc_value[0], parameters);
float deja_vu = parameters[ADC_CHANNEL_DEJA_VU_AMOUNT];
// Deadband near 12 o'clock for the deja vu parameter.
const float d = fabsf(deja_vu - 0.5f);
if (d > 0.03f) {
ui.set_deja_vu_lock(false);
} else if (d < 0.02f) {
ui.set_deja_vu_lock(true);
}
if (deja_vu < 0.47f) {
deja_vu *= 1.06382978723f;
} else if (deja_vu > 0.53f) {
deja_vu = 0.5f + (deja_vu - 0.53f) * 1.06382978723f;
} else {
deja_vu = 0.5f;
}
GateFlags* t_clock = block->input[0];
GateFlags* xy_clock = block->input[1];
// Determine the clock source for the XY section (2%)
ClockSource xy_clock_source = CLOCK_SOURCE_INTERNAL_T1_T2_T3;
if (block->input_patched[1]) {
xy_clock_source = CLOCK_SOURCE_EXTERNAL;
size_t best_score = 8;
for (size_t i = 0; i < kNumGateOutputs; ++i) {
size_t score = self_patching_detector[i].Process(block, size);
if (score >= best_score) {
xy_clock_source = ClockSource(CLOCK_SOURCE_INTERNAL_T1 + i);
best_score = score;
}
}
}

// Generate gates for T-section (16%).
ramps.master = &ramp_buffer[0];
ramps.external = &ramp_buffer[kBlockSize];
ramps.slave[0] = &ramp_buffer[kBlockSize * 2];
ramps.slave[1] = &ramp_buffer[kBlockSize * 3];
const State& state = settings.state();
int deja_vu_length = deja_vu_length_quantizer.Lookup(
loop_length,
parameters[ADC_CHANNEL_DEJA_VU_LENGTH],
sizeof(loop_length) / sizeof(int));
t_generator.set_model(TGeneratorModel(state.t_model));
t_generator.set_range(TGeneratorRange(state.t_range));
t_generator.set_rate(parameters[ADC_CHANNEL_T_RATE]);
t_generator.set_bias(parameters[ADC_CHANNEL_T_BIAS]);
t_generator.set_jitter(parameters[ADC_CHANNEL_T_JITTER]);
t_generator.set_deja_vu(state.t_deja_vu ? deja_vu : 0.0f);
t_generator.set_length(deja_vu_length);
t_generator.set_pulse_width_mean(float(state.t_pulse_width_mean) / 256.0f);
t_generator.set_pulse_width_std(float(state.t_pulse_width_std) / 256.0f);
t_generator.Process(
block->input_patched[0],
t_clock,
ramps,
gates,
size);
// Generate voltages for X-section (40%).
float note_cv_1 = cv_reader.channel(ADC_CHANNEL_X_SPREAD).scaled_raw_cv();
float note_cv_2 = cv_reader.channel(ADC_CHANNEL_X_SPREAD_2).scaled_raw_cv();
float note_cv = 0.5f * (note_cv_1 + note_cv_2);
float u = note_filter.Process(0.5f * (note_cv + 1.0f));
if (test_adc_noise) {
static float note_lp = 0.0f;
float note = note_cv_1;
ONE_POLE(note_lp, note, 0.0001f);
float cents = (note - note_lp) * 1200.0f * 5.0f;
fill(&voltages[0], &voltages[4 * size], cents);
} else if (ui.recording_scale()) {
float voltage = (u - 0.5f) * 10.0f;
for (size_t i = 0; i < size; ++i) {
GateFlags gate = block->input_patched[1]
? block->input[1][i]
: GATE_FLAG_LOW;
if (gate & GATE_FLAG_RISING) {
scale_recorder.NewNote(voltage);
}
if (gate & GATE_FLAG_HIGH) {
scale_recorder.UpdateVoltage(voltage);
}
if (gate & GATE_FLAG_FALLING) {
scale_recorder.AcceptNote();
}
}
fill(&voltages[0], &voltages[4 * size], voltage);
} else {
x.control_mode = ControlMode(state.x_control_mode);
x.voltage_range = VoltageRange(state.x_range % 3);
x.register_mode = state.x_register_mode;
x.register_value = u;
cv_reader.set_attenuverter(
ADC_CHANNEL_X_SPREAD, state.x_register_mode ? 0.5f : 1.0f);
x.spread = parameters[ADC_CHANNEL_X_SPREAD];
x.bias = parameters[ADC_CHANNEL_X_BIAS];
x.steps = parameters[ADC_CHANNEL_X_STEPS];
x.deja_vu = state.x_deja_vu ? deja_vu : 0.0f;
x.length = deja_vu_length;
x.ratio.p = 1;
x.ratio.q = 1;
y.control_mode = CONTROL_MODE_IDENTICAL;
y.voltage_range = VoltageRange(state.y_range);
y.register_mode = false;
y.register_value = 0.0f;
y.spread = float(state.y_spread) / 256.0f;
y.bias = float(state.y_bias) / 256.0f;
y.steps = float(state.y_steps) / 256.0f;
y.deja_vu = 0.0f;
y.length = 1;
y.ratio = y_divider_ratios[
static_cast<uint16_t>(state.y_divider) * 12 >> 8];
if (settings.dirty_scale_index() != -1) {
int i = settings.dirty_scale_index();
xy_generator.LoadScale(i, settings.persistent_data().scale[i]);
settings.set_dirty_scale_index(-1);
}
y.scale_index = x.scale_index = state.x_scale;

xy_generator.Process(
xy_clock_source,
x,
y,
xy_clock,
ramps,
voltages,
size);
}
const float* v = voltages;
const bool* g = gates;
for (size_t i = 0; i < size; ++i) {
block->cv_output[1][i] = DacCode(1, *v++);
block->cv_output[2][i] = DacCode(2, *v++);
block->cv_output[3][i] = DacCode(3, *v++);
block->cv_output[0][i] = DacCode(0, *v++);
block->gate_output[0][i + kGateDelay] = *g++;
block->gate_output[1][i + kGateDelay] = ramps.master[i] < 0.5f;
block->gate_output[2][i + kGateDelay] = *g++;
}
for (size_t i = 0; i < kNumGateOutputs; ++i) {
for (size_t j = 0; j < kGateDelay; ++j) {
block->gate_output[i][j] = gate_delay_tail[i][j];
gate_delay_tail[i][j] = block->gate_output[i][size + j];
}
}
if (PROFILE_RENDER) {
TOC;
}
}

void Init() {
System sys;
sys.Init(true);
settings.Init();
clock_inputs.Init();
dac.Init(kSampleRate, 1);
rng.Init();
note_filter.Init();
gate_outputs.Init();
io_buffer.Init();
deja_vu_length_quantizer.Init();
cv_reader.Init(settings.mutable_calibration_data());
scale_recorder.Init();
ui.Init(&settings, &cv_reader, &scale_recorder, &clock_inputs);
if (settings.freshly_baked()) {
settings.ProgramOptionBytes();
if (PROFILE_INTERRUPT || PROFILE_RENDER) {
DebugPin::Init();
} else {
debug_port.Init();
}
}
random_generator.Init(1);
random_stream.Init(&random_generator);
t_generator.Init(&random_stream, static_cast<float>(kSampleRate));
xy_generator.Init(&random_stream, static_cast<float>(kSampleRate));

for (size_t i = 0; i < kNumScales; ++i) {
xy_generator.LoadScale(i, settings.persistent_data().scale[i]);
}
for (size_t i = 0; i < kNumGateOutputs; ++i) {
self_patching_detector[i].Init(i);
}
sys.StartTimers();
dac.Start(&FillBuffer);
}

int main(void) {
Init();
while (1) {
ui.DoEvents();
io_buffer.Process(ui.output_test_mode() ? &ProcessTest : &Process);
}
}

+ 82
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/note_filter.h View File

@@ -0,0 +1,82 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Low pass filter for getting stable pitch data.

#ifndef MARBLES_NOTE_FILTER_H_
#define MARBLES_NOTE_FILTER_H_

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/delay_line.h"

namespace marbles {

// Note: this is a "slow" filter, since it takes about 10 samples to catch
// up with an edge in the input signal.
class NoteFilter {
public:
enum {
N = 7 // Median filter order
};
NoteFilter() { }
~NoteFilter() { }

void Init() {
lp_1_ = 0.0f;
lp_2_ = 0.0f;
std::fill(&previous_values_[0], &previous_values_[N], 0.0f);
}

inline float Process(float value) {
float sorted_values[N];

std::rotate(
&previous_values_[0],
&previous_values_[1],
&previous_values_[N]);
previous_values_[N - 1] = value;
std::copy(&previous_values_[0], &previous_values_[N], &sorted_values[0]);
std::sort(&sorted_values[0], &sorted_values[N]);
value = sorted_values[(N - 1) / 2];
const float kLPCoefficient = 0.65f;
ONE_POLE(lp_1_, value, kLPCoefficient);
ONE_POLE(lp_2_, lp_1_, kLPCoefficient);
return lp_2_;
}

private:
float previous_values_[N];
float lp_1_;
float lp_2_;
DISALLOW_COPY_AND_ASSIGN(NoteFilter);
};

} // namespace marbles

#endif // MARBLES_NOTE_FILTER_H_

+ 40
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp.h View File

@@ -0,0 +1,40 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Timing information is represented as a ramp from 0.0 to kMaxRampValue

#ifndef MARBLES_RAMP_RAMP_H_
#define MARBLES_RAMP_RAMP_H_

#include "stmlib/stmlib.h"

namespace marbles {

const float kMaxRampValue = 0.9999f;

} // namespace marbles

#endif // MARBLES_RAMP_RAMP_H_

+ 107
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_divider.h View File

@@ -0,0 +1,107 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Generates a ramp whose frequency is p/q times the frequency of the input.
// Phase is synchronized.

#ifndef MARBLES_RAMP_RAMP_DIVIDER_H_
#define MARBLES_RAMP_RAMP_DIVIDER_H_

#include "stmlib/stmlib.h"

#include <algorithm>

#include "marbles/ramp/ramp.h"

namespace marbles {

struct Ratio {
float to_float() { return static_cast<float>(p) / static_cast<float>(q); }
int p;
int q;
template<int n> void Simplify() {
while ((p % n) == 0 && (q % n) == 0) {
p /= n;
q /= n;
}
}
};

class RampDivider {
public:
RampDivider() { }
~RampDivider() { }
void Init() {
phase_ = 0.0f;
train_phase_ = 0.0f;
max_train_phase_ = 1.0f;
f_ratio_ = 0.99999f;
reset_counter_ = 1;
}

void Process(Ratio ratio, const float* in, float* out, size_t size) {
while (size--) {
float new_phase = *in++;
float frequency = new_phase - phase_;
if (frequency < 0.0f) {
frequency += 1.0f;
--reset_counter_;
if (!reset_counter_) {
train_phase_ = new_phase;
reset_counter_ = ratio.q;
f_ratio_ = ratio.to_float() * kMaxRampValue;
frequency = 0.0f;
max_train_phase_ = static_cast<float>(ratio.q);
}
}
train_phase_ += frequency;
if (train_phase_ >= max_train_phase_) {
train_phase_ = max_train_phase_;
}
float output_phase = train_phase_ * f_ratio_;
output_phase -= static_cast<float>(static_cast<int>(output_phase));
*out++ = output_phase;
phase_ = new_phase;
}
}

private:
float phase_;
float train_phase_;
float max_train_phase_;
float f_ratio_;
int reset_counter_;
DISALLOW_COPY_AND_ASSIGN(RampDivider);
};

} // namespace marbles

#endif // MARBLES_RAMP_RAMP_DIVIDER_H_

+ 312
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_extractor.cc View File

@@ -0,0 +1,312 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Recovers a ramp from a clock input by guessing at what time the next edge
// will occur. Prediction strategies:
// - Moving average of previous intervals.
// - Trigram model on quantized intervals.
// - Periodic rhythmic pattern.
// - Assume that the pulse width is constant, deduct the period from the on time
// and the pulse width.

#include "marbles/ramp/ramp_extractor.h"

#include <algorithm>

#include "marbles/ramp/ramp.h"

#include "stmlib/dsp/dsp.h"

namespace marbles {

using namespace std;
using namespace stmlib;

const float kLogOneFourth = 1.189207115f;
const float kPulseWidthTolerance = 0.05f;

inline bool IsWithinTolerance(float x, float y, float error) {
return x >= y * (1.0f - error) && x <= y * (1.0f + error);
}

void RampExtractor::Init(float max_frequency) {
max_frequency_ = max_frequency;
audio_rate_period_ = 1.0f / (100.0f / 32000.0f);
audio_rate_period_hysteresis_ = audio_rate_period_;
Reset();
}

void RampExtractor::Reset() {
audio_rate_ = false;
train_phase_ = 0.0f;
target_frequency_ = frequency_ = 0.0001f;
lp_coefficient_ = 0.5f;
next_max_train_phase_ = max_train_phase_ = 0.999f;
next_f_ratio_ = f_ratio_ = 1.0f;
reset_counter_ = 1;
reset_frequency_ = 0.0f;
reset_interval_ = 32000 * 3;
Pulse p;
p.bucket = 1;
p.on_duration = 2000;
p.total_duration = 4000;
p.pulse_width = 0.5f;
fill(&history_[0], &history_[kHistorySize], p);

current_pulse_ = 0;
next_bucket_ = 48.0f;
average_pulse_width_ = 0.0f;
fill(&predicted_period_[0], &predicted_period_[PREDICTOR_LAST], 4000.0f);
fill(&prediction_accuracy_[0], &prediction_accuracy_[PREDICTOR_LAST], 0.0f);
fill(
&prediction_hash_table_[0],
&prediction_hash_table_[kHashTableSize],
0.0f);
}

float RampExtractor::ComputeAveragePulseWidth(float tolerance) const {
float sum = 0.0f;
for (size_t i = 0; i < kHistorySize; ++i) {
if (!IsWithinTolerance(history_[i].pulse_width,
history_[current_pulse_].pulse_width,
tolerance)) {
return 0.0f;
}
sum += history_[i].pulse_width;
}
return sum / static_cast<float>(kHistorySize);
}

RampExtractor::Prediction RampExtractor::PredictNextPeriod() {
float last_period = static_cast<float>(history_[current_pulse_].total_duration);
Predictor best_predictor = PREDICTOR_FAST_MOVING_AVERAGE;

for (int i = PREDICTOR_FAST_MOVING_AVERAGE; i < PREDICTOR_LAST; ++i) {
float error = (predicted_period_[i] - last_period) / (last_period + 0.01f);
// Scoring function: 10% error is half as good as 0% error.
float accuracy = 1.0f / (1.0f + 100.0f * error * error);
// Slowly trust good predictors, quickly demote predictors who make errors.
SLOPE(prediction_accuracy_[i], accuracy, 0.1f, 0.5f);

// (Ugly code but I don't want virtuals for these.)
switch (i) {
case PREDICTOR_SLOW_MOVING_AVERAGE:
ONE_POLE(predicted_period_[i], last_period, 0.1f);
break;

case PREDICTOR_FAST_MOVING_AVERAGE:
ONE_POLE(predicted_period_[i], last_period, 0.5f);
break;

case PREDICTOR_HASH:
{
size_t t_2 = (current_pulse_ - 2 + kHistorySize) % kHistorySize;
size_t t_1 = (current_pulse_ - 1 + kHistorySize) % kHistorySize;
size_t t_0 = current_pulse_;
size_t hash = history_[t_1].bucket + 17 * history_[t_2].bucket;
ONE_POLE(
prediction_hash_table_[hash % kHashTableSize],
last_period,
0.5f);
hash = history_[t_0].bucket + 17 * history_[t_1].bucket;
predicted_period_[i] = prediction_hash_table_[hash % kHashTableSize];
if (predicted_period_[i] == 0.0f) {
predicted_period_[i] = last_period;
}
}
break;
default:
{
// Periodicity detector.
size_t candidate_period = i - PREDICTOR_PERIOD_1 + 1;
size_t t = current_pulse_ + 1 + kHistorySize - candidate_period;
predicted_period_[i] = history_[t % kHistorySize].total_duration;
}
break;
}
if (prediction_accuracy_[i] >= prediction_accuracy_[best_predictor]) {
best_predictor = Predictor(i);
}
}
Prediction p;
p.period = predicted_period_[best_predictor];
p.accuracy = prediction_accuracy_[best_predictor];
return p;
}

void RampExtractor::Process(
Ratio ratio,
bool always_ramp_to_maximum,
const GateFlags* gate_flags,
float* ramp,
size_t size) {
while (size--) {
GateFlags flags = *gate_flags++;
// We are done with the previous pulse.
if (flags & GATE_FLAG_RISING) {
Pulse& p = history_[current_pulse_];
const bool record_pulse = p.total_duration < reset_interval_;
if (!record_pulse) {
// Quite a long pause - the clock has probably been stopped
// and restarted.
reset_frequency_ = 0.0f;
train_phase_ = 0.0f;
reset_counter_ = ratio.q;
reset_interval_ = 4 * p.total_duration;
} else {
float period = float(p.total_duration);
if (period <= audio_rate_period_hysteresis_) {
audio_rate_ = true;
audio_rate_period_hysteresis_ = audio_rate_period_ * 1.1f;

average_pulse_width_ = 0.0f;
bool no_glide = f_ratio_ != ratio.to_float();
f_ratio_ = ratio.to_float();
float frequency = 1.0f / period;
target_frequency_ = std::min(f_ratio_ * frequency, max_frequency_);
float up_tolerance = (1.02f + 2.0f * frequency) * frequency_;
float down_tolerance = (0.98f - 2.0f * frequency) * frequency_;
no_glide |= target_frequency_ > up_tolerance ||
target_frequency_ < down_tolerance;
lp_coefficient_ = no_glide ? 1.0f : period * 0.00001f;
} else {
audio_rate_ = false;
audio_rate_period_hysteresis_ = audio_rate_period_;

// Compute the pulse width of the previous pulse, and check if the
// PW has been consistent over the past pulses.
p.pulse_width = static_cast<float>(p.on_duration) / period;
average_pulse_width_ = ComputeAveragePulseWidth(kPulseWidthTolerance);
if (p.on_duration < 32) {
average_pulse_width_ = 0.0f;
}

// Try to predict the next interval between pulses. If the prediction
// has been reliable over the past pulses, or if the PW is steady,
// we'll be able to make reliable prediction about the time at which
// the next pulse will occur
Prediction prediction = PredictNextPeriod();
frequency_ = 1.0f / prediction.period;
--reset_counter_;
if (!reset_counter_) {
next_f_ratio_ = ratio.to_float() * kMaxRampValue;
next_max_train_phase_ = static_cast<float>(ratio.q);
if (always_ramp_to_maximum && train_phase_ < max_train_phase_) {
reset_frequency_ = \
(0.01f + max_train_phase_ - train_phase_) * 0.0625f;
} else {
reset_frequency_ = 0.0f;
train_phase_ = 0.0f;
f_ratio_ = next_f_ratio_;
max_train_phase_ = next_max_train_phase_;
}
reset_counter_ = ratio.q;
} else {
float expected = max_train_phase_ - static_cast<float>(reset_counter_);
float warp = expected - train_phase_ + 1.0f;
frequency_ *= max(warp, 0.01f);
}
}
reset_interval_ = static_cast<uint32_t>(
std::max(4.0f / target_frequency_, 32000 * 3.0f));
current_pulse_ = (current_pulse_ + 1) % kHistorySize;
}
history_[current_pulse_].on_duration = 0;
history_[current_pulse_].total_duration = 0;
history_[current_pulse_].bucket = 0;
next_bucket_ = 48.0f;
}
// Update history buffer with total duration and on duration.
++history_[current_pulse_].total_duration;
if (flags & GATE_FLAG_HIGH) {
++history_[current_pulse_].on_duration;
}
if (float(history_[current_pulse_].total_duration) >= next_bucket_) {
++history_[current_pulse_].bucket;
next_bucket_ *= kLogOneFourth;
}
// If the pulse width is constant, and if a clock falling edge is
// detected, estimate the period using the on time and the pulse width,
// and correct the phase increment accordingly.
if ((flags & GATE_FLAG_FALLING) &&
average_pulse_width_ > 0.0f) {
float t_on = static_cast<float>(history_[current_pulse_].on_duration);
float next = max_train_phase_ - static_cast<float>(reset_counter_) + 1.0f;
float pw = average_pulse_width_;
frequency_ = max((next - train_phase_), 0.0f) * pw / ((1.0f - pw) * t_on);
}
if (audio_rate_) {
ONE_POLE(frequency_, target_frequency_, lp_coefficient_);
train_phase_ += frequency_;
if (train_phase_ >= 1.0f) {
train_phase_ -= 1.0f;
}
*ramp++ = train_phase_;
} else {
if (reset_frequency_) {
train_phase_ += reset_frequency_;
if (train_phase_ >= max_train_phase_) {
train_phase_ = 0.0f;
reset_frequency_ = 0.0f;
f_ratio_ = next_f_ratio_;
max_train_phase_ = next_max_train_phase_;
}
} else {
train_phase_ += frequency_;
if (train_phase_ >= max_train_phase_) {
if (frequency_ == max_frequency_) {
train_phase_ -= max_train_phase_;
} else {
train_phase_ = max_train_phase_;
}
}
}
float output_phase = train_phase_ * f_ratio_;
output_phase -= static_cast<float>(static_cast<int>(output_phase));
*ramp++ = output_phase;
}
}
}

} // namespace marbles

+ 132
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_extractor.h View File

@@ -0,0 +1,132 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Recovers a ramp from a clock input by guessing at what time the next edge
// will occur. Prediction strategies:
// - Moving average of previous intervals.
// - Trigram model on quantized intervals.
// - Periodic rhythmic pattern.
// - Assume that the pulse width is constant, deduct the period from the on time
// and the pulse width.
//
// All prediction strategies are concurrently tested, and the output from the
// best performing one is selected (Ă  la early Scheirer/Goto beat trackers).

#ifndef MARBLES_RAMP_RAMP_EXTRACTOR_H_
#define MARBLES_RAMP_RAMP_EXTRACTOR_H_

#include "stmlib/stmlib.h"
#include "stmlib/utils/gate_flags.h"

#include "marbles/ramp/ramp_divider.h"

namespace marbles {

class RampExtractor {
public:
RampExtractor() { }
~RampExtractor() { }
void Init(float max_frequency);
void Process(
Ratio r,
bool always_ramp_to_maximum,
const stmlib::GateFlags* gate_flags,
float* ramp,
size_t size);
void Reset();
private:
struct Pulse {
uint32_t on_duration;
uint32_t total_duration;
uint32_t bucket; // 4xlog2(total_duration).
float pulse_width;
};
struct Prediction {
float period;
float accuracy;
};
enum Predictor {
PREDICTOR_SLOW_MOVING_AVERAGE,
PREDICTOR_FAST_MOVING_AVERAGE,
PREDICTOR_HASH,
PREDICTOR_PERIOD_1,
PREDICTOR_PERIOD_2,
PREDICTOR_PERIOD_3,
PREDICTOR_PERIOD_4,
PREDICTOR_PERIOD_5,
PREDICTOR_PERIOD_6,
PREDICTOR_PERIOD_7,
PREDICTOR_PERIOD_8,
PREDICTOR_PERIOD_9,
PREDICTOR_PERIOD_10,
PREDICTOR_LAST
};
static const size_t kHistorySize = 16;
static const size_t kHashTableSize = 256;
float ComputeAveragePulseWidth(float tolerance) const;
Prediction PredictNextPeriod();

size_t current_pulse_;
Pulse history_[kHistorySize];
float next_bucket_;
float prediction_hash_table_[kHashTableSize];
float predicted_period_[PREDICTOR_LAST];
float prediction_accuracy_[PREDICTOR_LAST];
float average_pulse_width_;
float train_phase_;
float frequency_;
float max_output_phase_;
float max_train_phase_;
float reset_frequency_;
float target_frequency_;
float lp_coefficient_;
float f_ratio_;
float next_f_ratio_;
float next_max_train_phase_;
int reset_counter_;
uint32_t reset_interval_;
bool audio_rate_;
float max_frequency_;
float audio_rate_period_;
float audio_rate_period_hysteresis_;
DISALLOW_COPY_AND_ASSIGN(RampExtractor);
};

} // namespace marbles

#endif // MARBLES_RAMP_RAMP_EXTRACTOR_H_

+ 63
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/ramp_generator.h View File

@@ -0,0 +1,63 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Simple ramp generator.

#ifndef MARBLES_RAMP_RAMP_GENERATOR_H_
#define MARBLES_RAMP_RAMP_GENERATOR_H_

#include "stmlib/stmlib.h"

namespace marbles {

class RampGenerator {
public:
RampGenerator() { }
~RampGenerator() { }
void Init() {
phase_ = 0.0f;
}

void Render(float frequency, float* out, size_t size) {
while (size--) {
phase_ += frequency;
if (phase_ >= 1.0f) {
phase_ -= 1.0f;
}
*out++ = phase_;
}
}
private:
float phase_;

DISALLOW_COPY_AND_ASSIGN(RampGenerator);
};

} // namespace marbles

#endif // MARBLES_RAMP_RAMP_GENERATOR_H_

+ 132
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ramp/slave_ramp.h View File

@@ -0,0 +1,132 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// A ramp that follows a mater ramp through division/multiplication.

#ifndef MARBLES_RAMP_SLAVE_RAMP_H_
#define MARBLES_RAMP_SLAVE_RAMP_H_

#include "stmlib/stmlib.h"

#include "marbles/ramp/ramp.h"

namespace marbles {

class SlaveRamp {
public:
SlaveRamp() { }
~SlaveRamp() { }

inline void Init() {
phase_ = 0.0f;
max_phase_ = kMaxRampValue;
ratio_ = 1.0f;
pulse_width_ = 0.0f;
target_ = 1.0f;
pulse_length_ = 0;
bernoulli_ = false;
must_complete_ = false;
}

// Initialize with a multiplied/divided rate compared to the master.
inline void Init(int pattern_length, Ratio ratio, float pulse_width) {
bernoulli_ = false;

phase_ = 0.0f;
max_phase_ = static_cast<float>(pattern_length) * kMaxRampValue;
ratio_ = ratio.to_float();
pulse_width_ = pulse_width;
target_ = 1.0f;
pulse_length_ = 0;
}

// Initialize with an adaptive slope: divide the frequency by 2 every time
// we know we won't have to reach 1.0 at the next tick.
inline void Init(
bool must_complete,
float pulse_width,
float expected_value) {
bernoulli_ = true;

if (must_complete_) {
phase_ = 0.0f;
pulse_width_ = pulse_width;
ratio_ = 1.0f;
pulse_length_ = 0;
}

if (!must_complete) {
ratio_ = (1.0f - phase_) * expected_value;
} else {
ratio_ = 1.0f - phase_;
}
must_complete_ = must_complete;
}

inline void Process(float frequency, float* phase, bool* gate) {
float output_phase;
if (bernoulli_) {
phase_ += frequency * ratio_;
output_phase = phase_;
if (output_phase >= 1.0f) {
output_phase = 1.0f;
}
} else {
phase_ += frequency;
if (phase_ >= max_phase_) {
phase_ = max_phase_;
}
output_phase = phase_ * ratio_;
if (output_phase > target_) {
pulse_length_ = 0;
target_ += 1.0f;
}
output_phase -= static_cast<float>(static_cast<int>(output_phase));
}
*phase = output_phase;
*gate = pulse_width_ == 0.0f
? pulse_length_ < 32 && output_phase <= 0.5f
: output_phase < pulse_width_;
++pulse_length_;
}

private:
float phase_;
float max_phase_;
float ratio_;
float pulse_width_;
float target_;
int pulse_length_;

bool bernoulli_;
bool must_complete_;
DISALLOW_COPY_AND_ASSIGN(SlaveRamp);
};

} // namespace marbles

#endif // MARBLES_RAMP_SLAVE_RAMP_H_

+ 115
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/discrete_distribution_quantizer.cc View File

@@ -0,0 +1,115 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Quantize voltages by sampling from a discrete distribution.

#include "marbles/random/discrete_distribution_quantizer.h"

#include "stmlib/dsp/dsp.h"

#include <cmath>
#include <algorithm>

namespace marbles {

using namespace stmlib;
using namespace std;

void DiscreteDistributionQuantizer::Init(const Scale& scale) {
int n = scale.num_degrees;

// We don't want garbage scale data here...
if (!n || n > kMaxDegrees || scale.base_interval == 0.0f) {
return;
}

base_interval_ = scale.base_interval;
base_interval_reciprocal_ = 1.0f / scale.base_interval;
num_cells_ = n + 1;
for (int i = 0; i <= n; ++i) {
float previous_voltage = scale.cell_voltage(i == 0 ? 0 : i - 1);
float next_voltage = scale.cell_voltage(i == n ? n : i + 1);
cells_[i].center = scale.cell_voltage(i);
cells_[i].width = 0.5f * (next_voltage - previous_voltage);
cells_[i].weight = static_cast<float>(scale.degree[i % n].weight) / 256.0f;
}
}

float DiscreteDistributionQuantizer::Process(float value, float amount) {
if (amount < 0.0f) {
return value;
}
float raw_value = value;

// Assuming 1V/Octave and a scale repeating every octave, note_integral
// will store the octave number, and note_fractional the fractional
// pitch class.
const float note = value * base_interval_reciprocal_;
MAKE_INTEGRAL_FRACTIONAL(note);
if (value < 0.0f) {
note_integral -= 1;
note_fractional += 1.0f;
}

// For amount ranging between 0 and 0.25, do not remove notes from the scale
// just crossfade from the unquantized output to the quantized output.
const float scaled_amount = amount < 0.25f ? 0.0f : (amount - 0.25f) * 1.333f;
distribution_.Init();
for (int i = 0; i < num_cells_ - 1; ++i) {
distribution_.AddToken(i, cells_[i].scaled_width(scaled_amount));
}
distribution_.NoMoreTokens();
Distribution::Result r = distribution_.Sample(note_fractional);
float quantized_value = cells_[r.token_id].center;
float offset = static_cast<float>(note_integral) * base_interval_;
quantized_value += offset;
r.start *= base_interval_;
r.start += offset;
if (amount < 0.25f) {
amount *= 4.0f;
float x;
if (r.token_id == 0) {
x = r.fraction - 1.0f;
} else if (r.token_id == num_cells_ - 1) {
x = -r.fraction;
} else {
x = 2.0f * (fabs(r.fraction - 0.5f) - 0.5f);
}
const float slope = amount / (1.01f - amount);
const float y = max(x * slope + 1.0f, 0.0f);
quantized_value -= y * (quantized_value - raw_value);
}
return quantized_value;
}

} // namespace marbles

+ 75
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/discrete_distribution_quantizer.h View File

@@ -0,0 +1,75 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Quantize voltages by sampling from a discrete distribution.

#ifndef MARBLES_RANDOM_DISCRETE_DISTRIBUTION_QUANTIZER_H_
#define MARBLES_RANDOM_DISCRETE_DISTRIBUTION_QUANTIZER_H_

#include "stmlib/stmlib.h"

#include "marbles/random/distributions.h"
#include "marbles/random/quantizer.h"

namespace marbles {

class DiscreteDistributionQuantizer {
public:
typedef DiscreteDistribution<kMaxDegrees> Distribution;
struct Cell {
float center;
float width;
float weight;
inline float scaled_width(float amount) {
float w = 8.0f * (weight - amount) + 0.5f;
CONSTRAIN(w, 0.0f, 1.0f);
return w * width;
}
};
DiscreteDistributionQuantizer() { }
~DiscreteDistributionQuantizer() { }

void Init(const Scale& scale);
float Process(float value, float amount);

private:
float base_interval_;
float base_interval_reciprocal_;
int num_cells_;
Cell cells_[kMaxDegrees + 1];
Distribution distribution_;
DISALLOW_COPY_AND_ASSIGN(DiscreteDistributionQuantizer);
};

} // namespace marbles

#endif // MARBLES_RANDOM_DISCRETE_DISTRIBUTION_QUANTIZER_H_

+ 182
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/distributions.h View File

@@ -0,0 +1,182 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Generates samples from various kinds of random distributions.

#ifndef MARBLES_RANDOM_DISTRIBUTIONS_H_
#define MARBLES_RANDOM_DISTRIBUTIONS_H_

#include "stmlib/stmlib.h"

#include <algorithm>

#include "stmlib/dsp/dsp.h"

#include "marbles/resources.h"

namespace marbles {
const size_t kNumBiasValues = 5;
const size_t kNumRangeValues = 9;
const float kIcdfTableSize = 128.0f;

// Generates samples from beta distribution, from uniformly distributed samples.
// For higher throughput, uses pre-computed tables of inverse cdfs.
inline float BetaDistributionSample(float uniform, float spread, float bias) {
// Tables are pre-computed only for bias <= 0.5. For values above 0.5,
// symmetry is used.
bool flip_result = bias > 0.5f;
if (flip_result) {
uniform = 1.0f - uniform;
bias = 1.0f - bias;
}
bias *= (static_cast<float>(kNumBiasValues) - 1.0f) * 2.0f;
spread *= (static_cast<float>(kNumRangeValues) - 1.0f);
MAKE_INTEGRAL_FRACTIONAL(bias);
MAKE_INTEGRAL_FRACTIONAL(spread);
size_t cell = bias_integral * (kNumRangeValues + 1) + spread_integral;
// Lower 5% and 95% percentiles use a different table with higher resolution.
size_t offset = 0;
if (uniform <= 0.05f) {
offset = kIcdfTableSize + 1;
uniform *= 20.0f;
} else if (uniform >= 0.95f) {
offset = 2 * (kIcdfTableSize + 1);
uniform = (uniform - 0.95f) * 20.0f;
}
float x1y1 = stmlib::Interpolate(
distributions_table[cell] + offset,
uniform,
kIcdfTableSize);
float x2y1 = stmlib::Interpolate(
distributions_table[cell + 1] + offset,
uniform,
kIcdfTableSize);
float x1y2 = stmlib::Interpolate(
distributions_table[cell + kNumRangeValues + 1] + offset,
uniform,
kIcdfTableSize);
float x2y2 = stmlib::Interpolate(
distributions_table[cell + kNumRangeValues + 2] + offset,
uniform,
kIcdfTableSize);
float y1 = x1y1 + (x2y1 - x1y1) * spread_fractional;
float y2 = x1y2 + (x2y2 - x1y2) * spread_fractional;
float y = y1 + (y2 - y1) * bias_fractional;
if (flip_result) {
y = 1.0f - y;
}
return y;
}

// Pre-computed beta(3, 3) with a fatter tail.
inline float FastBetaDistributionSample(float uniform) {
return stmlib::Interpolate(dist_icdf_4_3, uniform, kIcdfTableSize);
}

// Draws samples from a discrete distribution. Used for the quantizer.
// Example:
// * 1 with probability 0.2
// * 20 with probability 0.7
// * 666 with probability 0.1
//
// DiscreteDistribution d;
// d.Init();
// d.AddToken(1, 0.2);
// d.AddToken(20, 0.7);
// d.AddToken(666, 0.1);
// d.NoMoreTokens();
// Result r = d.Sample(u);
// cout << r.token_id;
//
// Weights do not have to add to 1.0f - the class handles normalization.
//
template<size_t size>
class DiscreteDistribution {
public:
DiscreteDistribution() { }
~DiscreteDistribution() { }
void Init() {
sum_ = 0.0f;
num_tokens_ = 1;
cdf_[0] = 0.0f;
token_ids_[0] = 0;
}
void AddToken(int token_id, float weight) {
if (weight <= 0.0f) {
return;
}
sum_ += weight;
token_ids_[num_tokens_] = token_id;
cdf_[num_tokens_] = sum_;
++num_tokens_;
}
void NoMoreTokens() {
token_ids_[num_tokens_] = token_ids_[num_tokens_ - 1];
cdf_[num_tokens_] = sum_ + 1.0f;
}
struct Result {
int token_id;
float fraction;
float start;
float width;
};
inline Result Sample(float u) const {
Result r;
u *= sum_;
int n = std::upper_bound(&cdf_[1], &cdf_[num_tokens_ + 1], u) - &cdf_[0];
float norm = 1.0f / sum_;
r.token_id = token_ids_[n];
r.width = (cdf_[n] - cdf_[n - 1]) * norm;
r.start = (cdf_[n - 1]) * norm;
r.fraction = (u - cdf_[n - 1]) / (cdf_[n] - cdf_[n - 1]);
return r;
}
float sum_;
float cdf_[size + 2];
int token_ids_[size + 2];
int num_tokens_;
DISALLOW_COPY_AND_ASSIGN(DiscreteDistribution);
};

} // namespace marbles

#endif // MARBLES_RANDOM_DISTRIBUTIONS_H_

+ 88
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/lag_processor.cc View File

@@ -0,0 +1,88 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Lag processor for the STEPS control.

#include "marbles/random/lag_processor.h"

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/units.h"

#include "marbles/resources.h"

namespace marbles {

using namespace stmlib;

void LagProcessor::Init() {
ramp_start_ = 0.0f;
ramp_value_ = 0.0f;
lp_state_ = 0.0f;
previous_phase_ = 0.0f;
}

float LagProcessor::Process(float value, float smoothness, float phase) {
float frequency = phase - previous_phase_;
if (frequency < 0.0f) {
frequency += 1.0f;
}
previous_phase_ = phase;
// The frequency of the portamento/glide LP filter follows an exponential
// scale, with a minimum frequency corresponding to half the clock pulse
// frequency (giving a roughly linear glide), and a maximum value 7 octaves
// above.
//
// When smoothness approaches 0, the response curve is tweaked to give
// immediate voltage changes, without any lag.
frequency *= 0.25f;
frequency *= SemitonesToRatio(84.0f * (1.0f - smoothness));
if (frequency >= 1.0f) {
frequency = 1.0f;
}
if (smoothness <= 0.05f) {
frequency += 20.f * (0.05f - smoothness) * (1.0f - frequency);
}

ONE_POLE(lp_state_, value, frequency);
// The final output is a crossfade between a variable shape interpolation and
// the low-pass glide/lag.
float interp_amount = (smoothness - 0.6f) * 5.0f;
CONSTRAIN(interp_amount, 0.0f, 1.0f);
float interp_linearity = (1.0f - smoothness) * 5.0f;
CONSTRAIN(interp_linearity, 0.0f, 1.0f);
float warped_phase = Interpolate(lut_raised_cosine, phase, 256.0f);
float interp_phase = Crossfade(warped_phase, phase, interp_linearity);
float interp = Crossfade(ramp_start_, value, interp_phase);
ramp_value_ = interp;
return Crossfade(lp_state_, interp, interp_amount);
}

} // namespace marbles

+ 61
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/lag_processor.h View File

@@ -0,0 +1,61 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Lag processor for the STEPS control.

#ifndef MARBLES_RANDOM_LAG_PROCESSOR_H_
#define MARBLES_RANDOM_LAG_PROCESSOR_H_

#include "stmlib/stmlib.h"

#include <cstdio>

namespace marbles {

class LagProcessor {
public:
LagProcessor() { }
~LagProcessor() { }
void Init();
inline void ResetRamp() {
ramp_start_ = ramp_value_;
}
float Process(float value, float smoothness, float phase);

private:
float ramp_start_;
float ramp_value_;
float lp_state_;
float previous_phase_;
DISALLOW_COPY_AND_ASSIGN(LagProcessor);
};

} // namespace marbles

#endif // MARBLES_RANDOM_LAG_PROCESSOR_H_

+ 145
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/output_channel.cc View File

@@ -0,0 +1,145 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Random generation channel.

#include "marbles/random/output_channel.h"

#include "marbles/random/distributions.h"
#include "marbles/random/random_sequence.h"

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/parameter_interpolator.h"
#include "stmlib/utils/random.h"

namespace marbles {

using namespace stmlib;

const size_t kNumReacquisitions = 20; // 6.4 samples per millisecond

void OutputChannel::Init() {
spread_ = 0.5f;
bias_ = 0.5f;
steps_ = 0.5f;
scale_index_ = 0;

register_mode_ = false;
register_value_ = 0.0f;
register_transposition_ = 0.0f;

previous_steps_ = 0.0f;
previous_phase_ = 0.0f;
reacquisition_counter_ = 0;
previous_voltage_ = 0.0f;
voltage_ = 0.0f;
quantized_voltage_ = 0.0f;
scale_offset_ = ScaleOffset(10.0f, -5.0f);
lag_processor_.Init();
Scale scale;
scale.Init();
for (int i = 0; i < 6; ++i) {
quantizer_[i].Init(scale);
}
}

float OutputChannel::GenerateNewVoltage(RandomSequence* random_sequence) {
float u = random_sequence->NextValue(register_mode_, register_value_);
if (register_mode_) {
return 10.0f * (u - 0.5f) + register_transposition_;
} else {
float degenerate_amount = 1.25f - spread_ * 25.0f;
float bernoulli_amount = spread_ * 25.0f - 23.75f;
CONSTRAIN(degenerate_amount, 0.0f, 1.0f);
CONSTRAIN(bernoulli_amount, 0.0f, 1.0f);

float value = BetaDistributionSample(u, spread_, bias_);
float bernoulli_value = u >= (1.0f - bias_) ? 0.999999f : 0.0f;
value += degenerate_amount * (bias_ - value);
value += bernoulli_amount * (bernoulli_value - value);
return scale_offset_(value);
}
}

void OutputChannel::Process(
RandomSequence* random_sequence,
const float* phase,
float* output,
size_t size,
size_t stride) {
ParameterInterpolator steps_modulation(
&previous_steps_, steps_, size);
// This is a horrible hack that wouldn't be here if all the sequencers
// and MIDI/CV interfaces in this world didn't have *horrible* slew on
// their CV output (I'm looking at you KORG).
// Without this hack, the shift register gets its value as soon as the
// rising edge is observed on the GATE input. Problem: the CV input is
// probably still slewing up, so we acquire the wrong value in the shift
// register. What to do then? Over the next 2ms, we'll just track the CV
// input until it reaches its final value - which means that Marbles
// output will be slewed too. Another option would have been to wait 2ms
// between the rising edge and the actual acquisition, but we don't want
// to penalize people who use tighter sequencers.
if (reacquisition_counter_) {
--reacquisition_counter_;
float u = random_sequence->RewriteValue(register_value_);
voltage_ = 10.0f * (u - 0.5f) + register_transposition_;
quantized_voltage_ = Quantize(voltage_, 2.0f * steps_ - 1.0f);
}
while (size--) {
const float steps = steps_modulation.Next();
if (*phase < previous_phase_) {
previous_voltage_ = voltage_;
voltage_ = GenerateNewVoltage(random_sequence);
lag_processor_.ResetRamp();
quantized_voltage_ = Quantize(voltage_, 2.0f * steps - 1.0f);
if (register_mode_) {
reacquisition_counter_ = kNumReacquisitions;
}
}
if (steps >= 0.5f) {
*output = quantized_voltage_;
} else {
const float smoothness = 1.0f - 2.0f * steps;
*output = lag_processor_.Process(voltage_, smoothness, *phase);
}
output += stride;
previous_phase_ = *phase++;
}
}

} // namespace marbles

+ 139
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/output_channel.h View File

@@ -0,0 +1,139 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Random generation channel.

#ifndef MARBLES_RANDOM_OUTPUT_CHANNEL_H_
#define MARBLES_RANDOM_OUTPUT_CHANNEL_H_

#include "stmlib/stmlib.h"

#include "marbles/random/lag_processor.h"
#include "marbles/random/quantizer.h"

namespace marbles {

class RandomSequence;

struct ScaleOffset {
ScaleOffset(float s, float o) {
scale = s;
offset = o;
}
ScaleOffset() { scale = 1.0f; offset = 0.0f; }
float scale;
float offset;
inline float operator()(float x) { return x * scale + offset; }
};

class OutputChannel {
public:
OutputChannel() { }
~OutputChannel() { }
void Init();
void LoadScale(int i, const Scale& scale) {
quantizer_[i].Init(scale);
}
void Process(
RandomSequence* random_sequence,
const float* phase,
float* output,
size_t size,
size_t stride);

inline void set_spread(float spread) {
spread_ = spread;
}
inline void set_bias(float bias) {
bias_ = bias;
}
inline void set_scale_index(int i) {
scale_index_ = i;
}
inline void set_steps(float steps) {
steps_ = steps;
}
inline void set_register_mode(bool register_mode) {
register_mode_ = register_mode;
}

inline void set_register_value(float register_value) {
register_value_ = register_value;
}
inline void set_register_transposition(float register_transposition) {
register_transposition_ = register_transposition;
}
inline void set_scale_offset(const ScaleOffset& scale_offset) {
scale_offset_ = scale_offset;
}
inline float Quantize(float voltage, float amount) {
return quantizer_[scale_index_].Process(voltage, amount, false);
}
private:
float GenerateNewVoltage(RandomSequence* random_sequence);
float spread_;
float bias_;
float steps_;
int scale_index_;
bool register_mode_;
float register_value_;
float register_transposition_;
float previous_steps_;
float previous_phase_;
uint32_t reacquisition_counter_;
float previous_voltage_;
float voltage_;
float quantized_voltage_;
ScaleOffset scale_offset_;
LagProcessor lag_processor_;
Quantizer quantizer_[6];
DISALLOW_COPY_AND_ASSIGN(OutputChannel);
};

} // namespace marbles

#endif // MARBLES_RANDOM_OUTPUT_CHANNEL_H_

+ 138
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/quantizer.cc View File

@@ -0,0 +1,138 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Variable resolution quantizer.

#include "marbles/random/quantizer.h"

#include "stmlib/dsp/dsp.h"

#include <cmath>
#include <algorithm>

namespace marbles {

using namespace std;

void Quantizer::Init(const Scale& scale) {
int n = scale.num_degrees;

// We don't want garbage scale data here...
if (!n || n > kMaxDegrees || scale.base_interval == 0.0f) {
return;
}

num_degrees_ = n;
base_interval_ = scale.base_interval;
base_interval_reciprocal_ = 1.0f / scale.base_interval;
uint8_t second_largest_threshold = 0;
for (int i = 0; i < n; ++i) {
voltage_[i] = scale.degree[i].voltage;
if (scale.degree[i].weight != 255 && \
scale.degree[i].weight >= second_largest_threshold) {
second_largest_threshold = scale.degree[i].weight;
}
}
uint8_t thresholds_[kNumThresholds] = {
0, 16, 32, 64, 128, 192, 255
};
if (second_largest_threshold > 192) {
// Be more selective to only include the notes at rank 1 and 2 at
// the last but one position.
thresholds_[kNumThresholds - 2] = second_largest_threshold;
}
for (int t = 0; t < kNumThresholds; ++t) {
uint16_t bitmask = 0;
uint8_t first = 0xff;
uint8_t last = 0;
for (int i = 0; i < n; ++i) {
if (scale.degree[i].weight >= thresholds_[t]) {
bitmask |= 1 << i;
if (first == 0xff) first = i;
last = i;
}
}
level_[t].bitmask = bitmask;
level_[t].first = first;
level_[t].last = last;
}
level_quantizer_.Init();
fill(&feedback_[0], &feedback_[kNumThresholds], 0.0f);
}

float Quantizer::Process(float value, float amount, bool hysteresis) {
int level = level_quantizer_.Process(amount, kNumThresholds + 1);
float quantized_voltage = value;

if (level > 0) {
level -= 1;
float raw_value = value;
if (hysteresis) {
value += feedback_[level];
}

const float note = value * base_interval_reciprocal_;
MAKE_INTEGRAL_FRACTIONAL(note);
if (value < 0.0f) {
note_integral -= 1;
note_fractional += 1.0f;
}
note_fractional *= base_interval_;
// Search for the tightest upper/lower bound in the set of available
// voltages. stl::upper_bound / stl::lower_bound wouldn't work here
// because some entries are masked.
Level l = level_[level];
float a = voltage_[l.last] - base_interval_;
float b = voltage_[l.first] + base_interval_;

uint16_t bitmask = l.bitmask;
for (int i = 0; i < num_degrees_; ++i) {
if (bitmask & 1) {
float v = voltage_[i];
if (note_fractional > v) {
a = v;
} else {
b = v;
break;
}
}
bitmask >>= 1;
}
quantized_voltage = note_fractional < (a + b) * 0.5f ? a : b;
quantized_voltage += static_cast<float>(note_integral) * base_interval_;
feedback_[level] = (quantized_voltage - raw_value) * 0.25f;
}
return quantized_voltage;
}

} // namespace marbles

+ 122
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/quantizer.h View File

@@ -0,0 +1,122 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Variable resolution quantizer.

#ifndef MARBLES_RANDOM_QUANTIZER_H_
#define MARBLES_RANDOM_QUANTIZER_H_

#include "stmlib/stmlib.h"
#include "stmlib/dsp/hysteresis_quantizer.h"

#include "marbles/random/distributions.h"
#include "marbles/random/quantizer.h"

namespace marbles {

const int kMaxDegrees = 16;
const int kNumThresholds = 7;

struct Degree {
float voltage;
uint8_t weight;
};

struct Scale {
float base_interval;
int num_degrees;
Degree degree[kMaxDegrees];

inline float cell_voltage(int i) const {
float transposition = static_cast<float>(i / num_degrees) * base_interval;
return degree[i % num_degrees].voltage + transposition;
}
void Init() {
base_interval = 1.0f;
num_degrees = 1;
degree[0].voltage = 0.0f;
degree[0].weight = 0.0f;
}
void InitMajor() {
const uint8_t major_scale_weights[] = {
255, 16, 128, 16, 192, 64, 8, 224, 16, 96, 32, 160,
};

base_interval = 1.0f;
num_degrees = 12;
for (size_t i = 0; i < 12; ++i) {
degree[i].voltage = static_cast<float>(i) * 0.0833333333f;
degree[i].weight = major_scale_weights[i];
}
}
void InitTenth() {
const uint8_t major_scale_weights[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 25
};

base_interval = 1.0f;
num_degrees = 10;
for (size_t i = 0; i < 10; ++i) {
degree[i].voltage = static_cast<float>(i) * 0.1f;
degree[i].weight = major_scale_weights[i];
}
}
};

class Quantizer {
public:
Quantizer() { }
~Quantizer() { }

void Init(const Scale& scale);

float Process(float value, float amount, bool hysteresis);
private:
struct Level {
uint16_t bitmask; // bitmask of active degrees.
uint8_t first; // index of the first active degree.
uint8_t last; // index of the last active degree.
};
float voltage_[kMaxDegrees];

Level level_[kNumThresholds];
float feedback_[kNumThresholds];
float base_interval_;
float base_interval_reciprocal_;
int num_degrees_;
stmlib::HysteresisQuantizer level_quantizer_;
DISALLOW_COPY_AND_ASSIGN(Quantizer);
};

} // namespace marbles

#endif // MARBLES_RANDOM_QUANTIZER_H_

+ 65
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/random_generator.h View File

@@ -0,0 +1,65 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Pseudo-random generator used as a fallback when we need more random values
// than available in the hardware RNG buffer.

#ifndef MARBLES_RANDOM_RANDOM_GENERATOR_H_
#define MARBLES_RANDOM_RANDOM_GENERATOR_H_

#include "stmlib/stmlib.h"

#include "stmlib/utils/ring_buffer.h"

namespace marbles {

class RandomGenerator {
public:
RandomGenerator() { }
~RandomGenerator() { }
inline void Init(uint32_t seed) {
state_ = seed;
}
inline void Mix(uint32_t word) {
// state_ ^= word;
}
inline uint32_t GetWord() {
state_ = state_ * 1664525L + 1013904223L;
return state_;
}
private:
uint32_t state_;
DISALLOW_COPY_AND_ASSIGN(RandomGenerator);
};

} // namespace marbles

#endif // MARBLES_RANDOM_RANDOM_GENERATOR_H_

+ 228
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/random_sequence.h View File

@@ -0,0 +1,228 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Sequence of random values.

#ifndef MARBLES_RANDOM_RANDOM_SEQUENCE_H_
#define MARBLES_RANDOM_RANDOM_SEQUENCE_H_

#include "stmlib/stmlib.h"

#include "marbles/random/random_stream.h"

#include <algorithm>

namespace marbles {

const int kDejaVuBufferSize = 16;
const int kHistoryBufferSize = 16;

const float kMaxUint32 = 4294967296.0f;

class RandomSequence {
public:
RandomSequence() { }
~RandomSequence() { }
inline void Init(RandomStream* random_stream) {
random_stream_ = random_stream;
for (int i = 0; i < kDejaVuBufferSize; ++i) {
loop_[i] = random_stream_->GetFloat();
}
std::fill(&history_[0], &history_[kHistoryBufferSize], 0.0f);

loop_write_head_ = 0;
length_ = 8;
step_ = 0;

record_head_ = 0;
replay_head_ = -1;
replay_start_ = 0;
deja_vu_ = 0.0f;
replay_hash_ = replay_shift_ = 0;

redo_read_ptr_ = &loop_[0];
redo_write_ptr_ = NULL;
redo_write_history_ptr_ = NULL;
}
inline void Record() {
replay_start_ = record_head_;
replay_head_ = -1;
}
inline void ReplayPseudoRandom(uint32_t hash) {
replay_head_ = replay_start_;
replay_hash_ = hash;
replay_shift_ = 0;
}

inline void ReplayShifted(uint32_t shift) {
replay_head_ = replay_start_;
replay_hash_ = 0;
replay_shift_ = shift;
}
inline float GetReplayValue() const {
uint32_t h = (replay_head_ - 1 - replay_shift_ + \
2 * kHistoryBufferSize) % kHistoryBufferSize;
if (!replay_hash_) {
return history_[h];
} else {
uint32_t word = static_cast<float>(history_[h] * kMaxUint32);
word = (word ^ replay_hash_) * 1664525L + 1013904223L;
return static_cast<float>(word) / kMaxUint32;
}
}
inline float RewriteValue(float value) {
// RewriteValue(x) returns what the most recent call to NextValue would have
// returned if its second argument were x instead. This is used to "rewrite
// history" when the module acquires data from an external source (ASR,
// randomizer or quantizer mode).
if (replay_head_ >= 0) {
return GetReplayValue();
}
if (redo_write_ptr_) {
*redo_write_ptr_ = 1.0f + value;
}
float result = *redo_read_ptr_;
if (result >= 1.0f) {
result -= 1.0f;
} else {
result = 0.5f;
}
if (redo_write_history_ptr_) {
*redo_write_history_ptr_ = result;
}
return result;
}
inline float NextValue(bool deterministic, float value) {
if (replay_head_ >= 0) {
replay_head_ = (replay_head_ + 1) % kHistoryBufferSize;
return GetReplayValue();
}
const float p_sqrt = 2.0f * deja_vu_ - 1.0f;
const float p = p_sqrt * p_sqrt;

if (random_stream_->GetFloat() <= p && deja_vu_ <= 0.5f) {
// Generate a new value and put it at the end of the loop.
redo_write_ptr_ = &loop_[loop_write_head_];
*redo_write_ptr_ = deterministic
? 1.0f + value
: random_stream_->GetFloat();
loop_write_head_ = (loop_write_head_ + 1) % kDejaVuBufferSize;
step_ = length_ - 1;
} else {
// Do not generate a new value, just replay the loop or jump randomly.
// through it.
redo_write_ptr_ = NULL;
if (random_stream_->GetFloat() <= p) {
step_ = static_cast<int>(
random_stream_->GetFloat() * static_cast<float>(length_));
} else {
step_ = step_ + 1;
if (step_ >= length_) {
step_ = 0;
}
}
}
uint32_t i = loop_write_head_ + kDejaVuBufferSize - length_ + step_;
redo_read_ptr_ = &loop_[i % kDejaVuBufferSize];
float result = *redo_read_ptr_;
if (result >= 1.0f) {
result -= 1.0f;
} else if (deterministic) {
// We ask for a deterministic value (shift register), but the loop
// contain random values. return 0.5f in this case!
result = 0.5f;
}
redo_write_history_ptr_ = &history_[record_head_];
*redo_write_history_ptr_ = result;
record_head_ = (record_head_ + 1) % kHistoryBufferSize;
return result;
}
inline void NextVector(float* destination, size_t size) {
float seed = NextValue(false, 0.0f);
uint32_t word = static_cast<float>(seed * kMaxUint32);
while (size--) {
*destination++ = static_cast<float>(word) / kMaxUint32;
word = word * 1664525L + 1013904223L;
}
}
inline void set_deja_vu(float deja_vu) {
deja_vu_ = deja_vu;
}
inline void set_length(int length) {
if (length < 1 || length > kDejaVuBufferSize) {
return;
}
length_ = length;
step_ = step_ % length;
}
inline float deja_vu() const {
return deja_vu_;
}
inline int length() const {
return length_;
}

private:
RandomStream* random_stream_;
float loop_[kDejaVuBufferSize];
float history_[kHistoryBufferSize];
int loop_write_head_;
int length_;
int step_;
// Allows to go back in the past and get the same results again from NextValue
// calls. Allows the 3 X channels to be locked to the same random loop.
int record_head_;
int replay_head_;
int replay_start_;
uint32_t replay_hash_;
uint32_t replay_shift_;
float deja_vu_;
float* redo_read_ptr_;
float* redo_write_ptr_;
float* redo_write_history_ptr_;
DISALLOW_COPY_AND_ASSIGN(RandomSequence);
};

} // namespace marbles

#endif // MARBLES_RANDOM_RANDOM_SEQUENCE_H_

+ 82
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/random_stream.h View File

@@ -0,0 +1,82 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Stream of random values, filled from a hardware RNG, with a fallback
// mechanism.

#ifndef MARBLES_RANDOM_RANDOM_STREAM_H_
#define MARBLES_RANDOM_RANDOM_STREAM_H_

#include "stmlib/stmlib.h"

#include "stmlib/utils/ring_buffer.h"

#include "marbles/random/random_generator.h"

namespace marbles {

class RandomStream {
public:
RandomStream() { }
~RandomStream() { }
inline void Init(RandomGenerator* fallback_generator) {
fallback_generator_ = fallback_generator;
buffer_.Init();
}

inline void Write(uint32_t value) {
// buffer_.Swallow(1);
// buffer_.Overwrite(value);
if (buffer_.writable()) {
buffer_.Overwrite(value);
}
fallback_generator_->Mix(value);
}
inline uint32_t GetWord() {
if (buffer_.readable()) {
return buffer_.ImmediateRead();
} else {
return fallback_generator_->GetWord();
}
}
inline float GetFloat() {
uint32_t word = GetWord();
return static_cast<float>(word) / 4294967296.0f;
}
private:
stmlib::RingBuffer<uint32_t, 128> buffer_;
RandomGenerator* fallback_generator_;
DISALLOW_COPY_AND_ASSIGN(RandomStream);
};

} // namespace marbles

#endif // MARBLES_RANDOM_RANDOM_STREAM_H_

+ 414
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/t_generator.cc View File

@@ -0,0 +1,414 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Generator for the T outputs.

#include "marbles/random/t_generator.h"

#include <algorithm>

#include "stmlib/dsp/units.h"

#include "marbles/resources.h"

namespace marbles {

using namespace std;
using namespace stmlib;

/* static */
DividerPattern TGenerator::divider_patterns[kNumDividerPatterns] = {
{ { { 1, 1 }, { 1, 1 } }, 1 },
{ { { 1, 1 }, { 2, 1 } }, 1 },
{ { { 1, 2 }, { 1, 1 } }, 2 },
{ { { 1, 1 }, { 4, 1 } }, 1 },
{ { { 1, 2 }, { 2, 1 } }, 2 },
{ { { 1, 1 }, { 3, 2 } }, 2 },
{ { { 1, 4 }, { 4, 1 } }, 4 },
{ { { 1, 4 }, { 2, 1 } }, 4 },
{ { { 1, 2 }, { 3, 2 } }, 2 },
{ { { 1, 1 }, { 8, 1 } }, 1 },
{ { { 1, 1 }, { 3, 1 } }, 1 },
{ { { 1, 3 }, { 1, 1 } }, 3 },
{ { { 1, 1 }, { 5, 4 } }, 4 },
{ { { 1, 2 }, { 5, 4 } }, 4 },
{ { { 1, 1 }, { 6, 1 } }, 1 },
{ { { 1, 3 }, { 2, 1 } }, 3 },
{ { { 1, 1 }, { 16, 1 } }, 1 },
};

/* static */
DividerPattern TGenerator::fixed_divider_patterns[kNumDividerPatterns] = {
{ { { 8, 1 }, { 1, 8 } }, 8 },
{ { { 6, 1 }, { 1, 6 } }, 6 },
{ { { 4, 1 }, { 1, 4 } }, 4 },
{ { { 3, 1 }, { 1, 3 } }, 3 },
{ { { 2, 1 }, { 1, 2 } }, 2 },
{ { { 3, 2 }, { 2, 3 } }, 6 },
{ { { 4, 3 }, { 3, 4 } }, 12 },
{ { { 5, 4 }, { 4, 5 } }, 20 },

{ { { 1, 1 }, { 1, 1 } }, 1 },

{ { { 4, 5 }, { 5, 4 } }, 20 },
{ { { 3, 4 }, { 4, 3 } }, 12 },
{ { { 2, 2 }, { 3, 2 } }, 6 },
{ { { 1, 2 }, { 2, 1 } }, 2 },
{ { { 1, 3 }, { 3, 1 } }, 3 },
{ { { 1, 4 }, { 4, 1 } }, 4 },
{ { { 1, 6 }, { 6, 1 } }, 6 },
{ { { 1, 8 }, { 8, 1 } }, 8 },
};

/* static */
Ratio TGenerator::input_divider_ratios[kNumInputDividerRatios] = {
{ 1, 4 },
{ 1, 3 },
{ 1, 2 },
{ 2, 3 },
{ 1, 1 },
{ 3, 2 },
{ 2, 1 },
{ 3, 1 },
{ 4, 1 },
};

/* static */
uint8_t TGenerator::drum_patterns[kNumDrumPatterns][kDrumPatternSize] = {
{ 1, 0, 0, 0, 2, 0, 0, 0 },
{ 0, 0, 1, 0, 2, 0, 0, 0 },

{ 1, 0, 1, 0, 2, 0, 0, 0 },
{ 0, 0, 1, 0, 2, 0, 0, 2 },

{ 1, 0, 1, 0, 2, 0, 1, 0 },
{ 0, 2, 1, 0, 2, 0, 0, 2 },

{ 1, 0, 0, 0, 2, 0, 1, 0 },
{ 0, 2, 1, 0, 2, 0, 1, 2 },

{ 1, 0, 0, 1, 2, 0, 0, 0 },
{ 0, 2, 1, 1, 2, 0, 1, 2 },

{ 1, 0, 0, 1, 2, 0, 1, 0 },
{ 0, 2, 1, 1, 2, 2, 1, 2 },

{ 1, 0, 0, 1, 2, 0, 1, 2 },
{ 0, 2, 0, 1, 2, 0, 1, 2 },

{ 1, 0, 1, 1, 2, 0, 1, 2 },
{ 2, 0, 1, 2, 0, 1, 2, 0 },

{ 1, 2, 1, 1, 2, 0, 1, 2 },
{ 2, 0, 1, 2, 0, 1, 2, 2 }
};

void TGenerator::Init(RandomStream* random_stream, float sr) {
one_hertz_ = 1.0f / static_cast<float>(sr);
model_ = T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI;
range_ = T_GENERATOR_RANGE_1X;

rate_ = 0.0f;
bias_ = 0.5f;
jitter_ = 0.0f;
pulse_width_mean_ = 0.0f;
pulse_width_std_ = 0.0f;

master_phase_ = 0.0f;
jitter_multiplier_ = 1.0f;
phase_difference_ = 0.0f;
previous_external_ramp_value_ = 0.0f;

divider_pattern_length_ = 0;
fill(&streak_counter_[0], &streak_counter_[kMarkovHistorySize], 0);
fill(&markov_history_[0], &markov_history_[kMarkovHistorySize], 0);
markov_history_ptr_ = 0;
drum_pattern_step_ = 0;
drum_pattern_index_ = 0;

sequence_.Init(random_stream);
ramp_divider_.Init();
ramp_extractor_.Init(1000.0f / sr);
ramp_generator_.Init();
for (size_t i = 0; i < kNumTChannels; ++i) {
slave_ramp_[i].Init();
}
bias_quantizer_.Init();
rate_quantizer_.Init();
use_external_clock_ = false;
}

int TGenerator::GenerateComplementaryBernoulli(const RandomVector& x) {
int bitmask = 0;
for (size_t i = 0; i < kNumTChannels; ++i) {
if ((x.variables.u[i >> 1] > bias_) ^ (i & 1)) {
bitmask |= 1 << i;
}
}
return bitmask;
}

int TGenerator::GenerateIndependentBernoulli(const RandomVector& x) {
int bitmask = 0;
for (size_t i = 0; i < kNumTChannels; ++i) {
if ((x.variables.u[i] > bias_) ^ (i & 1)) {
bitmask |= 1 << i;
}
}
return bitmask;
}

int TGenerator::GenerateThreeStates(const RandomVector& x) {
int bitmask = 0;
float p_none = 0.75f - fabs(bias_ - 0.5f);
float threshold = p_none + (1.0f - p_none) * (0.25f + (bias_ * 0.5f));
for (size_t i = 0; i < kNumTChannels; ++i) {
float u = x.variables.u[i >> 1];
if (u > p_none && ((u > threshold) ^ (i & 1))) {
bitmask |= 1 << i;
}
}
return bitmask;
}

int TGenerator::GenerateDrums(const RandomVector& x) {
++drum_pattern_step_;
if (drum_pattern_step_ >= kDrumPatternSize) {
drum_pattern_step_ = 0;
float u = x.variables.u[0] * 2.0f * fabs(bias_ - 0.5f);
drum_pattern_index_ = static_cast<int32_t>(kNumDrumPatterns * u);
if (bias_ <= 0.5f) {
drum_pattern_index_ -= drum_pattern_index_ % 2;
}
}
return drum_patterns[drum_pattern_index_][drum_pattern_step_];
}

int TGenerator::GenerateMarkov(const RandomVector& x) {
int bitmask = 0;
float b = 1.5f * bias_ - 0.5f;
markov_history_[markov_history_ptr_] = 0;
const int32_t p = markov_history_ptr_;
for (size_t i = 0; i < kNumTChannels; ++i) {
int32_t mask = 1 << i;
// 4 rules:
// * We favor repeating what we played 8 ticks ago.
// * We do not favor pulses appearing on both channels.
// * We favor sparse patterns (no consecutive hits).
// * We favor patterns in which one channel "echoes" what the other
// channel played 4 ticks before.
bool periodic = markov_history_[(p + 8) % kMarkovHistorySize] & mask;
bool simultaneous = markov_history_[(p + 8) % kMarkovHistorySize] & ~mask;
bool dense = markov_history_[(p + 1) % kMarkovHistorySize] & mask;
bool alternate = markov_history_[(p + 4) % kMarkovHistorySize] & ~mask;

float logit = -1.5f;
logit += streak_counter_[i] > 24 ? 10.0f : 0.0f;
logit += 8.0f * fabs(b) * (periodic ? b : -b);
logit -= 2.0f * (simultaneous ? b : -b);
logit -= 1.0f * (dense ? b : 0.0f);
logit += 1.0f * (alternate ? b : 0.0f);
CONSTRAIN(logit, -10.0f, 10.0f);
float probability = lut_logit[static_cast<int>(logit * 12.8f + 128.0f)];
bool state = x.variables.u[i] < probability;
if (sequence_.deja_vu() >= x.variables.p) {
state = markov_history_[(p + sequence_.length()) % kMarkovHistorySize] & mask;
}
if (state) {
bitmask |= mask;
streak_counter_[i] = 0;
} else {
++streak_counter_[i];
}
}
markov_history_[p] |= bitmask;
markov_history_ptr_ = (p + kMarkovHistorySize - 1) % kMarkovHistorySize;
return bitmask;
}

void TGenerator::ScheduleOutputPulses(const RandomVector& x, int bitmask) {
for (size_t i = 0; i < kNumTChannels; ++i) {
slave_ramp_[i].Init(
bitmask & 1,
RandomPulseWidth(i, x.variables.pulse_width[i]),
0.5f);
bitmask >>= 1;
}
}

void TGenerator::ConfigureSlaveRamps(const RandomVector& x) {
switch (model_) {
// Generate a bitmask that will describe which outputs are active
// at this clock tick. Use this bitmask to actually schedule pulses on the
// outputs.
case T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI:
ScheduleOutputPulses(x, GenerateComplementaryBernoulli(x));
break;
case T_GENERATOR_MODEL_INDEPENDENT_BERNOULLI:
ScheduleOutputPulses(x, GenerateIndependentBernoulli(x));
break;

case T_GENERATOR_MODEL_THREE_STATES:
ScheduleOutputPulses(x, GenerateThreeStates(x));
break;
case T_GENERATOR_MODEL_DRUMS:
ScheduleOutputPulses(x, GenerateDrums(x));
break;
case T_GENERATOR_MODEL_MARKOV:
ScheduleOutputPulses(x, GenerateMarkov(x));
break;
case T_GENERATOR_MODEL_CLUSTERS:
case T_GENERATOR_MODEL_DIVIDER:
--divider_pattern_length_;
if (divider_pattern_length_ <= 0) {
DividerPattern pattern;
if (model_ == T_GENERATOR_MODEL_DIVIDER) {
pattern = bias_quantizer_.Lookup(
fixed_divider_patterns,
bias_,
kNumDividerPatterns);
} else {
float strength = fabs(bias_ - 0.5f) * 2.0f;
float u = x.variables.u[0];
u *= (u + strength * strength * (1.0f - u));
u *= strength;
pattern = divider_patterns[static_cast<size_t>(
u * kNumDividerPatterns)];
if (bias_ < 0.5f) {
for (size_t i = 0; i < kNumTChannels / 2; ++i) {
swap(pattern.ratios[i], pattern.ratios[kNumTChannels - 1 - i]);
}
}
}
for (size_t i = 0; i < kNumTChannels; ++i) {
slave_ramp_[i].Init(
pattern.length,
pattern.ratios[i],
RandomPulseWidth(i, x.variables.pulse_width[i]));
}
divider_pattern_length_ = pattern.length;
}
break;
}
}

void TGenerator::Process(
bool use_external_clock,
const GateFlags* external_clock,
Ramps ramps,
bool* gate,
size_t size) {
float internal_frequency;
if (use_external_clock) {
if (!use_external_clock_) {
ramp_extractor_.Reset();
}
Ratio ratio = rate_quantizer_.Lookup(
input_divider_ratios,
1.05f * rate_ / 96.0f + 0.5f,
kNumInputDividerRatios);
if (range_ == T_GENERATOR_RANGE_0_25X) {
ratio.q *= 4;
} else if (range_ == T_GENERATOR_RANGE_4X) {
ratio.p *= 4;
}
ratio.Simplify<2>();
ramp_extractor_.Process(ratio, true, external_clock, ramps.external, size);
internal_frequency = 0.0f;
} else {
float rate = 2.0f;
if (range_ == T_GENERATOR_RANGE_4X) {
rate = 8.0f;
} else if (range_ == T_GENERATOR_RANGE_0_25X) {
rate = 0.5f;
}
internal_frequency = rate * one_hertz_ * SemitonesToRatio(rate_);
}
use_external_clock_ = use_external_clock;
while (size--) {
float frequency = use_external_clock
? *ramps.external - previous_external_ramp_value_
: internal_frequency;
frequency += frequency < 0.0f ? 1.0f : 0.0f;

float jittery_frequency = frequency * jitter_multiplier_;
master_phase_ += jittery_frequency;
phase_difference_ += frequency - jittery_frequency;
if (master_phase_ > 1.0f) {
master_phase_ -= 1.0f;
RandomVector random_vector;
sequence_.NextVector(
random_vector.x,
sizeof(random_vector.x) / sizeof(float));
float jitter_amount = jitter_ * jitter_ * jitter_ * jitter_ * 36.0f;
float x = FastBetaDistributionSample(random_vector.variables.jitter);
float multiplier = SemitonesToRatio((x * 2.0f - 1.0f) * jitter_amount);
// This step is crucial in making sure that the jittered clock does not
// deviate too much from the master clock. The larger the phase difference
// difference between the two, the more likely the jittery clock will
// speed up or down to catch up with the straight clock.
multiplier *= phase_difference_ > 0.0f
? 1.0f + phase_difference_
: 1.0f / (1.0f - phase_difference_);
jitter_multiplier_ = multiplier;
ConfigureSlaveRamps(random_vector);
}
if (internal_frequency) {
*ramps.external = master_phase_;
}
previous_external_ramp_value_ = *ramps.external;
ramps.external++;
*ramps.master++ = master_phase_;
for (size_t j = 0; j < kNumTChannels; ++j) {
slave_ramp_[j].Process(
frequency * jitter_multiplier_,
ramps.slave[j],
gate);
ramps.slave[j]++;
gate++;
}
}
}

} // namespace marbles

+ 206
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/t_generator.h View File

@@ -0,0 +1,206 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Generator for the T outputs.

#ifndef MARBLES_RANDOM_T_GENERATOR_H_
#define MARBLES_RANDOM_T_GENERATOR_H_

#include "stmlib/stmlib.h"

#include "marbles/ramp/ramp_divider.h"
#include "marbles/ramp/ramp_extractor.h"
#include "marbles/ramp/ramp_generator.h"
#include "marbles/ramp/slave_ramp.h"
#include "marbles/random/distributions.h"
#include "marbles/random/random_sequence.h"
#include "stmlib/dsp/hysteresis_quantizer.h"

namespace marbles {

enum TGeneratorModel {
T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI,
T_GENERATOR_MODEL_CLUSTERS,
T_GENERATOR_MODEL_DRUMS,

T_GENERATOR_MODEL_INDEPENDENT_BERNOULLI,
T_GENERATOR_MODEL_DIVIDER,
T_GENERATOR_MODEL_THREE_STATES,
T_GENERATOR_MODEL_MARKOV,
};

enum TGeneratorRange {
T_GENERATOR_RANGE_0_25X,
T_GENERATOR_RANGE_1X,
T_GENERATOR_RANGE_4X,
};

const size_t kNumTChannels = 2;
const size_t kMarkovHistorySize = 16;
const size_t kNumDrumPatterns = 18;
const size_t kDrumPatternSize = 8;

struct DividerPattern {
Ratio ratios[kNumTChannels];
int32_t length;
};

struct Ramps {
float* external;
float* master;
float* slave[kNumTChannels];
};

const size_t kNumDividerPatterns = 17;
const size_t kNumInputDividerRatios = 9;

class TGenerator {
public:
TGenerator() { }
~TGenerator() { }
void Init(RandomStream* random_stream, float sr);
void Process(
bool use_external_clock,
const stmlib::GateFlags* external_clock,
Ramps ramps,
bool* gate,
size_t size);
inline void set_model(TGeneratorModel model) {
model_ = model;
}
inline void set_range(TGeneratorRange range) {
range_ = range;
}
inline void set_rate(float rate) {
rate_ = rate;
}
inline void set_bias(float bias) {
bias_ = bias;
}
inline void set_jitter(float jitter) {
jitter_ = jitter;
}
inline void set_deja_vu(float deja_vu) {
sequence_.set_deja_vu(deja_vu);
}

inline void set_length(int length) {
sequence_.set_length(length);
}

inline void set_pulse_width_mean(float pulse_width_mean) {
pulse_width_mean_ = pulse_width_mean;
}
inline void set_pulse_width_std(float pulse_width_std) {
pulse_width_std_ = pulse_width_std;
}
private:
union RandomVector {
struct {
float pulse_width[kNumTChannels];
float u[kNumTChannels];
float p;
float jitter;
} variables;
float x[2 * kNumTChannels + 2];
};
void ConfigureSlaveRamps(const RandomVector& v);
int GenerateComplementaryBernoulli(const RandomVector& v);
int GenerateIndependentBernoulli(const RandomVector& v);
int GenerateThreeStates(const RandomVector& v);
int GenerateDrums(const RandomVector& v);
int GenerateMarkov(const RandomVector& v);
void ScheduleOutputPulses(const RandomVector& v, int bitmask);

float RandomPulseWidth(int i, float u) {
if (pulse_width_std_ == 0.0f) {
return 0.05f + 0.9f * pulse_width_mean_;
} else {
return 0.05f + 0.9f * BetaDistributionSample(
u,
pulse_width_std_,
pulse_width_mean_); // Jon Brooks
// i & 1 ? 1.0f - pulse_width_mean_);
}
}
float one_hertz_;
TGeneratorModel model_;
TGeneratorRange range_;
float rate_;
float bias_;
float jitter_;
float pulse_width_mean_;
float pulse_width_std_;
float master_phase_;
float jitter_multiplier_;
float phase_difference_;
float previous_external_ramp_value_;
bool use_external_clock_;

int32_t divider_pattern_length_;
int32_t streak_counter_[kMarkovHistorySize];
int32_t markov_history_[kMarkovHistorySize];
int32_t markov_history_ptr_;
size_t drum_pattern_step_;
size_t drum_pattern_index_;

RandomSequence sequence_;
RampDivider ramp_divider_;
RampExtractor ramp_extractor_;
RampGenerator ramp_generator_;

SlaveRamp slave_ramp_[kNumTChannels];
stmlib::HysteresisQuantizer bias_quantizer_;
stmlib::HysteresisQuantizer rate_quantizer_;
static DividerPattern divider_patterns[kNumDividerPatterns];
static DividerPattern fixed_divider_patterns[kNumDividerPatterns];
static Ratio input_divider_ratios[kNumInputDividerRatios];
static uint8_t drum_patterns[kNumDrumPatterns][kDrumPatternSize];
DISALLOW_COPY_AND_ASSIGN(TGenerator);
};

} // namespace marbles

#endif // MARBLES_RANDOM_T_GENERATOR_H_

+ 184
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/x_y_generator.cc View File

@@ -0,0 +1,184 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Generator for the X/Y outputs.

#include "marbles/random/x_y_generator.h"

#include <algorithm>

#include "stmlib/dsp/dsp.h"

namespace marbles {

using namespace std;
using namespace stmlib;

void XYGenerator::Init(RandomStream* random_stream, float sr) {
for (size_t i = 0; i < kNumChannels; ++i) {
random_sequence_[i].Init(random_stream);
output_channel_[i].Init();
}
ramp_extractor_.Init(8000.0f / sr);
ramp_divider_.Init();
external_clock_stabilization_counter_ = 16;
}

const uint32_t hashes[kNumXChannels] = {
0, 0xbeca55e5, 0xf0cacc1a
};

void XYGenerator::Process(
ClockSource clock_source,
const GroupSettings& x_settings,
const GroupSettings& y_settings,
const GateFlags* external_clock,
const Ramps& ramps,
float* output,
size_t size) {
float* channel_ramp[kNumChannels];
if (clock_source != CLOCK_SOURCE_EXTERNAL) {
// For a couple of upcoming blocks, we'll still be receiving garbage from
// the normalization pin that we need to ignore.
external_clock_stabilization_counter_ = 16;
} else {
if (external_clock_stabilization_counter_) {
--external_clock_stabilization_counter_;
if (external_clock_stabilization_counter_ == 0) {
ramp_extractor_.Reset();
}
}
}
switch (clock_source) {
case CLOCK_SOURCE_EXTERNAL:
{
Ratio r = { 1, 1 };
ramp_extractor_.Process(r, false, external_clock, ramps.slave[0], size);
if (external_clock_stabilization_counter_) {
fill(&ramps.slave[0][0], &ramps.slave[0][size], 0.0f);
}
}
channel_ramp[0] = ramps.slave[0];
channel_ramp[1] = ramps.slave[0];
channel_ramp[2] = ramps.slave[0];
break;
case CLOCK_SOURCE_INTERNAL_T1:
channel_ramp[0] = ramps.slave[0];
channel_ramp[1] = ramps.slave[0];
channel_ramp[2] = ramps.slave[0];
break;

case CLOCK_SOURCE_INTERNAL_T2:
channel_ramp[0] = ramps.master;
channel_ramp[1] = ramps.master;
channel_ramp[2] = ramps.master;
break;
case CLOCK_SOURCE_INTERNAL_T3:
channel_ramp[0] = ramps.slave[1];
channel_ramp[1] = ramps.slave[1];
channel_ramp[2] = ramps.slave[1];
break;
default:
channel_ramp[0] = ramps.slave[0];
channel_ramp[1] = ramps.master;
channel_ramp[2] = ramps.slave[1];
break;
}
ramp_divider_.Process(y_settings.ratio, channel_ramp[1], ramps.external, size);
channel_ramp[kNumChannels - 1] = ramps.external;
for (size_t i = 0; i < kNumChannels; ++i) {
OutputChannel& channel = output_channel_[i];
const GroupSettings& settings = i < kNumXChannels ? x_settings : y_settings;
switch (settings.voltage_range) {
case VOLTAGE_RANGE_NARROW:
channel.set_scale_offset(ScaleOffset(2.0f, 0.0f));
break;
case VOLTAGE_RANGE_POSITIVE:
channel.set_scale_offset(ScaleOffset(5.0f, 0.0f));
break;
case VOLTAGE_RANGE_FULL:
channel.set_scale_offset(ScaleOffset(10.0f, -5.0f));
break;
default:
break;
}
float amount = 1.0f;
if (settings.control_mode == CONTROL_MODE_BUMP) {
amount = i == kNumXChannels / 2 ? 1.0f : -1.0f;
} else if (settings.control_mode == CONTROL_MODE_TILT) {
amount = 2.0f * static_cast<float>(i) / float(kNumXChannels - 1) - 1.0f;
}
channel.set_spread(0.5f + (settings.spread - 0.5f) * amount);
channel.set_bias(0.5f + (settings.bias - 0.5f) * amount);
channel.set_steps(0.5f + (settings.steps - 0.5f) * \
(settings.register_mode ? 1.0f : amount));
channel.set_scale_index(settings.scale_index);
channel.set_register_mode(settings.register_mode);
channel.set_register_value(settings.register_value);
channel.set_register_transposition(
4.0f * settings.spread * (settings.bias - 0.5f) * amount);
RandomSequence* sequence = &random_sequence_[i];
sequence->Record();
sequence->set_length(settings.length);
sequence->set_deja_vu(settings.deja_vu);
// When all channels follow the same clock, the deja-vu random looping will
// follow the same pattern and the constant-mode input will be shifted!
if (clock_source != CLOCK_SOURCE_INTERNAL_T1_T2_T3
&& i > 0 && i < kNumXChannels) {
sequence = &random_sequence_[0];
if (settings.register_mode) {
if (settings.control_mode == CONTROL_MODE_IDENTICAL) {
sequence->ReplayShifted(i);
} else if (settings.control_mode == CONTROL_MODE_BUMP) {
sequence->ReplayShifted(i == 2 ? 1 : 0);
} else {
sequence->ReplayShifted(0);
}
} else {
sequence->ReplayPseudoRandom(hashes[i]);
}
}
channel.Process(sequence, channel_ramp[i], &output[i], size, kNumChannels);
}
}

} // namespace marbles

+ 123
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/random/x_y_generator.h View File

@@ -0,0 +1,123 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Generator for the X/Y outputs.

#ifndef MARBLES_RANDOM_X_Y_GENERATOR_H_
#define MARBLES_RANDOM_X_Y_GENERATOR_H_

#include "stmlib/stmlib.h"

#include "marbles/ramp/ramp_divider.h"
#include "marbles/ramp/ramp_extractor.h"
#include "marbles/random/output_channel.h"
#include "marbles/random/random_sequence.h"
#include "marbles/random/t_generator.h"

namespace marbles {

enum VoltageRange {
VOLTAGE_RANGE_NARROW, // +2V
VOLTAGE_RANGE_POSITIVE, // +5V
VOLTAGE_RANGE_FULL // +/- 5V
};

enum ClockSource {
CLOCK_SOURCE_INTERNAL_T1_T2_T3,
CLOCK_SOURCE_INTERNAL_T1,
CLOCK_SOURCE_INTERNAL_T2,
CLOCK_SOURCE_INTERNAL_T3,
CLOCK_SOURCE_EXTERNAL
};

enum ControlMode {
CONTROL_MODE_IDENTICAL,
CONTROL_MODE_BUMP,
CONTROL_MODE_TILT
};

enum OutputGroup {
OUTPUT_GROUP_X,
OUTPUT_GROUP_Y,
OUTPUT_GROUP_LAST
};

const size_t kNumXChannels = 3;
const size_t kNumYChannels = 1;
const size_t kNumChannels = kNumXChannels + kNumYChannels;

struct GroupSettings {
ControlMode control_mode;
VoltageRange voltage_range;
bool register_mode;
float register_value;
float spread;
float bias;
float steps;
float deja_vu;
int scale_index;
int length;
Ratio ratio;
};

class XYGenerator {
public:
XYGenerator() { }
~XYGenerator() { }
void Init(RandomStream* random_stream, float sr);
void Process(
ClockSource clock_source,
const GroupSettings& x_settings,
const GroupSettings& y_settings,
const stmlib::GateFlags* external_clock,
const Ramps& ramps,
float* output,
size_t size);
void LoadScale(int channel, int scale_index, const Scale& scale) {
output_channel_[channel].LoadScale(scale_index, scale);
}
void LoadScale(int scale_index, const Scale& scale) {
for (size_t i = 0; i < kNumXChannels; ++i) {
output_channel_[i].LoadScale(scale_index, scale);
}
}
private:
RandomSequence random_sequence_[kNumChannels];
OutputChannel output_channel_[kNumChannels];
RampExtractor ramp_extractor_;
RampDivider ramp_divider_;
int external_clock_stabilization_counter_;
DISALLOW_COPY_AND_ASSIGN(XYGenerator);
};

} // namespace marbles

#endif // MARBLES_RANDOM_X_Y_GENERATOR_H_

+ 4767
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/resources.cc
File diff suppressed because it is too large
View File


+ 226
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/resources.h View File

@@ -0,0 +1,226 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Resources definitions.
//
// Automatically generated with:
// make resources


#ifndef MARBLES_RESOURCES_H_
#define MARBLES_RESOURCES_H_


#include "stmlib/stmlib.h"



namespace marbles {

typedef uint8_t ResourceId;

extern const float* lookup_table_table[];

extern const float* distributions_table[];

extern const float lut_raised_cosine[];
extern const float lut_sine[];
extern const float lut_logit[];
extern const float dist_icdf_0_0[];
extern const float dist_icdf_0_1[];
extern const float dist_icdf_0_2[];
extern const float dist_icdf_0_3[];
extern const float dist_icdf_0_4[];
extern const float dist_icdf_0_5[];
extern const float dist_icdf_0_6[];
extern const float dist_icdf_0_7[];
extern const float dist_icdf_0_8[];
extern const float dist_icdf_1_0[];
extern const float dist_icdf_1_1[];
extern const float dist_icdf_1_2[];
extern const float dist_icdf_1_3[];
extern const float dist_icdf_1_4[];
extern const float dist_icdf_1_5[];
extern const float dist_icdf_1_6[];
extern const float dist_icdf_1_7[];
extern const float dist_icdf_1_8[];
extern const float dist_icdf_2_0[];
extern const float dist_icdf_2_1[];
extern const float dist_icdf_2_2[];
extern const float dist_icdf_2_3[];
extern const float dist_icdf_2_4[];
extern const float dist_icdf_2_5[];
extern const float dist_icdf_2_6[];
extern const float dist_icdf_2_7[];
extern const float dist_icdf_2_8[];
extern const float dist_icdf_3_0[];
extern const float dist_icdf_3_1[];
extern const float dist_icdf_3_2[];
extern const float dist_icdf_3_3[];
extern const float dist_icdf_3_4[];
extern const float dist_icdf_3_5[];
extern const float dist_icdf_3_6[];
extern const float dist_icdf_3_7[];
extern const float dist_icdf_3_8[];
extern const float dist_icdf_4_0[];
extern const float dist_icdf_4_1[];
extern const float dist_icdf_4_2[];
extern const float dist_icdf_4_3[];
extern const float dist_icdf_4_4[];
extern const float dist_icdf_4_5[];
extern const float dist_icdf_4_6[];
extern const float dist_icdf_4_7[];
extern const float dist_icdf_4_8[];
#define LUT_RAISED_COSINE 0
#define LUT_RAISED_COSINE_SIZE 257
#define LUT_SINE 1
#define LUT_SINE_SIZE 257
#define LUT_LOGIT 2
#define LUT_LOGIT_SIZE 257
#define DIST_ICDF_0_0 0
#define DIST_ICDF_0_0_SIZE 387
#define DIST_ICDF_0_1 1
#define DIST_ICDF_0_1_SIZE 387
#define DIST_ICDF_0_2 2
#define DIST_ICDF_0_2_SIZE 387
#define DIST_ICDF_0_3 3
#define DIST_ICDF_0_3_SIZE 387
#define DIST_ICDF_0_4 4
#define DIST_ICDF_0_4_SIZE 387
#define DIST_ICDF_0_5 5
#define DIST_ICDF_0_5_SIZE 387
#define DIST_ICDF_0_6 6
#define DIST_ICDF_0_6_SIZE 387
#define DIST_ICDF_0_7 7
#define DIST_ICDF_0_7_SIZE 387
#define DIST_ICDF_0_8 8
#define DIST_ICDF_0_8_SIZE 387
#define DIST_ICDF_0_8_GUARD 9
#define DIST_ICDF_0_8_GUARD_SIZE 387
#define DIST_ICDF_1_0 10
#define DIST_ICDF_1_0_SIZE 387
#define DIST_ICDF_1_1 11
#define DIST_ICDF_1_1_SIZE 387
#define DIST_ICDF_1_2 12
#define DIST_ICDF_1_2_SIZE 387
#define DIST_ICDF_1_3 13
#define DIST_ICDF_1_3_SIZE 387
#define DIST_ICDF_1_4 14
#define DIST_ICDF_1_4_SIZE 387
#define DIST_ICDF_1_5 15
#define DIST_ICDF_1_5_SIZE 387
#define DIST_ICDF_1_6 16
#define DIST_ICDF_1_6_SIZE 387
#define DIST_ICDF_1_7 17
#define DIST_ICDF_1_7_SIZE 387
#define DIST_ICDF_1_8 18
#define DIST_ICDF_1_8_SIZE 387
#define DIST_ICDF_1_8_GUARD 19
#define DIST_ICDF_1_8_GUARD_SIZE 387
#define DIST_ICDF_2_0 20
#define DIST_ICDF_2_0_SIZE 387
#define DIST_ICDF_2_1 21
#define DIST_ICDF_2_1_SIZE 387
#define DIST_ICDF_2_2 22
#define DIST_ICDF_2_2_SIZE 387
#define DIST_ICDF_2_3 23
#define DIST_ICDF_2_3_SIZE 387
#define DIST_ICDF_2_4 24
#define DIST_ICDF_2_4_SIZE 387
#define DIST_ICDF_2_5 25
#define DIST_ICDF_2_5_SIZE 387
#define DIST_ICDF_2_6 26
#define DIST_ICDF_2_6_SIZE 387
#define DIST_ICDF_2_7 27
#define DIST_ICDF_2_7_SIZE 387
#define DIST_ICDF_2_8 28
#define DIST_ICDF_2_8_SIZE 387
#define DIST_ICDF_2_8_GUARD 29
#define DIST_ICDF_2_8_GUARD_SIZE 387
#define DIST_ICDF_3_0 30
#define DIST_ICDF_3_0_SIZE 387
#define DIST_ICDF_3_1 31
#define DIST_ICDF_3_1_SIZE 387
#define DIST_ICDF_3_2 32
#define DIST_ICDF_3_2_SIZE 387
#define DIST_ICDF_3_3 33
#define DIST_ICDF_3_3_SIZE 387
#define DIST_ICDF_3_4 34
#define DIST_ICDF_3_4_SIZE 387
#define DIST_ICDF_3_5 35
#define DIST_ICDF_3_5_SIZE 387
#define DIST_ICDF_3_6 36
#define DIST_ICDF_3_6_SIZE 387
#define DIST_ICDF_3_7 37
#define DIST_ICDF_3_7_SIZE 387
#define DIST_ICDF_3_8 38
#define DIST_ICDF_3_8_SIZE 387
#define DIST_ICDF_3_8_GUARD 39
#define DIST_ICDF_3_8_GUARD_SIZE 387
#define DIST_ICDF_4_0 40
#define DIST_ICDF_4_0_SIZE 387
#define DIST_ICDF_4_1 41
#define DIST_ICDF_4_1_SIZE 387
#define DIST_ICDF_4_2 42
#define DIST_ICDF_4_2_SIZE 387
#define DIST_ICDF_4_3 43
#define DIST_ICDF_4_3_SIZE 387
#define DIST_ICDF_4_4 44
#define DIST_ICDF_4_4_SIZE 387
#define DIST_ICDF_4_5 45
#define DIST_ICDF_4_5_SIZE 387
#define DIST_ICDF_4_6 46
#define DIST_ICDF_4_6_SIZE 387
#define DIST_ICDF_4_7 47
#define DIST_ICDF_4_7_SIZE 387
#define DIST_ICDF_4_8 48
#define DIST_ICDF_4_8_SIZE 387
#define DIST_ICDF_4_8_GUARD 49
#define DIST_ICDF_4_8_GUARD_SIZE 387
#define DIST_ICDF_4_0_GUARD 50
#define DIST_ICDF_4_0_GUARD_SIZE 387
#define DIST_ICDF_4_1_GUARD 51
#define DIST_ICDF_4_1_GUARD_SIZE 387
#define DIST_ICDF_4_2_GUARD 52
#define DIST_ICDF_4_2_GUARD_SIZE 387
#define DIST_ICDF_4_3_GUARD 53
#define DIST_ICDF_4_3_GUARD_SIZE 387
#define DIST_ICDF_4_4_GUARD 54
#define DIST_ICDF_4_4_GUARD_SIZE 387
#define DIST_ICDF_4_5_GUARD 55
#define DIST_ICDF_4_5_GUARD_SIZE 387
#define DIST_ICDF_4_6_GUARD 56
#define DIST_ICDF_4_6_GUARD_SIZE 387
#define DIST_ICDF_4_7_GUARD 57
#define DIST_ICDF_4_7_GUARD_SIZE 387
#define DIST__ICDF_4_8_GUARD 58
#define DIST__ICDF_4_8_GUARD_SIZE 387
#define DIST_ICDF_4_8_GUARD_GUARD 59
#define DIST_ICDF_4_8_GUARD_GUARD_SIZE 387

} // namespace marbles

#endif // MARBLES_RESOURCES_H_

+ 0
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/resources/__init__.py View File


+ 109
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/resources/lookup_tables.py View File

@@ -0,0 +1,109 @@
#!/usr/bin/python2.5
#
# Copyright 2015 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.
#
# -----------------------------------------------------------------------------
#
# Lookup table definitions.

import numpy
import scipy.stats

lookup_tables = []
distributions = []

"""----------------------------------------------------------------------------
Raised cosine
----------------------------------------------------------------------------"""

x = numpy.arange(0, 257) / 256.0
c = 1.0 - (0.5 * numpy.cos(x * numpy.pi) + 0.5)
lookup_tables += [('raised_cosine', c)]

x = numpy.arange(0, 257) / 256.0
c = numpy.sin(x * numpy.pi * 2)
lookup_tables += [('sine', c)]



"""----------------------------------------------------------------------------
Logit table
----------------------------------------------------------------------------"""

x = numpy.arange(0, 257) / 256.0
log_odds = x * 20.0 - 10.0
odds = 2 ** log_odds
p = odds / (1 + odds)
lookup_tables += [('logit', p)]



"""----------------------------------------------------------------------------
Inverse CDF of Beta distribution for various combinations of alpha/beta.
Used as a LUT for inverse transform sampling.
----------------------------------------------------------------------------"""

N_nu = 9
N_mu = 5

def squash(x):
return x / (1 + x ** 2) ** 0.5

nu_values = 2 ** numpy.array([9, 5, 3, 2.5, 2, 1.5, 1, 0.5, -1])
mu_values = numpy.linspace(0, 0.5, N_mu)
mu_values[0] = 0.05
plot = False

if plot:
import pylab
VOLTAGE_RANGE = 8

for i, mu in enumerate(mu_values):
row = []
for j, nu in enumerate(nu_values):
error = numpy.exp(-(numpy.log2(nu) - 1) ** 2 / 20.0)
corrected_mu = 0.5 * (2 * mu) ** (1 / (1 + 3.0 * error))
alpha, beta = corrected_mu * nu, (1 - corrected_mu) * nu
if plot:
x = numpy.arange(-VOLTAGE_RANGE, VOLTAGE_RANGE, 0.1)
p = scipy.stats.beta.pdf(0.5 * (x / VOLTAGE_RANGE + 1.0), alpha, beta)
pylab.subplot(N_mu, N_nu, i * N_nu + j + 1)
pylab.plot(x, p)
body = numpy.arange(0, 129) / 128.0
head = body / 20.0
tail = body / 20.0 + 0.95
values = numpy.hstack((body, head, tail))
ppf = scipy.stats.beta.ppf(values, alpha, beta)
row += [('icdf_%d_%d' % (i, j), ppf)]
if j == N_nu - 1:
row += [('icdf_%d_%d_guard' % (i, j), ppf)]
distributions += row
if i == N_mu - 1:
distributions += [(name + '_guard', values) for (name, values) in row]

if plot:
pylab.show()

+ 79
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/resources/resources.py View File

@@ -0,0 +1,79 @@
#!/usr/bin/python2.5
#
# Copyright 2015 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.
#
# -----------------------------------------------------------------------------
#
# Master resources file.

header = """// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Resources definitions.
//
// Automatically generated with:
// make resources
"""

namespace = 'marbles'
target = 'marbles'
types = ['uint8_t', 'uint16_t']
includes = """
#include "stmlib/stmlib.h"
"""

import lookup_tables

create_specialized_manager = True

resources = [
(lookup_tables.lookup_tables,
'lookup_table', 'LUT', 'float', float, False),
(lookup_tables.distributions,
'distributions', 'DIST', 'float', float, False)
]

+ 149
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/scale_recorder.h View File

@@ -0,0 +1,149 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Record a note CV distribution - to be used for the quantizer.

#ifndef MARBLES_SCALE_RECORDER_H_
#define MARBLES_SCALE_RECORDER_H_

#include "stmlib/stmlib.h"
#include "marbles/random/quantizer.h"

namespace marbles {

class ScaleRecorder {
public:
ScaleRecorder() { }
~ScaleRecorder() { }

struct Degree {
float average_voltage;
float total_voltage;
float count;
bool operator< (const Degree& rhs) const {
return average_voltage < rhs.average_voltage;
}
};
void Init() {
Clear();
}
void Clear() {
num_degrees_ = 0;
current_voltage_ = 0.0f;
total_count_ = 0.0f;
}
void NewNote(float v) {
current_voltage_ = v;
}
void UpdateVoltage(float v) {
ONE_POLE(current_voltage_, v, 0.01f);
}
void AcceptNote() {
const float base_interval = 1.0f;
float v = current_voltage_;
while (v < 0.0f) {
v += base_interval;
}
float octave = static_cast<float>(
static_cast<int>(v / base_interval)) * base_interval;
v -= octave;
int nearest_degree = -1;
for (int i = 0; i < num_degrees_; ++i) {
float av = degrees_[i].average_voltage;
const float tolerance = 1.0f / 36.0f;
if (fabsf(v - av) < tolerance) {
nearest_degree = i;
break;
}
if (fabsf((v - base_interval) - av) < tolerance) {
v -= base_interval;
nearest_degree = i;
break;
}
}
if (nearest_degree == -1 && num_degrees_ != kMaxDegrees) {
nearest_degree = num_degrees_;
Degree* d = &degrees_[nearest_degree];
d->total_voltage = 0.0f;
d->average_voltage = 0.0f;
d->count = 0.0f;
++num_degrees_;
}
if (nearest_degree != -1) {
Degree* d = &degrees_[nearest_degree];
d->total_voltage += v;
d->count += 1.0f;
d->average_voltage = d->total_voltage / d->count;
total_count_ += 1.0f;
}
}
bool ExtractScale(Scale* scale) {
if (num_degrees_ < 2) {
return false;
}
std::sort(&degrees_[0], &degrees_[num_degrees_]);

float max_count = 0.0f;
for (int i = 0; i < num_degrees_; ++i) {
max_count = std::max(degrees_[i].count, max_count);
}
scale->base_interval = 1.0f;
scale->num_degrees = num_degrees_;
for (int i = 0; i < num_degrees_; ++i) {
Degree* d = &degrees_[i];
scale->degree[i].voltage = d->average_voltage;
scale->degree[i].weight = static_cast<uint8_t>(
255.0f * d->count / max_count);
if (scale->degree[i].weight == 0) {
++scale->degree[i].weight;
}
}
return true;
}
private:
int num_degrees_;
float current_voltage_;
float total_count_;
Degree degrees_[kMaxDegrees];
DISALLOW_COPY_AND_ASSIGN(ScaleRecorder);
};

} // namespace marbles

#endif // MARBLES_SCALE_RECORDER_H_

+ 246
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/settings.cc View File

@@ -0,0 +1,246 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Settings storage.

#include "marbles/settings.h"

#include <algorithm>

#include "stmlib/system/storage.h"

namespace marbles {

using namespace std;

const Scale preset_scales[6] = {
// C major
{
1.0f,
12,
{
{ 0.0000f, 255 }, // C
{ 0.0833f, 16 }, // C#
{ 0.1667f, 96 }, // D
{ 0.2500f, 24 }, // D#
{ 0.3333f, 128 }, // E
{ 0.4167f, 64 }, // F
{ 0.5000f, 8 }, // F#
{ 0.5833f, 192 }, // G
{ 0.6667f, 16 }, // G#
{ 0.7500f, 96 }, // A
{ 0.8333f, 24 }, // A#
{ 0.9167f, 128 }, // B
}
},
// C minor
{
1.0f,
12,
{
{ 0.0000f, 255 }, // C
{ 0.0833f, 16 }, // C#
{ 0.1667f, 96 }, // D
{ 0.2500f, 128 }, // Eb
{ 0.3333f, 8 }, // E
{ 0.4167f, 64 }, // F
{ 0.5000f, 4 }, // F#
{ 0.5833f, 192 }, // G
{ 0.6667f, 96 }, // G#
{ 0.7500f, 16 }, // A
{ 0.8333f, 128 }, // Bb
{ 0.9167f, 16 }, // B
}
},
// Pentatonic
{
1.0f,
12,
{
{ 0.0000f, 255 }, // C
{ 0.0833f, 4 }, // C#
{ 0.1667f, 96 }, // D
{ 0.2500f, 4 }, // Eb
{ 0.3333f, 4 }, // E
{ 0.4167f, 140 }, // F
{ 0.5000f, 4 }, // F#
{ 0.5833f, 192 }, // G
{ 0.6667f, 4 }, // G#
{ 0.7500f, 96 }, // A
{ 0.8333f, 4 }, // Bb
{ 0.9167f, 4 }, // B
}
},
// Pelog
{
1.0f,
7,
{
{ 0.0000f, 255 }, // C
{ 0.1275f, 128 }, // Db+
{ 0.2625f, 32 }, // Eb-
{ 0.4600f, 8 }, // F#-
{ 0.5883f, 192 }, // G
{ 0.7067f, 64 }, // Ab
{ 0.8817f, 16 }, // Bb+
}
},
// Raag Bhairav That
{
1.0f,
12,
{
{ 0.0000f, 255 }, // ** Sa
{ 0.0752f, 128 }, // ** Komal Re
{ 0.1699f, 4 }, // Re
{ 0.2630f, 4 }, // Komal Ga
{ 0.3219f, 128 }, // ** Ga
{ 0.4150f, 64 }, // ** Ma
{ 0.4918f, 4 }, // Tivre Ma
{ 0.5850f, 192 }, // ** Pa
{ 0.6601f, 64 }, // ** Komal Dha
{ 0.7549f, 4 }, // Dha
{ 0.8479f, 4 }, // Komal Ni
{ 0.9069f, 64 }, // ** Ni
}
},
// Raag Shri
{
1.0f,
12,
{
{ 0.0000f, 255 }, // ** Sa
{ 0.0752f, 4 }, // Komal Re
{ 0.1699f, 128 }, // ** Re
{ 0.2630f, 64 }, // ** Komal Ga
{ 0.3219f, 4 }, // Ga
{ 0.4150f, 128 }, // ** Ma
{ 0.4918f, 4 }, // Tivre Ma
{ 0.5850f, 192 }, // ** Pa
{ 0.6601f, 4 }, // Komal Dha
{ 0.7549f, 64 }, // ** Dha
{ 0.8479f, 128 }, // ** Komal Ni
{ 0.9069f, 4 }, // Ni
}
},
};

#define FIX_OUTLIER(destination, expected_value) if (fabsf(destination / expected_value - 1.0f) > 0.1f) { destination = expected_value; }

void Settings::ResetScale(int i) {
persistent_data_.scale[i] = preset_scales[i];
}


void Settings::Init() {
freshly_baked_ = false;
// Set default values for all calibration and state settings.
// This settings will be written to flash memory the first time the module
// is powered on, or if corrupted data is found in the flash sector,
// following a major firmware upgrade.
CalibrationData& c = persistent_data_.calibration_data;
fill(&c.adc_scale[0], &c.adc_scale[ADC_CHANNEL_LAST], -2.0f);
fill(&c.adc_offset[0], &c.adc_offset[ADC_CHANNEL_LAST], +1.0f);
fill(&c.dac_scale[0], &c.dac_scale[DAC_CHANNEL_LAST], -6212.8f);
fill(&c.dac_offset[0], &c.dac_offset[DAC_CHANNEL_LAST], 32768.0f);
c.adc_offset[ADC_CHANNEL_T_RATE] = 60.0f;
c.adc_scale[ADC_CHANNEL_T_RATE] = -120.0f;
for (size_t i = 0; i < kNumScales; ++i) {
ResetScale(i);
}

state_.t_deja_vu = 0;
state_.t_model = 0;
state_.t_range = 1;
state_.t_pulse_width_mean = 128;
state_.t_pulse_width_std = 0;

state_.x_deja_vu = 0;
state_.x_control_mode = 0;
state_.x_register_mode = 0;
state_.x_range = 2;
state_.x_scale = 0;
state_.y_spread = 128;
state_.y_bias = 128;
state_.y_steps = 0;
state_.y_divider = 128;
state_.y_range = 2;
state_.color_blind = 0;
freshly_baked_ = !chunk_storage_.Init(&persistent_data_, &state_);
if (!freshly_baked_) {
CONSTRAIN(state_.t_model, 0, 5);
CONSTRAIN(state_.t_range, 0, 2);
CONSTRAIN(state_.x_control_mode, 0, 2);
CONSTRAIN(state_.x_range, 0, 2);
CONSTRAIN(state_.x_scale, 0, 5);
CONSTRAIN(state_.y_range, 0, 2);
CalibrationData& c = persistent_data_.calibration_data;
for (size_t i = 0; i < ADC_CHANNEL_LAST; ++i) {
if (i == ADC_CHANNEL_T_RATE) {
FIX_OUTLIER(c.adc_scale[i], -120.0f);
FIX_OUTLIER(c.adc_offset[i], 60.0f);
} else {
FIX_OUTLIER(c.adc_scale[i], -2.0f);
FIX_OUTLIER(c.adc_offset[i], +1.0f);
}
}
for (size_t i = 0; i < DAC_CHANNEL_LAST; ++i) {
FIX_OUTLIER(c.dac_scale[i], -6212.8f);
FIX_OUTLIER(c.dac_offset[i], 32768.0f);
}
}
}

void Settings::SavePersistentData() {
chunk_storage_.SavePersistentData();
}

void Settings::SaveState() {
chunk_storage_.SaveState();
}

/* static */
void Settings::ProgramOptionBytes() {
FLASH_Unlock();
FLASH_OB_Unlock();
FLASH_OB_BORConfig(OB_BOR_OFF);
FLASH_OB_Launch();
FLASH_OB_Lock();
}

} // namespace marbles

+ 152
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/settings.h View File

@@ -0,0 +1,152 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// Settings storage.

#ifndef MARBLES_SETTINGS_H_
#define MARBLES_SETTINGS_H_

#include "stmlib/stmlib.h"

#include "stmlib/dsp/dsp.h"
#include "stmlib/system/storage.h"

#include "marbles/drivers/adc.h"
#include "marbles/drivers/dac.h"
#include "marbles/random/quantizer.h"

namespace marbles {
struct CalibrationData {
float adc_offset[ADC_CHANNEL_LAST];
float adc_scale[ADC_CHANNEL_LAST];
float dac_offset[DAC_CHANNEL_LAST];
float dac_scale[DAC_CHANNEL_LAST];
};

const int kNumScales = 6;

struct PersistentData {
CalibrationData calibration_data;
Scale scale[kNumScales];
uint8_t padding[16];
enum { tag = 0x494C4143 };
};

struct State {
uint8_t t_deja_vu;
uint8_t t_model;
uint8_t t_range;
uint8_t t_pulse_width_mean;
uint8_t t_pulse_width_std;
uint8_t x_deja_vu;
uint8_t x_control_mode;
uint8_t x_register_mode;
uint8_t x_range;
uint8_t x_scale;
uint8_t y_spread;
uint8_t y_bias;
uint8_t y_steps;
uint8_t y_divider;
uint8_t y_range;
uint8_t color_blind;
uint8_t padding[8];

enum { tag = 0x54415453 };
};

class Settings {
public:
Settings() { }
~Settings() { }
void Init();

void SavePersistentData();
void SaveState();
void ResetScale(int i);
static void ProgramOptionBytes();
inline const CalibrationData& calibration_data() {
return persistent_data_.calibration_data;
}
inline CalibrationData* mutable_calibration_data() {
return &persistent_data_.calibration_data;
}
inline const Scale& scale(int i) const {
return persistent_data_.scale[i];
}

inline Scale* mutable_scale(int i) {
return &persistent_data_.scale[i];
}
inline const State& state() const {
return state_;
}

inline State* mutable_state() {
return &state_;
}
inline const PersistentData& persistent_data() const {
return persistent_data_;
}

inline bool freshly_baked() const {
return freshly_baked_;
}
inline void set_dirty_scale_index(int i) {
dirty_scale_index_ = i;
}
inline int dirty_scale_index() const {
return dirty_scale_index_;
}
private:
bool freshly_baked_;
int dirty_scale_index_;
PersistentData persistent_data_;
State state_;
stmlib::ChunkStorage<1, PersistentData, State> chunk_storage_;
DISALLOW_COPY_AND_ASSIGN(Settings);
};

} // namespace marbles

#endif // MARBLES_SETTINGS_H_

+ 227
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/test/fixtures.h View File

@@ -0,0 +1,227 @@
// Copyright 2015 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// 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/>.

#ifndef MARBLES_TEST_FIXTURES_H_
#define MARBLES_TEST_FIXTURES_H_

#include <cstdlib>
#include <vector>

#include "marbles/ramp/ramp_divider.h"
#include "marbles/ramp/ramp_extractor.h"

const size_t kSampleRate = 32000;
const size_t kAudioBlockSize = 8;

namespace marbles {

using namespace std;
using namespace stmlib;

class PulseGenerator {
public:
PulseGenerator() {
counter_ = 0;
previous_state_ = 0;
}
~PulseGenerator() { }
void AddPulses(int total_duration, int on_duration, int num_repetitions) {
Pulse p;
p.total_duration = total_duration;
p.on_duration = on_duration;
p.num_repetitions = num_repetitions;
pulses_.push_back(p);
}
void Render(GateFlags* clock, size_t size) {
while (size--) {
bool current_state = pulses_.size() && counter_ < pulses_[0].on_duration;
++counter_;
if (pulses_.size() && counter_ >= pulses_[0].total_duration) {
counter_ = 0;
--pulses_[0].num_repetitions;
if (pulses_[0].num_repetitions == 0) {
pulses_.erase(pulses_.begin());
}
}
previous_state_ = *clock++ = ExtractGateFlags(previous_state_, current_state);
}
}
private:
struct Pulse {
int total_duration;
int on_duration;
int num_repetitions;
};
int counter_;
GateFlags previous_state_;
vector<Pulse> pulses_;
DISALLOW_COPY_AND_ASSIGN(PulseGenerator);
};

enum PatternDifficulty {
FRIENDLY_PATTERNS,
FAST_PATTERNS,
TRICKY_PATTERNS,
PAUSE_PATTERNS,
};

class ClockGeneratorPatterns {
public:
ClockGeneratorPatterns(PatternDifficulty difficulty) {
if (difficulty == FRIENDLY_PATTERNS) {
pulse_generator_.AddPulses(800, 400, 100);
pulse_generator_.AddPulses(400, 32, 100);
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 5; ++j) {
pulse_generator_.AddPulses(600 - j * 100, 3, 2);
}
}
for (int i = 0; i < 300; ++i) {
int t = 200 + (rand() % 400);
pulse_generator_.AddPulses(t, t / 4, 1);
}

// Completely random clock.
for (int i = 0; i < 400; ++i) {
int t = 200 + (rand() % 800);
int pw = t / 4 + (rand() % (t / 2));
pulse_generator_.AddPulses(t, pw, 1);
}
return;
} else if (difficulty == FAST_PATTERNS) {
pulse_generator_.AddPulses(32, 16, 100);
pulse_generator_.AddPulses(16, 8, 100);
pulse_generator_.AddPulses(12, 6, 100);
pulse_generator_.AddPulses(8, 4, 100);
pulse_generator_.AddPulses(6, 3, 100);
pulse_generator_.AddPulses(4, 2, 100);
pulse_generator_.AddPulses(8, 4, 100);
pulse_generator_.AddPulses(12, 6, 100);
return;
} else if (difficulty == PAUSE_PATTERNS) {
pulse_generator_.AddPulses(800, 400, 100);
pulse_generator_.AddPulses(32000 * 5 + 10, 400, 1);
pulse_generator_.AddPulses(800, 400, 100);
}
// Steady clock
pulse_generator_.AddPulses(400, 200, 250);
pulse_generator_.AddPulses(4000 + (rand() % 1000), 2000, 10);
pulse_generator_.AddPulses(100, 10, 50);
pulse_generator_.AddPulses(16, 5, 100);
// Periodic clock with some jitter
for (int i = 0; i < 50; ++i) {
pulse_generator_.AddPulses(100, 10, 3);
pulse_generator_.AddPulses(400 + (i % 4), 10, 1);
pulse_generator_.AddPulses((i == 40) ? 40 : 300, 10, 1);
}
for (int i = 0; i < 50; ++i) {
pulse_generator_.AddPulses(100 + (i % 10), 10, 3);
pulse_generator_.AddPulses(200 + (i % 4), 10, 2);
pulse_generator_.AddPulses(300, 10, 1);
}

// Really long pattern that hashes well
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 10; ++j) {
pulse_generator_.AddPulses(100 + j * 30, 10, 1);
}
}
for (int i = 0; i < 15; ++i) {
for (int j = 0; j < 6; ++j) {
pulse_generator_.AddPulses(300 - j * 50, 3, 2);
}
}
// Random clock with reliable pulse width
for (int i = 0; i < 300; ++i) {
int t = 100 + (rand() % 400);
pulse_generator_.AddPulses(t, t / 4, 1);
}

// Completely random clock.
for (int i = 0; i < 400; ++i) {
int t = 100 + (rand() % 400);
int pw = t / 4 + (rand() % (t / 2));
pulse_generator_.AddPulses(t, pw, 1);
}
}
~ClockGeneratorPatterns() { }
void Render(size_t size) {
pulse_generator_.Render(buffer_, size);
}
GateFlags* clock() { return buffer_; }
private:
GateFlags buffer_[kAudioBlockSize];
PulseGenerator pulse_generator_;
DISALLOW_COPY_AND_ASSIGN(ClockGeneratorPatterns);
};

class MasterSlaveRampGenerator {
public:
MasterSlaveRampGenerator() {
ramp_extractor_.Init(4000.0f / kSampleRate);
ramp_divider_[0].Init();
ramp_divider_[1].Init();
}
~MasterSlaveRampGenerator() { }
void Process(const GateFlags* clock, size_t size) {
Ratio r = { 1, 1 };
ramp_extractor_.Process(r, true, clock, master_ramp_, size);
r.q = 2;
ramp_divider_[0].Process(r, master_ramp_, slave_ramp_1_, size);

r.p = 1; r.q = 2;
ramp_divider_[1].Process(r, master_ramp_, slave_ramp_2_, size);
}
Ramps ramps() {
Ramps r;
r.external = external_ramp_;
r.master = master_ramp_;
r.slave[0] = slave_ramp_1_;
r.slave[1] = slave_ramp_2_;
return r;
}
float* master_ramp() { return master_ramp_; }
float* slave_ramp_1() { return slave_ramp_1_; }
float* slave_ramp_2() { return slave_ramp_2_; }
private:
RampExtractor ramp_extractor_;
RampDivider ramp_divider_[2];
float external_ramp_[kAudioBlockSize];
float master_ramp_[kAudioBlockSize];
float slave_ramp_1_[kAudioBlockSize];
float slave_ramp_2_[kAudioBlockSize];
DISALLOW_COPY_AND_ASSIGN(MasterSlaveRampGenerator);
};

} // namespace marbles

#endif // MARBLES_TEST_FIXTURES_H_

+ 50
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/test/makefile View File

@@ -0,0 +1,50 @@
PACKAGES = marbles/test stmlib/utils marbles/ramp marbles/random marbles stmlib/dsp

VPATH = $(PACKAGES)

TARGET = marbles_test
BUILD_ROOT = build/
BUILD_DIR = $(BUILD_ROOT)$(TARGET)/
CC_FILES = marbles_test.cc \
lag_processor.cc \
output_channel.cc \
quantizer.cc \
discrete_distribution_quantizer.cc \
ramp_extractor.cc \
random.cc \
resources.cc \
units.cc \
t_generator.cc \
x_y_generator.cc
OBJ_FILES = $(CC_FILES:.cc=.o)
OBJS = $(patsubst %,$(BUILD_DIR)%,$(OBJ_FILES)) $(STARTUP_OBJ)
DEPS = $(OBJS:.o=.d)
DEP_FILE = $(BUILD_DIR)depends.mk

all: marbles_test

$(BUILD_DIR):
mkdir -p $(BUILD_DIR)

$(BUILD_DIR)%.o: %.cc
g++ -c -DTEST -g -Wall -Werror -msse2 -Wno-unused-variable -O2 -I. $< -o $@

$(BUILD_DIR)%.d: %.cc
g++ -MM -DTEST -I. $< -MF $@ -MT $(@:.d=.o)

marbles_test: $(OBJS)
g++ -g -o $(TARGET) $(OBJS) -Wl,-no_pie -lm -lprofiler -L/opt/local/lib

depends: $(DEPS)
cat $(DEPS) > $(DEP_FILE)

$(DEP_FILE): $(BUILD_DIR) $(DEPS)
cat $(DEPS) > $(DEP_FILE)

profile: marbles_test
env CPUPROFILE_FREQUENCY=1000 CPUPROFILE=$(BUILD_DIR)/marbles.prof ./marbles_test && pprof --pdf ./marbles_test $(BUILD_DIR)/marbles.prof > profile.pdf && open profile.pdf
clean:
rm $(BUILD_DIR)*.*

include $(DEP_FILE)

+ 721
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/test/marbles_test.cc View File

@@ -0,0 +1,721 @@
// Copyright 2015 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// 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 "marbles/cv_reader_channel.h"
#include "marbles/note_filter.h"
#include "marbles/ramp/ramp_divider.h"
#include "marbles/ramp/ramp_extractor.h"
#include "marbles/random/distributions.h"
#include "marbles/random/output_channel.h"
#include "marbles/random/random_generator.h"
#include "marbles/random/random_sequence.h"
#include "marbles/random/random_stream.h"
#include "marbles/random/t_generator.h"
#include "marbles/random/x_y_generator.h"
#include "marbles/scale_recorder.h"
#include "marbles/test/fixtures.h"
#include "marbles/test/ramp_checker.h"
#include "stmlib/test/wav_writer.h"
#include "stmlib/utils/random.h"

using namespace marbles;
using namespace std;
using namespace stmlib;


void TestBetaDistribution() {
// Plot result with:
// import numpy
// import pylab
// data = numpy.loadtxt('marbles_histograms.txt')
// n = 0
// for i in xrange(9):
// for j in xrange(13):
// pylab.subplot(9, 13, n + 1)
// pylab.plot(data[(n * 101):((n + 1) * 101)])
// pylab.gca().get_xaxis().set_visible(False)
// pylab.gca().get_yaxis().set_visible(False)
// n += 1
// pylab.show()
FILE* fp = fopen("marbles_histograms.txt", "w");
for (int i = 0; i < 9; ++i) {
for (int j = 0; j < 13; ++j) {
float bias = float(i) / 8.0f;
float range = float(j) / 12.0f;
vector<int> histogram(101);
for (int n = 0; n < 1000000; ++n) {
float value = BetaDistributionSample(Random::GetFloat(), range, bias);
histogram[int(value * 100.0f)]++;
}
for (int n = 0; n < 101; ++n) {
fprintf(fp, "%d\n", histogram[n]);
}
}
}
fclose(fp);
}

void TestQuantizer() {
// Plot result with:
// import numpy
// import pylab
//
// data = numpy.loadtxt('marbles_quantizer.txt')
// pylab.figure(figsize=(25,5))
// for i in xrange(9):
// pylab.subplot(1, 9, i + 1)
// indices = numpy.where(data[:, 0] == i)[0]
// pylab.plot(data[indices, 1], data[indices, 2])
// pylab.show()
FILE* fp = fopen("marbles_quantizer.txt", "w");
Quantizer q;
Scale scale;
scale.InitMajor();
q.Init(scale);
for (int i = 0; i <= 8; ++i) {
float amount = float(i) / 8.0f;
for (int j = 0; j <= 4000; ++j) {
float value = j / 1000.0f - 2.0f;
fprintf(fp, "%d %f %f\n", i, value, q.Process(value, amount, false));
}
}
fclose(fp);
}

void TestQuantizerNoise() {
// Plot result with:
// import numpy
// import pylab
//
// data = numpy.loadtxt('marbles_quantizer_hysteresis.txt')
// pylab.plot(data)
// pylab.show()
FILE* fp = fopen("marbles_quantizer_hysteresis.txt", "w");
Quantizer q;
Scale scale;
scale.InitTenth();
q.Init(scale);
for (int j = 0; j <= 4000; ++j) {
float noise = (rand() % 500) / 250.0f - 1.0f;
float tri = j / 2000.0f;
if (tri >= 1.0f) tri = 2.0f - tri;
float value = 1.0f * tri + noise * 1.0f / 60.0f;
float result = q.Process(value, 0.18f, true);
fprintf(fp, "%f %f\n", value, result);
}
fclose(fp);
}

void TestRampExtractorClockBug() {
WavWriter wav_writer(2, ::kSampleRate, 20);
wav_writer.Open("marbles_ramp_extractor_clock_bug.wav");
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(33);
random_stream.Init(&random_generator);
PulseGenerator pulse_generator;
for (size_t i = 0; i < 700; ++i) {
int t = (rand() % 8) + 796;
pulse_generator.AddPulses(t, t >> 1, 1);
}
TGenerator generator;
generator.Init(&random_stream, kSampleRate);
generator.set_model(T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI);
generator.set_rate(0.0f);
generator.set_pulse_width_mean(0.5f);
generator.set_pulse_width_std(0.0f);
generator.set_bias(0.5f);
generator.set_jitter(0.0f);
generator.set_deja_vu(0.0f);
generator.set_length(8);
generator.set_range(T_GENERATOR_RANGE_1X);
MasterSlaveRampGenerator ms_ramp_generator;
Ramps ramps = ms_ramp_generator.ramps();
float phase = 0.0f;
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
phase += 0.0001f;
if (phase >= 1.0f) {
phase -= 1.0f;
}
float tri = phase < 0.5f ? 2.0f * phase : 2.0f - 2.0f * phase;
bool gate[kAudioBlockSize * 2];
GateFlags clock[kAudioBlockSize];
pulse_generator.Render(clock, kAudioBlockSize);
generator.set_rate(tri * 24.0f - 12.0f);
generator.Process(
true,
clock,
ramps,
gate,
kAudioBlockSize);
for (size_t j = 0; j < kAudioBlockSize; ++j) {
float s[6];
s[0] = clock[j] ? 1.0f : 0.0f;
s[1] = ramps.external[j];
wav_writer.Write(s, 2, 32767.0f);
}
}
}

void TestRampExtractorPause() {
WavWriter wav_writer(6, ::kSampleRate, 10);
wav_writer.Open("marbles_ramp_pause.wav");
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(33);
random_stream.Init(&random_generator);
TGenerator generator;
generator.Init(&random_stream, kSampleRate);
generator.set_model(T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI);
generator.set_rate(0.0f);
generator.set_pulse_width_mean(0.0f);
generator.set_pulse_width_std(0.0f);
generator.set_bias(0.9f);
generator.set_jitter(0.0f);
generator.set_deja_vu(0.0f);
generator.set_length(8);
generator.set_range(T_GENERATOR_RANGE_0_25X);
ClockGeneratorPatterns patterns(PAUSE_PATTERNS);
MasterSlaveRampGenerator ms_ramp_generator;
Ramps ramps = ms_ramp_generator.ramps();
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
bool gate[kAudioBlockSize * 2];
patterns.Render(kAudioBlockSize);
generator.Process(
true,
patterns.clock(),
ramps,
gate,
kAudioBlockSize);
for (size_t j = 0; j < kAudioBlockSize; ++j) {
float s[6];
s[5] = s[0] = patterns.clock()[j] & GATE_FLAG_HIGH ? 0.8f : 0.0f;
s[1] = ramps.external[j];
s[2] = ramps.master[j];
s[3] = ramps.slave[0][j];
s[4] = ramps.slave[1][j];
wav_writer.Write(s, 6, 32767.0f);
}
}
}

void TestRampDivider(PatternDifficulty difficulty, const char* file_name) {
WavWriter wav_writer(4, ::kSampleRate, 10);
wav_writer.Open(file_name);
ClockGeneratorPatterns patterns(difficulty);
RampExtractor ramp_extractor;
RampDivider ramp_divider;
RampDivider ramp_divider_double;
RampDivider ramp_divider_half;
ramp_extractor.Init(0.25f);
ramp_divider.Init();
ramp_divider_double.Init();
ramp_divider_half.Init();
RampChecker ramp_checker[4];
Ratio r8x;
r8x.p = 8;
r8x.q = 1;
Ratio r2x;
r2x.p = 2;
r2x.q = 1;
Ratio half;
half.p = 1;
half.q = 2;
Ratio one;
one.p = 1;
one.q = 1;
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
float ramp[kAudioBlockSize];
float divided_ramp[kAudioBlockSize];
float divided_ramp_double[kAudioBlockSize];
float divided_ramp_half[kAudioBlockSize];
// switch ((i / (kSampleRate / 4)) % 3) {
// case 0:
// ramp_divider.set_ratio(1, 2);
// break;
// case 1:
// ramp_divider.set_ratio(4, 1);
// break;
// case 2:
// ramp_divider.set_ratio(3, 2);
// break;
// }
patterns.Render(kAudioBlockSize);
ramp_extractor.Process(one, true, patterns.clock(), ramp, kAudioBlockSize);
ramp_divider.Process(r8x, ramp, divided_ramp, kAudioBlockSize);
ramp_divider_double.Process(r2x, divided_ramp, divided_ramp_double, kAudioBlockSize);
ramp_divider_half.Process(half, divided_ramp, divided_ramp_half, kAudioBlockSize);
for (size_t j = 0; j < kAudioBlockSize; ++j) {
float s[4];
s[0] = ramp[j];
s[1] = divided_ramp[j];
s[2] = divided_ramp_double[j];
s[3] = divided_ramp_half[j];
wav_writer.Write(s, 4, 32767.0f);
}
ramp_checker[0].Check(ramp, kAudioBlockSize);
ramp_checker[1].Check(divided_ramp, kAudioBlockSize);
ramp_checker[2].Check(divided_ramp_double, kAudioBlockSize);
ramp_checker[3].Check(divided_ramp_half, kAudioBlockSize);
}
}

void TestOutputChannel() {
WavWriter wav_writer(2, ::kSampleRate, 3);
wav_writer.Open("marbles_random_voltage.wav");

RampExtractor ramp_extractor;
RandomGenerator random_generator;
RandomStream random_stream;
RandomSequence random_sequence;
OutputChannel output_channel;
PulseGenerator pulse_generator;

ramp_extractor.Init(0.25f);
random_generator.Init(32);
random_stream.Init(&random_generator);
random_sequence.Init(&random_stream);
output_channel.Init();
// Steady clock
pulse_generator.AddPulses(400, 10, 250);
pulse_generator.AddPulses(200, 10, 250);
output_channel.set_register_mode(false);
output_channel.set_steps(0.5f);
output_channel.set_bias(0.5f);
Ratio one;
one.p = 1;
one.q = 1;
for (size_t i = 0; i < ::kSampleRate * 3; i += kAudioBlockSize) {
GateFlags gate_flags[kAudioBlockSize];
float ramp[kAudioBlockSize];
float voltage[kAudioBlockSize];
pulse_generator.Render(gate_flags, kAudioBlockSize);
ramp_extractor.Process(one, false, gate_flags, ramp, kAudioBlockSize);
output_channel.set_spread(wav_writer.triangle(3));
output_channel.Process(&random_sequence, ramp, voltage, kAudioBlockSize, 1);
for (size_t j = 0; j < kAudioBlockSize; ++j) voltage[j] *= 0.1f;
wav_writer.Write(ramp, voltage, kAudioBlockSize);
}
}

void TestXYGenerator() {
WavWriter wav_writer(4, ::kSampleRate, 10);
wav_writer.Open("marbles_xy.wav");
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(32);
random_stream.Init(&random_generator);
XYGenerator generator;
generator.Init(&random_stream, ::kSampleRate);
GroupSettings x_settings, y_settings;

x_settings.control_mode = CONTROL_MODE_IDENTICAL;
x_settings.voltage_range = VOLTAGE_RANGE_FULL;
x_settings.register_mode = false;
x_settings.register_value = 0.0f;
x_settings.spread = 0.8f;
x_settings.bias = 0.8f;
x_settings.steps = 0.3f;
x_settings.deja_vu = 0.0f;
x_settings.length = 8;
x_settings.ratio.p = 1;
x_settings.ratio.q = 1;

y_settings.control_mode = CONTROL_MODE_IDENTICAL;
y_settings.voltage_range = VOLTAGE_RANGE_FULL;
y_settings.register_mode = false;
y_settings.register_value = 0.0f;
y_settings.spread = 0.5f;
y_settings.bias = 0.5f;
y_settings.steps = 0.1f;
y_settings.deja_vu = 0.0f;
y_settings.length = 8;
y_settings.ratio.p = 1;
y_settings.ratio.q = 8;
ClockGeneratorPatterns patterns(TRICKY_PATTERNS);
MasterSlaveRampGenerator ms_ramp_generator;
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
patterns.Render(kAudioBlockSize);
ms_ramp_generator.Process(patterns.clock(), kAudioBlockSize);
float samples[kAudioBlockSize * 4];
generator.Process(
CLOCK_SOURCE_INTERNAL_T1_T2_T3,
x_settings,
y_settings,
patterns.clock(),
ms_ramp_generator.ramps(),
samples,
kAudioBlockSize);
wav_writer.Write(samples, kAudioBlockSize * 4, 3276.7f);
}
}

void TestXYGeneratorASR() {
WavWriter wav_writer(4, ::kSampleRate, 10);
wav_writer.Open("marbles_xy_asr.wav");
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(32);
random_stream.Init(&random_generator);
XYGenerator generator;
generator.Init(&random_stream, ::kSampleRate);
GroupSettings x_settings, y_settings;

x_settings.control_mode = CONTROL_MODE_IDENTICAL;
x_settings.voltage_range = VOLTAGE_RANGE_FULL;
x_settings.register_mode = false;
x_settings.register_value = 0.0f;
x_settings.spread = 0.2f;
x_settings.bias = 0.7f;
x_settings.steps = 0.5f;
x_settings.deja_vu = 0.0f;
x_settings.length = 8;
x_settings.ratio.p = 1;
x_settings.ratio.q = 1;

y_settings.control_mode = CONTROL_MODE_IDENTICAL;
y_settings.voltage_range = VOLTAGE_RANGE_FULL;
y_settings.register_mode = false;
y_settings.register_value = 0.0f;
y_settings.spread = 0.5f;
y_settings.bias = 0.5f;
y_settings.steps = 0.1f;
y_settings.deja_vu = 0.0f;
y_settings.length = 8;
y_settings.ratio.p = 1;
y_settings.ratio.q = 8;
ClockGeneratorPatterns patterns(TRICKY_PATTERNS);
MasterSlaveRampGenerator ms_ramp_generator;
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
patterns.Render(kAudioBlockSize);
ms_ramp_generator.Process(patterns.clock(), kAudioBlockSize);
float samples[kAudioBlockSize * 4];
x_settings.register_mode = true;
x_settings.register_value = wav_writer.triangle(1);
generator.Process(
CLOCK_SOURCE_EXTERNAL,
x_settings,
y_settings,
patterns.clock(),
ms_ramp_generator.ramps(),
samples,
kAudioBlockSize);
wav_writer.Write(samples, kAudioBlockSize * 4, 3276.7f);
}
}

void TestTGenerator() {
WavWriter wav_writer(6, ::kSampleRate, 10);
wav_writer.Open("marbles_t.wav");
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(33);
random_stream.Init(&random_generator);
TGenerator generator;
generator.Init(&random_stream, kSampleRate);
generator.set_model(T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI);
generator.set_rate(24.0f);
generator.set_pulse_width_mean(0.0f);
generator.set_pulse_width_std(0.0f);
generator.set_bias(0.9f);
generator.set_jitter(0.5f);
generator.set_deja_vu(0.0f);
generator.set_length(8);
generator.set_range(T_GENERATOR_RANGE_4X);
ClockGeneratorPatterns patterns(FRIENDLY_PATTERNS);
MasterSlaveRampGenerator ms_ramp_generator;
Ramps ramps = ms_ramp_generator.ramps();
RampChecker ramp_checker[4];
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
bool gate[kAudioBlockSize * 2];
patterns.Render(kAudioBlockSize);
generator.Process(
true,
patterns.clock(),
ramps,
gate,
kAudioBlockSize);
if (i >= kSampleRate * 5) {
// generator.set_deja_vu(1.0f);
// generator.set_jitter(0.5f);
}
for (size_t j = 0; j < kAudioBlockSize; ++j) {
float s[6];
s[0] = ramps.external[j];
s[1] = ramps.master[j];
s[2] = ramps.slave[0][j];
s[3] = ramps.slave[1][j];
s[4] = gate[j * 2] ? 1.0f : 0.0f;
s[5] = gate[j * 2 + 1] ? 1.0f : 0.0f;
wav_writer.Write(s, 6, 32767.0f);
}
ramp_checker[0].Check(ramps.external, kAudioBlockSize);
ramp_checker[1].Check(ramps.master, kAudioBlockSize);
ramp_checker[2].Check(ramps.slave[0], kAudioBlockSize);
ramp_checker[3].Check(ramps.slave[1], kAudioBlockSize);
}
}

void TestTGeneratorSuperFastClock() {
WavWriter wav_writer(6, ::kSampleRate, 10);
wav_writer.Open("marbles_t_super_fast.wav");
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(33);
random_stream.Init(&random_generator);
TGenerator generator;
generator.Init(&random_stream, kSampleRate);
generator.set_model(T_GENERATOR_MODEL_COMPLEMENTARY_BERNOULLI);
generator.set_rate(60.0f);
generator.set_pulse_width_mean(0.0f);
generator.set_pulse_width_std(0.0f);
generator.set_bias(0.5f);
generator.set_jitter(0.0f);
generator.set_deja_vu(0.0f);
generator.set_length(8);
generator.set_range(T_GENERATOR_RANGE_4X);
ClockGeneratorPatterns patterns(FAST_PATTERNS);
MasterSlaveRampGenerator ms_ramp_generator;
Ramps ramps = ms_ramp_generator.ramps();
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
bool gate[kAudioBlockSize * 2];
patterns.Render(kAudioBlockSize);
generator.Process(
true,
patterns.clock(),
ramps,
gate,
kAudioBlockSize);
for (size_t j = 0; j < kAudioBlockSize; ++j) {
float s[6];
s[0] = ramps.external[j];
s[1] = ramps.master[j];
s[2] = ramps.slave[0][j];
s[3] = ramps.slave[1][j];
s[4] = gate[j * 2] ? 1.0f : 0.0f;
s[5] = gate[j * 2 + 1] ? 1.0f : 0.0f;
wav_writer.Write(s, 6, 32767.0f);
}
}
}

void TestTGeneratorRampIntegrity(
bool internal_clock,
float rate,
TGeneratorModel model,
float jitter) {
printf("Testing ramp integrity for intclock = %d\trate = %04.3f\tmodel = %d\tjitter = %04.3f\n", internal_clock, rate, model, jitter);
RandomGenerator random_generator;
RandomStream random_stream;
random_generator.Init(33);
random_stream.Init(&random_generator);

TGenerator generator;
generator.Init(&random_stream, kSampleRate);
generator.set_model(model);
generator.set_rate(rate + 36.0f);
generator.set_pulse_width_mean(0.0f);
generator.set_pulse_width_std(0.0f);
generator.set_bias(0.7f);
generator.set_jitter(jitter);
if (internal_clock) {
generator.set_range(T_GENERATOR_RANGE_4X);
} else {
generator.set_range(T_GENERATOR_RANGE_1X);
}

ClockGeneratorPatterns patterns(FRIENDLY_PATTERNS);
MasterSlaveRampGenerator ms_ramp_generator;
Ramps ramps = ms_ramp_generator.ramps();

RampChecker ramp_checker[4];
for (size_t i = 0; i < ::kSampleRate * 10; i += kAudioBlockSize) {
bool gate[kAudioBlockSize * 2];
patterns.Render(kAudioBlockSize);
generator.Process(
!internal_clock,
patterns.clock(),
ramps,
gate,
kAudioBlockSize);
ramp_checker[0].Check(ramps.external, kAudioBlockSize);
ramp_checker[1].Check(ramps.master, kAudioBlockSize);
ramp_checker[2].Check(ramps.slave[0], kAudioBlockSize);
ramp_checker[3].Check(ramps.slave[1], kAudioBlockSize);
}
}


void TestTGeneratorRampIntegrity() {
for (size_t clock_source = 0; clock_source < 2; ++clock_source) {
for (size_t model = 0; model < 6; ++model) {
for (size_t jitter = 0; jitter < 5; ++jitter) {
for (size_t rate = 0; rate < 5; ++rate) {
TestTGeneratorRampIntegrity(
clock_source,
float(rate) * 12.0f - 24.0f,
TGeneratorModel(model),
float(jitter) * 0.2f);
}
}
}
}
}

void TestScaleRecorder() {
int prelude[] = { 0, 4, 7, 7, 12, 12, 16, 16, 7, 7, 12, 12, 16, 0, 0, 4, 16,
4, 7, 7, 12, 12, 16, 16, 7, 7, 12, 12, 16, 0, 0, 4, 16, 2, 9, 9, 14, 14,
17, 17, 9, 9, 14, 14, 17, 0, 0, 2, 17, 2, 9, 9, 14, 14, 17, 17, 9, 9, 14,
14, 17, -1, 0, 2, 17, 2, 7, 7, 14, 14, 17, 17, 7, 7, 14, 14, 17, -1, -1,
2, 17, 2, 7, 7, 14, 14, 17, 17, 7, 7, 14, 14, 17, -1, 0, 2, 17, 4, 7, 7,
12, 12, 16, 16, 7, 7, 12, 12, 16, 0, 0, 4, 16, 4, 7, 7, 12, 12, 16, 16, 7,
7, 12, 12, 16, 0, 0, 4, 16, 4, 9, 9, 16, 16, 21, 21, 9, 9, 16, 16, 21, 0,
0, 4, 21, 4, 9, 9, 16, 16, 21, 21, 9, 9, 16, 16, 21, 0, 0, 4, 21, 2, 6, 6,
9, 9, 14, 14, 6, 6, 9, 9, 14, 0, 0, 2, 14, 2, 6, 6, 9, 9, 14, 14, 6, 6, 9,
9, 14, -1, 0, 2, 14, 2, 7, 7, 14, 14, 19, 19, 7, 7, 14, 14, 19, -1, -1, 2,
19, 2, 7, 7, 14, 14, 19, 19, 7, 7, 14, 14, 19, -1, -1, 2, 19, 0, 4, 4, 7,
7, 12, 12, 4, 4, 7, 7, 12, -1, -1, 0, 12, 0, 4, 4, 7, 7, 12, 12, 4, 4, 7,
7, 12, -3, -1, 0, 12, 0, 4, 4, 7, 7, 12, 12, 4, 4, 7, 7, 12, -3, -3, 0, 12,
0, 4, 4, 7, 7, 12, 12, 4, 4, 7, 7, 12, -10, -3, 0, 12, -3, 2, 2, 6, 6, 12,
12, 2, 2, 6, 6, 12, -10, -10, -3, 12, -3, 2, 2, 6, 6, 12, 12, 2, 2, 6, 6,
12, -10, -5, -3, 12, -1, 2, 2, 7, 7, 11, 11, 2, 2, 7, 7, 11, -5, -5, -1,
11, -1, 2, 2, 7, 7, 11, 11, 2, 2, 7, 7, 11, -5, -5, -1, 11, -2, 4, 4, 7, 7,
13, 13, 4, 4, 7, 7, 13, -5, -5, -2, 13, -2, 4, 4, 7, 7, 13, 13, 4, 4, 7, 7,
13, -7, -5, -2, 13, -3, 2, 2, 9, 9, 14, 14, 2, 2, 9, 9, 14, -7, -7, -3, 14,
-3, 2, 2, 9, 9, 14, 14, 2, 2, 9, 9, 14, -7, -7, -3, 14, -4, 2, 2, 5, 5, 11,
11, 2, 2, 5, 5, 11, -7, -7, -4, 11, -4, 2, 2, 5, 5, 11, 11, 2, 2, 5, 5, 11,
-8, -7, -4, 11, -5, 0, 0, 7, 7, 12, 12, 0, 0, 7, 7, 12, -8, -8, -5, 12, -5,
0, 0, 7, 7, 12, 12, 0, 0, 7, 7, 12, -8, -8, -5, 12, -7, -3, -3, 0, 0, 5, 5,
-3, -3, 0, 0, 5, -8, -8, -7, 5, -7, -3, -3, 0, 0, 5, 5, -3, -3, 0, 0, 5,
-10, -8, -7, 5, -7, -3, -3, 0, 0, 5, 5, -3, -3, 0, 0, 5, -10, -10, -7, 5,
-7, -3, -3, 0, 0, 5, 5, -3, -3, 0, 0, 5, -17, -10, -7, 5, -10, -5, -5, -1,
-1, 5, 5, -5, -5, -1, -1, 5, -17, -17, -10, 5, -10, -5, -5, -1, -1, 5, 5,
-5, -5, -1, -1, 5, -17, -12, -10, 5, -8, -5, -5, 0, 0, 4, 4, -5, -5, 0, 0,
4, -12, -12, -8, 4, -8, -5, -5, 0, 0, 4, 4, -5, -5, 0, 0, 4, -12, -12, -8,
4, -5, -2, -2, 0, 0, 4, 4, -2, -2, 0, 0, 4, -12, -12, -5, 4, -5, -2, -2, 0,
0, 4, 4, -2, -2, 0, 0, 4, -19, -12, -5, 4, -7, -3, -3, 0, 0, 4, 4, -3, -3,
0, 0, 4, -19, -19, -7, 4, -7, -3, -3, 0, 0, 4, 4, -3, -3, 0, 0, 4, -19,
-18, -7, 4, -12, -3, -3, 0, 0, 3, 3, -3, -3, 0, 0, 3, -18, -18, -12, 3,
-12, -3, -3, 0, 0, 3, 3, -3, -3, 0, 0, 3, -18, -16, -12, 3, -7, -1, -1, 0,
0, 2, 2, -1, -1, 0, 0, 2, -16, -16, -7, 2, -7, -1, -1, 0, 0, 2, 2, -1, -1,
0, 0, 2, -17, -16, -7, 2, -7, -5, -5, -1, -1, 2, 2, -5, -5, -1, -1, 2, -17,
-17, -7, 2, -7, -5, -5, -1, -1, 2, 2, -5, -5, -1, -1, 2, -17, -17, -7,
2, -8, -5, -5, 0, 0, 4, 4, -5, -5, 0, 0, 4, -17, -17, -8, 4, -8, -5, -5, 0,
0, 4, 4, -5, -5, 0, 0, 4, -17, -17, -8, 4, -10, -5, -5, 0, 0, 5, 5, -5, -5,
0, 0, 5, -17, -17, -10, 5, -10, -5, -5, 0, 0, 5, 5, -5, -5, 0, 0, 5, -17,
-17, -10, 5,-10, -5, -5, -1, -1, 5, 5, -5, -5, -1, -1, 5, -17, -17, -10, 5,
-10, -5, -5,-1, -1, 5, 5, -5, -5, -1, -1, 5, -17, -17, -10, 5, -9, -3, -3,
0, 0, 6, 6, -3,-3, 0, 0, 6, -17, -17, -9, 6, -9, -3, -3, 0, 0, 6, 6, -3,
-3, 0, 0, 6, -17,-17, -9, 6, -8, -5, -5, 0, 0, 7, 7, -5, -5, 0, 0, 7, -17,
-17, -8, 7, -8, -5,-5, 0, 0, 7, 7, -5, -5, 0, 0, 7, -17, -17, -8, 7, -10,
-5, -5, 0, 0, 5, 5, -5,-5, 0, 0, 5, -17, -17, -10, 5, -10, -5, -5, 0, 0, 5,
5, -5, -5, 0, 0, 5, -17,-17, -10, 5, -10, -5, -5, -1, -1, 5, 5, -5, -5, -1,
-1, 5, -17, -17, -10, 5,-10, -5, -5, -1, -1, 5, 5, -5, -5, -1, -1, 5, -24,
-17, -10, 5, -12, -5, -5,-2, -2, 4, 4, -5, -5, -2, -2, 4, -24, -24, -12, 4,
-12, -5, -5, -2, -2, 4, 4,-5, -5, -2, -2, 4, -24, -24, -12, 4, -12, -7, -7,
-3, -3, 0, 0, 5, 0, 5, -3, 0,-3, 0, -3, 0, -7, -3, -7, -3, -7, -3, -10, -7,
-10, -7, -10, -7, -12, -10, -24,-24, -10, 7, 7, 11, 11, 14, 14, 17, 14, 17,
11, 14, 11, 14, 11, 14, 7, 11, 7,11, 2, 11, 2, 5, 4, 5, 2, 4, -10, 2, -24,
-24, -12, 4, 7, 12, -24, -12, 4, 7, 12
};
ScaleRecorder recorder;
recorder.Init();
for (size_t i = 0; i < sizeof(prelude) / sizeof(int); ++i) {
float voltage = prelude[i] / 12.0f + Random::GetFloat() * 0.0001f;
recorder.NewNote(voltage);
recorder.UpdateVoltage(voltage);
recorder.AcceptNote();
}
Scale s;
recorder.ExtractScale(&s);
for (int i = 0; i < s.num_degrees; ++i) {
printf("%f %d\n", s.degree[i].voltage, s.degree[i].weight);
}
}

int main(void) {
// Test distributions and value processors.
// TestBetaDistribution();
// TestQuantizer();
// TestQuantizerNoise();

// Ramp tests.
// TestRampExtractor(FRIENDLY_PATTERNS, "marbles_ramp_extractor_friendly.wav");
// TestRampExtractor(TRICKY_PATTERNS, "marbles_ramp_extractor_tricky.wav");
// TestRampExtractor(FAST_PATTERNS, "marbles_ramp_extractor_fast.wav");
// TestRampDivider(TRICKY_PATTERNS, "marbles_ramp_divider_tricky.wav");

// TestRampExtractorClockBug();
// TestRampExtractorPause();

// TestOutputChannel();
// TestXYGenerator();
// TestXYGeneratorASR();
// TestTGeneratorRampIntegrity();
TestTGenerator();
// TestScaleRecorder();
}

+ 84
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/test/ramp_checker.h View File

@@ -0,0 +1,84 @@
// Copyright 2015 Olivier Gillet.
//
// Author: Olivier Gillet (ol.gillet@gmail.com)
//
// 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/>.

#ifndef MARBLES_TEST_RAMP_CHECKER_H_
#define MARBLES_TEST_RAMP_CHECKER_H_

#include <cstdio>

namespace marbles {

class RampChecker {
public:
RampChecker() {
previous_phase_ = 0.0f;
counter_ = 0;
average_frequency_ = 0.1f;
stall_counter_ = 0;
}
~RampChecker() { }
void Error(const char* message) {
printf("RAMP ERROR: %.5f %s\n", float(counter_) / kSampleRate, message);
}
void Check(float* ramp, size_t size) {
while (size--) {
float phase = *ramp++;
float frequency = phase - previous_phase_;
if (phase < 0.0f || phase > 1.0f) {
Error("incorrect ramp value");
}
if (frequency < 0.0f) {
if (average_frequency_ < 0.05f) {
if (previous_phase_ < 0.75f) {
Error("reset without having reached maximum value");
}
if (phase > 0.25f) {
Error("does not reset to zero");
}
}
} else {
average_frequency_ += 0.1f * (frequency - average_frequency_);
}
if (frequency == 0.0f && phase < 0.01f) {
++stall_counter_;
if (stall_counter_ == 40) {
Error("Ramp stalls after reset");
}
} else {
stall_counter_ = 0;
}
if (counter_ > 10 && frequency > 0.5f) {
Error("High slope");
}
previous_phase_ = phase;
++counter_;
}
}
private:
size_t stall_counter_;
float previous_phase_;
float average_frequency_;
size_t counter_;
DISALLOW_COPY_AND_ASSIGN(RampChecker);
};

} // namespace marbles

#endif // MARBLES_TEST_RAMP_CHECKER_H_

+ 533
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ui.cc View File

@@ -0,0 +1,533 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// User interface.

#include "marbles/ui.h"

#include <algorithm>

#include "stmlib/system/system_clock.h"

#include "marbles/drivers/clock_inputs.h"
#include "marbles/cv_reader.h"
#include "marbles/scale_recorder.h"
#include "marbles/settings.h"

namespace marbles {

const int32_t kLongPressDuration = 2000;

using namespace std;
using namespace stmlib;

/* static */
const LedColor Ui::palette_[4] = {
LED_COLOR_GREEN,
LED_COLOR_YELLOW,
LED_COLOR_RED,
LED_COLOR_OFF
};

/* static */
AlternateKnobMapping Ui::alternate_knob_mappings_[ADC_CHANNEL_LAST];

void Ui::Init(
Settings* settings,
CvReader* cv_reader,
ScaleRecorder* scale_recorder,
ClockInputs* clock_inputs) {
settings_ = settings;
cv_reader_ = cv_reader;
scale_recorder_ = scale_recorder;
clock_inputs_ = clock_inputs;
leds_.Init();
switches_.Init();
queue_.Init();
// Initialize generator from settings_->state();
fill(&pot_value_[0], &pot_value_[ADC_CHANNEL_LAST], 0.0f);
State* state = settings_->mutable_state();
alternate_knob_mappings_[ADC_CHANNEL_T_BIAS].unlock_switch = SWITCH_T_MODEL;
alternate_knob_mappings_[ADC_CHANNEL_T_BIAS].destination = &state->t_pulse_width_mean;
alternate_knob_mappings_[ADC_CHANNEL_T_JITTER].unlock_switch = SWITCH_T_MODEL;
alternate_knob_mappings_[ADC_CHANNEL_T_JITTER].destination = &state->t_pulse_width_std;
alternate_knob_mappings_[ADC_CHANNEL_T_RATE].unlock_switch = SWITCH_X_MODE;
alternate_knob_mappings_[ADC_CHANNEL_T_RATE].destination = &state->y_divider;
alternate_knob_mappings_[ADC_CHANNEL_X_SPREAD].unlock_switch = SWITCH_X_MODE;
alternate_knob_mappings_[ADC_CHANNEL_X_SPREAD].destination = &state->y_spread;
alternate_knob_mappings_[ADC_CHANNEL_X_BIAS].unlock_switch = SWITCH_X_MODE;
alternate_knob_mappings_[ADC_CHANNEL_X_BIAS].destination = &state->y_bias;
alternate_knob_mappings_[ADC_CHANNEL_X_STEPS].unlock_switch = SWITCH_X_MODE;
alternate_knob_mappings_[ADC_CHANNEL_X_STEPS].destination = &state->y_steps;
setting_modification_flag_ = false;
output_test_mode_ = false;
if (switches_.pressed_immediate(SWITCH_X_MODE)) {
if (state->color_blind == 1) {
state->color_blind = 0;
} else {
state->color_blind = 1;
}
settings_->SaveState();
}
deja_vu_lock_ = false;
}

void Ui::SaveState() {
settings_->SaveState();
}

void Ui::Poll() {
// 1kHz.
system_clock.Tick();
switches_.Debounce();
for (int i = 0; i < SWITCH_LAST; ++i) {
if (switches_.just_pressed(Switch(i))) {
queue_.AddEvent(CONTROL_SWITCH, i, 0);
press_time_[i] = system_clock.milliseconds();
ignore_release_[i] = false;
}
if (switches_.pressed(Switch(i)) && !ignore_release_[i]) {
int32_t pressed_time = system_clock.milliseconds() - press_time_[i];
if (pressed_time > kLongPressDuration && !setting_modification_flag_) {
queue_.AddEvent(CONTROL_SWITCH, i, pressed_time);
ignore_release_[i] = true;
}
}
if (switches_.released(Switch(i)) && !ignore_release_[i]) {
queue_.AddEvent(
CONTROL_SWITCH,
i,
system_clock.milliseconds() - press_time_[i] + 1);
ignore_release_[i] = true;
}
}
UpdateLEDs();
}

/* static */
LedColor Ui::MakeColor(uint8_t value, bool color_blind) {
bool slow_blink = (system_clock.milliseconds() & 255) > 128;

uint8_t bank = value >= 3 ? 1 : 0;
value -= bank * 3;
LedColor color = palette_[value];
if (color_blind) {
uint8_t pwm_counter = system_clock.milliseconds() & 15;
uint8_t triangle = (system_clock.milliseconds() >> 5) & 31;
triangle = triangle < 16 ? triangle : 31 - triangle;
if (value == 0) {
color = pwm_counter < (4 + (triangle >> 2))
? LED_COLOR_GREEN
: LED_COLOR_OFF;
} else if (value == 1) {
color = LED_COLOR_YELLOW;
} else {
color = pwm_counter == 0 ? LED_COLOR_RED : LED_COLOR_OFF;
}
}

return slow_blink || !bank ? color : LED_COLOR_OFF;
}

void Ui::UpdateLEDs() {
bool blink = (system_clock.milliseconds() & 127) > 64;
bool slow_blink = (system_clock.milliseconds() & 255) > 128;
bool fast_blink = (system_clock.milliseconds() & 63) > 32;
const State& state = settings_->state();
bool cb = state.color_blind == 1;
LedColor scale_color = state.x_scale < 3
? (slow_blink ? palette_[state.x_scale] : LED_COLOR_OFF)
: (fast_blink ? palette_[state.x_scale - 3] : LED_COLOR_OFF);
if (cb) {
int poly_counter = (system_clock.milliseconds() >> 6) % 12;
if ((poly_counter >> 1) < (state.x_scale + 1) && (poly_counter & 1)) {
scale_color = LED_COLOR_YELLOW;
} else {
scale_color = LED_COLOR_OFF;
}
}
leds_.Clear();
int slow_triangle = (system_clock.milliseconds() & 1023) >> 5;
slow_triangle = slow_triangle >= 16 ? 31 - slow_triangle : slow_triangle;
int pw = system_clock.milliseconds() & 15;
bool deja_vu_glow = !deja_vu_lock_ || (slow_triangle >= pw);
switch (mode_) {
case UI_MODE_NORMAL:
case UI_MODE_RECORD_SCALE:
{
leds_.set(LED_T_MODEL, MakeColor(state.t_model, cb));
leds_.set(LED_T_RANGE, MakeColor(state.t_range, cb));
leds_.set(LED_T_DEJA_VU,
state.t_deja_vu && deja_vu_glow ?
LED_COLOR_GREEN : LED_COLOR_OFF);
leds_.set(LED_X_CONTROL_MODE, MakeColor(state.x_control_mode, cb));
leds_.set(LED_X_DEJA_VU,
state.x_deja_vu && deja_vu_glow ?
LED_COLOR_GREEN : LED_COLOR_OFF);
if (mode_ == UI_MODE_NORMAL) {
leds_.set(LED_X_RANGE,
state.x_register_mode
? LED_COLOR_OFF
: MakeColor(state.x_range, cb));
leds_.set(LED_X_EXT,
state.x_register_mode ? LED_COLOR_GREEN : LED_COLOR_OFF);
} else {
leds_.set(LED_X_RANGE, scale_color);
leds_.set(LED_X_EXT, LED_COLOR_GREEN);
}
}
break;

case UI_MODE_SELECT_SCALE:
leds_.set(LED_X_RANGE, scale_color);
break;
case UI_MODE_CALIBRATION_1:
leds_.set(LED_T_RANGE, blink ? MakeColor(0, cb) : LED_COLOR_OFF);
break;

case UI_MODE_CALIBRATION_2:
leds_.set(LED_T_RANGE, blink ? MakeColor(1, cb) : LED_COLOR_OFF);
break;

case UI_MODE_CALIBRATION_3:
leds_.set(LED_X_RANGE, blink ? MakeColor(0, cb) : LED_COLOR_OFF);
break;

case UI_MODE_CALIBRATION_4:
leds_.set(LED_X_RANGE, blink ? MakeColor(1, cb) : LED_COLOR_OFF);
break;
case UI_MODE_PANIC:
leds_.set(LED_T_MODEL, blink ? LED_COLOR_RED : LED_COLOR_OFF);
leds_.set(LED_T_RANGE, !blink ? LED_COLOR_RED : LED_COLOR_OFF);
leds_.set(LED_X_CONTROL_MODE, !blink ? LED_COLOR_RED : LED_COLOR_OFF);
leds_.set(LED_X_RANGE, blink ? LED_COLOR_RED : LED_COLOR_OFF);
break;
}
leds_.Write();
}

void Ui::FlushEvents() {
queue_.Flush();
}

void Ui::OnSwitchPressed(const Event& e) {

}

void Ui::OnSwitchReleased(const Event& e) {
if (setting_modification_flag_) {
for (int i = 0; i < ADC_CHANNEL_LAST; ++i) {
cv_reader_->mutable_channel(i)->UnlockPot();
}
setting_modification_flag_ = false;
return;
}
// Check if the other switch is still pressed.
if (e.control_id == SWITCH_T_RANGE && switches_.pressed(SWITCH_X_RANGE)) {
mode_ = UI_MODE_CALIBRATION_1;
ignore_release_[SWITCH_T_RANGE] = ignore_release_[SWITCH_X_RANGE] = true;
return;
}
State* state = settings_->mutable_state();
switch (e.control_id) {
case SWITCH_T_DEJA_VU:
state->t_deja_vu = !state->t_deja_vu;
break;

case SWITCH_X_DEJA_VU:
state->x_deja_vu = !state->x_deja_vu;
break;
case SWITCH_T_MODEL:
{
uint8_t bank = state->t_model / 3;
if (e.data >= kLongPressDuration) {
if (!bank) {
state->t_model += 3;
}
} else {
if (bank) {
state->t_model -= 3;
} else {
state->t_model = (state->t_model + 1) % 3;
}
}
SaveState();
}
break;

case SWITCH_T_RANGE:
{
if (mode_ >= UI_MODE_CALIBRATION_1 && mode_ <= UI_MODE_CALIBRATION_4) {
NextCalibrationStep();
} else {
state->t_range = (state->t_range + 1) % 3;
}
SaveState();
}
break;
case SWITCH_X_MODE:
state->x_control_mode = (state->x_control_mode + 1) % 3;
SaveState();
break;
case SWITCH_X_EXT:
if (mode_ == UI_MODE_RECORD_SCALE) {
int scale_index = settings_->state().x_scale;
bool success = true;
if (e.data >= kLongPressDuration) {
settings_->ResetScale(scale_index);
} else {
success = scale_recorder_->ExtractScale(
settings_->mutable_scale(scale_index));
}
if (success) {
settings_->SavePersistentData();
settings_->set_dirty_scale_index(scale_index);
}
mode_ = UI_MODE_NORMAL;
} else if (e.data >= kLongPressDuration) {
mode_ = UI_MODE_RECORD_SCALE;
scale_recorder_->Clear();
} else {
state->x_register_mode = !state->x_register_mode;
SaveState();
}
break;

case SWITCH_X_RANGE:
if (mode_ >= UI_MODE_CALIBRATION_1 && mode_ <= UI_MODE_CALIBRATION_4) {
NextCalibrationStep();
} else if (e.data >= kLongPressDuration) {
if (mode_ == UI_MODE_NORMAL) {
mode_ = UI_MODE_SELECT_SCALE;
}
} else if (mode_ == UI_MODE_SELECT_SCALE) {
state->x_scale = (state->x_scale + 1) % kNumScales;
} else {
if (!state->x_register_mode) {
state->x_range = (state->x_range + 1) % 3;
}
}
SaveState();
break;
}
}

void Ui::TerminateScaleRecording() {
for (int i = 0; i < ADC_CHANNEL_LAST; ++i) {
cv_reader_->mutable_channel(i)->UnlockPot();
}
mode_ = UI_MODE_NORMAL;
}

void Ui::NextCalibrationStep() {
switch (mode_) {
case UI_MODE_CALIBRATION_1:
cv_reader_->CalibrateOffsets();
cv_reader_->CalibrateRateC1();
mode_ = UI_MODE_CALIBRATION_2;
break;

case UI_MODE_CALIBRATION_2:
cv_reader_->CalibrateRateC3();
mode_ = UI_MODE_CALIBRATION_3;
break;

case UI_MODE_CALIBRATION_3:
cv_reader_->CalibrateSpreadC1();
mode_ = UI_MODE_CALIBRATION_4;
break;
case UI_MODE_CALIBRATION_4:
if (cv_reader_->CalibrateSpreadC3()) {
settings_->SavePersistentData();
mode_ = UI_MODE_NORMAL;
} else {
mode_ = UI_MODE_PANIC;
}
break;
default:
break;
}
}

void Ui::UpdateHiddenParameters() {
// Check if some pots have been moved.
for (int i = 0; i < ADC_CHANNEL_LAST; ++i) {
float new_value = cv_reader_->channel(i).unscaled_pot();
float old_value = pot_value_[i];
bool changed = fabs(new_value - old_value) >= 0.008f;
if (changed) {
pot_value_[i] = new_value;
AlternateKnobMapping mapping = alternate_knob_mappings_[i];
if (switches_.pressed(mapping.unlock_switch)) {
if (mapping.unlock_switch == SWITCH_T_RANGE && new_value < 0.1f) {
new_value = 0.0f;
}
*mapping.destination = static_cast<uint8_t>(new_value * 255.0f);
cv_reader_->mutable_channel(i)->LockPot();

// The next time a switch is released, we unlock the pots.
setting_modification_flag_ = true;
}
}
}
}

void Ui::DoEvents() {
while (queue_.available()) {
Event e = queue_.PullEvent();
if (e.control_type == CONTROL_SWITCH) {
if (e.data == 0) {
OnSwitchPressed(e);
} else {
OnSwitchReleased(e);
}
}
}
UpdateHiddenParameters();
if (queue_.idle_time() > 800 && mode_ == UI_MODE_PANIC) {
mode_ = UI_MODE_NORMAL;
}
if (mode_ == UI_MODE_SELECT_SCALE) {
if (queue_.idle_time() > 4000) {
mode_ = UI_MODE_NORMAL;
queue_.Touch();
}
} else if (queue_.idle_time() > 1000) {
queue_.Touch();
}
}

uint8_t Ui::HandleFactoryTestingRequest(uint8_t command) {
uint8_t argument = command & 0x1f;
command = command >> 5;
uint8_t reply = 0;
switch (command) {
case FACTORY_TESTING_READ_POT:
case FACTORY_TESTING_READ_CV:
reply = cv_reader_->adc_value(argument);
break;
case FACTORY_TESTING_READ_NORMALIZATION:
reply = clock_inputs_->is_normalized(ClockInput(argument)) ? 255 : 0;
break;
case FACTORY_TESTING_READ_GATE:
reply = argument >= SWITCH_LAST
? clock_inputs_->value(ClockInput(argument - SWITCH_LAST))
: switches_.pressed(Switch(argument));
break;
case FACTORY_TESTING_GENERATE_TEST_SIGNALS:
output_test_mode_ = static_cast<bool>(argument);
fill(
&output_test_forced_dac_code_[0],
&output_test_forced_dac_code_[4],
0);
break;
case FACTORY_TESTING_CALIBRATE:
if (argument == 0) {
// Revert all settings before getting into calibration mode.
settings_->mutable_state()->t_deja_vu = 0;
settings_->mutable_state()->x_deja_vu = 0;
settings_->mutable_state()->t_model = 0;
settings_->mutable_state()->t_range = 1;
settings_->mutable_state()->x_control_mode = 0;
settings_->mutable_state()->x_range = 2;
settings_->mutable_state()->x_register_mode = 0;
settings_->SavePersistentData();
mode_ = UI_MODE_CALIBRATION_1;
} else {
NextCalibrationStep();
}
{
const CalibrationData& cal = settings_->calibration_data();
float voltage = (argument & 1) == 0 ? 1.0f : 3.0f;
for (int i = 0; i < 4; ++i) {
output_test_forced_dac_code_[i] = static_cast<uint16_t>(
voltage * cal.dac_scale[i] + cal.dac_offset[i]);
}
}
queue_.Touch();
break;
case FACTORY_TESTING_FORCE_DAC_CODE:
{
int channel = argument >> 2;
int step = argument & 0x3;
if (step == 0) {
output_test_forced_dac_code_[channel] = 0xaf35;
} else if (step == 1) {
output_test_forced_dac_code_[channel] = 0x1d98;
} else {
CalibrationData* cal = settings_->mutable_calibration_data();
cal->dac_offset[channel] = static_cast<float>(
calibration_data_ & 0xffff);
cal->dac_scale[channel] = static_cast<float>(
calibration_data_ >> 16) * -0.125f;
output_test_forced_dac_code_[channel] = static_cast<uint16_t>(cal->dac_scale[channel] + cal->dac_offset[channel]);
settings_->SavePersistentData();
}
}
break;
case FACTORY_TESTING_WRITE_CALIBRATION_DATA_NIBBLE:
calibration_data_ <<= 4;
calibration_data_ |= argument & 0xf;
break;
}
return reply;
}

} // namespace marbles

+ 146
- 0
plugins/community/repos/AudibleInstruments/eurorack/marbles/ui.h View File

@@ -0,0 +1,146 @@
// Copyright 2015 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.
//
// -----------------------------------------------------------------------------
//
// User interface.

#ifndef MARBLES_UI_H_
#define MARBLES_UI_H_

#include "stmlib/stmlib.h"

#include "stmlib/ui/event_queue.h"

#include "marbles/drivers/adc.h"
#include "marbles/drivers/leds.h"
#include "marbles/drivers/switches.h"

namespace marbles {

class ClockInputs;
class CvReader;
class ScaleRecorder;
class Settings;

enum UiMode {
UI_MODE_NORMAL,
UI_MODE_SELECT_SCALE,
UI_MODE_RECORD_SCALE,
UI_MODE_CALIBRATION_1,
UI_MODE_CALIBRATION_2,
UI_MODE_CALIBRATION_3,
UI_MODE_CALIBRATION_4,
UI_MODE_PANIC,
};

enum FactoryTestingCommand {
FACTORY_TESTING_READ_POT,
FACTORY_TESTING_READ_CV,
FACTORY_TESTING_READ_GATE,
FACTORY_TESTING_GENERATE_TEST_SIGNALS,
FACTORY_TESTING_CALIBRATE,
FACTORY_TESTING_READ_NORMALIZATION,
FACTORY_TESTING_FORCE_DAC_CODE,
FACTORY_TESTING_WRITE_CALIBRATION_DATA_NIBBLE,
};

struct AlternateKnobMapping {
Switch unlock_switch;
uint8_t* destination;
};

class Ui {
public:
Ui() { }
~Ui() { }
void Init(
Settings* settings,
CvReader* cv_reader,
ScaleRecorder* scale_recorder,
ClockInputs* clock_inputs);
void Poll();
void DoEvents();
void FlushEvents();
uint8_t HandleFactoryTestingRequest(uint8_t command);
bool recording_scale() const {
return mode_ == UI_MODE_RECORD_SCALE;
}
bool output_test_mode() const {
return output_test_mode_;
}
uint16_t output_test_forced_dac_code(int i) const {
return output_test_forced_dac_code_[i];
}
void set_deja_vu_lock(bool deja_vu_lock) {
deja_vu_lock_ = deja_vu_lock;
}
private:
void UpdateLEDs();
void OnSwitchPressed(const stmlib::Event& e);
void OnSwitchReleased(const stmlib::Event& e);
void NextCalibrationStep();
void SaveState();
void UpdateHiddenParameters();
void TerminateScaleRecording();
static LedColor MakeColor(uint8_t value, bool color_blind);
stmlib::EventQueue<16> queue_;
Leds leds_;
Switches switches_;
uint32_t press_time_[SWITCH_LAST];
bool ignore_release_[SWITCH_LAST];
UiMode mode_;
Settings* settings_;
CvReader* cv_reader_;
ScaleRecorder* scale_recorder_;
ClockInputs* clock_inputs_;
static const LedColor palette_[4];
static AlternateKnobMapping alternate_knob_mappings_[ADC_CHANNEL_LAST];
float pot_value_[ADC_CHANNEL_LAST];
bool setting_modification_flag_;
bool deja_vu_lock_;
bool output_test_mode_;
uint16_t output_test_forced_dac_code_[4];
uint32_t calibration_data_;
DISALLOW_COPY_AND_ASSIGN(Ui);
};

} // namespace marbles

#endif // MARBLES_UI_H_

+ 0
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/__init__.py View File


+ 267
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/bootloader/bootloader.cc View File

@@ -0,0 +1,267 @@
// Copyright 2016 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.

#include <stm32f37x_conf.h>
#include <cstring>

#include "stmlib/system/bootloader_utils.h"
#include "stmlib/system/flash_programming.h"
#include "stmlib/system/system_clock.h"

#include "stm_audio_bootloader/qpsk/packet_decoder.h"
#include "stm_audio_bootloader/qpsk/demodulator.h"

#include "plaits/drivers/audio_dac.h"
#include "plaits/drivers/firmware_update_adc.h"
#include "plaits/drivers/leds.h"
#include "plaits/drivers/switches.h"

extern "C" {

void NMI_Handler() { }
void HardFault_Handler() { while (1); }
void MemManage_Handler() { while (1); }
void BusFault_Handler() { while (1); }
void UsageFault_Handler() { while (1); }
void SVC_Handler() { }
void DebugMon_Handler() { }
void PendSV_Handler() { }

}

using namespace plaits;
using namespace std;
using namespace stm_audio_bootloader;
using namespace stmlib;

const double kSampleRate = 48000.0;
const double kModulationRate = 6000.0;
const double kBitRate = 12000.0;
const uint32_t kStartAddress = 0x08008000;
const uint16_t kPacketsPerPage = PAGE_SIZE / kPacketSize;

enum UiState {
UI_STATE_WAITING,
UI_STATE_RECEIVING,
UI_STATE_ERROR,
UI_STATE_WRITING
};

AudioDac audio_dac;
FirmwareUpdateAdc adc;
Leds leds;
Switches switches;
PacketDecoder decoder;
Demodulator demodulator;

int discard_samples = 8000;
int32_t peak = 0;
int32_t gain_pot = 0;
uint32_t current_address;
uint16_t packet_index;
uint8_t rx_buffer[PAGE_SIZE];

volatile bool switch_released = false;
volatile UiState ui_state;

inline void UpdateLeds() {
leds.Clear();
// Show bargraph on the upper 4 LEDs.
int32_t pwm = system_clock.milliseconds() & 15;
leds.set(3, (peak >> 9) > pwm ? LED_COLOR_GREEN : 0);
leds.set(2, ((peak - 8192) >> 9) >= pwm ? LED_COLOR_GREEN : 0);
leds.set(1, ((peak - 16384) >> 9) >= pwm ? LED_COLOR_YELLOW : 0);
leds.set(0, ((peak - 16384 - 8192) >> 9) >= pwm ? LED_COLOR_RED : 0);
// Show status info on the lower 4 LEDs.
switch (ui_state) {
case UI_STATE_WAITING:
{
bool on = system_clock.milliseconds() & 128;
for (int i = 4; i < 8; ++i) {
leds.set(i, on ? LED_COLOR_YELLOW : LED_COLOR_OFF);
}
}
break;

case UI_STATE_RECEIVING:
{
int stage = (system_clock.milliseconds() >> 7) & 3;
leds.set(stage + 4, LED_COLOR_GREEN);
}
break;

case UI_STATE_ERROR:
{
bool on = system_clock.milliseconds() & 256;
for (int i = 0; i < 8; ++i) {
leds.set(i, on ? LED_COLOR_RED : LED_COLOR_OFF);
}
}
break;

case UI_STATE_WRITING:
{
for (uint8_t i = 4; i < 8; ++i) {
leds.set(i, LED_COLOR_GREEN);
}
}
break;
}
leds.Write();
}

extern "C" {
void SysTick_Handler() {
system_clock.Tick();
switches.Debounce();
if (switches.released(Switch(0))) {
switch_released = true;
}
UpdateLeds();
}
}

void ProgramPage(const uint8_t* data, size_t size) {
FLASH_Unlock();
FLASH_ErasePage(current_address);
const uint32_t* words = static_cast<const uint32_t*>(
static_cast<const void*>(data));
for (size_t written = 0; written < size; written += 4) {
FLASH_ProgramWord(current_address, *words++);
current_address += 4;
}
}

void FillBuffer(AudioDac::Frame* output, size_t size) {
gain_pot = (adc.gain_pot() + 4095 * gain_pot) >> 12;
int32_t sample = 32768 - static_cast<int32_t>(adc.sample());
adc.Convert();

int32_t gain = ((gain_pot >> 1) * gain_pot >> 21) + 128;
sample = sample * gain >> 8;
CONSTRAIN(sample, -32767, 32767)
int32_t rect = abs(sample);
peak = rect > peak ? rect : (rect + 32767 * peak) >> 15;

if (!discard_samples) {
demodulator.PushSample(2048 + (sample >> 4));
} else {
--discard_samples;
}
output->l = -sample;
output->r = -sample;
}

void InitializeReception() {
decoder.Init(1000, true);
demodulator.Init(
kModulationRate / kSampleRate * 4294967296.0,
kSampleRate / kModulationRate,
2.0 * kSampleRate / kBitRate);
demodulator.SyncCarrier(true);
decoder.Reset();
current_address = kStartAddress;
packet_index = 0;
ui_state = UI_STATE_WAITING;
}

void Init() {
adc.Init();
leds.Init();
switches.Init();
audio_dac.Init(48000, 1);
audio_dac.Start(&FillBuffer);
SysTick_Config(F_CPU / 1000);
}

int main(void) {
Init();
InitializeReception();
bool exit_updater = !switches.pressed_immediate(Switch(0));
while (!exit_updater) {
bool error = false;

if (demodulator.state() == DEMODULATOR_STATE_OVERFLOW) {
error = true;
} else {
demodulator.ProcessAtLeast(32);
}
while (demodulator.available() && !error && !exit_updater) {
uint8_t symbol = demodulator.NextSymbol();
PacketDecoderState state = decoder.ProcessSymbol(symbol);
switch (state) {
case PACKET_DECODER_STATE_OK:
{
ui_state = UI_STATE_RECEIVING;
memcpy(
rx_buffer + (packet_index % kPacketsPerPage) * kPacketSize,
decoder.packet_data(),
kPacketSize);
++packet_index;
if ((packet_index % kPacketsPerPage) == 0) {
ui_state = UI_STATE_WRITING;
ProgramPage(rx_buffer, PAGE_SIZE);
decoder.Reset();
demodulator.SyncCarrier(false);
ui_state = UI_STATE_RECEIVING;
} else {
decoder.Reset();
demodulator.SyncDecision();
}
}
break;
case PACKET_DECODER_STATE_ERROR_CRC:
case PACKET_DECODER_STATE_ERROR_SYNC:
error = true;
break;
break;
case PACKET_DECODER_STATE_END_OF_TRANSMISSION:
exit_updater = true;
break;
default:
break;
}
}
if (error) {
ui_state = UI_STATE_ERROR;
switch_released = false;
while (!switch_released); // Polled in ISR
InitializeReception();
}
}
adc.DeInit();
audio_dac.Stop();
Uninitialize();
JumpTo(kStartAddress);
while (1) { }
}

+ 46
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/bootloader/makefile View File

@@ -0,0 +1,46 @@
# Copyright 2016 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.

# System specifications
F_CRYSTAL = 8000000L
F_CPU = 72000000L
SYSCLOCK = SYSCLK_FREQ_72MHz
FAMILY = f37x
# USB = enabled

# Preferred upload command
UPLOAD_COMMAND = upload_jtag_erase_first

# Packages to build
TARGET = plaits_bootloader
PACKAGES = plaits/bootloader \
plaits/drivers \
stm_audio_bootloader/qpsk \
stmlib/dsp \
stmlib/utils \
stmlib/system

TOOLCHAIN_PATH ?= /usr/local/arm-4.8.3/

include stmlib/makefile.inc

+ 132
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/audio_dac.cc View File

@@ -0,0 +1,132 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the PCM5100 DAC.

#include "plaits/drivers/audio_dac.h"

#include <stm32f37x_conf.h>

namespace plaits {

/* static */
AudioDac* AudioDac::instance_;

void AudioDac::Init(int sample_rate, size_t block_size) {
instance_ = this;
block_size_ = block_size;
callback_ = NULL;
InitializeGPIO();
InitializeAudioInterface(sample_rate);
InitializeDMA(block_size);
}

void AudioDac::InitializeGPIO() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_10 | GPIO_Pin_11;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_5);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_5);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_5);
}

void AudioDac::InitializeAudioInterface(int sample_rate) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
SPI_I2S_DeInit(SPI2);
I2S_InitTypeDef i2s_init;
i2s_init.I2S_Mode = I2S_Mode_MasterTx;
i2s_init.I2S_Standard = I2S_Standard_Phillips;
i2s_init.I2S_DataFormat = I2S_DataFormat_16b;
i2s_init.I2S_MCLKOutput = I2S_MCLKOutput_Disable;
i2s_init.I2S_AudioFreq = sample_rate;
i2s_init.I2S_CPOL = I2S_CPOL_Low;
I2S_Init(SPI2, &i2s_init);
I2S_Cmd(SPI2, ENABLE);
}

void AudioDac::InitializeDMA(size_t block_size) {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_Cmd(DMA1_Channel5, DISABLE);
DMA_DeInit(DMA1_Channel5);

DMA_InitTypeDef dma_init;
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&(SPI2->DR);
dma_init.DMA_MemoryBaseAddr = (uint32_t)(&tx_dma_buffer_[0]);
dma_init.DMA_DIR = DMA_DIR_PeripheralDST;
dma_init.DMA_BufferSize = 2 * block_size * 2; // 2 channels, 2 half blocks.
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_Priority = DMA_Priority_High;
dma_init.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &dma_init);
// Enable the interrupts: half transfer and transfer complete.
DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_HT, ENABLE);
// Enable the IRQ.
NVIC_EnableIRQ(DMA1_Channel5_IRQn);
SPI_I2S_DMACmd(SPI2, SPI_I2S_DMAReq_Tx, ENABLE);
}

void AudioDac::Start(FillBufferCallback callback) {
callback_ = callback;
DMA_Cmd(DMA1_Channel5, ENABLE);
}

void AudioDac::Stop() {
DMA_Cmd(DMA1_Channel5, DISABLE);
}

void AudioDac::Fill(size_t offset) {
(*callback_)(&tx_dma_buffer_[offset * block_size_], block_size_);
}

} // namespace plaits

extern "C" {

void DMA1_Channel5_IRQHandler(void) {
uint32_t flags = DMA1->ISR;
DMA1->IFCR = DMA1_FLAG_TC5 | DMA1_FLAG_HT5;
if (flags & DMA1_FLAG_TC5) {
plaits::AudioDac::GetInstance()->Fill(1);
} else if (flags & DMA1_FLAG_HT5) {
plaits::AudioDac::GetInstance()->Fill(0);
}
}
}

+ 73
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/audio_dac.h View File

@@ -0,0 +1,73 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the PCM5100 DAC.

#ifndef PLAITS_DRIVERS_DAC_H_
#define PLAITS_DRIVERS_DAC_H_

#include "stmlib/stmlib.h"

namespace plaits {

const size_t kMaxCodecBlockSize = 24;

class AudioDac {
public:
AudioDac() { }
~AudioDac() { }
typedef struct {
short l;
short r;
} Frame;
typedef void (*FillBufferCallback)(Frame* tx, size_t size);
void Init(int sample_rate, size_t block_size);
void Start(FillBufferCallback callback);
void Stop();
void Fill(size_t offset);
static AudioDac* GetInstance() { return instance_; }

private:
void InitializeGPIO();
void InitializeAudioInterface(int sample_rate);
void InitializeDMA(size_t block_size);
static AudioDac* instance_;
size_t block_size_;
FillBufferCallback callback_;
Frame tx_dma_buffer_[kMaxCodecBlockSize * 2];
DISALLOW_COPY_AND_ASSIGN(AudioDac);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_DAC_H_

+ 193
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/cv_adc.cc View File

@@ -0,0 +1,193 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the 16-bit SDADC scanning the CVs.

#include "plaits/drivers/cv_adc.h"

#include <stm32f37x_conf.h>

namespace plaits {

struct ChannelConfiguration {
CvAdcChannel map_to;
uint32_t channel;
GPIO_TypeDef* gpio;
uint16_t pin;
};

struct ConverterConfiguration {
SDADC_TypeDef* sdadc;
DMA_Channel_TypeDef* dma_channel;
int num_channels;
ChannelConfiguration channel[3];
};

const ConverterConfiguration converter_configuration[3] = {
{
SDADC1, DMA2_Channel3, 3, {
{ CV_ADC_CHANNEL_TIMBRE, SDADC_Channel_4, GPIOB, GPIO_Pin_2 },
{ CV_ADC_CHANNEL_MODEL, SDADC_Channel_5, GPIOB, GPIO_Pin_1 },
{ CV_ADC_CHANNEL_TRIGGER, SDADC_Channel_6, GPIOB, GPIO_Pin_0 }
}
},
{
SDADC2, DMA2_Channel4, 2, {
{ CV_ADC_CHANNEL_FM, SDADC_Channel_7, GPIOE, GPIO_Pin_9 },
{ CV_ADC_CHANNEL_LEVEL, SDADC_Channel_8, GPIOE, GPIO_Pin_8 }
}
},
{
SDADC3, DMA2_Channel5, 3, {
{ CV_ADC_CHANNEL_HARMONICS, SDADC_Channel_6, GPIOD, GPIO_Pin_8 },
{ CV_ADC_CHANNEL_MORPH, SDADC_Channel_7, GPIOB, GPIO_Pin_15 },
{ CV_ADC_CHANNEL_V_OCT, SDADC_Channel_8, GPIOB, GPIO_Pin_14 }
}
},
};

void CvAdc::Init() {
// Power all the SDADCs.
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_SDADCAnalogCmd(PWR_SDADCAnalog_1, ENABLE);
PWR_SDADCAnalogCmd(PWR_SDADCAnalog_2, ENABLE);
PWR_SDADCAnalogCmd(PWR_SDADCAnalog_3, ENABLE);

// Enable SDADC clock.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDADC2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDADC3, ENABLE);
RCC_SDADCCLKConfig(RCC_SDADCCLK_SYSCLK_Div12);
// Enable DMA2 clock.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
// Enable GPIO clock.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE);
// Init SDADC
SDADC_VREFSelect(SDADC_VREF_Ext);
DMA_InitTypeDef dma_init;
GPIO_InitTypeDef gpio_init;
SDADC_AINStructTypeDef sdadc_ain;
// Fill structures with the settings common to all channels/pins.
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Mode = GPIO_Mode_AN;

sdadc_ain.SDADC_InputMode = SDADC_InputMode_SEZeroReference;
sdadc_ain.SDADC_Gain = SDADC_Gain_1;
sdadc_ain.SDADC_CommonMode = SDADC_CommonMode_VDDA_2;
sdadc_ain.SDADC_Offset = 0;
int current_channel = 0;
// Configure all SDADCs, all their input channels, and all the DMA channels.
for (int i = 0; i < 3; ++i) {
const ConverterConfiguration& config = converter_configuration[i];

// Wait for SDADC to stabilize.
SDADC_Cmd(config.sdadc, ENABLE);
while (SDADC_GetFlagStatus(config.sdadc, SDADC_FLAG_STABIP) == SET);
// Configure GPIO pins.
for (int j = 0; j < config.num_channels; ++j) {
gpio_init.GPIO_Pin = config.channel[j].pin;
GPIO_Init(config.channel[j].gpio, &gpio_init);
}
// SDADC enters initialization mode.
SDADC_InitModeCmd(config.sdadc, ENABLE);
while (SDADC_GetFlagStatus(config.sdadc, SDADC_FLAG_INITRDY) == RESET);
// Configure DMA to read injected values into a slice of the
// values_ array.
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&(config.sdadc->JDATAR);
dma_init.DMA_MemoryBaseAddr = (uint32_t)(&values_[current_channel]);
dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
dma_init.DMA_BufferSize = config.num_channels;
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_Priority = DMA_Priority_High;
dma_init.DMA_M2M = DMA_M2M_Disable;
DMA_Init(config.dma_channel, &dma_init);

// Create a configuration and assign it to all channels used by this SDADC.
SDADC_AINInit(config.sdadc, SDADC_Conf_0, &sdadc_ain);
uint32_t channels = 0;
for (int j = 0; j < config.num_channels; ++j) {
channel_map_[config.channel[j].map_to] = current_channel++;
channels |= config.channel[j].channel;
SDADC_ChannelConfig(
config.sdadc, config.channel[j].channel, SDADC_Conf_0);
}
// Select injected channels.
SDADC_InjectedChannelSelect(config.sdadc, channels);
// Disable continuous mode - the conversions are restarted every time
// we render a block of samples.
SDADC_InjectedContinuousModeCmd(config.sdadc, DISABLE);
// Terminate initialization sequence.
SDADC_CalibrationSequenceConfig(config.sdadc, SDADC_CalibrationSequence_3);
SDADC_InitModeCmd(config.sdadc, DISABLE);
while (SDADC_GetFlagStatus(config.sdadc, SDADC_FLAG_INITRDY) == SET);
// Run calibration sequence.
SDADC_StartCalibration(config.sdadc);
while (SDADC_GetFlagStatus(config.sdadc, SDADC_FLAG_EOCAL) == RESET);

// Enable DMA.
DMA_Cmd(config.dma_channel, ENABLE);
SDADC_DMAConfig(config.sdadc, SDADC_DMATransfer_Injected, ENABLE);
}
Convert();
}

void CvAdc::DeInit() {
for (int i = 0; i < 3; ++i) {
const ConverterConfiguration& config = converter_configuration[i];
SDADC_Cmd(config.sdadc, DISABLE);
SDADC_DMAConfig(config.sdadc, SDADC_DMATransfer_Injected, DISABLE);
DMA_Cmd(config.dma_channel, DISABLE);
}
}

void CvAdc::Convert() {
SDADC_SoftwareStartInjectedConv(SDADC1);
SDADC_SoftwareStartInjectedConv(SDADC2);
SDADC_SoftwareStartInjectedConv(SDADC3);
}

} // namespace plaits

+ 74
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/cv_adc.h View File

@@ -0,0 +1,74 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the 16-bit SDADC scanning the CV inputs.

#ifndef PLAITS_DRIVERS_CV_ADC_H_
#define PLAITS_DRIVERS_CV_ADC_H_

#include "stmlib/stmlib.h"

namespace plaits {

enum CvAdcChannel {
CV_ADC_CHANNEL_MODEL,
CV_ADC_CHANNEL_V_OCT,
CV_ADC_CHANNEL_FM,
CV_ADC_CHANNEL_HARMONICS,
CV_ADC_CHANNEL_TIMBRE,
CV_ADC_CHANNEL_MORPH,
CV_ADC_CHANNEL_TRIGGER,
CV_ADC_CHANNEL_LEVEL,
CV_ADC_CHANNEL_LAST
};

class CvAdc {
public:
CvAdc() { }
~CvAdc() { }
void Init();
void DeInit();
void Convert();

inline int16_t value(CvAdcChannel channel) const {
return values_[channel_map_[channel]];
}
inline float float_value(CvAdcChannel channel) const {
return static_cast<float>(value(channel)) / 32768.0f;
}
private:
int channel_map_[CV_ADC_CHANNEL_LAST];
int16_t values_[CV_ADC_CHANNEL_LAST];
DISALLOW_COPY_AND_ASSIGN(CvAdc);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_CV_ADC_H_

+ 72
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/debug_pin.h View File

@@ -0,0 +1,72 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the debug (timing) pin.

#ifndef PLAITS_DRIVERS_DEBUG_PIN_H_
#define PLAITS_DRIVERS_DEBUG_PIN_H_

#include "stmlib/stmlib.h"

#include <stm32f37x_conf.h>

namespace plaits {

class DebugPin {
public:
DebugPin() { }
~DebugPin() { }
static void Init() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_8;
GPIO_Init(GPIOB, &gpio_init);
}

static inline void High() {
GPIOB->BSRR = GPIO_Pin_8;
}

static inline void Low() {
GPIOB->BRR = GPIO_Pin_8;
}
private:
DISALLOW_COPY_AND_ASSIGN(DebugPin);
};

#define TIC DebugPin::High();
#define TOC DebugPin::Low();

} // namespace plaits

#endif // PLAITS_DRIVERS_DEBUG_PIN_H_

+ 62
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/debug_port.cc View File

@@ -0,0 +1,62 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// UART driver for conversing with the factory testing program.

#include "plaits/drivers/debug_port.h"

namespace plaits {

void DebugPort::Init() {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
// Initialize TX and RX pins.
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOB, &gpio_init);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_7);
GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_7);
// Initialize USART.
USART_InitTypeDef usart_init;
usart_init.USART_BaudRate = 9600;
usart_init.USART_WordLength = USART_WordLength_8b;
usart_init.USART_StopBits = USART_StopBits_1;
usart_init.USART_Parity = USART_Parity_No;
usart_init.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart_init.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART3, &usart_init);
USART_Cmd(USART3, ENABLE);
}

} // namespace plaits

+ 67
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/debug_port.h View File

@@ -0,0 +1,67 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// UART driver for conversing with the factory testing program.

#ifndef PLAITS_DRIVERS_DEBUG_PORT_H_
#define PLAITS_DRIVERS_DEBUG_PORT_H_

#include "stmlib/stmlib.h"

#include <stm32f37x_conf.h>

namespace plaits {

class DebugPort {
public:
DebugPort() { }
~DebugPort() { }
void Init();
bool writable() {
return USART3->ISR & USART_FLAG_TXE;
}
bool readable() {
return USART3->ISR & USART_FLAG_RXNE;
}
void Write(uint8_t byte) {
USART3->TDR = byte;
}
uint8_t Read() {
return USART3->RDR;
}
private:
DISALLOW_COPY_AND_ASSIGN(DebugPort);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_DEBUG_PORT_H_

+ 111
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/firmware_update_adc.cc View File

@@ -0,0 +1,111 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the 12-bit ADC used for firmware updates through the MODEL CV in.

#include "plaits/drivers/firmware_update_adc.h"

#include <stm32f37x_conf.h>

namespace plaits {

void FirmwareUpdateAdc::Init() {
// Enable ADC clock.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
// Enable GPIO clock.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// Enable DMA1 clock.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef dma_init;
ADC_InitTypeDef adc_init;
GPIO_InitTypeDef gpio_init;
// Configure the two analog inputs.
gpio_init.GPIO_Pin = GPIO_Pin_1;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Mode = GPIO_Mode_AN;
GPIO_Init(GPIOB, &gpio_init);
gpio_init.GPIO_Pin = GPIO_Pin_0;
GPIO_Init(GPIOA, &gpio_init);
// Use DMA to automatically copy ADC data register to values_ buffer.
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
dma_init.DMA_MemoryBaseAddr = (uint32_t)&values_[0];
dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
dma_init.DMA_BufferSize = 2;
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_Priority = DMA_Priority_High;
dma_init.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &dma_init);
DMA_Cmd(DMA1_Channel1, ENABLE);

// Init ADC1.
adc_init.ADC_ScanConvMode = ENABLE;
adc_init.ADC_ContinuousConvMode = DISABLE;
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc_init.ADC_DataAlign = ADC_DataAlign_Left;
adc_init.ADC_NbrOfChannel = 2;
ADC_Init(ADC1, &adc_init);
// Sample rate: 53.6 kHz
// 72000 / 6 / (12.5 * 2 + 71.5 * 2)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_71Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_9, 2, ADC_SampleTime_71Cycles5);
// Enable and calibrate ADC1.
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
ADC_DMACmd(ADC1, ENABLE);

values_[0] = values_[1] = 32768;
Convert();
}

void FirmwareUpdateAdc::DeInit() {
ADC_DMACmd(ADC1, DISABLE);
ADC_Cmd(ADC1, DISABLE);
DMA_Cmd(DMA1_Channel1, DISABLE);
}

void FirmwareUpdateAdc::Convert() {
ADC_SoftwareStartConv(ADC1);
}

} // namespace plaits

+ 60
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/firmware_update_adc.h View File

@@ -0,0 +1,60 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the 12-bit ADC used for firmware updates through the MODEL CV in.

#ifndef PLAITS_DRIVERS_FIRMWARE_UPDATE_ADC_H_
#define PLAITS_DRIVERS_FIRMWARE_UPDATE_ADC_H_

#include "stmlib/stmlib.h"

namespace plaits {

class FirmwareUpdateAdc {
public:
FirmwareUpdateAdc() { }
~FirmwareUpdateAdc() { }
void Init();
void DeInit();
void Convert();
inline uint16_t gain_pot() const {
return values_[0];
}
inline uint16_t sample() const {
return values_[1];
}

private:
uint16_t values_[2];
DISALLOW_COPY_AND_ASSIGN(FirmwareUpdateAdc);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_FIRMWARE_UPDATE_ADC_H_

+ 114
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/leds.cc View File

@@ -0,0 +1,114 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the column of LEDs.

#include "plaits/drivers/leds.h"

#include <algorithm>

#include <stm32f37x_conf.h>

namespace plaits {

using namespace std;

const uint16_t kPinEnable = GPIO_Pin_7;

void Leds::Init() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);
GPIO_InitTypeDef gpio_init;
// SS
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = kPinEnable;
GPIO_Init(GPIOF, &gpio_init);
// MOSI
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOF, &gpio_init);
GPIO_PinAFConfig(GPIOF, GPIO_PinSource6, GPIO_AF_5);

// SCK
gpio_init.GPIO_Mode = GPIO_Mode_AF;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_12;
GPIO_Init(GPIOA, &gpio_init);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource12, GPIO_AF_6);
// Initialize SPI
SPI_InitTypeDef spi_init;
spi_init.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
spi_init.SPI_Mode = SPI_Mode_Master;
spi_init.SPI_DataSize = SPI_DataSize_16b;
spi_init.SPI_CPOL = SPI_CPOL_Low;
spi_init.SPI_CPHA = SPI_CPHA_1Edge;
spi_init.SPI_NSS = SPI_NSS_Soft;
spi_init.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
spi_init.SPI_FirstBit = SPI_FirstBit_MSB;
spi_init.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &spi_init);
SPI_Cmd(SPI1, ENABLE);
Clear();
}

void Leds::Clear() {
fill(&colors_[0], &colors_[kNumLEDs], LED_COLOR_OFF);
}

/* static */
const int Leds::led_map_[8] = {
0, 1, 2, 3, 7, 6, 5, 4
};

void Leds::Write() {
uint16_t leds_data = 0;
for (int i = 0; i < kNumLEDs; ++i) {
int j = led_map_[i];
leds_data <<= 2;
leds_data |= (colors_[j] & LED_COLOR_RED) ? 1 : 0;
leds_data |= (colors_[j] & LED_COLOR_GREEN) ? 2 : 0;
}
GPIOF->BSRR = kPinEnable;
__asm__("nop");
GPIOF->BRR = kPinEnable;
__asm__("nop");
SPI1->DR = leds_data;
}

} // namespace plaits

+ 72
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/leds.h View File

@@ -0,0 +1,72 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the column of LEDs.

#ifndef PLAITS_DRIVERS_LEDS_H_
#define PLAITS_DRIVERS_LEDS_H_

#include "stmlib/stmlib.h"

namespace plaits {

const int kNumLEDs = 8;

enum LedColor {
LED_COLOR_OFF = 0,
LED_COLOR_RED = 0xff0000,
LED_COLOR_GREEN = 0x00ff00,
LED_COLOR_YELLOW = 0xffff00,
};

class Leds {
public:
Leds() { }
~Leds() { }
void Init();
void Write();
void Clear();
void set(int index, uint32_t color) {
colors_[index] = color;
}

void mask(int index, uint32_t color) {
colors_[index] |= color;
}
private:
uint32_t colors_[kNumLEDs];
static const int led_map_[kNumLEDs];
DISALLOW_COPY_AND_ASSIGN(Leds);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_LEDS_H_

+ 87
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/normalization_probe.h View File

@@ -0,0 +1,87 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the normalization probe.

#ifndef PLAITS_DRIVERS_NORMALIZATION_PROBE_H_
#define PLAITS_DRIVERS_NORMALIZATION_PROBE_H_

#include "stmlib/stmlib.h"

#include <stm32f37x_conf.h>

namespace plaits {

class NormalizationProbe {
public:
NormalizationProbe() { }
~NormalizationProbe() { }
static inline void Init() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_OUT;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &gpio_init);
}
void Disable() {
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_IN;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_Pin = GPIO_Pin_9;
GPIO_Init(GPIOA, &gpio_init);
}
static inline void High() {
GPIOA->BSRR = GPIO_Pin_9;
}
static inline void Low() {
GPIOA->BRR = GPIO_Pin_9;
}
static inline void Write(bool value) {
if (value) {
High();
} else {
Low();
}
}

private:
DISALLOW_COPY_AND_ASSIGN(NormalizationProbe);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_NORMALIZATION_PROBE_H_

+ 111
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/pots_adc.cc View File

@@ -0,0 +1,111 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the 12-bit ADC scanning pots.

#include "plaits/drivers/pots_adc.h"

#include <algorithm>

#include <stm32f37x_conf.h>

namespace plaits {

using namespace std;

void PotsAdc::Init() {
// Enable ADC clock.
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div8);
// Enable GPIO clock.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
// Enable DMA1 clock.
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_InitTypeDef dma_init;
ADC_InitTypeDef adc_init;
GPIO_InitTypeDef gpio_init;
// Configure the two analog inputs.
gpio_init.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | \
GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
gpio_init.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Mode = GPIO_Mode_AN;
GPIO_Init(GPIOA, &gpio_init);

// Use DMA to automatically copy ADC data register to values_ buffer.
dma_init.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
dma_init.DMA_MemoryBaseAddr = (uint32_t)&values_[0];
dma_init.DMA_DIR = DMA_DIR_PeripheralSRC;
dma_init.DMA_BufferSize = POTS_ADC_CHANNEL_LAST;
dma_init.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
dma_init.DMA_MemoryInc = DMA_MemoryInc_Enable;
dma_init.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
dma_init.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
dma_init.DMA_Mode = DMA_Mode_Circular;
dma_init.DMA_Priority = DMA_Priority_High;
dma_init.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &dma_init);
DMA_Cmd(DMA1_Channel1, ENABLE);
// Init ADC1.
adc_init.ADC_ScanConvMode = ENABLE;
adc_init.ADC_ContinuousConvMode = DISABLE;
adc_init.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
adc_init.ADC_DataAlign = ADC_DataAlign_Left;
adc_init.ADC_NbrOfChannel = POTS_ADC_CHANNEL_LAST;
ADC_Init(ADC1, &adc_init);
// Sample rate: 5.10 kHz > 4kHz
// 72000 / 8 / ((12.5 + 239.5) * 7)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 2, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 3, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 4, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 5, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 6, ADC_SampleTime_239Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 7, ADC_SampleTime_239Cycles5);
// Enable and calibrate ADC1.
ADC_Cmd(ADC1, ENABLE);
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1));
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1));
ADC_DMACmd(ADC1, ENABLE);
fill(&values_[0], &values_[POTS_ADC_CHANNEL_LAST], 32768);
Convert();
}

void PotsAdc::Convert() {
ADC_SoftwareStartConv(ADC1);
}

} // namespace plaits

+ 71
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/pots_adc.h View File

@@ -0,0 +1,71 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Drivers for the 12-bit ADC scanning pots.

#ifndef PLAITS_DRIVERS_POTS_ADC_H_
#define PLAITS_DRIVERS_POTS_ADC_H_

#include "stmlib/stmlib.h"

namespace plaits {

enum PotsAdcChannel {
POTS_ADC_CHANNEL_FREQ_POT,
POTS_ADC_CHANNEL_HARMONICS_POT,
POTS_ADC_CHANNEL_TIMBRE_POT,
POTS_ADC_CHANNEL_MORPH_POT,
POTS_ADC_CHANNEL_TIMBRE_ATTENUVERTER,
POTS_ADC_CHANNEL_FM_ATTENUVERTER,
POTS_ADC_CHANNEL_MORPH_ATTENUVERTER,
POTS_ADC_CHANNEL_LAST
};

class PotsAdc {
public:
PotsAdc() { }
~PotsAdc() { }
void Init();
void Convert();

inline int32_t value(PotsAdcChannel channel) const {
return static_cast<int32_t>(values_[channel]);
}
inline float float_value(PotsAdcChannel channel) const {
return static_cast<float>(value(channel)) / 65536.0f;
}
private:
uint16_t values_[POTS_ADC_CHANNEL_LAST];
DISALLOW_COPY_AND_ASSIGN(PotsAdc);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_POTS_ADC_H_

+ 72
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/switches.cc View File

@@ -0,0 +1,72 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the 2 switches.

#include "plaits/drivers/switches.h"

#include <algorithm>

namespace plaits {

using namespace std;

struct SwitchDefinition {
GPIO_TypeDef* gpio;
uint16_t pin;
};

const SwitchDefinition switch_definitions[] = {
{ GPIOB, GPIO_Pin_7 },
{ GPIOB, GPIO_Pin_6 },
};

void Switches::Init() {
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
GPIO_InitTypeDef gpio_init;
gpio_init.GPIO_Mode = GPIO_Mode_IN;
gpio_init.GPIO_OType = GPIO_OType_PP;
gpio_init.GPIO_Speed = GPIO_Speed_2MHz;
gpio_init.GPIO_PuPd = GPIO_PuPd_UP;
for (int i = 0; i < SWITCH_LAST; ++i) {
const SwitchDefinition& definition = switch_definitions[i];
gpio_init.GPIO_Pin = definition.pin;
GPIO_Init(definition.gpio, &gpio_init);
}
fill(&switch_state_[0], &switch_state_[SWITCH_LAST], 0xff);
}

void Switches::Debounce() {
for (int i = 0; i < SWITCH_LAST; ++i) {
const SwitchDefinition& definition = switch_definitions[i];
switch_state_[i] = (switch_state_[i] << 1) | \
GPIO_ReadInputDataBit(definition.gpio, definition.pin);
}
}

} // namespace plaits

+ 82
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/drivers/switches.h View File

@@ -0,0 +1,82 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Driver for the 2 switches.

#ifndef PLAITS_DRIVERS_SWITCHES_H_
#define PLAITS_DRIVERS_SWITCHES_H_

#include "stmlib/stmlib.h"

#include <stm32f37x_conf.h>

namespace plaits {

enum Switch {
SWITCH_ROW_1,
SWITCH_ROW_2,
SWITCH_LAST
};
class Switches {
public:
Switches() { }
~Switches() { }
void Init();
void Debounce();
inline bool released(Switch s) const {
return switch_state_[s] == 0x7f;
}
inline bool just_pressed(Switch s) const {
return switch_state_[s] == 0x80;
}

inline bool pressed(Switch s) const {
return switch_state_[s] == 0x00;
}
inline bool pressed_immediate(Switch s) const {
if (s == SWITCH_ROW_1) {
return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7);
} else if (s == SWITCH_ROW_2) {
return !GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_6);
} else {
return false;
}
}
private:
uint8_t switch_state_[SWITCH_LAST];
DISALLOW_COPY_AND_ASSIGN(Switches);
};

} // namespace plaits

#endif // PLAITS_DRIVERS_SWITCHES_H_

+ 195
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/analog_bass_drum.h View File

@@ -0,0 +1,195 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// 808 bass drum model, revisited.

#ifndef PLAITS_DSP_DRUMS_ANALOG_BASS_DRUM_H_
#define PLAITS_DSP_DRUMS_ANALOG_BASS_DRUM_H_

#include <algorithm>

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/filter.h"
#include "stmlib/dsp/parameter_interpolator.h"
#include "stmlib/dsp/units.h"

#include "plaits/dsp/dsp.h"
#include "plaits/dsp/oscillator/sine_oscillator.h"

namespace plaits {

class AnalogBassDrum {
public:
AnalogBassDrum() { }
~AnalogBassDrum() { }

void Init() {
pulse_remaining_samples_ = 0;
fm_pulse_remaining_samples_ = 0;
pulse_ = 0.0f;
pulse_height_ = 0.0f;
pulse_lp_ = 0.0f;
fm_pulse_lp_ = 0.0f;
retrig_pulse_ = 0.0f;
lp_out_ = 0.0f;
tone_lp_ = 0.0f;
sustain_gain_ = 0.0f;

resonator_.Init();
oscillator_.Init();
}
inline float Diode(float x) {
if (x >= 0.0f) {
return x;
} else {
x *= 2.0f;
return 0.7f * x / (1.0f + fabsf(x));
}
}
void Render(
bool sustain,
bool trigger,
float accent,
float f0,
float tone,
float decay,
float attack_fm_amount,
float self_fm_amount,
float* out,
size_t size) {
const int kTriggerPulseDuration = 1.0e-3 * kSampleRate;
const int kFMPulseDuration = 6.0e-3 * kSampleRate;
const float kPulseDecayTime = 0.2e-3 * kSampleRate;
const float kPulseFilterTime = 0.1e-3 * kSampleRate;
const float kRetrigPulseDuration = 0.05f * kSampleRate;
const float scale = 0.001f / f0;
const float q = 1500.0f * stmlib::SemitonesToRatio(decay * 80.0f);
const float tone_f = std::min(
4.0f * f0 * stmlib::SemitonesToRatio(tone * 108.0f),
1.0f);
const float exciter_leak = 0.08f * (tone + 0.25f);

if (trigger) {
pulse_remaining_samples_ = kTriggerPulseDuration;
fm_pulse_remaining_samples_ = kFMPulseDuration;
pulse_height_ = 3.0f + 7.0f * accent;
lp_out_ = 0.0f;
}
stmlib::ParameterInterpolator sustain_gain(
&sustain_gain_,
accent * decay,
size);
while (size--) {
// Q39 / Q40
float pulse = 0.0f;
if (pulse_remaining_samples_) {
--pulse_remaining_samples_;
pulse = pulse_remaining_samples_ ? pulse_height_ : pulse_height_ - 1.0f;
pulse_ = pulse;
} else {
pulse_ *= 1.0f - 1.0f / kPulseDecayTime;
pulse = pulse_;
}
if (sustain) {
pulse = 0.0f;
}
// C40 / R163 / R162 / D83
ONE_POLE(pulse_lp_, pulse, 1.0f / kPulseFilterTime);
pulse = Diode((pulse - pulse_lp_) + pulse * 0.044f);

// Q41 / Q42
float fm_pulse = 0.0f;
if (fm_pulse_remaining_samples_) {
--fm_pulse_remaining_samples_;
fm_pulse = 1.0f;
// C39 / C52
retrig_pulse_ = fm_pulse_remaining_samples_ ? 0.0f : -0.8f;
} else {
// C39 / R161
retrig_pulse_ *= 1.0f - 1.0f / kRetrigPulseDuration;
}
if (sustain) {
fm_pulse = 0.0f;
}
ONE_POLE(fm_pulse_lp_, fm_pulse, 1.0f / kPulseFilterTime);

// Q43 and R170 leakage
float punch = 0.7f + Diode(10.0f * lp_out_ - 1.0f);

// Q43 / R165
float attack_fm = fm_pulse_lp_ * 1.7f * attack_fm_amount;
float self_fm = punch * 0.08f * self_fm_amount;
float f = f0 * (1.0f + attack_fm + self_fm);
CONSTRAIN(f, 0.0f, 0.4f);

float resonator_out;
if (sustain) {
oscillator_.Next(f, sustain_gain.Next(), &resonator_out, &lp_out_);
} else {
resonator_.set_f_q<stmlib::FREQUENCY_DIRTY>(f, 1.0f + q * f);
resonator_.Process<stmlib::FILTER_MODE_BAND_PASS,
stmlib::FILTER_MODE_LOW_PASS>(
(pulse - retrig_pulse_ * 0.2f) * scale,
&resonator_out,
&lp_out_);
}
ONE_POLE(tone_lp_, pulse * exciter_leak + resonator_out, tone_f);
*out++ = tone_lp_;
}
}

private:
int pulse_remaining_samples_;
int fm_pulse_remaining_samples_;
float pulse_;
float pulse_height_;
float pulse_lp_;
float fm_pulse_lp_;
float retrig_pulse_;
float lp_out_;
float tone_lp_;
float sustain_gain_;
stmlib::Svf resonator_;
// Replace the resonator in "free running" (sustain) mode.
SineOscillator oscillator_;
DISALLOW_COPY_AND_ASSIGN(AnalogBassDrum);
};
} // namespace plaits

#endif // PLAITS_DSP_DRUMS_ANALOG_BASS_DRUM_H_

+ 201
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/analog_snare_drum.h View File

@@ -0,0 +1,201 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// 808 snare drum model, revisited.

#ifndef PLAITS_DSP_DRUMS_ANALOG_SNARE_DRUM_H_
#define PLAITS_DSP_DRUMS_ANALOG_SNARE_DRUM_H_

#include <algorithm>

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/filter.h"
#include "stmlib/dsp/parameter_interpolator.h"
#include "stmlib/dsp/units.h"
#include "stmlib/utils/random.h"

#include "plaits/dsp/dsp.h"
#include "plaits/dsp/oscillator/sine_oscillator.h"

namespace plaits {

class AnalogSnareDrum {
public:
AnalogSnareDrum() { }
~AnalogSnareDrum() { }

static const int kNumModes = 5;

void Init() {
pulse_remaining_samples_ = 0;
pulse_ = 0.0f;
pulse_height_ = 0.0f;
pulse_lp_ = 0.0f;
noise_envelope_ = 0.0f;
sustain_gain_ = 0.0f;

for (int i = 0; i < kNumModes; ++i) {
resonator_[i].Init();
oscillator_[i].Init();
}
noise_filter_.Init();
}
void Render(
bool sustain,
bool trigger,
float accent,
float f0,
float tone,
float decay,
float snappy,
float* out,
size_t size) {
const float decay_xt = decay * (1.0f + decay * (decay - 1.0f));
const int kTriggerPulseDuration = 1.0e-3 * kSampleRate;
const float kPulseDecayTime = 0.1e-3 * kSampleRate;
const float q = 2000.0f * stmlib::SemitonesToRatio(decay_xt * 84.0f);
const float noise_envelope_decay = 1.0f - 0.0017f * \
stmlib::SemitonesToRatio(-decay * (50.0f + snappy * 10.0f));
const float exciter_leak = snappy * (2.0f - snappy) * 0.1f;
snappy = snappy * 1.1f - 0.05f;
CONSTRAIN(snappy, 0.0f, 1.0f);
if (trigger) {
pulse_remaining_samples_ = kTriggerPulseDuration;
pulse_height_ = 3.0f + 7.0f * accent;
noise_envelope_ = 2.0f;
}
static const float kModeFrequencies[kNumModes] = {
1.00f,
2.00f,
3.18f,
4.16f,
5.62f};
float f[kNumModes];
float gain[kNumModes];
for (int i = 0; i < kNumModes; ++i) {
f[i] = std::min(f0 * kModeFrequencies[i], 0.499f);
resonator_[i].set_f_q<stmlib::FREQUENCY_FAST>(
f[i],
1.0f + f[i] * (i == 0 ? q : q * 0.25f));
}
if (tone < 0.666667f) {
// 808-style (2 modes)
tone *= 1.5f;
gain[0] = 1.5f + (1.0f - tone) * (1.0f - tone) * 4.5f;
gain[1] = 2.0f * tone + 0.15f;
std::fill(&gain[2], &gain[kNumModes], 0.0f);
} else {
// What the 808 could have been if there were extra modes!
tone = (tone - 0.666667f) * 3.0f;
gain[0] = 1.5f - tone * 0.5f;
gain[1] = 2.15f - tone * 0.7f;
for (int i = 2; i < kNumModes; ++i) {
gain[i] = tone;
tone *= tone;
}
}

float f_noise = f0 * 16.0f;
CONSTRAIN(f_noise, 0.0f, 0.499f);
noise_filter_.set_f_q<stmlib::FREQUENCY_FAST>(
f_noise, 1.0f + f_noise * 1.5f);
stmlib::ParameterInterpolator sustain_gain(
&sustain_gain_,
accent * decay,
size);
while (size--) {
// Q45 / Q46
float pulse = 0.0f;
if (pulse_remaining_samples_) {
--pulse_remaining_samples_;
pulse = pulse_remaining_samples_ ? pulse_height_ : pulse_height_ - 1.0f;
pulse_ = pulse;
} else {
pulse_ *= 1.0f - 1.0f / kPulseDecayTime;
pulse = pulse_;
}
float sustain_gain_value = sustain_gain.Next();
// R189 / C57 / R190 + C58 / C59 / R197 / R196 / IC14
ONE_POLE(pulse_lp_, pulse, 0.75f);
float shell = 0.0f;
for (int i = 0; i < kNumModes; ++i) {
float excitation = i == 0
? (pulse - pulse_lp_) + 0.006f * pulse
: 0.026f * pulse;
shell += gain[i] * (sustain
? oscillator_[i].Next(f[i]) * sustain_gain_value * 0.25f
: resonator_[i].Process<stmlib::FILTER_MODE_BAND_PASS>(
excitation) + excitation * exciter_leak);
}
shell = stmlib::SoftClip(shell);
// C56 / R194 / Q48 / C54 / R188 / D54
float noise = 2.0f * stmlib::Random::GetFloat() - 1.0f;
if (noise < 0.0f) noise = 0.0f;
noise_envelope_ *= noise_envelope_decay;
noise *= (sustain ? sustain_gain_value : noise_envelope_) * snappy * 2.0f;

// C66 / R201 / C67 / R202 / R203 / Q49
noise = noise_filter_.Process<stmlib::FILTER_MODE_BAND_PASS>(noise);
// IC13
*out++ = noise + shell * (1.0f - snappy);
}
}

private:
int pulse_remaining_samples_;
float pulse_;
float pulse_height_;
float pulse_lp_;
float noise_envelope_;
float sustain_gain_;
stmlib::Svf resonator_[kNumModes];
stmlib::Svf noise_filter_;

// Replace the resonators in "free running" (sustain) mode.
SineOscillator oscillator_[kNumModes];
DISALLOW_COPY_AND_ASSIGN(AnalogSnareDrum);
};
} // namespace plaits

#endif // PLAITS_DSP_DRUMS_ANALOG_SNARE_DRUM_H_

+ 259
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/hi_hat.h View File

@@ -0,0 +1,259 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// 808 HH, with a few extra parameters to push things to the CY territory...
// The template parameter MetallicNoiseSource allows another kind of "metallic
// noise" to be used, for results which are more similar to KR-55 or FM hi-hats.

#ifndef PLAITS_DSP_DRUMS_HI_HAT_H_
#define PLAITS_DSP_DRUMS_HI_HAT_H_

#include <algorithm>

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/filter.h"
#include "stmlib/dsp/parameter_interpolator.h"
#include "stmlib/dsp/units.h"
#include "stmlib/utils/random.h"

#include "plaits/dsp/dsp.h"
#include "plaits/dsp/oscillator/oscillator.h"

namespace plaits {

// 808 style "metallic noise" with 6 square oscillators.
class SquareNoise {
public:
SquareNoise() { }
~SquareNoise() { }

void Init() {
std::fill(&phase_[0], &phase_[6], 0);
}
void Render(float f0, float* temp_1, float* temp_2, float* out, size_t size) {
const float ratios[6] = {
// Nominal f0: 414 Hz
1.0f, 1.304f, 1.466f, 1.787f, 1.932f, 2.536f
};
uint32_t increment[6];
uint32_t phase[6];
for (int i = 0; i < 6; ++i) {
float f = f0 * ratios[i];
if (f >= 0.499f) f = 0.499f;
increment[i] = static_cast<uint32_t>(f * 4294967296.0f);
phase[i] = phase_[i];
}

while (size--) {
phase[0] += increment[0];
phase[1] += increment[1];
phase[2] += increment[2];
phase[3] += increment[3];
phase[4] += increment[4];
phase[5] += increment[5];
uint32_t noise = 0;
noise += (phase[0] >> 31);
noise += (phase[1] >> 31);
noise += (phase[2] >> 31);
noise += (phase[3] >> 31);
noise += (phase[4] >> 31);
noise += (phase[5] >> 31);
*out++ = 0.33f * static_cast<float>(noise) - 1.0f;
}
for (int i = 0; i < 6; ++i) {
phase_[i] = phase[i];
}
}

private:
uint32_t phase_[6];

DISALLOW_COPY_AND_ASSIGN(SquareNoise);
};

class RingModNoise {
public:
RingModNoise() { }
~RingModNoise() { }

void Init() {
for (int i = 0; i < 6; ++i) {
oscillator_[i].Init();
}
}
void Render(float f0, float* temp_1, float* temp_2, float* out, size_t size) {
const float ratio = f0 / (0.01f + f0);
const float f1a = 200.0f / kSampleRate * ratio;
const float f1b = 7530.0f / kSampleRate * ratio;
const float f2a = 510.0f / kSampleRate * ratio;
const float f2b = 8075.0f / kSampleRate * ratio;
const float f3a = 730.0f / kSampleRate * ratio;
const float f3b = 10500.0f / kSampleRate * ratio;
std::fill(&out[0], &out[size], 0.0f);
RenderPair(&oscillator_[0], f1a, f1b, temp_1, temp_2, out, size);
RenderPair(&oscillator_[2], f2a, f2b, temp_1, temp_2, out, size);
RenderPair(&oscillator_[4], f3a, f3b, temp_1, temp_2, out, size);
}

private:
void RenderPair(
Oscillator* osc,
float f1,
float f2,
float* temp_1,
float* temp_2,
float* out,
size_t size) {
osc[0].Render<OSCILLATOR_SHAPE_SQUARE>(f1, 0.5f, temp_1, size);
osc[1].Render<OSCILLATOR_SHAPE_SAW>(f2, 0.5f, temp_2, size);
while (size--) {
*out++ += *temp_1++ * *temp_2++;
}
}
Oscillator oscillator_[6];
DISALLOW_COPY_AND_ASSIGN(RingModNoise);
};

class SwingVCA {
public:
float operator()(float s, float gain) {
s *= s > 0.0f ? 10.0f : 0.1f;
s = s / (1.0f + fabsf(s));
return (s + 1.0f) * gain;
}
};

class LinearVCA {
public:
float operator()(float s, float gain) {
return s * gain;
}
};

template<typename MetallicNoiseSource, typename VCA, bool resonance>
class HiHat {
public:
HiHat() { }
~HiHat() { }

void Init() {
envelope_ = 0.0f;
noise_clock_ = 0.0f;
noise_sample_ = 0.0f;
sustain_gain_ = 0.0f;

metallic_noise_.Init();
noise_coloration_svf_.Init();
hpf_.Init();
}
void Render(
bool sustain,
bool trigger,
float accent,
float f0,
float tone,
float decay,
float noisiness,
float* temp_1,
float* temp_2,
float* out,
size_t size) {
const float envelope_decay = 1.0f - 0.003f * stmlib::SemitonesToRatio(
-decay * 84.0f);
const float cut_decay = 1.0f - 0.0025f * stmlib::SemitonesToRatio(
-decay * 36.0f);
if (trigger) {
envelope_ = (1.5f + 0.5f * (1.0f - decay)) * (0.3f + 0.7f * accent);
}

// Render the metallic noise.
metallic_noise_.Render(2.0f * f0, temp_1, temp_2, out, size);

// Apply BPF on the metallic noise.
float cutoff = 150.0f / kSampleRate * stmlib::SemitonesToRatio(
tone * 72.0f);
CONSTRAIN(cutoff, 0.0f, 16000.0f / kSampleRate);
noise_coloration_svf_.set_f_q<stmlib::FREQUENCY_ACCURATE>(
cutoff, resonance ? 3.0f + 6.0f * tone : 1.0f);
noise_coloration_svf_.Process<stmlib::FILTER_MODE_BAND_PASS>(
out, out, size);
// This is not at all part of the 808 circuit! But to add more variety, we
// add a variable amount of clocked noise to the output of the 6 schmitt
// trigger oscillators.
noisiness *= noisiness;
float noise_f = f0 * (16.0f + 16.0f * (1.0f - noisiness));
CONSTRAIN(noise_f, 0.0f, 0.5f);
for (size_t i = 0; i < size; ++i) {
noise_clock_ += noise_f;
if (noise_clock_ >= 1.0f) {
noise_clock_ -= 1.0f;
noise_sample_ = stmlib::Random::GetFloat() - 0.5f;
}
out[i] += noisiness * (noise_sample_ - out[i]);
}

// Apply VCA.
stmlib::ParameterInterpolator sustain_gain(
&sustain_gain_,
accent * decay,
size);
for (size_t i = 0; i < size; ++i) {
VCA vca;
envelope_ *= envelope_ > 0.5f ? envelope_decay : cut_decay;
out[i] = vca(out[i], sustain ? sustain_gain.Next() : envelope_);
}
hpf_.set_f_q<stmlib::FREQUENCY_ACCURATE>(cutoff, 0.5f);
hpf_.Process<stmlib::FILTER_MODE_HIGH_PASS>(out, out, size);
}

private:
float envelope_;
float noise_clock_;
float noise_sample_;
float sustain_gain_;

MetallicNoiseSource metallic_noise_;
stmlib::Svf noise_coloration_svf_;
stmlib::Svf hpf_;
DISALLOW_COPY_AND_ASSIGN(HiHat);
};
} // namespace plaits

#endif // PLAITS_DSP_DRUMS_HI_HAT_H_

+ 249
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/synthetic_bass_drum.h View File

@@ -0,0 +1,249 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Naive bass drum model (modulated oscillator with FM + envelope).
// Inadvertently 909-ish.

#ifndef PLAITS_DSP_DRUMS_SYNTHETIC_BASS_DRUM_H_
#define PLAITS_DSP_DRUMS_SYNTHETIC_BASS_DRUM_H_

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/units.h"
#include "stmlib/utils/random.h"

#include "plaits/dsp/dsp.h"
#include "plaits/resources.h"

namespace plaits {

class SyntheticBassDrumClick {
public:
SyntheticBassDrumClick() { }
~SyntheticBassDrumClick() { }
void Init() {
lp_ = 0.0f;
hp_ = 0.0f;
filter_.Init();
filter_.set_f_q<stmlib::FREQUENCY_FAST>(5000.0f / kSampleRate, 2.0f);
}
float Process(float in) {
SLOPE(lp_, in, 0.5f, 0.1f);
ONE_POLE(hp_, lp_, 0.04f);
return filter_.Process<stmlib::FILTER_MODE_LOW_PASS>(lp_ - hp_);
}
private:
float lp_;
float hp_;
stmlib::Svf filter_;
DISALLOW_COPY_AND_ASSIGN(SyntheticBassDrumClick);
};

class SyntheticBassDrumAttackNoise {
public:
SyntheticBassDrumAttackNoise() { }
~SyntheticBassDrumAttackNoise() { }
void Init() {
lp_ = 0.0f;
hp_ = 0.0f;
}
float Render() {
float sample = stmlib::Random::GetFloat();
ONE_POLE(lp_, sample, 0.05f);
ONE_POLE(hp_, lp_, 0.005f);
return lp_ - hp_;
}
private:
float lp_;
float hp_;
DISALLOW_COPY_AND_ASSIGN(SyntheticBassDrumAttackNoise);
};

class SyntheticBassDrum {
public:
SyntheticBassDrum() { }
~SyntheticBassDrum() { }

void Init() {
phase_ = 0.0f;
phase_noise_ = 0.0f;
f0_ = 0.0f;
fm_ = 0.0f;
fm_lp_ = 0.0f;
body_env_lp_ = 0.0f;
body_env_ = 0.0f;
body_env_pulse_width_ = 0;
fm_pulse_width_ = 0;
tone_lp_ = 0.0f;
sustain_gain_ = 0.0f;
click_.Init();
noise_.Init();
}
inline float DistortedSine(float phase, float phase_noise, float dirtiness) {
phase += phase_noise * dirtiness;
MAKE_INTEGRAL_FRACTIONAL(phase);
phase = phase_fractional;
float triangle = (phase < 0.5f ? phase : 1.0f - phase) * 4.0f - 1.0f;
float sine = 2.0f * triangle / (1.0f + fabsf(triangle));
float clean_sine = stmlib::InterpolateWrap(
lut_sine, phase + 0.75f, 1024.0f);
return sine + (1.0f - dirtiness) * (clean_sine - sine);
}
inline float TransistorVCA(float s, float gain) {
s = (s - 0.6f) * gain;
return 3.0f * s / (2.0f + fabsf(s)) + gain * 0.3f;
}
void Render(
bool sustain,
bool trigger,
float accent,
float f0,
float tone,
float decay,
float dirtiness,
float fm_envelope_amount,
float fm_envelope_decay,
float* out,
size_t size) {
decay *= decay;
fm_envelope_decay *= fm_envelope_decay;
stmlib::ParameterInterpolator f0_mod(&f0_, f0, size);
dirtiness *= std::max(1.0f - 8.0f * f0, 0.0f);
const float fm_decay = 1.0f - \
1.0f / (0.008f * (1.0f + fm_envelope_decay * 4.0f) * kSampleRate);

const float body_env_decay = 1.0f - 1.0f / (0.02f * kSampleRate) * \
stmlib::SemitonesToRatio(-decay * 60.0f);
const float transient_env_decay = 1.0f - 1.0f / (0.005f * kSampleRate);
const float tone_f = std::min(
4.0f * f0 * stmlib::SemitonesToRatio(tone * 108.0f),
1.0f);
const float transient_level = tone;
if (trigger) {
fm_ = 1.0f;
body_env_ = transient_env_ = 0.3f + 0.7f * accent;
body_env_pulse_width_ = kSampleRate * 0.001f;
fm_pulse_width_ = kSampleRate * 0.0013f;
}
stmlib::ParameterInterpolator sustain_gain(
&sustain_gain_,
accent * decay,
size);
while (size--) {
ONE_POLE(phase_noise_, stmlib::Random::GetFloat() - 0.5f, 0.002f);
float mix = 0.0f;

if (sustain) {
phase_ += f0_mod.Next();
if (phase_ >= 1.0f) {
phase_ -= 1.0f;
}
float body = DistortedSine(phase_, phase_noise_, dirtiness);
mix -= TransistorVCA(body, sustain_gain.Next());
} else {
if (fm_pulse_width_) {
--fm_pulse_width_;
phase_ = 0.25f;
} else {
fm_ *= fm_decay;
float fm = 1.0f + fm_envelope_amount * 3.5f * fm_lp_;
phase_ += std::min(f0_mod.Next() * fm, 0.5f);
if (phase_ >= 1.0f) {
phase_ -= 1.0f;
}
}
if (body_env_pulse_width_) {
--body_env_pulse_width_;
} else {
body_env_ *= body_env_decay;
transient_env_ *= transient_env_decay;
}
const float envelope_lp_f = 0.1f;
ONE_POLE(body_env_lp_, body_env_, envelope_lp_f);
ONE_POLE(transient_env_lp_, transient_env_, envelope_lp_f);
ONE_POLE(fm_lp_, fm_, envelope_lp_f);
float body = DistortedSine(phase_, phase_noise_, dirtiness);
float transient = click_.Process(
body_env_pulse_width_ ? 0.0f : 1.0f) + noise_.Render();
mix -= TransistorVCA(body, body_env_lp_);
mix -= transient * transient_env_lp_ * transient_level;
}

ONE_POLE(tone_lp_, mix, tone_f);
*out++ = tone_lp_;
}
}

private:
float f0_;
float phase_;
float phase_noise_;

float fm_;
float fm_lp_;
float body_env_;
float body_env_lp_;
float transient_env_;
float transient_env_lp_;
float sustain_gain_;
float tone_lp_;
SyntheticBassDrumClick click_;
SyntheticBassDrumAttackNoise noise_;
int body_env_pulse_width_;
int fm_pulse_width_;
DISALLOW_COPY_AND_ASSIGN(SyntheticBassDrum);
};
} // namespace plaits

#endif // PLAITS_DSP_DRUMS_SYNTHETIC_BASS_DRUM_H_

+ 198
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/drums/synthetic_snare_drum.h View File

@@ -0,0 +1,198 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Naive snare drum model (two modulated oscillators + filtered noise).
// Uses a few magic numbers taken from the 909 schematics:
// - Ratio between the two modes of the drum set to 1.47.
// - Funky coupling between the two modes.
// - Noise coloration filters and envelope shapes for the snare.

#ifndef PLAITS_DSP_DRUMS_SYNTHETIC_SNARE_DRUM_H_
#define PLAITS_DSP_DRUMS_SYNTHETIC_SNARE_DRUM_H_

#include <algorithm>

#include "stmlib/dsp/dsp.h"
#include "stmlib/dsp/parameter_interpolator.h"
#include "stmlib/dsp/units.h"

#include "plaits/dsp/dsp.h"

namespace plaits {

class SyntheticSnareDrum {
public:
SyntheticSnareDrum() { }
~SyntheticSnareDrum() { }

void Init() {
phase_[0] = 0.0f;
phase_[1] = 0.0f;
drum_amplitude_ = 0.0f;
snare_amplitude_ = 0.0f;
fm_ = 0.0f;
hold_counter_ = 0;
sustain_gain_ = 0.0f;

drum_lp_.Init();
snare_hp_.Init();
snare_lp_.Init();
}
inline float DistortedSine(float phase) {
float triangle = (phase < 0.5f ? phase : 1.0f - phase) * 4.0f - 1.3f;
return 2.0f * triangle / (1.0f + fabsf(triangle));
}
void Render(
bool sustain,
bool trigger,
float accent,
float f0,
float fm_amount,
float decay,
float snappy,
float* out,
size_t size) {
const float decay_xt = decay * (1.0f + decay * (decay - 1.0f));
fm_amount *= fm_amount;
const float drum_decay = 1.0f - 1.0f / (0.015f * kSampleRate) * \
stmlib::SemitonesToRatio(
-decay_xt * 72.0f - fm_amount * 12.0f + snappy * 7.0f);
const float snare_decay = 1.0f - 1.0f / (0.01f * kSampleRate) * \
stmlib::SemitonesToRatio(-decay * 60.0f - snappy * 7.0f);
const float fm_decay = 1.0f - 1.0f / (0.007f * kSampleRate);
snappy = snappy * 1.1f - 0.05f;
CONSTRAIN(snappy, 0.0f, 1.0f);
const float drum_level = stmlib::Sqrt(1.0f - snappy);
const float snare_level = stmlib::Sqrt(snappy);
const float snare_f_min = std::min(10.0f * f0, 0.5f);
const float snare_f_max = std::min(35.0f * f0, 0.5f);

snare_hp_.set_f<stmlib::FREQUENCY_FAST>(snare_f_min);
snare_lp_.set_f_q<stmlib::FREQUENCY_FAST>(snare_f_max,
0.5f + 2.0f * snappy);
drum_lp_.set_f<stmlib::FREQUENCY_FAST>(3.0f * f0);
if (trigger) {
snare_amplitude_ = drum_amplitude_ = 0.3f + 0.7f * accent;
fm_ = 1.0f;
phase_[0] = phase_[1] = 0.0f;
hold_counter_ = static_cast<int>((0.04f + decay * 0.03f) * kSampleRate);
}
stmlib::ParameterInterpolator sustain_gain(
&sustain_gain_,
accent * decay,
size);
while (size--) {
if (sustain) {
snare_amplitude_ = sustain_gain.Next();
drum_amplitude_ = snare_amplitude_;
fm_ = 0.0f;
} else {
// Compute all D envelopes.
// The envelope for the drum has a very long tail.
// The envelope for the snare has a "hold" stage which lasts between
// 40 and 70 ms
drum_amplitude_ *= (drum_amplitude_ > 0.03f || !(size & 1))
? drum_decay
: 1.0f;
if (hold_counter_) {
--hold_counter_;
} else {
snare_amplitude_ *= snare_decay;
}
fm_ *= fm_decay;
}

// The 909 circuit has a funny kind of oscillator coupling - the signal
// leaving Q40's collector and resetting all oscillators allow some
// intermodulation.
float reset_noise = 0.0f;
float reset_noise_amount = (0.125f - f0) * 8.0f;
CONSTRAIN(reset_noise_amount, 0.0f, 1.0f);
reset_noise_amount *= reset_noise_amount;
reset_noise_amount *= fm_amount;
reset_noise += phase_[0] > 0.5f ? -1.0f : 1.0f;
reset_noise += phase_[1] > 0.5f ? -1.0f : 1.0f;
reset_noise *= reset_noise_amount * 0.025f;

float f = f0 * (1.0f + fm_amount * (4.0f * fm_));
phase_[0] += f;
phase_[1] += f * 1.47f;
if (reset_noise_amount > 0.1f) {
if (phase_[0] >= 1.0f + reset_noise) {
phase_[0] = 1.0f - phase_[0];
}
if (phase_[1] >= 1.0f + reset_noise) {
phase_[1] = 1.0f - phase_[1];
}
} else {
if (phase_[0] >= 1.0f) {
phase_[0] -= 1.0f;
}
if (phase_[1] >= 1.0f) {
phase_[1] -= 1.0f;
}
}
float drum = -0.1f;
drum += DistortedSine(phase_[0]) * 0.60f;
drum += DistortedSine(phase_[1]) * 0.25f;
drum *= drum_amplitude_ * drum_level;
drum = drum_lp_.Process<stmlib::FILTER_MODE_LOW_PASS>(drum);
float noise = stmlib::Random::GetFloat();
float snare = snare_lp_.Process<stmlib::FILTER_MODE_LOW_PASS>(noise);
snare = snare_hp_.Process<stmlib::FILTER_MODE_HIGH_PASS>(snare);
snare = (snare + 0.1f) * (snare_amplitude_ + fm_) * snare_level;
*out++ = snare + drum; // It's a snare, it's a drum, it's a snare drum.
}
}

private:
float phase_[2];
float drum_amplitude_;
float snare_amplitude_;
float fm_;
float sustain_gain_;
int hold_counter_;
stmlib::OnePole drum_lp_;
stmlib::OnePole snare_hp_;
stmlib::Svf snare_lp_;
DISALLOW_COPY_AND_ASSIGN(SyntheticSnareDrum);
};
} // namespace plaits

#endif // PLAITS_DSP_DRUMS_SYNTHETIC_SNARE_DRUM_H_

+ 55
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/dsp.h View File

@@ -0,0 +1,55 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Utility DSP routines.

#ifndef PLAITS_DSP_DSP_H_
#define PLAITS_DSP_DSP_H_

#include "stmlib/stmlib.h"

namespace plaits {
static const float kSampleRate = 48000.0f;

// There is no proper PLL for I2S, only a divider on the system clock to derive
// the bit clock.
// The division ratio is set to 47 (23 EVEN, 1 ODD) by the ST libraries.
//
// Bit clock = 72000000 / 47 = 1531.91 kHz
// Frame clock = Bit clock / 32 = 47872.34 Hz
//
// That's only 4.6 cts of error, but we care!

static const float kCorrectedSampleRate = 47872.34f;
const float a0 = (440.0f / 8.0f) / kCorrectedSampleRate;

const size_t kMaxBlockSize = 24;
const size_t kBlockSize = 12;

} // namespace plaits

#endif // PLAITS_DSP_DSP_H_

+ 152
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/engine/additive_engine.cc View File

@@ -0,0 +1,152 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Additive synthesis with 32 partials.

#include "plaits/dsp/engine/additive_engine.h"

#include <algorithm>

#include "stmlib/dsp/cosine_oscillator.h"

#include "plaits/resources.h"

namespace plaits {

using namespace std;
using namespace stmlib;

void AdditiveEngine::Init(BufferAllocator* allocator) {
fill(
&amplitudes_[0],
&amplitudes_[kNumHarmonics],
0.0f);
for (int i = 0; i < kNumHarmonicOscillators; ++i) {
harmonic_oscillator_[i].Init();
}
}

void AdditiveEngine::Reset() {

}

void AdditiveEngine::UpdateAmplitudes(
float centroid,
float slope,
float bumps,
float* amplitudes,
const int* harmonic_indices,
size_t num_harmonics) {
const float n = (static_cast<float>(num_harmonics) - 1.0f);
const float margin = (1.0f / slope - 1.0f) / (1.0f + bumps);
const float center = centroid * (n + margin) - 0.5f * margin;

float sum = 0.001f;

for (size_t i = 0; i < num_harmonics; ++i) {
float order = fabsf(static_cast<float>(i) - center) * slope;
float gain = 1.0f - order;
gain += fabsf(gain);
gain *= gain;

float b = 0.25f + order * bumps;
float bump_factor = 1.0f + InterpolateWrap(lut_sine, b, 1024.0f);

gain *= bump_factor;
gain *= gain;
gain *= gain;
int j = harmonic_indices[i];
// Warning about the following line: this is not a proper LP filter because
// of the normalization. But in spite of its strange working, this line
// turns out ot be absolutely essential.
//
// I have tried both normalizing the LP-ed spectrum, and LP-ing the
// normalized spectrum, and both of them cause more annoyances than this
// "incorrect" solution.
ONE_POLE(amplitudes[j], gain, 0.001f);
sum += amplitudes[j];
}

sum = 1.0f / sum;

for (size_t i = 0; i < num_harmonics; ++i) {
amplitudes[harmonic_indices[i]] *= sum;
}
}

inline float Bump(float x, float centroid, float slope) {
float d = fabsf(x - centroid);
float bump = 1.0f - d * slope;
return bump + fabsf(bump);
}

const int integer_harmonics[24] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23
};

const int organ_harmonics[8] = {
0, 1, 2, 3, 5, 7, 9, 11
};

void AdditiveEngine::Render(
const EngineParameters& parameters,
float* out,
float* aux,
size_t size,
bool* already_enveloped) {
const float f0 = NoteToFrequency(parameters.note);

const float centroid = parameters.timbre;
const float raw_bumps = parameters.harmonics;
const float raw_slope = (1.0f - 0.6f * raw_bumps) * parameters.morph;
const float slope = 0.01f + 1.99f * raw_slope * raw_slope * raw_slope;
const float bumps = 16.0f * raw_bumps * raw_bumps;
UpdateAmplitudes(
centroid,
slope,
bumps,
&amplitudes_[0],
integer_harmonics,
24);
harmonic_oscillator_[0].Render<1>(f0, &amplitudes_[0], out, size);
harmonic_oscillator_[1].Render<13>(f0, &amplitudes_[12], out, size);

UpdateAmplitudes(
centroid,
slope,
bumps,
&amplitudes_[24],
organ_harmonics,
8);

harmonic_oscillator_[2].Render<1>(f0, &amplitudes_[24], aux, size);
}

} // namespace plaits

+ 72
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/engine/additive_engine.h View File

@@ -0,0 +1,72 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// Additive synthesis with 24+8 partials.

#ifndef PLAITS_DSP_ENGINE_ADDITIVE_ENGINE_H_
#define PLAITS_DSP_ENGINE_ADDITIVE_ENGINE_H_

#include "plaits/dsp/engine/engine.h"
#include "plaits/dsp/oscillator/harmonic_oscillator.h"

namespace plaits {
const int kHarmonicBatchSize = 12;
const int kNumHarmonics = 36;
const int kNumHarmonicOscillators = kNumHarmonics / kHarmonicBatchSize;

class AdditiveEngine : public Engine {
public:
AdditiveEngine() { }
~AdditiveEngine() { }
virtual void Init(stmlib::BufferAllocator* allocator);
virtual void Reset();
virtual void Render(const EngineParameters& parameters,
float* out,
float* aux,
size_t size,
bool* already_enveloped);
private:
void UpdateAmplitudes(
float centroid,
float slope,
float bumps,
float* amplitudes,
const int* harmonic_indices,
size_t num_harmonics);
HarmonicOscillator<kHarmonicBatchSize> harmonic_oscillator_[kNumHarmonicOscillators];
float amplitudes_[kNumHarmonics];
DISALLOW_COPY_AND_ASSIGN(AdditiveEngine);
};

} // namespace plaits

#endif // PLAITS_DSP_ENGINE_ADDITIVE_ENGINE_H_

+ 96
- 0
plugins/community/repos/AudibleInstruments/eurorack/plaits/dsp/engine/bass_drum_engine.cc View File

@@ -0,0 +1,96 @@
// Copyright 2016 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.
//
// -----------------------------------------------------------------------------
//
// 808 and synthetic bass drum generators.

#include "plaits/dsp/engine/bass_drum_engine.h"

#include <algorithm>

namespace plaits {

using namespace std;
using namespace stmlib;

void BassDrumEngine::Init(BufferAllocator* allocator) {
analog_bass_drum_.Init();
synthetic_bass_drum_.Init();
overdrive_.Init();
}

void BassDrumEngine::Reset() {
}

void BassDrumEngine::Render(
const EngineParameters& parameters,
float* out,
float* aux,
size_t size,
bool* already_enveloped) {
const float f0 = NoteToFrequency(parameters.note);
const float attack_fm_amount = min(parameters.harmonics * 4.0f, 1.0f);
const float self_fm_amount = max(min(parameters.harmonics * 4.0f - 1.0f, 1.0f), 0.0f);
const float drive = max(parameters.harmonics * 2.0f - 1.0f, 0.0f) * \
max(1.0f - 16.0f * f0, 0.0f);
const bool sustain = parameters.trigger & TRIGGER_UNPATCHED;
analog_bass_drum_.Render(
sustain,
parameters.trigger & TRIGGER_RISING_EDGE,
parameters.accent,
f0,
parameters.timbre,
parameters.morph,
attack_fm_amount,
self_fm_amount,
out,
size);

overdrive_.Process(
0.5f + 0.5f * drive,
out,
size);

synthetic_bass_drum_.Render(
sustain,
parameters.trigger & TRIGGER_RISING_EDGE,
parameters.accent,
f0,
parameters.timbre,
parameters.morph,
sustain
? parameters.harmonics
: 0.4f - 0.25f * parameters.morph * parameters.morph,
min(parameters.harmonics * 2.0f, 1.0f),
max(parameters.harmonics * 2.0f - 1.0f, 0.0f),
aux,
size);
}

} // namespace plaits

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save