123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- /*
- Amp.cc
-
- Copyright 2003-7
- Tim Goetze <tim@quitte.de>
- David Yeh <dtyeh@ccrma.stanford.edu> (Tone Stack in TS models)
-
- http://quitte.de/dsp/
- Tube amplifier models
- */
- /*
- 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 2
- 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, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- 02111-1307, USA or point your web browser to http://www.gnu.org.
- */
- #include "basics.h"
- #include <stdio.h>
- #include "Amp.h"
- #include "Descriptor.h"
- void
- AmpStub::init (bool adjust_downsampler)
- {
- dc_blocker.set_f (10. / fs);
- /* going a bit lower than nominal with fc */
- double f = .7 * M_PI / OVERSAMPLE;
-
- /* construct the upsampler filter kernel */
- DSP::sinc (f, up.c, FIR_SIZE);
- DSP::kaiser<DSP::apply_window> (up.c, FIR_SIZE, 6.4);
- /* copy upsampler filter kernel for downsampler, make sum */
- double s = 0;
- for (int i = 0; i < up.n; ++i)
- down.c[i] = up.c[i],
- s += up.c[i];
-
- s = 1 / s;
- /* scale downsampler kernel for unity gain + correction for transfer */
- double t = adjust_downsampler ?
- s / max (fabs (tube.clip[0].value), fabs (tube.clip[1].value)) : s;
- for (int i = 0; i < down.n; ++i)
- down.c[i] *= t;
- /* scale upsampler kernel for unity gain */
- s *= OVERSAMPLE;
- for (int i = 0; i < up.n; ++i)
- up.c[i] *= s;
- }
- /* //////////////////////////////////////////////////////////////////////// */
- void
- AmpIII::init()
- {
- this->AmpStub::init (false);
-
- /* need to filter out dc before the power amp stage, which is running at
- * the oversampled rate */
- dc_blocker.set_f (10. / (fs * OVERSAMPLE));
-
- DSP::RBJ::LoShelve (200 / fs, .2, -3, filter.a, filter.b);
- }
- template <sample_func_t F, int OVERSAMPLE>
- void
- AmpIII::one_cycle (int frames)
- {
- sample_t * s = ports[0];
-
- sample_t gain = getport(1);
- sample_t temp = getport(2) * tube.scale;
-
- drive = getport(3) * .5;
- i_drive = 1 / (1 - drive);
-
- sample_t * d = ports[4];
-
- *ports[5] = OVERSAMPLE;
- double g = current.g;
- current.g = max (gain < 1 ? gain : exp2 (gain - 1), .000001);
- current.g *= tube.scale / fabs (tube.transfer (temp));
- /* recursive fade to prevent zipper noise from the 'gain' knob */
- if (g == 0) g = current.g;
-
- double one_over_n = frames > 0 ? 1. / frames : 1;
- double gf = pow (current.g / g, one_over_n);
- for (int i = 0; i < frames; ++i)
- {
- register sample_t a = s[i];
- a = g * tube.transfer (a * temp);
- a = filter.process (a + normal);
- a = tube.transfer_clip (up.upsample (a));
- a = power_transfer (dc_blocker.process (a));
-
- a = down.process (a);
- for (int o = 1; o < OVERSAMPLE; ++o)
- down.store (
- power_transfer (
- dc_blocker.process (
- normal + tube.transfer_clip (up.pad (o)))));
- F (d, i, a, adding_gain);
- g *= gf;
- }
- current.g = g;
- }
- /* //////////////////////////////////////////////////////////////////////// */
- PortInfo
- AmpIII::port_info [] =
- {
- {
- "in",
- INPUT | AUDIO,
- {BOUNDED, -1, 1}
- }, {
- "gain",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_1, 0, 10}
- }, {
- "temperature",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MID, 0.005, 1}
- }, {
- "drive",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MAX, 0.0001, 1} /* ^2 gives the nice drive */
- }, {
- "out",
- OUTPUT | AUDIO,
- {0}
- }, {
- "latency",
- OUTPUT | CONTROL,
- {0}
- }
- };
- template <> void
- Descriptor<AmpIII>::setup()
- {
- UniqueID = 1786;
- Label = "AmpIII";
- Properties = HARD_RT;
- Name = CAPS "AmpIII - Tube amp";
- Maker = "Tim Goetze <tim@quitte.de>";
- Copyright = "GPL, 2002-7";
- /* fill port info and vtable */
- autogen();
- }
- /* //////////////////////////////////////////////////////////////////////// */
- void
- AmpIV::init()
- {
- this->AmpStub::init (false);
-
- /* need to filter out dc before the power amp stage, which is running at
- * the oversampled rate */
- dc_blocker.set_f (10. / (fs * OVERSAMPLE));
-
- tone.init (fs);
- }
- template <sample_func_t F, int OVERSAMPLE>
- void
- AmpIV::one_cycle (int frames)
- {
- double one_over_n = frames > 0 ? 1. / frames : 1;
- sample_t * s = ports[0];
-
- sample_t gain = getport(1);
- sample_t temp = getport(2) * tube.scale;
- tone.start_cycle (ports + 3, one_over_n);
-
- drive = getport(7) * .5;
- i_drive = 1 / (1 - drive);
-
- sample_t * d = ports[8];
-
- *ports[9] = OVERSAMPLE;
- double g = current.g;
- current.g = max (gain < 1 ? gain : exp2 (gain - 1), .000001);
- current.g *= tube.scale / fabs (tube.transfer (temp));
- /* recursive fade to prevent zipper noise from the 'gain' knob */
- if (g == 0) g = current.g;
-
- double gf = pow (current.g / g, one_over_n);
- for (int i = 0; i < frames; ++i)
- {
- register sample_t a = s[i] + normal;
- a = g * tube.transfer (a * temp);
- a = tone.process (a);
- a = tube.transfer_clip (up.upsample (a));
- a = power_transfer (dc_blocker.process (a));
-
- a = down.process (a);
- for (int o = 1; o < OVERSAMPLE; ++o)
- down.store (
- power_transfer (
- dc_blocker.process (
- normal + tube.transfer_clip (up.pad (o)))));
- F (d, i, a, adding_gain);
- g *= gf;
- }
- current.g = g;
- }
- /* //////////////////////////////////////////////////////////////////////// */
- PortInfo
- AmpIV::port_info [] =
- {
- {
- "in",
- INPUT | AUDIO,
- {BOUNDED, -1, 1}
- }, {
- "gain",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_1, 0, 10}
- }, {
- "temperature",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MID, 0.005, 1}
- }, {
- "bass",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_0, -20, 20}
- }, {
- "mid",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_0, -20, 20}
- }, {
- "treble",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_0, -20, 20}
- }, {
- "hi",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_0, -20, 20}
- }, {
- "drive",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MAX, 0.0001, 1} /* ^2 gives the nice drive */
- }, {
- "out",
- OUTPUT | AUDIO,
- {0}
- }, {
- "latency",
- OUTPUT | CONTROL,
- {0}
- }
- };
- template <> void
- Descriptor<AmpIV>::setup()
- {
- UniqueID = 1794;
- Label = "AmpIV";
- Properties = HARD_RT;
- Name = CAPS "AmpIV - Tube amp + tone controls";
- Maker = "Tim Goetze <tim@quitte.de>";
- Copyright = "GPL, 2002-7";
- /* fill port info and vtable */
- autogen();
- }
- /* //////////////////////////////////////////////////////////////////////// */
- void
- AmpV::init()
- {
- this->AmpStub::init (false);
-
- /* need to filter out dc before the power amp stage, which is running at
- * the oversampled rate */
- dc_blocker.set_f (10. / (fs * OVERSAMPLE));
-
- DSP::RBJ::LoShelve (210. / fs, .2, -1, filter[0].a, filter[0].b);
- DSP::RBJ::LoShelve (4200. / fs, 1.2, +6, filter[1].a, filter[1].b);
- DSP::RBJ::LoShelve (420. / fs, .2, +2, filter[2].a, filter[2].b);
- /* power supply cap */
- for (int i = 0; i < 2; ++i)
- DSP::RBJ::LP (10. / fs, .3, power_cap[i].a, power_cap[i].b);
- }
- template <sample_func_t F, int OVERSAMPLE>
- void
- AmpV::one_cycle (int frames)
- {
- sample_t * s = ports[0];
-
- sample_t gain = getport(1);
- if (*ports[2] != cut)
- {
- cut = getport(2);
- DSP::RBJ::LoShelve (210. / fs, .2, cut, filter[0].a, filter[0].b);
- }
- if (*ports[3] != tone)
- {
- tone = getport(3);
- double f = tone * tone * 8400 + 420;
- double q = tone * .4 + .2;
- double db = tone * 2 + 2;
- DSP::RBJ::LoShelve (f / fs, q, db, filter[2].a, filter[2].b);
- }
- drive = getport(4) * .5;
- i_drive = 1 / (1 - drive);
-
- #define MAX_WATTS port_info[5].range.UpperBound
- sample_t sag = (MAX_WATTS - getport(5)) / MAX_WATTS;
- sag = .6 * sag * sag;
-
- sample_t * d = ports[6];
-
- *ports[7] = OVERSAMPLE;
- double g = current.g;
- current.g = max (gain < 1 ? gain : pow (20, gain - 1), .000001);
- #if 0
- if ( (++_turn & 127) == 0)
- fprintf (stderr, "supply = %.3f sag = %.3f\n", supply, sag);
- #endif
- if (g == 0) g = current.g;
- /* recursive fade to prevent zipper noise from the 'gain' knob */
- double one_over_n = frames > 0 ? 1. / frames : 1;
- double gf = pow (current.g / g, one_over_n);
- for (int i = 0; i < frames; ++i)
- {
- register sample_t a = s[i];
- register sample_t v = 3 - supply;
- /* alternative curve: v = v * v * .1 + .1; */
- v = v * v * .06 + .46;
- a = filter[0].process (a + normal);
- if (0)
- a = filter[2].process (a);
-
- a = g * (a + supply * .001);
- a = v * tube.transfer_clip (up.upsample (a));
- a = power_transfer (dc_blocker.process (a));
-
- a = down.process (a);
- a = filter[1].process (a - normal);
- if (1)
- a = filter[2].process (a + normal);
- {
- for (int o = 1; o < OVERSAMPLE; ++o)
- down.store (
- power_transfer (
- dc_blocker.process (
- normal + tube.transfer_clip (
- up.pad (o)))));
- }
- F (d, i, a, adding_gain);
-
- /* integrate for an approximation of cumulative output power */
- supply += sag * fabs (a) + normal;
- /* filter integrated power consumption */
- for (int j = 0; j < 2; ++j)
- supply = 0.9 * (power_cap[j].process (supply));
- g *= gf;
- normal = -normal;
- }
- current.g = g;
- }
- /* //////////////////////////////////////////////////////////////////////// */
- PortInfo
- AmpV::port_info [] =
- {
- {
- "in",
- INPUT | AUDIO,
- {BOUNDED, -1, 1}
- }, {
- "gain",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_1, 0, 3}
- }, {
- "bass",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_0, -9, 9}
- }, {
- "tone",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MIN, 0, 1}
- }, {
- "drive",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_HIGH, 0.0001, 1} /* ^2 gives the nice drive */
- }, {
- "watts",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MID, 5, 150}
- }, {
- "out",
- OUTPUT | AUDIO,
- {0}
- }, {
- "latency",
- OUTPUT | CONTROL,
- {0}
- }
- };
- template <> void
- Descriptor<AmpV>::setup()
- {
- UniqueID = 2587;
- Label = "AmpV";
- Properties = HARD_RT;
- Name = CAPS "AmpV - Tube amp";
- Maker = "Tim Goetze <tim@quitte.de>";
- Copyright = "GPL, 2002-7";
- /* fill port info and vtable */
- autogen();
- }
- /* //////////////////////////////////////////////////////////////////////// */
- void
- AmpVTS::init()
- {
- this->AmpStub::init (false);
-
- /* need to filter out dc before the power amp stage, which is running at
- * the oversampled rate */
- dc_blocker.set_f (10. / (fs * OVERSAMPLE));
-
- /* power supply capacitance */
- for (int i = 0; i < 2; ++i)
- DSP::RBJ::LP (10. / fs, .3, power_cap[i].a, power_cap[i].b);
- tonestack.init (fs);
- }
- template <sample_func_t F, int OVERSAMPLE>
- void
- AmpVTS::one_cycle (int frames)
- {
- sample_t * s = ports[0];
- tonestack.start_cycle (ports + 1, 2);
- sample_t gain = getport(2);
- drive = getport(6) * .5;
- i_drive = 1 / (1 - drive);
-
- sample_t sag = 1 - max (0.0001, min (1, getport(7)));
- sag = .6 * sag * sag; /* map to log space makes slider better */
-
- sample_t * d = ports[8];
-
- *ports[9] = OVERSAMPLE;
- double g = current.g;
- if (gain < 1)
- current.g = max (gain, .001);
- else
- {
- gain -= 1;
- gain *= gain;
- current.g = pow (10, gain);
- }
- /* recursive fade to prevent zipper noise from the 'gain' knob */
- double one_over_n = frames > 0 ? 1. / frames : 1;
- double gf = pow (current.g / g, one_over_n);
- for (int i = 0; i < frames; ++i)
- {
- register double a = s[i];
- register double v = 3 - supply;
- v = v * v * .06 + .46;
- a = tube.transfer (a);
- a = tonestack.process (a + normal);
-
- a = g * (a + supply * .001);
- a = v * tube.transfer_clip (up.upsample (a));
- a = power_transfer (dc_blocker.process (a));
-
- a = down.process (a);
- {
- for (int o = 1; o < OVERSAMPLE; ++o)
- down.store (
- power_transfer (
- dc_blocker.process (
- normal + tube.transfer_clip (
- up.pad (o)))));
- }
- F (d, i, a, adding_gain);
-
- /* integrate for an approximation of cumulative output power */
- supply += sag * fabs (a) + normal;
- /* filter integrated power consumption */
- for (int j = 0; j < 2; ++j)
- supply = 0.9 * (power_cap[j].process (supply + normal));
- g *= gf;
- normal = -normal;
- }
- current.g = g;
- }
- /* //////////////////////////////////////////////////////////////////////// */
- PortInfo
- AmpVTS::port_info [] =
- {
- {
- "in",
- INPUT | AUDIO,
- {BOUNDED, -1, 1}
- }, {
- "model",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_0 | INTEGER, 0, 5} /* no way to set dyn at compile t */
- }, {
- "gain",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_HIGH, 0, 3}
- }, {
- "bass",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_MID, 0, 1}
- }, {
- "mid",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_1, 0, 1}
- }, {
- "treble",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_HIGH, 0, 1}
- }, {
- "drive",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_LOW, 0.0001, 1}
- }, {
- "watts",
- INPUT | CONTROL,
- {BOUNDED | DEFAULT_HIGH, 0.0001, 1}
- }, {
- "out",
- OUTPUT | AUDIO,
- {0}
- }, {
- "latency",
- OUTPUT | CONTROL,
- {0}
- }
- };
- template <> void
- Descriptor<AmpVTS>::setup()
- {
- UniqueID = 2592;
- Label = "AmpVTS";
- Properties = HARD_RT;
- Name = CAPS "AmpVTS - Tube amp + Tone stack";
- Maker = "David Yeh <dtyeh@ccrma.stanford.edu> & Tim Goetze <tim@quitte.de>";
- Copyright = "GPL, 2002-7";
- /* fill port info and vtable */
- autogen();
- }
|