parent
c1b5edced8
commit
ab4a7de34d
@ -0,0 +1,931 @@ |
|||||||
|
/** |
||||||
|
* |
||||||
|
* Copyright (c) 2016-2017 Holger Wirtz <dcoredump@googlemail.com> |
||||||
|
* |
||||||
|
* 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 3 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "synth.h" |
||||||
|
#include "dexed.h" |
||||||
|
#include "EngineMkI.h" |
||||||
|
#include "EngineOpl.h" |
||||||
|
#include "fm_core.h" |
||||||
|
#include "exp2.h" |
||||||
|
#include "sin.h" |
||||||
|
#include "freqlut.h" |
||||||
|
#include "controllers.h" |
||||||
|
#include <unistd.h> |
||||||
|
#include <limits.h> |
||||||
|
|
||||||
|
Dexed::Dexed(double rate) : lvtk::Synth<DexedVoice, Dexed>(p_n_ports, p_midi_in) |
||||||
|
{ |
||||||
|
uint8_t i; |
||||||
|
|
||||||
|
TRACE("--------------------------------------------------------------------------------"); |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
Exp2::init(); |
||||||
|
Tanh::init(); |
||||||
|
Sin::init(); |
||||||
|
|
||||||
|
Freqlut::init(rate); |
||||||
|
Lfo::init(rate); |
||||||
|
PitchEnv::init(rate); |
||||||
|
Env::init_sr(rate); |
||||||
|
fx.init(rate); |
||||||
|
|
||||||
|
engineMkI=new EngineMkI; |
||||||
|
engineOpl=new EngineOpl; |
||||||
|
engineMsfa=new FmCore; |
||||||
|
|
||||||
|
/* |
||||||
|
if(!(engineMkI=new (std::nothrow) EngineMkI)) |
||||||
|
TRACE("Cannot not create engine EngineMkI"); |
||||||
|
if(!(engineOpl=new (std::nothrow) EngineOpl)) |
||||||
|
{ |
||||||
|
delete(engineMkI); |
||||||
|
TRACE("Cannot not create engine EngineOpl"); |
||||||
|
} |
||||||
|
if(!(engineMsfa=new (std::nothrow) FmCore)) |
||||||
|
{ |
||||||
|
delete(engineMkI); |
||||||
|
delete(engineOpl); |
||||||
|
TRACE("Cannot create engine FmCore"); |
||||||
|
} |
||||||
|
*/ |
||||||
|
|
||||||
|
for(i=0; i<MAX_ACTIVE_NOTES; i++) { |
||||||
|
// if(!(voices[i].dx7_note = new (std::nothrow) Dx7Note)) |
||||||
|
// TRACE("Cannot create DX7Note [%d]",i); |
||||||
|
voices[i].dx7_note = new Dx7Note; |
||||||
|
voices[i].keydown = false; |
||||||
|
voices[i].sustained = false; |
||||||
|
voices[i].live = false; |
||||||
|
} |
||||||
|
|
||||||
|
for(i=0;i<sizeof(data);++i) |
||||||
|
{ |
||||||
|
data_float[i]=static_cast<float>(data[i]); |
||||||
|
TRACE("%d->%f",i,data_float[i]); |
||||||
|
} |
||||||
|
|
||||||
|
max_notes=16; |
||||||
|
currentNote = 0; |
||||||
|
controllers.values_[kControllerPitch] = 0x2000; |
||||||
|
controllers.values_[kControllerPitchRange] = 0; |
||||||
|
controllers.values_[kControllerPitchStep] = 0; |
||||||
|
controllers.modwheel_cc = 0; |
||||||
|
controllers.foot_cc = 0; |
||||||
|
controllers.breath_cc = 0; |
||||||
|
controllers.aftertouch_cc = 0; |
||||||
|
controllers.masterTune=0; |
||||||
|
controllers.opSwitch=0x3f; // enable all operators |
||||||
|
//controllers.opSwitch=0x00; |
||||||
|
|
||||||
|
bufsize_=256; |
||||||
|
|
||||||
|
outbuf_=new float[bufsize_]; |
||||||
|
//if(!(outbuf_=new (std::nothrow) float[bufsize_])) |
||||||
|
// TRACE("Cannot create outbuf_ buffer"); |
||||||
|
|
||||||
|
lfo.reset(data+137); |
||||||
|
|
||||||
|
setMonoMode(false); |
||||||
|
|
||||||
|
sustain = false; |
||||||
|
|
||||||
|
extra_buf_size_ = 0; |
||||||
|
|
||||||
|
memset(&voiceStatus, 0, sizeof(VoiceStatus)); |
||||||
|
|
||||||
|
setEngineType(DEXED_ENGINE_MODERN); |
||||||
|
|
||||||
|
//add_voices(new DexedVoice(rate)); |
||||||
|
|
||||||
|
add_audio_outputs(p_audio_out); |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
Dexed::~Dexed() |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
delete [] outbuf_; |
||||||
|
|
||||||
|
currentNote = -1; |
||||||
|
|
||||||
|
for (uint8_t note = 0; note < MAX_ACTIVE_NOTES; ++note) |
||||||
|
delete voices[note].dx7_note; |
||||||
|
|
||||||
|
delete(engineMsfa); |
||||||
|
delete(engineOpl); |
||||||
|
delete(engineMkI); |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
TRACE("--------------------------------------------------------------------------------"); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::activate(void) |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
Plugin::activate(); |
||||||
|
panic(); |
||||||
|
controllers.values_[kControllerPitchRange] = data[155]; |
||||||
|
controllers.values_[kControllerPitchStep] = data[156]; |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::deactivate(void) |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
Plugin::deactivate(); |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::set_params(void) |
||||||
|
{ |
||||||
|
//TRACE("Hi"); |
||||||
|
|
||||||
|
_param_change_counter=0; |
||||||
|
|
||||||
|
bool polymono=bool(*p(p_polymono)); |
||||||
|
uint8_t engine=uint8_t(*p(p_engine)); |
||||||
|
float f_gain=*p(p_output); |
||||||
|
float f_cutoff=*p(p_cutoff); |
||||||
|
float f_reso=*p(p_resonance); |
||||||
|
|
||||||
|
// Dexed-Unisono |
||||||
|
if(isMonoMode()!=polymono) |
||||||
|
setMonoMode(polymono); |
||||||
|
|
||||||
|
// Dexed-Engine |
||||||
|
if(controllers.core==NULL || getEngineType()!=engine) |
||||||
|
{ |
||||||
|
setEngineType(engine); |
||||||
|
refreshVoice=true; |
||||||
|
} |
||||||
|
|
||||||
|
// Dexed-Filter |
||||||
|
if(fx.uiCutoff!=f_cutoff) |
||||||
|
{ |
||||||
|
fx.uiCutoff=f_cutoff; |
||||||
|
refreshVoice=true; |
||||||
|
} |
||||||
|
if(fx.uiReso!=f_reso) |
||||||
|
{ |
||||||
|
fx.uiReso=f_reso; |
||||||
|
refreshVoice=true; |
||||||
|
} |
||||||
|
if(fx.uiGain!=f_gain) |
||||||
|
{ |
||||||
|
fx.uiGain=f_gain; |
||||||
|
refreshVoice=true; |
||||||
|
} |
||||||
|
|
||||||
|
// OP6 |
||||||
|
onParam(0,*p(p_op6_eg_rate_1)); |
||||||
|
onParam(1,*p(p_op6_eg_rate_2)); |
||||||
|
onParam(2,*p(p_op6_eg_rate_3)); |
||||||
|
onParam(3,*p(p_op6_eg_rate_4)); |
||||||
|
onParam(4,*p(p_op6_eg_level_1)); |
||||||
|
onParam(5,*p(p_op6_eg_level_2)); |
||||||
|
onParam(6,*p(p_op6_eg_level_3)); |
||||||
|
onParam(7,*p(p_op6_eg_level_4)); |
||||||
|
onParam(8,*p(p_op6_kbd_lev_scl_brk_pt)); |
||||||
|
onParam(9,*p(p_op6_kbd_lev_scl_lft_depth)); |
||||||
|
onParam(10,*p(p_op6_kbd_lev_scl_rht_depth)); |
||||||
|
onParam(11,*p(p_op6_kbd_lev_scl_lft_curve)); |
||||||
|
onParam(12,*p(p_op6_kbd_lev_scl_rht_curve)); |
||||||
|
onParam(13,*p(p_op6_kbd_rate_scaling)); |
||||||
|
onParam(14,*p(p_op6_amp_mod_sensitivity)); |
||||||
|
onParam(15,*p(p_op6_key_vel_sensitivity)); |
||||||
|
onParam(16,*p(p_op6_operator_output_level)); |
||||||
|
onParam(17,*p(p_op6_osc_mode)); |
||||||
|
onParam(18,*p(p_op6_osc_freq_coarse)); |
||||||
|
onParam(19,*p(p_op6_osc_freq_fine)); |
||||||
|
onParam(20,*p(p_op6_osc_detune)+7); |
||||||
|
// OP5 |
||||||
|
onParam(21,*p(p_op5_eg_rate_1)); |
||||||
|
onParam(22,*p(p_op5_eg_rate_2)); |
||||||
|
onParam(23,*p(p_op5_eg_rate_3)); |
||||||
|
onParam(24,*p(p_op5_eg_rate_4)); |
||||||
|
onParam(25,*p(p_op5_eg_level_1)); |
||||||
|
onParam(26,*p(p_op5_eg_level_2)); |
||||||
|
onParam(27,*p(p_op5_eg_level_3)); |
||||||
|
onParam(28,*p(p_op5_eg_level_4)); |
||||||
|
onParam(29,*p(p_op5_kbd_lev_scl_brk_pt)); |
||||||
|
onParam(30,*p(p_op5_kbd_lev_scl_lft_depth)); |
||||||
|
onParam(31,*p(p_op5_kbd_lev_scl_rht_depth)); |
||||||
|
onParam(32,*p(p_op5_kbd_lev_scl_lft_curve)); |
||||||
|
onParam(33,*p(p_op5_kbd_lev_scl_rht_curve)); |
||||||
|
onParam(34,*p(p_op5_kbd_rate_scaling)); |
||||||
|
onParam(35,*p(p_op5_amp_mod_sensitivity)); |
||||||
|
onParam(36,*p(p_op5_key_vel_sensitivity)); |
||||||
|
onParam(37,*p(p_op5_operator_output_level)); |
||||||
|
onParam(38,*p(p_op5_osc_mode)); |
||||||
|
onParam(39,*p(p_op5_osc_freq_coarse)); |
||||||
|
onParam(40,*p(p_op5_osc_freq_fine)); |
||||||
|
onParam(41,*p(p_op5_osc_detune)+7); |
||||||
|
// OP4 |
||||||
|
onParam(42,*p(p_op4_eg_rate_1)); |
||||||
|
onParam(43,*p(p_op4_eg_rate_2)); |
||||||
|
onParam(44,*p(p_op4_eg_rate_3)); |
||||||
|
onParam(45,*p(p_op4_eg_rate_4)); |
||||||
|
onParam(46,*p(p_op4_eg_level_1)); |
||||||
|
onParam(47,*p(p_op4_eg_level_2)); |
||||||
|
onParam(48,*p(p_op4_eg_level_3)); |
||||||
|
onParam(49,*p(p_op4_eg_level_4)); |
||||||
|
onParam(50,*p(p_op4_kbd_lev_scl_brk_pt)); |
||||||
|
onParam(51,*p(p_op4_kbd_lev_scl_lft_depth)); |
||||||
|
onParam(52,*p(p_op4_kbd_lev_scl_rht_depth)); |
||||||
|
onParam(53,*p(p_op4_kbd_lev_scl_lft_curve)); |
||||||
|
onParam(54,*p(p_op4_kbd_lev_scl_rht_curve)); |
||||||
|
onParam(55,*p(p_op4_kbd_rate_scaling)); |
||||||
|
onParam(56,*p(p_op4_amp_mod_sensitivity)); |
||||||
|
onParam(57,*p(p_op4_key_vel_sensitivity)); |
||||||
|
onParam(58,*p(p_op4_operator_output_level)); |
||||||
|
onParam(59,*p(p_op4_osc_mode)); |
||||||
|
onParam(60,*p(p_op4_osc_freq_coarse)); |
||||||
|
onParam(61,*p(p_op4_osc_freq_fine)); |
||||||
|
onParam(62,*p(p_op4_osc_detune)+7); |
||||||
|
// OP3 |
||||||
|
onParam(63,*p(p_op3_eg_rate_1)); |
||||||
|
onParam(64,*p(p_op3_eg_rate_2)); |
||||||
|
onParam(65,*p(p_op3_eg_rate_3)); |
||||||
|
onParam(66,*p(p_op3_eg_rate_4)); |
||||||
|
onParam(67,*p(p_op3_eg_level_1)); |
||||||
|
onParam(68,*p(p_op3_eg_level_2)); |
||||||
|
onParam(69,*p(p_op3_eg_level_3)); |
||||||
|
onParam(70,*p(p_op3_eg_level_4)); |
||||||
|
onParam(71,*p(p_op3_kbd_lev_scl_brk_pt)); |
||||||
|
onParam(72,*p(p_op3_kbd_lev_scl_lft_depth)); |
||||||
|
onParam(73,*p(p_op3_kbd_lev_scl_rht_depth)); |
||||||
|
onParam(74,*p(p_op3_kbd_lev_scl_lft_curve)); |
||||||
|
onParam(75,*p(p_op3_kbd_lev_scl_rht_curve)); |
||||||
|
onParam(76,*p(p_op3_kbd_rate_scaling)); |
||||||
|
onParam(77,*p(p_op3_amp_mod_sensitivity)); |
||||||
|
onParam(78,*p(p_op3_key_vel_sensitivity)); |
||||||
|
onParam(79,*p(p_op3_operator_output_level)); |
||||||
|
onParam(80,*p(p_op3_osc_mode)); |
||||||
|
onParam(81,*p(p_op3_osc_freq_coarse)); |
||||||
|
onParam(82,*p(p_op3_osc_freq_fine)); |
||||||
|
onParam(83,*p(p_op3_osc_detune)+7); |
||||||
|
// OP2 |
||||||
|
onParam(84,*p(p_op2_eg_rate_1)); |
||||||
|
onParam(85,*p(p_op2_eg_rate_2)); |
||||||
|
onParam(86,*p(p_op2_eg_rate_3)); |
||||||
|
onParam(87,*p(p_op2_eg_rate_4)); |
||||||
|
onParam(88,*p(p_op2_eg_level_1)); |
||||||
|
onParam(89,*p(p_op2_eg_level_2)); |
||||||
|
onParam(90,*p(p_op2_eg_level_3)); |
||||||
|
onParam(91,*p(p_op2_eg_level_4)); |
||||||
|
onParam(92,*p(p_op2_kbd_lev_scl_brk_pt)); |
||||||
|
onParam(93,*p(p_op2_kbd_lev_scl_lft_depth)); |
||||||
|
onParam(94,*p(p_op2_kbd_lev_scl_rht_depth)); |
||||||
|
onParam(95,*p(p_op2_kbd_lev_scl_lft_curve)); |
||||||
|
onParam(96,*p(p_op2_kbd_lev_scl_rht_curve)); |
||||||
|
onParam(97,*p(p_op2_kbd_rate_scaling)); |
||||||
|
onParam(98,*p(p_op2_amp_mod_sensitivity)); |
||||||
|
onParam(99,*p(p_op2_key_vel_sensitivity)); |
||||||
|
onParam(100,*p(p_op2_operator_output_level)); |
||||||
|
onParam(101,*p(p_op2_osc_mode)); |
||||||
|
onParam(102,*p(p_op2_osc_freq_coarse)); |
||||||
|
onParam(103,*p(p_op2_osc_freq_fine)); |
||||||
|
onParam(104,*p(p_op2_osc_detune)+7); |
||||||
|
// OP1 |
||||||
|
onParam(105,*p(p_op1_eg_rate_1)); |
||||||
|
onParam(106,*p(p_op1_eg_rate_2)); |
||||||
|
onParam(107,*p(p_op1_eg_rate_3)); |
||||||
|
onParam(108,*p(p_op1_eg_rate_4)); |
||||||
|
onParam(109,*p(p_op1_eg_level_1)); |
||||||
|
onParam(110,*p(p_op1_eg_level_2)); |
||||||
|
onParam(111,*p(p_op1_eg_level_3)); |
||||||
|
onParam(112,*p(p_op1_eg_level_4)); |
||||||
|
onParam(113,*p(p_op1_kbd_lev_scl_brk_pt)); |
||||||
|
onParam(114,*p(p_op1_kbd_lev_scl_lft_depth)); |
||||||
|
onParam(115,*p(p_op1_kbd_lev_scl_rht_depth)); |
||||||
|
onParam(116,*p(p_op1_kbd_lev_scl_lft_curve)); |
||||||
|
onParam(117,*p(p_op1_kbd_lev_scl_rht_curve)); |
||||||
|
onParam(118,*p(p_op1_kbd_rate_scaling)); |
||||||
|
onParam(119,*p(p_op1_amp_mod_sensitivity)); |
||||||
|
onParam(120,*p(p_op1_key_vel_sensitivity)); |
||||||
|
onParam(121,*p(p_op1_operator_output_level)); |
||||||
|
onParam(122,*p(p_op1_osc_mode)); |
||||||
|
onParam(123,*p(p_op1_osc_freq_coarse)); |
||||||
|
onParam(124,*p(p_op1_osc_freq_fine)); |
||||||
|
onParam(125,*p(p_op1_osc_detune)+7); |
||||||
|
// Global for all OPs |
||||||
|
onParam(126,*p(p_pitch_eg_rate_1)); |
||||||
|
onParam(127,*p(p_pitch_eg_rate_2)); |
||||||
|
onParam(128,*p(p_pitch_eg_rate_3)); |
||||||
|
onParam(129,*p(p_pitch_eg_rate_4)); |
||||||
|
onParam(130,*p(p_pitch_eg_level_1)); |
||||||
|
onParam(131,*p(p_pitch_eg_level_2)); |
||||||
|
onParam(132,*p(p_pitch_eg_level_3)); |
||||||
|
onParam(133,*p(p_pitch_eg_level_4)); |
||||||
|
onParam(134,*p(p_algorithm_num)-1); |
||||||
|
onParam(135,*p(p_feedback)); |
||||||
|
onParam(136,*p(p_oscillator_sync)); |
||||||
|
onParam(137,*p(p_lfo_speed)); |
||||||
|
onParam(138,*p(p_lfo_delay)); |
||||||
|
onParam(139,*p(p_lfo_pitch_mod_depth)); |
||||||
|
onParam(140,*p(p_lfo_amp_mod_depth)); |
||||||
|
onParam(141,*p(p_lfo_sync)); |
||||||
|
onParam(142,*p(p_lfo_waveform)); |
||||||
|
onParam(143,*p(p_pitch_mod_sensitivity)); |
||||||
|
onParam(144,*p(p_transpose)); |
||||||
|
// 10 bytes (145-154) are the name of the patch |
||||||
|
// Controllers (added at the end of the data[]) |
||||||
|
onParam(155,*p(p_pitch_bend_range)); |
||||||
|
onParam(156,*p(p_pitch_bend_step)); |
||||||
|
onParam(157,*p(p_mod_wheel_range)); |
||||||
|
onParam(158,*p(p_mod_wheel_assign)); |
||||||
|
onParam(159,*p(p_foot_ctrl_range)); |
||||||
|
onParam(160,*p(p_foot_ctrl_assign)); |
||||||
|
onParam(161,*p(p_breath_ctrl_range)); |
||||||
|
onParam(162,*p(p_breath_ctrl_assign)); |
||||||
|
onParam(163,*p(p_aftertouch_range)); |
||||||
|
onParam(164,*p(p_aftertouch_assign)); |
||||||
|
onParam(165,*p(p_master_tune)); |
||||||
|
onParam(166,*p(p_op1_enable)); |
||||||
|
onParam(167,*p(p_op2_enable)); |
||||||
|
onParam(168,*p(p_op3_enable)); |
||||||
|
onParam(169,*p(p_op4_enable)); |
||||||
|
onParam(170,*p(p_op5_enable)); |
||||||
|
onParam(171,*p(p_op6_enable)); |
||||||
|
onParam(172,*p(p_number_of_voices)); |
||||||
|
|
||||||
|
if(_param_change_counter>PARAM_CHANGE_LEVEL) |
||||||
|
{ |
||||||
|
panic(); |
||||||
|
controllers.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
//TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
// override the run() method |
||||||
|
void Dexed::run (uint32_t sample_count) |
||||||
|
{ |
||||||
|
const LV2_Atom_Sequence* seq = p<LV2_Atom_Sequence> (p_midi_in); |
||||||
|
float* output = p(p_audio_out); |
||||||
|
uint32_t last_frame = 0, num_this_time = 0; |
||||||
|
bool drop_next_events=false; |
||||||
|
|
||||||
|
Plugin::run(sample_count); |
||||||
|
|
||||||
|
if(++_k_rate_counter%16) |
||||||
|
set_params(); // pre_process: copy actual voice params |
||||||
|
|
||||||
|
for (LV2_Atom_Event* ev = lv2_atom_sequence_begin (&seq->body); |
||||||
|
!lv2_atom_sequence_is_end(&seq->body, seq->atom.size, ev); |
||||||
|
ev = lv2_atom_sequence_next (ev)) |
||||||
|
{ |
||||||
|
num_this_time = ev->time.frames - last_frame; |
||||||
|
|
||||||
|
// If it's midi, send it to the engine |
||||||
|
if (ev->body.type == m_midi_type) |
||||||
|
{ |
||||||
|
drop_next_events|=ProcessMidiMessage((uint8_t*) LV2_ATOM_BODY (&ev->body),ev->body.size); |
||||||
|
if(drop_next_events==true) |
||||||
|
continue; |
||||||
|
} |
||||||
|
|
||||||
|
// render audio from the last frame until the timestamp of this event |
||||||
|
GetSamples (num_this_time, outbuf_); |
||||||
|
|
||||||
|
// i is the index of the engine's buf, which always starts at 0 (i think) |
||||||
|
// j is the index of the plugin's float output buffer which will be the timestamp |
||||||
|
// of the last processed atom event. |
||||||
|
for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j) |
||||||
|
output[j]=outbuf_[i]; |
||||||
|
|
||||||
|
last_frame = ev->time.frames; |
||||||
|
} |
||||||
|
|
||||||
|
// render remaining samples if any left |
||||||
|
if (last_frame < sample_count) |
||||||
|
{ |
||||||
|
// do the same thing as above except from last frame until the end of |
||||||
|
// the processing cycles last sample. at this point, all events have |
||||||
|
// already been handled. |
||||||
|
|
||||||
|
num_this_time = sample_count - last_frame; |
||||||
|
GetSamples (num_this_time, outbuf_); |
||||||
|
for (uint32_t i = 0, j = last_frame; i < num_this_time; ++i, ++j) |
||||||
|
output[j] = outbuf_[i]; |
||||||
|
} |
||||||
|
|
||||||
|
fx.process(output, sample_count); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::GetSamples(uint32_t n_samples, float* buffer) |
||||||
|
{ |
||||||
|
uint32_t i; |
||||||
|
|
||||||
|
if(refreshVoice) { |
||||||
|
for(i=0;i < max_notes;i++) { |
||||||
|
if ( voices[i].live ) |
||||||
|
voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity); |
||||||
|
} |
||||||
|
lfo.reset(data+137); |
||||||
|
refreshVoice = false; |
||||||
|
} |
||||||
|
|
||||||
|
// flush first events |
||||||
|
for (i=0; i < n_samples && i < extra_buf_size_; i++) { |
||||||
|
buffer[i] = extra_buf_[i]; |
||||||
|
} |
||||||
|
|
||||||
|
// remaining buffer is still to be processed |
||||||
|
if (extra_buf_size_ > n_samples) { |
||||||
|
for (uint32_t j = 0; j < extra_buf_size_ - n_samples; j++) { |
||||||
|
extra_buf_[j] = extra_buf_[j + n_samples]; |
||||||
|
} |
||||||
|
extra_buf_size_ -= n_samples; |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
for (; i < n_samples; i += N) { |
||||||
|
AlignedBuf<int32_t, N> audiobuf; |
||||||
|
float sumbuf[N]; |
||||||
|
|
||||||
|
for (uint32_t j = 0; j < N; ++j) { |
||||||
|
audiobuf.get()[j] = 0; |
||||||
|
sumbuf[j] = 0.0; |
||||||
|
} |
||||||
|
|
||||||
|
int32_t lfovalue = lfo.getsample(); |
||||||
|
int32_t lfodelay = lfo.getdelay(); |
||||||
|
|
||||||
|
for (uint8_t note = 0; note < max_notes; ++note) { |
||||||
|
if (voices[note].live) { |
||||||
|
voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); |
||||||
|
for (uint32_t j=0; j < N; ++j) { |
||||||
|
int32_t val = audiobuf.get()[j]; |
||||||
|
val = val >> 4; |
||||||
|
int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; |
||||||
|
float f = static_cast<float>(clip_val>>1)/0x8000; |
||||||
|
if(f>1) f=1; |
||||||
|
if(f<-1) f=-1; |
||||||
|
sumbuf[j]+=f; |
||||||
|
audiobuf.get()[j]=0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint32_t jmax = n_samples - i; |
||||||
|
for (uint32_t j = 0; j < N; ++j) { |
||||||
|
if (j < jmax) |
||||||
|
buffer[i + j] = sumbuf[j]; |
||||||
|
else |
||||||
|
extra_buf_[j - jmax] = sumbuf[j]; |
||||||
|
} |
||||||
|
} |
||||||
|
extra_buf_size_ = i - n_samples; |
||||||
|
} |
||||||
|
|
||||||
|
if(++_k_rate_counter%32 && !monoMode) |
||||||
|
{ |
||||||
|
uint8_t op_carrier=controllers.core->get_carrier_operators(data[134]); // look for carriers |
||||||
|
|
||||||
|
for(i=0;i < max_notes;i++) |
||||||
|
{ |
||||||
|
if(voices[i].live==true) |
||||||
|
{ |
||||||
|
uint8_t op_amp=0; |
||||||
|
uint8_t op_carrier_num=0; |
||||||
|
|
||||||
|
voices[i].dx7_note->peekVoiceStatus(voiceStatus); |
||||||
|
|
||||||
|
for(uint8_t op=0;op<6;op++) |
||||||
|
{ |
||||||
|
uint8_t op_bit=static_cast<uint8_t>(pow(2,op)); |
||||||
|
|
||||||
|
if((op_carrier&op_bit)>0) |
||||||
|
{ |
||||||
|
// this voice is a carrier! |
||||||
|
op_carrier_num++; |
||||||
|
|
||||||
|
//TRACE("Voice[%2d] OP [%d] amp=%ld,amp_step=%d,pitch_step=%d",i,op,voiceStatus.amp[op],voiceStatus.ampStep[op],voiceStatus.pitchStep); |
||||||
|
|
||||||
|
if(voiceStatus.amp[op]<=1069 && voiceStatus.ampStep[op]==4) // this voice produces no audio output |
||||||
|
op_amp++; |
||||||
|
} |
||||||
|
} |
||||||
|
if(op_amp==op_carrier_num) |
||||||
|
{ |
||||||
|
// all carrier-operators are silent -> disable the voice |
||||||
|
voices[i].live=false; |
||||||
|
voices[i].sustained=false; |
||||||
|
voices[i].keydown=false; |
||||||
|
TRACE("Shutted down Voice[%2d]",i); |
||||||
|
} |
||||||
|
} |
||||||
|
// TRACE("Voice[%2d] live=%d keydown=%d",i,voices[i].live,voices[i].keydown); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
bool Dexed::ProcessMidiMessage(const uint8_t *buf, uint32_t buf_size) { |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
uint8_t cmd = buf[0]; |
||||||
|
|
||||||
|
switch(cmd & 0xf0) { |
||||||
|
case 0x80 : |
||||||
|
TRACE("MIDI keyup event: %d",buf[1]); |
||||||
|
keyup(buf[1]); |
||||||
|
return(false); |
||||||
|
break; |
||||||
|
case 0x90 : |
||||||
|
TRACE("MIDI keydown event: %d %d",buf[1],buf[2]); |
||||||
|
keydown(buf[1], buf[2]); |
||||||
|
return(false); |
||||||
|
break; |
||||||
|
case 0xb0 : { |
||||||
|
uint8_t ctrl = buf[1]; |
||||||
|
uint8_t value = buf[2]; |
||||||
|
|
||||||
|
switch(ctrl) { |
||||||
|
case 1: |
||||||
|
TRACE("MIDI modwheel event: %d %d",ctrl,value); |
||||||
|
controllers.modwheel_cc = value; |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 2: |
||||||
|
TRACE("MIDI breath event: %d %d",ctrl,value); |
||||||
|
controllers.breath_cc = value; |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 4: |
||||||
|
TRACE("MIDI footsw event: %d %d",ctrl,value); |
||||||
|
controllers.foot_cc = value; |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 64: |
||||||
|
TRACE("MIDI sustain event: %d %d",ctrl,value); |
||||||
|
sustain = value > 63; |
||||||
|
if (!sustain) { |
||||||
|
for (uint8_t note = 0; note < max_notes; note++) { |
||||||
|
if (voices[note].sustained && !voices[note].keydown) { |
||||||
|
voices[note].dx7_note->keyup(); |
||||||
|
voices[note].sustained = false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
break; |
||||||
|
case 120: |
||||||
|
TRACE("MIDI all-sound-off: %d %d",ctrl,value); |
||||||
|
panic(); |
||||||
|
return(true); |
||||||
|
break; |
||||||
|
case 123: |
||||||
|
TRACE("MIDI all-notes-off: %d %d",ctrl,value); |
||||||
|
notes_off(); |
||||||
|
return(true); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// case 0xc0 : |
||||||
|
// setCurrentProgram(buf[1]); |
||||||
|
// break; |
||||||
|
|
||||||
|
// channel aftertouch |
||||||
|
case 0xd0 : |
||||||
|
TRACE("MIDI aftertouch 0xd0 event: %d %d",buf[1]); |
||||||
|
controllers.aftertouch_cc = buf[1]; |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
// pitchbend |
||||||
|
case 0xe0 : |
||||||
|
TRACE("MIDI pitchbend 0xe0 event: %d %d",buf[1],buf[2]); |
||||||
|
controllers.values_[kControllerPitch] = buf[1] | (buf[2] << 7); |
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
TRACE("MIDI event unknown: cmd=%d, val1=%d, val2=%d",buf[0],buf[1],buf[2]); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
return(false); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::keydown(uint8_t pitch, uint8_t velo) { |
||||||
|
TRACE("Hi"); |
||||||
|
TRACE("pitch=%d, velo=%d\n",pitch,velo); |
||||||
|
if ( velo == 0 ) { |
||||||
|
keyup(pitch); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
pitch += data[144] - 24; |
||||||
|
|
||||||
|
uint8_t note = currentNote; |
||||||
|
uint8_t keydown_counter=0; |
||||||
|
|
||||||
|
for (uint8_t i=0; i<max_notes; i++) { |
||||||
|
if (!voices[note].keydown) { |
||||||
|
currentNote = (note + 1) % max_notes; |
||||||
|
voices[note].midi_note = pitch; |
||||||
|
voices[note].velocity = velo; |
||||||
|
voices[note].sustained = sustain; |
||||||
|
voices[note].keydown = true; |
||||||
|
voices[note].dx7_note->init(data, pitch, velo); |
||||||
|
if ( data[136] ) |
||||||
|
voices[note].dx7_note->oscSync(); |
||||||
|
break; |
||||||
|
} |
||||||
|
else |
||||||
|
keydown_counter++; |
||||||
|
|
||||||
|
note = (note + 1) % max_notes; |
||||||
|
} |
||||||
|
|
||||||
|
if(keydown_counter==0) |
||||||
|
lfo.keydown(); |
||||||
|
if ( monoMode ) { |
||||||
|
for(uint8_t i=0; i<max_notes; i++) { |
||||||
|
if ( voices[i].live ) { |
||||||
|
// all keys are up, only transfer signal |
||||||
|
if ( ! voices[i].keydown ) { |
||||||
|
voices[i].live = false; |
||||||
|
voices[note].dx7_note->transferSignal(*voices[i].dx7_note); |
||||||
|
break; |
||||||
|
} |
||||||
|
if ( voices[i].midi_note < pitch ) { |
||||||
|
voices[i].live = false; |
||||||
|
voices[note].dx7_note->transferState(*voices[i].dx7_note); |
||||||
|
break; |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
voices[note].live = true; |
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::keyup(uint8_t pitch) { |
||||||
|
TRACE("Hi"); |
||||||
|
TRACE("pitch=%d\n",pitch); |
||||||
|
|
||||||
|
pitch += data[144] - 24; |
||||||
|
|
||||||
|
uint8_t note; |
||||||
|
for (note=0; note<max_notes; ++note) { |
||||||
|
if ( voices[note].midi_note == pitch && voices[note].keydown ) { |
||||||
|
voices[note].keydown = false; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// note not found ? |
||||||
|
if ( note >= max_notes ) { |
||||||
|
TRACE("note-off not found???"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if ( monoMode ) { |
||||||
|
int8_t highNote = -1; |
||||||
|
int8_t target = 0; |
||||||
|
for (int8_t i=0; i<max_notes;i++) { |
||||||
|
if ( voices[i].keydown && voices[i].midi_note > highNote ) { |
||||||
|
target = i; |
||||||
|
highNote = voices[i].midi_note; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ( highNote != -1 && voices[note].live ) { |
||||||
|
voices[note].live = false; |
||||||
|
voices[target].live = true; |
||||||
|
voices[target].dx7_note->transferState(*voices[note].dx7_note); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if ( sustain ) { |
||||||
|
voices[note].sustained = true; |
||||||
|
} else { |
||||||
|
voices[note].dx7_note->keyup(); |
||||||
|
} |
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::onParam(uint8_t param_num,float param_val) |
||||||
|
{ |
||||||
|
int32_t tune; |
||||||
|
|
||||||
|
if(param_val!=data_float[param_num]) |
||||||
|
{ |
||||||
|
TRACE("Parameter %d change from %f to %f",param_num, data_float[param_num], param_val); |
||||||
|
#ifdef DEBUG |
||||||
|
uint8_t tmp=data[param_num]; |
||||||
|
#endif |
||||||
|
|
||||||
|
_param_change_counter++; |
||||||
|
|
||||||
|
if(param_num==144 || param_num==134 || param_num==172) |
||||||
|
panic(); |
||||||
|
|
||||||
|
refreshVoice=true; |
||||||
|
data[param_num]=static_cast<uint8_t>(param_val); |
||||||
|
data_float[param_num]=param_val; |
||||||
|
|
||||||
|
switch(param_num) |
||||||
|
{ |
||||||
|
case 155: |
||||||
|
controllers.values_[kControllerPitchRange]=data[param_num]; |
||||||
|
break; |
||||||
|
case 156: |
||||||
|
controllers.values_[kControllerPitchStep]=data[param_num]; |
||||||
|
break; |
||||||
|
case 157: |
||||||
|
controllers.wheel.setRange(data[param_num]); |
||||||
|
controllers.wheel.setTarget(data[param_num+1]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 158: |
||||||
|
controllers.wheel.setRange(data[param_num-1]); |
||||||
|
controllers.wheel.setTarget(data[param_num]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 159: |
||||||
|
controllers.foot.setRange(data[param_num]); |
||||||
|
controllers.foot.setTarget(data[param_num+1]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 160: |
||||||
|
controllers.foot.setRange(data[param_num-1]); |
||||||
|
controllers.foot.setTarget(data[param_num]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 161: |
||||||
|
controllers.breath.setRange(data[param_num]); |
||||||
|
controllers.breath.setTarget(data[param_num+1]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 162: |
||||||
|
controllers.breath.setRange(data[param_num-1]); |
||||||
|
controllers.breath.setTarget(data[param_num]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 163: |
||||||
|
controllers.at.setRange(data[param_num]); |
||||||
|
controllers.at.setTarget(data[param_num+1]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 164: |
||||||
|
controllers.at.setRange(data[param_num-1]); |
||||||
|
controllers.at.setTarget(data[param_num]); |
||||||
|
controllers.refresh(); |
||||||
|
break; |
||||||
|
case 165: |
||||||
|
tune=param_val*0x4000; |
||||||
|
controllers.masterTune=(tune<<11)*(1.0/12); |
||||||
|
break; |
||||||
|
case 166: |
||||||
|
case 167: |
||||||
|
case 168: |
||||||
|
case 169: |
||||||
|
case 170: |
||||||
|
case 171: |
||||||
|
controllers.opSwitch=(data[166]<<5)|(data[167]<<4)|(data[168]<<3)|(data[169]<<2)|(data[170]<<1)|data[171]; |
||||||
|
break; |
||||||
|
case 172: |
||||||
|
max_notes=data[param_num]; |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
TRACE("Done: Parameter %d changed from %d to %d",param_num, tmp, data[param_num]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t Dexed::getEngineType() { |
||||||
|
return engineType; |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::setEngineType(uint8_t tp) { |
||||||
|
TRACE("settings engine %d", tp); |
||||||
|
|
||||||
|
if(engineType==tp && controllers.core!=NULL) |
||||||
|
return; |
||||||
|
|
||||||
|
switch (tp) { |
||||||
|
case DEXED_ENGINE_MARKI: |
||||||
|
TRACE("DEXED_ENGINE_MARKI:%d",DEXED_ENGINE_MARKI); |
||||||
|
controllers.core = engineMkI; |
||||||
|
break; |
||||||
|
case DEXED_ENGINE_OPL: |
||||||
|
TRACE("DEXED_ENGINE_OPL:%d",DEXED_ENGINE_OPL); |
||||||
|
controllers.core = engineOpl; |
||||||
|
break; |
||||||
|
default: |
||||||
|
TRACE("DEXED_ENGINE_MODERN:%d",DEXED_ENGINE_MODERN); |
||||||
|
controllers.core = engineMsfa; |
||||||
|
tp=DEXED_ENGINE_MODERN; |
||||||
|
break; |
||||||
|
} |
||||||
|
engineType = tp; |
||||||
|
panic(); |
||||||
|
controllers.refresh(); |
||||||
|
} |
||||||
|
|
||||||
|
bool Dexed::isMonoMode(void) { |
||||||
|
return monoMode; |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::setMonoMode(bool mode) { |
||||||
|
if(monoMode==mode) |
||||||
|
return; |
||||||
|
|
||||||
|
monoMode = mode; |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::panic(void) { |
||||||
|
for(uint8_t i=0;i<MAX_ACTIVE_NOTES;i++) |
||||||
|
{ |
||||||
|
if(voices[i].live == true) { |
||||||
|
voices[i].keydown = false; |
||||||
|
voices[i].live = false; |
||||||
|
voices[i].sustained = false; |
||||||
|
if ( voices[i].dx7_note != NULL ) { |
||||||
|
voices[i].dx7_note->oscSync(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Dexed::notes_off(void) { |
||||||
|
for(uint8_t i=0;i<MAX_ACTIVE_NOTES;i++) { |
||||||
|
if(voices[i].live==true&&voices[i].keydown==true) { |
||||||
|
voices[i].keydown=false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//============================================================================== |
||||||
|
|
||||||
|
DexedVoice::DexedVoice(double rate) : m_key(lvtk::INVALID_KEY), m_rate(rate) |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
DexedVoice::~DexedVoice() |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
void DexedVoice::on(unsigned char key, unsigned char velocity) |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
m_key = key; |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
void DexedVoice::off(unsigned char velocity) |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
m_key = lvtk::INVALID_KEY; |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
unsigned char DexedVoice::get_key(void) const |
||||||
|
{ |
||||||
|
TRACE("Hi"); |
||||||
|
|
||||||
|
return m_key; |
||||||
|
|
||||||
|
TRACE("Bye"); |
||||||
|
} |
||||||
|
|
||||||
|
static int _ = Dexed::register_class(p_uri); |
@ -0,0 +1,188 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2017 Pascal Gauthier. |
||||||
|
* Copyright 2012 Google Inc. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
#include "synth.h" |
||||||
|
#include "env.h" |
||||||
|
|
||||||
|
//using namespace std;
|
||||||
|
|
||||||
|
uint32_t Env::sr_multiplier = (1<<24); |
||||||
|
|
||||||
|
const int levellut[] = { |
||||||
|
0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46 |
||||||
|
}; |
||||||
|
|
||||||
|
#ifdef ACCURATE_ENVELOPE |
||||||
|
const int statics[] = { |
||||||
|
1764000, 1764000, 1411200, 1411200, 1190700, 1014300, 992250, |
||||||
|
882000, 705600, 705600, 584325, 507150, 502740, 441000, 418950, |
||||||
|
352800, 308700, 286650, 253575, 220500, 220500, 176400, 145530, |
||||||
|
145530, 125685, 110250, 110250, 88200, 88200, 74970, 61740, |
||||||
|
61740, 55125, 48510, 44100, 37485, 31311, 30870, 27562, 27562, |
||||||
|
22050, 18522, 17640, 15435, 14112, 13230, 11025, 9261, 9261, 7717, |
||||||
|
6615, 6615, 5512, 5512, 4410, 3969, 3969, 3439, 2866, 2690, 2249, |
||||||
|
1984, 1896, 1808, 1411, 1367, 1234, 1146, 926, 837, 837, 705, |
||||||
|
573, 573, 529, 441, 441 |
||||||
|
// and so on, I stopped measuring after R=76 (needs to be double-checked anyway)
|
||||||
|
}; |
||||||
|
#endif |
||||||
|
|
||||||
|
void Env::init_sr(double sampleRate) { |
||||||
|
sr_multiplier = (44100.0 / sampleRate) * (1<<24); |
||||||
|
} |
||||||
|
|
||||||
|
void Env::init(const int r[4], const int l[4], int ol, int rate_scaling) { |
||||||
|
for (int i = 0; i < 4; i++) { |
||||||
|
rates_[i] = r[i]; |
||||||
|
levels_[i] = l[i]; |
||||||
|
} |
||||||
|
outlevel_ = ol; |
||||||
|
rate_scaling_ = rate_scaling; |
||||||
|
level_ = 0; |
||||||
|
down_ = true; |
||||||
|
advance(0); |
||||||
|
} |
||||||
|
|
||||||
|
int32_t Env::getsample() { |
||||||
|
#ifdef ACCURATE_ENVELOPE |
||||||
|
if (staticcount_) { |
||||||
|
staticcount_ -= N; |
||||||
|
if (staticcount_ <= 0) { |
||||||
|
staticcount_ = 0; |
||||||
|
advance(ix_ + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
if (ix_ < 3 || ((ix_ < 4) && !down_)) { |
||||||
|
if (rising_) { |
||||||
|
const int jumptarget = 1716; |
||||||
|
if (level_ < (jumptarget << 16)) { |
||||||
|
level_ = jumptarget << 16; |
||||||
|
} |
||||||
|
level_ += (((17 << 24) - level_) >> 24) * inc_; |
||||||
|
// TODO: should probably be more accurate when inc is large
|
||||||
|
if (level_ >= targetlevel_) { |
||||||
|
level_ = targetlevel_; |
||||||
|
advance(ix_ + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
else if (staticcount_) { |
||||||
|
; |
||||||
|
} |
||||||
|
else { // !rising
|
||||||
|
level_ -= inc_; |
||||||
|
if (level_ <= targetlevel_) { |
||||||
|
level_ = targetlevel_; |
||||||
|
advance(ix_ + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
// TODO: this would be a good place to set level to 0 when under threshold
|
||||||
|
return level_; |
||||||
|
} |
||||||
|
|
||||||
|
void Env::keydown(bool d) { |
||||||
|
if (down_ != d) { |
||||||
|
down_ = d; |
||||||
|
advance(d ? 0 : 3); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
int Env::scaleoutlevel(int outlevel) { |
||||||
|
return outlevel >= 20 ? 28 + outlevel : levellut[outlevel]; |
||||||
|
} |
||||||
|
|
||||||
|
void Env::advance(int newix) { |
||||||
|
ix_ = newix; |
||||||
|
if (ix_ < 4) { |
||||||
|
int newlevel = levels_[ix_]; |
||||||
|
int actuallevel = scaleoutlevel(newlevel) >> 1; |
||||||
|
actuallevel = (actuallevel << 6) + outlevel_ - 4256; |
||||||
|
actuallevel = actuallevel < 16 ? 16 : actuallevel; |
||||||
|
// level here is same as Java impl
|
||||||
|
targetlevel_ = actuallevel << 16; |
||||||
|
rising_ = (targetlevel_ > level_); |
||||||
|
|
||||||
|
// rate
|
||||||
|
int qrate = (rates_[ix_] * 41) >> 6; |
||||||
|
qrate += rate_scaling_; |
||||||
|
qrate = min(qrate, 63); |
||||||
|
|
||||||
|
#ifdef ACCURATE_ENVELOPE |
||||||
|
if (targetlevel_ == level_) { |
||||||
|
// approximate number of samples at 44.100 kHz to achieve the time
|
||||||
|
// empirically gathered using 2 TF1s, could probably use some double-checking
|
||||||
|
// and cleanup, but it's pretty close for now.
|
||||||
|
int staticrate = rates_[ix_]; |
||||||
|
staticrate += rate_scaling_; // needs to be checked, as well, but seems correct
|
||||||
|
staticrate = min(staticrate, 99); |
||||||
|
staticcount_ = staticrate < 77 ? statics[staticrate] : 20 * (99 - staticrate); |
||||||
|
staticcount_ = (int)(((int64_t)staticcount_ * (int64_t)sr_multiplier) >> 24); |
||||||
|
} |
||||||
|
else { |
||||||
|
staticcount_ = 0; |
||||||
|
} |
||||||
|
#endif |
||||||
|
inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2)); |
||||||
|
// meh, this should be fixed elsewhere
|
||||||
|
inc_ = (int)(((int64_t)inc_ * (int64_t)sr_multiplier) >> 24); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Env::update(const int r[4], const int l[4], int ol, int rate_scaling) { |
||||||
|
for (int i = 0; i < 4; i++) { |
||||||
|
rates_[i] = r[i]; |
||||||
|
levels_[i] = l[i]; |
||||||
|
} |
||||||
|
outlevel_ = ol; |
||||||
|
rate_scaling_ = rate_scaling; |
||||||
|
if ( down_ ) { |
||||||
|
// for now we simply reset ourselve at level 3
|
||||||
|
int newlevel = levels_[2]; |
||||||
|
int actuallevel = scaleoutlevel(newlevel) >> 1; |
||||||
|
actuallevel = (actuallevel << 6) - 4256; |
||||||
|
actuallevel = actuallevel < 16 ? 16 : actuallevel; |
||||||
|
targetlevel_ = actuallevel << 16; |
||||||
|
advance(2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void Env::getPosition(char *step) { |
||||||
|
*step = ix_; |
||||||
|
} |
||||||
|
|
||||||
|
void Env::transfer(Env &src) { |
||||||
|
for(int i=0;i<4;i++) { |
||||||
|
rates_[i] = src.rates_[i]; |
||||||
|
levels_[i] = src.levels_[i]; |
||||||
|
} |
||||||
|
outlevel_ = src.outlevel_; |
||||||
|
rate_scaling_ = src.rate_scaling_; |
||||||
|
level_ = src.level_; |
||||||
|
targetlevel_ = src.targetlevel_; |
||||||
|
rising_= src.rising_; |
||||||
|
ix_ = src.ix_; |
||||||
|
down_ = src.down_; |
||||||
|
#ifdef ACCURATE_ENVELOPE |
||||||
|
staticcount_ = src.staticcount_; |
||||||
|
#endif |
||||||
|
inc_ = src.inc_; |
||||||
|
} |
||||||
|
|
@ -0,0 +1,283 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2012 Google Inc. |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <math.h> |
||||||
|
|
||||||
|
#include <cstdlib> |
||||||
|
|
||||||
|
#ifdef HAVE_NEON |
||||||
|
#include <cpu-features.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "synth.h" |
||||||
|
#include "sin.h" |
||||||
|
#include "fm_op_kernel.h" |
||||||
|
|
||||||
|
#ifdef HAVE_NEONx |
||||||
|
static bool hasNeon() { |
||||||
|
return true; |
||||||
|
return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; |
||||||
|
} |
||||||
|
|
||||||
|
extern "C" |
||||||
|
void neon_fm_kernel(const int *in, const int *busin, int *out, int count, |
||||||
|
int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); |
||||||
|
|
||||||
|
const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; |
||||||
|
|
||||||
|
#else |
||||||
|
static bool hasNeon() { |
||||||
|
return false; |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
void FmOpKernel::compute(int32_t *output, const int32_t *input, |
||||||
|
int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
if (hasNeon()) { |
||||||
|
#ifdef HAVE_NEON |
||||||
|
neon_fm_kernel(input, add ? output : zeros, output, _N_, |
||||||
|
phase0, freq, gain, dgain); |
||||||
|
#endif |
||||||
|
} else { |
||||||
|
if (add) { |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
int32_t y = Sin::lookup(phase + input[i]); |
||||||
|
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
||||||
|
output[i] += y1; |
||||||
|
phase += freq; |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
int32_t y = Sin::lookup(phase + input[i]); |
||||||
|
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
||||||
|
output[i] = y1; |
||||||
|
phase += freq; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
if (hasNeon()) { |
||||||
|
#ifdef HAVE_NEON |
||||||
|
neon_fm_kernel(zeros, add ? output : zeros, output, _N_, |
||||||
|
phase0, freq, gain, dgain); |
||||||
|
#endif |
||||||
|
} else { |
||||||
|
if (add) { |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
int32_t y = Sin::lookup(phase); |
||||||
|
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; |
||||||
|
output[i] += y1; |
||||||
|
phase += freq; |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
int32_t y = Sin::lookup(phase); |
||||||
|
int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24;
|
||||||
|
output[i] = y1; |
||||||
|
phase += freq; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#define noDOUBLE_ACCURACY |
||||||
|
#define HIGH_ACCURACY |
||||||
|
|
||||||
|
void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, |
||||||
|
int32_t *fb_buf, int fb_shift, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
int32_t y0 = fb_buf[0]; |
||||||
|
int32_t y = fb_buf[1]; |
||||||
|
if (add) { |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
||||||
|
y0 = y; |
||||||
|
y = Sin::lookup(phase + scaled_fb); |
||||||
|
y = ((int64_t)y * (int64_t)gain) >> 24; |
||||||
|
output[i] += y; |
||||||
|
phase += freq; |
||||||
|
} |
||||||
|
} else { |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); |
||||||
|
y0 = y; |
||||||
|
y = Sin::lookup(phase + scaled_fb); |
||||||
|
y = ((int64_t)y * (int64_t)gain) >> 24; |
||||||
|
output[i] = y; |
||||||
|
phase += freq; |
||||||
|
} |
||||||
|
} |
||||||
|
fb_buf[0] = y0; |
||||||
|
fb_buf[1] = y; |
||||||
|
} |
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Experimental sine wave generators below
|
||||||
|
#if 0 |
||||||
|
// Results: accuracy 64.3 mean, 170 worst case
|
||||||
|
// high accuracy: 5.0 mean, 49 worst case
|
||||||
|
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
#ifdef HIGH_ACCURACY |
||||||
|
int32_t u = Sin::compute10(phase << 6); |
||||||
|
u = ((int64_t)u * gain) >> 30; |
||||||
|
int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle
|
||||||
|
v = ((int64_t)v * gain) >> 30; |
||||||
|
int32_t s = Sin::compute10(freq << 6); |
||||||
|
int32_t c = Sin::compute10((freq << 6) + (1 << 28)); |
||||||
|
#else |
||||||
|
int32_t u = Sin::compute(phase); |
||||||
|
u = ((int64_t)u * gain) >> 24; |
||||||
|
int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle
|
||||||
|
v = ((int64_t)v * gain) >> 24; |
||||||
|
int32_t s = Sin::compute(freq) << 6; |
||||||
|
int32_t c = Sin::compute(freq + (1 << 22)) << 6; |
||||||
|
#endif |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
output[i] = u; |
||||||
|
int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; |
||||||
|
u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; |
||||||
|
v = t; |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if 0 |
||||||
|
// Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5)
|
||||||
|
// for freq < 0.25, 275.2 mean, 716 worst
|
||||||
|
// high accuracy: 57.4 mean, 7559 worst
|
||||||
|
// freq < 0.25: 17.9 mean, 78 worst
|
||||||
|
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
#ifdef HIGH_ACCURACY |
||||||
|
int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); |
||||||
|
int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); |
||||||
|
int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); |
||||||
|
#else |
||||||
|
int32_t u = Sin::compute(phase); |
||||||
|
u = ((int64_t)u * gain) >> 24; |
||||||
|
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
||||||
|
v = ((int64_t)v * gain) >> 24; |
||||||
|
int32_t a = Sin::compute(freq >> 1) << 1; |
||||||
|
#endif |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
output[i] = u; |
||||||
|
v -= ((int64_t)a * (int64_t)u) >> 24; |
||||||
|
u += ((int64_t)a * (int64_t)v) >> 24; |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if 0 |
||||||
|
// Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5)
|
||||||
|
// with double accuracy initialization: mean 1.55, worst 58 (near freq = 0)
|
||||||
|
// with high accuracy: mean 4.2, worst 292 (near freq = 0.5)
|
||||||
|
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
#ifdef DOUBLE_ACCURACY |
||||||
|
int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); |
||||||
|
double a_d = sin(freq * (M_PI / (1 << 24))); |
||||||
|
int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * |
||||||
|
(M_PI / (1 << 23))) + 0.5); |
||||||
|
int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); |
||||||
|
#else |
||||||
|
#ifdef HIGH_ACCURACY |
||||||
|
int32_t u = Sin::compute10(phase << 6); |
||||||
|
int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); |
||||||
|
int32_t a = Sin::compute10(freq << 5); |
||||||
|
v = ((int64_t)v * (int64_t)a) >> 29; |
||||||
|
int32_t aa = ((int64_t)a * (int64_t)a) >> 29; |
||||||
|
#else |
||||||
|
int32_t u = Sin::compute(phase) << 6; |
||||||
|
int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); |
||||||
|
int32_t a = Sin::compute(freq >> 1); |
||||||
|
v = ((int64_t)v * (int64_t)a) >> 17; |
||||||
|
int32_t aa = ((int64_t)a * (int64_t)a) >> 17; |
||||||
|
#endif |
||||||
|
#endif |
||||||
|
|
||||||
|
if (aa < 0) aa = (1 << 31) - 1; |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
||||||
|
v -= ((int64_t)aa * (int64_t)u) >> 29; |
||||||
|
u += v; |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if 0 |
||||||
|
// Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5)
|
||||||
|
// high accuracy 2.9 mean, 143 worst
|
||||||
|
void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, |
||||||
|
int32_t gain1, int32_t gain2, bool add) { |
||||||
|
int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; |
||||||
|
int32_t gain = gain1; |
||||||
|
int32_t phase = phase0; |
||||||
|
#ifdef HIGH_ACCURACY |
||||||
|
int32_t u = Sin::compute10(phase << 6); |
||||||
|
int32_t lastu = Sin::compute10((phase - freq) << 6); |
||||||
|
int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; |
||||||
|
#else |
||||||
|
int32_t u = Sin::compute(phase) << 6; |
||||||
|
int32_t lastu = Sin::compute(phase - freq) << 6; |
||||||
|
int32_t a = Sin::compute(freq + (1 << 22)) << 7; |
||||||
|
#endif |
||||||
|
if (a < 0 && freq < 256) a = (1 << 31) - 1; |
||||||
|
if (a > 0 && freq > 0x7fff00) a = -(1 << 31); |
||||||
|
for (int i = 0; i < _N_; i++) { |
||||||
|
gain += dgain; |
||||||
|
output[i] = ((int64_t)u * (int64_t)gain) >> 30; |
||||||
|
//output[i] = u;
|
||||||
|
int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; |
||||||
|
lastu = u; |
||||||
|
u = newu; |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
Loading…
Reference in new issue