diff --git a/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate b/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate index c0ced6b..9eac424 100644 Binary files a/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate and b/Builds/MacOSX/Dexed.xcodeproj/project.xcworkspace/xcuserdata/asb2m10.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/README.md b/README.md index c89d954..c8f541b 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Credits & thanks * DX Synth engine : Raph Levien and the [msfa](https://code.google.com/p/music-synthesizer-for-android) team * Graphical design : [AZur Studio](http://bji.yukihotaru.com/) * LP Filter : Filatov Vadim (2DaT); taken from the excellent [Obxd](https://obxd.wordpress.com) project -* PPPlay : Great [OPL3](http://sourceforge.net/projects/peepeeplayer) implementation, with documented code :D +* PPPlay : Great [OPL3](https://github.com/stohrendorf/ppplay) implementation, with documented code :D * DX7 program compilation : Jean-Marc Desprez (author of [SynprezFM](http://www.synprez.com/SynprezFM)) * DX7 programs : Dave Benson, Frank Carvalho, Tim Conrardy, Jack Deckard, Chris Dodunski, Tim Garrett, Hitaye, Stephan Ibsen, Christian Jezreel, Narfman, Godric Wilkie * falkTX [distrho](http://distrho.sourceforge.net/) diff --git a/Source/EngineMkI.cpp b/Source/EngineMkI.cpp index 690b146..9a80246 100644 --- a/Source/EngineMkI.cpp +++ b/Source/EngineMkI.cpp @@ -1,18 +1,24 @@ /* - * Copyright 2014 Pascal Gauthier. - * Copyright 2012 Google Inc. + * Copyright (C) 2015 Pascal Gauthier * - * 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 + * 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. * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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 + * + * The code is based on ppplay https://github.com/stohrendorf/ppplay and opl3 + * math documentation : + * https://github.com/gtaylormb/opl3_fpga/blob/master/docs/opl3math/opl3math.pdf * - * 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 "EngineMkI.h" @@ -23,10 +29,10 @@ #include "msfa/sin.h" #include "msfa/exp2.h" -static const uint16_t MKI_BITDEPTH = 8; -static const uint16_t MKI_TABLESIZE = 1< 90 ) { + TRACE("SINLOGTABLE: %s" ,buffer); + buffer[0] = 0; + pos = 0; + } } + TRACE("SINLOGTABLE: %s", buffer); + buffer[0] = 0; + pos = 0; + TRACE("----------------------------------------"); + for(int i=0;i 90 ) { + TRACE("SINEXTTABLE: %s" ,buffer); + buffer[0] = 0; + pos = 0; + } + } + TRACE("SINEXTTABLE: %s", buffer); + TRACE("****************************************"); +#endif } inline int32_t mkiSin(int32_t phase, uint16_t env) { - const uint16_t shift = 22 - MKI_BITDEPTH; - uint16_t expVal = sinLog(phase >> shift) + (env << 3); - const bool isSigned = expVal & NEGATIVE_BIT; + uint16_t expVal = sinLog(phase >> (22 - SINLOG_BITDEPTH)) + (env); + //int16_t expValShow = expVal; + const bool isSigned = expVal & NEGATIVE_BIT; expVal &= ~NEGATIVE_BIT; - // expVal: 0..2137+511*8 = 0..6225 - // result: 0..1018+1024 - uint16_t result = 0x0400 + sinExpTable[( expVal & MKI_TABLEFILTER ) ^ MKI_TABLEFILTER]; - result <<= 1; - result >>= ( expVal >> 8 ); // exp - uint32_t ret; - if( isSigned ) { - // -1 for one's complement - ret = -result - 1; - } else { - ret = result; + const uint16_t SINEXP_FILTER = 0x3FF; + uint16_t result = 4096 + sinExpTable[( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER]; + + //uint16_t resultB4 = result; + result >>= ( expVal >> 10 ); // exp + +#ifdef MKIDEBUG + if ( ( time(NULL) % 5 ) == 0 ) { + if ( expValShow < 0 ) { + expValShow = (expValShow + 0x7FFF) * -1; + } + //TRACE(",%d,%d,%d,%d,%d,%d", phase >> (22 - SINLOG_BITDEPTH), env, expValShow, ( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER, resultB4, result); } - return ret << shift; +#endif + + if( isSigned ) + return (-result - 1) << 13; + else + return result << 13; } void EngineMkI::compute(int32_t *output, const int32_t *input, @@ -148,44 +194,50 @@ void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq, fb_buf[1] = y; } - -// exclusively used for ALGO 6 with feedback -void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) { - int32_t dgain[2]; - int32_t gain[2]; - int32_t phase[2]; - int32_t y0 = fb_buf[0]; - int32_t y = fb_buf[1]; - - phase[0] = parms[0].phase; - phase[1] = parms[1].phase; - - gain[0] = gain01; - gain[1] = parms[1].gain_out; - - dgain[0] = (gain02 - gain01 + (N >> 1)) >> LG_N; - - parms[1].gain_out = Exp2::lookup(parms[1].level_in - (14 * (1 << 24))); - dgain[1] = (parms[1].gain_out - gain[1] + (N >> 1)) >> LG_N; - - for (int i = 0; i < N; i++) { - // op 0 - gain[0] += dgain[0]; - int32_t scaled_fb = (y0 + y) >> (fb_shift + 2); // tsk tsk tsk: this needs some tuning - y0 = y; - y = Sin::lookup(phase[0] + scaled_fb); - y = ((int64_t)y * (int64_t)gain[0]) >> 24; - phase[0] += parms[0].freq; +void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, + int32_t *fb_buf, int feedback_shift) { + const uint16_t ENV_MAX = 1<> 4) & 3; + int outbus = flags & 3; + int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); + int32_t gain1 = param.gain_out == 0 ? (ENV_MAX-1) : param.gain_out; + int32_t gain2 = ENV_MAX-(param.level_in >> (28-ENV_BITDEPTH)); + param.gain_out = gain2; - // op 1 - gain[1] += dgain[1]; - y = Sin::lookup(phase[1] + y); - y = ((int64_t)y * (int64_t)gain[1]) >> 24; - output[i] = y; - phase[1] += parms[1].freq; + if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) { + if (!has_contents[outbus]) { + add = false; + } + if (inbus == 0 || !has_contents[inbus]) { + // todo: more than one op in a feedback loop + if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) { + // cout << op << " fb " << inbus << outbus << add << endl; + compute_fb(outptr, param.phase, param.freq, + gain1, gain2, + fb_buf, feedback_shift, add); + } else { + // cout << op << " pure " << inbus << outbus << add << endl; + compute_pure(outptr, param.phase, param.freq, + gain1, gain2, add); + } + } else { + // cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl; + compute(outptr, buf_[inbus - 1].get(), + param.phase, param.freq, gain1, gain2, add); + } + has_contents[outbus] = true; + } else if (!add) { + has_contents[outbus] = false; + } + param.phase += param.freq << LG_N; } - fb_buf[0] = y0; - fb_buf[1] = y; } const FmAlgorithm EngineMkI::algo2[32] = { @@ -272,49 +324,43 @@ void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, fb_buf[1] = y; } -void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, - int32_t *fb_buf, int feedback_shift) { - const int kLevelThresh = 507; // really ???? - const FmAlgorithm alg = algorithms[algorithm]; - bool has_contents[3] = { true, false, false }; - for (int op = 0; op < 6; op++) { - int flags = alg.ops[op]; - bool add = (flags & OUT_BUS_ADD) != 0; - FmOpParams ¶m = params[op]; - int inbus = (flags >> 4) & 3; - int outbus = flags & 3; - int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); - int32_t gain1 = param.gain_out == 0 ? 511 : param.gain_out; - int32_t gain2 = 512-(param.level_in >> 19); - param.gain_out = gain2; +// exclusively used for ALGO 6 with feedback +void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) { + int32_t dgain[2]; + int32_t gain[2]; + int32_t phase[2]; + int32_t y0 = fb_buf[0]; + int32_t y = fb_buf[1]; + + phase[0] = parms[0].phase; + phase[1] = parms[1].phase; + + gain[0] = gain01; + gain[1] = parms[1].gain_out; + + dgain[0] = (gain02 - gain01 + (N >> 1)) >> LG_N; + + parms[1].gain_out = Exp2::lookup(parms[1].level_in - (14 * (1 << 24))); + dgain[1] = (parms[1].gain_out - gain[1] + (N >> 1)) >> LG_N; + + for (int i = 0; i < N; i++) { + // op 0 + gain[0] += dgain[0]; + int32_t scaled_fb = (y0 + y) >> (fb_shift + 2); // tsk tsk tsk: this needs some tuning + y0 = y; + y = Sin::lookup(phase[0] + scaled_fb); + y = ((int64_t)y * (int64_t)gain[0]) >> 24; + phase[0] += parms[0].freq; - if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) { - if (!has_contents[outbus]) { - add = false; - } - if (inbus == 0 || !has_contents[inbus]) { - // todo: more than one op in a feedback loop - if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) { - // cout << op << " fb " << inbus << outbus << add << endl; - compute_fb(outptr, param.phase, param.freq, - gain1, gain2, - fb_buf, feedback_shift, add); - } else { - // cout << op << " pure " << inbus << outbus << add << endl; - compute_pure(outptr, param.phase, param.freq, - gain1, gain2, add); - } - } else { - // cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl; - compute(outptr, buf_[inbus - 1].get(), - param.phase, param.freq, gain1, gain2, add); - } - has_contents[outbus] = true; - } else if (!add) { - has_contents[outbus] = false; - } - param.phase += param.freq << LG_N; + // op 1 + gain[1] += dgain[1]; + y = Sin::lookup(phase[1] + y); + y = ((int64_t)y * (int64_t)gain[1]) >> 24; + output[i] = y; + phase[1] += parms[1].freq; } + fb_buf[0] = y0; + fb_buf[1] = y; } /* diff --git a/Source/ParamDialog.cpp b/Source/ParamDialog.cpp index 6b061de..3b78420 100644 --- a/Source/ParamDialog.cpp +++ b/Source/ParamDialog.cpp @@ -70,7 +70,7 @@ ParamDialog::ParamDialog () engineReso->setJustificationType (Justification::centredLeft); engineReso->setTextWhenNothingSelected (String::empty); engineReso->setTextWhenNoChoicesAvailable (TRANS("(no choices)")); - engineReso->addItem (TRANS("Modern (Direct)"), 1); + engineReso->addItem (TRANS("Modern (24-bit)"), 1); engineReso->addItem (TRANS("Mark I"), 2); engineReso->addItem (TRANS("OPL Series"), 3); engineReso->addListener (this); @@ -635,7 +635,7 @@ BEGIN_JUCER_METADATA textBoxWidth="80" textBoxHeight="20" skewFactor="1"/>