1 /*
2     ampmidid.cpp
3 
4     Copyright (C) 2006 Michael Gogins
5 
6     This file is part of Csound.
7 
8     The Csound Library is free software; you can redistribute it
9     and/or modify it under the terms of the GNU Lesser General Public
10     License as published by the Free Software Foundation; either
11     version 2.1 of the License, or (at your option) any later version.
12 
13     Csound is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU Lesser General Public License for more details.
17 
18     You should have received a copy of the GNU Lesser General Public
19     License along with Csound; if not, write to the Free Software
20     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21     02110-1301 USA
22  */
23 #include "OpcodeBase.hpp"
24 
25 using namespace csound;
26 
27 #include <cmath>
28 
29 /**
30  * Musically map MIDI velocity to peak amplitude
31  * within a specified dynamic range in decibels:
32  * a = (m * v + b) ^ 2
33  * where a = amplitude,
34  * v = MIDI velocity,
35  * r = 10 ^ (R / 20),
36  * b = 127 / (126 * sqrt( r )) - 1 / 126,
37  * m = (1 - b) / 127,
38  * and R = specified dynamic range in decibels.
39  * See Roger Dannenberg, "The Interpretation of MIDI Velocity,"
40  * in Georg Essl and Ichiro Fujinaga (Eds.), Proceedings of the
41  * 2006 International Computer Music Conference,
42  * November 6-11, 2006 (San Francisco:
43  * The International Computer Music Association), pp. 193-196.
44  */
45 class KAMPMIDID : public OpcodeBase<KAMPMIDID> {
46 public:
47     // Outputs.
48     MYFLT *kamplitude;
49     // Inputs.
50     MYFLT *kvelocity;
51     MYFLT *irdb;
52     MYFLT *iuse0dbfs;
53     // State.
54     MYFLT ir;
55     MYFLT im;
56     MYFLT ib;
57     MYFLT onedrms;
58     MYFLT dbfs;
KAMPMIDID()59     KAMPMIDID()
60         : kamplitude(0), kvelocity(0), irdb(0), iuse0dbfs(0), ir(0), im(0), ib(0),
61           onedrms(0), dbfs(1) {}
init(CSOUND * csound)62     int init(CSOUND *csound) {
63         // Convert RMS power to amplitude (assuming a sinusoidal signal).
64         onedrms = MYFLT(1.0) / MYFLT(0.707);
65         // Convert dynamic range in decibels to RMS dynamic range.
66         ir = std::pow(MYFLT(10.0), *irdb / MYFLT(20.0));
67         // Solve for coefficients of the linear conversion function given
68         // RMS dynamic range.
69         ib = MYFLT(127.0) / (MYFLT(126.0) * std::sqrt(ir)) -
70              MYFLT(1.0) / MYFLT(126.0);
71         im = (MYFLT(1.0) - ib) / MYFLT(127.0);
72         if (*iuse0dbfs == FL(0.0)) {
73             dbfs = csound->Get0dBFS(csound);
74         } else {
75             dbfs = *iuse0dbfs;
76         }
77         return OK;
78     }
kontrol(CSOUND * csound)79     int kontrol(CSOUND *csound) {
80         IGN(csound);
81         *kamplitude =
82             dbfs * std::pow((*kvelocity * im) + ib, MYFLT(2.0)) * onedrms;
83         return OK;
84     }
85 };
86 
87 class IAMPMIDID : public OpcodeBase<IAMPMIDID> {
88 public:
89     // Outputs.
90     MYFLT *iamplitude;
91     // Inputs.
92     MYFLT *ivelocity;
93     MYFLT *irdb;
94     MYFLT *iuse0dbfs;
95     // State.
96     MYFLT ir;
97     MYFLT im;
98     MYFLT ib;
99     MYFLT onedrms;
100     MYFLT dbfs;
IAMPMIDID()101     IAMPMIDID()
102         : iamplitude(0), ivelocity(0), irdb(0), iuse0dbfs(0), ir(0), im(0), ib(0),
103           onedrms(0), dbfs(1) {}
init(CSOUND * csound)104     int init(CSOUND *csound) {
105         // Convert RMS power to amplitude (assuming a sinusoidal signal).
106         onedrms = MYFLT(1.0) / MYFLT(0.707);
107         // Convert dynamic range in decibels to RMS dynamic range.
108         ir = std::pow(MYFLT(10.0), *irdb / MYFLT(20.0));
109         // Solve for coefficients of the linear conversion function given
110         // RMS dynamic range.
111         ib = MYFLT(127.0) / (MYFLT(126.0) * std::sqrt(ir)) -
112              MYFLT(1.0) / MYFLT(126.0);
113         im = (MYFLT(1.0) - ib) / MYFLT(127.0);
114         if (*iuse0dbfs == FL(0.0)) {
115             dbfs = csound->Get0dBFS(csound);
116         } else {
117             dbfs = *iuse0dbfs;
118         }
119         *iamplitude =
120             dbfs * std::pow((*ivelocity * im) + ib, MYFLT(2.0)) * onedrms;
121         return OK;
122     }
noteoff(CSOUND *)123     int noteoff(CSOUND *) {
124         return OK;
125     }
126 };
127 
128 /**
129  * Maps an input MIDI velocity number to an output gain factor with a maximum
130  * value of 1, modifying the output gain by a dynamic range and a shaping
131  * exponent. The minimum output gain is 1 minus the dynamic
132  * range. A shaping exponent of 1 is a linear response; increasing the
133  * exponent produces an increasingly depressed knee in the gain response
134  * curve. This opcode was suggested by Mauro Giubileo, and its behavior
135  * can be seen at https://www.desmos.com/calculator/fvxupgp4ef.
136  */
137 class AMPMIDICURVE : public OpcodeBase<AMPMIDICURVE> {
138 public:
139     MYFLT *k_gain;
140     MYFLT *k_midi_velocity;
141     MYFLT *k_dynamic_range;
142     MYFLT *k_exponent;
init(CSOUND * csound)143     int init(CSOUND *csound) {
144         *k_gain = *k_dynamic_range * std::pow(*k_midi_velocity / FL(127.), *k_exponent) + FL(1.) - *k_dynamic_range;
145         return OK;
146     }
kontrol(CSOUND * csound)147     int kontrol(CSOUND *csound) {
148         *k_gain = *k_dynamic_range * std::pow(*k_midi_velocity / FL(127.), *k_exponent) + FL(1.) - *k_dynamic_range;
149         return OK;
150     }
151 };
152 
153 extern "C" {
csoundModuleInit_ampmidid(CSOUND * csound)154     PUBLIC int csoundModuleInit_ampmidid(CSOUND *csound) {
155         int status = csound->AppendOpcode(
156                          csound, (char *)"ampmidid.k", sizeof(KAMPMIDID), 0, 3, (char *)"k",
157                          (char *)"kio",
158                          (int (*)(CSOUND *, void *))KAMPMIDID::init_,
159                          (int (*)(CSOUND *, void *))KAMPMIDID::kontrol_,
160                          (int (*)(CSOUND *, void *))0);
161         status |= csound->AppendOpcode(
162                       csound, (char *)"ampmidid.i", sizeof(IAMPMIDID), 0, 1, (char *)"i",
163                       (char *)"iio",
164                       (int (*)(CSOUND *, void *))IAMPMIDID::init_,
165                       (int (*)(CSOUND *, void *))0,
166                       (int (*)(CSOUND *, void *))0);
167         status |= csound->AppendOpcode(csound, (char *)"ampmidid", 0xffff, 0, 0, 0, 0,
168                                        0, 0, 0);
169         status = csound->AppendOpcode(
170                      csound, (char *)"ampmidicurve.k", sizeof(AMPMIDICURVE), 0, 3, (char *)"k",
171                      (char *)"kkk",
172                      (int (*)(CSOUND *, void *))AMPMIDICURVE::init_,
173                      (int (*)(CSOUND *, void *))AMPMIDICURVE::kontrol_,
174                      (int (*)(CSOUND *, void *))0);
175         status |= csound->AppendOpcode(
176                       csound, (char *)"ampmidicurve.i", sizeof(AMPMIDICURVE), 0, 1, (char *)"i",
177                       (char *)"iii",
178                       (int (*)(CSOUND *, void *))AMPMIDICURVE::init_,
179                       (int (*)(CSOUND *, void *))0,
180                       (int (*)(CSOUND *, void *))0);
181         status |= csound->AppendOpcode(csound, (char *)"ampmidicurve", 0xffff, 0, 0, 0, 0,
182                                        0, 0, 0);
183         return status;
184     }
185 
186 #ifndef INIT_STATIC_MODULES
csoundModuleCreate(CSOUND * csound)187     PUBLIC int csoundModuleCreate(CSOUND *csound) {
188         IGN(csound);
189         return 0;
190     }
191 
csoundModuleInit(CSOUND * csound)192     PUBLIC int csoundModuleInit(CSOUND *csound) {
193         return csoundModuleInit_ampmidid(csound);
194     }
195 
csoundModuleDestroy(CSOUND * csound)196     PUBLIC int csoundModuleDestroy(CSOUND *csound) {
197         IGN(csound);
198         return 0;
199     }
200 #endif
201 }
202