diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb750b8..16a746c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,16 +13,21 @@ jobs: run: | python -m pip install --upgrade pip pip install --upgrade platformio - - name: build OC_teensy40 + - name: build run: | git submodule update --init - pio run -e OC_teensy40 + pio run export hash=$(git rev-parse --short HEAD) - mv .pio/build/*/firmware.hex ./firmware_OC_T40_$hash.hex mv .github/workflows/LICENSE.txt ./LICENSE.txt - zip -j ./firmware_OC_T40_$hash.zip ./firmware_OC_T40_$hash.hex ./LICENSE.txt - curl -f -X PUT -u ${{ secrets.UPLOAD_KEY }} ${{ secrets.LATEST_DROP_FOLDER }}/firmware_OC_T40_latest.zip --upload-file ./firmware_OC_T40_$hash.zip - curl -f -X PUT -u ${{ secrets.UPLOAD_KEY }} ${{ secrets.LATEST_DROP_FOLDER }}/firmware_latest.sha -d "$hash" + for f in .pio/build/*/*.hex; do + FIRMWARE=$(basename $(dirname ${f%.*})) + HEX_FILE=./firmware_${FIRMWARE}_$hash.hex + ZIP_FILE=./firmware_${FIRMWARE}_$hash.zip + mv -v "$f" $HEX_FILE + zip -j $ZIP_FILE $HEX_FILE ./LICENSE.txt + curl -X PUT -u ${{ secrets.UPLOAD_KEY }} ${{ secrets.LATEST_DROP_FOLDER }}/firmware_${FIRMWARE}_latest.zip --upload-file $ZIP_FILE || true + curl -X PUT -u ${{ secrets.UPLOAD_KEY }} ${{ secrets.LATEST_DROP_FOLDER }}/firmware_${FIRMWARE}_latest.sha -d "$hash" + done - name: firmware_artifacts uses: actions/upload-artifact@v2 with: diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 01f863b..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,68 +0,0 @@ -// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY -// -// PIO Unified Debugger -// -// Documentation: https://docs.platformio.org/page/plus/debugging.html -// Configuration: https://docs.platformio.org/page/projectconf/section_env_debug.html - -{ - "version": "0.2.0", - "configurations": [ - { - "name": "(gdb) Launch", - "type": "cppdbg", - "request": "launch", - "program": "${workspaceFolder}/.test/test.exe", - "args": [], - "stopAtEntry": false, - "cwd": "${workspaceFolder}/.test/", - "environment": [], - "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - }, - { - "description": "Set Disassembly Flavor to Intel", - "text": "-gdb-set disassembly-flavor intel", - "ignoreFailures": true - } - ] - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug", - "executable": "/home/master/git/squares-and-circles/.pio/build/OC_teensy40/firmware.elf", - "projectEnvName": "OC_teensy40", - "toolchainBinDir": "/home/master/.platformio/packages/toolchain-gccarmnoneeabi/bin", - "internalConsoleOptions": "openOnSessionStart", - "preLaunchTask": { - "type": "PlatformIO", - "task": "Pre-Debug" - } - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug (skip Pre-Debug)", - "executable": "/home/master/git/squares-and-circles/.pio/build/OC_teensy40/firmware.elf", - "projectEnvName": "OC_teensy40", - "toolchainBinDir": "/home/master/.platformio/packages/toolchain-gccarmnoneeabi/bin", - "internalConsoleOptions": "openOnSessionStart" - }, - { - "type": "platformio-debug", - "request": "launch", - "name": "PIO Debug (without uploading)", - "executable": "/home/master/git/squares-and-circles/.pio/build/OC_teensy40/firmware.elf", - "projectEnvName": "OC_teensy40", - "toolchainBinDir": "/home/master/.platformio/packages/toolchain-gccarmnoneeabi/bin", - "internalConsoleOptions": "openOnSessionStart", - "loadMode": "manual" - } - ] -} diff --git a/README.md b/README.md index c4b5ad9..bb10d33 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,8 @@ When I am asked why I do such projects - my general reply is: I don't know, it makes fun and I like to get deeper understanding of technical things. Maybe the reason was the chip shortage that makes Eurorack DIY projects tricky at the moment, and I used that to get more involved with coding in the eurorack land. -At this point, a big thanks to the people behind ornament & crime (o_C), Teensy and specially Mutable Instruments for the inspiring playground and the basis regarding hardware and software for this project. +At this point, a big thanks to the people behind ornament & crime (o_C) for the inspiring playground and the basis regarding hardware for this project. +Many thanks also to all those who have worked on the code and algorithms partly reused here and make this treasure trove freely available, especially Mutable Instruments. ## Challenge @@ -36,7 +37,8 @@ E.g you can chain the mono audio signal from an oscillator machine to the neighb * [Long press [RIGHT]] enters the I/O-configuration-page. * [Long press [LEFT] + [RIGHT]] enters the MIDI-settings-page. * [Long press left or right [ENCODER]] shows the modulation popup -* [Long press [L-ENCODER] + [R-ENCODER]] saves the patch - will be restored at startup. +* [Long press [L-ENCODER] + [R-ENCODER]] saves the patch - will be restored at startup + - DEBUG: skip restore - press [RIGHT] button while startup ).
@@ -50,7 +52,7 @@ E.g you can chain the mono audio signal from an oscillator machine to the neighb * **GND** * `---` * **CV** - * V/OCT, Envelope, LFO + * V/OCT, Envelope, LFO, Noise * **Drums** * Analog-BD, Analog SD, Analog HH, Analog HH2 * 909ish-BD, 909ish-SD, TR909-HiHat, TR909-Ride @@ -64,7 +66,7 @@ E.g you can chain the mono audio signal from an oscillator machine to the neighb * Virt.Analog, Waveshaping, FM, Grain, Additive, Wavetable, Chord * Resonator * **Stereo-FX** - * Reverb, Rev-Dattorro, Delay, Gated-Reverb, Reverb-HP-LP + * Reverb, ReverbSC, Rev-Dattorro, Delay, Gated-Reverb, Reverb-HP-LP * **SPEECH** * LPC, SAM * **MIDI** @@ -89,6 +91,7 @@ Machines/Engines are controlled by individual parameters. For each parameter a modulation can be assigned: * **CV**: * SRC: `C1`, `C2`, `C3`, `C4` + * OP: `THRU`, `S&H-T1`, `S&H-T1`, `S&H-T1`, `S&H-T1`, `T&H-T1`, `T&H-T1`, `T&H-T1`, `T&H-T1` * Hints: * Parameter 0 (top-left) is mainly used for V/OCT control. Thus, one single V/OCT signal / CV-Input can be shared by using modulation on parameter-0 with attenuverter = +1 (-3V..+6V) range. It is also possible to select the V/OCT input in the io-configuration page. * All other parameters can be modulated via CV-input with a assumed voltage-range of -4V..4V at 2kHz sample rate. @@ -265,21 +268,11 @@ To callibrate the ADC `0V` reference, remove all patch cables from the module. U The project was originally a kind of research that I did over half a year. The current O_C hardware could certainly be optimized. As you know, the DAC and the display share the SPI port - this is not ideal for simultaneous operation (display updates are sometimes audible). Furthermore, the Teensy 4.0 does not have "high-end" ADCs - my focus here was to achieve operation at audio rate (aux input) - the issue of noise has not been the focus so far. At the moment I like to make the project partially available to the community as open-source, so that everyone has the possibility to adapt and experiment with it. -In principle, this project is a suite of apps so-called machines/engines interfacing with a system library ("libmachine"). - -You are welcome for any suggestions and feedback or collaboration. - -#### Build & Flash firmware - * Install VSCode + platformio extension (https://platformio.org/platformio-ide) - - On linux: curl https://www.pjrc.com/teensy/00-teensy.rules > /etc/udev/rules.d/49-teensy.rules - - Open Folder or `code .` inside project directory - - In VSCode - choose environment e.g "OC_teensy40", press "build" or "upload" (ensure teensy connected via usb) - * Alternatively: use Teensy Loader to flash compiled hex: https://www.pjrc.com/teensy/loader.html ## License The application code respectively the suite of machines/engines is released under the MIT license. -For details e.g. the copyright holders - see the header of individual source code files or readme/license file in the sub folder. +For licenses e.g. the copyright holders of 3rd-party libraries - see the header of individual source code files or readme/license file in the sub folder. The previously mentioned "libmachine", a hardware abstraction layer, will remain "closed software" until I follow some not yet discarded ideas. This is to prevent the firmware from being forked/ported to similar digital Eurorack modules and some theoretical licensing questions. So if you consider commercially distributing hardware with this firmware, please contact me (eh2k◯gmx.de). diff --git a/lib/SAM/debug.c b/lib/SAM/debug.c index d663ab1..31c03ac 100755 --- a/lib/SAM/debug.c +++ b/lib/SAM/debug.c @@ -1,5 +1,7 @@ #include +int debug = 0; + extern const unsigned char signInputTable1[]; extern const unsigned char signInputTable2[]; diff --git a/lib/fft/fft4g.c b/lib/fft/fft4g.c new file mode 100644 index 0000000..4e63a9b --- /dev/null +++ b/lib/fft/fft4g.c @@ -0,0 +1,1357 @@ +/* + * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any purpose (include + * commercial use) and without fee. Please refer to this package when you modify + * this code. + * + * Changes: + * replaced "double to float" by eh2k. + */ + +/* +Fast Fourier/Cosine/Sine Transform + dimension :one + data length :power of 2 + decimation :frequency + radix :4, 2 + data :inplace + table :use +functions + cdft: Complex Discrete Fourier Transform + rdft: Real Discrete Fourier Transform + ddct: Discrete Cosine Transform + ddst: Discrete Sine Transform + dfct: Cosine Transform of RDFT (Real Symmetric DFT) + dfst: Sine Transform of RDFT (Real Anti-symmetric DFT) +function prototypes + void cdft(int, int, double *, int *, double *); + void rdft(int, int, double *, int *, double *); + void ddct(int, int, double *, int *, double *); + void ddst(int, int, double *, int *, double *); + void dfct(int, double *, double *, int *, double *); + void dfst(int, double *, double *, int *, double *); + + +-------- Complex DFT (Discrete Fourier Transform) -------- + [definition] + + X[k] = sum_j=0^n-1 x[j]*exp(2*pi*i*j*k/n), 0<=k + X[k] = sum_j=0^n-1 x[j]*exp(-2*pi*i*j*k/n), 0<=k + ip[0] = 0; // first time only + cdft(2*n, 1, a, ip, w); + + ip[0] = 0; // first time only + cdft(2*n, -1, a, ip, w); + [parameters] + 2*n :data length (int) + n >= 1, n = power of 2 + a[0...2*n-1] :input/output data (double *) + input data + a[2*j] = Re(x[j]), + a[2*j+1] = Im(x[j]), 0<=j= 2+sqrt(n) + strictly, + length of ip >= + 2+(1<<(int)(log(n+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + cdft(2*n, -1, a, ip, w); + is + cdft(2*n, 1, a, ip, w); + for (j = 0; j <= 2 * n - 1; j++) { + a[j] *= 1.0 / n; + } + . + + +-------- Real DFT / Inverse of Real DFT -------- + [definition] + RDFT + R[k] = sum_j=0^n-1 a[j]*cos(2*pi*j*k/n), 0<=k<=n/2 + I[k] = sum_j=0^n-1 a[j]*sin(2*pi*j*k/n), 0 IRDFT (excluding scale) + a[k] = (R[0] + R[n/2]*cos(pi*k))/2 + + sum_j=1^n/2-1 R[j]*cos(2*pi*j*k/n) + + sum_j=1^n/2-1 I[j]*sin(2*pi*j*k/n), 0<=k + ip[0] = 0; // first time only + rdft(n, 1, a, ip, w); + + ip[0] = 0; // first time only + rdft(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + + output data + a[2*k] = R[k], 0<=k + input data + a[2*j] = R[j], 0<=j= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n/2-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + rdft(n, 1, a, ip, w); + is + rdft(n, -1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DCT (Discrete Cosine Transform) / Inverse of DCT -------- + [definition] + IDCT (excluding scale) + C[k] = sum_j=0^n-1 a[j]*cos(pi*j*(k+1/2)/n), 0<=k DCT + C[k] = sum_j=0^n-1 a[j]*cos(pi*(j+1/2)*k/n), 0<=k + ip[0] = 0; // first time only + ddct(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddct(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + output data + a[k] = C[k], 0<=k= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddct(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddct(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- DST (Discrete Sine Transform) / Inverse of DST -------- + [definition] + IDST (excluding scale) + S[k] = sum_j=1^n A[j]*sin(pi*j*(k+1/2)/n), 0<=k DST + S[k] = sum_j=0^n-1 a[j]*sin(pi*(j+1/2)*k/n), 0 + ip[0] = 0; // first time only + ddst(n, 1, a, ip, w); + + ip[0] = 0; // first time only + ddst(n, -1, a, ip, w); + [parameters] + n :data length (int) + n >= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + + input data + a[j] = A[j], 0 + output data + a[k] = S[k], 0= 2+sqrt(n/2) + strictly, + length of ip >= + 2+(1<<(int)(log(n/2+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/4-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + ddst(n, -1, a, ip, w); + is + a[0] *= 0.5; + ddst(n, 1, a, ip, w); + for (j = 0; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Cosine Transform of RDFT (Real Symmetric DFT) -------- + [definition] + C[k] = sum_j=0^n a[j]*cos(pi*j*k/n), 0<=k<=n + [usage] + ip[0] = 0; // first time only + dfct(n, a, t, ip, w); + [parameters] + n :data length - 1 (int) + n >= 2, n = power of 2 + a[0...n] :input/output data (double *) + output data + a[k] = C[k], 0<=k<=n + t[0...n/2] :work area (double *) + ip[0...*] :work area for bit reversal (int *) + length of ip >= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + is + a[0] *= 0.5; + a[n] *= 0.5; + dfct(n, a, t, ip, w); + for (j = 0; j <= n; j++) { + a[j] *= 2.0 / n; + } + . + + +-------- Sine Transform of RDFT (Real Anti-symmetric DFT) -------- + [definition] + S[k] = sum_j=1^n-1 a[j]*sin(pi*j*k/n), 0= 2, n = power of 2 + a[0...n-1] :input/output data (double *) + output data + a[k] = S[k], 0= 2+sqrt(n/4) + strictly, + length of ip >= + 2+(1<<(int)(log(n/4+0.5)/log(2))/2). + ip[0],ip[1] are pointers of the cos/sin table. + w[0...n*5/8-1] :cos/sin table (double *) + w[],ip[] are initialized if ip[0] == 0. + [remark] + Inverse of + dfst(n, a, t, ip, w); + is + dfst(n, a, t, ip, w); + for (j = 1; j <= n - 1; j++) { + a[j] *= 2.0 / n; + } + . + + +Appendix : + The cos/sin table is recalculated when the larger table required. + w[] and ip[] are compatible with all routines. +*/ + + +void cdft(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void bitrv2(int n, int *ip, float *a); + void bitrv2conj(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void cftbsub(int n, float *a, float *w); + + if (n > (ip[0] << 2)) { + makewt(n >> 2, ip, w); + } + if (n > 4) { + if (isgn >= 0) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + } else { + bitrv2conj(n, ip + 2, a); + cftbsub(n, a, w); + } + } else if (n == 4) { + cftfsub(n, a, w); + } +} + + +void rdft(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void bitrv2(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void cftbsub(int n, float *a, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + int nw, nc; + float xi; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 2)) { + nc = n >> 2; + makect(nc, ip, w + nw); + } + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xi = a[0] - a[1]; + a[0] += a[1]; + a[1] = xi; + } else { + a[1] = 0.5 * (a[0] - a[1]); + a[0] -= a[1]; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } +} + + +void ddct(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void bitrv2(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void cftbsub(int n, float *a, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + void dctsub(int n, float *a, int nc, float *c); + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = a[j] - a[j - 1]; + a[j] += a[j - 1]; + } + a[1] = a[0] - xr; + a[0] += xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } + dctsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = a[j] - a[j + 1]; + a[j] += a[j + 1]; + } + a[n - 1] = xr; + } +} + + +void ddst(int n, int isgn, float *a, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void bitrv2(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void cftbsub(int n, float *a, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void rftbsub(int n, float *a, int nc, float *c); + void dstsub(int n, float *a, int nc, float *c); + int j, nw, nc; + float xr; + + nw = ip[0]; + if (n > (nw << 2)) { + nw = n >> 2; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > nc) { + nc = n; + makect(nc, ip, w + nw); + } + if (isgn < 0) { + xr = a[n - 1]; + for (j = n - 2; j >= 2; j -= 2) { + a[j + 1] = -a[j] - a[j - 1]; + a[j] -= a[j - 1]; + } + a[1] = a[0] + xr; + a[0] -= xr; + if (n > 4) { + rftbsub(n, a, nc, w + nw); + bitrv2(n, ip + 2, a); + cftbsub(n, a, w); + } else if (n == 4) { + cftfsub(n, a, w); + } + } + dstsub(n, a, nc, w + nw); + if (isgn >= 0) { + if (n > 4) { + bitrv2(n, ip + 2, a); + cftfsub(n, a, w); + rftfsub(n, a, nc, w + nw); + } else if (n == 4) { + cftfsub(n, a, w); + } + xr = a[0] - a[1]; + a[0] += a[1]; + for (j = 2; j < n; j += 2) { + a[j - 1] = -a[j] - a[j + 1]; + a[j] -= a[j + 1]; + } + a[n - 1] = -xr; + } +} + + +void dfct(int n, float *a, float *t, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void bitrv2(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void dctsub(int n, float *a, int nc, float *c); + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + m = n >> 1; + yi = a[m]; + xi = a[0] + a[n]; + a[0] -= a[n]; + t[0] = xi - yi; + t[m] = xi + yi; + if (n > 2) { + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] - a[n - j]; + xi = a[j] + a[n - j]; + yr = a[k] - a[n - k]; + yi = a[k] + a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi - yi; + t[k] = xi + yi; + } + t[mh] = a[mh] + a[n - mh]; + a[mh] -= a[n - mh]; + dctsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[0] - a[1]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] + a[j + 1]; + a[2 * j - 1] = a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dctsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[0] - t[1]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = t[j] - t[j + 1]; + a[k + l] = t[j] + t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 0; j < mh; j++) { + k = m - j; + t[j] = t[m + k] - t[m + j]; + t[k] = t[m + k] + t[m + j]; + } + t[mh] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + a[n] = t[2] - t[1]; + a[0] = t[2] + t[1]; + } else { + a[1] = a[0]; + a[2] = t[0]; + a[0] = t[1]; + } +} + + +void dfst(int n, float *a, float *t, int *ip, float *w) +{ + void makewt(int nw, int *ip, float *w); + void makect(int nc, int *ip, float *c); + void bitrv2(int n, int *ip, float *a); + void cftfsub(int n, float *a, float *w); + void rftfsub(int n, float *a, int nc, float *c); + void dstsub(int n, float *a, int nc, float *c); + int j, k, l, m, mh, nw, nc; + float xr, xi, yr, yi; + + nw = ip[0]; + if (n > (nw << 3)) { + nw = n >> 3; + makewt(nw, ip, w); + } + nc = ip[1]; + if (n > (nc << 1)) { + nc = n >> 1; + makect(nc, ip, w + nw); + } + if (n > 2) { + m = n >> 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + xr = a[j] + a[n - j]; + xi = a[j] - a[n - j]; + yr = a[k] + a[n - k]; + yi = a[k] - a[n - k]; + a[j] = xr; + a[k] = yr; + t[j] = xi + yi; + t[k] = xi - yi; + } + t[0] = a[mh] - a[n - mh]; + a[mh] += a[n - mh]; + a[0] = a[m]; + dstsub(m, a, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, a); + cftfsub(m, a, w); + rftfsub(m, a, nc, w + nw); + } else if (m == 4) { + cftfsub(m, a, w); + } + a[n - 1] = a[1] - a[0]; + a[1] = a[0] + a[1]; + for (j = m - 2; j >= 2; j -= 2) { + a[2 * j + 1] = a[j] - a[j + 1]; + a[2 * j - 1] = -a[j] - a[j + 1]; + } + l = 2; + m = mh; + while (m >= 2) { + dstsub(m, t, nc, w + nw); + if (m > 4) { + bitrv2(m, ip + 2, t); + cftfsub(m, t, w); + rftfsub(m, t, nc, w + nw); + } else if (m == 4) { + cftfsub(m, t, w); + } + a[n - l] = t[1] - t[0]; + a[l] = t[0] + t[1]; + k = 0; + for (j = 2; j < m; j += 2) { + k += l << 2; + a[k - l] = -t[j] - t[j + 1]; + a[k + l] = t[j] - t[j + 1]; + } + l <<= 1; + mh = m >> 1; + for (j = 1; j < mh; j++) { + k = m - j; + t[j] = t[m + k] + t[m + j]; + t[k] = t[m + k] - t[m + j]; + } + t[0] = t[m + mh]; + m = mh; + } + a[l] = t[0]; + } + a[0] = 0; +} + + +/* -------- initializing routines -------- */ + + +#include + +void makewt(int nw, int *ip, float *w) +{ + void bitrv2(int n, int *ip, float *a); + int j, nwh; + float delta, x, y; + + ip[0] = nw; + ip[1] = 1; + if (nw > 2) { + nwh = nw >> 1; + delta = atan(1.0) / nwh; + w[0] = 1; + w[1] = 0; + w[nwh] = cos(delta * nwh); + w[nwh + 1] = w[nwh]; + if (nwh > 2) { + for (j = 2; j < nwh; j += 2) { + x = cos(delta * j); + y = sin(delta * j); + w[j] = x; + w[j + 1] = y; + w[nw - j] = y; + w[nw - j + 1] = x; + } + bitrv2(nw, ip + 2, w); + } + } +} + + +void makect(int nc, int *ip, float *c) +{ + int j, nch; + float delta; + + ip[1] = nc; + if (nc > 1) { + nch = nc >> 1; + delta = atanf(1.0) / nch; + c[0] = cosf(delta * nch); + c[nch] = 0.5 * c[0]; + for (j = 1; j < nch; j++) { + c[j] = 0.5 * cosf(delta * j); + c[nc - j] = 0.5 * sinf(delta * j); + } + } +} + + +/* -------- child routines -------- */ + + +void bitrv2(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + j1 = 2 * k + m2 + ip[k]; + k1 = j1 + m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } else { + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = a[j1 + 1]; + yr = a[k1]; + yi = a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + } + } +} + + +void bitrv2conj(int n, int *ip, float *a) +{ + int j, j1, k, k1, l, m, m2; + float xr, xi, yr, yi; + + ip[0] = 0; + l = n; + m = 1; + while ((m << 3) < l) { + l >>= 1; + for (j = 0; j < m; j++) { + ip[m + j] = ip[j] + l; + } + m <<= 1; + } + m2 = 2 * m; + if ((m << 3) == l) { + for (k = 0; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 -= m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += 2 * m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + j1 = k1 + m2; + k1 = j1 + m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + k1 += m2; + a[k1 + 1] = -a[k1 + 1]; + } + } else { + a[1] = -a[1]; + a[m2 + 1] = -a[m2 + 1]; + for (k = 1; k < m; k++) { + for (j = 0; j < k; j++) { + j1 = 2 * j + ip[k]; + k1 = 2 * k + ip[j]; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + j1 += m2; + k1 += m2; + xr = a[j1]; + xi = -a[j1 + 1]; + yr = a[k1]; + yi = -a[k1 + 1]; + a[j1] = yr; + a[j1 + 1] = yi; + a[k1] = xr; + a[k1 + 1] = xi; + } + k1 = 2 * k + ip[k]; + a[k1 + 1] = -a[k1 + 1]; + a[k1 + m2 + 1] = -a[k1 + m2 + 1]; + } + } +} + + +void cftfsub(int n, float *a, float *w) +{ + void cft1st(int n, float *a, float *w); + void cftmdl(int n, int l, float *a, float *w); + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = a[j + 1] - a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] += a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cftbsub(int n, float *a, float *w) +{ + void cft1st(int n, float *a, float *w); + void cftmdl(int n, int l, float *a, float *w); + int j, j1, j2, j3, l; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + l = 2; + if (n > 8) { + cft1st(n, a, w); + l = 8; + while ((l << 2) < n) { + cftmdl(n, l, a, w); + l <<= 2; + } + } + if ((l << 2) == n) { + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = -a[j + 1] - a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = -a[j + 1] + a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i - x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i + x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i - x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i + x3r; + } + } else { + for (j = 0; j < l; j += 2) { + j1 = j + l; + x0r = a[j] - a[j1]; + x0i = -a[j + 1] + a[j1 + 1]; + a[j] += a[j1]; + a[j + 1] = -a[j + 1] - a[j1 + 1]; + a[j1] = x0r; + a[j1 + 1] = x0i; + } + } +} + + +void cft1st(int n, float *a, float *w) +{ + int j, k1, k2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + x0r = a[0] + a[2]; + x0i = a[1] + a[3]; + x1r = a[0] - a[2]; + x1i = a[1] - a[3]; + x2r = a[4] + a[6]; + x2i = a[5] + a[7]; + x3r = a[4] - a[6]; + x3i = a[5] - a[7]; + a[0] = x0r + x2r; + a[1] = x0i + x2i; + a[4] = x0r - x2r; + a[5] = x0i - x2i; + a[2] = x1r - x3i; + a[3] = x1i + x3r; + a[6] = x1r + x3i; + a[7] = x1i - x3r; + wk1r = w[2]; + x0r = a[8] + a[10]; + x0i = a[9] + a[11]; + x1r = a[8] - a[10]; + x1i = a[9] - a[11]; + x2r = a[12] + a[14]; + x2i = a[13] + a[15]; + x3r = a[12] - a[14]; + x3i = a[13] - a[15]; + a[8] = x0r + x2r; + a[9] = x0i + x2i; + a[12] = x2i - x0i; + a[13] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[10] = wk1r * (x0r - x0i); + a[11] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[14] = wk1r * (x0i - x0r); + a[15] = wk1r * (x0i + x0r); + k1 = 0; + for (j = 16; j < n; j += 16) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + x0r = a[j] + a[j + 2]; + x0i = a[j + 1] + a[j + 3]; + x1r = a[j] - a[j + 2]; + x1i = a[j + 1] - a[j + 3]; + x2r = a[j + 4] + a[j + 6]; + x2i = a[j + 5] + a[j + 7]; + x3r = a[j + 4] - a[j + 6]; + x3i = a[j + 5] - a[j + 7]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 4] = wk2r * x0r - wk2i * x0i; + a[j + 5] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 2] = wk1r * x0r - wk1i * x0i; + a[j + 3] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 6] = wk3r * x0r - wk3i * x0i; + a[j + 7] = wk3r * x0i + wk3i * x0r; + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + x0r = a[j + 8] + a[j + 10]; + x0i = a[j + 9] + a[j + 11]; + x1r = a[j + 8] - a[j + 10]; + x1i = a[j + 9] - a[j + 11]; + x2r = a[j + 12] + a[j + 14]; + x2i = a[j + 13] + a[j + 15]; + x3r = a[j + 12] - a[j + 14]; + x3i = a[j + 13] - a[j + 15]; + a[j + 8] = x0r + x2r; + a[j + 9] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j + 12] = -wk2i * x0r - wk2r * x0i; + a[j + 13] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j + 10] = wk1r * x0r - wk1i * x0i; + a[j + 11] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j + 14] = wk3r * x0r - wk3i * x0i; + a[j + 15] = wk3r * x0i + wk3i * x0r; + } +} + + +void cftmdl(int n, int l, float *a, float *w) +{ + int j, j1, j2, j3, k, k1, k2, m, m2; + float wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; + float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; + + m = l << 2; + for (j = 0; j < l; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x0r - x2r; + a[j2 + 1] = x0i - x2i; + a[j1] = x1r - x3i; + a[j1 + 1] = x1i + x3r; + a[j3] = x1r + x3i; + a[j3 + 1] = x1i - x3r; + } + wk1r = w[2]; + for (j = m; j < l + m; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + a[j2] = x2i - x0i; + a[j2 + 1] = x0r - x2r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * (x0r - x0i); + a[j1 + 1] = wk1r * (x0r + x0i); + x0r = x3i + x1r; + x0i = x3r - x1i; + a[j3] = wk1r * (x0i - x0r); + a[j3 + 1] = wk1r * (x0i + x0r); + } + k1 = 0; + m2 = 2 * m; + for (k = m2; k < n; k += m2) { + k1 += 2; + k2 = 2 * k1; + wk2r = w[k1]; + wk2i = w[k1 + 1]; + wk1r = w[k2]; + wk1i = w[k2 + 1]; + wk3r = wk1r - 2 * wk2i * wk1i; + wk3i = 2 * wk2i * wk1r - wk1i; + for (j = k; j < l + k; j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = wk2r * x0r - wk2i * x0i; + a[j2 + 1] = wk2r * x0i + wk2i * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + wk1r = w[k2 + 2]; + wk1i = w[k2 + 3]; + wk3r = wk1r - 2 * wk2r * wk1i; + wk3i = 2 * wk2r * wk1r - wk1i; + for (j = k + m; j < l + (k + m); j += 2) { + j1 = j + l; + j2 = j1 + l; + j3 = j2 + l; + x0r = a[j] + a[j1]; + x0i = a[j + 1] + a[j1 + 1]; + x1r = a[j] - a[j1]; + x1i = a[j + 1] - a[j1 + 1]; + x2r = a[j2] + a[j3]; + x2i = a[j2 + 1] + a[j3 + 1]; + x3r = a[j2] - a[j3]; + x3i = a[j2 + 1] - a[j3 + 1]; + a[j] = x0r + x2r; + a[j + 1] = x0i + x2i; + x0r -= x2r; + x0i -= x2i; + a[j2] = -wk2i * x0r - wk2r * x0i; + a[j2 + 1] = -wk2i * x0i + wk2r * x0r; + x0r = x1r - x3i; + x0i = x1i + x3r; + a[j1] = wk1r * x0r - wk1i * x0i; + a[j1 + 1] = wk1r * x0i + wk1i * x0r; + x0r = x1r + x3i; + x0i = x1i - x3r; + a[j3] = wk3r * x0r - wk3i * x0i; + a[j3 + 1] = wk3r * x0i + wk3i * x0r; + } + } +} + + +void rftfsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr - wki * xi; + yi = wkr * xi + wki * xr; + a[j] -= yr; + a[j + 1] -= yi; + a[k] += yr; + a[k + 1] -= yi; + } +} + + +void rftbsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr, xi, yr, yi; + + a[1] = -a[1]; + m = n >> 1; + ks = 2 * nc / m; + kk = 0; + for (j = 2; j < m; j += 2) { + k = n - j; + kk += ks; + wkr = 0.5 - c[nc - kk]; + wki = c[kk]; + xr = a[j] - a[k]; + xi = a[j + 1] + a[k + 1]; + yr = wkr * xr + wki * xi; + yi = wkr * xi - wki * xr; + a[j] -= yr; + a[j + 1] = yi - a[j + 1]; + a[k] += yr; + a[k + 1] = yi - a[k + 1]; + } + a[m + 1] = -a[m + 1]; +} + + +void dctsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[j] - wkr * a[k]; + a[j] = wkr * a[j] + wki * a[k]; + a[k] = xr; + } + a[m] *= c[0]; +} + + +void dstsub(int n, float *a, int nc, float *c) +{ + int j, k, kk, ks, m; + float wkr, wki, xr; + + m = n >> 1; + ks = nc / n; + kk = 0; + for (j = 1; j < m; j++) { + k = n - j; + kk += ks; + wkr = c[kk] - c[nc - kk]; + wki = c[kk] + c[nc - kk]; + xr = wki * a[k] - wkr * a[j]; + a[k] = wkr * a[k] + wki * a[j]; + a[j] = xr; + } + a[m] *= c[0]; +} + diff --git a/lib/fft/fft4g.h b/lib/fft/fft4g.h new file mode 100644 index 0000000..e1760f0 --- /dev/null +++ b/lib/fft/fft4g.h @@ -0,0 +1,20 @@ +/* + * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html + * Copyright Takuya OOURA, 1996-2001 + * + * You may use, copy, modify and distribute this code for any purpose (include + * commercial use) and without fee. Please refer to this package when you modify + * this code. + * + * Changes: + * replaced "double to float" by eh2k. + */ + +#include + +__BEGIN_DECLS + +void rdft(int n, int isgn, float *a, int *ip, float *w); +void cdft(int n, int isgn, float *a, int *ip, float *w); + +__END_DECLS \ No newline at end of file diff --git a/lib/misc/noise.hxx b/lib/misc/noise.hxx new file mode 100644 index 0000000..072ed25 --- /dev/null +++ b/lib/misc/noise.hxx @@ -0,0 +1,96 @@ + +// Copyright (C)2021 - Eduard Heidt +// +// Author: Eduard Heidt (eh2k@gmx.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// See http://creativecommons.org/licenses/MIT/ for more information. +// + +#include + +// 31-bit Park-Miller-Carta Pseudo-Random Number Generator +// http://www.firstpr.com.au/dsp/rand31/ + +struct WhiteNoise +{ + uint32_t seed; + + WhiteNoise() + { + seed = (uint32_t)(void*)this; + } + + int32_t next() // 0 to INT32_MAX + { + uint32_t lo, hi; + lo = seed; + + hi = 16807 * (lo >> 16); + lo = 16807 * (lo & 0xFFFF); + lo += (hi & 0x7FFF) << 16; + lo += hi >> 15; + lo = (lo & 0x7FFFFFFF) + (lo >> 31); + + seed = lo; + return (int32_t)lo; + } + + float nextf(float min, float max) + { + return ((float)next() / INT32_MAX) * (max - min) + min; + } +}; + +/** Based on "The Voss algorithm" +http://www.firstpr.com.au/dsp/pink-noise/ +*/ + +template +struct PinkNoise +{ + WhiteNoise white; + + int key = 0; + float values[N] = {}; + + float nextf(float min, float max) + { + int last_key = key; + key++; + if (key > (0x1 << N)) + key = 0; + + // Exclusive-Or previous value with current value. This gives + // a list of bits that have changed. + int diff = last_key ^ key; + + float sum = 0.f; + for (int i = 0; i < N; i++) + { + if (diff & (1 << i)) + { + values[i] = white.nextf(min, max) / N; + } + sum += values[i]; + } + return sum; + } +}; \ No newline at end of file diff --git a/lib/soundpipe/LICENSE-csound.txt b/lib/soundpipe/LICENSE-csound.txt new file mode 100644 index 0000000..efce2a8 --- /dev/null +++ b/lib/soundpipe/LICENSE-csound.txt @@ -0,0 +1,503 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/lib/soundpipe/LICENSE-soundpipe.txt b/lib/soundpipe/LICENSE-soundpipe.txt new file mode 100644 index 0000000..686bc3f --- /dev/null +++ b/lib/soundpipe/LICENSE-soundpipe.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Paul Batchelor + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/lib/soundpipe/revsc.c b/lib/soundpipe/revsc.c new file mode 100644 index 0000000..69f6298 --- /dev/null +++ b/lib/soundpipe/revsc.c @@ -0,0 +1,285 @@ +/* + * RevSC + * + * This code has been extracted from the Csound opcode "reverbsc". + * It has been modified to work as a Soundpipe module. + * + * Original Author(s): Sean Costello, Istvan Varga + * Year: 1999, 2005 + * Location: Opcodes/reverbsc.c + * + */ + +#include +#include +#include +#include +#include "revsc.h" + +#define DEFAULT_SRATE 44100.0 +#define MIN_SRATE 5000.0 +#define MAX_SRATE 1000000.0 +#define MAX_PITCHMOD 20.0 +#define DELAYPOS_SHIFT 28 +#define DELAYPOS_SCALE 0x10000000 +#define DELAYPOS_MASK 0x0FFFFFFF + +#ifndef M_PI +#define M_PI 3.14159265358979323846 /* pi */ +#endif + +/* reverbParams[n][0] = delay time (in seconds) */ +/* reverbParams[n][1] = random variation in delay time (in seconds) */ +/* reverbParams[n][2] = random variation frequency (in 1/sec) */ +/* reverbParams[n][3] = random seed (0 - 32767) */ + +static const SPFLOAT reverbParams[8][4] = { + { (2473.0 / DEFAULT_SRATE), 0.0010, 3.100, 1966.0 }, + { (2767.0 / DEFAULT_SRATE), 0.0011, 3.500, 29491.0 }, + { (3217.0 / DEFAULT_SRATE), 0.0017, 1.110, 22937.0 }, + { (3557.0 / DEFAULT_SRATE), 0.0006, 3.973, 9830.0 }, + { (3907.0 / DEFAULT_SRATE), 0.0010, 2.341, 20643.0 }, + { (4127.0 / DEFAULT_SRATE), 0.0011, 1.897, 22937.0 }, + { (2143.0 / DEFAULT_SRATE), 0.0017, 0.891, 29491.0 }, + { (1933.0 / DEFAULT_SRATE), 0.0006, 3.221, 14417.0 } +}; + +static int delay_line_max_samples(SPFLOAT sr, SPFLOAT iPitchMod, int n); +static int init_delay_line(sp_revsc *p, sp_revsc_dl *lp, int n); +static int delay_line_bytes_alloc(SPFLOAT sr, SPFLOAT iPitchMod, int n); +static const SPFLOAT outputGain = 0.35; +static const SPFLOAT jpScale = 0.25; +int sp_revsc_create(sp_revsc **p){ + *p = malloc(sizeof(sp_revsc)); + return SP_OK; +} + +int sp_revsc_init(sp_data *sp, sp_revsc *p) +{ + p->iSampleRate = sp->sr; + p->sampleRate = sp->sr; + p->feedback = 0.97; + p->lpfreq = 10000; + p->iPitchMod = 1; + p->iSkipInit = 0; + p->dampFact = 1.0; + p->prv_LPFreq = 0.0; + p->initDone = 1; + int i, nBytes = 0; + for(i = 0; i < 8; i++){ + nBytes += delay_line_bytes_alloc(sp->sr, 1, i); + } + + if(sp->aux.size >= nBytes) + { + p->aux.ptr = sp->aux.ptr; + p->aux.size = nBytes; + } + else + { + return SP_NOT_OK; + } + + nBytes = 0; + for (i = 0; i < 8; i++) { + p->delayLines[i].buf = (p->aux.ptr) + nBytes; + init_delay_line(p, &p->delayLines[i], i); + nBytes += delay_line_bytes_alloc(sp->sr, 1, i); + } + + return SP_OK; +} + + +int sp_revsc_destroy(sp_revsc **p) +{ + sp_revsc *pp = *p; + free(*p); + return SP_OK; +} + +static int delay_line_max_samples(SPFLOAT sr, SPFLOAT iPitchMod, int n) +{ + SPFLOAT maxDel; + + maxDel = reverbParams[n][0]; + maxDel += (reverbParams[n][1] * (SPFLOAT) iPitchMod * 1.125); + return (int) (maxDel * sr + 16.5); +} + +static int delay_line_bytes_alloc(SPFLOAT sr, SPFLOAT iPitchMod, int n) +{ + int nBytes = 0; + + nBytes += (delay_line_max_samples(sr, iPitchMod, n) * (int) sizeof(SPFLOAT)); + return nBytes; +} + +static void next_random_lineseg(sp_revsc *p, sp_revsc_dl *lp, int n) +{ + SPFLOAT prvDel, nxtDel, phs_incVal; + + /* update random seed */ + if (lp->seedVal < 0) + lp->seedVal += 0x10000; + lp->seedVal = (lp->seedVal * 15625 + 1) & 0xFFFF; + if (lp->seedVal >= 0x8000) + lp->seedVal -= 0x10000; + /* length of next segment in samples */ + lp->randLine_cnt = (int) ((p->sampleRate / reverbParams[n][2]) + 0.5); + prvDel = (SPFLOAT) lp->writePos; + prvDel -= ((SPFLOAT) lp->readPos + + ((SPFLOAT) lp->readPosFrac / (SPFLOAT) DELAYPOS_SCALE)); + while (prvDel < 0.0) + prvDel += lp->bufferSize; + prvDel = prvDel / p->sampleRate; /* previous delay time in seconds */ + nxtDel = (SPFLOAT) lp->seedVal * reverbParams[n][1] / 32768.0; + /* next delay time in seconds */ + nxtDel = reverbParams[n][0] + (nxtDel * (SPFLOAT) p->iPitchMod); + /* calculate phase increment per sample */ + phs_incVal = (prvDel - nxtDel) / (SPFLOAT) lp->randLine_cnt; + phs_incVal = phs_incVal * p->sampleRate + 1.0; + lp->readPosFrac_inc = (int) (phs_incVal * DELAYPOS_SCALE + 0.5); +} + +static int init_delay_line(sp_revsc *p, sp_revsc_dl *lp, int n) +{ + SPFLOAT readPos; + /* int i; */ + + /* calculate length of delay line */ + lp->bufferSize = delay_line_max_samples(p->sampleRate, 1, n); + lp->dummy = 0; + lp->writePos = 0; + /* set random seed */ + lp->seedVal = (int) (reverbParams[n][3] + 0.5); + /* set initial delay time */ + readPos = (SPFLOAT) lp->seedVal * reverbParams[n][1] / 32768; + readPos = reverbParams[n][0] + (readPos * (SPFLOAT) p->iPitchMod); + readPos = (SPFLOAT) lp->bufferSize - (readPos * p->sampleRate); + lp->readPos = (int) readPos; + readPos = (readPos - (SPFLOAT) lp->readPos) * (SPFLOAT) DELAYPOS_SCALE; + lp->readPosFrac = (int) (readPos + 0.5); + /* initialise first random line segment */ + next_random_lineseg(p, lp, n); + /* clear delay line to zero */ + lp->filterState = 0.0; + memset(lp->buf, 0, sizeof(SPFLOAT) * lp->bufferSize); + return SP_OK; +} + + +int sp_revsc_compute(sp_data *sp, sp_revsc *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2) +{ + SPFLOAT ainL, ainR, aoutL, aoutR; + SPFLOAT vm1, v0, v1, v2, am1, a0, a1, a2, frac; + sp_revsc_dl *lp; + int readPos; + uint32_t n; + int bufferSize; /* Local copy */ + SPFLOAT dampFact = p->dampFact; + + if (p->initDone <= 0) return SP_NOT_OK; + + /* calculate tone filter coefficient if frequency changed */ + + if (p->lpfreq != p->prv_LPFreq) { + p->prv_LPFreq = p->lpfreq; + dampFact = 2.0f - cosf(p->prv_LPFreq * (2 * M_PI) / p->sampleRate); + dampFact = p->dampFact = dampFact - sqrtf(dampFact * dampFact - 1.0f); + } + + /* calculate "resultant junction pressure" and mix to input signals */ + + ainL = aoutL = aoutR = 0.0; + for (n = 0; n < 8; n++) { + ainL += p->delayLines[n].filterState; + } + ainL *= jpScale; + ainR = ainL + *in2; + ainL = ainL + *in1; + + /* loop through all delay lines */ + + for (n = 0; n < 8; n++) { + lp = &p->delayLines[n]; + bufferSize = lp->bufferSize; + + /* send input signal and feedback to delay line */ + + lp->buf[lp->writePos] = (SPFLOAT) ((n & 1 ? ainR : ainL) + - lp->filterState); + if (++lp->writePos >= bufferSize) { + lp->writePos -= bufferSize; + } + + /* read from delay line with cubic interpolation */ + + if (lp->readPosFrac >= DELAYPOS_SCALE) { + lp->readPos += (lp->readPosFrac >> DELAYPOS_SHIFT); + lp->readPosFrac &= DELAYPOS_MASK; + } + if (lp->readPos >= bufferSize) + lp->readPos -= bufferSize; + readPos = lp->readPos; + frac = (SPFLOAT) lp->readPosFrac * (1.0 / (SPFLOAT) DELAYPOS_SCALE); + + /* calculate interpolation coefficients */ + + a2 = frac * frac; a2 -= 1.0; a2 *= (1.0 / 6.0); + a1 = frac; a1 += 1.0; a1 *= 0.5; am1 = a1 - 1.0; + a0 = 3.0 * a2; a1 -= a0; am1 -= a2; a0 -= frac; + + /* read four samples for interpolation */ + + if (readPos > 0 && readPos < (bufferSize - 2)) { + vm1 = (SPFLOAT) (lp->buf[readPos - 1]); + v0 = (SPFLOAT) (lp->buf[readPos]); + v1 = (SPFLOAT) (lp->buf[readPos + 1]); + v2 = (SPFLOAT) (lp->buf[readPos + 2]); + } + else { + + /* at buffer wrap-around, need to check index */ + + if (--readPos < 0) readPos += bufferSize; + vm1 = (SPFLOAT) lp->buf[readPos]; + if (++readPos >= bufferSize) readPos -= bufferSize; + v0 = (SPFLOAT) lp->buf[readPos]; + if (++readPos >= bufferSize) readPos -= bufferSize; + v1 = (SPFLOAT) lp->buf[readPos]; + if (++readPos >= bufferSize) readPos -= bufferSize; + v2 = (SPFLOAT) lp->buf[readPos]; + } + v0 = (am1 * vm1 + a0 * v0 + a1 * v1 + a2 * v2) * frac + v0; + + /* update buffer read position */ + + lp->readPosFrac += lp->readPosFrac_inc; + + /* apply feedback gain and lowpass filter */ + + v0 *= (SPFLOAT) p->feedback; + v0 = (lp->filterState - v0) * dampFact + v0; + lp->filterState = v0; + + /* mix to output */ + + if (n & 1) { + aoutR += v0; + }else{ + aoutL += v0; + } + + /* start next random line segment if current one has reached endpoint */ + + if (--(lp->randLine_cnt) <= 0) { + next_random_lineseg(p, lp, n); + } + } + /* someday, use aoutR for multimono out */ + + *out1 = aoutL * outputGain; + *out2 = aoutR * outputGain; + return SP_OK; +} diff --git a/lib/soundpipe/revsc.h b/lib/soundpipe/revsc.h new file mode 100644 index 0000000..bd8d729 --- /dev/null +++ b/lib/soundpipe/revsc.h @@ -0,0 +1,31 @@ +#pragma once +#include "soundpipe.h" + +typedef struct { + int writePos; + int bufferSize; + int readPos; + int readPosFrac; + int readPosFrac_inc; + int dummy; + int seedVal; + int randLine_cnt; + SPFLOAT filterState; + SPFLOAT *buf; +} sp_revsc_dl; + +typedef struct { + SPFLOAT feedback, lpfreq; + SPFLOAT iSampleRate, iPitchMod, iSkipInit; + SPFLOAT sampleRate; + SPFLOAT dampFact; + SPFLOAT prv_LPFreq; + int initDone; + sp_revsc_dl delayLines[8]; + sp_auxdata aux; +} sp_revsc; + +int sp_revsc_create(sp_revsc **p); +int sp_revsc_destroy(sp_revsc **p); +int sp_revsc_init(sp_data *sp, sp_revsc *p); +int sp_revsc_compute(sp_data *sp, sp_revsc *p, SPFLOAT *in1, SPFLOAT *in2, SPFLOAT *out1, SPFLOAT *out2); diff --git a/lib/soundpipe/soundpipe.c b/lib/soundpipe/soundpipe.c new file mode 100644 index 0000000..85b8ff6 --- /dev/null +++ b/lib/soundpipe/soundpipe.c @@ -0,0 +1,9 @@ +#include "soundpipe.h" +#include + +uint32_t sp_rand(sp_data *sp) +{ + uint32_t val = (1103515245 * sp->rand + 12345) % SP_RANDMAX; + sp->rand = val; + return val; +} \ No newline at end of file diff --git a/lib/soundpipe/soundpipe.h b/lib/soundpipe/soundpipe.h new file mode 100644 index 0000000..d231fae --- /dev/null +++ b/lib/soundpipe/soundpipe.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include + +typedef float SPFLOAT; +#define SP_OK 1 +#define SP_NOT_OK 0 +#define SP_RANDMAX 2147483648 + +typedef struct sp_auxdata { + size_t size; + void *ptr; +} sp_auxdata; + +typedef struct { + SPFLOAT *out; + uint32_t sr; + uint32_t rand; + sp_auxdata aux; +} sp_data; + +uint32_t sp_rand(sp_data *sp); \ No newline at end of file diff --git a/lib/weegfx/gfx_font_6x8.h b/lib/weegfx/gfx_font_6x8.h new file mode 100644 index 0000000..39aa77c --- /dev/null +++ b/lib/weegfx/gfx_font_6x8.h @@ -0,0 +1,119 @@ +/* + * SSD1306xLED - Drivers for SSD1306 controlled dot matrix OLED/PLED 128x64 displays + * + * @created: 2014-08-12 + * @author: Neven Boyanov + * + * Copyright (c) 2015 Neven Boyanov, Tinusaur Team. All Rights Reserved. + * Distributed as open source software under MIT License, see LICENSE.txt file. + * Please, as a favour, retain the link http://tinusaur.org to The Tinusaur Project. + * + * Source code available at: https://bitbucket.org/tinusaur/ssd1306xled + * + */ + +// ---------------------------------------------------------------------------- + +#ifndef PROGMEM +#include "pgmspace.h" +#endif + +// ---------------------------------------------------------------------------- + +/* Standard ASCII 6x8 font */ +const uint8_t ssd1306xled_font6x8 [] PROGMEM = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sp + 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, // ! + 0x00, 0x00, 0x07, 0x00, 0x07, 0x00, // " + 0x00, 0x14, 0x7f, 0x14, 0x7f, 0x14, // # + 0x00, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $ + 0x00, 0x23, 0x13, 0x08, 0x64, 0x62, // % + 0x00, 0x36, 0x49, 0x55, 0x22, 0x50, // & + 0x00, 0x00, 0x05, 0x03, 0x00, 0x00, // ' + 0x00, 0x00, 0x1c, 0x22, 0x41, 0x00, // ( + 0x00, 0x00, 0x41, 0x22, 0x1c, 0x00, // ) + 0x00, 0x14, 0x08, 0x3E, 0x08, 0x14, // * + 0x00, 0x08, 0x08, 0x3E, 0x08, 0x08, // + + 0x00, 0x00, 0x00, 0xA0, 0x60, 0x00, // , + 0x00, 0x08, 0x08, 0x08, 0x08, 0x08, // - + 0x00, 0x00, 0x60, 0x60, 0x00, 0x00, // . + 0x00, 0x20, 0x10, 0x08, 0x04, 0x02, // / + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // 0 + 0x00, 0x00, 0x42, 0x7F, 0x40, 0x00, // 1 + 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, // 2 + 0x00, 0x21, 0x41, 0x45, 0x4B, 0x31, // 3 + 0x00, 0x18, 0x14, 0x12, 0x7F, 0x10, // 4 + 0x00, 0x27, 0x45, 0x45, 0x45, 0x39, // 5 + 0x00, 0x3C, 0x4A, 0x49, 0x49, 0x30, // 6 + 0x00, 0x01, 0x71, 0x09, 0x05, 0x03, // 7 + 0x00, 0x36, 0x49, 0x49, 0x49, 0x36, // 8 + 0x00, 0x06, 0x49, 0x49, 0x29, 0x1E, // 9 + 0x00, 0x00, 0x36, 0x36, 0x00, 0x00, // : + 0x00, 0x00, 0x56, 0x36, 0x00, 0x00, // ; + 0x00, 0x08, 0x14, 0x22, 0x41, 0x00, // < + 0x00, 0x14, 0x14, 0x14, 0x14, 0x14, // = + 0x00, 0x00, 0x41, 0x22, 0x14, 0x08, // > + 0x00, 0x02, 0x01, 0x51, 0x09, 0x06, // ? + 0x00, 0x32, 0x49, 0x59, 0x51, 0x3E, // @ + 0x00, 0x7C, 0x12, 0x11, 0x12, 0x7C, // A + 0x00, 0x41, 0x7f, 0x49, 0x49, 0x36, // B + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x22, // C + 0x00, 0x7F, 0x41, 0x41, 0x22, 0x1C, // D + 0x00, 0x7F, 0x49, 0x49, 0x49, 0x41, // E + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x01, // F + 0x00, 0x3E, 0x41, 0x49, 0x49, 0x7A, // G + 0x00, 0x7F, 0x08, 0x08, 0x08, 0x7F, // H + 0x00, 0x00, 0x41, 0x7F, 0x41, 0x00, // I + 0x00, 0x20, 0x40, 0x41, 0x3F, 0x01, // J + 0x00, 0x7F, 0x08, 0x14, 0x22, 0x41, // K + 0x00, 0x7F, 0x40, 0x40, 0x40, 0x40, // L + 0x00, 0x7F, 0x02, 0x0C, 0x02, 0x7F, // M + 0x00, 0x7F, 0x04, 0x08, 0x10, 0x7F, // N + 0x00, 0x3E, 0x41, 0x41, 0x41, 0x3E, // O + 0x00, 0x7F, 0x09, 0x09, 0x09, 0x06, // P + 0x00, 0x3E, 0x41, 0x51, 0x21, 0x5E, // Q + 0x00, 0x7F, 0x09, 0x19, 0x29, 0x46, // R + 0x00, 0x46, 0x49, 0x49, 0x49, 0x31, // S + 0x00, 0x01, 0x01, 0x7F, 0x01, 0x01, // T + 0x00, 0x3F, 0x40, 0x40, 0x40, 0x3F, // U + 0x00, 0x07, 0x18, 0x60, 0x18, 0x07, // V + 0x00, 0x3F, 0x40, 0x38, 0x40, 0x3F, // W + 0x00, 0x63, 0x14, 0x08, 0x14, 0x63, // X + 0x00, 0x07, 0x08, 0x70, 0x08, 0x07, // Y + 0x00, 0x61, 0x51, 0x49, 0x45, 0x43, // Z + 0x00, 0x00, 0x7F, 0x41, 0x41, 0x00, // [ + 0x00, 0x55, 0x2A, 0x55, 0x2A, 0x55, // 55 + 0x00, 0x00, 0x41, 0x41, 0x7F, 0x00, // ] + 0x00, 0x04, 0x02, 0x01, 0x02, 0x04, // ^ + 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, // _ + 0x00, 0x00, 0x0e, 0x11, 0x0e, 0x00, // ' Grave accent, used as "degree" symbol at HS1.6 + 0x00, 0x20, 0x54, 0x54, 0x54, 0x78, // a + 0x00, 0x7F, 0x48, 0x44, 0x44, 0x38, // b + 0x00, 0x38, 0x44, 0x44, 0x44, 0x20, // c + 0x00, 0x38, 0x44, 0x44, 0x48, 0x7F, // d + 0x00, 0x38, 0x54, 0x54, 0x54, 0x18, // e + 0x00, 0x08, 0x7E, 0x09, 0x01, 0x02, // f + 0x00, 0x18, 0xA4, 0xA4, 0xA4, 0x7C, // g + 0x00, 0x7F, 0x08, 0x04, 0x04, 0x78, // h + 0x00, 0x00, 0x44, 0x7D, 0x40, 0x00, // i + 0x00, 0x40, 0x80, 0x84, 0x7D, 0x00, // j + 0x00, 0x7F, 0x10, 0x28, 0x44, 0x00, // k + 0x00, 0x00, 0x41, 0x7F, 0x40, 0x00, // l + 0x00, 0x7C, 0x04, 0x18, 0x04, 0x78, // m + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x78, // n + 0x00, 0x38, 0x44, 0x44, 0x44, 0x38, // o + 0x00, 0xFC, 0x24, 0x24, 0x24, 0x18, // p + 0x00, 0x18, 0x24, 0x24, 0x18, 0xFC, // q + 0x00, 0x7C, 0x08, 0x04, 0x04, 0x08, // r + 0x00, 0x48, 0x54, 0x54, 0x54, 0x20, // s + 0x00, 0x04, 0x3F, 0x44, 0x40, 0x20, // t + 0x00, 0x3C, 0x40, 0x40, 0x20, 0x7C, // u + 0x00, 0x1C, 0x20, 0x40, 0x20, 0x1C, // v + 0x00, 0x3C, 0x40, 0x30, 0x40, 0x3C, // w + 0x00, 0x44, 0x28, 0x10, 0x28, 0x44, // x + 0x00, 0x1C, 0xA0, 0xA0, 0xA0, 0x7C, // y + 0x00, 0x44, 0x64, 0x54, 0x4C, 0x44, // z + 0x14, 0x14, 0x14, 0x14, 0x14, 0x14, // horiz lines +}; + +// ---------------------------------------------------------------------------- diff --git a/lib/weegfx/weegfx.cpp b/lib/weegfx/weegfx.cpp new file mode 100644 index 0000000..d96d298 --- /dev/null +++ b/lib/weegfx/weegfx.cpp @@ -0,0 +1,637 @@ +// Copyright (c) 2016 Patrick Dowling +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#include "weegfx.h" + +#include +#include +#include + +#define SWAP std::swap + +namespace weegfx { + +using weegfx::Graphics; + +// TODO +// - Bench templated draw_pixel_row (inlined versions) vs. function pointers +// - Offer specialized functions w/o clipping or specific draw mode (e.g. text overwrite) +// - Remainder masks as LUT or switch +// - 32bit ops? Should be possible along x-axis (use SIMD instructions?) but not y (page stride) +// - Clipping for x, y < 0 +// - Support 16 bit text characters? +// - Kerning/BBX etc. +// - print(string) -> print(char) can re-use variables +// - etc. + +#define CLIPX(x, w) \ + if (x + w > kWidth) w = kWidth - x; \ + if (x < 0) { \ + w += x; \ + x = 0; \ + } \ + if (w <= 0) return; \ + do { \ + } while (0) + +#define CLIPY(y, h) \ + if (y + h > kHeight) h = kHeight - y; \ + if (y < 0) { \ + h += y; \ + y = 0; \ + } \ + if (h <= 0) return; \ + do { \ + } while (0) + +// clang-format off +template +inline uint8_t pixel_op_impl(uint8_t a, uint8_t n) __attribute__((always_inline)); +template <> inline uint8_t pixel_op_impl(uint8_t a, uint8_t b) { return a | b; }; +template <> inline uint8_t pixel_op_impl(uint8_t a, uint8_t b) { return a ^ b; }; +template <> inline uint8_t pixel_op_impl(uint8_t, uint8_t b) { return b; }; +template <> inline uint8_t pixel_op_impl(uint8_t a, uint8_t b) { return a & ~b; }; + +template inline void draw_pixel_row(uint8_t *dst, weegfx::coord_t count, uint8_t mask) __attribute__((always_inline)); +template inline void draw_pixel_row(uint8_t *dst, weegfx::coord_t count, const uint8_t *src) __attribute__((always_inline)); +template inline void draw_pixel_row_lshift(uint8_t *dst, weegfx::coord_t count, const uint8_t *src, int shift) __attribute__((always_inline)); +template inline void draw_pixel_row_rshift(uint8_t *dst, weegfx::coord_t count, const uint8_t *src, int shift) __attribute__((always_inline)); +template inline void draw_rect(uint8_t *buf, weegfx::coord_t y, weegfx::coord_t w, weegfx::coord_t h) __attribute__((always_inline)); +template inline void blit(uint8_t *dst, coord_t y, coord_t w, coord_t h, const uint8_t *src); +// clang-format on + +template +inline void draw_pixel_row(uint8_t *dst, weegfx::coord_t count, uint8_t mask) +{ + while (count--) { + *dst = pixel_op_impl(*dst, mask); + ++dst; + } +} + +template +inline void draw_pixel_row(uint8_t *dst, weegfx::coord_t count, const uint8_t *src) +{ + while (count--) { + *dst = pixel_op_impl(*dst, *src); + ++dst; + ++src; + } +} + +template +inline void draw_pixel_row_lshift(uint8_t *dst, weegfx::coord_t count, const uint8_t *src, + int shift) +{ + while (count--) { + *dst = pixel_op_impl(*dst, *src << shift); + ++dst; + ++src; + } +} + +template +inline void draw_pixel_row_rshift(uint8_t *dst, weegfx::coord_t count, const uint8_t *src, + int shift) +{ + while (count--) { + *dst = pixel_op_impl(*dst, *src >> shift); + ++dst; + ++src; + } +} + +template +inline void draw_rect(uint8_t *buf, weegfx::coord_t y, weegfx::coord_t w, weegfx::coord_t h) +{ + weegfx::coord_t remainder = y & 0x7; + if (remainder) { + remainder = 8 - remainder; + uint8_t mask = ~(0xff >> remainder); + if (h < remainder) { + mask &= (0xff >> (remainder - h)); + h = 0; + } else { + h -= remainder; + } + + draw_pixel_row(buf, w, mask); + buf += Graphics::kWidth; + } + + remainder = h & 0x7; + h >>= 3; + while (h--) { + draw_pixel_row(buf, w, 0xff); + buf += Graphics::kWidth; + } + + if (remainder) { draw_pixel_row(buf, w, ~(0xff << remainder)); } +} + +template +inline void blit(uint8_t *dst, coord_t y, coord_t w, coord_t h, const uint8_t *src) +{ + coord_t remainder = y & 0x7; + if (!remainder) { + draw_pixel_row(dst, w, src); + } else { + draw_pixel_row_lshift(dst, w, src, remainder); + if (h >= 8) { + dst += Graphics::kWidth; + draw_pixel_row_rshift(dst, w, src, 8 - remainder); + } + } +} + +void Graphics::Init() +{ + frame_ = NULL; + setPrintPos(0, 0); +} + +void Graphics::Begin(uint8_t *frame, bool clear_frame) +{ + frame_ = frame; + if (clear_frame) memset(frame_, 0, kFrameSize); + + setPrintPos(0, 0); +} + +void Graphics::End() +{ + frame_ = NULL; +} + +void Graphics::drawRect(coord_t x, coord_t y, coord_t w, coord_t h) +{ + CLIPX(x, w); + CLIPY(y, h); + draw_rect(get_frame_ptr(x, y), y, w, h); +} + +void Graphics::clearRect(coord_t x, coord_t y, coord_t w, coord_t h) +{ + CLIPX(x, w); + CLIPY(y, h); + draw_rect(get_frame_ptr(x, y), y, w, h); +} + +void Graphics::invertRect(coord_t x, coord_t y, coord_t w, coord_t h) +{ + CLIPX(x, w); + CLIPY(y, h); + draw_rect(get_frame_ptr(x, y), y, w, h); +} + +void Graphics::drawFrame(coord_t x, coord_t y, coord_t w, coord_t h) +{ + // Obvious candidate for optimizing + // TODO Check w/h + drawHLine(x, y, w); + drawVLine(x, y + 1, h - 1); + drawVLine(x + w - 1, y + 1, h - 1); + drawHLine(x, y + h - 1, w); +} + +void Graphics::drawHLine(coord_t x, coord_t y, coord_t w) +{ + coord_t h = 1; + CLIPX(x, w); + CLIPY(y, h); + uint8_t *start = get_frame_ptr(x, y); + + draw_pixel_row(start, w, 0x1 << (y & 0x7)); +} + +void Graphics::drawVLine(coord_t x, coord_t y, coord_t h) +{ + coord_t w = 1; + CLIPX(x, w); + CLIPY(y, h); + uint8_t *buf = get_frame_ptr(x, y); + + // unaligned start + coord_t remainder = y & 0x7; + if (remainder) { + remainder = 8 - remainder; + uint8_t mask = ~(0xff >> remainder); + if (h < remainder) { + mask &= (0xff >> (remainder - h)); + h = 0; + } else { + h -= remainder; + } + + *buf |= mask; + buf += kWidth; + } + + // aligned loop + remainder = h & 0x7; + h >>= 3; + while (h--) { + *buf = 0xff; + buf += kWidth; + } + + // unaligned remainder + if (remainder) { *buf |= ~(0xff << remainder); } +} + +void Graphics::drawVLinePattern(coord_t x, coord_t y, coord_t h, uint8_t pattern) +{ + CLIPY(y, h); + uint8_t *buf = get_frame_ptr(x, y); + + // unaligned start + coord_t remainder = y & 0x7; + if (remainder) { + remainder = 8 - remainder; + uint8_t mask = ~(0xff >> remainder); + if (h < remainder) { + mask &= (pattern >> (remainder - h)); + h = 0; + } else { + h -= remainder; + } + + *buf |= (mask & pattern); + buf += kWidth; + } + + // aligned loop + remainder = h & 0x7; + h >>= 3; + while (h--) { + *buf = pattern; // FIXME this is probably not aligned right + buf += kWidth; + } + + // unaligned remainder + if (remainder) { *buf |= ~(pattern << remainder); } +} + +void Graphics::drawHLinePattern(coord_t x, coord_t y, coord_t w, uint8_t skip) +{ + uint8_t h = 1; + CLIPX(x, w); + CLIPY(y, h); + + uint8_t *buf = get_frame_ptr(x, y); + auto end = buf + w; + uint8_t mask = 0x1 << (y & 0x7); + while (buf < end) { + *buf |= mask; + buf += skip; + } +} + +void Graphics::drawBitmap8(coord_t x, coord_t y, coord_t w, const uint8_t *data) +{ + if (x + w > kWidth) w = kWidth - x; + if (x < 0) { + data += x; + w += x; + } + if (w <= 0) return; + + coord_t h = 8; + CLIPY(y, h); + + blit(get_frame_ptr(x, y), y, w, h, data); +} + +void Graphics::writeBitmap8(coord_t x, coord_t y, coord_t w, const uint8_t *data) +{ + if (x + w > kWidth) w = kWidth - x; + if (x < 0) { + data += x; + w += x; + } + if (w <= 0) return; + + coord_t h = 8; + CLIPY(y, h); + + blit(get_frame_ptr(x, y), y, w, h, data); +} + +void Graphics::drawLine(coord_t x0, coord_t y0, coord_t x1, coord_t y1) { + drawLine(x0, y0, x1, y1, 1); +} + +// p = period. Draw a dotted line with a pixel every p +void Graphics::drawLine(coord_t x0, coord_t y0, coord_t x1, coord_t y1, uint8_t p) { + uint8_t c = 0; + coord_t dx, dy; + if (x0 > x1 ) dx = x0-x1; else dx = x1-x0; + if (y0 > y1 ) dy = y0-y1; else dy = y1-y0; + + bool steep = false; + if (dy > dx) { + steep = true; + SWAP(dx, dy); + SWAP(x0, y0); + SWAP(x1, y1); + } + if (x0 > x1) { + SWAP(x0, x1); + SWAP(y0, y1); + } + coord_t err = dx >> 1; + coord_t ystep = (y1 > y0) ? 1 : -1; + coord_t y = y0; + + // OPTIMIZE Generate mask/buffer offset before loop and update on-the-fly instead of setPixeling + // OPTIMIZE Generate spans of pixels to draw + + if (steep) { + for(coord_t x = x0; x <= x1; x++ ) { + if (++c % p == 0) setPixel(y, x); + err -= dy; + if (err < 0) { + y += ystep; + err += dx; + } + } + } else { + for(coord_t x = x0; x <= x1; x++ ) { + if (++c % p == 0) setPixel(x, y); + err -= dy; + if (err < 0) { + y += ystep; + err += dx; + } + } + } +} + +void Graphics::drawCircle(coord_t center_x, coord_t center_y, coord_t r) +{ + coord_t f = 1 - r; + coord_t ddF_x = 1; + coord_t ddF_y = -2 * r; + coord_t x = 0; + coord_t y = r; + + setPixel(center_x, center_y + r); + setPixel(center_x, center_y - r); + setPixel(center_x + r, center_y); + setPixel(center_x - r, center_y); + + while (x < y) { + if (f >= 0) { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + + setPixel(center_x + x, center_y + y); + setPixel(center_x - x, center_y + y); + setPixel(center_x + x, center_y - y); + setPixel(center_x - x, center_y - y); + setPixel(center_x + y, center_y + x); + setPixel(center_x - y, center_y + x); + setPixel(center_x + y, center_y - x); + setPixel(center_x - y, center_y - x); + } +} + +#include "gfx_font_6x8.h" +static inline weegfx::font_glyph get_char_glyph(char c) __attribute__((always_inline)); +static inline weegfx::font_glyph get_char_glyph(char c) +{ + return ssd1306xled_font6x8 + kFixedFontW * (c - 32); +} + +static char print_buf[128] = {0}; + +// OPTIMIZE When printing strings, all chars will have the same y/remainder +// This will probably only save a few cycles, if any. Also the clipping can +// be made optional (template?) +template +void Graphics::blit_char(char c, coord_t x, coord_t y) +{ + if (!c) c = '0'; + if (c <= 32 || c > 127) return; + + coord_t w = kFixedFontW; + coord_t h = kFixedFontH; + font_glyph data = get_char_glyph(c); + if (c + w > kWidth) w = kWidth - x; + if (x < 0) { + w += x; + data += x; + } + if (w <= 0) return; + CLIPY(y, h); + + blit(get_frame_ptr(x, y), y, w, h, data); +} + +template +void Graphics::print_impl(const char *s) +{ + coord_t x = text_x_; + coord_t y = text_y_; + + // TODO Track position, only clip when necessary or early-out? + while (*s) { + blit_char(*s++, x, y); + x += kFixedFontW; + } + + text_x_ = x; +} + +void Graphics::print(char c) +{ + blit_char(c, text_x_, text_y_); + text_x_ += kFixedFontW; +} + +template +char *itos(type value, char *buf, size_t buflen) +{ + char *pos = buf + buflen; + *--pos = '\0'; + if (!value) { + *--pos = '0'; + if (pretty) // avoid jump when 0 -> +1 or -1 + *--pos = ' '; + } else { + char sign = 0; + if (value < 0) { + sign = '-'; + value = -value; + } else if (pretty) { + sign = '+'; + } + + while (value) { + *--pos = '0' + value % 10; + value /= 10; + } + if (sign) *--pos = sign; + } + + return pos; +} + +void Graphics::print(int value) +{ + print(itos(value, print_buf, sizeof(print_buf))); +} + +void Graphics::print(long value) +{ + print(itos(value, print_buf, sizeof(print_buf))); +} + +void Graphics::pretty_print(int value) +{ + print(itos(value, print_buf, sizeof(print_buf))); +} + +void Graphics::print(int value, unsigned width) +{ + char *str = itos(value, print_buf, sizeof(print_buf)); + while (str > print_buf && (unsigned)(str - print_buf) >= sizeof(print_buf) - width) *--str = ' '; + print_impl(str); +} + +void Graphics::write(int value, unsigned width) +{ + char *str = itos(value, print_buf, sizeof(print_buf)); + while (str > print_buf && (unsigned)(str - print_buf) >= sizeof(print_buf) - width) *--str = ' '; + print_impl(str); +} + +void Graphics::print(uint16_t value, unsigned width) +{ + char *str = itos(value, print_buf, sizeof(print_buf)); + while (str > print_buf && (unsigned)(str - print_buf) >= sizeof(print_buf) - width) *--str = ' '; + print(str); +} + +void Graphics::print(uint32_t value, size_t width) +{ + char *str = itos(value, print_buf, sizeof(print_buf)); + while (str > print_buf && (size_t)(str - print_buf) >= sizeof(print_buf) - width) *--str = ' '; + print(str); +} + +void Graphics::pretty_print(int value, unsigned width) +{ + char *str = itos(value, print_buf, sizeof(print_buf)); + + while (str > print_buf && (unsigned)(str - print_buf) >= sizeof(print_buf) - width) *--str = ' '; + print(str); +} + +void Graphics::pretty_print_right(int value) +{ + coord_t x = text_x_ - kFixedFontW; + coord_t y = text_y_; + + if (!value) { + blit_char('0', x, y); + } else { + char sign; + if (value < 0) { + value = -value; + sign = '-'; + } else { + sign = '+'; + } + + while (value) { + blit_char('0' + value % 10, x, y); + x -= kFixedFontW; + value /= 10; + } + if (sign) blit_char(sign, x, y); + } +} + +void Graphics::print(const char *s) +{ + print_impl(s); +} + +void Graphics::print(const char *s, unsigned len) +{ + coord_t x = text_x_; + coord_t y = text_y_; + while (*s && len--) { + blit_char(*s++, x, y); + x += kFixedFontW; + } + + text_x_ = x; +} + +void Graphics::print_right(const char *s) +{ + weegfx::coord_t x = text_x_; + weegfx::coord_t y = text_y_; + const char *c = s; + while (*c) ++c; // find end + + while (c > s) { + x -= kFixedFontW; + blit_char(*--c, x, y); + } +} + +void Graphics::write_right(const char *s) +{ + auto x = text_x_; + auto y = text_y_; + auto c = s; + while (*c) ++c; + while (c > s) { + x -= kFixedFontW; + blit_char(*--c, x, y); + } +} + +void Graphics::printf(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vsnprintf(print_buf, sizeof(print_buf), fmt, args); + va_end(args); + print(print_buf); +} + +void Graphics::drawStr(coord_t x, coord_t y, const char *s) +{ + while (*s) { + blit_char(*s++, x, y); + x += kFixedFontW; + } +} + +} // namespace weegfx diff --git a/lib/weegfx/weegfx.h b/lib/weegfx/weegfx.h new file mode 100644 index 0000000..d8ddf4e --- /dev/null +++ b/lib/weegfx/weegfx.h @@ -0,0 +1,156 @@ +// Copyright (c) 2016 Patrick Dowling +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +#ifndef WEEGFX_H_ +#define WEEGFX_H_ + +#include +#include + +namespace weegfx { + +using coord_t = int_fast16_t; +using font_glyph = const uint8_t *; + +static constexpr weegfx::coord_t kFixedFontW = 6; +static constexpr weegfx::coord_t kFixedFontH = 8; + +enum PIXEL_OP { + PIXEL_OP_OR, // DST | SRC + PIXEL_OP_XOR, // DST ^ SRC + PIXEL_OP_NAND, // DST &= ~SRC + PIXEL_OP_SRC, // DST = SRC +}; + +// Quick & dirty graphics for 128 x 64 framebuffer with vertical pixels. +// - Writes to provided framebuffer +// - Interface pseudo-compatible with u8glib +// - Makes some assumptions based on fixed size and pixel orientation +class Graphics { +public: + static constexpr uint8_t kWidth = 128; + static constexpr uint8_t kHeight = 64; + static constexpr size_t kFrameSize = kWidth * kHeight / 8; + + void Init(); + + void Begin(uint8_t *frame, bool clear_frame); + void End(); + + inline void setPixel(coord_t x, coord_t y) __attribute__((always_inline)); + + void drawRect(coord_t x, coord_t y, coord_t w, coord_t h); + void clearRect(coord_t x, coord_t y, coord_t w, coord_t h); + void invertRect(coord_t x, coord_t y, coord_t w, coord_t h); + void drawFrame(coord_t x, coord_t y, coord_t w, coord_t h); + + void drawHLine(coord_t x, coord_t y, coord_t w); + void drawVLine(coord_t x, coord_t y, coord_t h); + void drawVLinePattern(coord_t x, coord_t y, coord_t h, uint8_t pattern); + void drawHLinePattern(coord_t x, coord_t y, coord_t w, uint8_t skip); + + void drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2, uint8_t p); + void drawLine(coord_t x1, coord_t y1, coord_t x2, coord_t y2); + + void drawBitmap8(coord_t x, coord_t y, coord_t w, const uint8_t *data); + void writeBitmap8(coord_t x, coord_t y, coord_t w, const uint8_t *data); + + // Beware: No clipping + void drawCircle(coord_t center_x, coord_t center_y, coord_t r); + + void setPrintPos(coord_t x, coord_t y); + void movePrintPos(coord_t dx, coord_t dy); + + void print(char); + void print(int); + void print(int, unsigned width); + void write(int, unsigned width); + void print(uint16_t, unsigned width); + void print(uint32_t, unsigned width); + void print(long); + + void pretty_print(int); + void pretty_print(int, unsigned width); + + // Print right-aligned number at current print pos; print pos is unchanged + void pretty_print_right(int); + + // Print string at current print pos and move print pos + void print(const char *); + void print(const char *, unsigned len); + + // Print right-aligned string at current print pos; print pos is unchanged + void print_right(const char *); + + // Write right-aligned string at current print pos; print pos is unchanged + void write_right(const char *); + + // Print string at absolute coords, doesn't move print pos + void drawStr(coord_t x, coord_t y, const char *str); + + // Might be time-consuming + void printf(const char *fmt, ...); + + inline void drawAlignedByte(coord_t x, coord_t y, uint8_t byte) __attribute__((always_inline)); + +private: + uint8_t *frame_; + + coord_t text_x_; + coord_t text_y_; + + inline uint8_t *get_frame_ptr(const coord_t x, const coord_t y) __attribute__((always_inline)); + + // clang-format off + template void blit_char(char c, coord_t x, coord_t y); + template void print_impl(const char *s); + // clang-format on +}; + +inline void Graphics::setPixel(coord_t x, coord_t y) +{ + *(get_frame_ptr(x, y)) |= (0x1 << (y & 0x7)); +} + +inline void Graphics::drawAlignedByte(coord_t x, coord_t y, uint8_t byte) +{ + *get_frame_ptr(x, y) = byte; +} + +inline void Graphics::setPrintPos(coord_t x, coord_t y) +{ + text_x_ = x; + text_y_ = y; +} + +inline void Graphics::movePrintPos(coord_t dx, coord_t dy) +{ + text_x_ += dx; + text_y_ += dy; +} + +inline uint8_t *Graphics::get_frame_ptr(const coord_t x, const coord_t y) +{ + return frame_ + ((y >> 3) << 7) + x; +} + +}; // namespace weegfx + +#endif // WEEGFX_H_ diff --git a/platformio.ini b/platformio.ini index 89eac25..5a63c76 100644 --- a/platformio.ini +++ b/platformio.ini @@ -9,21 +9,24 @@ [env] extra_scripts = pre:lib/.fetch_deps.py +;upload_protocol = teensy-cli -[env:OC_teensy40] -platform = teensy@4.15.0 ; https://github.com/platformio/platform-teensy/releases +[env:OC_T40] +platform = teensy@4.17.0 ; https://github.com/platformio/platform-teensy/releases framework = arduino board = teensy40 -build_flags = -DUSB_MIDI_SERIAL -D_GLIBCXX_USE_C99 -I./lib/ -Wdouble-promotion ;-D TEENSY_OPT_SMALLEST_CODE +build_flags = -DUSB_MIDI_SERIAL -std=c++17 -I./lib/ -Wdouble-promotion ;-D TEENSY_OPT_SMALLEST_CODE board_build.f_cpu = 600000000L lib_deps = - https://github.com/eh2k/squares-and-circles/releases/download/0.0.0/machine_OC40_a26a733.zip + https://github.com/eh2k/squares-and-circles/releases/download/0.0.0/machine_OC40_6964451.zip ./lib/stmlib/ ./lib/braids/ ./lib/plaits/ ./lib/rings/ ./lib/peaks/ ./lib/fv1/ + ./lib/soundpipe/ lib_ldf_mode = off build_src_filter = +<*> + -<../src/main_TU.cxx> \ No newline at end of file diff --git a/src/SAM.cxx b/src/SAM.cxx index 3e227dc..07e0094 100644 --- a/src/SAM.cxx +++ b/src/SAM.cxx @@ -25,11 +25,10 @@ #include "machine.h" #include "stmlib/dsp/dsp.h" -#include "sample.hxx" +#include "base/SampleEngine.hxx" namespace sam { - int debug = 0; #include "SAM/sam.c" #include "SAM/render.c" #include "SAM/reciter.c" diff --git a/src/base/FaustEngine.hxx b/src/base/FaustEngine.hxx new file mode 100644 index 0000000..97cf25b --- /dev/null +++ b/src/base/FaustEngine.hxx @@ -0,0 +1,144 @@ +#include "machine.h" +#include "stmlib/dsp/dsp.h" +#include "stmlib/dsp/units.h" + +struct dsp +{ +}; + +struct Meta +{ + void declare(const char *key, const char *value) + { + } +}; + +struct Soundfile +{ +}; + +struct UI +{ + virtual void openTabBox(const char *label){}; + virtual void openVerticalBox(const char *title) = 0; + virtual void openHorizontalBox(const char *title) = 0; + virtual void closeBox() = 0; + + virtual void declare(float *zone, const char *key, const char *val) = 0; + virtual void addNumEntry(const char *label, float *zone, float init, float min, float max, float step) = 0; + virtual void addHorizontalSlider(const char *label, float *zone, float init, float min, float max, float step) = 0; + virtual void addVerticalSlider(const char *label, float *zone, float init, float min, float max, float step) = 0; + virtual void addButton(const char *label, float *zone) = 0; + virtual void addCheckButton(const char *label, float *zone) = 0; + + // -- passive widgets + + virtual void addHorizontalBargraph(const char *label, float *zone, float min, float max){}; + virtual void addVerticalBargraph(const char *label, float *zone, float min, float max){}; + + // -- soundfiles + + virtual void addSoundfile(const char *label, const char *filename, Soundfile **sf_zone){}; +}; + +using namespace machine; + +template +class FaustEngine : public machine::Engine, UI +{ + static_assert(std::is_base_of::value, "T must derive from dsp"); + + float bufferL[FRAME_BUFFER_SIZE]; + float bufferR[FRAME_BUFFER_SIZE]; + + float _pitch = 0.5f; + + T _faust; + + float *_trigger = nullptr; + + void openVerticalBox(const char *title) override + { + } + void openHorizontalBox(const char *title) override + { + } + void closeBox() override + { + } + void declare(float *zone, const char *key, const char *val) override + { + } + + virtual void addButton(const char *label, float *zone) + { + if (_trigger == nullptr) + _trigger = zone; + } + + virtual void addNumEntry(const char *label, float *zone, float init, float min, float max, float step) + { + } + + virtual void addCheckButton(const char *label, float *zone) + { + addHorizontalSlider(label, zone, 0, 0, 1, 1); + } + + virtual void addVerticalSlider(const char *label, float *zone, float init, float min, float max, float step) override + { + addHorizontalSlider(label, zone, init, min, max, step); + } + + void addHorizontalSlider(const char *label, float *zone, float init, float min, float max, float step) override + { + for (size_t i = 0; i < LEN_OF(param); i++) + { + if (param[i].name == nullptr) + { + param[i].init(label, zone, init, min, max); + return; + } + } + } + +public: + FaustEngine() : machine::Engine(engine_props) + { + // static_assert(sizeof(T) < 150000, ""); + + _faust.init(48000); + _faust.buildUserInterface(this); + } + + ~FaustEngine() override + { + } + + float base_frequency = 440.f; + float base_pitch = 69.f; + + float note_to_frequency(float note) + { + return base_frequency * powf(2.f, (note - base_pitch) / 12.f); + } + + void process(const machine::ControlFrame &frame, OutputFrame &of) override + { + if (_trigger != nullptr) + *_trigger = frame.trigger ? 1.f : 0.f; + + // auto note = (float)frame.midi.key + _pitch * 12.f + (frame.midi.pitch / 128) + frame.cv_voltage * 12; + // auto f = note_to_frequency(note + 12); + + float *outputs[] = {bufferL, bufferR}; + float *ins[] = {machine::get_aux(AUX_L), machine::get_aux(AUX_R)}; + + _faust.compute(FRAME_BUFFER_SIZE, &ins[0], &outputs[0]); + + of.out = bufferL; + + if (_faust.getNumOutputs() > 1) + of.aux = bufferR; + } +}; \ No newline at end of file diff --git a/src/ch_oh.hxx b/src/base/HiHatsEngine.hxx similarity index 96% rename from src/ch_oh.hxx rename to src/base/HiHatsEngine.hxx index e88d110..93144b0 100644 --- a/src/ch_oh.hxx +++ b/src/base/HiHatsEngine.hxx @@ -30,7 +30,7 @@ using namespace machine; -struct CHOH : public Engine +struct HiHatsEngine : public Engine { Engine *_oh = nullptr; Engine *_ch = nullptr; @@ -46,7 +46,7 @@ struct CHOH : public Engine float bufferOut[machine::FRAME_BUFFER_SIZE]; public: - CHOH() : Engine(TRIGGER_INPUT | ACCENT_INPUT | VOCT_INPUT) + HiHatsEngine() : Engine(TRIGGER_INPUT | ACCENT_INPUT | VOCT_INPUT) { _ch_env.Init(); _oh_env.Init(); diff --git a/src/sample.hxx b/src/base/SampleEngine.hxx similarity index 96% rename from src/sample.hxx rename to src/base/SampleEngine.hxx index e08f616..fb83b12 100644 --- a/src/sample.hxx +++ b/src/base/SampleEngine.hxx @@ -80,7 +80,7 @@ struct SampleEngine : public machine::Engine } public: - bool loop = false; + int loop = 0; // 1 = knight rider, 2 infinite uint8_t rtrg = 0; uint8_t rtim = 0; @@ -145,11 +145,22 @@ public: i += this->start < this->end ? inc : -inc; } - if (loop) + if (loop == 0) + { + + } + else if (loop == 1) { if (i < s || i > e) inc = -inc; } + else if (loop == 2) + { + if (i < s) + i += (e-s); + else if (i > e) + i -= (e-s); + } of.out = buffer; } diff --git a/src/braids.cxx b/src/braids.cxx index c142162..861bf51 100644 --- a/src/braids.cxx +++ b/src/braids.cxx @@ -62,7 +62,8 @@ struct BraidsEngine : public Engine if (frame.gate) { - envelope.Trigger(braids::ENV_SEGMENT_ATTACK); + //Not working witch Attack > 0 + //envelope.Trigger(braids::ENV_SEGMENT_DECAY); } uint32_t ad_value = envelope.Render(); @@ -91,10 +92,10 @@ struct BraidsEngine : public Engine osc.Render(sync_samples, audio_samples, FRAME_BUFFER_SIZE); - uint16_t gain = _decay < UINT16_MAX ? ad_value : 65535; + uint32_t gain = _decay < UINT16_MAX ? ad_value : UINT16_MAX; for (int i = 0; i < FRAME_BUFFER_SIZE; i++) - audio_samples[i] = audio_samples[i] * gain / UINT16_MAX / 4; + audio_samples[i] = (gain * audio_samples[i]) / UINT16_MAX; of.push(audio_samples, LEN_OF(audio_samples)); } diff --git a/src/clap.cxx b/src/clap.cxx index 9b2cf06..b1fd8ef 100644 --- a/src/clap.cxx +++ b/src/clap.cxx @@ -35,7 +35,7 @@ #define FLASHMEM #endif -#include "sample.hxx" +#include "base/SampleEngine.hxx" #include "claps/cp909.h" #include "claps/cp808.h" diff --git a/src/faust.cxx b/src/faust.cxx index 5ee2683..802e8d1 100644 --- a/src/faust.cxx +++ b/src/faust.cxx @@ -1,148 +1,4 @@ -#include "machine.h" -#include "stmlib/dsp/dsp.h" -#include "stmlib/dsp/units.h" - -struct dsp -{ -}; - -struct Meta -{ - void declare(const char *key, const char *value) - { - } -}; - -struct Soundfile -{ -}; - -struct UI -{ - virtual void openTabBox(const char *label){}; - virtual void openVerticalBox(const char *title) = 0; - virtual void openHorizontalBox(const char *title) = 0; - virtual void closeBox() = 0; - - virtual void declare(float *zone, const char *key, const char *val) = 0; - virtual void addNumEntry(const char *label, float *zone, float init, float min, float max, float step) = 0; - virtual void addHorizontalSlider(const char *label, float *zone, float init, float min, float max, float step) = 0; - virtual void addVerticalSlider(const char *label, float *zone, float init, float min, float max, float step) = 0; - virtual void addButton(const char *label, float *zone) = 0; - virtual void addCheckButton(const char *label, float *zone) = 0; - - // -- passive widgets - - virtual void addHorizontalBargraph(const char *label, float *zone, float min, float max){}; - virtual void addVerticalBargraph(const char *label, float *zone, float min, float max){}; - - // -- soundfiles - - virtual void addSoundfile(const char *label, const char *filename, Soundfile **sf_zone){}; -}; - -using namespace machine; - -template -class FaustEngine : public machine::Engine, UI -{ - static_assert(std::is_base_of::value, "T must derive from dsp"); - - float bufferL[FRAME_BUFFER_SIZE]; - float bufferR[FRAME_BUFFER_SIZE]; - - float _pitch = 0.5f; - - T _faust; - - float *_trigger = nullptr; - - void openVerticalBox(const char *title) override - { - } - void openHorizontalBox(const char *title) override - { - } - void closeBox() override - { - } - void declare(float *zone, const char *key, const char *val) override - { - } - - virtual void addButton(const char *label, float *zone) - { - if (_trigger == nullptr) - _trigger = zone; - } - - virtual void addNumEntry(const char *label, float *zone, float init, float min, float max, float step) - { - } - - virtual void addCheckButton(const char *label, float *zone) - { - addHorizontalSlider(label, zone, 0, 0, 1, 1); - } - - virtual void addVerticalSlider(const char *label, float *zone, float init, float min, float max, float step) override - { - addHorizontalSlider(label, zone, init, min, max, step); - } - - void addHorizontalSlider(const char *label, float *zone, float init, float min, float max, float step) override - { - for (size_t i = 0; i < LEN_OF(param); i++) - { - if (param[i].name == nullptr) - { - param[i].init(label, zone, init, min, max); - return; - } - } - } - -public: - FaustEngine() : machine::Engine(engine_props) - { - // static_assert(sizeof(T) < 150000, ""); - - _faust.init(48000); - _faust.buildUserInterface(this); - } - - ~FaustEngine() override - { - } - - float base_frequency = 440.f; - float base_pitch = 69.f; - - float note_to_frequency(float note) - { - return base_frequency * powf(2.f, (note - base_pitch) / 12.f); - } - - void process(const machine::ControlFrame &frame, OutputFrame &of) override - { - if (_trigger != nullptr) - *_trigger = frame.trigger ? 1.f : 0.f; - - // auto note = (float)frame.midi.key + _pitch * 12.f + (frame.midi.pitch / 128) + frame.cv_voltage * 12; - // auto f = note_to_frequency(note + 12); - - float *outputs[] = {bufferL, bufferR}; - float *ins[] = {machine::get_aux(AUX_L), machine::get_aux(AUX_R)}; - - _faust.compute(FRAME_BUFFER_SIZE, &ins[0], &outputs[0]); - - of.out = bufferL; - - if (_faust.getNumOutputs() > 1) - of.aux = bufferR; - } -}; - +#include "base/FaustEngine.hxx" #include "faust/djembe.hxx" #include "faust/rev_dattorro.hxx" diff --git a/src/fx_reverb.cxx b/src/fx_reverb.cxx index 335cc8f..6407daf 100644 --- a/src/fx_reverb.cxx +++ b/src/fx_reverb.cxx @@ -108,7 +108,7 @@ struct CloudsDiffuser : public Engine { fx_.set_amount(raw); - float *ins[] = {machine::get_aux(-2), machine::get_aux(-1)}; + float *ins[] = {machine::get_aux(AUX_L), machine::get_aux(AUX_R)}; for (int i = 0; i < FRAME_BUFFER_SIZE; i++) { diff --git a/src/fx_reverbSC.cxx b/src/fx_reverbSC.cxx new file mode 100644 index 0000000..e712e88 --- /dev/null +++ b/src/fx_reverbSC.cxx @@ -0,0 +1,84 @@ +// Copyright (C)2022 - Eduard Heidt +// +// Author: Eduard Heidt (eh2k@gmx.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// See http://creativecommons.org/licenses/MIT/ for more information. +// + +#include "stmlib/stmlib.h" +#include "stmlib/dsp/filter.h" +#include "machine.h" +#include + +extern "C" { +#include "soundpipe/revsc.h" +} + +using namespace machine; + +struct ReverbSC : public Engine +{ + sp_data sp_data_; + sp_revsc sp_revsc_; + uint8_t aux[107440]; //sp_revsc_.aux.size + + float raw = 1.f; + float reverb_amount; + float feedback; + float gain; + + float bufferL[FRAME_BUFFER_SIZE]; + float bufferR[FRAME_BUFFER_SIZE]; + + ReverbSC() : Engine(AUDIO_PROCESSOR) + { + sp_data_.sr = machine::SAMPLE_RATE; + sp_data_.aux.ptr = &aux[0]; + sp_data_.aux.size = sizeof(aux); + sp_revsc_init(&sp_data_, &sp_revsc_); + + param[0].init("D/W", &raw, raw); + param[1].init("Feedback", &sp_revsc_.feedback, 0.97f, 0, 1); + param[2].init("LpFreq", &sp_revsc_.lpfreq, 10000, 0, machine::SAMPLE_RATE/2); + } + + void process(const ControlFrame &frame, OutputFrame &of) override + { + float *ins[] = {machine::get_aux(AUX_L), machine::get_aux(AUX_R)}; + + for (int i = 0; i < FRAME_BUFFER_SIZE; ++i) + { + sp_revsc_compute(&sp_data_, &sp_revsc_, &ins[0][i], &ins[1][i], &bufferL[i], &bufferR[i]); + bufferL[i] = raw * bufferL[i] + (1 - raw) * ins[0][i]; + bufferR[i] = raw * bufferR[i] + (1 - raw) * ins[1][i]; + } + + of.out = bufferL; + of.aux = bufferR; + } +}; + +void init_reverbSC() +{ + machine::add(FX, "ReverbSC"); +} + +MACHINE_INIT(init_reverbSC); \ No newline at end of file diff --git a/src/gnd_noise.cxx b/src/gnd_noise.cxx new file mode 100644 index 0000000..7569b3a --- /dev/null +++ b/src/gnd_noise.cxx @@ -0,0 +1,72 @@ +// Copyright (C)2022 - Eduard Heidt +// +// Author: Eduard Heidt (eh2k@gmx.de) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// See http://creativecommons.org/licenses/MIT/ for more information. +// + +#include "machine.h" +#include "misc/noise.hxx" + +using namespace machine; + +struct NoiseEngine : public Engine +{ + const char *modes[2] = {">White", ">Pink"}; + PinkNoise<> pink; + float gain; + uint8_t mode; + + NoiseEngine() : Engine(0) + { + param[0].init("Level", &gain, 1); + param[1].init(">Type", &mode, 0, 0, LEN_OF(modes) - 1); + }; + + float buffer[machine::FRAME_BUFFER_SIZE]; + + void process(const ControlFrame &frame, OutputFrame &of) override + { + for (int i = 0; i < FRAME_BUFFER_SIZE; i++) + { + if (mode == 0) + buffer[i] = pink.white.nextf(-1.f, 1.f) * gain; + else + buffer[i] = pink.nextf(-1.f, 1.f) * gain; + } + + of.push(buffer, machine::FRAME_BUFFER_SIZE); + } + + void onDisplay(uint8_t *display) override + { + param[1].name = modes[mode]; + + gfx::drawEngine(display, this); + } +}; + +void init_noise() +{ + machine::add(CV, "Noise"); +} + +MACHINE_INIT(init_noise); \ No newline at end of file diff --git a/src/main.cxx b/src/main.cxx index ae2a7d2..1d02cf2 100644 --- a/src/main.cxx +++ b/src/main.cxx @@ -33,6 +33,7 @@ int main() { MACHINE_INIT(init_voltage); + MACHINE_INIT(init_noise); MACHINE_INIT(init_midi_monitor); MACHINE_INIT(init_midi_clock); MACHINE_INIT(init_quantizer); @@ -42,6 +43,7 @@ int main() MACHINE_INIT(init_sample_roms); MACHINE_INIT(init_clap); MACHINE_INIT(init_reverb); + MACHINE_INIT(init_reverbSC); MACHINE_INIT(init_faust); MACHINE_INIT(init_rings); MACHINE_INIT(init_speech); @@ -49,9 +51,9 @@ int main() MACHINE_INIT(init_delay); MACHINE_INIT(init_modulations); MACHINE_INIT(init_fv1); - MACHINE_INIT(init_midi_polyVA); + MACHINE_INIT(init_midi_polyVA) - machine::setup("0.0l", 0); + machine::setup("0.0m", 0); while (true) machine::loop(); diff --git a/src/modulations.cxx b/src/modulations.cxx index 514d37f..8f6dcec 100644 --- a/src/modulations.cxx +++ b/src/modulations.cxx @@ -64,6 +64,7 @@ struct ModulationBase : machine::ModulationSource struct CV : ModulationBase { uint8_t cv_channel = 0; + uint8_t tr_channel = 0; void eeprom(std::function read_write) override { @@ -78,13 +79,33 @@ struct CV : ModulationBase { machine::get_io_info(1, cv_channel, tmp); }; - param[1].init(".", &attenuverter, attenuverter, -3, +3); + param[1].init("OP", &tr_channel, tr_channel, 0, machine::get_io_info(0)); + param[1].print_value = [&](char *tmp) + { + if (tr_channel == 0) + sprintf(tmp, "Thru"); + else if (tr_channel <= 4) + sprintf(tmp, "S&H-T%d", tr_channel); + else if (tr_channel <= 8) + sprintf(tmp, "T&H-T%d", tr_channel - 4); + else + machine::get_io_info(0, tr_channel - 1, tmp); + }; + + param[2].init(".", &attenuverter, attenuverter, -1, +1); } void process(machine::Parameter &target, machine::ControlFrame &frame) override { - value = machine::get_cv(cv_channel); - target.modulate(value * attenuverter); + if (tr_channel == 0 || (tr_channel <= 4 && machine::get_trigger(tr_channel - 1)) || (tr_channel <= 8 && machine::get_gate(tr_channel - 4))) + value = machine::get_cv(cv_channel); + + float a = attenuverter; + + if(!(target.flags & machine::Parameter::IS_V_OCT)) + a *= 3; // ADC is -3...6V ...faktor 3 for non V/OCT + + target.modulate(value * a); } }; diff --git a/src/peaks.cxx b/src/peaks.cxx index f25ffca..5fe0b76 100644 --- a/src/peaks.cxx +++ b/src/peaks.cxx @@ -13,7 +13,7 @@ #include "peaks/pulse_processor/pulse_shaper.h" #include "peaks/pulse_processor/pulse_randomizer.h" #include "peaks/gate_processor.h" -#include "ch_oh.hxx" +#include "base/HiHatsEngine.hxx" using namespace machine; @@ -109,7 +109,7 @@ struct PeaksEngine : public Engine } }; -class Hihat808 : public CHOH +class Hihat808 : public HiHatsEngine { PeaksEngine oh; PeaksEngine ch; @@ -125,15 +125,15 @@ class Hihat808 : public CHOH void init_peaks() { add>(DRUM, "FM-Drum", INT16_MAX, INT16_MAX, INT16_MAX, INT16_MAX, "Freq.", "Noise", "FM", "Decay"); - + add>(DRUM, "808ish-BD", INT16_MAX, INT16_MAX, INT16_MAX, INT16_MAX, "Pitch", "Punch", "Tone", "Decay"); add>(DRUM, "808ish-SD", INT16_MAX, INT16_MAX, INT16_MAX, INT16_MAX, "Pitch", "Snappy", "Tone", "Decay"); add(DRUM, "808ish-HiHat"); - //add>(DRUM, "808ish-HiHat", INT16_MAX, INT16_MAX, INT16_MAX, INT16_MAX, "Decay"); - add>(CV, "Envelope", 0, INT16_MAX, INT16_MAX, INT16_MAX, "Attack", "Decay", "Sustain", "Release"); - add>(CV, "LFO", 0, 0, INT16_MAX, 0, "Freq.", "Shape", "Param", "Phase"); + // add>(DRUM, "808ish-HiHat", INT16_MAX, INT16_MAX, INT16_MAX, INT16_MAX, "Decay"); + add>(CV, "Envelope", 0, INT16_MAX, INT16_MAX, INT16_MAX, "Attack", "Decay", "Sustain", "Release"); + add>(CV, "LFO", 0, 0, INT16_MAX, 0, "Freq.", "Shape", "Param", "Phase"); } MACHINE_INIT(init_peaks); \ No newline at end of file diff --git a/src/sample.cxx b/src/sample.cxx index 91af69c..9b2d6b3 100644 --- a/src/sample.cxx +++ b/src/sample.cxx @@ -23,7 +23,7 @@ // See http://creativecommons.org/licenses/MIT/ for more information. // -#include "sample.hxx" +#include "base/SampleEngine.hxx" template <> float tsample_spec::get_float(int index) const diff --git a/src/sample_roms.cxx b/src/sample_roms.cxx index 4000d3e..9c1bf1a 100644 --- a/src/sample_roms.cxx +++ b/src/sample_roms.cxx @@ -1,4 +1,4 @@ -#include "sample.hxx" +#include "base/SampleEngine.hxx" #include #ifndef PROGMEM #include "pgmspace.h" @@ -36,8 +36,7 @@ #include "eproms/linn9k/clap0125.bin.h" //#include "eproms/linn9k/hat0124.bin.h" -#include "ch_oh.hxx" -#include "sample.hxx" +#include "base/HiHatsEngine.hxx" struct Am6070Engine : public SampleEngine { @@ -100,7 +99,7 @@ struct HIHATS : public SampleEngine } }; -struct CH_OH : public CHOH +struct CH_OH : public HiHatsEngine { HIHATS oh; HIHATS ch; @@ -164,7 +163,7 @@ struct TR707 : public SampleEngine } }; -struct TR707_CH_OH : public CHOH +struct TR707_CH_OH : public HiHatsEngine { TR707 oh; TR707 ch; @@ -209,7 +208,7 @@ struct TR909_Ride : public SampleEngine } }; -struct TR909_CH_OH : public CHOH +struct TR909_CH_OH : public HiHatsEngine { TR909_OH oh; TR909_CH ch; @@ -221,7 +220,7 @@ struct TR909_CH_OH : public CHOH } }; -struct TR909_CR_OR : public CHOH +struct TR909_CR_OR : public HiHatsEngine { TR909_Ride oh; TR909_Ride ch; diff --git a/src/voltage.cxx b/src/voltage.cxx index 04c49e6..d4b96ec 100644 --- a/src/voltage.cxx +++ b/src/voltage.cxx @@ -25,39 +25,42 @@ // #include "machine.h" +#include "stmlib/dsp/dsp.h" using namespace machine; class VoltsPerOctave : public Engine { char tmp[64]; - uint8_t note = DEFAULT_NOTE; + uint16_t note = DEFAULT_NOTE * machine::PITCH_PER_OCTAVE / 12; uint8_t tune = 32; + int32_t cv0 = 0; int32_t cv = 0; + float glide = 0; public: - VoltsPerOctave() : Engine(OUT_EQ_VOLT | VOCT_INPUT) + VoltsPerOctave() : Engine(OUT_EQ_VOLT | VOCT_INPUT | TRANSPOSE_EQ_0) { param[0].init_v_oct("Tone", ¬e); - param[1].init("\t", &tune, tune, 0, 64); + param[1].init("Fine", &tune, tune, 0, 64); + param[2].init("Slew", &glide, 0, 0, 0.5f); } void process(const ControlFrame &frame, OutputFrame &of) override { - cv = frame.cv_voltage_ + - (((int)note - DEFAULT_NOTE) * machine::PITCH_PER_OCTAVE / 12) + - (((int)tune - 32) << 3); + cv0 = frame.cv_voltage_ + + (((int)note - (DEFAULT_NOTE * machine::PITCH_PER_OCTAVE / 12))) + + (((int)tune - 32) << 4); + + ONE_POLE(cv, cv0, powf(1 - glide, 10)); of.push(&cv, 1); } void onDisplay(uint8_t *display) override { - gfx::drawString(display, 18, 16, "Note"); - gfx::drawString(display, 82, 16, "Tune"); - - sprintf(tmp, "DAC: %.2fV", ((float)cv / machine::PITCH_PER_OCTAVE)); - gfx::drawString(display, 64, 53, tmp, 0); + sprintf(tmp, "OUT: %.2fV", ((float)cv / machine::PITCH_PER_OCTAVE)); + gfx::drawString(display, 4 + 64, 52, tmp, 0); gfx::drawEngine(display, this); } diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..fb367ea --- /dev/null +++ b/test/index.html @@ -0,0 +1,69 @@ + + + + + + + + + + +
+
+
+
+ +
+ + + + + +
+ + + + +
+ + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/test/launch.json b/test/launch.json new file mode 100644 index 0000000..04aa61c --- /dev/null +++ b/test/launch.json @@ -0,0 +1,36 @@ +// AUTOMATICALLY GENERATED FILE. PLEASE DO NOT MODIFY IT MANUALLY +// +// PIO Unified Debugger +// +// Documentation: https://docs.platformio.org/page/plus/debugging.html +// Configuration: https://docs.platformio.org/page/projectconf/section_env_debug.html + +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + "program": "${workspaceFolder}/.test/test.exe", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}/.test/", + "environment": [], + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + }, + { + "description": "Set Disassembly Flavor to Intel", + "text": "-gdb-set disassembly-flavor intel", + "ignoreFailures": true + } + ] + } + } +} \ No newline at end of file diff --git a/test/serial.js b/test/serial.js new file mode 100644 index 0000000..9b4ac8c --- /dev/null +++ b/test/serial.js @@ -0,0 +1,119 @@ + +document.getElementById('connectButton').onclick = teensyConnect +document.getElementById('leftButton').onclick = async () => await teensySend("l") +document.getElementById('rightButton').onclick = async () => await teensySend("r") +document.getElementById('menuButton').onclick = async () => await teensySend("L") +document.getElementById('ioButton').onclick = async () => await teensySend("R") +document.getElementById('midiButton').onclick = async () => await teensySend("I") + +document.getElementById('leftEncoder-').onclick = async () => await teensySend("j") +document.getElementById('leftEncoder+').onclick = async () => await teensySend("J") +document.getElementById('leftEncoderB').onclick = async () => await teensySend("y") +document.getElementById('leftEncoderH').onclick = async () => await teensySend("Y") + +document.getElementById('rightEncoder-').onclick = async () => await teensySend("k") +document.getElementById('rightEncoder+').onclick = async () => await teensySend("K") +document.getElementById('rightEncoderB').onclick = async () => await teensySend("z") +document.getElementById('rightEncoderH').onclick = async () => await teensySend("Z") + +document.getElementById('saveButton').onclick = async () => await teensySend("S") +document.getElementById('debugButton').onclick = async () => await teensySend("d") + +var canvas = document.getElementById('display'); +var context = canvas.getContext('2d'); + +const n = 2; +canvas.width *= n; +canvas.height *= n; +context.scale(n, n) + +async function drawPixel(x, y, color) { + // Math.round() used to decrease smoothing when numbers have decimal parts. + var roundedX = Math.round(x); + var roundedY = Math.round(y); + context.fillStyle = color || '#000'; + context.fillRect(roundedX, roundedY, 1, 1); +} + +async function drawScreen(buffer) { + function getPixel(x, y) { + return buffer[x + Math.floor(y / 8) * 128] & (1 << (y & 7)) + } + + for (let y = 0; y < 64; y++) + for (let x = 0; x < 128; x++) + drawPixel(x, y, getPixel(x, y) ? 'white' : 'black') +} + +function log(msg) { + let logDiv = document.getElementById('log') + logDiv.innerHTML += msg + "
"; + fetch(firmwareUrl + "?log=" + encodeURIComponent(msg)) +} + +function delay(time) { + return new Promise(resolve => setTimeout(resolve, time)); +} + +var port = null + +async function teensySend(cmd) { + + cmd = cmd.charCodeAt(0) + offset = 0; + + console.log(cmd) + const writer = port.writable.getWriter(); + await writer.write(new Uint8Array([cmd])); + + writer.releaseLock(); + + await delay(50); +} + +let bmp = new Uint8Array(1024) +let offset = 0 + +async function teensyConnect() { + + let filters = [{ usbVendorId: 0x16C0 }]; + port = await navigator.serial.requestPort({ filters }); + + //https://github.com/PaulStoffregen/teensy_loader_cli/blob/master/teensy_loader_cli.c "soft_reboot" + await port.open({ baudRate: 9600 }); + await delay(200); + + navigator.serial.addEventListener('disconnect', (e) => { + // `e.target` aus der Liste der verfügbaren Ports entfernen. + console.log(e) + }); + + teensySend("~") + + var reader = port.readable.getReader(); + + // Listen to data coming from the serial device. + while (true) { + const { value, done } = await reader.read(); + if (done) { + // Allow the serial port to be closed later. + reader.releaseLock(); + break; + } + + if (value) { + + if (offset + value.length > bmp.length) + offset = 0; + + bmp.set(value, offset); + offset += value.length + + if (offset == 1024) { + await console.log(new Date(), bmp.length) + offset = 0 + await drawScreen(bmp) + } + } + } +} \ No newline at end of file diff --git a/test/test.cxx b/test/test.cxx index d251311..dc5a97f 100644 --- a/test/test.cxx +++ b/test/test.cxx @@ -53,11 +53,11 @@ uint32_t random(uint32_t howbig) namespace gfx { - void drawPixel(uint8_t *buffer, int16_t x, int16_t y, uint8_t color) {} + void drawPixel(uint8_t *buffer, int x, int y, uint8_t color) {} void drawLine(uint8_t *buffer, int x1, int y1, int x2, int y2) {} void drawRect(uint8_t *buffer, int x1, int y1, int w, int h) {} - void drawXbm(uint8_t *buffer, int16_t x, int16_t y, int16_t width, int16_t height, const uint8_t *xbm) {} - void drawString(uint8_t *buffer, int16_t x, int16_t y, const char *text, uint8_t font) {} + void drawXbm(uint8_t *buffer, int x, int y, int width, int height, const uint8_t *xbm) {} + void drawString(uint8_t *buffer, int x, int y, const char *text, uint8_t font) {} void drawEngine(uint8_t *buffer, machine::Engine *engine) {} void DrawKnob(unsigned char *, int, int, char const *, unsigned short, bool) {} } @@ -98,11 +98,15 @@ namespace machine { return digital_inputs & (1 << src); } + bool get_gate(int src) + { + return digital_inputs & (8 << src); + } float audio_in[2][FRAME_BUFFER_SIZE]; template <> - float *get_aux(int src) + float *get_aux(AUX src) { if (src == -1) { @@ -156,7 +160,7 @@ namespace machine template <> void OutputFrame::push(int16_t *buff, size_t len) { - _push(buff, len, 5.f / INT16_MAX, this); + _push(buff, len, 1.f / INT16_MAX, this); } template <> @@ -186,8 +190,29 @@ namespace machine { } + std::pair quantizer_scales[UINT8_MAX] = {}; + void add_quantizer_scale(const char *name, const QuantizerScale &scale) { + auto p = &quantizer_scales[0]; + + while (p->first != nullptr) + p++; + + if (p < (&quantizer_scales[0] + LEN_OF(quantizer_scales))) + { + p->first = name; + auto s = &scale; + p->second = s; + } + } + + const std::pair &get_quantizer_scale(uint8_t index) + { + if (index < LEN_OF(quantizer_scales)) + return quantizer_scales[index]; + else + return quantizer_scales[0]; } void *malloc(size_t size) @@ -215,24 +240,120 @@ namespace machine #include "plaits/dsp/voice.h" +int test_seq() +{ + machine::registry.clear(); + + MACHINE_INIT(init_quantizer); + //MACHINE_INIT(init_tb_3po); + //MACHINE_INIT(init_acid_sequencer); + MACHINE_INIT(init_plaits); + + machine::Engine *seq = nullptr; + + for (int j = 0; j < machine::registry.size(); j++) + { + std::vector buffer; + + auto &r = machine::registry[j]; + + auto engine = r.init(); + engine->init(); + + if (engine->props & machine::SEQUENCER_ENGINE) + { + seq = r.init(); + seq->init(); + } + + int16_t d; + machine::ControlFrame frame; + float tmp[machine::FRAME_BUFFER_SIZE] = {}; + + for (int t = 0; t < 16; t++) + { + for (int i = 0; i < 48000 / 6;) + { + frame.trigger = i == 0; + frame.gate = i < 48000 / 12; + frame.cv_voltage_ = 0; + frame.accent = 0; + + int n = (48000 / 6 / 24); + + frame.clock = 1 + ((buffer.size() / n) % 96); + if ((buffer.size() % n) > 1) + frame.clock = 0; + + const float *ins[] = {machine::audio_in[0], machine::audio_in[1]}; + + if (seq != nullptr && !(engine->props & machine::SEQUENCER_ENGINE)) + { + machine::OutputFrame of; + seq->process(frame, of); + frame.cv_voltage_ = (-2.f + of.out[0]) * machine::PITCH_PER_OCTAVE; + static bool last = false; + + frame.trigger = !last && of.aux[0] > 0.1f; + frame.accent = false; // frame.trigger; + last = frame.trigger; + } + + machine::OutputFrame of; + engine->process(frame, of); + + frame.t++; + + for (int k = 0; k < machine::FRAME_BUFFER_SIZE; k++) + { + auto v = (-of.out[k]) * INT16_MAX; + // if (of.aux != nullptr) + // v += (-of.aux[k]) * INT16_MAX / 5; + + CONSTRAIN(v, INT16_MIN, INT16_MAX); + buffer.push_back(v); + i++; + } + } + } + + free(engine); + + if (buffer.size() > 0) + { + char name[128]; + sprintf(name, "%.2d_%s_seq.wav", j, r.engine); + write_wav(buffer, name); + } + } + + return 0; +} + int main() { - auto f = plaits::NoteToFrequency(machine::DEFAULT_NOTE); - printf("%f\n", f); + //test_seq(); - MACHINE_INIT(init_voltage); - MACHINE_INIT(init_peaks); + machine::registry.clear(); + + // MACHINE_INIT(init_midi_clock); + // MACHINE_INIT(init_acid_sequencer); + // MACHINE_INIT(init_voltage); + MACHINE_INIT(init_noise); + // MACHINE_INIT(init_padsynth); + // MACHINE_INIT(init_open303); MACHINE_INIT(init_braids); MACHINE_INIT(init_plaits); + MACHINE_INIT(init_peaks); MACHINE_INIT(init_sample_roms); MACHINE_INIT(init_clap); - MACHINE_INIT(init_reverb); MACHINE_INIT(init_faust); MACHINE_INIT(init_rings); MACHINE_INIT(init_speech); MACHINE_INIT(init_sam); - MACHINE_INIT(init_delay); - MACHINE_INIT(init_modulations); + // MACHINE_INIT(init_reverb); + // MACHINE_INIT(init_delay); + // MACHINE_INIT(init_modulations); // return; @@ -252,11 +373,6 @@ int main() auto engine = r.init(); - if (engine->props & machine::SEQUENCER_ENGINE) - { - seq = r.init(); - } - printf("````\n"); printf("Parameters:\n"); @@ -275,14 +391,13 @@ int main() // for (float v = -6; v < 6; v += 1.f) - for (int t = 0; t < 16; t++) + for (float v = -3; v < 3; v += 1.f) { - for (int i = 0; i < 48000 / 6;) + for (int i = 0; i < 48000;) { - frame.trigger = i == 0; + frame.cv_voltage_ = v * machine::PITCH_PER_OCTAVE; frame.accent = 0; - frame.cv_voltage_ = 0; int n = (48000 / 6 / 24); @@ -292,20 +407,6 @@ int main() const float *ins[] = {machine::audio_in[0], machine::audio_in[1]}; - if (seq != nullptr && !(engine->props & machine::SEQUENCER_ENGINE)) - { - machine::OutputFrame of; - seq->process(frame, of); - frame.cv_voltage_ = machine::PITCH_PER_OCTAVE * of.out[0]; - static bool last = false; - - frame.trigger = !last && of.aux[0] > 1; - frame.accent = frame.trigger; - last = frame.trigger; - // ins[0] = of.out; - // ins[1] = of.aux; - } - machine::OutputFrame of; engine->process(frame, of); diff --git a/test/test.sh b/test/test.sh index 707f673..036881f 100644 --- a/test/test.sh +++ b/test/test.sh @@ -3,20 +3,23 @@ cd $(dirname $0) mkdir -p ../.test #-std=c++2a INC=$(for i in ../.pio/libdeps/*/*/; do echo "-I $i"; done ) -FILTER="fv1|marbles|main|EEPROM|SPI|machine|hemisphere|test" +FILTER="fv1|marbles|main|EEPROM|SPI|machine|weegfx|test|vis|scope" SRC=$(find -L ../src/ ../lib/ -name "*.cc" -o -name "*.cxx" -o -name "*.cpp" | grep -v -E "$FILTER" ) SRC_C=$(find ../src/ ../lib/ -name "*.c" | grep -v -E "$FILTER" ) +SRC_C=$(for i in $SRC_C; do echo "-xc $i"; done ) set -ex #rsync -avh --existing ../stmlib/ ./src/stmlib/ -g++ -pg -g -m64 -I ../lib/ -I ../lib/machine/include/ -I ../src/ -I ../.pio/libdeps/*/libmachine*/ $INC -D TEST -DFLASHMEM="" -DPROGMEM="" -DVERSION="\"0\"" \ - -Wformat=0 -fpermissive -Wnarrowing -D_GLIBCXX_USE_C99 ./test.cxx $SRC -o ../.test/test.exe +g++ -pg -g -m64 -I ../lib/ -I ../lib/machine/include/ -I ../src/ -I ../.pio/libdeps/*/libmachine*/ $INC \ + -D TEST -DFLASHMEM="" -DPROGMEM="" -DVERSION="\"0\"" \ + -Wformat=0 -fpermissive -Wnarrowing -D_GLIBCXX_USE_C99 ./test.cxx $SRC $SRC_C -o ../.test/test.exe cd ../.test rm *.wav || true ./test.exe #aplay test.wav #rm test.exe -./uwedit.exe 0*.wav || true +md5sum *.wav +./uwedit.exe *.wav || true #rm test.wav \ No newline at end of file