├── .gitattributes ├── .gitignore ├── .vscode ├── arduino.json ├── c_cpp_properties.json ├── settings.json └── tasks.json ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── capture.txt └── main ├── CMakeLists.txt ├── Encoder4.h ├── EngineMkI.cpp ├── EngineMkI.h ├── EngineOpl.cpp ├── EngineOpl.h ├── Kconfig.projbuild ├── LiquidCrystalPlus_I2C.h ├── aligned_buf.h ├── component.mk ├── config.h ├── controllers.h ├── dexed.cpp ├── dexed.h ├── dx7note.cpp ├── dx7note.h ├── dx7rom.h ├── env.cc ├── env.cpp ├── env.h ├── exp2.cpp ├── exp2.h ├── fm_core.cpp ├── fm_core.h ├── fm_op_kernel.cc ├── fm_op_kernel.cpp ├── fm_op_kernel.h ├── freqlut.cpp ├── freqlut.h ├── lfo.cpp ├── lfo.h ├── main.cpp ├── midinotes.h ├── module.h ├── pitchenv.cpp ├── pitchenv.h ├── playtune.cpp ├── playtune.h ├── sin.cpp ├── sin.h ├── songdata.h └── synth.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | sdkconfig 3 | sdkconfig.old 4 | -------------------------------------------------------------------------------- /.vscode/arduino.json: -------------------------------------------------------------------------------- 1 | { 2 | "board": "esp8266:esp8266:d1", 3 | "configuration": "xtal=80,vt=flash,exception=enabled,eesz=4M,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=921600", 4 | "port": "COM6" 5 | } -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Win32", 5 | "includePath": [ 6 | "C:\\Users\\comp1\\AppData\\Local\\Arduino15\\packages\\esp8266\\tools\\**", 7 | "C:\\Users\\comp1\\AppData\\Local\\Arduino15\\packages\\esp8266\\hardware\\esp8266\\2.5.0-beta2\\**", 8 | "${workspaceRoot}", 9 | "C:/esp/esp-idf/components" 10 | ], 11 | "browse": { 12 | "limitSymbolsToIncludedHeaders": true, 13 | "databaseFilename": "", 14 | "path": [ 15 | "${workspaceRoot}", 16 | "C:/esp/esp-idf/components" 17 | ] 18 | }, 19 | "intelliSenseMode": "clang-x64", 20 | "compilerPath": "C:\\Program Files (x86)\\mingw-w64\\i686-8.1.0-posix-dwarf-rt_v6-rev0\\mingw32\\bin\\gcc.exe", 21 | "cStandard": "c11", 22 | "cppStandard": "c++17", 23 | "forcedInclude": [] 24 | } 25 | ], 26 | "version": 4 27 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "i2s.h": "c", 4 | "periph_ctrl.h": "c", 5 | "future": "cpp", 6 | "optional": "cpp" 7 | } 8 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.1.0", 3 | "command": "c:/msys32/usr/bin/bash", 4 | "args": ["--login","-c"], 5 | "isShellCommand": true, 6 | "showOutput": "always", 7 | "suppressTaskName": true, 8 | "options": { 9 | "cwd": "${workspaceRoot}", 10 | "env": { 11 | "CHERE_INVOKING": "enabled_from_arguments", 12 | "MSYSTEM": "MINGW32" 13 | } 14 | }, 15 | "tasks": [ 16 | { 17 | "taskName": "build app", 18 | "args": [ 19 | "make app -j8" 20 | ], 21 | "isBuildCommand": true, 22 | "problemMatcher": { 23 | "owner": "cpp", 24 | "fileLocation": "absolute", 25 | "pattern": { 26 | "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$", 27 | "file": 1, 28 | "line": 2, 29 | "column": 3, 30 | "severity": 4, 31 | "message": 5 32 | } 33 | } 34 | }, 35 | { 36 | "taskName": "clean app", 37 | "args": [ 38 | "make app-clean" 39 | ] 40 | }, 41 | { 42 | "taskName": "flash app", 43 | "args": [ 44 | "make app-flash" 45 | ], 46 | "problemMatcher": [] 47 | }, 48 | { 49 | "taskName": "monitor", 50 | "command": "c:/msys32/mingw32", 51 | "args": [ 52 | "make", 53 | "monitor" 54 | ], 55 | "isShellCommand": false, 56 | "showOutput": "never" 57 | }, 58 | { 59 | "taskName": "menuconfig", 60 | "command": "c:/msys32/mingw32", 61 | "args": [ 62 | "make", 63 | "menuconfig" 64 | ], 65 | "isShellCommand": false, 66 | "showOutput": "never" 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # The following lines of boilerplate have to be in your project's 2 | # CMakeLists in this exact order for cmake to work correctly 3 | cmake_minimum_required(VERSION 3.5) 4 | 5 | include($ENV{IDF_PATH}/tools/cmake/project.cmake) 6 | project(app-template) 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := esp32-dexed 7 | 8 | include $(IDF_PATH)/make/project.mk 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MicroDexed for ESP32, beta 1.0, by nyh. 2 | 3 | ## Thanks to Holger W. for the MicroDexed code: https://github.com/dcoredump/MicroDexed 4 | 5 | ---------- 6 | 7 | The sample program plays a short tune (Holst's Jupiter) using MicroDexed engine using the I2S module (PCM5102). 8 | 9 | Also, thanks to Len Shustek for the Miditones code: https://github.com/LenShustek/miditones 10 | 11 | Note: This only works with 8 notes - more than that the whole thing will stutter. 12 | 13 | Suggestions are welcome to improve this with more than 8 notes. In the meantime I'm still finding ways and means 14 | to improve the FM synth algorithm. 15 | 16 | A slight modification is done on dexed.cpp -> the SSAT instruction is replaced by CLAMPS because ESP32 doesn't 17 | have SSAT. Fundamentally, CLAMPS works the same as SSAT. 18 | 19 | As this is a beta, the code can be very messy. Again, I'm working to reorganize the code as much as possible. 20 | 21 | ---------- 22 | 23 | Notes: Project archived. Due to the MicroDexed code here being very old (Apr 2019), porting to other architectures is difficult. Please check alternative ports: [pico2dexed](https://github.com/nyh-workshop/pico2dexed) and [picodexed](https://github.com/diyelectromusic/picodexed). 24 | -------------------------------------------------------------------------------- /capture.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nyh-workshop/esp32-microdexed/cbc5c418bfaf321d90422def8b818c8805cb5278/capture.txt -------------------------------------------------------------------------------- /main/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Edit following two lines to set component requirements (see docs) 2 | set(COMPONENT_REQUIRES ) 3 | set(COMPONENT_PRIV_REQUIRES ) 4 | 5 | set(COMPONENT_SRCS "main.c") 6 | set(COMPONENT_ADD_INCLUDEDIRS "") 7 | 8 | register_component() 9 | -------------------------------------------------------------------------------- /main/Encoder4.h: -------------------------------------------------------------------------------- 1 | /* 2 | MicroDexed 3 | 4 | MicroDexed is a port of the Dexed sound engine 5 | (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6 with audio shield. 6 | Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android 7 | 8 | (c)2018 H. Wirtz 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software Foundation, 22 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | */ 25 | 26 | #include 27 | 28 | #ifndef ENCODER4_H_INCLUDED 29 | #define ENCODER4_H_INCLUDED 30 | 31 | class Encoder4 : public Encoder 32 | { 33 | public: 34 | 35 | using Encoder::Encoder; 36 | 37 | int32_t read() 38 | { 39 | return (Encoder::read() / 4); 40 | } 41 | 42 | void write(int32_t p) 43 | { 44 | Encoder::write(p * 4); 45 | } 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /main/EngineMkI.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2015-2017 Pascal Gauthier. 3 | * 4 | * This program is free software; you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation; either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software Foundation, 16 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 | * 18 | * The code is based on ppplay https://github.com/stohrendorf/ppplay and opl3 19 | * math documentation : 20 | * https://github.com/gtaylormb/opl3_fpga/blob/master/docs/opl3math/opl3math.pdf 21 | * 22 | */ 23 | 24 | #include "EngineMkI.h" 25 | #define _USE_MATH_DEFINES 26 | #include 27 | #include 28 | 29 | #include "sin.h" 30 | #include "exp2.h" 31 | 32 | #ifdef DEBUG 33 | #include "time.h" 34 | //#define MKIDEBUG 35 | #endif 36 | 37 | #ifdef _WIN32 38 | #if _MSC_VER < 1800 39 | FRAC_NUM log2(FRAC_NUM n) { 40 | //return log(n) / log(2.0); 41 | return LOG_FUNC(n) / LOG_FUNC(2.0); 42 | } 43 | FRAC_NUM round(FRAC_NUM n) { 44 | return n < 0.0 ? ceil(n - 0.5) : floor(n + 0.5); 45 | } 46 | #endif 47 | __declspec(align(16)) const int zeros[N] = {0}; 48 | #else 49 | const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0}; 50 | #endif 51 | 52 | static const uint16_t NEGATIVE_BIT = 0x8000; 53 | static const uint16_t ENV_BITDEPTH = 14; 54 | 55 | static const uint16_t SINLOG_BITDEPTH = 10; 56 | static const uint16_t SINLOG_TABLESIZE = 1< 90 ) { 103 | buffer[0] = 0; 104 | pos = 0; 105 | } 106 | } 107 | buffer[0] = 0; 108 | pos = 0; 109 | for(int i=0;i 90 ) { 112 | buffer[0] = 0; 113 | pos = 0; 114 | } 115 | } 116 | #endif 117 | } 118 | 119 | inline int32_t mkiSin(int32_t phase, uint16_t env) { 120 | uint16_t expVal = sinLog(phase >> (22 - SINLOG_BITDEPTH)) + (env); 121 | //int16_t expValShow = expVal; 122 | 123 | const bool isSigned = expVal & NEGATIVE_BIT; 124 | expVal &= ~NEGATIVE_BIT; 125 | 126 | const uint16_t SINEXP_FILTER = 0x3FF; 127 | uint16_t result = 4096 + sinExpTable[( expVal & SINEXP_FILTER ) ^ SINEXP_FILTER]; 128 | 129 | //uint16_t resultB4 = result; 130 | result >>= ( expVal >> 10 ); // exp 131 | 132 | #ifdef MKIDEBUG 133 | if ( ( time(NULL) % 5 ) == 0 ) { 134 | if ( expValShow < 0 ) { 135 | expValShow = (expValShow + 0x7FFF) * -1; 136 | } 137 | } 138 | #endif 139 | 140 | if( isSigned ) 141 | return (-result - 1) << 13; 142 | else 143 | return result << 13; 144 | } 145 | 146 | void EngineMkI::compute(int32_t *output, const int32_t *input, 147 | int32_t phase0, int32_t freq, 148 | int32_t gain1, int32_t gain2, bool add) { 149 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 150 | int32_t gain = gain1; 151 | int32_t phase = phase0; 152 | const int32_t *adder = add ? output : zeros; 153 | 154 | for (int i = 0; i < _N_; i++) { 155 | gain += dgain; 156 | int32_t y = mkiSin((phase+input[i]), gain); 157 | output[i] = y + adder[i]; 158 | phase += freq; 159 | } 160 | 161 | } 162 | 163 | void EngineMkI::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 164 | int32_t gain1, int32_t gain2, bool add) { 165 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 166 | int32_t gain = gain1; 167 | int32_t phase = phase0; 168 | const int32_t *adder = add ? output : zeros; 169 | 170 | for (int i = 0; i < _N_; i++) { 171 | gain += dgain; 172 | int32_t y = mkiSin(phase , gain); 173 | output[i] = y + adder[i]; 174 | phase += freq; 175 | } 176 | } 177 | 178 | void EngineMkI::compute_fb(int32_t *output, int32_t phase0, int32_t freq, 179 | int32_t gain1, int32_t gain2, 180 | int32_t *fb_buf, int fb_shift, bool add) { 181 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 182 | int32_t gain = gain1; 183 | int32_t phase = phase0; 184 | const int32_t *adder = add ? output : zeros; 185 | int32_t y0 = fb_buf[0]; 186 | int32_t y = fb_buf[1]; 187 | 188 | for (int i = 0; i < _N_; i++) { 189 | gain += dgain; 190 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 191 | y0 = y; 192 | y = mkiSin((phase+scaled_fb), gain); 193 | output[i] = y + adder[i]; 194 | phase += freq; 195 | } 196 | 197 | fb_buf[0] = y0; 198 | fb_buf[1] = y; 199 | } 200 | 201 | // exclusively used for ALGO 6 with feedback 202 | void EngineMkI::compute_fb2(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) { 203 | int32_t dgain[2]; 204 | int32_t gain[2]; 205 | int32_t phase[2]; 206 | int32_t y0 = fb_buf[0]; 207 | int32_t y = fb_buf[1]; 208 | 209 | phase[0] = parms[0].phase; 210 | phase[1] = parms[1].phase; 211 | 212 | parms[1].gain_out = (ENV_MAX-(parms[1].level_in >> (28-ENV_BITDEPTH))); 213 | 214 | gain[0] = gain01; 215 | gain[1] = parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out; 216 | 217 | dgain[0] = (gain02 - gain01 + (_N_ >> 1)) >> LG_N; 218 | dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out)); 219 | 220 | for (int i = 0; i < _N_; i++) { 221 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 222 | 223 | // op 0 224 | gain[0] += dgain[0]; 225 | y0 = y; 226 | y = mkiSin(phase[0]+scaled_fb, gain[0]); 227 | phase[0] += parms[0].freq; 228 | 229 | // op 1 230 | gain[1] += dgain[1]; 231 | y = mkiSin(phase[1]+y, gain[1]); 232 | phase[1] += parms[1].freq; 233 | 234 | output[i] = y; 235 | } 236 | fb_buf[0] = y0; 237 | fb_buf[1] = y; 238 | } 239 | 240 | // exclusively used for ALGO 4 with feedback 241 | void EngineMkI::compute_fb3(int32_t *output, FmOpParams *parms, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift) { 242 | int32_t dgain[3]; 243 | int32_t gain[3]; 244 | int32_t phase[3]; 245 | int32_t y0 = fb_buf[0]; 246 | int32_t y = fb_buf[1]; 247 | 248 | phase[0] = parms[0].phase; 249 | phase[1] = parms[1].phase; 250 | phase[2] = parms[2].phase; 251 | 252 | parms[1].gain_out = (ENV_MAX-(parms[1].level_in >> (28-ENV_BITDEPTH))); 253 | parms[2].gain_out = (ENV_MAX-(parms[2].level_in >> (28-ENV_BITDEPTH))); 254 | 255 | gain[0] = gain01; 256 | gain[1] = parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out; 257 | gain[2] = parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out; 258 | 259 | dgain[0] = (gain02 - gain01 + (_N_ >> 1)) >> LG_N; 260 | dgain[1] = (parms[1].gain_out - (parms[1].gain_out == 0 ? (ENV_MAX-1) : parms[1].gain_out)); 261 | dgain[2] = (parms[2].gain_out - (parms[2].gain_out == 0 ? (ENV_MAX-1) : parms[2].gain_out)); 262 | 263 | 264 | for (int i = 0; i < _N_; i++) { 265 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 266 | 267 | // op 0 268 | gain[0] += dgain[0]; 269 | y0 = y; 270 | y = mkiSin(phase[0]+scaled_fb, gain[0]); 271 | phase[0] += parms[0].freq; 272 | 273 | // op 1 274 | gain[1] += dgain[1]; 275 | y = mkiSin(phase[1]+y, gain[1]); 276 | phase[1] += parms[1].freq; 277 | 278 | // op 2 279 | gain[2] += dgain[2]; 280 | y = mkiSin(phase[2]+y, gain[2]); 281 | phase[2] += parms[2].freq; 282 | 283 | output[i] = y; 284 | } 285 | fb_buf[0] = y0; 286 | fb_buf[1] = y; 287 | } 288 | 289 | void EngineMkI::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift) { 290 | const int kLevelThresh = ENV_MAX-100; 291 | FmAlgorithm alg = algorithms[algorithm]; 292 | bool has_contents[3] = { true, false, false }; 293 | bool fb_on = feedback_shift < 16; 294 | 295 | switch(algorithm) { 296 | case 3 : case 5 : 297 | if ( fb_on ) 298 | alg.ops[0] = 0xc4; 299 | } 300 | 301 | for (int op = 0; op < 6; op++) { 302 | int flags = alg.ops[op]; 303 | bool add = (flags & OUT_BUS_ADD) != 0; 304 | FmOpParams ¶m = params[op]; 305 | int inbus = (flags >> 4) & 3; 306 | int outbus = flags & 3; 307 | int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); 308 | int32_t gain1 = param.gain_out == 0 ? (ENV_MAX-1) : param.gain_out; 309 | int32_t gain2 = ENV_MAX-(param.level_in >> (28-ENV_BITDEPTH)); 310 | param.gain_out = gain2; 311 | 312 | if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) { 313 | 314 | if (!has_contents[outbus]) { 315 | add = false; 316 | } 317 | 318 | if (inbus == 0 || !has_contents[inbus]) { 319 | // PG: this is my 'dirty' implementation of FB for 2 and 3 operators... 320 | if ((flags & 0xc0) == 0xc0 && fb_on) { 321 | switch ( algorithm ) { 322 | // three operator feedback, process exception for ALGO 4 323 | case 3 : 324 | compute_fb3(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16)); 325 | params[1].phase += params[1].freq << LG_N; // hack, we already processed op-5 - op-4 326 | params[2].phase += params[2].freq << LG_N; // yuk yuk 327 | op += 2; // ignore the 2 other operators 328 | break; 329 | // two operator feedback, process exception for ALGO 6 330 | case 5 : 331 | compute_fb2(outptr, params, gain1, gain2, fb_buf, min((feedback_shift+2), 16)); 332 | params[1].phase += params[1].freq << LG_N; // yuk, hack, we already processed op-5 333 | op++; // ignore next operator; 334 | break; 335 | default: 336 | // one operator feedback, normal proces 337 | compute_fb(outptr, param.phase, param.freq, gain1, gain2, fb_buf, feedback_shift, add); 338 | break; 339 | } 340 | } else { 341 | compute_pure(outptr, param.phase, param.freq, gain1, gain2, add); 342 | } 343 | } else { 344 | compute(outptr, buf_[inbus - 1].get(), param.phase, param.freq, gain1, gain2, add); 345 | } 346 | 347 | has_contents[outbus] = true; 348 | } else if (!add) { 349 | has_contents[outbus] = false; 350 | } 351 | param.phase += param.freq << LG_N; 352 | } 353 | } 354 | 355 | -------------------------------------------------------------------------------- /main/EngineMkI.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2014 Pascal Gauthier. 3 | * Copyright 2012 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef ENGINEMKI_H_INCLUDED 19 | #define ENGINEMKI_H_INCLUDED 20 | 21 | //#include 22 | #include "synth.h" 23 | #include "aligned_buf.h" 24 | #include "fm_op_kernel.h" 25 | #include "controllers.h" 26 | #include "fm_core.h" 27 | 28 | 29 | class EngineMkI : public FmCore { 30 | public: 31 | EngineMkI(); 32 | 33 | void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int32_t feedback_shift); 34 | 35 | void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, 36 | bool add); 37 | 38 | void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, 39 | bool add); 40 | 41 | void compute_fb(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, 42 | int32_t *fb_buf, int fb_gain, bool add); 43 | 44 | void compute_fb2(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift); 45 | 46 | void compute_fb3(int32_t *output, FmOpParams *params, int32_t gain01, int32_t gain02, int32_t *fb_buf, int fb_shift); 47 | }; 48 | 49 | 50 | #endif // ENGINEMKI_H_INCLUDED 51 | -------------------------------------------------------------------------------- /main/EngineOpl.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Pascal Gauthier. 3 | * Copyright (C) 2012 Steffen Ohrendorf 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | * 19 | * Original Java Code: Copyright (C) 2008 Robson Cozendey 20 | * 21 | * Some code based on forum posts in: http://forums.submarine.org.uk/phpBB/viewforum.php?f=9, 22 | * Copyright (C) 2010-2013 by carbon14 and opl3 23 | * 24 | */ 25 | 26 | #include "EngineOpl.h" 27 | 28 | #ifdef _WIN32 29 | __declspec(align(16)) const int zeros[N] = {0}; 30 | #else 31 | const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0}; 32 | #endif 33 | 34 | uint16_t SignBit = 0x8000; 35 | 36 | uint16_t sinLogTable[256] = { 37 | 2137, 1731, 1543, 1419, 1326, 1252, 1190, 1137, 1091, 1050, 1013, 979, 949, 920, 894, 869, 38 | 846, 825, 804, 785, 767, 749, 732, 717, 701, 687, 672, 659, 646, 633, 621, 609, 39 | 598, 587, 576, 566, 556, 546, 536, 527, 518, 509, 501, 492, 484, 476, 468, 461, 40 | 453, 446, 439, 432, 425, 418, 411, 405, 399, 392, 386, 380, 375, 369, 363, 358, 41 | 352, 347, 341, 336, 331, 326, 321, 316, 311, 307, 302, 297, 293, 289, 284, 280, 42 | 276, 271, 267, 263, 259, 255, 251, 248, 244, 240, 236, 233, 229, 226, 222, 219, 43 | 215, 212, 209, 205, 202, 199, 196, 193, 190, 187, 184, 181, 178, 175, 172, 169, 44 | 167, 164, 161, 159, 156, 153, 151, 148, 146, 143, 141, 138, 136, 134, 131, 129, 45 | 127, 125, 122, 120, 118, 116, 114, 112, 110, 108, 106, 104, 102, 100, 98, 96, 46 | 94, 92, 91, 89, 87, 85, 83, 82, 80, 78, 77, 75, 74, 72, 70, 69, 47 | 67, 66, 64, 63, 62, 60, 59, 57, 56, 55, 53, 52, 51, 49, 48, 47, 48 | 46, 45, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 49 | 29, 28, 27, 26, 25, 24, 23, 23, 22, 21, 20, 20, 19, 18, 17, 17, 50 | 16, 15, 15, 14, 13, 13, 12, 12, 11, 10, 10, 9, 9, 8, 8, 7, 51 | 7, 7, 6, 6, 5, 5, 5, 4, 4, 4, 3, 3, 3, 2, 2, 2, 52 | 2, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0 53 | }; 54 | 55 | uint16_t sinExpTable[256] = { 56 | 0, 3, 6, 8, 11, 14, 17, 20, 22, 25, 28, 31, 34, 37, 40, 42, 57 | 45, 48, 51, 54, 57, 60, 63, 66, 69, 72, 75, 78, 81, 84, 87, 90, 58 | 93, 96, 99, 102, 105, 108, 111, 114, 117, 120, 123, 126, 130, 133, 136, 139, 59 | 142, 145, 148, 152, 155, 158, 161, 164, 168, 171, 174, 177, 181, 184, 187, 190, 60 | 194, 197, 200, 204, 207, 210, 214, 217, 220, 224, 227, 231, 234, 237, 241, 244, 61 | 248, 251, 255, 258, 262, 265, 268, 272, 276, 279, 283, 286, 290, 293, 297, 300, 62 | 304, 308, 311, 315, 318, 322, 326, 329, 333, 337, 340, 344, 348, 352, 355, 359, 63 | 363, 367, 370, 374, 378, 382, 385, 389, 393, 397, 401, 405, 409, 412, 416, 420, 64 | 424, 428, 432, 436, 440, 444, 448, 452, 456, 460, 464, 468, 472, 476, 480, 484, 65 | 488, 492, 496, 501, 505, 509, 513, 517, 521, 526, 530, 534, 538, 542, 547, 551, 66 | 555, 560, 564, 568, 572, 577, 581, 585, 590, 594, 599, 603, 607, 612, 616, 621, 67 | 625, 630, 634, 639, 643, 648, 652, 657, 661, 666, 670, 675, 680, 684, 689, 693, 68 | 698, 703, 708, 712, 717, 722, 726, 731, 736, 741, 745, 750, 755, 760, 765, 770, 69 | 774, 779, 784, 789, 794, 799, 804, 809, 814, 819, 824, 829, 834, 839, 844, 849, 70 | 854, 859, 864, 869, 874, 880, 885, 890, 895, 900, 906, 911, 916, 921, 927, 932, 71 | 937, 942, 948, 953, 959, 964, 969, 975, 980, 986, 991, 996, 1002, 1007, 1013, 1018 72 | }; 73 | 74 | inline uint16_t sinLog(uint16_t phi) { 75 | const uint8_t index = (phi & 0xff); 76 | 77 | switch( ( phi & 0x0300 ) ) { 78 | case 0x0000: 79 | // rising quarter wave Shape A 80 | return sinLogTable[index]; 81 | case 0x0100: 82 | // falling quarter wave Shape B 83 | return sinLogTable[index ^ 0xFF]; 84 | case 0x0200: 85 | // rising quarter wave -ve Shape C 86 | return sinLogTable[index] | SignBit; 87 | default: 88 | // falling quarter wave -ve Shape D 89 | return sinLogTable[index ^ 0xFF] | SignBit; 90 | } 91 | } 92 | 93 | // 16 env units are ~3dB and halve the output 94 | /** 95 | * @brief OPL Sine Wave calculation 96 | * @param[in] phase Wave phase (0..1023) 97 | * @param[in] env Envelope value (0..511) 98 | * @warning @a env will not be checked for correct values. 99 | */ 100 | inline int16_t oplSin( uint16_t phase, uint16_t env ) { 101 | uint16_t expVal = sinLog(phase) + (env << 3); 102 | const bool isSigned = expVal & SignBit; 103 | 104 | expVal &= ~SignBit; 105 | // expVal: 0..2137+511*8 = 0..6225 106 | // result: 0..1018+1024 107 | uint32_t result = 0x0400 + sinExpTable[( expVal & 0xff ) ^ 0xFF]; 108 | result <<= 1; 109 | result >>= ( expVal >> 8 ); // exp 110 | 111 | if( isSigned ) { 112 | // -1 for one's complement 113 | return -result - 1; 114 | } else { 115 | return result; 116 | } 117 | } 118 | 119 | void EngineOpl::compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) { 120 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 121 | int32_t gain = gain1; 122 | int32_t phase = phase0; 123 | const int32_t *adder = add ? output : zeros; 124 | 125 | for (int i = 0; i < _N_; i++) { 126 | gain += dgain; 127 | int32_t y = oplSin((phase+input[i]) >> 14, gain); 128 | output[i] = (y << 14) + adder[i]; 129 | phase += freq; 130 | } 131 | 132 | } 133 | void EngineOpl::compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add) { 134 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 135 | int32_t gain = gain1; 136 | int32_t phase = phase0; 137 | const int32_t *adder = add ? output : zeros; 138 | 139 | for (int i = 0; i < _N_; i++) { 140 | gain += dgain; 141 | int32_t y = oplSin(phase >> 14, gain); 142 | output[i] = (y << 14) + adder[i]; 143 | phase += freq; 144 | } 145 | } 146 | 147 | void EngineOpl::compute_fb(int32_t *output, int32_t phase0, int32_t freq, 148 | int32_t gain1, int32_t gain2, 149 | int32_t *fb_buf, int fb_shift, bool add) { 150 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 151 | int32_t gain = gain1; 152 | int32_t phase = phase0; 153 | const int32_t *adder = add ? output : zeros; 154 | int32_t y0 = fb_buf[0]; 155 | int32_t y = fb_buf[1]; 156 | 157 | for (int i = 0; i < _N_; i++) { 158 | gain += dgain; 159 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 160 | y0 = y; 161 | y = oplSin((phase+scaled_fb) >> 14, gain) << 14; 162 | output[i] = y + adder[i]; 163 | phase += freq; 164 | } 165 | 166 | fb_buf[0] = y0; 167 | fb_buf[1] = y; 168 | } 169 | 170 | 171 | void EngineOpl::render(int32_t *output, FmOpParams *params, int algorithm,int32_t *fb_buf, int32_t feedback_shift) { 172 | const int kLevelThresh = 507; // really ???? 173 | const FmAlgorithm alg = algorithms[algorithm]; 174 | bool has_contents[3] = { true, false, false }; 175 | for (int op = 0; op < 6; op++) { 176 | int flags = alg.ops[op]; 177 | bool add = (flags & OUT_BUS_ADD) != 0; 178 | FmOpParams ¶m = params[op]; 179 | int inbus = (flags >> 4) & 3; 180 | int outbus = flags & 3; 181 | int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); 182 | int32_t gain1 = param.gain_out == 0 ? 511 : param.gain_out; 183 | int32_t gain2 = 512-(param.level_in >> 19); 184 | param.gain_out = gain2; 185 | 186 | if (gain1 <= kLevelThresh || gain2 <= kLevelThresh) { 187 | if (!has_contents[outbus]) { 188 | add = false; 189 | } 190 | if (inbus == 0 || !has_contents[inbus]) { 191 | // todo: more than one op in a feedback loop 192 | if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) { 193 | // cout << op << " fb " << inbus << outbus << add << endl; 194 | compute_fb(outptr, param.phase, param.freq, 195 | gain1, gain2, 196 | fb_buf, feedback_shift, add); 197 | } else { 198 | // cout << op << " pure " << inbus << outbus << add << endl; 199 | compute_pure(outptr, param.phase, param.freq, 200 | gain1, gain2, add); 201 | } 202 | } else { 203 | // cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl; 204 | compute(outptr, buf_[inbus - 1].get(), 205 | param.phase, param.freq, gain1, gain2, add); 206 | } 207 | has_contents[outbus] = true; 208 | } else if (!add) { 209 | has_contents[outbus] = false; 210 | } 211 | param.phase += param.freq << LG_N; 212 | } 213 | } 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | -------------------------------------------------------------------------------- /main/EngineOpl.h: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * Copyright (c) 2014 Pascal Gauthier. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 3 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License 16 | * along with this program; if not, write to the Free Software Foundation, 17 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 | * 19 | */ 20 | 21 | #ifndef ENGINEOPL_H_INCLUDED 22 | #define ENGINEOPL_H_INCLUDED 23 | //#include "Arduino.h" 24 | #include "synth.h" 25 | #include "aligned_buf.h" 26 | #include "fm_op_kernel.h" 27 | #include "controllers.h" 28 | #include "fm_core.h" 29 | 30 | 31 | class EngineOpl : public FmCore { 32 | public: 33 | virtual void render(int32_t *output, FmOpParams *params, int algorithm, 34 | int32_t *fb_buf, int32_t feedback_shift); 35 | void compute(int32_t *output, const int32_t *input, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add); 36 | void compute_pure(int32_t *output, int32_t phase0, int32_t freq, int32_t gain1, int32_t gain2, bool add); 37 | void compute_fb(int32_t *output, int32_t phase0, int32_t freq, 38 | int32_t gain1, int32_t gain2, 39 | int32_t *fb_buf, int fb_gain, bool add); 40 | }; 41 | 42 | 43 | 44 | #endif // ENGINEOPL_H_INCLUDED 45 | -------------------------------------------------------------------------------- /main/Kconfig.projbuild: -------------------------------------------------------------------------------- 1 | # put here your custom config value 2 | menu "Example Configuration" 3 | config ESP_WIFI_SSID 4 | string "WiFi SSID" 5 | default "myssid" 6 | help 7 | SSID (network name) for the example to connect to. 8 | 9 | config ESP_WIFI_PASSWORD 10 | string "WiFi Password" 11 | default "mypassword" 12 | help 13 | WiFi password (WPA or WPA2) for the example to use. 14 | endmenu 15 | -------------------------------------------------------------------------------- /main/LiquidCrystalPlus_I2C.h: -------------------------------------------------------------------------------- 1 | /* 2 | MicroDexed 3 | 4 | MicroDexed is a port of the Dexed sound engine 5 | (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6 with audio shield. 6 | Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android 7 | 8 | (c)2018 H. Wirtz 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software Foundation, 22 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | */ 25 | 26 | #include // https://www.arduinolibraries.info/libraries/liquid-crystal-i2-c 27 | 28 | #ifndef LIQUIDCRYSTALPLUS_I2C_H_INCLUDED 29 | #define LIQUIDCRYSTALPLUS_I2C_H_INCLUDED 30 | 31 | #define STRING_BUF_SIZE 21 32 | 33 | class LiquidCrystalPlus_I2C : public LiquidCrystal_I2C 34 | { 35 | public: 36 | 37 | using LiquidCrystal_I2C::LiquidCrystal_I2C; 38 | 39 | void show(uint8_t y, uint8_t x, uint8_t fs, char *str) 40 | { 41 | _show(y, x, fs, str, false, false); 42 | } 43 | 44 | void show(uint8_t y, uint8_t x, uint8_t fs, long num) 45 | { 46 | char _buf10[STRING_BUF_SIZE]; 47 | 48 | _show(y, x, fs, itoa(num, _buf10, 10), true, true); 49 | } 50 | 51 | private: 52 | void _show(uint8_t pos_y, uint8_t pos_x, uint8_t field_size, char *str, bool justify_right, bool fill_zero) 53 | { 54 | { 55 | char tmp[STRING_BUF_SIZE]; 56 | char *s = tmp; 57 | uint8_t l = strlen(str); 58 | 59 | memset(tmp, 0, sizeof(tmp)); 60 | if (fill_zero == true) 61 | memset(tmp, '0', field_size); 62 | else 63 | memset(tmp, 0x20, field_size - 1); // blank 64 | 65 | if (l > field_size) 66 | l = field_size; 67 | 68 | if (justify_right == true) 69 | s += field_size - l; 70 | 71 | strncpy(s, str, l); 72 | 73 | setCursor(pos_x, pos_y); 74 | print(tmp); 75 | 76 | #ifdef DEBUG 77 | Serial.print(pos_y, DEC); 78 | Serial.print(F("/")); 79 | Serial.print(pos_x, DEC); 80 | Serial.print(F(": [")); 81 | Serial.print(tmp); 82 | Serial.println(F("]")); 83 | #endif 84 | } 85 | } 86 | }; 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /main/aligned_buf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // A convenient wrapper for buffers with alignment constraints 18 | 19 | // Note that if we were on C++11, we'd use aligned_storage or somesuch. 20 | 21 | #ifndef __ALIGNED_BUF_H 22 | #define __ALIGNED_BUF_H 23 | 24 | #include 25 | 26 | template 27 | class AlignedBuf { 28 | public: 29 | T *get() { 30 | return (T *)((((intptr_t)storage_) + alignment - 1) & -alignment); 31 | } 32 | private: 33 | unsigned char storage_[size * sizeof(T) + alignment]; 34 | }; 35 | 36 | #endif // __ALIGNED_BUF_H 37 | -------------------------------------------------------------------------------- /main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Main component makefile. 3 | # 4 | # This Makefile can be left empty. By default, it will take the sources in the 5 | # src/ directory, compile them and link them into lib(subdirectory_name).a 6 | # in the build directory. This behaviour is entirely configurable, 7 | # please read the ESP-IDF documents if you need to do this. 8 | # 9 | 10 | CXXFLAGS += "-Wa,-ahl=$*.s" -------------------------------------------------------------------------------- /main/config.h: -------------------------------------------------------------------------------- 1 | /* 2 | MicroDexed 3 | 4 | MicroDexed is a port of the Dexed sound engine 5 | (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6 with audio shield. 6 | Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android 7 | 8 | (c)2018 H. Wirtz 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software Foundation, 22 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | */ 25 | 26 | #include "midinotes.h" 27 | 28 | 29 | // ATTENTION! For better latency you have to redefine AUDIO_BLOCK_SAMPLES from 30 | // 128 to 64 in /cores/teensy3/AudioStream.h 31 | 32 | #ifndef CONFIG_H_INCLUDED 33 | #define CONFIG_H_INCLUDED 34 | 35 | // Initial values 36 | #define VERSION 1.0.0 37 | #define MIDI_DEVICE Serial1 38 | #define USE_ONBOARD_USB_HOST 1 39 | #define MIDI_MERGE_THRU 1 40 | #define TEENSY_AUDIO_BOARD 1 41 | #define VOLUME 0.6 42 | #define DEFAULT_MIDI_CHANNEL MIDI_CHANNEL_OMNI 43 | #define DEFAULT_SYSEXBANK 0 44 | #define DEFAULT_SYSEXSOUND 0 45 | //#define DEXED_ENGINE DEXED_ENGINE_MODERN 46 | // #ifndef TEENSY_AUDIO_BOARD 47 | // #define AUDIO_MEM 450 48 | // #define DELAY_MAX_TIME 600.0 49 | // #else 50 | // #define AUDIO_MEM 900 51 | // #define DELAY_MAX_TIME 1200.0 52 | // #endif 53 | 54 | #define AUDIO_MEM 225 55 | #define DELAY_MAX_TIME 600.0 56 | 57 | #define SAMPLE_RATE 44100 58 | #define MAX_BANKS 100 59 | #define MAX_VOICES 32 // voices per bank 60 | #define BANK_NAME_LEN 13 // FAT12 filenames (plus '\0') 61 | #define VOICE_NAME_LEN 11 // 10 (plus '\0') 62 | 63 | #if !defined(__MK66FX1M0__) // check for Teensy-3.6 64 | //#define MAX_NOTES 11 // No? 65 | #define MAX_NOTES 8 // No? 66 | #undef USE_ONBOARD_USB_HOST 67 | #else 68 | #define MAX_NOTES 16 // Yes 69 | #endif 70 | 71 | // EFFECTS 72 | #define FILTER_MAX_FREQ 10000 73 | 74 | // Master key handling (comment for disabling) 75 | //#define MASTER_KEY_MIDI MIDI_C7 76 | #define MASTER_NUM1 MIDI_C2 77 | 78 | // Debug output 79 | #define SERIAL_SPEED 38400 80 | //#define DEBUG 1 81 | #define SHOW_MIDI_EVENT 1 82 | #define SHOW_XRUN 1 83 | #define SHOW_CPU_LOAD_MSEC 5000 84 | 85 | // Some optimizations 86 | //#define USE_TEENSY_DSP 1 87 | #define SUM_UP_AS_INT 1 88 | #define REDUCE_LOUDNESS 1 89 | 90 | // Enable TEST_NOTE for adding code to drop some midi notes for testing without keyboard 91 | //#define TEST_NOTE MIDI_E2 92 | #define TEST_VEL_MIN 60 93 | #define TEST_VEL_MAX 110 94 | 95 | // Use these with the Teensy Audio Shield 96 | //#define SDCARD_CS_PIN 10 97 | //#define SDCARD_MOSI_PIN 7 98 | //#define SDCARD_SCK_PIN 14 99 | // Use these with the Teensy 3.5 & 3.6 SD card 100 | #define SDCARD_CS_PIN BUILTIN_SDCARD 101 | #define SDCARD_MOSI_PIN 11 // not actually used 102 | #define SDCARD_SCK_PIN 13 // not actually used 103 | 104 | // LCD Display 105 | #define I2C_DISPLAY 1 106 | // [I2C] SCL: Pin 19, SDA: Pin 18 (https://www.pjrc.com/teensy/td_libs_Wire.html) 107 | #define LCD_I2C_ADDRESS 0x27 108 | #define LCD_CHARS 16 109 | #define LCD_LINES 2 110 | #define UI_AUTO_BACK_MS 3000 111 | #define AUTOSTORE_MS 5000 112 | #define AUTOSTORE_FAST_MS 50 113 | 114 | // Encoder with button 115 | #define ENC_VOL_STEPS 43 116 | #define ENC_FILTER_FRQ_STEPS 50 117 | #define ENC_FILTER_RES_STEPS 35 118 | #define ENC_FILTER_OCT_STEPS 27 119 | #define ENC_DELAY_TIME_STEPS 50 120 | #define ENC_DELAY_FB_STEPS 35 121 | #define ENC_DELAY_VOLUME_STEPS 50 122 | #define TIMER_UI_HANDLING_MS 100 123 | #define NUM_ENCODER 2 124 | #define ENC_L_PIN_A 3 125 | #define ENC_L_PIN_B 2 126 | #define BUT_L_PIN 4 127 | #define INITIAL_ENC_L_VALUE 0 128 | #define ENC_R_PIN_A 28 129 | #define ENC_R_PIN_B 29 130 | #define BUT_R_PIN 30 131 | #define INITIAL_ENC_R_VALUE 0 132 | #define BUT_DEBOUNCE_MS 20 133 | #define LONG_BUTTON_PRESS 500 134 | 135 | // EEPROM address 136 | #define EEPROM_OFFSET 0 137 | #define EEPROM_DATA_LENGTH 6 138 | 139 | #define EEPROM_CRC32_ADDR EEPROM.length()-sizeof(uint32_t) 140 | #define EEPROM_BANK_ADDR 0 141 | #define EEPROM_VOICE_ADDR 1 142 | #define EEPROM_MASTER_VOLUME_ADDR 2 143 | #define EEPROM_VOLUME_RIGHT_ADDR 3 144 | #define EEPROM_VOLUME_LEFT_ADDR 4 145 | #define EEPROM_MIDICHANNEL_ADDR 5 146 | 147 | #define EEPROM_UPDATE_BANK (1<<0) 148 | #define EEPROM_UPDATE_VOICE (1<<1) 149 | #define EEPROM_UPDATE_VOL (1<<2) 150 | #define EEPROM_UPDATE_VOL_R (1<<3) 151 | #define EEPROM_UPDATE_VOL_L (1<<4) 152 | #define EEPROM_UPDATE_MIDICHANNEL (1<<5) 153 | #define EEPROM_UPDATE_CHECKSUM (1<<7) 154 | #endif 155 | -------------------------------------------------------------------------------- /main/controllers.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2013 Google Inc. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef __CONTROLLERS_H 18 | #define __CONTROLLERS_H 19 | 20 | //#include 21 | #include "synth.h" 22 | #include 23 | #include 24 | 25 | #ifdef _WIN32 26 | #define snprintf _snprintf 27 | #endif 28 | 29 | // State of MIDI controllers 30 | const int kControllerPitch = 0; 31 | const int kControllerPitchRange = 1; 32 | const int kControllerPitchStep = 2; 33 | 34 | class FmCore; 35 | 36 | struct FmMod { 37 | uint8_t range; 38 | bool pitch; 39 | bool amp; 40 | bool eg; 41 | 42 | FmMod() { 43 | range = 0; 44 | pitch = false; 45 | amp = false; 46 | eg = false; 47 | } 48 | 49 | void setRange(uint8_t r) { 50 | range = r < 0 && r > 127 ? 0 : r; 51 | } 52 | 53 | void setTarget(uint8_t assign) { 54 | assign = assign < 0 && assign > 7 ? 0 : assign; 55 | pitch = assign & 1; // AMP 56 | amp = assign & 2; // PITCH 57 | eg = assign & 4; // EG 58 | } 59 | }; 60 | 61 | class Controllers { 62 | void applyMod(int cc, FmMod &mod) { 63 | float range = 0.01 * mod.range; 64 | uint8_t total = (float)cc * range; 65 | if (mod.amp) 66 | amp_mod = max(amp_mod, total); 67 | 68 | if (mod.pitch) 69 | pitch_mod = max(pitch_mod, total); 70 | 71 | if (mod.eg) 72 | eg_mod = max(eg_mod, total); 73 | } 74 | 75 | public: 76 | int32_t values_[3]; 77 | 78 | uint8_t amp_mod; 79 | uint8_t pitch_mod; 80 | uint8_t eg_mod; 81 | 82 | uint8_t aftertouch_cc; 83 | uint8_t breath_cc; 84 | uint8_t foot_cc; 85 | uint8_t modwheel_cc; 86 | 87 | int masterTune; 88 | 89 | uint8_t opSwitch; 90 | 91 | FmMod wheel; 92 | FmMod foot; 93 | FmMod breath; 94 | FmMod at; 95 | 96 | Controllers() { 97 | amp_mod = 0; 98 | pitch_mod = 0; 99 | eg_mod = 0; 100 | } 101 | 102 | void refresh() { 103 | amp_mod = pitch_mod = eg_mod = 0; 104 | 105 | applyMod(modwheel_cc, wheel); 106 | applyMod(breath_cc, breath); 107 | applyMod(foot_cc, foot); 108 | applyMod(aftertouch_cc, at); 109 | 110 | if ( ! ((wheel.eg || foot.eg) || (breath.eg || at.eg)) ) 111 | eg_mod = 127; 112 | } 113 | 114 | FmCore *core; 115 | }; 116 | 117 | #endif // __CONTROLLERS_H 118 | -------------------------------------------------------------------------------- /main/dexed.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | MicroDexed 3 | 4 | MicroDexed is a port of the Dexed sound engine 5 | (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6 with audio shield. 6 | Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android 7 | 8 | (c)2018 H. Wirtz 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software Foundation, 22 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | */ 25 | 26 | #include "freertos/FreeRTOS.h" 27 | #include "freertos/task.h" 28 | 29 | #include "synth.h" 30 | #include "dexed.h" 31 | #include "config.h" 32 | #include "EngineMkI.h" 33 | #include "EngineOpl.h" 34 | #include "fm_core.h" 35 | #include "exp2.h" 36 | #include "sin.h" 37 | #include "freqlut.h" 38 | #include "controllers.h" 39 | #include 40 | #include 41 | // #ifdef USE_TEENSY_DSP 42 | // #include 43 | // #endif 44 | 45 | 46 | Dexed::Dexed(int rate) 47 | { 48 | uint8_t i; 49 | 50 | Exp2::init(); 51 | Tanh::init(); 52 | Sin::init(); 53 | 54 | Freqlut::init(rate); 55 | Lfo::init(rate); 56 | PitchEnv::init(rate); 57 | Env::init_sr(rate); 58 | 59 | engineMkI = new EngineMkI; 60 | engineOpl = new EngineOpl; 61 | engineMsfa = new FmCore; 62 | 63 | for (i = 0; i < MAX_ACTIVE_NOTES; i++) { 64 | voices[i].dx7_note = new Dx7Note; 65 | voices[i].keydown = false; 66 | voices[i].sustained = false; 67 | voices[i].live = false; 68 | } 69 | 70 | max_notes = 16; 71 | currentNote = 0; 72 | resetControllers(); 73 | controllers.masterTune = 0; 74 | controllers.opSwitch = 0x3f; // enable all operators 75 | //controllers.opSwitch=0x00; 76 | 77 | lfo.reset(data + 137); 78 | 79 | setMonoMode(false); 80 | 81 | sustain = false; 82 | 83 | setEngineType(DEXED_ENGINE_MODERN); 84 | } 85 | 86 | Dexed::~Dexed() 87 | { 88 | currentNote = -1; 89 | 90 | for (uint8_t note = 0; note < MAX_ACTIVE_NOTES; note++) 91 | delete voices[note].dx7_note; 92 | 93 | delete(engineMsfa); 94 | delete(engineOpl); 95 | delete(engineMkI); 96 | } 97 | 98 | void Dexed::activate(void) 99 | { 100 | panic(); 101 | controllers.values_[kControllerPitchRange] = data[155]; 102 | controllers.values_[kControllerPitchStep] = data[156]; 103 | controllers.refresh(); 104 | } 105 | 106 | void Dexed::deactivate(void) 107 | { 108 | panic(); 109 | } 110 | 111 | void Dexed::getSamples(uint16_t n_samples, int16_t* buffer) 112 | { 113 | uint16_t i; 114 | 115 | if (refreshVoice) { 116 | for (i = 0; i < max_notes; i++) { 117 | if ( voices[i].live ) 118 | voices[i].dx7_note->update(data, voices[i].midi_note, voices[i].velocity); 119 | } 120 | lfo.reset(data + 137); 121 | refreshVoice = false; 122 | } 123 | 124 | for (i = 0; i < n_samples; i += _N_) { 125 | AlignedBuf audiobuf; 126 | #ifndef SUM_UP_AS_INT 127 | float sumbuf[_N_]; 128 | #endif 129 | 130 | for (uint8_t j = 0; j < _N_; ++j) { 131 | audiobuf.get()[j] = 0; 132 | #ifndef SUM_UP_AS_INT 133 | sumbuf[j] = 0.0; 134 | #else 135 | buffer[i + j] = 0; 136 | #endif 137 | } 138 | 139 | int32_t lfovalue = lfo.getsample(); 140 | int32_t lfodelay = lfo.getdelay(); 141 | #ifdef SUM_UP_AS_INT 142 | int32_t sum; 143 | #endif 144 | 145 | for (uint8_t note = 0; note < max_notes; ++note) { 146 | if (voices[note].live) { 147 | voices[note].dx7_note->compute(audiobuf.get(), lfovalue, lfodelay, &controllers); 148 | for (uint8_t j = 0; j < _N_; ++j) { 149 | int32_t val = audiobuf.get()[j]; 150 | val = val >> 4; 151 | #ifdef USE_TEENSY_DSP 152 | int32_t clip_val = signed_saturate_rshift(val, 24, 9); 153 | #else 154 | //int32_t clip_val = val < -(1 << 24) ? 0x8000 : val >= (1 << 24) ? 0x7fff : val >> 9; 155 | 156 | // ARM SSAT instruction: 157 | // variable is shifted by 9. 158 | // then saturate to the negative of the desired value. 159 | // and saturate to the positive of the desired value. 160 | 161 | int32_t clip_val; 162 | val = val >> 9; 163 | // This is for ESP32! 164 | asm volatile("clamps a8, a9, 15" : "=r" (clip_val) 165 | : "r" (val) 166 | ); 167 | 168 | #endif 169 | #ifdef SUM_UP_AS_INT 170 | //sum = buffer[i + j] + (clip_val >> REDUCE_LOUDNESS)*(float(data[DEXED_GLOBAL_PARAMETER_OFFSET+DEXED_VOICE_VOLUME])/255); 171 | sum = buffer[i + j] + (clip_val >> REDUCE_LOUDNESS); 172 | if (buffer[i + j] > 0 && clip_val > 0 && sum < 0) 173 | { 174 | sum = INT_MAX; 175 | overload++; 176 | } 177 | else if (buffer[i + j] < 0 && clip_val < 0 && sum > 0) 178 | { 179 | sum = INT_MIN; 180 | overload++; 181 | } 182 | buffer[i + j] = sum; 183 | audiobuf.get()[j] = 0; 184 | #else 185 | float f = static_cast(clip_val >> REDUCE_LOUDNESS) / 0x8000; 186 | if (f > 1) 187 | { 188 | f = 1; 189 | overload++; 190 | } 191 | else if (f < -1) 192 | { 193 | f = -1; 194 | overload++; 195 | } 196 | sumbuf[j] += f; 197 | audiobuf.get()[j] = 0; 198 | #endif 199 | } 200 | } 201 | } 202 | #ifndef SUM_UP_AS_INT 203 | for (uint8_t j = 0; j < _N_; ++j) { 204 | buffer[i + j] = static_cast(sumbuf[j] * 0x8000); 205 | } 206 | #endif 207 | } 208 | } 209 | 210 | bool Dexed::processMidiMessage(uint8_t type, uint8_t data1, uint8_t data2) 211 | { 212 | switch (type & 0xf0) { 213 | case 0x80 : 214 | keyup(data1); 215 | return (false); 216 | break; 217 | case 0x90 : 218 | keydown(data1, data2); 219 | return (false); 220 | break; 221 | case 0xb0 : { 222 | uint8_t ctrl = data1; 223 | uint8_t value = data2; 224 | 225 | switch (ctrl) { 226 | case 0: // ignore BankSelect MSB 227 | break; 228 | case 1: 229 | controllers.modwheel_cc = value; 230 | controllers.refresh(); 231 | break; 232 | case 2: 233 | controllers.breath_cc = value; 234 | controllers.refresh(); 235 | break; 236 | case 4: 237 | controllers.foot_cc = value; 238 | controllers.refresh(); 239 | break; 240 | case 7: // Volume 241 | vol = float(value) / 0x7f; 242 | set_volume(vol, vol_left, vol_right); 243 | break; 244 | case 10: // Pan 245 | if (value < 64) 246 | { 247 | vol_left = 1.0; 248 | vol_right = float(value) / 0x40; 249 | set_volume(vol, vol_left, vol_right); 250 | } 251 | else if (value > 64) 252 | { 253 | vol_left = float(0x7f - value) / 0x40; 254 | vol_right = 1.0; 255 | set_volume(vol, vol_left, vol_right); 256 | } 257 | else 258 | { 259 | vol_left = 1.0; 260 | vol_right = 1.0; 261 | set_volume(vol, vol_left, vol_right); 262 | } 263 | break; 264 | case 32: // BankSelect LSB 265 | bank = data2; 266 | break; 267 | case 64: 268 | sustain = value > 63; 269 | if (!sustain) { 270 | for (uint8_t note = 0; note < max_notes; note++) { 271 | if (voices[note].sustained && !voices[note].keydown) { 272 | voices[note].dx7_note->keyup(); 273 | voices[note].sustained = false; 274 | } 275 | } 276 | } 277 | break; 278 | case 120: 279 | panic(); 280 | return (true); 281 | break; 282 | case 121: 283 | resetControllers(); 284 | return (true); 285 | break; 286 | case 123: 287 | notesOff(); 288 | return (true); 289 | break; 290 | case 126: 291 | setMonoMode(true); 292 | return (true); 293 | break; 294 | case 127: 295 | setMonoMode(false); 296 | return (true); 297 | break; 298 | } 299 | break; 300 | } 301 | 302 | case 0xc0 : // ProgramChange 303 | load_sysex(bank, data1); 304 | break; 305 | 306 | // channel aftertouch 307 | case 0xd0 : 308 | controllers.aftertouch_cc = data1; 309 | controllers.refresh(); 310 | break; 311 | // pitchbend 312 | case 0xe0 : 313 | controllers.values_[kControllerPitch] = data1 | (data2 << 7); 314 | break; 315 | 316 | default: 317 | break; 318 | } 319 | 320 | return (false); 321 | } 322 | 323 | void Dexed::keydown(uint8_t pitch, uint8_t velo) { 324 | if ( velo == 0 ) { 325 | keyup(pitch); 326 | return; 327 | } 328 | 329 | pitch += data[144] - 24; 330 | 331 | uint8_t note = currentNote; 332 | uint8_t keydown_counter = 0; 333 | 334 | for (uint8_t i = 0; i < max_notes; i++) { 335 | if (!voices[note].keydown) { 336 | currentNote = (note + 1) % max_notes; 337 | voices[note].midi_note = pitch; 338 | voices[note].velocity = velo; 339 | voices[note].sustained = sustain; 340 | voices[note].keydown = true; 341 | voices[note].dx7_note->init(data, (int)pitch, (int)velo); 342 | if ( data[136] ) 343 | voices[note].dx7_note->oscSync(); 344 | break; 345 | } 346 | else 347 | keydown_counter++; 348 | 349 | note = (note + 1) % max_notes; 350 | } 351 | 352 | if (keydown_counter == 0) 353 | lfo.keydown(); 354 | if ( monoMode ) { 355 | for (uint8_t i = 0; i < max_notes; i++) { 356 | if ( voices[i].live ) { 357 | // all keys are up, only transfer signal 358 | if ( ! voices[i].keydown ) { 359 | voices[i].live = false; 360 | voices[note].dx7_note->transferSignal(*voices[i].dx7_note); 361 | break; 362 | } 363 | if ( voices[i].midi_note < pitch ) { 364 | voices[i].live = false; 365 | voices[note].dx7_note->transferState(*voices[i].dx7_note); 366 | break; 367 | } 368 | return; 369 | } 370 | } 371 | } 372 | 373 | voices[note].live = true; 374 | } 375 | 376 | void Dexed::keyup(uint8_t pitch) { 377 | pitch += data[144] - 24; 378 | 379 | uint8_t note; 380 | for (note = 0; note < max_notes; ++note) { 381 | if ( voices[note].midi_note == pitch && voices[note].keydown ) { 382 | voices[note].keydown = false; 383 | break; 384 | } 385 | } 386 | 387 | // note not found ? 388 | if ( note >= max_notes ) { 389 | return; 390 | } 391 | 392 | if ( monoMode ) { 393 | int8_t highNote = -1; 394 | int8_t target = 0; 395 | for (int8_t i = 0; i < max_notes; i++) { 396 | if ( voices[i].keydown && voices[i].midi_note > highNote ) { 397 | target = i; 398 | highNote = voices[i].midi_note; 399 | } 400 | } 401 | 402 | if ( highNote != -1 && voices[note].live ) { 403 | voices[note].live = false; 404 | voices[target].live = true; 405 | voices[target].dx7_note->transferState(*voices[note].dx7_note); 406 | } 407 | } 408 | 409 | if ( sustain ) { 410 | voices[note].sustained = true; 411 | } else { 412 | voices[note].dx7_note->keyup(); 413 | } 414 | } 415 | 416 | void Dexed::doRefreshVoice(void) 417 | { 418 | refreshVoice = true; 419 | } 420 | void Dexed::setOPs(uint8_t ops) 421 | { 422 | controllers.opSwitch = ops; 423 | } 424 | 425 | /* 426 | void Dexed::onParam(uint8_t param_num, float param_val) 427 | { 428 | int32_t tune; 429 | 430 | if (param_val != data_float[param_num]) 431 | { 432 | #ifdef DEBUG 433 | uint8_t tmp = data[param_num]; 434 | #endif 435 | 436 | _param_change_counter++; 437 | 438 | if (param_num == 144 || param_num == 134 || param_num == 172) 439 | panic(); 440 | 441 | refreshVoice = true; 442 | data[param_num] = static_cast(param_val); 443 | data_float[param_num] = param_val; 444 | 445 | switch (param_num) 446 | { 447 | case 155: 448 | controllers.values_[kControllerPitchRange] = data[param_num]; 449 | break; 450 | case 156: 451 | controllers.values_[kControllerPitchStep] = data[param_num]; 452 | break; 453 | case 157: 454 | controllers.wheel.setRange(data[param_num]); 455 | controllers.wheel.setTarget(data[param_num + 1]); 456 | controllers.refresh(); 457 | break; 458 | case 158: 459 | controllers.wheel.setRange(data[param_num - 1]); 460 | controllers.wheel.setTarget(data[param_num]); 461 | controllers.refresh(); 462 | break; 463 | case 159: 464 | controllers.foot.setRange(data[param_num]); 465 | controllers.foot.setTarget(data[param_num + 1]); 466 | controllers.refresh(); 467 | break; 468 | case 160: 469 | controllers.foot.setRange(data[param_num - 1]); 470 | controllers.foot.setTarget(data[param_num]); 471 | controllers.refresh(); 472 | break; 473 | case 161: 474 | controllers.breath.setRange(data[param_num]); 475 | controllers.breath.setTarget(data[param_num + 1]); 476 | controllers.refresh(); 477 | break; 478 | case 162: 479 | controllers.breath.setRange(data[param_num - 1]); 480 | controllers.breath.setTarget(data[param_num]); 481 | controllers.refresh(); 482 | break; 483 | case 163: 484 | controllers.at.setRange(data[param_num]); 485 | controllers.at.setTarget(data[param_num + 1]); 486 | controllers.refresh(); 487 | break; 488 | case 164: 489 | controllers.at.setRange(data[param_num - 1]); 490 | controllers.at.setTarget(data[param_num]); 491 | controllers.refresh(); 492 | break; 493 | case 165: 494 | tune = param_val * 0x4000; 495 | controllers.masterTune = (tune << 11) * (1.0 / 12); 496 | break; 497 | case 166: 498 | case 167: 499 | case 168: 500 | case 169: 501 | case 170: 502 | case 171: 503 | controllers.opSwitch = (data[166] << 5) | (data[167] << 4) | (data[168] << 3) | (data[169] << 2) | (data[170] << 1) | data[171]; 504 | break; 505 | case 172: 506 | max_notes = data[param_num]; 507 | break; 508 | } 509 | 510 | } 511 | } 512 | */ 513 | 514 | uint8_t Dexed::getEngineType() { 515 | return engineType; 516 | } 517 | 518 | void Dexed::setEngineType(uint8_t tp) { 519 | if (engineType == tp) 520 | return; 521 | 522 | switch (tp) { 523 | case DEXED_ENGINE_MARKI: 524 | controllers.core = engineMkI; 525 | break; 526 | case DEXED_ENGINE_OPL: 527 | controllers.core = engineOpl; 528 | break; 529 | default: 530 | controllers.core = engineMsfa; 531 | tp = DEXED_ENGINE_MODERN; 532 | break; 533 | } 534 | engineType = tp; 535 | panic(); 536 | controllers.refresh(); 537 | } 538 | 539 | bool Dexed::isMonoMode(void) { 540 | return monoMode; 541 | } 542 | 543 | void Dexed::setMonoMode(bool mode) { 544 | if (monoMode == mode) 545 | return; 546 | 547 | monoMode = mode; 548 | } 549 | 550 | void Dexed::panic(void) { 551 | for (uint8_t i = 0; i < MAX_ACTIVE_NOTES; i++) 552 | { 553 | if (voices[i].live == true) { 554 | voices[i].keydown = false; 555 | voices[i].live = false; 556 | voices[i].sustained = false; 557 | if ( voices[i].dx7_note != NULL ) { 558 | voices[i].dx7_note->oscSync(); 559 | } 560 | } 561 | } 562 | } 563 | 564 | void Dexed::resetControllers(void) 565 | { 566 | controllers.values_[kControllerPitch] = 0x2000; 567 | controllers.values_[kControllerPitchRange] = 0; 568 | controllers.values_[kControllerPitchStep] = 0; 569 | controllers.modwheel_cc = 0; 570 | controllers.foot_cc = 0; 571 | controllers.breath_cc = 0; 572 | controllers.aftertouch_cc = 0; 573 | controllers.refresh(); 574 | } 575 | 576 | void Dexed::notesOff(void) { 577 | for (uint8_t i = 0; i < MAX_ACTIVE_NOTES; i++) { 578 | if (voices[i].live == true && voices[i].keydown == true) { 579 | voices[i].keydown = false; 580 | } 581 | } 582 | } 583 | 584 | void Dexed::setMaxNotes(uint8_t n) { 585 | if (n <= MAX_ACTIVE_NOTES) 586 | { 587 | notesOff(); 588 | max_notes = n; 589 | panic(); 590 | controllers.refresh(); 591 | } 592 | } 593 | 594 | bool Dexed::loadSysexVoice(uint8_t* new_data) 595 | { 596 | uint8_t* p_data = data; 597 | uint8_t op; 598 | uint8_t tmp; 599 | 600 | //notesOff(); 601 | 602 | for (op = 0; op < 6; op++) 603 | { 604 | // DEXED_OP_EG_R1, // 0 605 | // DEXED_OP_EG_R2, // 1 606 | // DEXED_OP_EG_R3, // 2 607 | // DEXED_OP_EG_R4, // 3 608 | // DEXED_OP_EG_L1, // 4 609 | // DEXED_OP_EG_L2, // 5 610 | // DEXED_OP_EG_L3, // 6 611 | // DEXED_OP_EG_L4, // 7 612 | // DEXED_OP_LEV_SCL_BRK_PT, // 8 613 | // DEXED_OP_SCL_LEFT_DEPTH, // 9 614 | // DEXED_OP_SCL_RGHT_DEPTH, // 10 615 | memcpy(&data[op * 21], &new_data[op * 17], 11); 616 | tmp = new_data[(op * 17) + 11]; 617 | *(p_data + DEXED_OP_SCL_LEFT_CURVE + (op * 21)) = (tmp & 0x3); 618 | *(p_data + DEXED_OP_SCL_RGHT_CURVE + (op * 21)) = (tmp & 0x0c) >> 2; 619 | tmp = new_data[(op * 17) + 12]; 620 | *(p_data + DEXED_OP_OSC_DETUNE + (op * 21)) = (tmp & 0x78) >> 3; 621 | *(p_data + DEXED_OP_OSC_RATE_SCALE + (op * 21)) = (tmp & 0x07); 622 | tmp = new_data[(op * 17) + 13]; 623 | *(p_data + DEXED_OP_KEY_VEL_SENS + (op * 21)) = (tmp & 0x1c) >> 2; 624 | *(p_data + DEXED_OP_AMP_MOD_SENS + (op * 21)) = (tmp & 0x03); 625 | *(p_data + DEXED_OP_OUTPUT_LEV + (op * 21)) = new_data[(op * 17) + 14]; 626 | tmp = new_data[(op * 17) + 15]; 627 | *(p_data + DEXED_OP_FREQ_COARSE + (op * 21)) = (tmp & 0x3e) >> 1; 628 | *(p_data + DEXED_OP_OSC_MODE + (op * 21)) = (tmp & 0x01); 629 | *(p_data + DEXED_OP_FREQ_FINE + (op * 21)) = new_data[(op * 17) + 16]; 630 | } 631 | // DEXED_PITCH_EG_R1, // 0 632 | // DEXED_PITCH_EG_R2, // 1 633 | // DEXED_PITCH_EG_R3, // 2 634 | // DEXED_PITCH_EG_R4, // 3 635 | // DEXED_PITCH_EG_L1, // 4 636 | // DEXED_PITCH_EG_L2, // 5 637 | // DEXED_PITCH_EG_L3, // 6 638 | // DEXED_PITCH_EG_L4, // 7 639 | memcpy(&data[DEXED_VOICE_OFFSET], &new_data[102], 8); 640 | tmp = new_data[110]; 641 | *(p_data + DEXED_VOICE_OFFSET + DEXED_ALGORITHM) = (tmp & 0x1f); 642 | tmp = new_data[111]; 643 | *(p_data + DEXED_VOICE_OFFSET + DEXED_OSC_KEY_SYNC) = (tmp & 0x08) >> 3; 644 | *(p_data + DEXED_VOICE_OFFSET + DEXED_FEEDBACK) = (tmp & 0x07); 645 | // DEXED_LFO_SPEED, // 11 646 | // DEXED_LFO_DELAY, // 12 647 | // DEXED_LFO_PITCH_MOD_DEP, // 13 648 | // DEXED_LFO_AMP_MOD_DEP, // 14 649 | memcpy(&data[DEXED_VOICE_OFFSET + DEXED_LFO_SPEED], &new_data[112], 4); 650 | tmp = new_data[116]; 651 | *(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_PITCH_MOD_SENS) = (tmp & 0x30) >> 4; 652 | *(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_WAVE) = (tmp & 0x0e) >> 1; 653 | *(p_data + DEXED_VOICE_OFFSET + DEXED_LFO_SYNC) = (tmp & 0x01); 654 | *(p_data + DEXED_VOICE_OFFSET + DEXED_TRANSPOSE) = new_data[117]; 655 | memcpy(&data[DEXED_VOICE_OFFSET + DEXED_NAME], &new_data[118], 10); 656 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PITCHBEND_RANGE) = 1; 657 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PITCHBEND_STEP) = 1; 658 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MODWHEEL_RANGE) = 99; 659 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MODWHEEL_ASSIGN) = 7; 660 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_FOOTCTRL_RANGE) = 99; 661 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_FOOTCTRL_ASSIGN) = 7; 662 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_BREATHCTRL_RANGE) = 99; 663 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_BREATHCTRL_ASSIGN) = 7; 664 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_RANGE) = 99; 665 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_ASSIGN) = 7; 666 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MASTER_TUNE) = 0; 667 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP1_ENABLE) = 1; 668 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP2_ENABLE) = 1; 669 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP3_ENABLE) = 1; 670 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP4_ENABLE) = 1; 671 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP5_ENABLE) = 1; 672 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP6_ENABLE) = 1; 673 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MAX_NOTES) = MAX_NOTES; 674 | 675 | controllers.values_[kControllerPitchRange] = data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PITCHBEND_RANGE]; 676 | controllers.values_[kControllerPitchStep] = data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_PITCHBEND_STEP]; 677 | controllers.wheel.setRange(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MODWHEEL_RANGE]); 678 | controllers.wheel.setTarget(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MODWHEEL_ASSIGN]); 679 | controllers.foot.setRange(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_FOOTCTRL_RANGE]); 680 | controllers.foot.setTarget(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_FOOTCTRL_ASSIGN]); 681 | controllers.breath.setRange(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_BREATHCTRL_RANGE]); 682 | controllers.breath.setTarget(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_BREATHCTRL_ASSIGN]); 683 | controllers.at.setRange(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_RANGE]); 684 | controllers.at.setTarget(data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_AT_ASSIGN]); 685 | controllers.masterTune = (data[DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MASTER_TUNE] * 0x4000 << 11) * (1.0 / 12); 686 | controllers.refresh(); 687 | 688 | setOPs((*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP1_ENABLE) << 5) | 689 | (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP2_ENABLE) << 4) | 690 | (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP3_ENABLE) << 3) | 691 | (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP4_ENABLE) << 2) | 692 | (*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP5_ENABLE) << 1) | 693 | *(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_OP6_ENABLE )); 694 | setMaxNotes(*(p_data + DEXED_GLOBAL_PARAMETER_OFFSET + DEXED_MAX_NOTES)); 695 | //panic(); 696 | doRefreshVoice(); 697 | //activate(); 698 | 699 | strncpy(voice_name, (char *)&data[145], sizeof(voice_name) - 1); 700 | 701 | #ifdef DEBUG 702 | //char voicename[11]; 703 | //memset(voicename, 0, sizeof(voicename)); 704 | //strncpy(voicename, (char *)&data[145], sizeof(voicename) - 1); 705 | 706 | Serial.print(F("Voice [")); 707 | //Serial.print(voicename); 708 | Serial.print(voice_name); 709 | Serial.println(F("] loaded.")); 710 | #endif 711 | 712 | return (true); 713 | } 714 | -------------------------------------------------------------------------------- /main/dexed.h: -------------------------------------------------------------------------------- 1 | /* 2 | MicroDexed 3 | 4 | MicroDexed is a port of the Dexed sound engine 5 | (https://github.com/asb2m10/dexed) for the Teensy-3.5/3.6 with audio shield. 6 | Dexed ist heavily based on https://github.com/google/music-synthesizer-for-android 7 | 8 | (c)2018 H. Wirtz 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software Foundation, 22 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 | 24 | */ 25 | 26 | #ifndef DEXED_H_INCLUDED 27 | #define DEXED_H_INCLUDED 28 | 29 | //#include 30 | #include "controllers.h" 31 | #include "dx7note.h" 32 | #include "lfo.h" 33 | #include "synth.h" 34 | #include "fm_core.h" 35 | #include "EngineMkI.h" 36 | #include "EngineOpl.h" 37 | //#include 38 | #include "config.h" 39 | 40 | 41 | extern uint8_t bank; 42 | extern uint32_t overload; 43 | extern bool load_sysex(uint8_t bank, uint8_t voice_number); 44 | //extern AudioControlSGTL5000 sgtl5000_1; 45 | extern float vol; 46 | extern float vol_right; 47 | extern float vol_left; 48 | extern void set_volume(float master_volume, float volume_right, float volume_left); 49 | extern char voice_name[11]; 50 | 51 | struct ProcessorVoice { 52 | uint8_t midi_note; 53 | uint8_t velocity; 54 | bool keydown; 55 | bool sustained; 56 | bool live; 57 | Dx7Note *dx7_note; 58 | }; 59 | 60 | enum DexedEngineResolution { 61 | DEXED_ENGINE_MODERN, // 0 62 | DEXED_ENGINE_MARKI, // 1 63 | DEXED_ENGINE_OPL // 2 64 | }; 65 | 66 | enum DexedVoiceOPParameters { 67 | DEXED_OP_EG_R1, // 0 68 | DEXED_OP_EG_R2, // 1 69 | DEXED_OP_EG_R3, // 2 70 | DEXED_OP_EG_R4, // 3 71 | DEXED_OP_EG_L1, // 4 72 | DEXED_OP_EG_L2, // 5 73 | DEXED_OP_EG_L3, // 6 74 | DEXED_OP_EG_L4, // 7 75 | DEXED_OP_LEV_SCL_BRK_PT, // 8 76 | DEXED_OP_SCL_LEFT_DEPTH, // 9 77 | DEXED_OP_SCL_RGHT_DEPTH, // 10 78 | DEXED_OP_SCL_LEFT_CURVE, // 11 79 | DEXED_OP_SCL_RGHT_CURVE, // 12 80 | DEXED_OP_OSC_RATE_SCALE, // 13 81 | DEXED_OP_AMP_MOD_SENS, // 14 82 | DEXED_OP_KEY_VEL_SENS, // 15 83 | DEXED_OP_OUTPUT_LEV, // 16 84 | DEXED_OP_OSC_MODE, // 17 85 | DEXED_OP_FREQ_COARSE, // 18 86 | DEXED_OP_FREQ_FINE, // 19 87 | DEXED_OP_OSC_DETUNE // 20 88 | }; 89 | 90 | #define DEXED_VOICE_OFFSET 126 91 | enum DexedVoiceParameters { 92 | DEXED_PITCH_EG_R1, // 0 93 | DEXED_PITCH_EG_R2, // 1 94 | DEXED_PITCH_EG_R3, // 2 95 | DEXED_PITCH_EG_R4, // 3 96 | DEXED_PITCH_EG_L1, // 4 97 | DEXED_PITCH_EG_L2, // 5 98 | DEXED_PITCH_EG_L3, // 6 99 | DEXED_PITCH_EG_L4, // 7 100 | DEXED_ALGORITHM, // 8 101 | DEXED_FEEDBACK, // 9 102 | DEXED_OSC_KEY_SYNC, // 10 103 | DEXED_LFO_SPEED, // 11 104 | DEXED_LFO_DELAY, // 12 105 | DEXED_LFO_PITCH_MOD_DEP, // 13 106 | DEXED_LFO_AMP_MOD_DEP, // 14 107 | DEXED_LFO_SYNC, // 15 108 | DEXED_LFO_WAVE, // 16 109 | DEXED_LFO_PITCH_MOD_SENS, // 17 110 | DEXED_TRANSPOSE, // 18 111 | DEXED_NAME // 19 112 | }; 113 | 114 | #define DEXED_GLOBAL_PARAMETER_OFFSET 155 115 | enum DexedGlobalParameters { 116 | DEXED_PITCHBEND_RANGE, // 0 117 | DEXED_PITCHBEND_STEP, // 1 118 | DEXED_MODWHEEL_RANGE, // 2 119 | DEXED_MODWHEEL_ASSIGN, // 3 120 | DEXED_FOOTCTRL_RANGE, // 4 121 | DEXED_FOOTCTRL_ASSIGN, // 5 122 | DEXED_BREATHCTRL_RANGE, // 6 123 | DEXED_BREATHCTRL_ASSIGN, // 7 124 | DEXED_AT_RANGE, // 8 125 | DEXED_AT_ASSIGN, // 9 126 | DEXED_MASTER_TUNE, // 10 127 | DEXED_OP1_ENABLE, // 11 128 | DEXED_OP2_ENABLE, // 12 129 | DEXED_OP3_ENABLE, // 13 130 | DEXED_OP4_ENABLE, // 14 131 | DEXED_OP5_ENABLE, // 15 132 | DEXED_OP6_ENABLE, // 16 133 | DEXED_MAX_NOTES, // 17 134 | DEXED_VOICE_VOLUME // 18 135 | }; 136 | 137 | // GLOBALS 138 | 139 | //============================================================================== 140 | 141 | class Dexed 142 | { 143 | public: 144 | Dexed(int rate); 145 | ~Dexed(); 146 | void activate(void); 147 | void deactivate(void); 148 | uint8_t getEngineType(); 149 | void setEngineType(uint8_t tp); 150 | bool isMonoMode(void); 151 | void setMonoMode(bool mode); 152 | void getSamples(uint16_t n_samples, int16_t* buffer); 153 | bool processMidiMessage(uint8_t type, uint8_t data1, uint8_t data2); 154 | void panic(void); 155 | void notesOff(void); 156 | void resetControllers(void); 157 | void setMaxNotes(uint8_t n); 158 | void doRefreshVoice(void); 159 | void setOPs(uint8_t ops); 160 | bool loadSysexVoice(uint8_t* data); 161 | 162 | Controllers controllers; 163 | 164 | uint8_t data[173] = { 165 | 95, 29, 20, 50, 99, 95, 00, 00, 41, 00, 19, 00, 00, 03, 00, 06, 79, 00, 01, 00, 14, // OP6 eg_rate_1-4, level_1-4, kbd_lev_scl_brk_pt, kbd_lev_scl_lft_depth, kbd_lev_scl_rht_depth, kbd_lev_scl_lft_curve, kbd_lev_scl_rht_curve, kbd_rate_scaling, amp_mod_sensitivity, key_vel_sensitivity, operator_output_level, osc_mode, osc_freq_coarse, osc_freq_fine, osc_detune 166 | 95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 00, 99, 00, 01, 00, 00, // OP5 167 | 95, 29, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 06, 89, 00, 01, 00, 07, // OP4 168 | 95, 20, 20, 50, 99, 95, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 07, // OP3 169 | 95, 50, 35, 78, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 07, 58, 00, 14, 00, 07, // OP2 170 | 96, 25, 25, 67, 99, 75, 00, 00, 00, 00, 00, 00, 00, 03, 00, 02, 99, 00, 01, 00, 10, // OP1 171 | 94, 67, 95, 60, 50, 50, 50, 50, // 4 * pitch EG rates, 4 *pitch EG level 172 | 04, 06, 00, // algorithm, feedback, osc sync 173 | 34, 33, 00, 00, 00, 04, // lfo speed, lfo delay, lfo pitch_mod_depth, lfo_amp_mod_depth, lfo_sync, lfo_waveform 174 | 03, 24, // pitch_mod_sensitivity, transpose 175 | 69, 68, 80, 56, 85 , 76, 84, 00, 00, 00, // 10 * char for name ("DEFAULT ") 176 | 01, 00, 99, 00, 99, 00, 99, 00, 99, 00, // pitch_bend_range, pitch_bend_step, mod_wheel_range, mod_wheel_assign, foot_ctrl_range, foot_ctrl_assign, breath_ctrl_range, breath_ctrl_assign, aftertouch_range, aftertouch_assign 177 | 00, // master tune 178 | 01, 01, 01, 01, 01, 01, // OP1-6 enable 179 | 16 // number of voices 180 | }; // FM-Piano 181 | 182 | protected: 183 | //void onParam(uint8_t param_num,float param_val); 184 | void keyup(uint8_t pitch); 185 | void keydown(uint8_t pitch, uint8_t velo); 186 | 187 | static const uint8_t MAX_ACTIVE_NOTES = MAX_NOTES; 188 | uint8_t max_notes = MAX_ACTIVE_NOTES; 189 | ProcessorVoice voices[MAX_ACTIVE_NOTES]; 190 | uint8_t currentNote; 191 | bool sustain; 192 | bool monoMode; 193 | bool refreshVoice; 194 | uint8_t engineType; 195 | Lfo lfo; 196 | FmCore* engineMsfa; 197 | EngineMkI* engineMkI; 198 | EngineOpl* engineOpl; 199 | }; 200 | 201 | #endif // PLUGINPROCESSOR_H_INCLUDED 202 | -------------------------------------------------------------------------------- /main/dx7note.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2016-2017 Pascal Gauthier. 3 | Copyright 2012 Google Inc. 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | #include 19 | #include 20 | #include "synth.h" 21 | #include "freqlut.h" 22 | #include "exp2.h" 23 | #include "controllers.h" 24 | #include "dx7note.h" 25 | 26 | const int FEEDBACK_BITDEPTH = 8; 27 | 28 | int32_t midinote_to_logfreq(int midinote) { 29 | //const int32_t base = 50857777; // (1 << 24) * (log(440) / log(2) - 69/12) 30 | const int32_t base = 50857777; // (1 << 24) * (LOG_FUNC(440) / LOG_FUNC(2) - 69/12) 31 | const int32_t step = (1 << 24) / 12; 32 | return base + step * midinote; 33 | } 34 | 35 | const int32_t coarsemul[] = { 36 | -16777216, 0, 16777216, 26591258, 33554432, 38955489, 43368474, 47099600, 37 | 50331648, 53182516, 55732705, 58039632, 60145690, 62083076, 63876816, 38 | 65546747, 67108864, 68576247, 69959732, 71268397, 72509921, 73690858, 39 | 74816848, 75892776, 76922906, 77910978, 78860292, 79773775, 80654032, 40 | 81503396, 82323963, 83117622 41 | }; 42 | 43 | int32_t osc_freq(int midinote, int mode, int coarse, int fine, int detune) { 44 | // TODO: pitch randomization 45 | int32_t logfreq; 46 | if (mode == 0) { 47 | logfreq = midinote_to_logfreq(midinote); 48 | // could use more precision, closer enough for now. those numbers comes from my DX7 49 | //FRAC_NUM detuneRatio = 0.0209 * exp(-0.396 * (((float)logfreq) / (1 << 24))) / 7; 50 | FRAC_NUM detuneRatio = 0.0209 * EXP_FUNC(-0.396 * (((float)logfreq) / (1 << 24))) / 7; 51 | logfreq += detuneRatio * logfreq * (detune - 7); 52 | 53 | logfreq += coarsemul[coarse & 31]; 54 | if (fine) { 55 | // (1 << 24) / log(2) 56 | //logfreq += (int32_t)floor(24204406.323123 * log(1 + 0.01 * fine) + 0.5); 57 | logfreq += (int32_t)floor(24204406.323123 * LOG_FUNC(1 + 0.01 * fine) + 0.5); 58 | } 59 | 60 | // // This was measured at 7.213Hz per count at 9600Hz, but the exact 61 | // // value is somewhat dependent on midinote. Close enough for now. 62 | // //logfreq += 12606 * (detune -7); 63 | } else { 64 | // ((1 << 24) * log(10) / log(2) * .01) << 3 65 | logfreq = (4458616 * ((coarse & 3) * 100 + fine)) >> 3; 66 | logfreq += detune > 7 ? 13457 * (detune - 7) : 0; 67 | } 68 | return logfreq; 69 | } 70 | 71 | const uint8_t velocity_data[64] = { 72 | 0, 70, 86, 97, 106, 114, 121, 126, 132, 138, 142, 148, 152, 156, 160, 163, 73 | 166, 170, 173, 174, 178, 181, 184, 186, 189, 190, 194, 196, 198, 200, 202, 74 | 205, 206, 209, 211, 214, 216, 218, 220, 222, 224, 225, 227, 229, 230, 232, 75 | 233, 235, 237, 238, 240, 241, 242, 243, 244, 246, 246, 248, 249, 250, 251, 76 | 252, 253, 254 77 | }; 78 | 79 | // See "velocity" section of notes. Returns velocity delta in microsteps. 80 | int ScaleVelocity(int velocity, int sensitivity) { 81 | int clamped_vel = max(0, min(127, velocity)); 82 | int vel_value = velocity_data[clamped_vel >> 1] - 239; 83 | int scaled_vel = ((sensitivity * vel_value + 7) >> 3) << 4; 84 | return scaled_vel; 85 | } 86 | 87 | int ScaleRate(int midinote, int sensitivity) { 88 | int x = min(31, max(0, midinote / 3 - 7)); 89 | int qratedelta = (sensitivity * x) >> 3; 90 | #ifdef SUPER_PRECISE 91 | int rem = x & 7; 92 | if (sensitivity == 3 && rem == 3) { 93 | qratedelta -= 1; 94 | } else if (sensitivity == 7 && rem > 0 && rem < 4) { 95 | qratedelta += 1; 96 | } 97 | #endif 98 | return qratedelta; 99 | } 100 | 101 | const uint8_t exp_scale_data[] = { 102 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 14, 16, 19, 23, 27, 33, 39, 47, 56, 66, 103 | 80, 94, 110, 126, 142, 158, 174, 190, 206, 222, 238, 250 104 | }; 105 | 106 | int ScaleCurve(int group, int depth, int curve) { 107 | int scale; 108 | if (curve == 0 || curve == 3) { 109 | // linear 110 | scale = (group * depth * 329) >> 12; 111 | } else { 112 | // exponential 113 | int n_scale_data = sizeof(exp_scale_data); 114 | int raw_exp = exp_scale_data[min(group, n_scale_data - 1)]; 115 | scale = (raw_exp * depth * 329) >> 15; 116 | } 117 | if (curve < 2) { 118 | scale = -scale; 119 | } 120 | return scale; 121 | } 122 | 123 | int ScaleLevel(int midinote, int break_pt, int left_depth, int right_depth, 124 | int left_curve, int right_curve) { 125 | int offset = midinote - break_pt - 17; 126 | if (offset >= 0) { 127 | return ScaleCurve((offset + 1) / 3, right_depth, right_curve); 128 | } else { 129 | return ScaleCurve(-(offset - 1) / 3, left_depth, left_curve); 130 | } 131 | } 132 | 133 | static const uint8_t pitchmodsenstab[] = { 134 | 0, 10, 20, 33, 55, 92, 153, 255 135 | }; 136 | 137 | // 0, 66, 109, 255 138 | static const uint32_t ampmodsenstab[] = { 139 | 0, 4342338, 7171437, 16777216 140 | }; 141 | 142 | Dx7Note::Dx7Note() { 143 | for (int op = 0; op < 6; op++) { 144 | params_[op].phase = 0; 145 | params_[op].gain_out = 0; 146 | } 147 | } 148 | 149 | //void Dx7Note::init(const uint8_t patch[156], int midinote, int velocity) { 150 | void Dx7Note::init(const uint8_t patch[173], int midinote, int velocity) { 151 | int rates[4]; 152 | int levels[4]; 153 | for (int op = 0; op < 6; op++) { 154 | int off = op * 21; 155 | for (int i = 0; i < 4; i++) { 156 | rates[i] = patch[off + i]; 157 | levels[i] = patch[off + 4 + i]; 158 | } 159 | int outlevel = patch[off + 16]; 160 | outlevel = Env::scaleoutlevel(outlevel); 161 | int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], 162 | patch[off + 10], patch[off + 11], patch[off + 12]); 163 | outlevel += level_scaling; 164 | outlevel = min(127, outlevel); 165 | outlevel = outlevel << 5; 166 | outlevel += ScaleVelocity(velocity, patch[off + 15]); 167 | outlevel = max(0, outlevel); 168 | int rate_scaling = ScaleRate(midinote, patch[off + 13]); 169 | env_[op].init(rates, levels, outlevel, rate_scaling); 170 | 171 | int mode = patch[off + 17]; 172 | int coarse = patch[off + 18]; 173 | int fine = patch[off + 19]; 174 | int detune = patch[off + 20]; 175 | int32_t freq = osc_freq(midinote, mode, coarse, fine, detune); 176 | opMode[op] = mode; 177 | basepitch_[op] = freq; 178 | ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; 179 | } 180 | for (int i = 0; i < 4; i++) { 181 | rates[i] = patch[126 + i]; 182 | levels[i] = patch[130 + i]; 183 | } 184 | pitchenv_.set(rates, levels); 185 | algorithm_ = patch[134]; 186 | int feedback = patch[135]; 187 | fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16; 188 | pitchmoddepth_ = (patch[139] * 165) >> 6; 189 | pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; 190 | ampmoddepth_ = (patch[140] * 165) >> 6; 191 | } 192 | 193 | void Dx7Note::compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, const Controllers *ctrls) { 194 | // ==== PITCH ==== 195 | uint32_t pmd = pitchmoddepth_ * lfo_delay; // Q32 196 | int32_t senslfo = pitchmodsens_ * (lfo_val - (1 << 23)); 197 | int32_t pmod_1 = (((int64_t) pmd) * (int64_t) senslfo) >> 39; 198 | pmod_1 = abs(pmod_1); 199 | int32_t pmod_2 = (int32_t)(((int64_t)ctrls->pitch_mod * (int64_t)senslfo) >> 14); 200 | pmod_2 = abs(pmod_2); 201 | int32_t pitch_mod = max(pmod_1, pmod_2); 202 | pitch_mod = pitchenv_.getsample() + (pitch_mod * (senslfo < 0 ? -1 : 1)); 203 | 204 | // ---- PITCH BEND ---- 205 | int pitchbend = ctrls->values_[kControllerPitch]; 206 | int32_t pb = (pitchbend - 0x2000); 207 | if (pb != 0) { 208 | if (ctrls->values_[kControllerPitchStep] == 0) { 209 | pb = ((float) (pb << 11)) * ((float) ctrls->values_[kControllerPitchRange]) / 12.0; 210 | } else { 211 | int stp = 12 / ctrls->values_[kControllerPitchStep]; 212 | pb = pb * stp / 8191; 213 | pb = (pb * (8191 / stp)) << 11; 214 | } 215 | } 216 | int32_t pitch_base = pb + ctrls->masterTune; 217 | pitch_mod += pitch_base; 218 | 219 | // ==== AMP MOD ==== 220 | lfo_val = (1 << 24) - lfo_val; 221 | uint32_t amod_1 = (uint32_t)(((int64_t) ampmoddepth_ * (int64_t) lfo_delay) >> 8); // Q24 :D 222 | amod_1 = (uint32_t)(((int64_t) amod_1 * (int64_t) lfo_val) >> 24); 223 | uint32_t amod_2 = (uint32_t)(((int64_t) ctrls->amp_mod * (int64_t) lfo_val) >> 7); // Q?? :| 224 | uint32_t amd_mod = max(amod_1, amod_2); 225 | 226 | // ==== EG AMP MOD ==== 227 | uint32_t amod_3 = (ctrls->eg_mod + 1) << 17; 228 | amd_mod = max((1 << 24) - amod_3, amd_mod); 229 | 230 | // ==== OP RENDER ==== 231 | for (int op = 0; op < 6; op++) { 232 | // if ( ctrls->opSwitch[op] == '0' ) { 233 | if (!(ctrls->opSwitch & (1 << op))) { 234 | env_[op].getsample(); // advance the envelop even if it is not playing 235 | params_[op].level_in = 0; 236 | } else { 237 | //int32_t gain = pow(2, 10 + level * (1.0 / (1 << 24))); 238 | 239 | if ( opMode[op] ) 240 | params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_base); 241 | else 242 | params_[op].freq = Freqlut::lookup(basepitch_[op] + pitch_mod); 243 | 244 | int32_t level = env_[op].getsample(); 245 | if (ampmodsens_[op] != 0) { 246 | uint32_t sensamp = (uint32_t)(((uint64_t) amd_mod) * ((uint64_t) ampmodsens_[op]) >> 24); 247 | 248 | // TODO: mehhh.. this needs some real tuning. 249 | //uint32_t pt = exp(((float)sensamp) / 262144 * 0.07 + 12.2); 250 | uint32_t pt = EXP_FUNC(((float)sensamp) / 262144 * 0.07 + 12.2); 251 | uint32_t ldiff = (uint32_t)(((uint64_t)level) * (((uint64_t)pt << 4)) >> 28); 252 | level -= ldiff; 253 | } 254 | params_[op].level_in = level; 255 | } 256 | } 257 | ctrls->core->render(buf, params_, algorithm_, fb_buf_, fb_shift_); 258 | } 259 | 260 | void Dx7Note::keyup() { 261 | for (int op = 0; op < 6; op++) { 262 | env_[op].keydown(false); 263 | } 264 | pitchenv_.keydown(false); 265 | } 266 | 267 | //void Dx7Note::update(const uint8_t patch[156], int midinote, int velocity) { 268 | void Dx7Note::update(const uint8_t patch[173], int midinote, int velocity) { 269 | int rates[4]; 270 | int levels[4]; 271 | for (int op = 0; op < 6; op++) { 272 | int off = op * 21; 273 | int mode = patch[off + 17]; 274 | int coarse = patch[off + 18]; 275 | int fine = patch[off + 19]; 276 | int detune = patch[off + 20]; 277 | basepitch_[op] = osc_freq(midinote, mode, coarse, fine, detune); 278 | ampmodsens_[op] = ampmodsenstab[patch[off + 14] & 3]; 279 | opMode[op] = mode; 280 | 281 | for (int i = 0; i < 4; i++) { 282 | rates[i] = patch[off + i]; 283 | levels[i] = patch[off + 4 + i]; 284 | } 285 | int outlevel = patch[off + 16]; 286 | outlevel = Env::scaleoutlevel(outlevel); 287 | int level_scaling = ScaleLevel(midinote, patch[off + 8], patch[off + 9], 288 | patch[off + 10], patch[off + 11], patch[off + 12]); 289 | outlevel += level_scaling; 290 | outlevel = min(127, outlevel); 291 | outlevel = outlevel << 5; 292 | outlevel += ScaleVelocity(velocity, patch[off + 15]); 293 | outlevel = max(0, outlevel); 294 | int rate_scaling = ScaleRate(midinote, patch[off + 13]); 295 | env_[op].update(rates, levels, outlevel, rate_scaling); 296 | } 297 | algorithm_ = patch[134]; 298 | int feedback = patch[135]; 299 | fb_shift_ = feedback != 0 ? FEEDBACK_BITDEPTH - feedback : 16; 300 | pitchmoddepth_ = (patch[139] * 165) >> 6; 301 | pitchmodsens_ = pitchmodsenstab[patch[143] & 7]; 302 | ampmoddepth_ = (patch[140] * 165) >> 6; 303 | } 304 | 305 | void Dx7Note::peekVoiceStatus(VoiceStatus &status) { 306 | for (int i = 0; i < 6; i++) { 307 | status.amp[i] = Exp2::lookup(params_[i].level_in - (14 * (1 << 24))); 308 | env_[i].getPosition(&status.ampStep[i]); 309 | } 310 | pitchenv_.getPosition(&status.pitchStep); 311 | } 312 | 313 | /** 314 | Used in monophonic mode to transfert voice state from different notes 315 | */ 316 | void Dx7Note::transferState(Dx7Note &src) { 317 | for (int i = 0; i < 6; i++) { 318 | env_[i].transfer(src.env_[i]); 319 | params_[i].gain_out = src.params_[i].gain_out; 320 | params_[i].phase = src.params_[i].phase; 321 | } 322 | } 323 | 324 | void Dx7Note::transferSignal(Dx7Note &src) { 325 | for (int i = 0; i < 6; i++) { 326 | params_[i].gain_out = src.params_[i].gain_out; 327 | params_[i].phase = src.params_[i].phase; 328 | } 329 | } 330 | 331 | void Dx7Note::oscSync() { 332 | for (int i = 0; i < 6; i++) { 333 | params_[i].gain_out = 0; 334 | params_[i].phase = 0; 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /main/dx7note.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016-2017 Pascal Gauthier. 3 | * Copyright 2012 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef SYNTH_DX7NOTE_H_ 19 | #define SYNTH_DX7NOTE_H_ 20 | 21 | // This is the logic to put together a note from the MIDI description 22 | // and run the low-level modules. 23 | 24 | // It will continue to evolve a bit, as note-stealing logic, scaling, 25 | // and real-time control of parameters live here. 26 | 27 | //#include 28 | #include "env.h" 29 | #include "pitchenv.h" 30 | #include "fm_core.h" 31 | 32 | struct VoiceStatus { 33 | uint32_t amp[6]; 34 | char ampStep[6]; 35 | char pitchStep; 36 | }; 37 | 38 | class Dx7Note { 39 | public: 40 | Dx7Note(); 41 | void init(const uint8_t patch[156], int midinote, int velocity); 42 | 43 | // Note: this _adds_ to the buffer. Interesting question whether it's 44 | // worth it... 45 | void compute(int32_t *buf, int32_t lfo_val, int32_t lfo_delay, 46 | const Controllers *ctrls); 47 | 48 | void keyup(); 49 | 50 | // TODO: some way of indicating end-of-note. Maybe should be a return 51 | // value from the compute method? (Having a count return from keyup 52 | // is also tempting, but if there's a dynamic parameter change after 53 | // keyup, that won't work. 54 | 55 | // PG:add the update 56 | void update(const uint8_t patch[156], int midinote, int velocity); 57 | void peekVoiceStatus(VoiceStatus &status); 58 | void transferState(Dx7Note& src); 59 | void transferSignal(Dx7Note &src); 60 | void oscSync(); 61 | 62 | private: 63 | Env env_[6]; 64 | FmOpParams params_[6]; 65 | PitchEnv pitchenv_; 66 | int32_t basepitch_[6]; 67 | int32_t fb_buf_[2]; 68 | int32_t fb_shift_; 69 | int32_t ampmodsens_[6]; 70 | int32_t opMode[6]; 71 | 72 | int ampmoddepth_; 73 | int algorithm_; 74 | int pitchmoddepth_; 75 | int pitchmodsens_; 76 | }; 77 | 78 | #endif // SYNTH_DX7NOTE_H_ 79 | -------------------------------------------------------------------------------- /main/dx7rom.h: -------------------------------------------------------------------------------- 1 | #ifndef DX7ROM_H 2 | #define DX7ROM_H 3 | 4 | // http://bobbyblues.recup.ch 5 | unsigned char dx7rom[4104] = { 6 | 0xF0, 0x43, 0x00, 0x09, 0x20, 0x00, 0x31, 0x63, 0x1C, 0x44, 0x62, 0x62, 7 | 0x5B, 0x00, 0x27, 0x36, 0x32, 0x05, 0x3C, 0x08, 0x52, 0x02, 0x00, 0x4D, 8 | 0x24, 0x29, 0x47, 0x63, 0x62, 0x62, 0x00, 0x27, 0x00, 0x00, 0x0F, 0x40, 9 | 0x08, 0x62, 0x02, 0x00, 0x4D, 0x24, 0x29, 0x47, 0x63, 0x62, 0x62, 0x00, 10 | 0x27, 0x00, 0x00, 0x0F, 0x38, 0x08, 0x63, 0x02, 0x00, 0x4D, 0x4C, 0x52, 11 | 0x47, 0x63, 0x62, 0x62, 0x00, 0x27, 0x00, 0x00, 0x0F, 0x28, 0x08, 0x63, 12 | 0x02, 0x00, 0x3E, 0x33, 0x1D, 0x47, 0x52, 0x5F, 0x60, 0x00, 0x1B, 0x00, 13 | 0x07, 0x07, 0x70, 0x00, 0x56, 0x00, 0x00, 0x48, 0x4C, 0x63, 0x47, 0x63, 14 | 0x58, 0x60, 0x00, 0x27, 0x00, 0x0E, 0x0F, 0x70, 0x00, 0x62, 0x00, 0x00, 15 | 0x54, 0x5F, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x15, 0x0F, 0x25, 0x00, 16 | 0x05, 0x00, 0x38, 0x18, 0x42, 0x52, 0x41, 0x53, 0x53, 0x20, 0x20, 0x20, 17 | 0x31, 0x20, 0x63, 0x27, 0x20, 0x47, 0x63, 0x62, 0x58, 0x00, 0x33, 0x00, 18 | 0x00, 0x0F, 0x38, 0x00, 0x50, 0x00, 0x00, 0x63, 0x27, 0x20, 0x47, 0x63, 19 | 0x62, 0x51, 0x00, 0x27, 0x00, 0x00, 0x0F, 0x40, 0x00, 0x63, 0x00, 0x00, 20 | 0x63, 0x27, 0x20, 0x47, 0x63, 0x62, 0x51, 0x00, 0x27, 0x00, 0x00, 0x0F, 21 | 0x28, 0x00, 0x63, 0x00, 0x00, 0x63, 0x27, 0x20, 0x47, 0x63, 0x62, 0x51, 22 | 0x00, 0x27, 0x00, 0x00, 0x0F, 0x20, 0x00, 0x63, 0x00, 0x00, 0x63, 0x27, 23 | 0x20, 0x47, 0x63, 0x62, 0x50, 0x00, 0x33, 0x00, 0x26, 0x0F, 0x70, 0x00, 24 | 0x54, 0x00, 0x00, 0x63, 0x27, 0x20, 0x47, 0x63, 0x62, 0x50, 0x00, 0x33, 25 | 0x00, 0x26, 0x0F, 0x70, 0x00, 0x63, 0x00, 0x00, 0x54, 0x5F, 0x5F, 0x3C, 26 | 0x32, 0x32, 0x32, 0x32, 0x15, 0x0F, 0x25, 0x00, 0x00, 0x00, 0x38, 0x18, 27 | 0x42, 0x52, 0x41, 0x53, 0x53, 0x20, 0x20, 0x20, 0x32, 0x20, 0x4D, 0x38, 28 | 0x14, 0x46, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x00, 29 | 0x4F, 0x0E, 0x15, 0x30, 0x37, 0x16, 0x32, 0x62, 0x3D, 0x3E, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x30, 0x00, 0x46, 0x06, 0x06, 0x42, 0x5C, 0x16, 0x32, 31 | 0x35, 0x3D, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x4F, 0x02, 32 | 0x00, 0x2E, 0x23, 0x16, 0x32, 0x63, 0x56, 0x56, 0x00, 0x00, 0x00, 0x00, 33 | 0x00, 0x39, 0x04, 0x4D, 0x02, 0x00, 0x25, 0x22, 0x0F, 0x46, 0x55, 0x00, 34 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x04, 0x46, 0x02, 0x00, 0x37, 35 | 0x18, 0x13, 0x37, 0x63, 0x56, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 36 | 0x08, 0x63, 0x02, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 37 | 0x11, 0x0E, 0x23, 0x00, 0x05, 0x00, 0x30, 0x0C, 0x42, 0x52, 0x41, 0x53, 38 | 0x53, 0x20, 0x20, 0x20, 0x33, 0x20, 0x35, 0x13, 0x14, 0x36, 0x63, 0x5C, 39 | 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x08, 0x35, 0x1C, 0x00, 0x35, 40 | 0x13, 0x14, 0x36, 0x56, 0x5C, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 41 | 0x08, 0x54, 0x06, 0x00, 0x60, 0x13, 0x14, 0x36, 0x63, 0x5C, 0x56, 0x00, 42 | 0x00, 0x00, 0x00, 0x00, 0x3A, 0x08, 0x4D, 0x02, 0x00, 0x2C, 0x2D, 0x14, 43 | 0x36, 0x63, 0x55, 0x52, 0x00, 0x38, 0x00, 0x61, 0x00, 0x38, 0x1C, 0x56, 44 | 0x02, 0x00, 0x4B, 0x47, 0x11, 0x31, 0x52, 0x5C, 0x3E, 0x00, 0x36, 0x00, 45 | 0x00, 0x00, 0x39, 0x00, 0x53, 0x02, 0x00, 0x2D, 0x18, 0x14, 0x29, 0x63, 46 | 0x55, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x0C, 0x63, 0x02, 0x00, 47 | 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x01, 0x0F, 0x1E, 0x00, 48 | 0x08, 0x00, 0x20, 0x18, 0x53, 0x54, 0x52, 0x49, 0x4E, 0x47, 0x53, 0x20, 49 | 0x31, 0x20, 0x48, 0x4C, 0x0A, 0x20, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 50 | 0x00, 0x00, 0x38, 0x00, 0x46, 0x10, 0x00, 0x4C, 0x49, 0x0A, 0x1C, 0x63, 51 | 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x42, 0x04, 0x00, 52 | 0x31, 0x4A, 0x0A, 0x20, 0x62, 0x62, 0x24, 0x00, 0x62, 0x00, 0x00, 0x00, 53 | 0x38, 0x00, 0x4C, 0x04, 0x00, 0x33, 0x0F, 0x0A, 0x2F, 0x63, 0x5C, 0x00, 54 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x5C, 0x04, 0x00, 0x51, 0x0D, 55 | 0x07, 0x19, 0x63, 0x5C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 56 | 0x4A, 0x04, 0x00, 0x30, 0x38, 0x0A, 0x2F, 0x62, 0x62, 0x24, 0x00, 0x62, 57 | 0x00, 0x00, 0x00, 0x38, 0x00, 0x5C, 0x04, 0x00, 0x54, 0x5F, 0x5F, 0x3C, 58 | 0x32, 0x32, 0x32, 0x32, 0x01, 0x0F, 0x1E, 0x51, 0x08, 0x00, 0x28, 0x0C, 59 | 0x53, 0x54, 0x52, 0x49, 0x4E, 0x47, 0x53, 0x20, 0x32, 0x20, 0x35, 0x40, 60 | 0x2C, 0x36, 0x63, 0x5C, 0x38, 0x00, 0x37, 0x19, 0x00, 0x03, 0x3A, 0x08, 61 | 0x36, 0x1C, 0x00, 0x35, 0x43, 0x26, 0x36, 0x56, 0x5C, 0x4A, 0x00, 0x00, 62 | 0x00, 0x00, 0x00, 0x3A, 0x04, 0x54, 0x06, 0x00, 0x60, 0x13, 0x14, 0x36, 63 | 0x63, 0x5C, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x08, 0x4B, 0x02, 64 | 0x00, 0x32, 0x34, 0x23, 0x29, 0x63, 0x5C, 0x5B, 0x00, 0x33, 0x62, 0x3C, 65 | 0x03, 0x3A, 0x04, 0x63, 0x02, 0x00, 0x63, 0x47, 0x23, 0x33, 0x52, 0x5C, 66 | 0x57, 0x00, 0x36, 0x00, 0x00, 0x00, 0x39, 0x00, 0x56, 0x02, 0x00, 0x34, 67 | 0x1E, 0x19, 0x2B, 0x63, 0x5C, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 68 | 0x04, 0x63, 0x02, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 69 | 0x0E, 0x0F, 0x1C, 0x2E, 0x1E, 0x00, 0x18, 0x0C, 0x53, 0x54, 0x52, 0x49, 70 | 0x4E, 0x47, 0x53, 0x20, 0x33, 0x20, 0x48, 0x4C, 0x0A, 0x20, 0x63, 0x5C, 71 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x52, 0x04, 0x00, 0x4C, 72 | 0x49, 0x0A, 0x37, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 73 | 0x00, 0x50, 0x04, 0x00, 0x38, 0x4A, 0x0A, 0x2D, 0x62, 0x62, 0x24, 0x00, 74 | 0x62, 0x00, 0x00, 0x00, 0x38, 0x00, 0x48, 0x04, 0x00, 0x36, 0x0F, 0x0A, 75 | 0x2F, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x68, 0x00, 0x60, 76 | 0x04, 0x00, 0x35, 0x2E, 0x20, 0x3D, 0x63, 0x5D, 0x5A, 0x00, 0x00, 0x00, 77 | 0x00, 0x00, 0x08, 0x00, 0x53, 0x02, 0x00, 0x50, 0x38, 0x0A, 0x2D, 0x62, 78 | 0x62, 0x24, 0x00, 0x62, 0x00, 0x00, 0x00, 0x38, 0x00, 0x63, 0x02, 0x00, 79 | 0x54, 0x5F, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x01, 0x0F, 0x1E, 0x3F, 80 | 0x06, 0x00, 0x38, 0x0C, 0x4F, 0x52, 0x43, 0x48, 0x45, 0x53, 0x54, 0x52, 81 | 0x41, 0x20, 0x63, 0x00, 0x19, 0x00, 0x63, 0x4B, 0x00, 0x00, 0x00, 0x00, 82 | 0x0A, 0x00, 0x35, 0x00, 0x52, 0x02, 0x00, 0x51, 0x3A, 0x24, 0x27, 0x63, 83 | 0x0E, 0x00, 0x00, 0x30, 0x00, 0x42, 0x00, 0x35, 0x04, 0x5D, 0x02, 0x3A, 84 | 0x51, 0x17, 0x16, 0x2D, 0x63, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 85 | 0x45, 0x08, 0x63, 0x02, 0x00, 0x51, 0x19, 0x19, 0x0E, 0x63, 0x63, 0x63, 86 | 0x00, 0x2F, 0x20, 0x4A, 0x03, 0x3D, 0x00, 0x39, 0x06, 0x00, 0x63, 0x00, 87 | 0x19, 0x00, 0x63, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x00, 0x4D, 0x00, 88 | 0x57, 0x02, 0x00, 0x51, 0x19, 0x14, 0x30, 0x63, 0x52, 0x00, 0x00, 0x00, 89 | 0x55, 0x00, 0x03, 0x2C, 0x08, 0x63, 0x02, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 90 | 0x32, 0x32, 0x32, 0x32, 0x12, 0x0E, 0x23, 0x00, 0x00, 0x00, 0x40, 0x18, 91 | 0x50, 0x49, 0x41, 0x4E, 0x4F, 0x20, 0x20, 0x20, 0x31, 0x20, 0x5C, 0x47, 92 | 0x3A, 0x24, 0x63, 0x00, 0x00, 0x00, 0x24, 0x00, 0x62, 0x00, 0x43, 0x04, 93 | 0x4E, 0x00, 0x00, 0x5A, 0x47, 0x21, 0x1F, 0x63, 0x00, 0x00, 0x00, 0x1B, 94 | 0x00, 0x1A, 0x02, 0x3B, 0x04, 0x5E, 0x00, 0x00, 0x61, 0x1B, 0x0A, 0x19, 95 | 0x63, 0x56, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x04, 0x54, 0x02, 96 | 0x00, 0x5A, 0x1B, 0x14, 0x32, 0x63, 0x55, 0x00, 0x00, 0x20, 0x00, 0x1B, 97 | 0x00, 0x45, 0x04, 0x53, 0x0A, 0x00, 0x5F, 0x00, 0x19, 0x00, 0x63, 0x4B, 98 | 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x4A, 0x04, 0x56, 0x02, 0x00, 0x50, 99 | 0x18, 0x0A, 0x32, 0x63, 0x3E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 100 | 0x08, 0x5E, 0x02, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 101 | 0x11, 0x05, 0x1E, 0x00, 0x00, 0x00, 0x40, 0x0C, 0x50, 0x49, 0x41, 0x4E, 102 | 0x4F, 0x20, 0x20, 0x20, 0x32, 0x20, 0x50, 0x49, 0x0F, 0x0A, 0x63, 0x13, 103 | 0x00, 0x00, 0x35, 0x00, 0x00, 0x0C, 0x4B, 0x14, 0x54, 0x00, 0x00, 0x62, 104 | 0x14, 0x06, 0x02, 0x5B, 0x5A, 0x00, 0x00, 0x29, 0x00, 0x1B, 0x03, 0x2A, 105 | 0x04, 0x57, 0x02, 0x00, 0x5A, 0x40, 0x1C, 0x2D, 0x63, 0x61, 0x00, 0x00, 106 | 0x2E, 0x00, 0x00, 0x00, 0x53, 0x08, 0x5F, 0x02, 0x00, 0x5E, 0x50, 0x13, 107 | 0x0C, 0x53, 0x43, 0x00, 0x00, 0x2B, 0x09, 0x14, 0x03, 0x23, 0x0C, 0x61, 108 | 0x0E, 0x00, 0x62, 0x24, 0x06, 0x20, 0x5B, 0x5A, 0x00, 0x00, 0x32, 0x16, 109 | 0x32, 0x03, 0x5A, 0x00, 0x55, 0x02, 0x00, 0x5A, 0x1E, 0x1C, 0x2D, 0x63, 110 | 0x5F, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x1B, 0x0C, 0x56, 0x02, 0x00, 111 | 0x00, 0x00, 0x00, 0x00, 0x32, 0x32, 0x32, 0x32, 0x02, 0x0C, 0x2D, 0x00, 112 | 0x00, 0x00, 0x40, 0x18, 0x50, 0x49, 0x41, 0x4E, 0x4F, 0x20, 0x20, 0x20, 113 | 0x33, 0x20, 0x5F, 0x1D, 0x14, 0x32, 0x63, 0x5F, 0x00, 0x00, 0x29, 0x00, 114 | 0x13, 0x00, 0x73, 0x18, 0x4F, 0x02, 0x00, 0x5F, 0x14, 0x14, 0x32, 0x63, 115 | 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x63, 0x02, 0x00, 116 | 0x5F, 0x1D, 0x14, 0x32, 0x63, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 117 | 0x3B, 0x18, 0x59, 0x02, 0x00, 0x5F, 0x14, 0x14, 0x32, 0x63, 0x5F, 0x00, 118 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x08, 0x63, 0x02, 0x00, 0x5F, 0x32, 119 | 0x23, 0x4E, 0x63, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x1C, 120 | 0x3A, 0x1C, 0x00, 0x60, 0x19, 0x19, 0x43, 0x63, 0x4B, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, 0x00, 0x53, 0x08, 0x63, 0x02, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 122 | 0x32, 0x32, 0x32, 0x32, 0x04, 0x06, 0x22, 0x21, 0x00, 0x00, 0x38, 0x18, 123 | 0x45, 0x2E, 0x50, 0x49, 0x41, 0x4E, 0x4F, 0x20, 0x31, 0x20, 0x63, 0x39, 124 | 0x63, 0x4B, 0x63, 0x00, 0x00, 0x00, 0x27, 0x35, 0x14, 0x00, 0x38, 0x1B, 125 | 0x39, 0x18, 0x00, 0x51, 0x57, 0x16, 0x4B, 0x63, 0x5C, 0x00, 0x00, 0x00, 126 | 0x00, 0x0F, 0x00, 0x3C, 0x1C, 0x63, 0x06, 0x00, 0x51, 0x57, 0x16, 0x4B, 127 | 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x3C, 0x10, 0x59, 0x06, 128 | 0x00, 0x4E, 0x57, 0x16, 0x4B, 0x63, 0x5C, 0x00, 0x00, 0x22, 0x09, 0x00, 129 | 0x00, 0x3B, 0x1C, 0x63, 0x02, 0x00, 0x5B, 0x19, 0x27, 0x3C, 0x63, 0x56, 130 | 0x00, 0x00, 0x00, 0x00, 0x41, 0x00, 0x3A, 0x1C, 0x5D, 0x06, 0x00, 0x4A, 131 | 0x55, 0x1B, 0x46, 0x63, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 132 | 0x14, 0x63, 0x02, 0x00, 0x4B, 0x50, 0x4B, 0x3C, 0x32, 0x32, 0x32, 0x32, 133 | 0x07, 0x07, 0x23, 0x00, 0x01, 0x03, 0x38, 0x18, 0x47, 0x55, 0x49, 0x54, 134 | 0x41, 0x52, 0x20, 0x20, 0x31, 0x20, 0x63, 0x2C, 0x01, 0x47, 0x63, 0x60, 135 | 0x4B, 0x00, 0x3C, 0x00, 0x2E, 0x03, 0x38, 0x08, 0x49, 0x06, 0x00, 0x63, 136 | 0x63, 0x0C, 0x00, 0x63, 0x63, 0x4C, 0x00, 0x3C, 0x00, 0x00, 0x03, 0x38, 137 | 0x1C, 0x55, 0x00, 0x00, 0x5C, 0x63, 0x0F, 0x47, 0x63, 0x60, 0x4B, 0x00, 138 | 0x3C, 0x00, 0x00, 0x03, 0x38, 0x00, 0x46, 0x00, 0x00, 0x63, 0x63, 0x63, 139 | 0x47, 0x63, 0x63, 0x63, 0x00, 0x27, 0x00, 0x28, 0x03, 0x38, 0x1C, 0x63, 140 | 0x02, 0x32, 0x63, 0x63, 0x63, 0x2A, 0x63, 0x63, 0x63, 0x63, 0x30, 0x00, 141 | 0x00, 0x03, 0x39, 0x1C, 0x57, 0x02, 0x00, 0x5F, 0x43, 0x63, 0x47, 0x63, 142 | 0x63, 0x63, 0x00, 0x00, 0x52, 0x00, 0x0F, 0x38, 0x08, 0x56, 0x06, 0x00, 143 | 0x54, 0x5F, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x0F, 0x07, 0x23, 0x00, 144 | 0x00, 0x00, 0x40, 0x18, 0x47, 0x55, 0x49, 0x54, 0x41, 0x52, 0x20, 0x20, 145 | 0x32, 0x20, 0x63, 0x46, 0x3C, 0x00, 0x63, 0x63, 0x61, 0x00, 0x20, 0x00, 146 | 0x15, 0x00, 0x3B, 0x00, 0x2F, 0x22, 0x00, 0x63, 0x63, 0x61, 0x00, 0x63, 147 | 0x41, 0x3C, 0x00, 0x20, 0x00, 0x00, 0x00, 0x29, 0x00, 0x2B, 0x06, 0x00, 148 | 0x63, 0x5C, 0x1C, 0x3C, 0x63, 0x5A, 0x00, 0x00, 0x30, 0x00, 0x3C, 0x00, 149 | 0x4E, 0x00, 0x47, 0x04, 0x00, 0x63, 0x57, 0x00, 0x00, 0x5D, 0x5A, 0x00, 150 | 0x00, 0x20, 0x00, 0x15, 0x00, 0x3B, 0x00, 0x52, 0x02, 0x00, 0x63, 0x5F, 151 | 0x00, 0x00, 0x63, 0x60, 0x59, 0x00, 0x20, 0x00, 0x00, 0x00, 0x33, 0x00, 152 | 0x47, 0x02, 0x00, 0x63, 0x00, 0x0C, 0x46, 0x63, 0x5F, 0x5F, 0x00, 0x20, 153 | 0x00, 0x00, 0x00, 0x41, 0x00, 0x63, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 154 | 0x32, 0x32, 0x32, 0x32, 0x11, 0x07, 0x25, 0x2A, 0x00, 0x63, 0x48, 0x24, 155 | 0x53, 0x59, 0x4E, 0x2D, 0x4C, 0x45, 0x41, 0x44, 0x20, 0x31, 0x5E, 0x38, 156 | 0x18, 0x37, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x1C, 157 | 0x55, 0x12, 0x00, 0x63, 0x00, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x34, 158 | 0x4B, 0x00, 0x00, 0x3F, 0x0C, 0x3E, 0x00, 0x00, 0x5A, 0x2A, 0x07, 0x37, 159 | 0x5A, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x14, 0x5D, 0x0A, 160 | 0x00, 0x58, 0x60, 0x20, 0x1E, 0x4F, 0x41, 0x00, 0x00, 0x00, 0x00, 0x00, 161 | 0x00, 0x3E, 0x0C, 0x63, 0x00, 0x00, 0x63, 0x14, 0x00, 0x00, 0x63, 0x00, 162 | 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x3F, 0x00, 0x50, 0x00, 0x00, 0x5F, 163 | 0x3E, 0x11, 0x3A, 0x63, 0x5F, 0x20, 0x00, 0x24, 0x39, 0x0E, 0x03, 0x3F, 164 | 0x00, 0x63, 0x00, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 165 | 0x0F, 0x0F, 0x23, 0x00, 0x00, 0x00, 0x30, 0x0C, 0x42, 0x41, 0x53, 0x53, 166 | 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, 0x19, 0x32, 0x18, 0x37, 0x60, 0x61, 167 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x1C, 0x57, 0x00, 0x00, 0x63, 168 | 0x33, 0x00, 0x00, 0x63, 0x4A, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, 0x3C, 169 | 0x08, 0x4B, 0x02, 0x01, 0x50, 0x27, 0x1C, 0x35, 0x5D, 0x39, 0x00, 0x00, 170 | 0x00, 0x00, 0x00, 0x00, 0x3B, 0x08, 0x63, 0x00, 0x00, 0x49, 0x19, 0x20, 171 | 0x1E, 0x61, 0x4E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x0C, 0x44, 172 | 0x02, 0x00, 0x1C, 0x25, 0x2A, 0x32, 0x63, 0x00, 0x00, 0x00, 0x29, 0x00, 173 | 0x23, 0x00, 0x39, 0x08, 0x50, 0x00, 0x03, 0x4B, 0x25, 0x12, 0x3F, 0x63, 174 | 0x46, 0x00, 0x00, 0x30, 0x00, 0x20, 0x00, 0x3B, 0x08, 0x63, 0x00, 0x01, 175 | 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x10, 0x07, 0x1F, 0x21, 176 | 0x00, 0x00, 0x28, 0x0C, 0x42, 0x41, 0x53, 0x53, 0x20, 0x20, 0x20, 0x20, 177 | 0x32, 0x20, 0x63, 0x36, 0x16, 0x5A, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 178 | 0x00, 0x00, 0x38, 0x00, 0x5E, 0x06, 0x00, 0x63, 0x50, 0x16, 0x5A, 0x63, 179 | 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x5E, 0x02, 0x00, 180 | 0x63, 0x50, 0x16, 0x5A, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 181 | 0x60, 0x00, 0x5E, 0x00, 0x00, 0x63, 0x50, 0x36, 0x52, 0x63, 0x63, 0x63, 182 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x5E, 0x02, 0x32, 0x63, 0x14, 183 | 0x16, 0x5A, 0x63, 0x63, 0x61, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x08, 0x00, 184 | 0x5E, 0x02, 0x01, 0x63, 0x50, 0x16, 0x5A, 0x63, 0x63, 0x63, 0x00, 0x00, 185 | 0x00, 0x00, 0x00, 0x28, 0x00, 0x5E, 0x00, 0x00, 0x4B, 0x50, 0x4B, 0x3C, 186 | 0x32, 0x32, 0x32, 0x32, 0x1F, 0x08, 0x23, 0x00, 0x00, 0x00, 0x40, 0x18, 187 | 0x45, 0x2E, 0x4F, 0x52, 0x47, 0x41, 0x4E, 0x20, 0x31, 0x20, 0x48, 0x19, 188 | 0x19, 0x46, 0x63, 0x63, 0x63, 0x00, 0x2E, 0x0A, 0x01, 0x0C, 0x3B, 0x08, 189 | 0x4C, 0x14, 0x00, 0x3D, 0x19, 0x19, 0x3D, 0x63, 0x63, 0x5D, 0x00, 0x00, 190 | 0x00, 0x00, 0x00, 0x3B, 0x00, 0x61, 0x04, 0x00, 0x3D, 0x19, 0x19, 0x32, 191 | 0x63, 0x63, 0x61, 0x00, 0x3C, 0x0A, 0x0A, 0x00, 0x3B, 0x00, 0x58, 0x08, 192 | 0x00, 0x63, 0x61, 0x3E, 0x2F, 0x63, 0x63, 0x5A, 0x00, 0x2E, 0x11, 0x28, 193 | 0x03, 0x3D, 0x00, 0x4B, 0x02, 0x00, 0x63, 0x61, 0x3E, 0x2F, 0x63, 0x63, 194 | 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x5A, 0x00, 0x00, 0x2D, 195 | 0x19, 0x19, 0x24, 0x63, 0x63, 0x62, 0x00, 0x29, 0x00, 0x32, 0x00, 0x3D, 196 | 0x00, 0x63, 0x00, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 197 | 0x12, 0x0F, 0x22, 0x21, 0x00, 0x00, 0x28, 0x0C, 0x50, 0x49, 0x50, 0x45, 198 | 0x53, 0x20, 0x20, 0x20, 0x31, 0x20, 0x5F, 0x48, 0x47, 0x63, 0x63, 0x61, 199 | 0x5B, 0x62, 0x40, 0x00, 0x37, 0x00, 0x39, 0x00, 0x57, 0x0C, 0x00, 0x5F, 200 | 0x1C, 0x1B, 0x2F, 0x63, 0x5A, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 201 | 0x0C, 0x53, 0x08, 0x00, 0x5F, 0x48, 0x47, 0x63, 0x63, 0x61, 0x5B, 0x62, 202 | 0x40, 0x00, 0x2E, 0x00, 0x39, 0x00, 0x63, 0x06, 0x00, 0x5F, 0x1C, 0x1B, 203 | 0x2F, 0x63, 0x5A, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x33, 0x08, 0x55, 204 | 0x02, 0x00, 0x5F, 0x48, 0x47, 0x63, 0x63, 0x61, 0x5B, 0x62, 0x31, 0x00, 205 | 0x00, 0x00, 0x39, 0x00, 0x63, 0x00, 0x00, 0x5F, 0x1C, 0x1B, 0x2F, 0x63, 206 | 0x5A, 0x00, 0x00, 0x31, 0x00, 0x00, 0x00, 0x3B, 0x08, 0x59, 0x08, 0x00, 207 | 0x00, 0x00, 0x00, 0x00, 0x32, 0x32, 0x32, 0x32, 0x04, 0x09, 0x23, 0x00, 208 | 0x00, 0x00, 0x20, 0x18, 0x48, 0x41, 0x52, 0x50, 0x53, 0x49, 0x43, 0x48, 209 | 0x20, 0x31, 0x62, 0x57, 0x00, 0x00, 0x57, 0x56, 0x00, 0x00, 0x20, 0x00, 210 | 0x15, 0x00, 0x3B, 0x1C, 0x4E, 0x10, 0x00, 0x5F, 0x5F, 0x00, 0x00, 0x63, 211 | 0x60, 0x59, 0x00, 0x20, 0x00, 0x00, 0x00, 0x2B, 0x18, 0x63, 0x00, 0x00, 212 | 0x5F, 0x5C, 0x1C, 0x3C, 0x63, 0x5A, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 213 | 0x3B, 0x08, 0x63, 0x04, 0x00, 0x62, 0x57, 0x00, 0x00, 0x57, 0x56, 0x00, 214 | 0x00, 0x20, 0x00, 0x15, 0x00, 0x3B, 0x04, 0x47, 0x08, 0x32, 0x5F, 0x5F, 215 | 0x00, 0x00, 0x63, 0x60, 0x59, 0x00, 0x20, 0x00, 0x00, 0x00, 0x33, 0x04, 216 | 0x63, 0x00, 0x00, 0x5F, 0x5C, 0x1C, 0x3C, 0x63, 0x5A, 0x00, 0x00, 0x20, 217 | 0x00, 0x00, 0x00, 0x43, 0x0C, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 218 | 0x32, 0x32, 0x32, 0x32, 0x02, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x28, 0x18, 219 | 0x43, 0x4C, 0x41, 0x56, 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, 0x63, 0x30, 220 | 0x63, 0x32, 0x63, 0x20, 0x00, 0x00, 0x27, 0x0C, 0x0C, 0x0C, 0x3D, 0x1C, 221 | 0x39, 0x1C, 0x00, 0x50, 0x55, 0x18, 0x32, 0x63, 0x5A, 0x2A, 0x00, 0x09, 222 | 0x00, 0x00, 0x05, 0x73, 0x14, 0x63, 0x02, 0x00, 0x50, 0x55, 0x18, 0x32, 223 | 0x63, 0x5A, 0x00, 0x00, 0x09, 0x00, 0x00, 0x05, 0x03, 0x04, 0x63, 0x02, 224 | 0x00, 0x50, 0x55, 0x2B, 0x32, 0x63, 0x4A, 0x00, 0x00, 0x27, 0x0C, 0x0C, 225 | 0x0C, 0x3C, 0x10, 0x48, 0x06, 0x00, 0x50, 0x55, 0x18, 0x32, 0x63, 0x5A, 226 | 0x00, 0x00, 0x27, 0x04, 0x0C, 0x0C, 0x3A, 0x04, 0x63, 0x02, 0x00, 0x63, 227 | 0x1C, 0x63, 0x32, 0x63, 0x19, 0x00, 0x00, 0x27, 0x0C, 0x0C, 0x0C, 0x3A, 228 | 0x1C, 0x32, 0x08, 0x00, 0x63, 0x62, 0x4B, 0x3C, 0x32, 0x32, 0x32, 0x32, 229 | 0x16, 0x0D, 0x1A, 0x00, 0x00, 0x00, 0x21, 0x18, 0x56, 0x49, 0x42, 0x45, 230 | 0x20, 0x20, 0x20, 0x20, 0x31, 0x20, 0x00, 0x3F, 0x37, 0x00, 0x4E, 0x4E, 231 | 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x38, 0x08, 0x63, 0x08, 0x0D, 0x63, 232 | 0x4B, 0x00, 0x08, 0x52, 0x30, 0x00, 0x00, 0x36, 0x00, 0x2E, 0x00, 0x38, 233 | 0x08, 0x5D, 0x00, 0x32, 0x63, 0x4B, 0x00, 0x52, 0x52, 0x30, 0x00, 0x00, 234 | 0x36, 0x00, 0x2E, 0x00, 0x38, 0x08, 0x55, 0x0A, 0x00, 0x5F, 0x21, 0x31, 235 | 0x29, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x04, 0x63, 236 | 0x00, 0x00, 0x63, 0x48, 0x00, 0x00, 0x52, 0x30, 0x00, 0x00, 0x36, 0x00, 237 | 0x2E, 0x00, 0x38, 0x08, 0x60, 0x06, 0x00, 0x5F, 0x28, 0x31, 0x37, 0x63, 238 | 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x5F, 0x00, 0x00, 239 | 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x06, 0x08, 0x23, 0x00, 240 | 0x00, 0x00, 0x31, 0x18, 0x4D, 0x41, 0x52, 0x49, 0x4D, 0x42, 0x41, 0x20, 241 | 0x20, 0x20, 0x52, 0x35, 0x25, 0x30, 0x63, 0x51, 0x00, 0x00, 0x00, 0x00, 242 | 0x05, 0x00, 0x3E, 0x04, 0x51, 0x06, 0x00, 0x5B, 0x25, 0x1D, 0x1D, 0x63, 243 | 0x5A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x3E, 0x04, 0x53, 0x08, 0x00, 244 | 0x5A, 0x1C, 0x11, 0x27, 0x63, 0x4C, 0x00, 0x00, 0x0A, 0x00, 0x11, 0x04, 245 | 0x3E, 0x04, 0x52, 0x02, 0x00, 0x5E, 0x40, 0x1E, 0x21, 0x63, 0x5C, 0x00, 246 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x0C, 0x63, 0x02, 0x00, 0x63, 0x44, 247 | 0x1C, 0x30, 0x63, 0x53, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x00, 0x3E, 0x00, 248 | 0x63, 0x08, 0x00, 0x5E, 0x3E, 0x3A, 0x22, 0x63, 0x5C, 0x00, 0x00, 0x00, 249 | 0x00, 0x00, 0x00, 0x3E, 0x0C, 0x5A, 0x02, 0x00, 0x55, 0x63, 0x4B, 0x00, 250 | 0x31, 0x32, 0x32, 0x32, 0x01, 0x0F, 0x1E, 0x28, 0x11, 0x0F, 0x29, 0x18, 251 | 0x4B, 0x4F, 0x54, 0x4F, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x40, 252 | 0x62, 0x3D, 0x63, 0x43, 0x34, 0x00, 0x2E, 0x00, 0x00, 0x0C, 0x58, 0x08, 253 | 0x53, 0x02, 0x35, 0x41, 0x26, 0x00, 0x3D, 0x63, 0x00, 0x00, 0x00, 0x35, 254 | 0x00, 0x2B, 0x00, 0x38, 0x00, 0x38, 0x04, 0x00, 0x3D, 0x19, 0x19, 0x3C, 255 | 0x63, 0x63, 0x61, 0x00, 0x3C, 0x0A, 0x0A, 0x00, 0x3B, 0x00, 0x00, 0x04, 256 | 0x00, 0x35, 0x26, 0x4B, 0x3D, 0x58, 0x2C, 0x18, 0x00, 0x2E, 0x00, 0x00, 257 | 0x03, 0x20, 0x01, 0x4C, 0x02, 0x00, 0x63, 0x61, 0x3E, 0x36, 0x63, 0x63, 258 | 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x08, 0x4B, 0x02, 0x00, 0x3D, 259 | 0x43, 0x46, 0x41, 0x5D, 0x59, 0x62, 0x00, 0x29, 0x00, 0x00, 0x00, 0x28, 260 | 0x08, 0x62, 0x02, 0x00, 0x5E, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 261 | 0x0F, 0x05, 0x1E, 0x17, 0x08, 0x0D, 0x10, 0x18, 0x46, 0x4C, 0x55, 0x54, 262 | 0x45, 0x20, 0x20, 0x20, 0x31, 0x20, 0x63, 0x00, 0x00, 0x00, 0x63, 0x63, 263 | 0x63, 0x00, 0x0F, 0x00, 0x00, 0x04, 0x07, 0x00, 0x4B, 0x02, 0x00, 0x29, 264 | 0x2A, 0x47, 0x22, 0x63, 0x63, 0x63, 0x00, 0x0F, 0x00, 0x00, 0x04, 0x73, 265 | 0x00, 0x62, 0x02, 0x00, 0x50, 0x46, 0x09, 0x0C, 0x58, 0x50, 0x00, 0x00, 266 | 0x0F, 0x00, 0x00, 0x04, 0x3B, 0x0C, 0x5B, 0x04, 0x39, 0x50, 0x31, 0x11, 267 | 0x1E, 0x63, 0x5F, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x04, 0x3B, 0x08, 0x63, 268 | 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x63, 0x63, 0x63, 0x00, 0x0F, 0x00, 269 | 0x00, 0x04, 0x67, 0x00, 0x57, 0x00, 0x00, 0x22, 0x2A, 0x47, 0x22, 0x63, 270 | 0x63, 0x63, 0x00, 0x0F, 0x00, 0x00, 0x04, 0x63, 0x00, 0x61, 0x00, 0x00, 271 | 0x63, 0x63, 0x63, 0x63, 0x32, 0x32, 0x32, 0x32, 0x04, 0x0F, 0x1E, 0x00, 272 | 0x05, 0x00, 0x30, 0x18, 0x4F, 0x52, 0x43, 0x48, 0x2D, 0x43, 0x48, 0x49, 273 | 0x4D, 0x45, 0x62, 0x5B, 0x00, 0x1C, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 274 | 0x00, 0x00, 0x02, 0x00, 0x55, 0x04, 0x00, 0x4C, 0x4E, 0x47, 0x46, 0x63, 275 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3A, 0x14, 0x63, 0x05, 0x33, 276 | 0x62, 0x0C, 0x47, 0x1C, 0x63, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 277 | 0x2A, 0x00, 0x4B, 0x04, 0x4B, 0x5F, 0x21, 0x47, 0x19, 0x63, 0x00, 0x20, 278 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x63, 0x02, 0x00, 0x62, 0x0C, 279 | 0x47, 0x1C, 0x63, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 280 | 0x4E, 0x04, 0x4B, 0x5F, 0x21, 0x47, 0x19, 0x63, 0x00, 0x20, 0x00, 0x00, 281 | 0x00, 0x00, 0x00, 0x4A, 0x00, 0x5F, 0x02, 0x00, 0x43, 0x5F, 0x5F, 0x3C, 282 | 0x32, 0x32, 0x32, 0x32, 0x04, 0x07, 0x23, 0x00, 0x00, 0x00, 0x12, 0x18, 283 | 0x54, 0x55, 0x42, 0x20, 0x42, 0x45, 0x4C, 0x4C, 0x53, 0x20, 0x63, 0x31, 284 | 0x1C, 0x0C, 0x5B, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 285 | 0x31, 0x05, 0x3C, 0x63, 0x28, 0x26, 0x00, 0x5B, 0x52, 0x00, 0x00, 0x00, 286 | 0x00, 0x00, 0x00, 0x3B, 0x00, 0x40, 0x08, 0x21, 0x63, 0x2C, 0x32, 0x15, 287 | 0x5B, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x73, 0x04, 0x58, 0x04, 288 | 0x00, 0x63, 0x1E, 0x23, 0x2A, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 289 | 0x00, 0x3B, 0x0C, 0x63, 0x02, 0x00, 0x63, 0x13, 0x14, 0x09, 0x63, 0x57, 290 | 0x00, 0x00, 0x39, 0x00, 0x47, 0x02, 0x3A, 0x08, 0x40, 0x02, 0x46, 0x63, 291 | 0x28, 0x21, 0x26, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 292 | 0x00, 0x63, 0x02, 0x00, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 0x32, 293 | 0x0E, 0x0D, 0x19, 0x00, 0x0A, 0x63, 0x28, 0x18, 0x53, 0x54, 0x45, 0x45, 294 | 0x4C, 0x20, 0x44, 0x52, 0x55, 0x4D, 0x62, 0x02, 0x1A, 0x1B, 0x62, 0x00, 295 | 0x00, 0x00, 0x03, 0x00, 0x00, 0x08, 0x3B, 0x04, 0x49, 0x00, 0x38, 0x63, 296 | 0x32, 0x1A, 0x13, 0x63, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x07, 0x38, 297 | 0x04, 0x49, 0x00, 0x00, 0x63, 0x1F, 0x11, 0x1E, 0x63, 0x4B, 0x00, 0x00, 298 | 0x50, 0x00, 0x00, 0x07, 0x3F, 0x1C, 0x57, 0x00, 0x4B, 0x63, 0x4D, 0x1A, 299 | 0x17, 0x63, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x23, 0x00, 0x55, 300 | 0x00, 0x24, 0x63, 0x4A, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x29, 0x00, 301 | 0x00, 0x04, 0x51, 0x04, 0x56, 0x00, 0x00, 0x63, 0x24, 0x62, 0x21, 0x63, 302 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x3B, 0x04, 0x63, 0x00, 0x00, 303 | 0x63, 0x62, 0x4B, 0x3C, 0x32, 0x33, 0x32, 0x32, 0x0F, 0x0F, 0x0B, 0x00, 304 | 0x10, 0x00, 0x20, 0x18, 0x54, 0x49, 0x4D, 0x50, 0x41, 0x4E, 0x49, 0x20, 305 | 0x20, 0x20, 0x5E, 0x38, 0x18, 0x37, 0x60, 0x4E, 0x00, 0x00, 0x00, 0x00, 306 | 0x00, 0x00, 0x39, 0x00, 0x4E, 0x0B, 0x00, 0x63, 0x00, 0x00, 0x00, 0x63, 307 | 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x38, 0x00, 0x40, 0x09, 0x00, 308 | 0x5E, 0x44, 0x18, 0x37, 0x60, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 309 | 0x39, 0x00, 0x4B, 0x0F, 0x52, 0x3C, 0x27, 0x08, 0x00, 0x63, 0x63, 0x63, 310 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x42, 0x03, 0x43, 0x3C, 0x27, 311 | 0x1C, 0x2D, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3C, 0x00, 312 | 0x5D, 0x13, 0x35, 0x3C, 0x27, 0x1C, 0x31, 0x63, 0x63, 0x63, 0x00, 0x00, 313 | 0x00, 0x00, 0x00, 0x3C, 0x04, 0x5A, 0x07, 0x20, 0x26, 0x43, 0x5F, 0x3C, 314 | 0x27, 0x32, 0x32, 0x32, 0x11, 0x0A, 0x63, 0x00, 0x00, 0x00, 0x6B, 0x18, 315 | 0x52, 0x45, 0x46, 0x53, 0x20, 0x57, 0x48, 0x49, 0x53, 0x4C, 0x63, 0x48, 316 | 0x30, 0x11, 0x63, 0x63, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 317 | 0x37, 0x0A, 0x02, 0x23, 0x15, 0x24, 0x3F, 0x63, 0x5A, 0x55, 0x00, 0x00, 318 | 0x00, 0x00, 0x00, 0x30, 0x04, 0x35, 0x02, 0x01, 0x48, 0x13, 0x29, 0x0C, 319 | 0x30, 0x3A, 0x14, 0x09, 0x00, 0x00, 0x00, 0x00, 0x50, 0x04, 0x63, 0x02, 320 | 0x02, 0x21, 0x14, 0x35, 0x27, 0x63, 0x5E, 0x61, 0x00, 0x00, 0x00, 0x00, 321 | 0x00, 0x70, 0x0C, 0x63, 0x02, 0x00, 0x13, 0x1A, 0x35, 0x19, 0x33, 0x3D, 322 | 0x4C, 0x33, 0x00, 0x00, 0x00, 0x00, 0x38, 0x0A, 0x63, 0x02, 0x00, 0x22, 323 | 0x14, 0x35, 0x39, 0x63, 0x5E, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 324 | 0x00, 0x57, 0x02, 0x00, 0x12, 0x3C, 0x5F, 0x3C, 0x30, 0x33, 0x32, 0x32, 325 | 0x06, 0x0F, 0x23, 0x23, 0x0B, 0x02, 0x40, 0x18, 0x56, 0x4F, 0x49, 0x43, 326 | 0x45, 0x20, 0x20, 0x20, 0x31, 0x20, 0x31, 0x11, 0x19, 0x35, 0x63, 0x63, 327 | 0x63, 0x62, 0x24, 0x00, 0x00, 0x03, 0x38, 0x00, 0x63, 0x0A, 0x00, 0x2A, 328 | 0x11, 0x19, 0x35, 0x63, 0x63, 0x63, 0x63, 0x24, 0x00, 0x00, 0x03, 0x50, 329 | 0x03, 0x53, 0x12, 0x00, 0x62, 0x1D, 0x1C, 0x1B, 0x63, 0x00, 0x00, 0x00, 330 | 0x14, 0x00, 0x00, 0x05, 0x28, 0x00, 0x59, 0x15, 0x63, 0x62, 0x1D, 0x1C, 331 | 0x21, 0x63, 0x00, 0x00, 0x00, 0x63, 0x62, 0x00, 0x05, 0x48, 0x00, 0x63, 332 | 0x2D, 0x39, 0x27, 0x0D, 0x0C, 0x48, 0x63, 0x3D, 0x42, 0x00, 0x34, 0x00, 333 | 0x00, 0x03, 0x3D, 0x00, 0x48, 0x06, 0x01, 0x41, 0x18, 0x13, 0x39, 0x63, 334 | 0x55, 0x55, 0x00, 0x27, 0x00, 0x62, 0x03, 0x3B, 0x00, 0x63, 0x02, 0x40, 335 | 0x4B, 0x43, 0x5F, 0x3C, 0x32, 0x32, 0x32, 0x32, 0x04, 0x0F, 0x27, 0x00, 336 | 0x00, 0x63, 0x00, 0x18, 0x54, 0x52, 0x41, 0x49, 0x4E, 0x20, 0x20, 0x20, 337 | 0x20, 0x20, 0x59, 0x16, 0x14, 0x29, 0x63, 0x5C, 0x00, 0x00, 0x00, 0x00, 338 | 0x00, 0x00, 0x38, 0x00, 0x63, 0x00, 0x00, 0x58, 0x18, 0x17, 0x25, 0x63, 339 | 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x60, 0x04, 0x01, 340 | 0x0D, 0x0E, 0x14, 0x1E, 0x63, 0x5F, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 341 | 0x38, 0x00, 0x63, 0x00, 0x00, 0x4C, 0x23, 0x63, 0x0B, 0x43, 0x26, 0x49, 342 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x63, 0x0C, 0x01, 0x52, 0x50, 343 | 0x13, 0x0E, 0x50, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 344 | 0x60, 0x02, 0x00, 0x09, 0x0E, 0x11, 0x22, 0x3D, 0x60, 0x00, 0x00, 0x00, 345 | 0x00, 0x00, 0x00, 0x38, 0x00, 0x63, 0x08, 0x01, 0x20, 0x1E, 0x5E, 0x10, 346 | 0x32, 0x07, 0x51, 0x63, 0x09, 0x08, 0x41, 0x00, 0x00, 0x00, 0x55, 0x00, 347 | 0x54, 0x41, 0x4B, 0x45, 0x20, 0x4F, 0x46, 0x46, 0x20, 0x20, 0x33, 0xF7 348 | }; 349 | 350 | 351 | 352 | 353 | #endif -------------------------------------------------------------------------------- /main/env.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Pascal Gauthier. 3 | * Copyright 2012 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include 19 | 20 | #include "synth.h" 21 | #include "env.h" 22 | 23 | //using namespace std; 24 | 25 | uint32_t Env::sr_multiplier = (1<<24); 26 | 27 | const int levellut[] = { 28 | 0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46 29 | }; 30 | 31 | #ifdef ACCURATE_ENVELOPE 32 | const int statics[] = { 33 | 1764000, 1764000, 1411200, 1411200, 1190700, 1014300, 992250, 34 | 882000, 705600, 705600, 584325, 507150, 502740, 441000, 418950, 35 | 352800, 308700, 286650, 253575, 220500, 220500, 176400, 145530, 36 | 145530, 125685, 110250, 110250, 88200, 88200, 74970, 61740, 37 | 61740, 55125, 48510, 44100, 37485, 31311, 30870, 27562, 27562, 38 | 22050, 18522, 17640, 15435, 14112, 13230, 11025, 9261, 9261, 7717, 39 | 6615, 6615, 5512, 5512, 4410, 3969, 3969, 3439, 2866, 2690, 2249, 40 | 1984, 1896, 1808, 1411, 1367, 1234, 1146, 926, 837, 837, 705, 41 | 573, 573, 529, 441, 441 42 | // and so on, I stopped measuring after R=76 (needs to be FRAC_NUM-checked anyway) 43 | }; 44 | #endif 45 | 46 | void Env::init_sr(FRAC_NUM sampleRate) { 47 | sr_multiplier = (44100.0 / sampleRate) * (1<<24); 48 | } 49 | 50 | void Env::init(const int r[4], const int l[4], int ol, int rate_scaling) { 51 | for (int i = 0; i < 4; i++) { 52 | rates_[i] = r[i]; 53 | levels_[i] = l[i]; 54 | } 55 | outlevel_ = ol; 56 | rate_scaling_ = rate_scaling; 57 | level_ = 0; 58 | down_ = true; 59 | advance(0); 60 | } 61 | 62 | int32_t Env::getsample() { 63 | #ifdef ACCURATE_ENVELOPE 64 | if (staticcount_) { 65 | staticcount_ -= N; 66 | if (staticcount_ <= 0) { 67 | staticcount_ = 0; 68 | advance(ix_ + 1); 69 | } 70 | } 71 | #endif 72 | 73 | if (ix_ < 3 || ((ix_ < 4) && !down_)) { 74 | if (rising_) { 75 | const int jumptarget = 1716; 76 | if (level_ < (jumptarget << 16)) { 77 | level_ = jumptarget << 16; 78 | } 79 | level_ += (((17 << 24) - level_) >> 24) * inc_; 80 | // TODO: should probably be more accurate when inc is large 81 | if (level_ >= targetlevel_) { 82 | level_ = targetlevel_; 83 | advance(ix_ + 1); 84 | } 85 | } 86 | else if (staticcount_) { 87 | ; 88 | } 89 | else { // !rising 90 | level_ -= inc_; 91 | if (level_ <= targetlevel_) { 92 | level_ = targetlevel_; 93 | advance(ix_ + 1); 94 | } 95 | } 96 | } 97 | // TODO: this would be a good place to set level to 0 when under threshold 98 | return level_; 99 | } 100 | 101 | void Env::keydown(bool d) { 102 | if (down_ != d) { 103 | down_ = d; 104 | advance(d ? 0 : 3); 105 | } 106 | } 107 | 108 | int Env::scaleoutlevel(int outlevel) { 109 | return outlevel >= 20 ? 28 + outlevel : levellut[outlevel]; 110 | } 111 | 112 | void Env::advance(int newix) { 113 | ix_ = newix; 114 | if (ix_ < 4) { 115 | int newlevel = levels_[ix_]; 116 | int actuallevel = scaleoutlevel(newlevel) >> 1; 117 | actuallevel = (actuallevel << 6) + outlevel_ - 4256; 118 | actuallevel = actuallevel < 16 ? 16 : actuallevel; 119 | // level here is same as Java impl 120 | targetlevel_ = actuallevel << 16; 121 | rising_ = (targetlevel_ > level_); 122 | 123 | // rate 124 | int qrate = (rates_[ix_] * 41) >> 6; 125 | qrate += rate_scaling_; 126 | qrate = min(qrate, 63); 127 | 128 | #ifdef ACCURATE_ENVELOPE 129 | if (targetlevel_ == level_) { 130 | // approximate number of samples at 44.100 kHz to achieve the time 131 | // empirically gathered using 2 TF1s, could probably use some FRAC_NUM-checking 132 | // and cleanup, but it's pretty close for now. 133 | int staticrate = rates_[ix_]; 134 | staticrate += rate_scaling_; // needs to be checked, as well, but seems correct 135 | staticrate = min(staticrate, 99); 136 | staticcount_ = staticrate < 77 ? statics[staticrate] : 20 * (99 - staticrate); 137 | staticcount_ = (int)(((int64_t)staticcount_ * (int64_t)sr_multiplier) >> 24); 138 | } 139 | else { 140 | staticcount_ = 0; 141 | } 142 | #endif 143 | inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2)); 144 | // meh, this should be fixed elsewhere 145 | inc_ = (int)(((int64_t)inc_ * (int64_t)sr_multiplier) >> 24); 146 | } 147 | } 148 | 149 | void Env::update(const int r[4], const int l[4], int ol, int rate_scaling) { 150 | for (int i = 0; i < 4; i++) { 151 | rates_[i] = r[i]; 152 | levels_[i] = l[i]; 153 | } 154 | outlevel_ = ol; 155 | rate_scaling_ = rate_scaling; 156 | if ( down_ ) { 157 | // for now we simply reset ourselve at level 3 158 | int newlevel = levels_[2]; 159 | int actuallevel = scaleoutlevel(newlevel) >> 1; 160 | actuallevel = (actuallevel << 6) - 4256; 161 | actuallevel = actuallevel < 16 ? 16 : actuallevel; 162 | targetlevel_ = actuallevel << 16; 163 | advance(2); 164 | } 165 | } 166 | 167 | void Env::getPosition(char *step) { 168 | *step = ix_; 169 | } 170 | 171 | void Env::transfer(Env &src) { 172 | for(int i=0;i<4;i++) { 173 | rates_[i] = src.rates_[i]; 174 | levels_[i] = src.levels_[i]; 175 | } 176 | outlevel_ = src.outlevel_; 177 | rate_scaling_ = src.rate_scaling_; 178 | level_ = src.level_; 179 | targetlevel_ = src.targetlevel_; 180 | rising_= src.rising_; 181 | ix_ = src.ix_; 182 | down_ = src.down_; 183 | #ifdef ACCURATE_ENVELOPE 184 | staticcount_ = src.staticcount_; 185 | #endif 186 | inc_ = src.inc_; 187 | } 188 | 189 | -------------------------------------------------------------------------------- /main/env.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Pascal Gauthier. 3 | * Copyright 2012 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include 19 | 20 | #include "synth.h" 21 | #include "env.h" 22 | 23 | //using namespace std; 24 | 25 | uint32_t Env::sr_multiplier = (1<<24); 26 | 27 | const int levellut[] = { 28 | 0, 5, 9, 13, 17, 20, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 42, 43, 45, 46 29 | }; 30 | 31 | void Env::init_sr(FRAC_NUM sampleRate) { 32 | sr_multiplier = (44100.0 / sampleRate) * (1<<24); 33 | } 34 | 35 | void Env::init(const int r[4], const int l[4], int32_t ol, int rate_scaling) { 36 | for (int i = 0; i < 4; i++) { 37 | rates_[i] = r[i]; 38 | levels_[i] = l[i]; 39 | } 40 | outlevel_ = ol; 41 | rate_scaling_ = rate_scaling; 42 | level_ = 0; 43 | down_ = true; 44 | advance(0); 45 | } 46 | 47 | int32_t Env::getsample() { 48 | if (ix_ < 3 || ((ix_ < 4) && !down_)) { 49 | if (rising_) { 50 | const int jumptarget = 1716; 51 | if (level_ < (jumptarget << 16)) { 52 | level_ = jumptarget << 16; 53 | } 54 | level_ += (((17 << 24) - level_) >> 24) * inc_; 55 | // TODO: should probably be more accurate when inc is large 56 | if (level_ >= targetlevel_) { 57 | level_ = targetlevel_; 58 | advance(ix_ + 1); 59 | } 60 | } else { // !rising 61 | level_ -= inc_; 62 | if (level_ <= targetlevel_) { 63 | level_ = targetlevel_; 64 | advance(ix_ + 1); 65 | } 66 | } 67 | } 68 | // TODO: this would be a good place to set level to 0 when under threshold 69 | return level_; 70 | } 71 | 72 | void Env::keydown(bool d) { 73 | if (down_ != d) { 74 | down_ = d; 75 | advance(d ? 0 : 3); 76 | } 77 | } 78 | 79 | int Env::scaleoutlevel(int outlevel) { 80 | return outlevel >= 20 ? 28 + outlevel : levellut[outlevel]; 81 | } 82 | 83 | void Env::advance(int newix) { 84 | ix_ = newix; 85 | if (ix_ < 4) { 86 | int newlevel = levels_[ix_]; 87 | int actuallevel = scaleoutlevel(newlevel) >> 1; 88 | actuallevel = (actuallevel << 6) + outlevel_ - 4256; 89 | actuallevel = actuallevel < 16 ? 16 : actuallevel; 90 | // level here is same as Java impl 91 | targetlevel_ = actuallevel << 16; 92 | rising_ = (targetlevel_ > level_); 93 | 94 | // rate 95 | int qrate = (rates_[ix_] * 41) >> 6; 96 | qrate += rate_scaling_; 97 | qrate = min(qrate, 63); 98 | inc_ = (4 + (qrate & 3)) << (2 + LG_N + (qrate >> 2)); 99 | 100 | // meh, this should be fixed elsewhere 101 | inc_ = ((int64_t)inc_ * (int64_t)sr_multiplier) >> 24; 102 | } 103 | } 104 | 105 | void Env::update(const int r[4], const int l[4], int32_t ol, int rate_scaling) { 106 | for (int i = 0; i < 4; i++) { 107 | rates_[i] = r[i]; 108 | levels_[i] = l[i]; 109 | } 110 | outlevel_ = ol; 111 | rate_scaling_ = rate_scaling; 112 | if ( down_ ) { 113 | // for now we simply reset ourselve at level 3 114 | int newlevel = levels_[2]; 115 | int actuallevel = scaleoutlevel(newlevel) >> 1; 116 | actuallevel = (actuallevel << 6) - 4256; 117 | actuallevel = actuallevel < 16 ? 16 : actuallevel; 118 | targetlevel_ = actuallevel << 16; 119 | advance(2); 120 | } 121 | } 122 | 123 | void Env::getPosition(char *step) { 124 | *step = ix_; 125 | } 126 | 127 | void Env::transfer(Env &src) { 128 | for(int i=0;i<4;i++) { 129 | rates_[i] = src.rates_[i]; 130 | levels_[i] = src.levels_[i]; 131 | } 132 | outlevel_ = src.outlevel_; 133 | rate_scaling_ = src.rate_scaling_; 134 | level_ = src.level_; 135 | targetlevel_ = src.targetlevel_; 136 | rising_= src.rising_; 137 | ix_ = src.ix_; 138 | inc_ = src.inc_; 139 | down_ = src.down_; 140 | } 141 | 142 | -------------------------------------------------------------------------------- /main/env.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 Pascal Gauthier. 3 | * Copyright 2012 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef __ENV_H 19 | #define __ENV_H 20 | 21 | #include "synth.h" 22 | 23 | // DX7 envelope generation 24 | 25 | #define ACCURATE_ENVELOPE 26 | 27 | class Env { 28 | public: 29 | 30 | // The rates and levels arrays are calibrated to match the Dx7 parameters 31 | // (ie, value 0..99). The outlevel parameter is calibrated in microsteps 32 | // (ie units of approx .023 dB), with 99 * 32 = nominal full scale. The 33 | // rate_scaling parameter is in qRate units (ie 0..63). 34 | void init(const int rates[4], const int levels[4], int32_t outlevel, 35 | int rate_scaling); 36 | 37 | void update(const int rates[4], const int levels[4], int32_t outlevel, 38 | int rate_scaling); 39 | // Result is in Q24/doubling log format. Also, result is subsampled 40 | // for every N samples. 41 | // A couple more things need to happen for this to be used as a gain 42 | // value. First, the # of outputs scaling needs to be applied. Also, 43 | // modulation. 44 | // Then, of course, log to linear. 45 | int32_t getsample(); 46 | 47 | void keydown(bool down); 48 | static int scaleoutlevel(int outlevel); 49 | void getPosition(char *step); 50 | 51 | static void init_sr(FRAC_NUM sample_rate); 52 | void transfer(Env &src); 53 | 54 | private: 55 | 56 | // PG: This code is normalized to 44100, need to put a multiplier 57 | // if we are not using 44100. 58 | static uint32_t sr_multiplier; 59 | 60 | int rates_[4]; 61 | int levels_[4]; 62 | int outlevel_; 63 | int rate_scaling_; 64 | // Level is stored so that 2^24 is one doubling, ie 16 more bits than 65 | // the DX7 itself (fraction is stored in level rather than separate 66 | // counter) 67 | int32_t level_; 68 | int targetlevel_; 69 | bool rising_; 70 | int ix_; 71 | int inc_; 72 | #ifdef ACCURATE_ENVELOPE 73 | int staticcount_; 74 | #endif 75 | 76 | bool down_; 77 | 78 | void advance(int newix); 79 | }; 80 | 81 | #endif // __ENV_H 82 | 83 | -------------------------------------------------------------------------------- /main/exp2.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define _USE_MATH_DEFINES 18 | #include 19 | 20 | #include "synth.h" 21 | #include "exp2.h" 22 | 23 | #include 24 | 25 | #ifdef _MSC_VER 26 | #define exp2(arg) pow(2.0, arg) 27 | #endif 28 | 29 | 30 | 31 | int32_t exp2tab[EXP2_N_SAMPLES << 1]; 32 | 33 | void Exp2::init() { 34 | FRAC_NUM inc = exp2(1.0 / EXP2_N_SAMPLES); 35 | FRAC_NUM y = 1 << 30; 36 | for (int i = 0; i < EXP2_N_SAMPLES; i++) { 37 | exp2tab[(i << 1) + 1] = (int32_t)floor(y + 0.5); 38 | y *= inc; 39 | } 40 | for (int i = 0; i < EXP2_N_SAMPLES - 1; i++) { 41 | exp2tab[i << 1] = exp2tab[(i << 1) + 3] - exp2tab[(i << 1) + 1]; 42 | } 43 | exp2tab[(EXP2_N_SAMPLES << 1) - 2] = (1U << 31) - exp2tab[(EXP2_N_SAMPLES << 1) - 1]; 44 | } 45 | 46 | int32_t tanhtab[TANH_N_SAMPLES << 1]; 47 | 48 | static FRAC_NUM dtanh(FRAC_NUM y) { 49 | return 1 - y * y; 50 | } 51 | 52 | void Tanh::init() { 53 | FRAC_NUM step = 4.0 / TANH_N_SAMPLES; 54 | FRAC_NUM y = 0; 55 | for (int i = 0; i < TANH_N_SAMPLES; i++) { 56 | tanhtab[(i << 1) + 1] = (1 << 24) * y + 0.5; 57 | //printf("%d\n", tanhtab[(i << 1) + 1]); 58 | // Use a basic 4th order Runge-Kutte to compute tanh from its 59 | // differential equation. 60 | FRAC_NUM k1 = dtanh(y); 61 | FRAC_NUM k2 = dtanh(y + 0.5 * step * k1); 62 | FRAC_NUM k3 = dtanh(y + 0.5 * step * k2); 63 | FRAC_NUM k4 = dtanh(y + step * k3); 64 | FRAC_NUM dy = (step / 6) * (k1 + k4 + 2 * (k2 + k3)); 65 | y += dy; 66 | } 67 | for (int i = 0; i < TANH_N_SAMPLES - 1; i++) { 68 | tanhtab[i << 1] = tanhtab[(i << 1) + 3] - tanhtab[(i << 1) + 1]; 69 | } 70 | int32_t lasty = (1 << 24) * y + 0.5; 71 | tanhtab[(TANH_N_SAMPLES << 1) - 2] = lasty - tanhtab[(TANH_N_SAMPLES << 1) - 1]; 72 | } 73 | -------------------------------------------------------------------------------- /main/exp2.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class Exp2 { 18 | public: 19 | Exp2(); 20 | 21 | static void init(); 22 | 23 | // Q24 in, Q24 out 24 | static int32_t lookup(int32_t x); 25 | }; 26 | 27 | #define EXP2_LG_N_SAMPLES 10 28 | #define EXP2_N_SAMPLES (1 << EXP2_LG_N_SAMPLES) 29 | 30 | #define EXP2_INLINE 31 | 32 | extern int32_t exp2tab[EXP2_N_SAMPLES << 1]; 33 | 34 | #ifdef EXP2_INLINE 35 | inline 36 | int32_t Exp2::lookup(int32_t x) { 37 | const int SHIFT = 24 - EXP2_LG_N_SAMPLES; 38 | int lowbits = x & ((1 << SHIFT) - 1); 39 | int x_int = (x >> (SHIFT - 1)) & ((EXP2_N_SAMPLES - 1) << 1); 40 | int dy = exp2tab[x_int]; 41 | int y0 = exp2tab[x_int + 1]; 42 | 43 | int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); 44 | return y >> (6 - (x >> 24)); 45 | } 46 | #endif 47 | 48 | class Tanh { 49 | public: 50 | static void init(); 51 | 52 | // Q24 in, Q24 out 53 | static int32_t lookup(int32_t x); 54 | }; 55 | 56 | #define TANH_LG_N_SAMPLES 10 57 | #define TANH_N_SAMPLES (1 << TANH_LG_N_SAMPLES) 58 | 59 | extern int32_t tanhtab[TANH_N_SAMPLES << 1]; 60 | 61 | inline 62 | int32_t Tanh::lookup(int32_t x) { 63 | int32_t signum = x >> 31; 64 | x ^= signum; 65 | if (x >= (4 << 24)) { 66 | if (x >= (17 << 23)) { 67 | return signum ^ (1 << 24); 68 | } 69 | int32_t sx = ((int64_t)-48408812 * (int64_t)x) >> 24; 70 | return signum ^ ((1 << 24) - 2 * Exp2::lookup(sx)); 71 | } else { 72 | const int SHIFT = 26 - TANH_LG_N_SAMPLES; 73 | int lowbits = x & ((1 << SHIFT) - 1); 74 | int x_int = (x >> (SHIFT - 1)) & ((TANH_N_SAMPLES - 1) << 1); 75 | int dy = tanhtab[x_int]; 76 | int y0 = tanhtab[x_int + 1]; 77 | int y = y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); 78 | return y ^ signum; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /main/fm_core.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifdef VERBOSE 18 | #include 19 | #endif 20 | 21 | #include "synth.h" 22 | #include "exp2.h" 23 | #include "fm_op_kernel.h" 24 | #include "fm_core.h" 25 | 26 | 27 | //using namespace std; 28 | 29 | const FmAlgorithm FmCore::algorithms[32] = { 30 | { { 0xc1, 0x11, 0x11, 0x14, 0x01, 0x14 } }, // 1 31 | { { 0x01, 0x11, 0x11, 0x14, 0xc1, 0x14 } }, // 2 32 | { { 0xc1, 0x11, 0x14, 0x01, 0x11, 0x14 } }, // 3 33 | { { 0xc1, 0x11, 0x94, 0x01, 0x11, 0x14 } }, // 4 34 | { { 0xc1, 0x14, 0x01, 0x14, 0x01, 0x14 } }, // 5 35 | { { 0xc1, 0x94, 0x01, 0x14, 0x01, 0x14 } }, // 6 36 | { { 0xc1, 0x11, 0x05, 0x14, 0x01, 0x14 } }, // 7 37 | { { 0x01, 0x11, 0xc5, 0x14, 0x01, 0x14 } }, // 8 38 | { { 0x01, 0x11, 0x05, 0x14, 0xc1, 0x14 } }, // 9 39 | { { 0x01, 0x05, 0x14, 0xc1, 0x11, 0x14 } }, // 10 40 | { { 0xc1, 0x05, 0x14, 0x01, 0x11, 0x14 } }, // 11 41 | { { 0x01, 0x05, 0x05, 0x14, 0xc1, 0x14 } }, // 12 42 | { { 0xc1, 0x05, 0x05, 0x14, 0x01, 0x14 } }, // 13 43 | { { 0xc1, 0x05, 0x11, 0x14, 0x01, 0x14 } }, // 14 44 | { { 0x01, 0x05, 0x11, 0x14, 0xc1, 0x14 } }, // 15 45 | { { 0xc1, 0x11, 0x02, 0x25, 0x05, 0x14 } }, // 16 46 | { { 0x01, 0x11, 0x02, 0x25, 0xc5, 0x14 } }, // 17 47 | { { 0x01, 0x11, 0x11, 0xc5, 0x05, 0x14 } }, // 18 48 | { { 0xc1, 0x14, 0x14, 0x01, 0x11, 0x14 } }, // 19 49 | { { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x14 } }, // 20 50 | { { 0x01, 0x14, 0x14, 0xc1, 0x14, 0x14 } }, // 21 51 | { { 0xc1, 0x14, 0x14, 0x14, 0x01, 0x14 } }, // 22 52 | { { 0xc1, 0x14, 0x14, 0x01, 0x14, 0x04 } }, // 23 53 | { { 0xc1, 0x14, 0x14, 0x14, 0x04, 0x04 } }, // 24 54 | { { 0xc1, 0x14, 0x14, 0x04, 0x04, 0x04 } }, // 25 55 | { { 0xc1, 0x05, 0x14, 0x01, 0x14, 0x04 } }, // 26 56 | { { 0x01, 0x05, 0x14, 0xc1, 0x14, 0x04 } }, // 27 57 | { { 0x04, 0xc1, 0x11, 0x14, 0x01, 0x14 } }, // 28 58 | { { 0xc1, 0x14, 0x01, 0x14, 0x04, 0x04 } }, // 29 59 | { { 0x04, 0xc1, 0x11, 0x14, 0x04, 0x04 } }, // 30 60 | { { 0xc1, 0x14, 0x04, 0x04, 0x04, 0x04 } }, // 31 61 | { { 0xc4, 0x04, 0x04, 0x04, 0x04, 0x04 } }, // 32 62 | }; 63 | 64 | int n_out(const FmAlgorithm &alg) { 65 | int count = 0; 66 | for (int i = 0; i < 6; i++) { 67 | if ((alg.ops[i] & 7) == OUT_BUS_ADD) count++; 68 | } 69 | return count; 70 | } 71 | 72 | uint8_t FmCore::get_carrier_operators(uint8_t algorithm) 73 | { 74 | uint8_t op_out=0; 75 | FmAlgorithm alg=algorithms[algorithm]; 76 | 77 | for(uint8_t i=0; i<6;i++) 78 | { 79 | if((alg.ops[i]&OUT_BUS_ADD)==OUT_BUS_ADD) 80 | op_out|=1<"; 96 | cout << (flags & OUT_BUS_ONE ? "1" : flags & OUT_BUS_TWO ? "2" : "0"); 97 | if (flags & OUT_BUS_ADD) cout << "+"; 98 | //cout << alg.ops[j].in << "->" << alg.ops[j].out; 99 | if (flags & FB_OUT) cout << "]"; 100 | } 101 | cout << " " << n_out(alg); 102 | cout << endl; 103 | } 104 | #endif 105 | } 106 | 107 | void FmCore::render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_shift) { 108 | const int kLevelThresh = 1120; 109 | const FmAlgorithm alg = algorithms[algorithm]; 110 | bool has_contents[3] = { true, false, false }; 111 | for (int op = 0; op < 6; op++) { 112 | int flags = alg.ops[op]; 113 | bool add = (flags & OUT_BUS_ADD) != 0; 114 | FmOpParams ¶m = params[op]; 115 | int inbus = (flags >> 4) & 3; 116 | int outbus = flags & 3; 117 | int32_t *outptr = (outbus == 0) ? output : buf_[outbus - 1].get(); 118 | int32_t gain1 = param.gain_out; 119 | int32_t gain2 = Exp2::lookup(param.level_in - (14 * (1 << 24))); 120 | param.gain_out = gain2; 121 | 122 | if (gain1 >= kLevelThresh || gain2 >= kLevelThresh) { 123 | if (!has_contents[outbus]) { 124 | add = false; 125 | } 126 | if (inbus == 0 || !has_contents[inbus]) { 127 | // todo: more than one op in a feedback loop 128 | if ((flags & 0xc0) == 0xc0 && feedback_shift < 16) { 129 | // cout << op << " fb " << inbus << outbus << add << endl; 130 | FmOpKernel::compute_fb(outptr, param.phase, param.freq, 131 | gain1, gain2, 132 | fb_buf, feedback_shift, add); 133 | } else { 134 | // cout << op << " pure " << inbus << outbus << add << endl; 135 | FmOpKernel::compute_pure(outptr, param.phase, param.freq, 136 | gain1, gain2, add); 137 | } 138 | } else { 139 | // cout << op << " normal " << inbus << outbus << " " << param.freq << add << endl; 140 | FmOpKernel::compute(outptr, buf_[inbus - 1].get(), 141 | param.phase, param.freq, gain1, gain2, add); 142 | } 143 | has_contents[outbus] = true; 144 | } else if (!add) { 145 | has_contents[outbus] = false; 146 | } 147 | param.phase += param.freq << LG_N; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /main/fm_core.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef __FM_CORE_H 18 | #define __FM_CORE_H 19 | 20 | //#include 21 | #include "aligned_buf.h" 22 | #include "fm_op_kernel.h" 23 | #include "synth.h" 24 | #include "controllers.h" 25 | 26 | 27 | class FmOperatorInfo { 28 | public: 29 | int in; 30 | int out; 31 | }; 32 | 33 | enum FmOperatorFlags { 34 | OUT_BUS_ONE = 1 << 0, 35 | OUT_BUS_TWO = 1 << 1, 36 | OUT_BUS_ADD = 1 << 2, 37 | IN_BUS_ONE = 1 << 4, 38 | IN_BUS_TWO = 1 << 5, 39 | FB_IN = 1 << 6, 40 | FB_OUT = 1 << 7 41 | }; 42 | 43 | class FmAlgorithm { 44 | public: 45 | int ops[6]; 46 | }; 47 | 48 | class FmCore { 49 | public: 50 | virtual ~FmCore() {}; 51 | static void dump(); 52 | uint8_t get_carrier_operators(uint8_t algorithm); 53 | virtual void render(int32_t *output, FmOpParams *params, int algorithm, int32_t *fb_buf, int feedback_gain); 54 | protected: 55 | AlignedBufbuf_[2]; 56 | const static FmAlgorithm algorithms[32]; 57 | }; 58 | 59 | #endif // __FM_CORE_H 60 | -------------------------------------------------------------------------------- /main/fm_op_kernel.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | 21 | #ifdef HAVE_NEON 22 | #include 23 | #endif 24 | 25 | #include "synth.h" 26 | #include "sin.h" 27 | #include "fm_op_kernel.h" 28 | 29 | #ifdef HAVE_NEONx 30 | static bool hasNeon() { 31 | return true; 32 | return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; 33 | } 34 | 35 | extern "C" 36 | void neon_fm_kernel(const int *in, const int *busin, int *out, int count, 37 | int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); 38 | 39 | const int32_t __attribute__ ((aligned(16))) zeros[N] = {0}; 40 | 41 | #else 42 | static bool hasNeon() { 43 | return false; 44 | } 45 | #endif 46 | 47 | void FmOpKernel::compute(int32_t *output, const int32_t *input, 48 | int32_t phase0, int32_t freq, 49 | int32_t gain1, int32_t gain2, bool add) { 50 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 51 | int32_t gain = gain1; 52 | int32_t phase = phase0; 53 | if (hasNeon()) { 54 | #ifdef HAVE_NEON 55 | neon_fm_kernel(input, add ? output : zeros, output, _N_, 56 | phase0, freq, gain, dgain); 57 | #endif 58 | } else { 59 | if (add) { 60 | for (int i = 0; i < _N_; i++) { 61 | gain += dgain; 62 | int32_t y = Sin::lookup(phase + input[i]); 63 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 64 | output[i] += y1; 65 | phase += freq; 66 | } 67 | } else { 68 | for (int i = 0; i < _N_; i++) { 69 | gain += dgain; 70 | int32_t y = Sin::lookup(phase + input[i]); 71 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 72 | output[i] = y1; 73 | phase += freq; 74 | } 75 | } 76 | } 77 | } 78 | 79 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 80 | int32_t gain1, int32_t gain2, bool add) { 81 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 82 | int32_t gain = gain1; 83 | int32_t phase = phase0; 84 | if (hasNeon()) { 85 | #ifdef HAVE_NEON 86 | neon_fm_kernel(zeros, add ? output : zeros, output, _N_, 87 | phase0, freq, gain, dgain); 88 | #endif 89 | } else { 90 | if (add) { 91 | for (int i = 0; i < _N_; i++) { 92 | gain += dgain; 93 | int32_t y = Sin::lookup(phase); 94 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 95 | output[i] += y1; 96 | phase += freq; 97 | } 98 | } else { 99 | for (int i = 0; i < _N_; i++) { 100 | gain += dgain; 101 | int32_t y = Sin::lookup(phase); 102 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 103 | output[i] = y1; 104 | phase += freq; 105 | } 106 | } 107 | } 108 | } 109 | 110 | #define noDOUBLE_ACCURACY 111 | #define HIGH_ACCURACY 112 | 113 | void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, 114 | int32_t gain1, int32_t gain2, 115 | int32_t *fb_buf, int fb_shift, bool add) { 116 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 117 | int32_t gain = gain1; 118 | int32_t phase = phase0; 119 | int32_t y0 = fb_buf[0]; 120 | int32_t y = fb_buf[1]; 121 | if (add) { 122 | for (int i = 0; i < _N_; i++) { 123 | gain += dgain; 124 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 125 | y0 = y; 126 | y = Sin::lookup(phase + scaled_fb); 127 | y = ((int64_t)y * (int64_t)gain) >> 24; 128 | output[i] += y; 129 | phase += freq; 130 | } 131 | } else { 132 | for (int i = 0; i < _N_; i++) { 133 | gain += dgain; 134 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 135 | y0 = y; 136 | y = Sin::lookup(phase + scaled_fb); 137 | y = ((int64_t)y * (int64_t)gain) >> 24; 138 | output[i] = y; 139 | phase += freq; 140 | } 141 | } 142 | fb_buf[0] = y0; 143 | fb_buf[1] = y; 144 | } 145 | 146 | //////////////////////////////////////////////////////////////////////////////////// 147 | //////////////////////////////////////////////////////////////////////////////////// 148 | //////////////////////////////////////////////////////////////////////////////////// 149 | //////////////////////////////////////////////////////////////////////////////////// 150 | 151 | // Experimental sine wave generators below 152 | #if 0 153 | // Results: accuracy 64.3 mean, 170 worst case 154 | // high accuracy: 5.0 mean, 49 worst case 155 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 156 | int32_t gain1, int32_t gain2, bool add) { 157 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 158 | int32_t gain = gain1; 159 | int32_t phase = phase0; 160 | #ifdef HIGH_ACCURACY 161 | int32_t u = Sin::compute10(phase << 6); 162 | u = ((int64_t)u * gain) >> 30; 163 | int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle 164 | v = ((int64_t)v * gain) >> 30; 165 | int32_t s = Sin::compute10(freq << 6); 166 | int32_t c = Sin::compute10((freq << 6) + (1 << 28)); 167 | #else 168 | int32_t u = Sin::compute(phase); 169 | u = ((int64_t)u * gain) >> 24; 170 | int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle 171 | v = ((int64_t)v * gain) >> 24; 172 | int32_t s = Sin::compute(freq) << 6; 173 | int32_t c = Sin::compute(freq + (1 << 22)) << 6; 174 | #endif 175 | for (int i = 0; i < _N_; i++) { 176 | output[i] = u; 177 | int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; 178 | u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; 179 | v = t; 180 | } 181 | } 182 | #endif 183 | 184 | #if 0 185 | // Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5) 186 | // for freq < 0.25, 275.2 mean, 716 worst 187 | // high accuracy: 57.4 mean, 7559 worst 188 | // freq < 0.25: 17.9 mean, 78 worst 189 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 190 | int32_t gain1, int32_t gain2, bool add) { 191 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 192 | int32_t gain = gain1; 193 | int32_t phase = phase0; 194 | #ifdef HIGH_ACCURACY 195 | int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); 196 | int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); 197 | int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); 198 | #else 199 | int32_t u = Sin::compute(phase); 200 | u = ((int64_t)u * gain) >> 24; 201 | int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); 202 | v = ((int64_t)v * gain) >> 24; 203 | int32_t a = Sin::compute(freq >> 1) << 1; 204 | #endif 205 | for (int i = 0; i < _N_; i++) { 206 | output[i] = u; 207 | v -= ((int64_t)a * (int64_t)u) >> 24; 208 | u += ((int64_t)a * (int64_t)v) >> 24; 209 | } 210 | } 211 | #endif 212 | 213 | #if 0 214 | // Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5) 215 | // with FRAC_NUM accuracy initialization: mean 1.55, worst 58 (near freq = 0) 216 | // with high accuracy: mean 4.2, worst 292 (near freq = 0.5) 217 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 218 | int32_t gain1, int32_t gain2, bool add) { 219 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 220 | int32_t gain = gain1; 221 | int32_t phase = phase0; 222 | #ifdef DOUBLE_ACCURACY 223 | int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); 224 | FRAC_NUM a_d = sin(freq * (M_PI / (1 << 24))); 225 | int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * 226 | (M_PI / (1 << 23))) + 0.5); 227 | int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); 228 | #else 229 | #ifdef HIGH_ACCURACY 230 | int32_t u = Sin::compute10(phase << 6); 231 | int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); 232 | int32_t a = Sin::compute10(freq << 5); 233 | v = ((int64_t)v * (int64_t)a) >> 29; 234 | int32_t aa = ((int64_t)a * (int64_t)a) >> 29; 235 | #else 236 | int32_t u = Sin::compute(phase) << 6; 237 | int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); 238 | int32_t a = Sin::compute(freq >> 1); 239 | v = ((int64_t)v * (int64_t)a) >> 17; 240 | int32_t aa = ((int64_t)a * (int64_t)a) >> 17; 241 | #endif 242 | #endif 243 | 244 | if (aa < 0) aa = (1 << 31) - 1; 245 | for (int i = 0; i < _N_; i++) { 246 | gain += dgain; 247 | output[i] = ((int64_t)u * (int64_t)gain) >> 30; 248 | v -= ((int64_t)aa * (int64_t)u) >> 29; 249 | u += v; 250 | } 251 | } 252 | #endif 253 | 254 | #if 0 255 | // Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5) 256 | // high accuracy 2.9 mean, 143 worst 257 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 258 | int32_t gain1, int32_t gain2, bool add) { 259 | int32_t dgain = (gain2 - gain1 + (N >> 1)) >> LG_N; 260 | int32_t gain = gain1; 261 | int32_t phase = phase0; 262 | #ifdef HIGH_ACCURACY 263 | int32_t u = Sin::compute10(phase << 6); 264 | int32_t lastu = Sin::compute10((phase - freq) << 6); 265 | int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; 266 | #else 267 | int32_t u = Sin::compute(phase) << 6; 268 | int32_t lastu = Sin::compute(phase - freq) << 6; 269 | int32_t a = Sin::compute(freq + (1 << 22)) << 7; 270 | #endif 271 | if (a < 0 && freq < 256) a = (1 << 31) - 1; 272 | if (a > 0 && freq > 0x7fff00) a = -(1 << 31); 273 | for (int i = 0; i < _N_; i++) { 274 | gain += dgain; 275 | output[i] = ((int64_t)u * (int64_t)gain) >> 30; 276 | //output[i] = u; 277 | int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; 278 | lastu = u; 279 | u = newu; 280 | } 281 | } 282 | #endif 283 | 284 | -------------------------------------------------------------------------------- /main/fm_op_kernel.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include 18 | 19 | #include 20 | 21 | #ifdef HAVE_NEON 22 | #include 23 | #endif 24 | 25 | #include "synth.h" 26 | #include "sin.h" 27 | #include "fm_op_kernel.h" 28 | #include "config.h" 29 | 30 | #ifdef HAVE_NEON 31 | static bool hasNeon() { 32 | return true; 33 | return (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0; 34 | } 35 | 36 | extern "C" 37 | void neon_fm_kernel(const int *in, const int *busin, int *out, int count, 38 | int32_t phase0, int32_t freq, int32_t gain1, int32_t dgain); 39 | 40 | const int32_t __attribute__ ((aligned(16))) zeros[_N_] = {0}; 41 | 42 | #else 43 | static bool hasNeon() { 44 | return false; 45 | } 46 | #endif 47 | 48 | void FmOpKernel::compute(int32_t *output, const int32_t *input, 49 | int32_t phase0, int32_t freq, 50 | int32_t gain1, int32_t gain2, bool add) { 51 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 52 | int32_t gain = gain1; 53 | int32_t phase = phase0; 54 | if (hasNeon()) { 55 | #ifdef HAVE_NEON 56 | neon_fm_kernel(input, add ? output : zeros, output, _N_, 57 | phase0, freq, gain, dgain); 58 | #endif 59 | } else { 60 | if (add) { 61 | for (int i = 0; i < _N_; i++) { 62 | gain += dgain; 63 | int32_t y = Sin::lookup(phase + input[i]); 64 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 65 | output[i] += y1; 66 | phase += freq; 67 | } 68 | } else { 69 | for (int i = 0; i < _N_; i++) { 70 | gain += dgain; 71 | int32_t y = Sin::lookup(phase + input[i]); 72 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 73 | output[i] = y1; 74 | phase += freq; 75 | } 76 | } 77 | } 78 | } 79 | 80 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 81 | int32_t gain1, int32_t gain2, bool add) { 82 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 83 | int32_t gain = gain1; 84 | int32_t phase = phase0; 85 | if (hasNeon()) { 86 | #ifdef HAVE_NEON 87 | neon_fm_kernel(zeros, add ? output : zeros, output, _N_, 88 | phase0, freq, gain, dgain); 89 | #endif 90 | } else { 91 | if (add) { 92 | for (int i = 0; i < _N_; i++) { 93 | gain += dgain; 94 | int32_t y = Sin::lookup(phase); 95 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 96 | output[i] += y1; 97 | phase += freq; 98 | } 99 | } else { 100 | for (int i = 0; i < _N_; i++) { 101 | gain += dgain; 102 | int32_t y = Sin::lookup(phase); 103 | int32_t y1 = ((int64_t)y * (int64_t)gain) >> 24; 104 | output[i] = y1; 105 | phase += freq; 106 | } 107 | } 108 | } 109 | } 110 | 111 | #define noDOUBLE_ACCURACY 112 | #define HIGH_ACCURACY 113 | 114 | void FmOpKernel::compute_fb(int32_t *output, int32_t phase0, int32_t freq, 115 | int32_t gain1, int32_t gain2, 116 | int32_t *fb_buf, int fb_shift, bool add) { 117 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 118 | int32_t gain = gain1; 119 | int32_t phase = phase0; 120 | int32_t y0 = fb_buf[0]; 121 | int32_t y = fb_buf[1]; 122 | if (add) { 123 | for (int i = 0; i < _N_; i++) { 124 | gain += dgain; 125 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 126 | y0 = y; 127 | y = Sin::lookup(phase + scaled_fb); 128 | y = ((int64_t)y * (int64_t)gain) >> 24; 129 | output[i] += y; 130 | phase += freq; 131 | } 132 | } else { 133 | for (int i = 0; i < _N_; i++) { 134 | gain += dgain; 135 | int32_t scaled_fb = (y0 + y) >> (fb_shift + 1); 136 | y0 = y; 137 | y = Sin::lookup(phase + scaled_fb); 138 | y = ((int64_t)y * (int64_t)gain) >> 24; 139 | output[i] = y; 140 | phase += freq; 141 | } 142 | } 143 | fb_buf[0] = y0; 144 | fb_buf[1] = y; 145 | } 146 | 147 | //////////////////////////////////////////////////////////////////////////////////// 148 | //////////////////////////////////////////////////////////////////////////////////// 149 | //////////////////////////////////////////////////////////////////////////////////// 150 | //////////////////////////////////////////////////////////////////////////////////// 151 | 152 | // Experimental sine wave generators below 153 | #if 0 154 | // Results: accuracy 64.3 mean, 170 worst case 155 | // high accuracy: 5.0 mean, 49 worst case 156 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 157 | int32_t gain1, int32_t gain2, bool add) { 158 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 159 | int32_t gain = gain1; 160 | int32_t phase = phase0; 161 | #ifdef HIGH_ACCURACY 162 | int32_t u = Sin::compute10(phase << 6); 163 | u = ((int64_t)u * gain) >> 30; 164 | int32_t v = Sin::compute10((phase << 6) + (1 << 28)); // quarter cycle 165 | v = ((int64_t)v * gain) >> 30; 166 | int32_t s = Sin::compute10(freq << 6); 167 | int32_t c = Sin::compute10((freq << 6) + (1 << 28)); 168 | #else 169 | int32_t u = Sin::compute(phase); 170 | u = ((int64_t)u * gain) >> 24; 171 | int32_t v = Sin::compute(phase + (1 << 22)); // quarter cycle 172 | v = ((int64_t)v * gain) >> 24; 173 | int32_t s = Sin::compute(freq) << 6; 174 | int32_t c = Sin::compute(freq + (1 << 22)) << 6; 175 | #endif 176 | for (int i = 0; i < _N_; i++) { 177 | output[i] = u; 178 | int32_t t = ((int64_t)v * (int64_t)c - (int64_t)u * (int64_t)s) >> 30; 179 | u = ((int64_t)u * (int64_t)c + (int64_t)v * (int64_t)s) >> 30; 180 | v = t; 181 | } 182 | } 183 | #endif 184 | 185 | #if 0 186 | // Results: accuracy 392.3 mean, 15190 worst case (near freq = 0.5) 187 | // for freq < 0.25, 275.2 mean, 716 worst 188 | // high accuracy: 57.4 mean, 7559 worst 189 | // freq < 0.25: 17.9 mean, 78 worst 190 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 191 | int32_t gain1, int32_t gain2, bool add) { 192 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 193 | int32_t gain = gain1; 194 | int32_t phase = phase0; 195 | #ifdef HIGH_ACCURACY 196 | int32_t u = floor(gain * sin(phase * (M_PI / (1 << 23))) + 0.5); 197 | int32_t v = floor(gain * cos((phase - freq * 0.5) * (M_PI / (1 << 23))) + 0.5); 198 | int32_t a = floor((1 << 25) * sin(freq * (M_PI / (1 << 24))) + 0.5); 199 | #else 200 | int32_t u = Sin::compute(phase); 201 | u = ((int64_t)u * gain) >> 24; 202 | int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); 203 | v = ((int64_t)v * gain) >> 24; 204 | int32_t a = Sin::compute(freq >> 1) << 1; 205 | #endif 206 | for (int i = 0; i < _N_; i++) { 207 | output[i] = u; 208 | v -= ((int64_t)a * (int64_t)u) >> 24; 209 | u += ((int64_t)a * (int64_t)v) >> 24; 210 | } 211 | } 212 | #endif 213 | 214 | #if 0 215 | // Results: accuracy 370.0 mean, 15480 worst case (near freq = 0.5) 216 | // with FRAC_NUM accuracy initialization: mean 1.55, worst 58 (near freq = 0) 217 | // with high accuracy: mean 4.2, worst 292 (near freq = 0.5) 218 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 219 | int32_t gain1, int32_t gain2, bool add) { 220 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 221 | int32_t gain = gain1; 222 | int32_t phase = phase0; 223 | #ifdef DOUBLE_ACCURACY 224 | int32_t u = floor((1 << 30) * sin(phase * (M_PI / (1 << 23))) + 0.5); 225 | FRAC_NUM a_d = sin(freq * (M_PI / (1 << 24))); 226 | int32_t v = floor((1LL << 31) * a_d * cos((phase - freq * 0.5) * 227 | (M_PI / (1 << 23))) + 0.5); 228 | int32_t aa = floor((1LL << 31) * a_d * a_d + 0.5); 229 | #else 230 | #ifdef HIGH_ACCURACY 231 | int32_t u = Sin::compute10(phase << 6); 232 | int32_t v = Sin::compute10((phase << 6) + (1 << 28) - (freq << 5)); 233 | int32_t a = Sin::compute10(freq << 5); 234 | v = ((int64_t)v * (int64_t)a) >> 29; 235 | int32_t aa = ((int64_t)a * (int64_t)a) >> 29; 236 | #else 237 | int32_t u = Sin::compute(phase) << 6; 238 | int32_t v = Sin::compute(phase + (1 << 22) - (freq >> 1)); 239 | int32_t a = Sin::compute(freq >> 1); 240 | v = ((int64_t)v * (int64_t)a) >> 17; 241 | int32_t aa = ((int64_t)a * (int64_t)a) >> 17; 242 | #endif 243 | #endif 244 | 245 | if (aa < 0) aa = (1 << 31) - 1; 246 | for (int i = 0; i < _N_; i++) { 247 | gain += dgain; 248 | output[i] = ((int64_t)u * (int64_t)gain) >> 30; 249 | v -= ((int64_t)aa * (int64_t)u) >> 29; 250 | u += v; 251 | } 252 | } 253 | #endif 254 | 255 | #if 0 256 | // Results:: accuracy 112.3 mean, 4262 worst (near freq = 0.5) 257 | // high accuracy 2.9 mean, 143 worst 258 | void FmOpKernel::compute_pure(int32_t *output, int32_t phase0, int32_t freq, 259 | int32_t gain1, int32_t gain2, bool add) { 260 | int32_t dgain = (gain2 - gain1 + (_N_ >> 1)) >> LG_N; 261 | int32_t gain = gain1; 262 | int32_t phase = phase0; 263 | #ifdef HIGH_ACCURACY 264 | int32_t u = Sin::compute10(phase << 6); 265 | int32_t lastu = Sin::compute10((phase - freq) << 6); 266 | int32_t a = Sin::compute10((freq << 6) + (1 << 28)) << 1; 267 | #else 268 | int32_t u = Sin::compute(phase) << 6; 269 | int32_t lastu = Sin::compute(phase - freq) << 6; 270 | int32_t a = Sin::compute(freq + (1 << 22)) << 7; 271 | #endif 272 | if (a < 0 && freq < 256) a = (1 << 31) - 1; 273 | if (a > 0 && freq > 0x7fff00) a = -(1 << 31); 274 | for (int i = 0; i < _N_; i++) { 275 | gain += dgain; 276 | output[i] = ((int64_t)u * (int64_t)gain) >> 30; 277 | //output[i] = u; 278 | int32_t newu = (((int64_t)u * (int64_t)a) >> 30) - lastu; 279 | lastu = u; 280 | u = newu; 281 | } 282 | } 283 | #endif 284 | 285 | -------------------------------------------------------------------------------- /main/fm_op_kernel.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef __FM_OP_KERNEL_H 18 | #define __FM_OP_KERNEL_H 19 | 20 | struct FmOpParams { 21 | int32_t level_in; // value to be computed (from level to gain[0]) 22 | int32_t gain_out; // computed value (gain[1] to gain[0]) 23 | int32_t freq; 24 | int32_t phase; 25 | }; 26 | 27 | class FmOpKernel { 28 | public: 29 | // gain1 and gain2 represent linear step: gain for sample i is 30 | // gain1 + (1 + i) / 64 * (gain2 - gain1) 31 | 32 | // This is the basic FM operator. No feedback. 33 | static void compute(int32_t *output, const int32_t *input, 34 | int32_t phase0, int32_t freq, 35 | int32_t gain1, int32_t gain2, bool add); 36 | 37 | // This is a sine generator, no feedback. 38 | static void compute_pure(int32_t *output, int32_t phase0, int32_t freq, 39 | int32_t gain1, int32_t gain2, bool add); 40 | 41 | // One op with feedback, no add. 42 | static void compute_fb(int32_t *output, int32_t phase0, int32_t freq, 43 | int32_t gain1, int32_t gain2, 44 | int32_t *fb_buf, int fb_gain, bool add); 45 | }; 46 | 47 | #endif 48 | -------------------------------------------------------------------------------- /main/freqlut.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Resolve frequency signal (1.0 in Q24 format = 1 octave) to phase delta. 18 | 19 | // The LUT is just a global, and we'll need the init function to be called before 20 | // use. 21 | 22 | #include 23 | #include 24 | 25 | #include "freqlut.h" 26 | 27 | #define LG_N_SAMPLES 10 28 | #define N_SAMPLES (1 << LG_N_SAMPLES) 29 | #define SAMPLE_SHIFT (24 - LG_N_SAMPLES) 30 | 31 | #define MAX_LOGFREQ_INT 20 32 | 33 | int32_t lut[N_SAMPLES + 1]; 34 | 35 | void Freqlut::init(FRAC_NUM sample_rate) { 36 | FRAC_NUM y = (1LL << (24 + MAX_LOGFREQ_INT)) / sample_rate; 37 | FRAC_NUM inc = pow(2, 1.0 / N_SAMPLES); 38 | for (int i = 0; i < N_SAMPLES + 1; i++) { 39 | lut[i] = (int32_t)floor(y + 0.5); 40 | y *= inc; 41 | } 42 | } 43 | 44 | // Note: if logfreq is more than 20.0, the results will be inaccurate. However, 45 | // that will be many times the Nyquist rate. 46 | int32_t Freqlut::lookup(int32_t logfreq) { 47 | int ix = (logfreq & 0xffffff) >> SAMPLE_SHIFT; 48 | 49 | int32_t y0 = lut[ix]; 50 | int32_t y1 = lut[ix + 1]; 51 | int lowbits = logfreq & ((1 << SAMPLE_SHIFT) - 1); 52 | int32_t y = y0 + ((((int64_t)(y1 - y0) * (int64_t)lowbits)) >> SAMPLE_SHIFT); 53 | int hibits = logfreq >> 24; 54 | return y >> (MAX_LOGFREQ_INT - hibits); 55 | } 56 | -------------------------------------------------------------------------------- /main/freqlut.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "synth.h" 18 | 19 | class Freqlut { 20 | public: 21 | static void init(FRAC_NUM sample_rate); 22 | static int32_t lookup(int32_t logfreq); 23 | }; 24 | -------------------------------------------------------------------------------- /main/lfo.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Low frequency oscillator, compatible with DX7 18 | 19 | #include "synth.h" 20 | 21 | #include "sin.h" 22 | #include "lfo.h" 23 | 24 | uint32_t Lfo::unit_; 25 | 26 | void Lfo::init(FRAC_NUM sample_rate) { 27 | // constant is 1 << 32 / 15.5s / 11 28 | Lfo::unit_ = (int32_t)(_N_ * 25190424 / sample_rate + 0.5); 29 | } 30 | 31 | void Lfo::reset(const uint8_t params[6]) { 32 | int rate = params[0]; // 0..99 33 | int sr = rate == 0 ? 1 : (165 * rate) >> 6; 34 | sr *= sr < 160 ? 11 : (11 + ((sr - 160) >> 4)); 35 | delta_ = unit_ * sr; 36 | int a = 99 - params[1]; // LFO delay 37 | if (a == 99) { 38 | delayinc_ = ~0u; 39 | delayinc2_ = ~0u; 40 | } else { 41 | a = (16 + (a & 15)) << (1 + (a >> 4)); 42 | delayinc_ = unit_ * a; 43 | a &= 0xff80; 44 | a = max(0x80, a); 45 | delayinc2_ = unit_ * a; 46 | } 47 | waveform_ = params[5]; 48 | sync_ = params[4] != 0; 49 | } 50 | 51 | int32_t Lfo::getsample() { 52 | phase_ += delta_; 53 | int32_t x; 54 | switch (waveform_) { 55 | case 0: // triangle 56 | x = phase_ >> 7; 57 | x ^= -(phase_ >> 31); 58 | x &= (1 << 24) - 1; 59 | return x; 60 | case 1: // sawtooth down 61 | return (~phase_ ^ (1U << 31)) >> 8; 62 | case 2: // sawtooth up 63 | return (phase_ ^ (1U << 31)) >> 8; 64 | case 3: // square 65 | return ((~phase_) >> 7) & (1 << 24); 66 | case 4: // sine 67 | return (1 << 23) + (Sin::lookup(phase_ >> 8) >> 1); 68 | case 5: // s&h 69 | if (phase_ < delta_) { 70 | randstate_ = (randstate_ * 179 + 17) & 0xff; 71 | } 72 | x = randstate_ ^ 0x80; 73 | return (x + 1) << 16; 74 | } 75 | return 1 << 23; 76 | } 77 | 78 | int32_t Lfo::getdelay() { 79 | uint32_t delta = delaystate_ < (1U << 31) ? delayinc_ : delayinc2_; 80 | uint64_t d = ((uint64_t)delaystate_) + delta; 81 | if (d > ~0u) { 82 | return 1 << 24; 83 | } 84 | delaystate_ = d; 85 | if (d < (1U << 31)) { 86 | return 0; 87 | } else { 88 | return (d >> 7) & ((1 << 24) - 1); 89 | } 90 | } 91 | 92 | void Lfo::keydown() { 93 | if (sync_) { 94 | phase_ = (1U << 31) - 1; 95 | } 96 | delaystate_ = 0; 97 | } 98 | -------------------------------------------------------------------------------- /main/lfo.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | // Low frequency oscillator, compatible with DX7 18 | 19 | class Lfo { 20 | public: 21 | static void init(FRAC_NUM sample_rate); 22 | void reset(const uint8_t params[6]); 23 | 24 | // result is 0..1 in Q24 25 | int32_t getsample(); 26 | 27 | // result is 0..1 in Q24 28 | int32_t getdelay(); 29 | 30 | void keydown(); 31 | private: 32 | static uint32_t unit_; 33 | 34 | uint32_t phase_; // Q32 35 | uint32_t delta_; 36 | uint8_t waveform_; 37 | uint8_t randstate_; 38 | bool sync_; 39 | 40 | uint32_t delaystate_; 41 | uint32_t delayinc_; 42 | uint32_t delayinc2_; 43 | }; 44 | -------------------------------------------------------------------------------- /main/main.cpp: -------------------------------------------------------------------------------- 1 | // MicroDexed for ESP32, beta 1.0, by nyh. 2 | // Thanks to Holger W. for the MicroDexed code: https://github.com/dcoredump/MicroDexed 3 | // 4 | // The sample program plays a short tune (Holst's Jupiter) using MicroDexed engine using the I2S module (PCM5102). 5 | // 6 | // Also, thanks to Len Shustek for the Miditones code: https://github.com/LenShustek/miditones 7 | // 8 | // Note: This only works with 8 notes - more than that the whole thing will stutter. 9 | // 10 | // Suggestions are welcome to improve this with more than 8 notes. In the meantime I'm still finding ways and means 11 | // to improve the FM synth algorithm. 12 | // 13 | // A slight modification is done on dexed.cpp -> the SSAT instruction is replaced by CLAMPS because ESP32 doesn't 14 | // have SSAT. Fundamentally, CLAMPS works the same as SSAT. 15 | // 16 | // As this is a beta, the code can be very messy. Again, I'm working to reorganize the code as much as possible. 17 | // 18 | // FreeRTOS settings: 1000Hz tick rate. 19 | 20 | #include "freertos/FreeRTOS.h" 21 | #include "esp_wifi.h" 22 | #include "esp_system.h" 23 | #include "esp_event.h" 24 | #include "esp_event_loop.h" 25 | #include "nvs_flash.h" 26 | #include "driver/gpio.h" 27 | 28 | #include "driver/i2s.h" 29 | #include "driver/timer.h" 30 | #include "esp_task_wdt.h" 31 | 32 | #include "dexed.h" 33 | #include "config.h" 34 | #include "dx7rom.h" 35 | 36 | #include "playtune.h" 37 | 38 | #define BUFFER_SIZE 256 // Buffer size in samples! 39 | 40 | char voice_name[11]; 41 | TaskHandle_t nTask1 = NULL, nTask2 = NULL; 42 | uint32_t overload = 0; 43 | 44 | esp_err_t event_handler(void *ctx, system_event_t *event) 45 | { 46 | return ESP_OK; 47 | } 48 | 49 | int16_t audio_buffer[BUFFER_SIZE]; 50 | 51 | void microDexedMainTask(void* pvParameter) { 52 | uint32_t bytesWritten = 0; 53 | 54 | TickType_t xLastWakeTime; 55 | const TickType_t xFrequency = pdMS_TO_TICKS(5); 56 | xLastWakeTime = xTaskGetTickCount(); 57 | 58 | memset(audio_buffer, 0x00, sizeof(audio_buffer)); 59 | // For debugging purposes! (Measure execution times) 60 | gpio_set_direction(GPIO_NUM_17, GPIO_MODE_OUTPUT); 61 | gpio_set_direction(GPIO_NUM_16, GPIO_MODE_OUTPUT); 62 | gpio_set_level(GPIO_NUM_16, 0); 63 | gpio_set_level(GPIO_NUM_17, 0); 64 | //------------------------------------------------------- 65 | 66 | printf("Starting microDexedMainTask...\n"); 67 | printf("microDexedMainTask running on core: %d\n", xPortGetCoreID()); 68 | 69 | //xLastWakeTime = xTaskGetTickCount(); 70 | 71 | printf("starting time: %llu\n", esp_timer_get_time()); 72 | 73 | while (1) 74 | { 75 | // This is a very naive method of giving the idle task a chance to do its work! 76 | // Every 256 samples, or 5.805ms, another 1ms will be given to the idle task. 77 | for (auto i = 0; i < 256; i++) 78 | { 79 | ((DexedMiditones *)pvParameter)->getSamples(BUFFER_SIZE, audio_buffer); 80 | i2s_write(I2S_NUM_0, audio_buffer, sizeof(audio_buffer), &bytesWritten, portMAX_DELAY); 81 | } 82 | vTaskDelay(1); 83 | } 84 | } 85 | 86 | void playTask(void* pvParameter) { 87 | Miditones mdt; 88 | 89 | printf("Starting playTask...\n"); 90 | printf("playTask running on core: %d\n", xPortGetCoreID()); 91 | 92 | while (1) 93 | { 94 | ulTaskNotifyTake( pdTRUE, portMAX_DELAY ); 95 | mdt.updateNote((DexedMiditones*)pvParameter); 96 | } 97 | } 98 | 99 | extern "C" void app_main(void) 100 | { 101 | nvs_flash_init(); 102 | //float vol = 1.00f; 103 | //float vol_left = 1.00f; 104 | //float vol_right = 1.00f; 105 | 106 | // b = bank. 107 | // v = voice number. 108 | 109 | static const i2s_config_t i2s_config = { 110 | .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX), 111 | .sample_rate = 44100, 112 | .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, 113 | .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, 114 | .communication_format = (i2s_comm_format_t)(I2S_COMM_FORMAT_I2S | I2S_COMM_FORMAT_I2S_MSB), 115 | .intr_alloc_flags = 0, // default interrupt priority 116 | .dma_buf_count = 16, 117 | .dma_buf_len = BUFFER_SIZE*2, 118 | .use_apll = false, 119 | .tx_desc_auto_clear = true 120 | }; 121 | 122 | static const i2s_pin_config_t pin_config = { 123 | .bck_io_num = 26, 124 | .ws_io_num = 25, 125 | .data_out_num = 22, 126 | .data_in_num = I2S_PIN_NO_CHANGE 127 | }; 128 | 129 | i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); //install and start i2s driver 130 | i2s_set_pin(I2S_NUM_0, &pin_config); 131 | 132 | uint8_t v = 7; 133 | uint8_t voice_number = v; 134 | 135 | // voice data? 136 | uint8_t data[128]; 137 | 138 | //Dexed* dexed = new Dexed(SAMPLE_RATE); 139 | static DexedMiditones* dxmiditones = new DexedMiditones(SAMPLE_RATE); 140 | 141 | // load directly from the file! 142 | // we deal directly with one bank first! 143 | // one bank has 32 voices! 144 | for (auto n = 0; n < 4096; n++) 145 | { 146 | uint8_t d = dx7rom[n + 6]; 147 | if (n >= voice_number * 128 && n < (voice_number + 1) * 128) 148 | { 149 | data[n - (voice_number * 128)] = d; 150 | } 151 | } 152 | 153 | //dexed->loadSysexVoice(data); 154 | dxmiditones->loadSysexVoice(data); 155 | 156 | printf("Testing Dexed on ESP32!\n"); 157 | printf("Voice name: %s\n", voice_name); 158 | 159 | TickType_t xLastWakeTime; 160 | const TickType_t xFrequency = pdMS_TO_TICKS(100); 161 | 162 | xTaskCreate(µDexedMainTask, "microDexedMainTask", 4096, dxmiditones, 2, &nTask1); 163 | xTaskCreatePinnedToCore(&playTask,"playTask",2048,dxmiditones, 1, &nTask2, 0 ); 164 | 165 | gpio_set_direction(GPIO_NUM_4, GPIO_MODE_OUTPUT); 166 | int level = 0; 167 | while (true) { 168 | vTaskDelayUntil(&xLastWakeTime, xFrequency); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /main/midinotes.h: -------------------------------------------------------------------------------- 1 | /************************************************* 2 | * MIDI note values 3 | *************************************************/ 4 | 5 | #ifndef _MIDINOTES_H 6 | #define _MIDINOTES_H 7 | 8 | #define MIDI_A0 21 9 | #define MIDI_AIS0 22 10 | #define MIDI_B0 23 11 | #define MIDI_C1 24 12 | #define MIDI_CIS1 25 13 | #define MIDI_D1 26 14 | #define MIDI_DIS1 27 15 | #define MIDI_E1 28 16 | #define MIDI_F1 29 17 | #define MIDI_FIS1 30 18 | #define MIDI_G1 31 19 | #define MIDI_GIS1 32 20 | #define MIDI_A1 33 21 | #define MIDI_AIS1 34 22 | #define MIDI_B1 35 23 | #define MIDI_C2 36 24 | #define MIDI_CIS2 37 25 | #define MIDI_D2 38 26 | #define MIDI_DIS2 39 27 | #define MIDI_E2 40 28 | #define MIDI_F2 41 29 | #define MIDI_FIS2 42 30 | #define MIDI_G2 43 31 | #define MIDI_GIS2 44 32 | #define MIDI_A2 45 33 | #define MIDI_AIS2 46 34 | #define MIDI_B2 47 35 | #define MIDI_C3 48 36 | #define MIDI_CIS3 49 37 | #define MIDI_D3 50 38 | #define MIDI_DIS3 51 39 | #define MIDI_E3 52 40 | #define MIDI_F3 53 41 | #define MIDI_FIS3 54 42 | #define MIDI_G3 55 43 | #define MIDI_GIS3 56 44 | #define MIDI_A3 57 45 | #define MIDI_AIS3 58 46 | #define MIDI_B3 59 47 | #define MIDI_C4 60 48 | #define MIDI_CIS4 61 49 | #define MIDI_D4 62 50 | #define MIDI_DIS4 63 51 | #define MIDI_E4 64 52 | #define MIDI_F4 65 53 | #define MIDI_FIS4 66 54 | #define MIDI_G4 67 55 | #define MIDI_GIS4 68 56 | #define MIDI_A4 69 57 | #define MIDI_AIS4 70 58 | #define MIDI_B4 71 59 | #define MIDI_C5 72 60 | #define MIDI_CIS5 73 61 | #define MIDI_D5 74 62 | #define MIDI_DIS5 75 63 | #define MIDI_E5 76 64 | #define MIDI_F5 77 65 | #define MIDI_FIS5 78 66 | #define MIDI_G5 79 67 | #define MIDI_GIS5 80 68 | #define MIDI_A5 81 69 | #define MIDI_AIS5 82 70 | #define MIDI_B5 83 71 | #define MIDI_C6 84 72 | #define MIDI_CIS6 85 73 | #define MIDI_D6 86 74 | #define MIDI_DIS6 87 75 | #define MIDI_E6 88 76 | #define MIDI_F6 89 77 | #define MIDI_FIS6 90 78 | #define MIDI_G6 91 79 | #define MIDI_GIS6 92 80 | #define MIDI_A6 93 81 | #define MIDI_AIS6 94 82 | #define MIDI_B6 95 83 | #define MIDI_C7 96 84 | #define MIDI_CIS7 97 85 | #define MIDI_D7 98 86 | #define MIDI_DIS7 99 87 | #define MIDI_E7 100 88 | #define MIDI_F7 101 89 | #define MIDI_FIS7 102 90 | #define MIDI_G7 103 91 | #define MIDI_GIS7 104 92 | #define MIDI_A7 105 93 | #define MIDI_AIS7 106 94 | #define MIDI_B7 107 95 | #define MIDI_C8 108 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /main/module.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef SYNTH_MODULE_H 18 | #define SYNTH_MODULE_H 19 | 20 | #include 21 | 22 | class Module { 23 | public: 24 | static const int lg_n = 6; 25 | static const int n = 1 << lg_n; 26 | virtual void process(const int32_t **inbufs, const int32_t *control_in, 27 | const int32_t *control_last, int32_t **outbufs) = 0; 28 | }; 29 | 30 | #endif // SYNTH_MODULE_H 31 | 32 | -------------------------------------------------------------------------------- /main/pitchenv.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "synth.h" 18 | #include "pitchenv.h" 19 | 20 | int PitchEnv::unit_; 21 | 22 | void PitchEnv::init(FRAC_NUM sample_rate) { 23 | unit_ = _N_ * (1 << 24) / (21.3 * sample_rate) + 0.5; 24 | } 25 | 26 | const uint8_t pitchenv_rate[] = { 27 | 1, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 28 | 12, 13, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 29 | 25, 26, 27, 28, 30, 31, 33, 34, 36, 37, 38, 39, 41, 42, 44, 46, 47, 30 | 49, 51, 53, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 79, 82, 31 | 85, 88, 91, 94, 98, 102, 106, 110, 115, 120, 125, 130, 135, 141, 147, 32 | 153, 159, 165, 171, 178, 185, 193, 202, 211, 232, 243, 254, 255 33 | }; 34 | 35 | const int8_t pitchenv_tab[] = { 36 | -128, -116, -104, -95, -85, -76, -68, -61, -56, -52, -49, -46, -43, 37 | -41, -39, -37, -35, -33, -32, -31, -30, -29, -28, -27, -26, -25, -24, 38 | -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12, -11, -10, 39 | -9, -8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 40 | 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 41 | 28, 29, 30, 31, 32, 33, 34, 35, 38, 40, 43, 46, 49, 53, 58, 65, 73, 42 | 82, 92, 103, 115, 127 43 | }; 44 | 45 | void PitchEnv::set(const int r[4], const int l[4]) { 46 | for (int i = 0; i < 4; i++) { 47 | rates_[i] = r[i]; 48 | levels_[i] = l[i]; 49 | } 50 | level_ = pitchenv_tab[l[3]] << 19; 51 | down_ = true; 52 | advance(0); 53 | } 54 | 55 | int32_t PitchEnv::getsample() { 56 | if (ix_ < 3 || ((ix_ < 4) && !down_)) { 57 | if (rising_) { 58 | level_ += inc_; 59 | if (level_ >= targetlevel_) { 60 | level_ = targetlevel_; 61 | advance(ix_ + 1); 62 | } 63 | } else { // !rising 64 | level_ -= inc_; 65 | if (level_ <= targetlevel_) { 66 | level_ = targetlevel_; 67 | advance(ix_ + 1); 68 | } 69 | } 70 | } 71 | return level_; 72 | } 73 | 74 | void PitchEnv::keydown(bool d) { 75 | if (down_ != d) { 76 | down_ = d; 77 | advance(d ? 0 : 3); 78 | } 79 | } 80 | 81 | void PitchEnv::advance(int newix) { 82 | ix_ = newix; 83 | if (ix_ < 4) { 84 | int newlevel = levels_[ix_]; 85 | targetlevel_ = pitchenv_tab[newlevel] << 19; 86 | rising_ = (targetlevel_ > level_); 87 | inc_ = pitchenv_rate[rates_[ix_]] * unit_; 88 | } 89 | } 90 | 91 | void PitchEnv::getPosition(char *step) { 92 | *step = ix_; 93 | } 94 | 95 | 96 | -------------------------------------------------------------------------------- /main/pitchenv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef __PITCHENV_H 18 | #define __PITCHENV_H 19 | 20 | // Computation of the DX7 pitch envelope 21 | 22 | class PitchEnv { 23 | public: 24 | static void init(FRAC_NUM sample_rate); 25 | 26 | // The rates and levels arrays are calibrated to match the Dx7 parameters 27 | // (ie, value 0..99). 28 | void set(const int rates[4], const int levels[4]); 29 | 30 | // Result is in Q24/octave 31 | int32_t getsample(); 32 | void keydown(bool down); 33 | void getPosition(char *step); 34 | private: 35 | static int unit_; 36 | int rates_[4]; 37 | int levels_[4]; 38 | int32_t level_; 39 | int targetlevel_; 40 | bool rising_; 41 | int ix_; 42 | int inc_; 43 | 44 | bool down_; 45 | 46 | void advance(int newix); 47 | }; 48 | 49 | extern const uint8_t pitchenv_rate[]; 50 | extern const int8_t pitchenv_tab[]; 51 | 52 | #endif // __PITCHENV_H 53 | 54 | -------------------------------------------------------------------------------- /main/playtune.cpp: -------------------------------------------------------------------------------- 1 | #include "freertos/FreeRTOS.h" 2 | #include "freertos/task.h" 3 | #include "freertos/queue.h" 4 | 5 | #include "esp_wifi.h" 6 | #include "esp_system.h" 7 | #include "esp_event.h" 8 | #include "esp_event_loop.h" 9 | #include "nvs_flash.h" 10 | #include "driver/gpio.h" 11 | #include "driver/i2s.h" 12 | #include "driver/timer.h" 13 | #include "math.h" 14 | 15 | #include "playtune.h" 16 | #include "songdata.h" 17 | 18 | #include "dexed.h" 19 | 20 | unsigned char noteState[8]; 21 | 22 | extern TaskHandle_t nTask2; 23 | 24 | Miditones::Miditones() { 25 | initTimer0(); 26 | timePlay = 0; 27 | timePlayCount = 0; 28 | songIndex = 0; 29 | speed = 1.00f; 30 | isPlaying = 0; 31 | 32 | xSemaphore = xSemaphoreCreateBinary(); 33 | } 34 | 35 | void Miditones::updateNote(void* pvParameter) { 36 | unsigned char cmd = songData1[songIndex]; 37 | unsigned char opcode = cmd & 0xf0; 38 | unsigned char chan = cmd & 0x0f; 39 | //unsigned char noteCount = 0; 40 | 41 | //printf("cmd: %x\n", cmd); 42 | 43 | if (opcode == 0x90) { 44 | while (opcode == 0x90) { 45 | // Play a note here! Reset accumulators and set tuning words from the next note! 46 | // Also reset decay and attack states! 47 | 48 | ((DexedMiditones*)pvParameter)->notePlay(songData1[songIndex + 1], 0xff); 49 | 50 | noteState[chan] = songData1[songIndex + 1]; 51 | 52 | songIndex += 2; 53 | cmd = songData1[songIndex]; 54 | opcode = cmd & 0xf0; 55 | chan = cmd & 0x0f; 56 | } 57 | 58 | } 59 | 60 | if (opcode == 0x80) { 61 | while (opcode == 0x80) { 62 | // Stop note: the note is dampened immediately in order to prevent the click 63 | // once the next note is played. Not all the clicks in the sound can be removed: this is currently 64 | // being investigated. 65 | 66 | //noteCount = noteCount & 0x7; 67 | ((DexedMiditones*)pvParameter)->noteStop(noteState[chan]); 68 | //noteCount++; 69 | 70 | songIndex += 1; 71 | cmd = songData1[songIndex]; 72 | opcode = cmd & 0xf0; 73 | chan = cmd & 0x0f; 74 | timePlay = 20; 75 | } 76 | //unsigned char noteCount = 0; 77 | return; 78 | } 79 | 80 | if (opcode == 0xf0) { // stop playing score! 81 | isPlaying = 0; 82 | return; 83 | } 84 | 85 | if (opcode == 0xe0) { // start playing from beginning! 86 | songIndex = 0; 87 | timePlay = 0; 88 | timePlayCount = 0; 89 | return; 90 | } 91 | 92 | //cmd = songData1[songIndex]; 93 | //opcode = cmd & 0xf0; 94 | 95 | if ( ((opcode & 0x80) == 0) || ((opcode & 0x90) == 0) ) { 96 | timePlay = (unsigned int)( ((songData1[songIndex] << 8) | songData1[songIndex + 1]) * (1/speed) ); 97 | songIndex += 2; 98 | } 99 | }; 100 | 101 | float Miditones::speed = 1.00f; 102 | uint32_t Miditones::timePlay = 0; 103 | uint32_t Miditones::timePlayCount = 0; 104 | uint32_t Miditones::songIndex = 0; 105 | bool Miditones::isPlaying = 0; 106 | SemaphoreHandle_t Miditones::xSemaphore; 107 | QueueHandle_t Miditones::mdtQueue; 108 | 109 | void IRAM_ATTR timer_group0_isr(void *para) { 110 | unsigned char tempVal = 0; 111 | static BaseType_t xHigherPriorityTaskWoken; 112 | static int level; 113 | 114 | int timer_idx = (int)para; 115 | uint32_t intr_status = TIMERG0.int_st_timers.val; 116 | if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) 117 | { 118 | TIMERG0.hw_timer[timer_idx].update = 1; 119 | TIMERG0.int_clr_timers.t0 = 1; 120 | TIMERG0.hw_timer[timer_idx].config.alarm_en = 1; 121 | 122 | if (Miditones::timePlayCount > Miditones::timePlay) 123 | { 124 | Miditones::timePlayCount = 0; 125 | vTaskNotifyGiveFromISR( nTask2, &xHigherPriorityTaskWoken ); 126 | } 127 | else 128 | Miditones::timePlayCount++; 129 | 130 | } 131 | 132 | portYIELD_FROM_ISR( ); 133 | } 134 | 135 | // reference: https://github.com/sankarcheppali/esp_idf_esp32_posts/blob/master/timer_group/main/timer_group.c 136 | void Miditones::initTimer0() { 137 | /* Select and initialize basic parameters of the timer */ 138 | 139 | const auto TIMER_DIVIDER = 80; 140 | const auto TIMER_SCALE = (TIMER_BASE_CLK / TIMER_DIVIDER); 141 | const auto timer_interval_sec = 0.001; 142 | 143 | timer_config_t config; 144 | config.divider = TIMER_DIVIDER; 145 | config.counter_dir = TIMER_COUNT_UP; 146 | config.counter_en = TIMER_PAUSE; 147 | config.alarm_en = TIMER_ALARM_EN; 148 | config.intr_type = TIMER_INTR_LEVEL; 149 | config.auto_reload = 1; 150 | timer_init(TIMER_GROUP_0, TIMER_0, &config); 151 | 152 | /* Timer's counter will initially start from value below. 153 | Also, if auto_reload is set, this value will be automatically reload on alarm */ 154 | timer_set_counter_value(TIMER_GROUP_0, TIMER_0, 0x00000000ULL); 155 | 156 | /* Configure the alarm value and the interrupt on alarm. */ 157 | timer_set_alarm_value(TIMER_GROUP_0, TIMER_0, (uint64_t)(timer_interval_sec * TIMER_SCALE)); 158 | timer_enable_intr(TIMER_GROUP_0, TIMER_0); 159 | 160 | timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_group0_isr, 161 | (void *) TIMER_0, ESP_INTR_FLAG_IRAM, NULL); 162 | 163 | timer_start(TIMER_GROUP_0, TIMER_0); 164 | 165 | 166 | }; 167 | 168 | void updateNote(void* pvParameter, unsigned char& isPlaying, unsigned int& timePlay, unsigned int& timePlayCount, unsigned int& songIndex, float& speed) { 169 | 170 | unsigned char cmd = songData1[songIndex]; 171 | unsigned char opcode = cmd & 0xf0; 172 | unsigned char chan = cmd & 0x0f; 173 | //unsigned char noteCount = 0; 174 | 175 | //printf("cmd: %x\n", cmd); 176 | 177 | if (opcode == 0x90) { 178 | while (opcode == 0x90) { 179 | // Play a note here! Reset accumulators and set tuning words from the next note! 180 | // Also reset decay and attack states! 181 | 182 | ((DexedMiditones*)pvParameter)->notePlay(songData1[songIndex + 1], 0xff); 183 | 184 | noteState[chan] = songData1[songIndex + 1]; 185 | 186 | songIndex += 2; 187 | cmd = songData1[songIndex]; 188 | opcode = cmd & 0xf0; 189 | chan = cmd & 0x0f; 190 | } 191 | 192 | } 193 | 194 | if (opcode == 0x80) { 195 | while (opcode == 0x80) { 196 | // Stop note: the note is dampened immediately in order to prevent the click 197 | // once the next note is played. Not all the clicks in the sound can be removed: this is currently 198 | // being investigated. 199 | 200 | //noteCount = noteCount & 0x7; 201 | ((DexedMiditones*)pvParameter)->noteStop(noteState[chan]); 202 | //noteCount++; 203 | 204 | songIndex += 1; 205 | cmd = songData1[songIndex]; 206 | opcode = cmd & 0xf0; 207 | chan = cmd & 0x0f; 208 | timePlay = 20; 209 | } 210 | //unsigned char noteCount = 0; 211 | return; 212 | } 213 | 214 | if (opcode == 0xf0) { // stop playing score! 215 | isPlaying = 0; 216 | return; 217 | } 218 | 219 | if (opcode == 0xe0) { // start playing from beginning! 220 | songIndex = 0; 221 | timePlay = 0; 222 | timePlayCount = 0; 223 | return; 224 | } 225 | 226 | //cmd = songData1[songIndex]; 227 | //opcode = cmd & 0xf0; 228 | 229 | if ( ((opcode & 0x80) == 0) || ((opcode & 0x90) == 0) ) { 230 | timePlay = (unsigned int)( ((songData1[songIndex] << 8) | songData1[songIndex + 1]) * (1/speed) ); 231 | songIndex += 2; 232 | } 233 | } -------------------------------------------------------------------------------- /main/playtune.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYTUNE_H 2 | #define PLAYTUNE_H 3 | 4 | #include "dexed.h" 5 | #include "freertos/FreeRTOS.h" 6 | 7 | // I prefer to leave the library untouched! 8 | class DexedMiditones : public Dexed { 9 | public: 10 | DexedMiditones(int rate) : Dexed(rate) {}; 11 | void notePlay(uint8_t note, uint8_t velocity) { keydown(note, velocity); } 12 | void noteStop(uint8_t note) { keyup(note); } 13 | }; 14 | 15 | class Miditones { 16 | public: 17 | Miditones(); 18 | void updateNote(void* pvParameter); 19 | inline SemaphoreHandle_t getSemaphore() { return xSemaphore; } 20 | inline QueueHandle_t getQueue() { return mdtQueue; } 21 | //static void IRAM_ATTR timer_group0_isr(void *para); 22 | 23 | private: 24 | void initTimer0(); 25 | static unsigned int timePlay; 26 | static unsigned int timePlayCount; 27 | static unsigned int songIndex; 28 | static float speed; 29 | static bool isPlaying; 30 | 31 | friend void IRAM_ATTR timer_group0_isr(void *para); 32 | 33 | static SemaphoreHandle_t xSemaphore; 34 | static QueueHandle_t mdtQueue; 35 | }; 36 | 37 | void updateNote(void* pvParameter, unsigned char& isPlaying, unsigned int& timePlay, unsigned int& timePlayCount, unsigned int& songIndex, float& speed); 38 | 39 | #endif -------------------------------------------------------------------------------- /main/sin.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define _USE_MATH_DEFINES 18 | #include 19 | 20 | #include "synth.h" 21 | #include "sin.h" 22 | 23 | #define R (1 << 29) 24 | 25 | #ifdef SIN_DELTA 26 | int32_t sintab[SIN_N_SAMPLES << 1]; 27 | #else 28 | int32_t sintab[SIN_N_SAMPLES + 1]; 29 | #endif 30 | 31 | void Sin::init() { 32 | FRAC_NUM dphase = 2 * M_PI / SIN_N_SAMPLES; 33 | //int32_t c = (int32_t)floor(cos(dphase) * (1 << 30) + 0.5); 34 | int32_t c = (int32_t)floor(COS_FUNC(dphase) * (1 << 30) + 0.5); 35 | //int32_t s = (int32_t)floor(sin(dphase) * (1 << 30) + 0.5); 36 | int32_t s = (int32_t)floor(SIN_FUNC(dphase) * (1 << 30) + 0.5); 37 | int32_t u = 1 << 30; 38 | int32_t v = 0; 39 | for (int i = 0; i < SIN_N_SAMPLES / 2; i++) { 40 | #ifdef SIN_DELTA 41 | sintab[(i << 1) + 1] = (v + 32) >> 6; 42 | sintab[((i + SIN_N_SAMPLES / 2) << 1) + 1] = -((v + 32) >> 6); 43 | #else 44 | sintab[i] = (v + 32) >> 6; 45 | sintab[i + SIN_N_SAMPLES / 2] = -((v + 32) >> 6); 46 | #endif 47 | int32_t t = ((int64_t)u * (int64_t)s + (int64_t)v * (int64_t)c + R) >> 30; 48 | u = ((int64_t)u * (int64_t)c - (int64_t)v * (int64_t)s + R) >> 30; 49 | v = t; 50 | } 51 | #ifdef SIN_DELTA 52 | for (int i = 0; i < SIN_N_SAMPLES - 1; i++) { 53 | sintab[i << 1] = sintab[(i << 1) + 3] - sintab[(i << 1) + 1]; 54 | } 55 | sintab[(SIN_N_SAMPLES << 1) - 2] = -sintab[(SIN_N_SAMPLES << 1) - 1]; 56 | #else 57 | sintab[SIN_N_SAMPLES] = 0; 58 | #endif 59 | } 60 | 61 | #ifndef SIN_INLINE 62 | int32_t Sin::lookup(int32_t phase) { 63 | const int SHIFT = 24 - SIN_LG_N_SAMPLES; 64 | int lowbits = phase & ((1 << SHIFT) - 1); 65 | #ifdef SIN_DELTA 66 | int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1); 67 | int dy = sintab[phase_int]; 68 | int y0 = sintab[phase_int + 1]; 69 | 70 | return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); 71 | #else 72 | int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1); 73 | int y0 = sintab[phase_int]; 74 | int y1 = sintab[phase_int + 1]; 75 | 76 | return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT); 77 | #endif 78 | } 79 | #endif 80 | 81 | 82 | #if 0 83 | // The following is an implementation designed not to use any lookup tables, 84 | // based on the following implementation by Basile Graf: 85 | // http://www.rossbencina.com/static/code/sinusoids/even_polynomial_sin_approximation.txt 86 | 87 | #define C0 (1 << 24) 88 | #define C1 (331121857 >> 2) 89 | #define C2 (1084885537 >> 4) 90 | #define C3 (1310449902 >> 6) 91 | 92 | int32_t Sin::compute(int32_t phase) { 93 | int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22); 94 | int32_t x2 = ((int64_t)x * (int64_t)x) >> 22; 95 | int32_t x4 = ((int64_t)x2 * (int64_t)x2) >> 24; 96 | int32_t x6 = ((int64_t)x2 * (int64_t)x4) >> 24; 97 | int32_t y = C0 - 98 | (((int64_t)C1 * (int64_t)x2) >> 24) + 99 | (((int64_t)C2 * (int64_t)x4) >> 24) - 100 | (((int64_t)C3 * (int64_t)x6) >> 24); 101 | y ^= -((phase >> 23) & 1); 102 | return y; 103 | } 104 | #endif 105 | 106 | #if 1 107 | // coefficients are Chebyshev polynomial, computed by compute_cos_poly.py 108 | #define C8_0 16777216 109 | #define C8_2 -331168742 110 | #define C8_4 1089453524 111 | #define C8_6 -1430910663 112 | #define C8_8 950108533 113 | 114 | int32_t Sin::compute(int32_t phase) { 115 | int32_t x = (phase & ((1 << 23) - 1)) - (1 << 22); 116 | int32_t x2 = ((int64_t)x * (int64_t)x) >> 16; 117 | int32_t y = (((((((((((((int64_t)C8_8 118 | * (int64_t)x2) >> 32) + C8_6) 119 | * (int64_t)x2) >> 32) + C8_4) 120 | * (int64_t)x2) >> 32) + C8_2) 121 | * (int64_t)x2) >> 32) + C8_0); 122 | y ^= -((phase >> 23) & 1); 123 | return y; 124 | } 125 | #endif 126 | 127 | #define C10_0 (1 << 30) 128 | #define C10_2 -1324675874 // scaled * 4 129 | #define C10_4 1089501821 130 | #define C10_6 -1433689867 131 | #define C10_8 1009356886 132 | #define C10_10 -421101352 133 | int32_t Sin::compute10(int32_t phase) { 134 | int32_t x = (phase & ((1 << 29) - 1)) - (1 << 28); 135 | int32_t x2 = ((int64_t)x * (int64_t)x) >> 26; 136 | int32_t y = ((((((((((((((((int64_t)C10_10 137 | * (int64_t)x2) >> 34) + C10_8) 138 | * (int64_t)x2) >> 34) + C10_6) 139 | * (int64_t)x2) >> 34) + C10_4) 140 | * (int64_t)x2) >> 32) + C10_2) 141 | * (int64_t)x2) >> 30) + C10_0); 142 | y ^= -((phase >> 29) & 1); 143 | return y; 144 | } 145 | -------------------------------------------------------------------------------- /main/sin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | class Sin { 18 | public: 19 | Sin(); 20 | 21 | static void init(); 22 | static int32_t lookup(int32_t phase); 23 | static int32_t compute(int32_t phase); 24 | 25 | // A more accurate sine, both input and output Q30 26 | static int32_t compute10(int32_t phase); 27 | }; 28 | 29 | #define SIN_LG_N_SAMPLES 10 30 | #define SIN_N_SAMPLES (1 << SIN_LG_N_SAMPLES) 31 | 32 | #define SIN_INLINE 33 | 34 | // Use twice as much RAM for the LUT but avoid a little computation 35 | #define SIN_DELTA 36 | 37 | #ifdef SIN_DELTA 38 | extern int32_t sintab[SIN_N_SAMPLES << 1]; 39 | #else 40 | extern int32_t sintab[SIN_N_SAMPLES + 1]; 41 | #endif 42 | 43 | #ifdef SIN_INLINE 44 | inline 45 | int32_t Sin::lookup(int32_t phase) { 46 | const int SHIFT = 24 - SIN_LG_N_SAMPLES; 47 | int lowbits = phase & ((1 << SHIFT) - 1); 48 | #ifdef SIN_DELTA 49 | int phase_int = (phase >> (SHIFT - 1)) & ((SIN_N_SAMPLES - 1) << 1); 50 | int dy = sintab[phase_int]; 51 | int y0 = sintab[phase_int + 1]; 52 | 53 | return y0 + (((int64_t)dy * (int64_t)lowbits) >> SHIFT); 54 | #else 55 | int phase_int = (phase >> SHIFT) & (SIN_N_SAMPLES - 1); 56 | int y0 = sintab[phase_int]; 57 | int y1 = sintab[phase_int + 1]; 58 | 59 | return y0 + (((int64_t)(y1 - y0) * (int64_t)lowbits) >> SHIFT); 60 | #endif 61 | } 62 | #endif 63 | -------------------------------------------------------------------------------- /main/songdata.h: -------------------------------------------------------------------------------- 1 | #ifndef SONGDATA_H 2 | #define SONGDATA_H 3 | 4 | // Playtune bytestream for file "holst_jupiter.mid" created by MIDITONES V1.12 on Sun Jan 13 20:48:01 2019 5 | // command line: miditones holst_jupiter 6 | const unsigned char songData1[] = { 7 | 0,0, 0x90,55, 1,119, 0x91,58, 0,6, 0x80, 1,92, 0x81, 0,20, 0x90,60, 0x91,44, 0x92,51, 2,238, 0x80, 0x90,60, 8 | 0x81, 0x82, 1,119, 0x91,63, 0,6, 0x80, 1,92, 0x81, 0,20, 0x90,62, 0x91,46, 0x92,53, 2,50, 0x93,58, 0,8, 9 | 0x80, 0,167, 0x83, 0,11, 0x90,63, 0x81, 0x82, 0x91,43, 0x92,51, 1,119, 0x93,65, 0,6, 0x80, 1,92, 0x83, 0,20, 10 | 0x90,63, 0x81, 0x82, 2,238, 0x80, 0x90,58, 0x91,62, 0x92,43, 0x93,51, 2,238, 0x80, 0x81, 0x90,60, 0x82, 0x83, 0x91,44, 0x92,51, 11 | 1,119, 0x93,62, 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,60, 0x81, 0x82, 2,238, 0x80, 0x90,58, 0x91,46, 0x92,50, 2,238, 12 | 0x80, 0x90,55, 0x81, 0x82, 0x91,48, 0x92,51, 5,220, 0x80, 0x90,55, 0x81, 0x82, 0x91,46, 0x92,50, 1,119, 0x93,58, 0,6, 13 | 0x80, 1,92, 0x83, 0,20, 0x90,60, 0x81, 0x82, 0x91,44, 0x92,51, 2,238, 0x80, 0x90,60, 0x81, 0x82, 1,119, 0x91,63, 0,6, 14 | 0x80, 1,92, 0x81, 0,20, 0x90,62, 0x91,46, 0x92,53, 2,50, 0x93,58, 0,8, 0x80, 0,167, 0x83, 0,11, 0x90,63, 15 | 0x81, 0x82, 0x91,43, 0x92,51, 1,119, 0x93,65, 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,67, 0x81, 0x82, 2,238, 0x80, 0x90,58, 16 | 0x91,67, 0x92,39, 1,119, 0x82, 0x92,43, 1,119, 0x80, 0x81, 0x90,60, 0x91,67, 0x82, 0x92,44, 0x93,51, 1,119, 0x94,65, 17 | 0,6, 0x80, 0x81, 1,92, 0x84, 0,20, 0x90,63, 0x82, 0x83, 2,238, 0x80, 0x90,60, 0x91,65, 0x92,48, 0x93,56, 2,238, 18 | 0x80, 0x81, 0x90,63, 0x82, 0x83, 2,238, 0x91,51, 0x92,55, 2,238, 0x80, 0x90,70, 0x81, 0x82, 0x91,43, 0x92,51, 1,119, 0x93,67, 19 | 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,65, 0x81, 0x82, 0x91,46, 0x92,53, 2,238, 0x80, 0x90,65, 0x81, 0x82, 2,238, 0x80, 20 | 0x90,63, 0x91,48, 0x92,55, 1,119, 0x93,67, 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,65, 0x81, 0x82, 0x91,50, 0x92,58, 21 | 2,238, 0x80, 0x90,58, 0x81, 0x80, 0x82, 2,238, 0x90,70, 0x91,43, 0x92,51, 1,119, 0x93,67, 0,6, 0x80, 1,92, 0x83, 22 | 0,20, 0x90,65, 0x81, 0x82, 0x91,46, 0x92,53, 2,238, 0x80, 0x90,65, 0x81, 0x82, 2,238, 0x80, 0x90,67, 0x91,51, 0x92,58, 23 | 1,119, 0x93,70, 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,72, 0x81, 0x82, 0x91,56, 2,238, 0x81, 0x91,55, 2,238, 0x80, 24 | 0x90,72, 0x81, 0x91,53, 0x92,56, 1,119, 0x93,74, 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,72, 0x93,75, 0x81, 0x82, 0x91,48, 25 | 0x92,55, 2,238, 0x80, 0x83, 0x90,70, 0x93,74, 0x81, 0x82, 0x91,46, 0x92,55, 2,238, 0x80, 0x83, 0x90,68, 0x93,72, 0x81, 0x82, 26 | 0x91,44, 0x92,51, 2,238, 0x80, 0x83, 0x90,67, 0x93,70, 0x81, 0x82, 0x91,43, 0x92,51, 2,238, 0x80, 0x83, 0x90,63, 0x93,75, 27 | 0x81, 0x82, 0x91,41, 0x92,51, 2,238, 0x80, 0x83, 0x90,63, 0x93,67, 0x81, 0x82, 0x91,39, 0x92,51, 2,238, 0x80, 0x83, 0x90,65, 28 | 0x81, 0x82, 0x91,44, 0x92,53, 1,119, 0x80, 0x90,63, 1,119, 0x80, 0x90,65, 0x81, 0x82, 0x91,53, 0x92,56, 2,238, 0x80, 0x90,67, 29 | 0x81, 0x82, 0x91,51, 0x92,58, 2,238, 0x80, 0x90,65, 0x93,70, 0x81, 0x82, 0x91,50, 2,238, 0x81, 0x91,48, 2,238, 0x80, 0x83, 30 | 0x90,63, 0x92,67, 0x81, 0x91,46, 1,119, 0x93,70, 0,6, 0x80, 0x82, 1,92, 0x83, 0,20, 0x90,63, 0x92,72, 0x81, 0x91,44, 31 | 0x93,51, 2,238, 0x80, 0x82, 0x90,72, 0x81, 0x83, 1,119, 0x91,75, 0,6, 0x80, 1,92, 0x81, 0,20, 0x90,65, 0x91,74, 32 | 0x92,46, 0x93,53, 2,50, 0x94,70, 0,8, 0x80, 0x81, 0,167, 0x84, 0,11, 0x90,70, 0x91,75, 0x82, 0x83, 0x92,43, 0x93,51, 33 | 1,119, 0x94,77, 0,6, 0x80, 0x81, 1,92, 0x84, 0,20, 0x90,75, 0x82, 0x83, 2,238, 0x80, 0x90,70, 0x91,74, 0x92,43, 34 | 0x93,51, 2,238, 0x80, 0x81, 0x90,72, 0x82, 0x83, 0x91,44, 0x92,51, 1,119, 0x93,74, 0,6, 0x80, 1,92, 0x83, 0,20, 35 | 0x90,72, 0x81, 0x82, 2,238, 0x80, 0x90,62, 0x91,70, 0x92,46, 0x93,53, 2,238, 0x80, 0x81, 0x90,63, 0x91,67, 0x82, 0x83, 0x92,48, 36 | 0x93,51, 5,220, 0x80, 0x81, 0x90,67, 0x82, 0x83, 0x91,46, 0x92,50, 1,119, 0x93,70, 0,6, 0x80, 1,92, 0x83, 0,20, 37 | 0x90,72, 0x81, 0x82, 0x91,44, 0x92,51, 2,238, 0x80, 0x90,72, 0x81, 0x82, 1,119, 0x91,75, 0,6, 0x80, 1,92, 0x81, 0,20, 38 | 0x90,74, 0x91,46, 0x92,53, 2,50, 0x93,70, 0,8, 0x80, 0,167, 0x83, 0,11, 0x90,75, 0x81, 0x82, 0x91,43, 0x92,51, 39 | 1,119, 0x93,77, 0,6, 0x80, 1,92, 0x83, 0,20, 0x90,79, 0x81, 0x82, 2,238, 0x80, 0x90,67, 0x91,79, 0x92,39, 1,119, 40 | 0x82, 0x92,43, 1,119, 0x80, 0x81, 0x90,79, 0x82, 0x91,44, 0x92,51, 1,119, 0x93,77, 0,6, 0x80, 1,92, 0x83, 0,20, 41 | 0x90,75, 0x81, 0x82, 2,238, 0x80, 0x90,68, 0x91,77, 0x92,48, 0x93,56, 2,238, 0x80, 0x81, 0x90,67, 0x91,75, 0x82, 0x83, 0x92,39, 42 | 0x93,55, 5,220, 0x80, 0x81, 0x82, 0x83, 0xf0}; 43 | // This score contains 933 bytes, and 5 tone generators are used. 44 | 45 | #endif -------------------------------------------------------------------------------- /main/synth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2012 Google Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef __SYNTH_H 18 | #define __SYNTH_H 19 | 20 | //#include 21 | //#define SUPER_PRECISE 22 | //#include 23 | 24 | // This IS not be present on MSVC. 25 | // See http://stackoverflow.com/questions/126279/c99-stdint-h-header-and-ms-visual-studio 26 | #include 27 | #ifdef _MSC_VER 28 | typedef __int32 int32_t; 29 | typedef unsigned __int32 uint32_t; 30 | typedef __int16 SInt16; 31 | #endif 32 | 33 | #define LG_N 6 34 | #define _N_ (1 << LG_N) 35 | 36 | #if defined(__APPLE__) 37 | #include 38 | #define SynthMemoryBarrier() OSMemoryBarrier() 39 | #elif defined(__GNUC__) 40 | #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) 41 | #define SynthMemoryBarrier() __sync_synchronize() 42 | #endif 43 | #endif 44 | 45 | 46 | // #undef SynthMemoryBarrier() 47 | 48 | #ifndef SynthMemoryBarrier 49 | // need to understand why this must be defined 50 | // #warning Memory barrier is not enabled 51 | #define SynthMemoryBarrier() 52 | #endif 53 | 54 | template 55 | inline static T min(const T& a, const T& b) { 56 | return a < b ? a : b; 57 | } 58 | 59 | template 60 | inline static T max(const T& a, const T& b) { 61 | return a > b ? a : b; 62 | } 63 | 64 | #define QER(n,b) ( ((float)n)/(1<