1 // Copyright 2015 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.gillet@gmail.com)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files (the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 //
23 // See http://creativecommons.org/licenses/MIT/ for more information.
24 //
25 // -----------------------------------------------------------------------------
26 //
27 // Random generation channel.
28
29 #include "marbles/random/output_channel.h"
30
31 #include "marbles/random/distributions.h"
32 #include "marbles/random/random_sequence.h"
33
34 #include "stmlib/dsp/dsp.h"
35 #include "stmlib/dsp/parameter_interpolator.h"
36 #include "stmlib/utils/random.h"
37
38 namespace marbles {
39
40 using namespace stmlib;
41
42 const size_t kNumReacquisitions = 20; // 6.4 samples per millisecond
43
Init()44 void OutputChannel::Init() {
45 spread_ = 0.5f;
46 bias_ = 0.5f;
47 steps_ = 0.5f;
48 scale_index_ = 0;
49
50 register_mode_ = false;
51 register_value_ = 0.0f;
52 register_transposition_ = 0.0f;
53
54 previous_steps_ = 0.0f;
55 previous_phase_ = 0.0f;
56 reacquisition_counter_ = 0;
57
58 previous_voltage_ = 0.0f;
59 voltage_ = 0.0f;
60 quantized_voltage_ = 0.0f;
61
62 scale_offset_ = ScaleOffset(10.0f, -5.0f);
63
64 lag_processor_.Init();
65
66 Scale scale;
67 scale.Init();
68 for (int i = 0; i < 6; ++i) {
69 quantizer_[i].Init(scale);
70 }
71 }
72
GenerateNewVoltage(RandomSequence * random_sequence)73 float OutputChannel::GenerateNewVoltage(RandomSequence* random_sequence) {
74 float u = random_sequence->NextValue(register_mode_, register_value_);
75
76 if (register_mode_) {
77 return 10.0f * (u - 0.5f) + register_transposition_;
78 } else {
79 float degenerate_amount = 1.25f - spread_ * 25.0f;
80 float bernoulli_amount = spread_ * 25.0f - 23.75f;
81
82 CONSTRAIN(degenerate_amount, 0.0f, 1.0f);
83 CONSTRAIN(bernoulli_amount, 0.0f, 1.0f);
84
85 float value = BetaDistributionSample(u, spread_, bias_);
86 float bernoulli_value = u >= (1.0f - bias_) ? 0.999999f : 0.0f;
87
88 value += degenerate_amount * (bias_ - value);
89 value += bernoulli_amount * (bernoulli_value - value);
90 return scale_offset_(value);
91 }
92 }
93
Process(RandomSequence * random_sequence,const float * phase,float * output,size_t size,size_t stride)94 void OutputChannel::Process(
95 RandomSequence* random_sequence,
96 const float* phase,
97 float* output,
98 size_t size,
99 size_t stride) {
100
101 ParameterInterpolator steps_modulation(
102 &previous_steps_, steps_, size);
103
104 // This is a horrible hack that wouldn't be here if all the sequencers
105 // and MIDI/CV interfaces in this world didn't have *horrible* slew on
106 // their CV output (I'm looking at you KORG).
107 // Without this hack, the shift register gets its value as soon as the
108 // rising edge is observed on the GATE input. Problem: the CV input is
109 // probably still slewing up, so we acquire the wrong value in the shift
110 // register. What to do then? Over the next 2ms, we'll just track the CV
111 // input until it reaches its final value - which means that Marbles
112 // output will be slewed too. Another option would have been to wait 2ms
113 // between the rising edge and the actual acquisition, but we don't want
114 // to penalize people who use tighter sequencers.
115 if (reacquisition_counter_) {
116 --reacquisition_counter_;
117 float u = random_sequence->RewriteValue(register_value_);
118 voltage_ = 10.0f * (u - 0.5f) + register_transposition_;
119 quantized_voltage_ = Quantize(voltage_, 2.0f * steps_ - 1.0f);
120 }
121
122 while (size--) {
123 const float steps = steps_modulation.Next();
124 if (*phase < previous_phase_) {
125 previous_voltage_ = voltage_;
126 voltage_ = GenerateNewVoltage(random_sequence);
127 lag_processor_.ResetRamp();
128 quantized_voltage_ = Quantize(voltage_, 2.0f * steps - 1.0f);
129 if (register_mode_) {
130 reacquisition_counter_ = kNumReacquisitions;
131 }
132 }
133
134 if (steps >= 0.5f) {
135 *output = quantized_voltage_;
136 } else {
137 const float smoothness = 1.0f - 2.0f * steps;
138 *output = lag_processor_.Process(voltage_, smoothness, *phase);
139 }
140 output += stride;
141 previous_phase_ = *phase++;
142 }
143 }
144
145 } // namespace marbles