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