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