Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f26df3dbe | ||
|
|
c4f48f6e6d | ||
|
|
374fc151f0 | ||
|
|
d7cf5b7399 | ||
|
|
8194bda9c8 | ||
|
|
2a0ea726f3 | ||
|
|
161f57bc4e | ||
|
|
f54689f071 | ||
|
|
d713ca50bb | ||
|
|
ab29ad2820 | ||
|
|
39353751e6 | ||
|
|
97b1c34cee | ||
|
|
cee75c523b | ||
|
|
54a67562db | ||
|
|
608ae003df | ||
|
|
e151036963 | ||
|
|
a65b71ab86 | ||
|
|
468b4c36de | ||
|
|
e9a5784ed1 | ||
|
|
f72a89ed61 | ||
|
|
17b12fe3f1 | ||
|
|
255858cee8 | ||
|
|
15bcf87529 | ||
|
|
31a0c4c009 | ||
|
|
6e67193c3d | ||
|
|
cbdb18e6ba | ||
|
|
bc2020e612 | ||
|
|
8ce2db511b | ||
|
|
6be1b4a911 | ||
|
|
a8e6593eed | ||
|
|
c392104aec | ||
|
|
0de8c05f00 | ||
|
|
597c70bb53 | ||
|
|
c7c9366dbb | ||
|
|
2ab8c68c9b | ||
|
|
21411d2c56 | ||
|
|
4f7803a8b4 | ||
|
|
64c6745c76 | ||
|
|
fd522e1b99 | ||
|
|
7e11e3e72b | ||
|
|
ed737f526f | ||
|
|
83afc11612 | ||
|
|
8a81dd649e | ||
|
|
42e877c08d | ||
|
|
1e0760e49b | ||
|
|
db4fdf68ff | ||
|
|
314b57b1ed | ||
|
|
a3ad0e989e | ||
|
|
159fa60b08 | ||
|
|
e3fb2074f6 | ||
|
|
7fcbb9b509 | ||
|
|
3a19db6a86 | ||
|
|
d73f172fc1 | ||
|
|
f05e3be55f | ||
|
|
2ec3cc9543 | ||
|
|
0f70ea025c | ||
|
|
2ac71f33f1 | ||
|
|
73332a7292 | ||
|
|
4bf46df821 | ||
|
|
072280c603 | ||
|
|
4a4d67f5f1 | ||
|
|
12783fdfdb | ||
|
|
63e1daa706 | ||
|
|
bc8a2b5b20 | ||
|
|
26c3ee4cb6 | ||
|
|
c0e651bf68 | ||
|
|
d801ce21e8 | ||
|
|
025818a042 | ||
|
|
3bda521d62 | ||
|
|
ff6ba8a179 | ||
|
|
2afe0cfccc | ||
|
|
e666247a45 | ||
|
|
dab1ed956f | ||
|
|
0dd21799f9 | ||
|
|
bcfe5e4edd | ||
|
|
4d25a24e53 | ||
|
|
3e63bdd4ce | ||
|
|
465bc88e8f | ||
|
|
6e8b990391 | ||
|
|
6fc6b9f567 | ||
|
|
7ef6975cb3 |
+12
@@ -0,0 +1,12 @@
|
||||
build
|
||||
.cache
|
||||
c_cpp_properties.json
|
||||
compile_commands.json
|
||||
venv
|
||||
.codechecker
|
||||
.vscode
|
||||
matlab/osc/blip/saw.wav
|
||||
matlab/osc/blip/sqr.wav
|
||||
matlab/osc/blip/tri.wav
|
||||
octave-workspace
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
[submodule "submodule/make"]
|
||||
path = submodule/make
|
||||
url = http://192.168.22.90:3001/jayfield/make.git
|
||||
[submodule "submodule/fir"]
|
||||
path = submodule/fir
|
||||
url = http://192.168.22.90:3001/jayfield/fir.git
|
||||
@@ -0,0 +1,56 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## What this is
|
||||
|
||||
JaySynth is a subtractive/wavetable software synthesizer built as a **VST2 audio plugin** using JUCE (bundled at `sdk/juce/JUCE-3.1.1`) and the Steinberg VST2.4 SDK (`sdk/vst/vstsdk2.4`). It targets Linux (`.so`) and Windows (`.dll`) hosts. Development/testing happens against the Carla plugin host (`~/jaysynth+tal.carxp`) and Reaper.
|
||||
|
||||
## Build
|
||||
|
||||
Building is plain GNU Make, driven by two submodule-provided include files (`submodule/make/defaults.mk`, `compile.mk`, `link.mk`, pulled in via `MAKE_HOME`). There is no CMake/autotools.
|
||||
|
||||
```sh
|
||||
make # build release .so into build/linux/release/
|
||||
make CONFIG=debug # debug build
|
||||
make install # copies waves.bin + JaySynth.so into ~/.vst/jaysynth/
|
||||
make run # install, then launch Carla with the test project (~/jaysynth+tal.carxp)
|
||||
make bear # distclean + rebuild via `bear` to regenerate compile_commands.json (used by clangd)
|
||||
```
|
||||
|
||||
Windows build uses `Makefile.win` with the same `PACKAGES` structure but different tool names (`Source`, `Synth`, `Fir` targets instead of lowercase).
|
||||
|
||||
Submodules must be checked out (`git submodule update --init`) before building:
|
||||
- `submodule/make` — shared Makefile fragments (compiler flags, link rules)
|
||||
- `submodule/fir` — FIR/resampling C library used by the synth core
|
||||
|
||||
`sdk/juce` and `sdk/vst` are vendored SDKs, not submodules — they're expected to already exist at those paths.
|
||||
|
||||
There are no automated tests in this repo; verification is manual, by building, installing, and playing the plugin in a host (Carla/Reaper — see `.vscode/launch.json` for debug configs).
|
||||
|
||||
## Architecture
|
||||
|
||||
The codebase has a hard split between a **portable C DSP core** and a **C++/JUCE plugin shell**, built as separate sub-Makefiles and linked together:
|
||||
|
||||
- `src/synth/` — pure C DSP engine (no JUCE, no C++). Oscillators (`vco.c`, BLIT-based band-limited synthesis in `blit.c`), wavetables (`wavetable.c`, `mw_wave_data.c` — Waldorf Microwave/PPG-style wavetables), filter (`vcf.c`), envelopes (`env.c`), LFOs (`lfo.c`), noise (`noise.c`), and parameter scaling (`param_scale.c`). `synth_float_t` is a build-time typedef (`double` on Linux, `float` on Windows — set via `-Dsynth_float_t=...` in the Makefiles).
|
||||
- `voice.h`/`voice.c` is the center of this layer: a `voice_t` (per-voice state: 2 VCOs, 4 LFOs, 4 envelopes, 1 VCF) driven by a shared `voice_common_t` (global LFO/env/VCF/noise tables and shared per-block buffers, to avoid recomputing identical modulation sources per voice). All per-voice parameters are addressed by the flat `VOICE_PARAM_*` enum in `voice.h`; modulation routing (FM/AM/PWM sources for oscillators, filter cutoff/Q sources) is expressed as source/amount/op triples pointing into `VOICE_MOD_SRC_*`.
|
||||
- Voice audio rendering can run multi-threaded: `JaySynthThread` (in `src/plug/JaySynth.h`) splits the voice array across CPU cores, each thread calling `VoiceProcessDataV`.
|
||||
- `src/plug/` — the JUCE `AudioProcessor`/`Synthesiser` plugin implementation, C++11.
|
||||
- `PluginProcessor.{h,cpp}` (`JaySynthAudioProcessor`) is the VST entry point: parameter get/set, program (patch) management, and all patch/bank XML and VST `.fxp`/`.fxb` import/export.
|
||||
- `JaySynth.{h,cpp}` (`class JaySynth : public Synthesiser`) owns the array of `voice_t` (via `JaySynthVoice`), MIDI note/CC/NRPN handling, humanize/unison/monophonic modes, and MIDI clock sync (`JK_MidiClock`).
|
||||
- `JaySynthSound` is a patch: a flat `parameter[SYNTH_NUM_PARAMS]` array plus a `JaySynthMidiCC` (per-parameter MIDI CC/NRPN assignment table). `JaySynthAudioProcessor` holds `NUM_PROGRAMS` (64) of these plus a separate "current patch" edit buffer, and tracks a modified/"[edited]" state independent of the stored program.
|
||||
- `JaySynthMidiCC`/`MidiNrpn`/`MidiCC_PopUp` implement per-parameter MIDI-learn: any synth parameter can be bound to a CC or NRPN controller, with absolute/relative modes.
|
||||
- `JaySynthAudioProcessorEditor` + `graph_window`/`ButtonLED`/`VelKey_PopUp` are the custom GUI (no Projucer-managed component files beyond the `.jucer`'s single editor group — most UI is hand-written).
|
||||
- Notifications from the audio/synth layer to the GUI go through `JaySynthActionListener` (a `JUCE::ActionBroadcaster` wrapping typed events like `JAYSYNTH_CHANGED_PARAM`, `JAYSYNTH_CHANGED_MIDICC`, `JAYSYNTH_CHANGED_PROGRAM` as encoded strings), not direct callbacks — needed because it crosses the audio-thread/message-thread boundary.
|
||||
- `extras/waves/` — binary wavetable sets (Microwave/PPG format, 8/12-bit) that must be installed as `waves.bin` next to the plugin for the wavetable oscillator feature to activate (see `extras/waves.readme`).
|
||||
- `extras/sounds/` — example patches/banks in both VST2 native format (`.fxp`/`.fxb`) and a custom XML format (`.xmp`/`.xmb`, patch/bank respectively) — these are the two import/export paths implemented in `PluginProcessor.cpp` (`patchImportXml`/`bankImportXml` vs. `setCurrentProgramStateInformation`/`setStateInformation`).
|
||||
- `matlab/` — MATLAB/Octave prototyping scripts for the DSP algorithms (oscillator BLEP/BLIT, filter, envelope, LFO, limiter, param curves) — reference models for the C implementation, not part of the build.
|
||||
- `doc/papers/` — background papers on the DSP techniques used (band-limited synthesis, wavetables).
|
||||
- `history.txt` — the changelog (German/English mixed); check here before assuming a described bug is still open.
|
||||
- `todo.txt` — open design/feature notes, also mixed German/English.
|
||||
|
||||
## Conventions to be aware of
|
||||
|
||||
- Per-voice DSP parameters are plain enums (`VOICE_PARAM_*`), not named struct fields — when adding a synth parameter, it must be threaded through the enum in `voice.h`, `param_scale.c`'s scaling table, and the plugin-side parameter name/XML (de)serialization in `PluginProcessor.cpp`.
|
||||
- Modulation source enums are duplicated per destination (`VOICE_MOD_SRC_0_*` vs `VOICE_MOD_SRC_1_*` vs the general `VOICE_MOD_SRC_*`) because not all destinations can be modulated by all sources (e.g. VCO0 can't modulate itself) — keep this distinction when touching mod routing.
|
||||
- Comments and log/history text are a German/English mix; don't assume English-only when grepping for TODOs or history entries.
|
||||
@@ -236,13 +236,13 @@
|
||||
#define JucePlugin_EditorRequiresKeyboardFocus 0
|
||||
#endif
|
||||
#ifndef JucePlugin_Version
|
||||
#define JucePlugin_Version 0.5.8
|
||||
#define JucePlugin_Version 1.1.1
|
||||
#endif
|
||||
#ifndef JucePlugin_VersionCode
|
||||
#define JucePlugin_VersionCode 0x00050008
|
||||
#define JucePlugin_VersionCode 0x010101
|
||||
#endif
|
||||
#ifndef JucePlugin_VersionString
|
||||
#define JucePlugin_VersionString "0.5.8"
|
||||
#define JucePlugin_VersionString "1.1.1"
|
||||
#endif
|
||||
#ifndef JucePlugin_VSTUniqueID
|
||||
#define JucePlugin_VSTUniqueID JucePlugin_PluginCode
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
include $(MAKE_HOME)/defaults.mk
|
||||
include make/defaults.mk
|
||||
|
||||
CONFIG ?= release
|
||||
TARGET ?= JaySynth.so
|
||||
|
||||
LIBSRC_PATH ?= $(realpath ../../libsrc)
|
||||
APP_VERSION := 1.1.1
|
||||
APP_VERSION_HEX := 0x010101
|
||||
LIBSRC_PATH ?= $(realpath ./submodule)
|
||||
JUCE_PATH := $(realpath ./sdk/juce/JUCE-3.1.1)
|
||||
VST2_SDK_PATH ?= $(realpath ./sdk/vst/vstsdk2.4)
|
||||
JUCE_LIBCODE_PATH ?= $(realpath ./JuceLibraryCode)
|
||||
SRC_PATH := $(realpath ./src)
|
||||
|
||||
NAME =: JaySynth
|
||||
|
||||
DEFINES := -Dradio_float_t=float -Dfir_float_t=float -Dsynth_float_t=float
|
||||
DEFINES := -Dradio_float_t=double -Dfir_float_t=double -Dsynth_float_t=double
|
||||
DEFINES += -DHAVE_LROUND
|
||||
CFLAGS += -fPIC
|
||||
CXXFLAGS += -fPIC
|
||||
@@ -19,20 +23,21 @@ LIBS := -lstdc++ -lm -lGL -lX11 -lXext -lXinerama -lasound -ldl -lfreetype -lpth
|
||||
LDFLAGS += $(TARGET_ARCH) $(LIBDIR) -L/usr/X11R6/lib/ -shared
|
||||
|
||||
DEFINES += -D__cdecl=""
|
||||
DEFINES += -DJUCE_GCC=1 -DLINUX=1 -DJUCE_APP_VERSION=1.0.0 -DJUCE_APP_VERSION_HEX=0x10000
|
||||
DEFINES_debug += -DDEBUG=1 -D_DEBUG=1
|
||||
DEFINES += -DJUCE_GCC=1 -DLINUX=1 -DJUCE_APP_VERSION=$(APP_VERSION) -DJUCE_APP_VERSION_HEX=$(APP_VERSION_HEX)
|
||||
DEFINES_debug += -DDEBUG=1 -D_DEBUG=1 -DSYNTH_DEBUG
|
||||
DEFINES_release += -DNDEBUG
|
||||
|
||||
DEFINES += -DJucePlugin_Version=1.0.0
|
||||
DEFINES += -DJucePlugin_VersionCode=0x01000000
|
||||
DEFINES += -DJucePlugin_VersionString=\"1.0.0\"
|
||||
DEFINES += -DJucePlugin_Version=$(APP_VERSION)
|
||||
DEFINES += -DJucePlugin_VersionCode=$(APP_VERSION_HEX)
|
||||
DEFINES += -DJucePlugin_VersionString=\"$(APP_VERSION)\"
|
||||
|
||||
INCLUDES += -I $(JUCE_PATH) -I $(JUCE_PATH)/modules -I $(VST2_SDK_PATH) -I $(LIBSRC_PATH)
|
||||
INCLUDES += -I $(JUCE_PATH) -I $(JUCE_PATH)/modules -I $(VST2_SDK_PATH) -I $(LIBSRC_PATH) -I $(JUCE_LIBCODE_PATH) -I $(SRC_PATH)
|
||||
|
||||
PACKAGES := JuceLibraryCode Source Synth Fir
|
||||
PACKAGES := JuceLibraryCode plug synth fir
|
||||
|
||||
export
|
||||
|
||||
|
||||
all: app
|
||||
app: objects link
|
||||
|
||||
@@ -40,16 +45,27 @@ app: objects link
|
||||
|
||||
objects: ${PACKAGES}
|
||||
|
||||
Synth:
|
||||
@$(MAKE) -C Source/synth
|
||||
synth:
|
||||
@$(MAKE) -C $(SRC_PATH)/synth
|
||||
|
||||
Source:
|
||||
@$(MAKE) -C $@
|
||||
plug:
|
||||
@$(MAKE) -C $(SRC_PATH)/plug
|
||||
|
||||
JuceLibraryCode:
|
||||
@$(MAKE) -C $@
|
||||
|
||||
Fir:
|
||||
fir:
|
||||
@$(MAKE) -C $(LIBSRC_PATH)/fir
|
||||
|
||||
bear:
|
||||
make distclean
|
||||
bear --output compile_commands.json -- $(MAKE)
|
||||
|
||||
run: install
|
||||
carla ~/jaysynth+tal.carxp
|
||||
|
||||
install: app
|
||||
cp extras/waves/waves_microwave_v2.0_8bit ~/.vst/jaysynth/waves.bin
|
||||
cp build/linux/$(CONFIG)/JaySynth.so ~/.vst/jaysynth/JaySynth.so
|
||||
|
||||
include $(MAKE_HOME)/link.mk
|
||||
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
|
||||
//#######################################################################################
|
||||
//Class to insert Midiclock messages into a given MidiBuffer, suitable for block
|
||||
//based audio callbacks. The class can act on position jumps e.g. "Loops" by sending a
|
||||
//positioning message.
|
||||
//NOTE: No ballistik came in to play so I wouldn't recommend using it to drive a mechanical
|
||||
//tape deck!!!
|
||||
//
|
||||
//solar3d-software, April- 10- 2012
|
||||
//#######################################################################################
|
||||
|
||||
*/
|
||||
#include "JK_MidiClock.h"
|
||||
|
||||
JK_MidiClock::JK_MidiClock()
|
||||
:wasPlaying(false),
|
||||
syncPpqPosition(-999.0),
|
||||
posChangeThreshold(0.001),
|
||||
ppqToStartSyncAt(0.0),
|
||||
followSongPosition(true),
|
||||
syncFlag(0),
|
||||
ppqOffset(0)
|
||||
{
|
||||
//Prepare Midi messages to avoid blocking the caller of generateMidiclock()!
|
||||
continueMessage = new MidiMessage(MidiMessage::midiContinue());
|
||||
stopMessage = new MidiMessage(MidiMessage::midiStop());
|
||||
clockMessage = new MidiMessage(MidiMessage::midiClock());
|
||||
songPositionMessage = new MidiMessage(MidiMessage::songPositionPointer(0));
|
||||
}
|
||||
//=================================================================================================
|
||||
void JK_MidiClock::generateMidiclock(AudioPlayHead::CurrentPositionInfo &lastPosInfo,
|
||||
MidiBuffer* midiBuffer, const int bufferSize, const double sampleRate)
|
||||
{
|
||||
//###################################Some explanation about musical tempo #################
|
||||
//A Time Signature, is two numbers, one on top of the other. The numerator describes the #
|
||||
//number of Beats in a Bar, while the denominator describes of what note value a Beat is. #
|
||||
//So 4/4 would be four quarter-notes per Bar, while 4/2 would be four half-notes per Bar, #
|
||||
//4/8 would be four eighth-notes per Bar, and 2/4 would be two quarter-notes per Bar. #
|
||||
//#########################################################################################
|
||||
|
||||
|
||||
if (midiBuffer != nullptr)
|
||||
{
|
||||
//PPQ value of one sample
|
||||
const double ppqPerSample = (lastPosInfo.bpm / 60.0) / sampleRate;
|
||||
|
||||
//PPQ offset to compensate Midi interface latency
|
||||
double hostPpqPosition = lastPosInfo.ppqPosition + ppqOffset * ppqPerSample;;
|
||||
|
||||
if (lastPosInfo.isPlaying || lastPosInfo.isRecording)
|
||||
{
|
||||
if (! wasPlaying)
|
||||
{
|
||||
//set the point where to start the slave
|
||||
ppqToStartSyncAt = getNearestSixteenthInPPQ(hostPpqPosition);
|
||||
|
||||
//Special case: Master is set to always start playback from the previous start position...
|
||||
if (positionJumped(syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
||||
{
|
||||
//Cue Midiclock slave to the nearest sixteenth note to new start position
|
||||
//because the one calculated in stop mode isn't valid anymore.
|
||||
sendSongPositionPointerMessage(ppqToStartSyncAt, 0, midiBuffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Position jump (loop or manually position change while playing)
|
||||
if (positionJumped(syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
||||
{
|
||||
//set the point where to start the slave
|
||||
ppqToStartSyncAt = getNearestSixteenthInPPQ(hostPpqPosition);
|
||||
|
||||
//User has changed position manually while playing
|
||||
if (syncFlag == 0)
|
||||
{
|
||||
midiBuffer->addEvent(*stopMessage, 0);
|
||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
||||
|
||||
syncFlag = startSlave_;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (followSongPosition)
|
||||
{
|
||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
||||
|
||||
syncFlag = startSlave_;
|
||||
}
|
||||
else
|
||||
syncFlag = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int posInBuffer = 0; posInBuffer < bufferSize; ++posInBuffer)
|
||||
{
|
||||
syncPpqPosition = hostPpqPosition + (posInBuffer * ppqPerSample);
|
||||
|
||||
const int clockDistanceInSamples = roundToInt((60.0 * sampleRate) / (lastPosInfo.bpm * 24.0));
|
||||
const int64 hostSamplePos = roundToInt64((hostPpqPosition * (60.0 / lastPosInfo.bpm)) * sampleRate);
|
||||
const int64 syncSamplePos = hostSamplePos + posInBuffer;
|
||||
|
||||
//Some hosts like Cubase come up with a wacky ppqPosition
|
||||
//that could break the timing! Best is to "wait"
|
||||
//here for the right ppqPosition to jump on.
|
||||
if (syncPpqPosition >= ppqToStartSyncAt)
|
||||
{
|
||||
if ((syncFlag & startSlave_) == startSlave_)
|
||||
{
|
||||
midiBuffer->addEvent(*continueMessage, posInBuffer);
|
||||
|
||||
syncFlag &= cycleEnd_;
|
||||
}
|
||||
|
||||
//Cycle mode on
|
||||
if (lastPosInfo.isLooping && lastPosInfo.ppqLoopStart != lastPosInfo.ppqLoopEnd)
|
||||
{
|
||||
const double ppqToCycleEnd = fabs(lastPosInfo.ppqLoopEnd - syncPpqPosition);
|
||||
const int64 samplesToCycleEnd = roundToInt64(ppqToCycleEnd * (60.0 / lastPosInfo.bpm) * sampleRate);
|
||||
|
||||
if ((syncFlag & cycleEnd_) == 0)
|
||||
{
|
||||
if (samplesToCycleEnd <= clockDistanceInSamples) //For fine tuning tweak here
|
||||
{
|
||||
//We have reached the cycle- end position
|
||||
//and must stop the Midiclock slave here
|
||||
if (followSongPosition)
|
||||
midiBuffer->addEvent(*stopMessage, posInBuffer);
|
||||
|
||||
syncFlag |= cycleEnd_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//For best timing we should never interupt Midiclock messages!
|
||||
//Seems that some slaves constantly adjusting their internal clock
|
||||
//to Midiclock even if they are in stop mode.
|
||||
if (syncSamplePos % clockDistanceInSamples == 0)
|
||||
midiBuffer->addEvent(*clockMessage, posInBuffer);
|
||||
}
|
||||
|
||||
wasPlaying = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Send positioning message if the user has stopped or if he changed the playhead position
|
||||
//manually in stop mode! This will also initially cue slave after loading plugin instance.
|
||||
if (wasPlaying || positionJumped(syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
||||
{
|
||||
midiBuffer->addEvent(*stopMessage, 0);
|
||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
||||
}
|
||||
|
||||
syncPpqPosition = hostPpqPosition;
|
||||
|
||||
syncFlag = startSlave_;
|
||||
|
||||
wasPlaying = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
//=================================================================================================
|
||||
bool JK_MidiClock::positionJumped(const double lastPosInPPQ, const double currentPosInPPQ,
|
||||
const double sampleRate, const double ppqPerSample)
|
||||
{
|
||||
//This returns true if the user has changed the playhead position manually or if
|
||||
//a jump has occured! The comperator's default threshold is lastPosInPPQ +- 10ms.
|
||||
if (currentPosInPPQ < lastPosInPPQ - ((posChangeThreshold * sampleRate) * ppqPerSample) ||
|
||||
currentPosInPPQ > lastPosInPPQ + ((posChangeThreshold * sampleRate) * ppqPerSample))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//=================================================================================================
|
||||
void JK_MidiClock::sendSongPositionPointerMessage(const double ppqPosition, const int posInBuffer, MidiBuffer* buffer)
|
||||
{
|
||||
//This will cue the slave to the NEAREST
|
||||
//16th note to the given ppqPosition.
|
||||
int intBeat = int(ceil(ppqPosition * 4));
|
||||
|
||||
uint8* pSongPositionTime((uint8*)(songPositionMessage->getRawData()));
|
||||
|
||||
*(pSongPositionTime + 1) = (uint8)(intBeat & 0x7f);
|
||||
*(pSongPositionTime + 2) = (uint8)((intBeat & 0x3f80)>>7);
|
||||
|
||||
buffer->addEvent(*songPositionMessage, posInBuffer);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
include $(MAKE_HOME)/defaults.mk
|
||||
|
||||
LIBSRC_PATH ?= $(realpath ../../../libsrc)
|
||||
JUCE_PATH ?= $(realpath ../sdk/juce/JUCE-3.1.1)
|
||||
VST2_SDK_PATH ?= $(realpath ../sdk/vst/vstsdk2.4)
|
||||
|
||||
include config.mk
|
||||
|
||||
INCLUDES += -I $(LIBSRC_PATH) -I $(JUCE_PATH) -I $(VST2_SDK_PATH)
|
||||
|
||||
include $(MAKE_HOME)/compile.mk
|
||||
@@ -1,10 +0,0 @@
|
||||
include $(MAKE_HOME)/defaults.mk
|
||||
|
||||
LIBSRC_PATH ?= $(realpath ../../../../libsrc)
|
||||
VST2_SDK_PATH ?= $(realpath ../../sdk/vst/vstsdk2.4)
|
||||
|
||||
include config.mk
|
||||
|
||||
INCLUDES ?= -I $(LIBSRC_PATH) -I $(VST2_SDK_PATH)
|
||||
|
||||
include $(MAKE_HOME)/compile.mk
|
||||
@@ -0,0 +1,37 @@
|
||||
# Hardening todo
|
||||
|
||||
Findings from a code-review pass over `src/plug` and `src/synth` (2026-07-27, branch `hardening`).
|
||||
|
||||
## Critical — crashes, memory corruption (fixed, commit `a65b71a`)
|
||||
|
||||
- [x] **NRPN controller ID caused an out-of-bounds write on the audio thread.** `JaySynth::handleController` (`src/plug/JaySynth.cpp:899`) now bounds-checks `midiCC_info.ID` against `NUM_MIDI_CONTROLLERS` before indexing `last_midiCC_info[]` (NRPN IDs can reach 16383, array is sized 1024).
|
||||
- [x] **Negative controller IDs from patch files could corrupt memory.** `JaySynthMidiCC::add`/`remove` (`src/plug/JaySynthMidiCC.cpp:433-460`) now also reject `< 0`, not just out-of-range-high.
|
||||
- [x] **Null-pointer dereference on malformed/unparsable patch or bank files.** `bankImportXml` and the `.xmp` branch of `loadPatchFromFile` (`src/plug/PluginProcessor.cpp:860-885, 949-975`) now null-check XML elements after `getFirstChildElement()`/`XmlDocument::parse()` before dereferencing.
|
||||
- [x] **No size validation before casting raw file bytes to `fxBank`/`fxProgram` structs.** `loadBankFromFile`/`loadPatchFromFile` (`src/plug/PluginProcessor.cpp:834-858, 930-957`) now check the loaded file is at least header-sized and that the embedded chunk size fits within it before reading through the raw pointer.
|
||||
- [x] **Host block sizes larger than `SYNTH_MAX_BUFSIZE` (8192) caused a stack buffer overflow or a permanent deadlock.** `processBlock` (`src/plug/PluginProcessor.cpp:547`) now chunks rendering into `SYNTH_MAX_BUFSIZE`-sized passes instead of writing the host's full block size into a fixed 8192-sample stack buffer; `JaySynth::renderNextBlock` (`src/plug/JaySynth.cpp:977`) clamps the same way and correctly holds a pending MIDI event over between passes.
|
||||
- [x] **Unchecked file stream could be null, crashing on plugin construction.** `JaySynth::JaySynth` (`src/plug/JaySynth.cpp:55-57`) now guards the wavetable file read against `createInputStream()` returning null.
|
||||
- [x] **Parameter-changing calls raced with real-time audio rendering — no lock protected them.** `JaySynth::setParameter`/`setControl`/`setPerVoiceControl`/`setParam`/`ClearControls` (`src/plug/JaySynth.cpp`) now take `ScopedLock sl(lock)`, matching `renderNextBlock` and the note/controller handlers.
|
||||
- [x] **Found while building `tools/testhost`'s regression suite for this list: the block-size fix above wasn't sufficient on its own.** `VCF_CalcCoeff_LPF`/`_HPF`/`_BPF` (`src/synth/vcf.c`) advance the coefficient pointer once per *(sample, filter-section)* pair - a 4th-order filter uses `FILTER_MAX_ORDER/2` = 2 cascaded sections per sample - but `VCF_SetBufsize` only allocated `pCoef` with `bufsize` entries, not `bufsize * sections`. Any host block between 4097 and 8192 samples (inclusive of the now-correctly-chunked range from the fix above) with a 4th-order filter selected wrote past the end of that buffer, corrupting adjacent heap memory (confirmed with AddressSanitizer: `heap-buffer-overflow`, `WRITE of size 8`, exactly 8 bytes past a 393216-byte/8192-`vcf_coef_t` allocation). Fixed by sizing the allocation for the worst case, `bufsize*(FILTER_MAX_ORDER/2)`. Verified with ASan across the full range (4097, 8192, 16384, 20000 samples) after the fix - zero errors.
|
||||
|
||||
## High — real-time audio-thread safety (fixed)
|
||||
|
||||
- [x] **Every note/CC/voice-count change allocated Strings and posted a message from the audio thread.** `synthChanged()` (`src/plug/PluginProcessor.cpp`) no longer calls `JaySynthActionListener::call*` directly from the audio thread. It now pushes a small POD event into a lock-free single-producer/single-consumer queue (JUCE's `AbstractFifo`, added to `PluginProcessor.h`), and the existing 10ms UI `Timer` (`timerCallback`/`dispatchUiEvents`) drains it on the message thread, where the String-building and `sendActionMessage` now safely happen.
|
||||
- [x] **~450KB–900KB of stack arrays allocated per voice per audio block.** `VoiceProcessDataV`'s 11 `SYNTH_MAX_BUFSIZE`-sized locals (`src/synth/voice.c`) are now persistent per-voice buffers (`pBuf_VCO_fmout`, `pBuf_vco_pwm`, etc., declared in `voice.h`), allocated once in `VoiceSetBufsize`/freed in `VoiceFree` — same lifecycle/pattern already used by `pBuf_Q` and by `vcf.c`/`env.c`/`lfo.c`.
|
||||
- [x] **`SynthDebug` used unbounded `vsprintf` into a fixed 1024-byte buffer.** `src/synth/synth_debug.c` now uses `vsnprintf` with the buffer size. (Left the blocking `puts()`/`OutputDebugString` I/O as-is — it's compiled out entirely unless `SYNTH_DEBUG` is defined, so release builds are unaffected, and a lock-free logging redesign felt like overreach for a debug-only path.)
|
||||
|
||||
## Memory management (fixed)
|
||||
|
||||
- [x] **`new[]`/`delete` mismatches (UB) in `JaySynth`'s destructor** — `m_pVoices`, `pPer_voice_controls`, `pCurrNoteInfos`, `humanize_voice_param[i]`, `ppHumanizedSliders[i]`, `m_ppAudioThread`, `m_ppEventAudioThreadRdy` (`src/plug/JaySynth.cpp`) all now freed with `delete[]` to match their `new T[n]` allocations. Also found and fixed the same pattern via `ScopedPointer` in `PluginProcessor.cpp` (`setStateInformation`/`setCurrentProgramStateInformation`) — `ScopedPointer` always calls scalar `delete` (per JUCE's own doc comment), so `ScopedPointer<char> pUncompressedData = new char[...]` was the same bug; replaced with `HeapBlock<char>`, JUCE's array-owning smart pointer.
|
||||
- [x] **`malloc` was never null-checked** across the C DSP core. Added a shared `SynthCheckAlloc()` helper (`synth_defs.h`/`synth_debug.c`) that aborts with a clear diagnostic instead of returning NULL, and wrapped all 24 `malloc` call sites across `env.c`, `lfo.c`, `vcf.c`, `vco.c`, `blit.c`, `wavetable.c`, `voice.c`, `param_scale.c`. All are one-time init/bufsize-change calls, never in the per-block render path, so this adds no real-time overhead.
|
||||
|
||||
## Patch/Bank import-export (fixed)
|
||||
|
||||
- [x] **Legacy patch import decoded the wrong XML node.** `patchImportXml` now handles the legacy single-patch format (`JSYNTH_PATCH` root) as its own top-level branch instead of incorrectly nested inside the modern per-child loop, which checked/decoded the outer `xml` on every iteration instead of the loop variable. Also switched its CC-table import to `import_midi_cc` (matching the actually-exercised convention already used by `bankDecodeXml_legacy`, since this branch was previously unreachable dead code). `loadPatchFromFile`'s `.xmp` branch now also accepts legacy-rooted files, so single legacy patches can actually be loaded. Also fixed a related bug found in passing: `setCurrentProgramStateInformation` (VST2 "copy plugin state to another track") was passing `patchImportXml` an XML node one level too deep, making it a complete no-op.
|
||||
- [x] **Found a much bigger bug while investigating "regular .fxb/.fxp silently ignored": the magic-number check itself was completely broken.** It compared `String((const char*)&bank->chunkMagic)` (raw file bytes as text, e.g. `"CcnK"`) against `String(cMagic)` (JUCE's `int`-to-*decimal-text* constructor, e.g. `"1130589771"`) — these can never be equal, so **the entire opaque `.fxb`/`.fxp` load path was dead code**, regardless of format. Fixed using `ByteOrder::bigEndianInt`, the correct byte-order-aware comparison (matches the pattern JUCE's own VST3 wrapper uses for the identical struct). "Regular" (non-chunk) format is still not supported for load, but now surfaces an `AlertWindow` explaining why instead of silently doing nothing. Verified with a standalone round-trip test of the binary format logic (magic detection, size/payload round-trip, safe rejection of truncated/bogus files).
|
||||
- [x] **`.fxp`/`.fxb` saving was unimplemented.** Now implemented using the same opaque-chunk format the (now-fixed) load path expects, reusing `getCurrentProgramStateInformation`/`getStateInformation` for the payload.
|
||||
- [x] **`patchDecodeXml`/`patchDecodeXml_legacy` duplication** — `_legacy` now just delegates to `patchDecodeXml`.
|
||||
|
||||
## Design
|
||||
|
||||
- [ ] **`JaySynth` is a god object** (`src/plug/JaySynth.h`) — owns parameters, MIDI CC/NRPN, MIDI clock, 3 humanize subsystems, unison, monophonic orchestration, voice stealing, and the thread pool, all via direct field access with no internal boundaries. This is very plausibly why the parameter-locking bug (now fixed) was missed in the first place — no seam enforces consistent locking discipline. Consider splitting into a handful of focused collaborators (e.g. MIDI dispatch, voice allocation, humanize/unison modes) that `JaySynth` composes.
|
||||
- [ ] **`void *the_editor` (`src/plug/PluginProcessor.h:394`) duplicates JUCE's own editor tracking**, is never cleared when the editor is destroyed, and every other call site correctly uses `getEditor(this)` instead — currently harmless but a dangling-pointer trap. Fix: remove the field and route `createEditor()` through `getEditor(this)` like everywhere else.
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
Waldorf MicroWave Wellenformen und WaveTables hinzugef�gt. WaveTable-Scan �ber PWM.
|
||||
wave.bin: Original MicroWave Wellenformen m�ssen als Datei ("waves.bin", 16bit, Big-Endian) im Plugin-Verzeichnis vorliegen, damit Feature freigeschaltet wird.
|
||||
wave2.bin: Original PPG EVU Wellenformen 8bit
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Executable
BIN
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,7 @@
|
||||
include $(MAKE_HOME)/defaults.mk
|
||||
|
||||
# Environment
|
||||
CC=clang
|
||||
CCC=clang++
|
||||
CXX=clang++
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
function eval_adsr(Ta, Td, Vs, Tr, ks)
|
||||
|
||||
% eval_adsr(0.1, 0.2, 0.2, 0.2)
|
||||
|
||||
fs = 1000;
|
||||
|
||||
a_a = -log(1-ks)/(Ta*fs);
|
||||
a_d = 5/(Td*fs);
|
||||
a_r = 5/(Tr*fs);
|
||||
state = 0;
|
||||
s = 0;
|
||||
tol = 1E-4;
|
||||
n = 1;
|
||||
p = 0;
|
||||
k = 1/ks;
|
||||
while (1),
|
||||
|
||||
if state == 0 % Attack
|
||||
s = a_a + (1-a_a)*s;
|
||||
y(n) = k*s;
|
||||
if s >= (ks-tol)
|
||||
state = 1;
|
||||
s = 1-ks;
|
||||
end;
|
||||
end;
|
||||
if state == 1 % Decay
|
||||
s = (1-a_d)*s;
|
||||
y(n) = s/(1-ks);
|
||||
if s <= (Vs+tol)
|
||||
state = 2;
|
||||
p = 0;
|
||||
end;
|
||||
end;
|
||||
if state == 2 % sustain
|
||||
y(n) = Vs;
|
||||
if p >= 1*fs
|
||||
state = 3;
|
||||
s = Vs;
|
||||
end;
|
||||
p = p + 1;
|
||||
end;
|
||||
if state == 3 % Release
|
||||
s = (1-a_r)*s;
|
||||
y(n) = s;
|
||||
if s <= (tol)
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
n = n + 1;
|
||||
end;
|
||||
|
||||
plot((0:n-1)/fs, y); grid;
|
||||
set(gca,'xtick',[0:0.1:(n-1)/fs])
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
function eval_adsr(Ta, Td, Vs, Tr, ks)
|
||||
|
||||
% eval_adsr(0.1, 0.2, 0.2, 0.2)
|
||||
|
||||
fs = 1000;
|
||||
|
||||
a_a = -log(1-ks)/(Ta*fs);
|
||||
a_d = 5/(Td*fs);
|
||||
a_r = 5/(Tr*fs);
|
||||
state = 0;
|
||||
s = 0;
|
||||
tol = 1E-4;
|
||||
n = 1;
|
||||
p = 0;
|
||||
k = 1/ks;
|
||||
while (1),
|
||||
|
||||
if state == 0 % Attack
|
||||
s = a_a + (1-a_a)*s;
|
||||
y(n) = s/ks;
|
||||
if s >= (ks-tol)
|
||||
state = 1;
|
||||
end;
|
||||
end;
|
||||
if state == 1 % Decay
|
||||
s = (1-a_d)*s;
|
||||
y(n) = s/(ks);
|
||||
if s <= (Vs*ks+tol)
|
||||
state = 2;
|
||||
p = 0;
|
||||
end;
|
||||
end;
|
||||
if state == 2 % sustain
|
||||
y(n) = Vs;
|
||||
if p >= 1*fs
|
||||
state = 3;
|
||||
s = Vs;
|
||||
end;
|
||||
p = p + 1;
|
||||
end;
|
||||
if state == 3 % Release
|
||||
s = (1-a_r)*s;
|
||||
y(n) = s;
|
||||
if s <= (tol)
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
n = n + 1;
|
||||
end;
|
||||
|
||||
plot((0:n-1)/fs, y); grid;
|
||||
set(gca,'xtick',[0:0.1:(n-1)/fs])
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
function eval_adsr(Ta, Td, Vs, Tr)
|
||||
|
||||
% eval_adsr(0.1, 0.2, 0.2, 0.2)
|
||||
|
||||
fs = 10000;
|
||||
|
||||
aa = 1/(Ta*fs)
|
||||
ba = (1-aa)
|
||||
a_d = 5/(Td*fs)
|
||||
a_r = 5/(Tr*fs)
|
||||
state = 0;
|
||||
s = 0;
|
||||
tol = 1E-4;
|
||||
ke = 1-exp(-1)
|
||||
n = 0;
|
||||
p = 0;
|
||||
k = 1/ke;
|
||||
kk = 0;
|
||||
s
|
||||
while (1),
|
||||
|
||||
if state == 0
|
||||
s = aa + ba*s;
|
||||
if s >= (ke-tol)
|
||||
state = 1;
|
||||
k = (1-Vs)/ke;
|
||||
kk = Vs;
|
||||
end;
|
||||
end;
|
||||
if state == 1
|
||||
s = (1-a_d)*s;
|
||||
if s <= (tol)
|
||||
state = 2;
|
||||
s = 1;
|
||||
k = Vs;
|
||||
kk = 0;
|
||||
p = 0;
|
||||
end;
|
||||
end;
|
||||
if state == 2
|
||||
if p >= 0.1*fs
|
||||
state = 3;
|
||||
end;
|
||||
p = p + 1;
|
||||
end;
|
||||
if state == 3
|
||||
s = (1-a_r)*s;
|
||||
if s <= (tol)
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
n = n + 1;
|
||||
y(n) = k*s + kk;
|
||||
end;
|
||||
|
||||
plot((0:n-1)/fs, 0.8-y); grid;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
function eval_adsr(Ta, Td, Vs, Tr)
|
||||
|
||||
% eval_adsr(0.1, 0.2, 0.2, 0.2)
|
||||
|
||||
fs = 1000;
|
||||
|
||||
a_a = 1/(Ta*fs);
|
||||
a_d = -1/(Td*fs/log(Vs))
|
||||
a_d = 5/(Td*fs)
|
||||
a_r = 5/(Tr*fs)
|
||||
state = 0;
|
||||
s = 0;
|
||||
tol = 1E-4;
|
||||
target = 1;
|
||||
n = 0;
|
||||
p = 0;
|
||||
k = 1/0.63;
|
||||
while (1),
|
||||
|
||||
if state == 0 % Attack
|
||||
s = a_a + (1-a_a)*s;
|
||||
if s >= (0.63-tol)
|
||||
state = 1
|
||||
target = Vs;
|
||||
end;
|
||||
end;
|
||||
if state == 1 % Decay
|
||||
s = (1-a_d)*s;
|
||||
if s <= (0.63*Vs+tol)
|
||||
state = 2
|
||||
target = 0;
|
||||
p = 0;
|
||||
end;
|
||||
end;
|
||||
if state == 2 % sustain
|
||||
s = Vs/k;
|
||||
if p >= 1*fs
|
||||
state = 3
|
||||
end;
|
||||
p = p + 1;
|
||||
end;
|
||||
if state == 3 % Release
|
||||
s = (1-a_r)*s;
|
||||
if s <= (target+tol)
|
||||
break;
|
||||
end;
|
||||
end;
|
||||
n = n + 1;
|
||||
y(n) = k*s;
|
||||
end;
|
||||
|
||||
plot((0:n-1)/fs, y); grid;
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
function eval_vcf_lut()
|
||||
N = 1000;
|
||||
fs = 48000;
|
||||
fmin = 18;
|
||||
fmax = 18000;
|
||||
sweepbase = 10;
|
||||
|
||||
filterParam = struct('sweepbase', 10, 'omega_min', 2*fmin/fs, 'omega_max', 2*fmax/fs, 'num_octaves', log(fmax/fmin)/log(sweepbase));
|
||||
filterParam
|
||||
|
||||
cv = (0:N-1)/N;
|
||||
plot (c
|
||||
function cv = omega2cv(filterParam, omega)
|
||||
cv = log(omega/filterParam.omega_min)/(filterParam.num_octaves * log(filterParam.sweepbase));
|
||||
cv = min(1, max(0, cv));
|
||||
|
||||
function omega = cv2omega(filterParam, cv)
|
||||
omega = filterParam.omega_min*filterParam.sweepbase ^ (filterParam.num_octaves*min(1, max(0, cv)));
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
function eval_vcf_lut()
|
||||
N = 1000;
|
||||
fs = 48000;
|
||||
fmin = 18;
|
||||
fmax = 18000;
|
||||
sweepbase = 10;
|
||||
|
||||
filterParam = struct('sweepbase', 10, 'omega_min', fmin/fs, 'omega_max', fmax/fs, 'num_octaves', log(fmax/fmin)/log(sweepbase));
|
||||
filterParam
|
||||
|
||||
cv = (0:N-1)/N;
|
||||
|
||||
subplot(2, 1, 1)
|
||||
plot (cv, fs*cv2omega(filterParam, cv)); grid; legend('F'); xlabel('cv');
|
||||
subplot(2, 1, 2)
|
||||
plot (cv, cos(2*pi*cv2omega(filterParam, cv)), cv, sin(2*pi*cv2omega(filterParam, cv))); grid; legend('kc', 'ks'); xlabel('cv');
|
||||
|
||||
function cv = omega2cv(filterParam, omega)
|
||||
cv = log(omega/filterParam.omega_min)/(filterParam.num_octaves * log(filterParam.sweepbase));
|
||||
cv = min(1, max(0, cv));
|
||||
|
||||
function omega = cv2omega(filterParam, cv)
|
||||
omega = filterParam.omega_min*filterParam.sweepbase .^ (filterParam.num_octaves*min(1, max(0, cv)));
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
function eval_lfo()
|
||||
|
||||
L = 10000;
|
||||
fs = 44100;
|
||||
fstart = 40;
|
||||
fend = 400;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
ip = 0 % phase of the first output sample in radians
|
||||
w = freq*pi / samplerate
|
||||
b1 = 2.0 * cos(w)
|
||||
|
||||
% Init
|
||||
y1=sin(ip-w)
|
||||
y2=sin(ip-2*w)
|
||||
|
||||
% Loop
|
||||
for n=1:L,
|
||||
|
||||
y0 = b1*y1 - y2
|
||||
y2 = y1
|
||||
y1 = y0
|
||||
|
||||
|
||||
close all
|
||||
|
||||
figure;
|
||||
plot(1:L, vsaw, 1:L, vblit); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblit, fs, 'blit.wav');
|
||||
|
||||
Executable
+44
@@ -0,0 +1,44 @@
|
||||
function eval_lfo()
|
||||
|
||||
L = 10000;
|
||||
fs = 44100;
|
||||
fstart = 44;
|
||||
fend = 400;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
ip = 0 % phase of the first output sample in radians
|
||||
|
||||
% Init
|
||||
|
||||
% Loop
|
||||
f = fstart;
|
||||
fchg = 1;
|
||||
|
||||
y1=sin(ip-f*pi / fs);
|
||||
y2=sin(ip-2*f*pi / fs);
|
||||
|
||||
for n=1:L,
|
||||
|
||||
if fchg == 1,
|
||||
b1 = 2.0 * cos(f*pi / fs);
|
||||
fchg = 1000;
|
||||
end;
|
||||
|
||||
y0 = b1*y1 - y2;
|
||||
y2 = y1;
|
||||
y1 = y0;
|
||||
|
||||
fchg = fchg - 1;
|
||||
|
||||
lfo(n) = y0;
|
||||
f = f + df;
|
||||
|
||||
close all
|
||||
|
||||
end;
|
||||
|
||||
figure;
|
||||
plot(1:L, lfo); grid;
|
||||
|
||||
wavwrite(0.5*lfo, fs, 'lfo.wav');
|
||||
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
function eval_lfo2()
|
||||
|
||||
L = 80000;
|
||||
fs = 48000;
|
||||
fstart = 10;
|
||||
fend = 1000;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
ip = 0.0 % phase of the first output sample in radians
|
||||
|
||||
% Init
|
||||
|
||||
% Loop
|
||||
f = fstart;
|
||||
fchg = 0;
|
||||
|
||||
ylast = 0;
|
||||
a = 0.5;
|
||||
b = 2.0 * sin(f*pi / fs);
|
||||
y0 = a*cos(2*pi*ip);
|
||||
y1 = a*sin(2*pi*ip);
|
||||
|
||||
for n=1:L,
|
||||
|
||||
y0 = y0 - b*y1;
|
||||
y1 = y1 + b*y0;
|
||||
|
||||
fchg = (mod(n, 1000) == 0);
|
||||
|
||||
if fchg
|
||||
b = 2.0 * sin(f*pi / fs);
|
||||
end
|
||||
|
||||
ylast = y1;
|
||||
|
||||
lfo(n) = y0;
|
||||
f = f + df;
|
||||
|
||||
close all
|
||||
|
||||
end;
|
||||
|
||||
figure;
|
||||
plot(1:L, lfo); grid;
|
||||
|
||||
wavwrite(0.5*lfo, fs, 'lfo.wav');
|
||||
|
||||
Executable
+47
@@ -0,0 +1,47 @@
|
||||
function eval_lfo2()
|
||||
|
||||
L = 80000;
|
||||
fs = 48000;
|
||||
fstart = 10;
|
||||
fend = 5000;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
ip = 0.0 % phase of the first output sample in radians
|
||||
|
||||
% Init
|
||||
|
||||
% Loop
|
||||
f = fstart;
|
||||
fchg = 0;
|
||||
|
||||
ylast = 0;
|
||||
a = 0.5;
|
||||
b = 2.0 * sin(f*pi / fs);
|
||||
y0 = a*cos(2*pi*ip);
|
||||
y1 = a*sin(2*pi*ip);
|
||||
|
||||
for n=1:L,
|
||||
|
||||
y0 = y0 - b*y1;
|
||||
y1 = y1 + b*y0;
|
||||
|
||||
fchg = (mod(n, 1000) == 0);
|
||||
|
||||
if fchg
|
||||
b = 2.0 * sin(f*pi / fs);
|
||||
end
|
||||
|
||||
ylast = y1;
|
||||
|
||||
lfo(n) = y0;
|
||||
f = f + df;
|
||||
|
||||
close all
|
||||
|
||||
end;
|
||||
|
||||
figure;
|
||||
plot(1:L, lfo); grid;
|
||||
|
||||
wavwrite(0.5*lfo, fs, 'lfo.wav');
|
||||
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
function eval_smooth(Ta)
|
||||
|
||||
fs = 10000;
|
||||
|
||||
b = 1;
|
||||
if (abs(Ta) > 0)
|
||||
b = 1/(abs(Ta)*fs);
|
||||
end;
|
||||
|
||||
a = 1 - b;
|
||||
|
||||
x = [ zeros(1, 1000) ones(1, 1000) zeros(1, 1000)];
|
||||
|
||||
y = 0;
|
||||
for i=1:length(x)
|
||||
if (Ta < 0)
|
||||
y = b*x(i) + a*y;
|
||||
yn(i) = y;
|
||||
end;
|
||||
|
||||
plot(yn); grid;
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
function eval_smooth(Ta)
|
||||
|
||||
fs = 10000;
|
||||
|
||||
b = 1;
|
||||
if (abs(Ta) > 0)
|
||||
b = 1/(abs(Ta)*fs);
|
||||
end;
|
||||
|
||||
a = 1 - b;
|
||||
|
||||
x = [ zeros(1, 1000) ones(1, 1000) zeros(1, 1000)];
|
||||
N = length(x);
|
||||
|
||||
y = 0;
|
||||
y1 = 0;
|
||||
for i=1:length(x)
|
||||
y1 = b*x(i) + a*y1;
|
||||
% if (Ta > 0)
|
||||
y1n(i) = y1;
|
||||
% else
|
||||
y2n(i) = 2*x(i) - y1;
|
||||
% end
|
||||
end;
|
||||
|
||||
subplot(3, 1, 1)
|
||||
plot(1:N, x, 'r'); grid;
|
||||
xlabel('Original LFO Wellenform (Square)')
|
||||
subplot(3, 1, 2)
|
||||
plot(1:N, y1n, 'r'); grid;
|
||||
xlabel('Positive smoothed')
|
||||
subplot(3, 1, 3)
|
||||
plot(1:N, y2n, 'r'); grid;
|
||||
xlabel('Negative smoothed')
|
||||
Executable
BIN
Binary file not shown.
Executable
+38
@@ -0,0 +1,38 @@
|
||||
function eval_limiter(name)
|
||||
|
||||
Tr = 1; % ms
|
||||
Tf = 100; % ms
|
||||
input_gain = 2;
|
||||
output_gain = 0.5;
|
||||
|
||||
limit_thresh = 0.25;
|
||||
|
||||
[x, fs, nbits, opts] = wavread(name);
|
||||
x = x.*input_gain;
|
||||
|
||||
N = fix(length(x)/);
|
||||
|
||||
a_r = 1000/(Tr*fs);
|
||||
a_f = 1000/(Tf*fs);
|
||||
|
||||
y = 0;
|
||||
for n=1:N
|
||||
if (y < abs(x(n)))
|
||||
y = y + a_r*(abs(x(n)) - y);
|
||||
else
|
||||
y = y + a_f*(abs(x(n)) - y);
|
||||
end
|
||||
env(n) = y;
|
||||
end;
|
||||
|
||||
limiter_gain = limit_thresh./max(limit_thresh, env)';
|
||||
x_limited = x(1:N).*limiter_gain.*output_gain;
|
||||
|
||||
wavwrite(x_limited,fs,'limiter_out.wav');
|
||||
|
||||
subplot(2, 1, 1)
|
||||
plot(0:N-1, x(1:N), 0:N-1, env, 'r-'); grid;
|
||||
subplot(2, 1, 2)
|
||||
plot(0:N-1, x_limited, 0:N-1, limiter_gain, 'r-'); grid;
|
||||
|
||||
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
function eval_limiter(name)
|
||||
|
||||
Tr = 1; % ms
|
||||
Tf = 100; % ms
|
||||
input_gain = 2;
|
||||
output_gain = 0.5;
|
||||
|
||||
limit_thresh = 0.25;
|
||||
|
||||
[x, fs, nbits, opts] = wavread(name);
|
||||
x = x.*input_gain;
|
||||
|
||||
N = fix(length(x));
|
||||
|
||||
a_r = 1000/(Tr*fs);
|
||||
a_f = 1000/(Tf*fs);
|
||||
|
||||
y = 0;
|
||||
for n=1:N
|
||||
if (y < abs(x(n)))
|
||||
y = y + a_r*(abs(x(n)) - y);
|
||||
else
|
||||
y = y + a_f*(abs(x(n)) - y);
|
||||
end
|
||||
env(n) = y;
|
||||
end;
|
||||
|
||||
limiter_gain = limit_thresh./max(limit_thresh, env)';
|
||||
x_limited = x(1:N).*limiter_gain.*output_gain;
|
||||
|
||||
wavwrite(x_limited,fs,'limiter_out.wav');
|
||||
|
||||
subplot(2, 1, 1)
|
||||
plot(0:N-1, x(1:N), 0:N-1, env, 'r-'); grid;
|
||||
subplot(2, 1, 2)
|
||||
plot(0:N-1, x_limited, 0:N-1, limiter_gain, 'r-'); grid;
|
||||
|
||||
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+77
@@ -0,0 +1,77 @@
|
||||
function eval_midi_sync(pe, fe)
|
||||
N = 5000;
|
||||
t = (0:N-1)/N;
|
||||
|
||||
x1 = mod(20*t, 1) >=0.5;
|
||||
x2 = mod((20+fe)*t+pe, 1) >=0.5;
|
||||
|
||||
dx1 = 50/N
|
||||
dx2 = (50+fe)/N
|
||||
|
||||
qi1 = 0;
|
||||
qi2 = 0;
|
||||
xi1 = 0;
|
||||
xi2 = 0;
|
||||
xx1 = 0;
|
||||
xx2 = 0;
|
||||
|
||||
lead = 0;
|
||||
lag = 0;
|
||||
ddx = 0;
|
||||
for i=1:N,
|
||||
%[qi1, qi2, xi1, xi2] = pd(x1(i), x2(i), qi1, qi2, xi1, xi2);
|
||||
[qi1, qi2, xi1, xi2] = pd(xx1 >=0.5, xx2 >=0.5, qi1, qi2, xi1, xi2);
|
||||
err = (qi1-qi2);
|
||||
lead = 0.*err;
|
||||
lag = lag + 0.000001*err;
|
||||
ddx = (lead+lag);
|
||||
xx1 = mod(xx1 + dx1, 1);
|
||||
xx2 = mod(xx2 + dx2 + ddx, 1);
|
||||
|
||||
|
||||
q1(i) = qi1;
|
||||
q2(i) = qi2;
|
||||
|
||||
end;
|
||||
|
||||
dx2 = dx2 + ddx
|
||||
dx1 = dx1
|
||||
|
||||
subplot(3, 1, 1)
|
||||
plot(t, x1, t, x2); grid; axis([0 1 -0.2 1.2])
|
||||
subplot(3, 1, 2)
|
||||
plot(t, q1); grid; axis([0 1 -0.2 1.2])
|
||||
subplot(3, 1, 3)
|
||||
plot(t, q2); grid; axis([0 1 -0.2 1.2])
|
||||
|
||||
|
||||
function [Q1, Q2, xo1, xo2] = pd(x1, x2, qi1, qi2, xi1, xi2)
|
||||
xo1 = xi1;
|
||||
xo2 = xi2;
|
||||
qo1 = qi1;
|
||||
qo2 = qi2;
|
||||
for i=1:length(x1)
|
||||
% Q1
|
||||
% Detect edge
|
||||
if (x1(i) - xo1) > 0.5
|
||||
qo1 = 1;
|
||||
end;
|
||||
xo1 = x1(i);
|
||||
|
||||
% Q2
|
||||
% Detect edge
|
||||
if (x2(i) - xo2) > 0.5
|
||||
qo2 = 1;
|
||||
end;
|
||||
xo2 = x2(i);
|
||||
|
||||
% Asynchronous reset
|
||||
if (qo1 * qo2) > 0.5
|
||||
qo1 = 0;
|
||||
qo2 = 0;
|
||||
end;
|
||||
|
||||
Q1(i) = qo1;
|
||||
Q2(i) = qo2;
|
||||
|
||||
end;
|
||||
Executable
+75
@@ -0,0 +1,75 @@
|
||||
function eval_midi_sync(pe, fe)
|
||||
N = 5000;
|
||||
t = (0:N-1)/N;
|
||||
|
||||
dx1 = 40/N
|
||||
dx2 = (40+fe)/N
|
||||
|
||||
qi1 = 0;
|
||||
qi2 = 0;
|
||||
xi1 = 0;
|
||||
xi2 = 0;
|
||||
xx1 = 0;
|
||||
xx2 = pe;
|
||||
|
||||
lead = 0;
|
||||
lag = 0;
|
||||
ddx = 0;
|
||||
for i=1:N,
|
||||
|
||||
[qi1, qi2, xi1, xi2] = pd(xx1 >=0.5, xx2 >=0.5, qi1, qi2, xi1, xi2);
|
||||
err = (qi1-qi2);
|
||||
lead = 0.002*err;
|
||||
lag = lag + 0.000005*err;
|
||||
ddx(i) = (lead+lag);
|
||||
xx1 = mod(xx1 + dx1, 1);
|
||||
xx2 = mod(xx2 + dx2 + ddx(i), 1);
|
||||
|
||||
|
||||
q1(i) = qi1;
|
||||
q2(i) = qi2;
|
||||
x1(i) = xx1 >=0.5;
|
||||
x2(i) = xx2 >=0.5;
|
||||
|
||||
end;
|
||||
|
||||
subplot(4, 1, 1)
|
||||
plot(t, x1, t, x2); grid; axis([0 1 -0.2 1.2])
|
||||
subplot(4, 1, 2)
|
||||
plot(t, q1); grid; axis([0 1 -0.2 1.2])
|
||||
subplot(4, 1, 3)
|
||||
plot(t, q2); grid; axis([0 1 -0.2 1.2])
|
||||
subplot(4, 1, 4)
|
||||
plot(t, ddx); grid;
|
||||
|
||||
|
||||
function [Q1, Q2, xo1, xo2] = pd(x1, x2, qi1, qi2, xi1, xi2)
|
||||
xo1 = xi1;
|
||||
xo2 = xi2;
|
||||
qo1 = qi1;
|
||||
qo2 = qi2;
|
||||
for i=1:length(x1)
|
||||
% Q1
|
||||
% Detect edge
|
||||
if (x1(i) - xo1) > 0.5
|
||||
qo1 = 1;
|
||||
end;
|
||||
xo1 = x1(i);
|
||||
|
||||
% Q2
|
||||
% Detect edge
|
||||
if (x2(i) - xo2) > 0.5
|
||||
qo2 = 1;
|
||||
end;
|
||||
xo2 = x2(i);
|
||||
|
||||
% Asynchronous reset
|
||||
if (qo1 * qo2) > 0.5
|
||||
qo1 = 0;
|
||||
qo2 = 0;
|
||||
end;
|
||||
|
||||
Q1(i) = qo1;
|
||||
Q2(i) = qo2;
|
||||
|
||||
end;
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
function eval_minblep()
|
||||
|
||||
nsin = 4096;
|
||||
nharm_max = 1800;
|
||||
L = 48000/2;
|
||||
fs = 48000;
|
||||
fstart = 440;
|
||||
fend = 440;
|
||||
f2start = 200;
|
||||
f2end = 600;
|
||||
df = (fend-fstart)/L
|
||||
df2 = (f2end-f2start)/L
|
||||
saw = 0;
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
|
||||
for mm=1:nharm
|
||||
blitm(mm,:) = sin(xx*(mm-1)*pi)./sin(xx*pi).*Hw';
|
||||
blitm(mm,(find(isnan(blitm(mm,:))))) = (mm-1);
|
||||
end;
|
||||
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
w = [1; 2*ones(nsin/2-1,1); ones(1 - rem(nsin,2),1); zeros(nsin/2-1,1)]';
|
||||
for mm=1:nharm
|
||||
x_rc = real(ifft(log(abs(fft(blitm(mm, :))))));
|
||||
y = real(ifft(exp(fft(w.*x_rc))));
|
||||
minblepm(mm, :) = cumsum(y)/nsin;
|
||||
end;
|
||||
|
||||
x = 0;
|
||||
x2 = 0;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
f2 = f2start;
|
||||
tri = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
startup = 1;
|
||||
blep = 1;
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 1 || startup,
|
||||
x = 0;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
blep_offset = 0;
|
||||
blep_gain = 1;
|
||||
z = ~z;
|
||||
end;
|
||||
if x2 >= 1 || startup,
|
||||
x2 =0;
|
||||
dx2 = f2/fs;
|
||||
% blep_offset = -(1-x);
|
||||
% blep_gain = x;
|
||||
% x = 0;
|
||||
% z = ~z;
|
||||
end;
|
||||
startup = 0;
|
||||
|
||||
nn = (x+0.0)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
h = lagrange(NLG, nf);
|
||||
blep = h(NLG+1)*minblepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*minblepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
|
||||
saw = x - blep_gain*blep + blep_offset;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
vtri(n) = tri;
|
||||
vsaw(n,1) = x;
|
||||
vsaw(n,2) = saw+0.5;
|
||||
vblep(n,1) = x;
|
||||
vblep(n,2) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
x2 = x2 + dx2;
|
||||
f2 = f2 + df2;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :), 1:nsin, minblepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
function eval_minblep()
|
||||
|
||||
nsin = 4096;
|
||||
nharm_max = 1800;
|
||||
L = 48000/2;
|
||||
fs = 48000;
|
||||
fstart = 440;
|
||||
fend = 440;
|
||||
df = (fend-fstart)/L
|
||||
f2start = 200;
|
||||
f2end = 600;
|
||||
df2 = (f2end-f2start)/L
|
||||
saw = 0;
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
|
||||
for mm=1:nharm
|
||||
blitm(mm,:) = sin(xx*(mm-1)*pi)./sin(xx*pi).*Hw';
|
||||
blitm(mm,(find(isnan(blitm(mm,:))))) = (mm-1);
|
||||
end;
|
||||
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
w = [1; 2*ones(nsin/2-1,1); ones(1 - rem(nsin,2),1); zeros(nsin/2-1,1)]';
|
||||
for mm=1:nharm
|
||||
x_rc = real(ifft(log(abs(fft(blitm(mm, :))))));
|
||||
y = real(ifft(exp(fft(w.*x_rc))));
|
||||
minblepm(mm, :) = cumsum(y)/nsin;
|
||||
end;
|
||||
|
||||
x = 0;
|
||||
x2 = 0;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
f2 = f2start;
|
||||
tri = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
startup = 1;
|
||||
blep = 1;
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 1 || startup,
|
||||
x = 0;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
blep_offset = 0;
|
||||
blep_gain = 1;
|
||||
z = ~z;
|
||||
end;
|
||||
if x2 >= 1 || startup,
|
||||
x2 =0;
|
||||
dx2 = f2/fs;
|
||||
% blep_offset = -(1-x);
|
||||
% blep_gain = x;
|
||||
% x = 0;
|
||||
% z = ~z;
|
||||
end;
|
||||
startup = 0;
|
||||
|
||||
nn = (x+0.0)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
h = lagrange(NLG, nf);
|
||||
blep = h(NLG+1)*minblepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*minblepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
|
||||
saw = x - blep_gain*blep + blep_offset;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n) = tri;
|
||||
vsaw(n,1) = x;
|
||||
vsaw(n,2) = saw+0.5;
|
||||
vblep(n,1) = x;
|
||||
vblep(n,2) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
x2 = x2 + dx2;
|
||||
f2 = f2 + df2;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :), 1:nsin, minblepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
function eval_minblep_pre()
|
||||
|
||||
f = 440;
|
||||
N = 4000;
|
||||
t = (-N/2:N/2-1)/N;
|
||||
|
||||
x = sin(t*f*pi)./sin(t*pi); .* kaiser(N, 8)';
|
||||
x(find(isnan(x))) = f;
|
||||
%x = x / f;
|
||||
|
||||
% Real cepstrum
|
||||
x_rc = real(ifft(log(abs(fft(x)))));
|
||||
w = [1; 2*ones(N/2-1,1); ones(1 - rem(N,2),1); zeros(N/2-1,1)]';
|
||||
y = real(ifft(exp(fft(w.*x_rc))));
|
||||
|
||||
xi = cumsum(x)/N;
|
||||
yi = cumsum(y)/N;
|
||||
|
||||
close all;
|
||||
subplot(2, 1, 1)
|
||||
plot(t, x/f, t, xi, 'r'); grid;
|
||||
|
||||
subplot(2, 1, 2)
|
||||
plot(0:N-1, y/f, 0:N-1, yi, 'r'); grid;
|
||||
|
||||
figure;
|
||||
plot(t, xi); grid;
|
||||
|
||||
figure;
|
||||
plot(t, yi); grid;
|
||||
|
||||
figure;
|
||||
plot(w); grid;
|
||||
Executable
+33
@@ -0,0 +1,33 @@
|
||||
function eval_minblep_pre()
|
||||
|
||||
f = 440;
|
||||
N = 4000;
|
||||
t = (-N/2:N/2-1)/N;
|
||||
|
||||
x = sin(t*f*pi)./sin(t*pi);% .* kaiser(N, 8)';
|
||||
x(find(isnan(x))) = f;
|
||||
%x = x / f;
|
||||
|
||||
% Real cepstrum
|
||||
x_rc = real(ifft(log(abs(fft(x)))));
|
||||
w = [1; 2*ones(N/2-1,1); ones(1 - rem(N,2),1); zeros(N/2-1,1)]';
|
||||
y = real(ifft(exp(fft(w.*x_rc))));
|
||||
|
||||
xi = cumsum(x)/N;
|
||||
yi = cumsum(y)/N;
|
||||
|
||||
close all;
|
||||
subplot(2, 1, 1)
|
||||
plot(t, x/f, t, xi, 'r'); grid;
|
||||
|
||||
subplot(2, 1, 2)
|
||||
plot(0:N-1, y/f, 0:N-1, yi, 'r'); grid;
|
||||
|
||||
figure;
|
||||
plot(t, xi); grid;
|
||||
|
||||
figure;
|
||||
plot(t, yi); grid;
|
||||
|
||||
figure;
|
||||
plot(w); grid;
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+96
@@ -0,0 +1,96 @@
|
||||
function eval_blep()
|
||||
|
||||
nsin = 2048;
|
||||
nharm_max = 1800;
|
||||
L = 48000;
|
||||
fs = 48000;
|
||||
fstart = 110;
|
||||
fend = 110;
|
||||
df = (fend-fstart)/L
|
||||
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
for mm=1:nharm,
|
||||
bb(mm, :) = sin((mm-1)*xx*pi);
|
||||
end;
|
||||
aa = sin(xx*pi);
|
||||
|
||||
for mm=1:nharm
|
||||
for nn=1:length(xx)
|
||||
if (aa(nn) == 0)
|
||||
blitm(mm,nn) = (mm-1);
|
||||
else
|
||||
blitm(mm,nn) = bb(mm, nn)/aa(nn).*Hw(nn);
|
||||
end
|
||||
end
|
||||
end;
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
%blep2 = blepm(nharm_max/2+1, :)'
|
||||
%aa2 = aa(:)'
|
||||
%bb2 = bb(nharm_max/2+1, :)'
|
||||
|
||||
x = 0.5;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
tri = 0;
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 0.5,
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
z = ~z;
|
||||
end;
|
||||
nn = (x+0.5)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
|
||||
h = lagrange(1, nf);
|
||||
|
||||
% blep = (blepm(m, min(ni+1, nsin)) - blepm(m, ni))*nf + blepm(m, ni);
|
||||
|
||||
[blep, Zi] = filter(h, 1, blepm(m
|
||||
blep = h(1)*blepm(m, max(1, ni-1)) + h(2)*blepm(m, ni);
|
||||
|
||||
saw = x - blep;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n) = tri;
|
||||
vsaw(n) = saw+0.5;
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
vblep(n) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
function eval_blep()
|
||||
|
||||
nsin = 4096;
|
||||
nharm_max = 1800;
|
||||
L = 48000/2;
|
||||
fs = 48000;
|
||||
fstart = 440;
|
||||
fend = 440;
|
||||
df = (fend-fstart)/L
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
|
||||
for mm=1:nharm
|
||||
blitm(mm,:) = sin(xx*(mm-1)*pi)./sin(xx*pi).*Hw';
|
||||
blitm(mm,(find(isnan(blitm(mm,:))))) = (mm-1);
|
||||
end;
|
||||
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
x = 0.0;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
tri = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
startup = 1;
|
||||
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 0.5 || startup;
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
z = ~z;
|
||||
end;
|
||||
startup = 0;
|
||||
nn = (x+0.5)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
|
||||
h = lagrange(NLG, nf);
|
||||
blep = h(NLG+1)*blepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*blepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
saw = x - blep;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n) = tri;
|
||||
vsaw(n,1) = x;
|
||||
vsaw(n,2) = saw+0.5;
|
||||
vblep(n,1) = x;
|
||||
vblep(n,2) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
function eval_blep_hs()
|
||||
|
||||
nsin = 1024;
|
||||
nharm_max = 1800;
|
||||
L = 48000;
|
||||
fs = 48000;
|
||||
fstart = 44;
|
||||
fend = 44;
|
||||
df = (fend-fstart)/L
|
||||
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
for mm=1:nharm,
|
||||
bb(mm, :) = sin((mm-1)*xx*pi);
|
||||
end;
|
||||
aa = sin(xx*pi);
|
||||
|
||||
for mm=1:nharm
|
||||
for nn=1:length(xx)
|
||||
if (aa(nn) == 0)
|
||||
blitm(mm,nn) = (mm-1);
|
||||
else
|
||||
blitm(mm,nn) = bb(mm, nn)/aa(nn).*Hw(nn);
|
||||
end
|
||||
end
|
||||
end;
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
%blep2 = blepm(nharm_max/2+1, :)'
|
||||
%aa2 = aa(:)'
|
||||
%bb2 = bb(nharm_max/2+1, :)'
|
||||
|
||||
x = 0.5;
|
||||
x2 = 0.5;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
|
||||
tri = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 0.5,
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
z = ~z;
|
||||
end;
|
||||
nn = (x+0.5)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
|
||||
h = lagrange(NLG, nf);
|
||||
|
||||
% blep = (blepm(m, min(ni+1, nsin)) - blepm(m, ni))*nf + blepm(m, ni);
|
||||
|
||||
% blep = h(1)*blepm(m, max(1, ni-3)) + h(2)*blepm(m, max(1, ni-2)) + h(3)*blepm(m, max(1, ni-1)) + h(4)*blepm(m, max(1, ni));
|
||||
blep = h(NLG+1)*blepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*blepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
saw = x - blep;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n) = tri;
|
||||
vsaw(n) = saw+0.5;
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
vblep(n) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
function eval_blep_hs()
|
||||
|
||||
nsin = 1024;
|
||||
nharm_max = 256;
|
||||
L = 4800;
|
||||
fs = 48000;
|
||||
fstart = 44;
|
||||
fend = 44;
|
||||
df = (fend-fstart)/L
|
||||
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
for mm=1:nharm,
|
||||
bb(mm, :) = sin((mm-1)*xx*pi);
|
||||
end;
|
||||
aa = sin(xx*pi);
|
||||
|
||||
for mm=1:nharm
|
||||
for nn=1:length(xx)
|
||||
if (aa(nn) == 0)
|
||||
blitm(mm,nn) = (mm-1);
|
||||
else
|
||||
blitm(mm,nn) = bb(mm, nn)/aa(nn).*Hw(nn);
|
||||
end
|
||||
end
|
||||
end;
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
%blep2 = blepm(nharm_max/2+1, :)'
|
||||
%aa2 = aa(:)'
|
||||
%bb2 = bb(nharm_max/2+1, :)'
|
||||
|
||||
x = 0.0;
|
||||
x2 = -0.5;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
f2 = 0.6*f;
|
||||
dx2 = f2/fs;
|
||||
tri = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
sync = 0;
|
||||
restart = 0;
|
||||
startup = 1;
|
||||
minblep_active = 0;
|
||||
minblep_count = 0;
|
||||
minblep_length = 200;
|
||||
minblep = 1-(0:minblep_length-1)/minblep_length;
|
||||
for n = 1:L,
|
||||
|
||||
sync = 0;
|
||||
if startup,
|
||||
x = 0.0;
|
||||
startup = 0;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
z = 0;
|
||||
end;
|
||||
if (x >= 0.5),
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
z = ~z;
|
||||
end;
|
||||
if (x2 >= 0.5),
|
||||
x2 = x2 - 1;
|
||||
sync = 1;
|
||||
startup = 1;
|
||||
minblep_count = minblep_length;
|
||||
end;
|
||||
nn = (x+0.5)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
|
||||
h = lagrange(NLG, nf);
|
||||
blep = h(NLG+1)*blepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*blepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
if (minblep_count > 0)
|
||||
saw = saw_last - minblep(minblep_count)/(1-2*saw_last+minblep_length*dx);
|
||||
minblep_count = minblep_count - 1;
|
||||
minblep_active = 1;
|
||||
else
|
||||
minblep_active = 0;
|
||||
saw = x - blep;
|
||||
saw_last = saw;
|
||||
end
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n) = tri;
|
||||
|
||||
vsaw(n) = 2*(saw+0.5);
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
vblep(n) = blep;
|
||||
vx(n) = x;
|
||||
vsync(n) = sync;
|
||||
vminblep_active(n) = minblep_active;
|
||||
x2 = x2 + dx2;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :)); grid;
|
||||
|
||||
figure;
|
||||
subplot(3, 1, 1)
|
||||
plot(1:L, vx, 1:L, vsync, 'r'); grid;
|
||||
subplot(3, 1, 2)
|
||||
plot(1:L, vblep); grid;
|
||||
subplot(3, 1, 3)
|
||||
plot(1:L, vsaw, 1:L, vminblep_active); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+103
@@ -0,0 +1,103 @@
|
||||
function eval_blit()
|
||||
|
||||
sinc_use_lut = 1
|
||||
|
||||
L = 40000;
|
||||
fs = 48000;
|
||||
fstart = 110;
|
||||
fend = 1100;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
nhw = 4096;
|
||||
Hw = kaiser(nhw, 8)';
|
||||
|
||||
if sinc_use_lut == 1
|
||||
|
||||
nharm = 512;
|
||||
nsin = 4096;
|
||||
xx = (0:nsin)/nsin - 0.5;
|
||||
mmm=0;
|
||||
for mm=1:nharm,
|
||||
mmm=mmm+1;
|
||||
bb(mm, :) = sin(mmm*xx*pi);
|
||||
end;
|
||||
aa = sin(xx*pi);
|
||||
end;
|
||||
|
||||
sinc_m(mm, find(aa == 0)) = 1;
|
||||
|
||||
x = 0.5;
|
||||
z = -1;
|
||||
f = fstart;
|
||||
sqr = 0.0;
|
||||
c4 = 0;
|
||||
a = 1;
|
||||
b = 1;
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 0.5,
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
fraq = 1.0/p;
|
||||
m = fix((p/2.0)) + 1;
|
||||
saw = 0.0;
|
||||
c3 = 0.0;
|
||||
z = -z;
|
||||
end;
|
||||
if sinc_use_lut == 1
|
||||
nn = (x+0.5)*nsin + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
bbi = (bb(m, ni+1) - bb(m, ni))*nf + bb(m, ni); % Fractional delay (linear interpolation)
|
||||
aai = (aa(ni+1) - aa(ni))*nf + aa(ni); % Fractional delay (linear interpolation)
|
||||
if (aai ~= 0)
|
||||
blit = fraq* bbi / aai * Hw(fix((x+0.5)*nhw)+1);
|
||||
else
|
||||
blit = fraq;
|
||||
end
|
||||
else
|
||||
b = sin(m*x*pi);
|
||||
a = sin(x*pi);
|
||||
if (a ~= 0)
|
||||
blit = fraq * b/a * Hw(fix((x+0.5)*nhw)+1);
|
||||
else
|
||||
blit = fraq;
|
||||
end
|
||||
end
|
||||
|
||||
saw = saw + blit;
|
||||
sqr = sqr + z*blit;
|
||||
vsaw(n) = 2*(saw - c3);
|
||||
vsqr(n) = 2*(sqr - 0.5);
|
||||
vblit(n) = blit;
|
||||
vc3(n) = b;
|
||||
x = x + fraq;
|
||||
c3 = c3 + fraq;
|
||||
f = f + df;
|
||||
c4 = c4 + fraq - blit;
|
||||
vc4(n) = a;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
|
||||
figure;
|
||||
plot(1:L, vsaw, 1:L, vc4, 1:L, vblit); grid;
|
||||
|
||||
figure;
|
||||
plot(vc4); grid;
|
||||
|
||||
figure;
|
||||
plot(vc3); grid;
|
||||
|
||||
if sinc_use_lut == 1
|
||||
figure;
|
||||
plot(sinc_m(nharm, :)); grid;
|
||||
end;
|
||||
|
||||
wavwrite(0.5*vc4, fs, 'c4.wav');
|
||||
wavwrite(0.5*vc3, fs, 'c3.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblit, fs, 'blit.wav');
|
||||
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
function eval_blit()
|
||||
Nb = 33;
|
||||
Nos = 1000;
|
||||
fs = 48000;
|
||||
f = 440;
|
||||
fcut = 16000;
|
||||
P = fs/f
|
||||
Pi = floor(P)
|
||||
Pf = fix(Nos*(fs/f - Pi))
|
||||
|
||||
x = (0:Nb*Nos-1) - (Nb*Nos-1)/2;
|
||||
H = sinc(2*fcut/fs.*x/Nos).*Kaiser(Nos*Nb, 8)';
|
||||
for ii=0:Nos-1,
|
||||
for jj=0:Nb-1
|
||||
Hs(ii+1,jj+1) = H(Nos-ii+Nos*jj);
|
||||
end
|
||||
end;
|
||||
|
||||
%Hs = init_steps(Nb, Nos);
|
||||
|
||||
k = 1./sum(Hs');
|
||||
%k = ones(1, Nb);
|
||||
y0 = 0;
|
||||
y1 = 0;
|
||||
y2 = .5;
|
||||
ks = 0.002*f/55
|
||||
ii = 0;
|
||||
ff = 0;
|
||||
nn = 1;
|
||||
jj = Nb;
|
||||
kk = 1;
|
||||
for i = 1:32000,
|
||||
if ii == 0
|
||||
jj = 1;
|
||||
kk = ff + 1;
|
||||
ii = Pi;
|
||||
ff = ff + Pf;
|
||||
if ff > (Nos-1)
|
||||
ff = ff - Nos;
|
||||
ii = ii + 1;
|
||||
end
|
||||
end;
|
||||
ii = ii - 1;
|
||||
if jj <= Nb
|
||||
y0 = y0 + k(kk)*Hs(kk,jj); % - ks*y0;
|
||||
jj = jj + 1;
|
||||
end;
|
||||
y0 = y0 - ks*y0;
|
||||
y1 = 1 - y0;
|
||||
y2 = 0.001*y1 + 0.999*y2;
|
||||
blit(nn) = y1-y2;
|
||||
nn = nn + 1;
|
||||
end;
|
||||
wavwrite(0.7*blit, fs, 'blit.wav');
|
||||
|
||||
for ii = 1:Nos
|
||||
step(ii,:) = k(ii).*filter(Hs(ii,:), 1, [ones(1,Nb)]);
|
||||
end
|
||||
|
||||
close all
|
||||
plot(1:length(blit), blit, '-o'); grid;
|
||||
|
||||
figure
|
||||
plot(abs(fft(blit))); grid;
|
||||
|
||||
figure
|
||||
plot(step', '-'); grid;
|
||||
|
||||
figure
|
||||
plot(Hs(1,:)', '-+'); grid;
|
||||
|
||||
function steps = init_steps(step_width, phase_count)
|
||||
|
||||
low_pass = 0.999; % lower values filter more high frequency
|
||||
high_pass = 0.990; % lower values filter more low frequency
|
||||
|
||||
%phase_count = 32; % number of phase offsets to sample band-limited step at
|
||||
%step_width = 16; % number of samples in each final band-limited step
|
||||
|
||||
%steps [phase_count] [step_width]; // would use short for speed in a real program
|
||||
|
||||
% Generate master band-limited step by adding sine components of a square wave
|
||||
master_size = step_width * phase_count;
|
||||
% master [master_size]; // large; might want to malloc() instead
|
||||
for i = 0:master_size-1
|
||||
master(i+1) = 0.5;
|
||||
end;
|
||||
|
||||
gain = 0.5 / 0.777; % adjust normal square wave's amplitude of ~0.777 to 0.5
|
||||
sine_size = 256 * phase_count + 2;
|
||||
max_harmonic = sine_size / 2 / phase_count;
|
||||
for h = 1:2:max_harmonic
|
||||
amplitude = gain / h;
|
||||
to_angle = 3.14159265358979323846 * 2 / sine_size * h;
|
||||
for i = 0:master_size-1
|
||||
master(i+1) = master(i+1) + sin( (i - master_size / 2) * to_angle ) * amplitude;
|
||||
end
|
||||
gain = gain * low_pass;
|
||||
end
|
||||
|
||||
% Sample master step at several phases
|
||||
for phase = 0:phase_count-1
|
||||
error = 1.0;
|
||||
prev = 0.0;
|
||||
for i = 0:step_width-1
|
||||
cur = master (i * phase_count + (phase_count - 1 - phase)+1);
|
||||
delta = cur - prev;
|
||||
error = error - delta;
|
||||
prev = cur;
|
||||
steps (phase+1, i+1) = delta;
|
||||
end
|
||||
|
||||
% each delta should total 1.0
|
||||
steps (phase+1, step_width / 2) = steps (phase+1, step_width / 2) + error * 0.5;
|
||||
steps (phase+1, step_width / 2 + 1) = steps (phase+1, step_width / 2 + 1) + error * 0.5;
|
||||
end
|
||||
Executable
+90
@@ -0,0 +1,90 @@
|
||||
function eval_blit()
|
||||
|
||||
sinc_use_lut = 1
|
||||
|
||||
L = 96000;
|
||||
fs = 48000;
|
||||
fstart = 220;
|
||||
fend = 880;
|
||||
df = (fend-fstart)/L
|
||||
|
||||
# for saw
|
||||
rho = 1.0;
|
||||
|
||||
nhw = 4096;
|
||||
Hw = kaiser(nhw, 8)';
|
||||
|
||||
max_m = fix((fs/fstart/2.0)) + 1
|
||||
|
||||
if sinc_use_lut == 1
|
||||
nharm = max_m
|
||||
nsin = 1024;
|
||||
xx = (0:nsin)/nsin - 0.5;
|
||||
mmm=0;
|
||||
for mm=1:nharm,
|
||||
mmm=mmm+1;
|
||||
bb(mm, :) = sin(mmm*xx*pi);
|
||||
end;
|
||||
aa = sin(xx*pi);
|
||||
end;
|
||||
|
||||
x = 0.5;
|
||||
z = -1;
|
||||
f = fstart;
|
||||
sqr = 0.0;
|
||||
saw = 0.0;
|
||||
tri = 0.0;
|
||||
a = 1;
|
||||
b = 1;
|
||||
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 0.5,
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
fraq = 1.0/p;
|
||||
m = fix((p/2.0)) + 1;
|
||||
z = -z;
|
||||
end;
|
||||
if sinc_use_lut == 1
|
||||
nn = (x+0.5)*nsin + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
b = (bb(m, ni+1) - bb(m, ni))*nf + bb(m, ni); % Fractional delay (linear interpolation)
|
||||
a = (aa(ni+1) - aa(ni))*nf + aa(ni); % Fractional delay (linear interpolation)
|
||||
else
|
||||
b = sin(m*x*pi);
|
||||
a = sin(x*pi);
|
||||
end
|
||||
|
||||
if (a ~= 0)
|
||||
blit = fraq * b/a * Hw(fix((x+0.5)*nhw)+1);
|
||||
else
|
||||
blit = fraq;
|
||||
end
|
||||
|
||||
saw = rho*saw + (fraq - blit);
|
||||
sqr = sqr + z*blit;
|
||||
tri = tri + 2*(sqr-0.5)*fraq;
|
||||
vtri(n) = tri;
|
||||
vsaw(n) = saw;
|
||||
vsqr(n) = 2*(sqr - 0.5);
|
||||
vblit(n) = blit;
|
||||
x = x + fraq;
|
||||
f = f + df;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
|
||||
figure;
|
||||
plot(1:L, vsaw, 1:L, vblit); grid;
|
||||
|
||||
N_UNDC = fs/100;
|
||||
|
||||
a, b = undcc(N_UNDC);
|
||||
wavwrite(0.5*filter(b, a, vtri), fs, 'tri.wav');
|
||||
wavwrite(0.5*filter(b, a, vsqr), fs, 'sqr.wav');
|
||||
wavwrite(0.5*filter(b, a, vsaw), fs, 'saw.wav');
|
||||
wavwrite(0.5*vblit, fs, 'blit.wav');
|
||||
|
||||
Executable
+128
@@ -0,0 +1,128 @@
|
||||
function eval_blit2()
|
||||
L = 64000;
|
||||
Nb = 65;
|
||||
Nos = 8;
|
||||
fs = 48000;
|
||||
fstart = 440;
|
||||
fend = 1760;
|
||||
df = (fend-fstart)/L
|
||||
fcut = 16000;
|
||||
|
||||
x = (0:Nb*Nos-1) - (Nb*Nos-1)/2;
|
||||
H = sinc(2*fcut/fs.*x/Nos).*kaiser(Nos*Nb, 8)';
|
||||
for ii=0:Nos-1,
|
||||
for jj=0:Nb-1
|
||||
Hs(ii+1,jj+1) = H(Nos-ii+Nos*jj);
|
||||
end
|
||||
end;
|
||||
|
||||
%Hs = init_steps(Nb, Nos);
|
||||
|
||||
f = fstart;
|
||||
k = 1./sum(Hs')
|
||||
%k = ones(1, Nb);
|
||||
y0 = 0;
|
||||
y1 = 0;
|
||||
y2 = .5;
|
||||
ks = 0.002*f/55
|
||||
ii = 0;
|
||||
ff = 0;
|
||||
nn = 1;
|
||||
jj = Nb;
|
||||
kk = 1;
|
||||
for i = 1:L,
|
||||
P = fs/f;
|
||||
Pi = floor(P);
|
||||
Pf = fix(Nos*(fs/f - Pi));
|
||||
|
||||
if ii == 0
|
||||
jj = 1;
|
||||
kk = ff + 1;
|
||||
ii = Pi;
|
||||
ff = ff + Pf;
|
||||
if ff > (Nos-1)
|
||||
ff = ff - Nos;
|
||||
ii = ii + 1;
|
||||
end
|
||||
end;
|
||||
ii = ii - 1;
|
||||
if jj <= Nb
|
||||
y0 = k(kk)*Hs(kk,jj); % - ks*y0;
|
||||
jj = jj + 1;
|
||||
end;
|
||||
y0 = y0 - ks*y0;
|
||||
y1 = 1 - y0;
|
||||
y2 = 0.001*y1 + 0.999*y2;
|
||||
blit(nn) = y1-y2; % DC removal
|
||||
nn = nn + 1;
|
||||
f = f + df;
|
||||
|
||||
end;
|
||||
wavwrite(0.7*blit, fs, 'blit.wav');
|
||||
|
||||
for ii = 1:Nos
|
||||
step(ii,:) = k(ii).*filter(Hs(ii,:), 1, [ones(1,Nb)]);
|
||||
end
|
||||
|
||||
DC = y2
|
||||
|
||||
close all
|
||||
plot(H); grid;
|
||||
|
||||
figure
|
||||
plot(1:length(blit), blit, '-o'); grid;
|
||||
|
||||
figure
|
||||
plot(abs(fft(blit))); grid;
|
||||
|
||||
figure
|
||||
plot(step', '-'); grid;
|
||||
|
||||
figure
|
||||
plot(Hs(1,:)', '-+'); grid;
|
||||
|
||||
function steps = init_steps(step_width, phase_count)
|
||||
|
||||
low_pass = 0.999; % lower values filter more high frequency
|
||||
high_pass = 0.990; % lower values filter more low frequency
|
||||
|
||||
%phase_count = 32; % number of phase offsets to sample band-limited step at
|
||||
%step_width = 16; % number of samples in each final band-limited step
|
||||
|
||||
%steps [phase_count] [step_width]; // would use short for speed in a real program
|
||||
|
||||
% Generate master band-limited step by adding sine components of a square wave
|
||||
master_size = step_width * phase_count;
|
||||
% master [master_size]; // large; might want to malloc() instead
|
||||
for i = 0:master_size-1
|
||||
master(i+1) = 0.5;
|
||||
end;
|
||||
|
||||
gain = 0.5 / 0.777; % adjust normal square wave's amplitude of ~0.777 to 0.5
|
||||
sine_size = 256 * phase_count + 2;
|
||||
max_harmonic = sine_size / 2 / phase_count;
|
||||
for h = 1:2:max_harmonic
|
||||
amplitude = gain / h;
|
||||
to_angle = 3.14159265358979323846 * 2 / sine_size * h;
|
||||
for i = 0:master_size-1
|
||||
master(i+1) = master(i+1) + sin( (i - master_size / 2) * to_angle ) * amplitude;
|
||||
end
|
||||
gain = gain * low_pass;
|
||||
end
|
||||
|
||||
% Sample master step at several phases
|
||||
for phase = 0:phase_count-1
|
||||
error = 1.0;
|
||||
prev = 0.0;
|
||||
for i = 0:step_width-1
|
||||
cur = master (i * phase_count + (phase_count - 1 - phase)+1);
|
||||
delta = cur - prev;
|
||||
error = error - delta;
|
||||
prev = cur;
|
||||
steps (phase+1, i+1) = delta;
|
||||
end
|
||||
|
||||
% each delta should total 1.0
|
||||
steps (phase+1, step_width / 2) = steps (phase+1, step_width / 2) + error * 0.5;
|
||||
steps (phase+1, step_width / 2 + 1) = steps (phase+1, step_width / 2 + 1) + error * 0.5;
|
||||
end
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
function eval_blit3()
|
||||
L = 64000;
|
||||
Nb = 137;
|
||||
Nphases = 256;
|
||||
fs = 48000;
|
||||
fstart = 110*4;
|
||||
fend = 110*4;
|
||||
df = (fend-fstart)/L;
|
||||
fcut = 16000;
|
||||
|
||||
x = (0:Nb*Nphases-1) - (Nb*Nphases-1)/2;
|
||||
H = sinc(2*fcut/fs.*x/Nphases);%.*kaiser(Nphases*Nb, 8)';
|
||||
Hw = kaiser(Nb, 18)';
|
||||
|
||||
for ii=0:Nphases-1,
|
||||
for jj=0:Nb-1
|
||||
Hs(ii+1,jj+1) = H(Nphases-ii+Nphases*jj);
|
||||
end
|
||||
end;
|
||||
|
||||
|
||||
f = fstart;
|
||||
Pic = 1;
|
||||
blit(1:L) = zeros(1, L);
|
||||
blit_bp(1:L) = zeros(1, L);
|
||||
pol = 1;
|
||||
for n = Nb:L,
|
||||
P = fs/f;
|
||||
Pi = floor(P);
|
||||
Pf = 1+fix(Nphases*(P - Pi));
|
||||
|
||||
if Pic >= Pi,
|
||||
blit(n-Nb+1:n) = blit(n-Nb+1:n) + Hs(Pf,:).*Hw;
|
||||
blit_bp(n-Nb+1:n) = blit_bp(n-Nb+1:n) + pol*Hs(Pf,:).*Hw;
|
||||
Pic = 0;
|
||||
pol = -pol;
|
||||
end;
|
||||
Pic = Pic + 1;
|
||||
f = f + df;
|
||||
end;
|
||||
|
||||
mean_blit = 0.0034;
|
||||
mean_saw = 0.00;
|
||||
|
||||
saw(1:L) = zeros(1, L);
|
||||
sqr(1:L) = zeros(1, L);
|
||||
y_saw = 0;
|
||||
y_sqr = 0;
|
||||
for n = 2:L,
|
||||
y_saw = y_saw + blit(n)-mean_blit;
|
||||
y_sqr = y_sqr + blit_bp(n);
|
||||
saw(n) = -y_saw -1;
|
||||
sqr(n) = y_sqr;
|
||||
end;
|
||||
|
||||
saw_nodc = filter([1 -1], [1 -0.995], saw);
|
||||
sqr_nodc = filter([1 -1], [1 -0.995], sqr);
|
||||
|
||||
close all;
|
||||
plot(saw); grid;
|
||||
|
||||
figure;
|
||||
plot(saw_nodc); grid;
|
||||
|
||||
wavwrite(0.8*blit, fs, 'blit.wav');
|
||||
wavwrite(0.7*saw_nodc, fs, 'saw.wav');
|
||||
wavwrite(0.5*sqr_nodc, fs, 'sqr.wav');
|
||||
|
||||
Executable
+92
@@ -0,0 +1,92 @@
|
||||
function eval_blit()
|
||||
|
||||
sinc_use_lut = 0
|
||||
|
||||
L = 40000;
|
||||
fs = 48000;
|
||||
fstart = 110;
|
||||
fend = 1550;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
nhw = 4096;
|
||||
Hw = kaiser(nhw, 8)';
|
||||
|
||||
ya = zeros(3,1);
|
||||
yb = zeros(3,1);
|
||||
|
||||
x = 0.5;
|
||||
z = -1;
|
||||
f = fstart;
|
||||
sqr = 0.0;
|
||||
c4 = 0;
|
||||
for n = 1:L,
|
||||
|
||||
|
||||
if x >= 0.5,
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
fraq = 1.0/p;
|
||||
m = fix((p/2.0)) + 1;
|
||||
saw = 0.0;
|
||||
c3 = 0.0;
|
||||
z = -z;
|
||||
ip = 0; % phase of the first output sample in radians
|
||||
w = f*pi / fs
|
||||
ba1 = 2.0 * cos(w)
|
||||
bb1 = 2.0 * cos(w)
|
||||
|
||||
end;
|
||||
|
||||
if sinc_use_lut == 1
|
||||
xi = fix((x+0.5)*nsin)+1
|
||||
if aa(xi) ~= 0
|
||||
blit = fraq*sinc_m(m, xi) * Hw(fix((x+0.5)*nhw)+1);;
|
||||
else
|
||||
blit = fraq;
|
||||
end
|
||||
else
|
||||
b = fraq*sin(m*x*pi);
|
||||
a = sin(x*pi);
|
||||
if (a ~= 0)
|
||||
blit = b/a * Hw(fix((x+0.5)*nhw)+1);
|
||||
else
|
||||
blit = fraq;
|
||||
end
|
||||
end
|
||||
|
||||
saw = saw + blit;
|
||||
sqr = sqr + z*blit;
|
||||
vsaw(n) = 2*(saw - c3);
|
||||
vsqr(n) = 2*(sqr - 0.5);
|
||||
vblit(n) = blit;
|
||||
vc3(n) = c3;
|
||||
x = x + fraq;
|
||||
c3 = c3 + fraq;
|
||||
f = f + df;
|
||||
c4 = c4 + fraq - blit;
|
||||
vc4(n) = c4;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
|
||||
figure;
|
||||
plot(1:L, vsaw, 1:L, vc4, 1:L, vblit); grid;
|
||||
|
||||
figure;
|
||||
plot(vblit); grid;
|
||||
|
||||
figure;
|
||||
plot(vc3); grid;
|
||||
|
||||
if sinc_use_lut == 1
|
||||
figure;
|
||||
plot(sinc_m(nharm, :)); grid;
|
||||
end;
|
||||
|
||||
wavwrite(0.5*vc4, fs, 'c4.wav');
|
||||
wavwrite(0.5*vc3, fs, 'c3.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblit, fs, 'blit.wav');
|
||||
|
||||
Executable
+88
@@ -0,0 +1,88 @@
|
||||
function eval_blit4()
|
||||
|
||||
L = 4000;
|
||||
fs = 48000;
|
||||
fstart = 110;
|
||||
fend = 110;
|
||||
df = (fend-fstart)/L;
|
||||
|
||||
nhw = 4096;
|
||||
Hw = kaiser(nhw, 8)';
|
||||
|
||||
ya = zeros(3,1);
|
||||
yb = zeros(3,1);
|
||||
|
||||
x = 0.5;
|
||||
z = -1;
|
||||
f = fstart;
|
||||
sqr = 0.0;
|
||||
c4 = 0;
|
||||
|
||||
for n = 1:L,
|
||||
|
||||
|
||||
if x >= 0.5,
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
fraq = 1.0/p;
|
||||
m = fix((p/2.0)) + 1
|
||||
saw = 0.0;
|
||||
c3 = 0.0;
|
||||
z = -z;
|
||||
ip = -pi/2; % phase of the first output sample in radians
|
||||
w = f*pi / fs;
|
||||
ba1 = 2.0 * cos(w);
|
||||
bb1 = 2.0 * cos(m*w);
|
||||
ya(2)=sin(ip-w);
|
||||
ya(3)=sin(ip-2*w);
|
||||
yb(2)=sin(ip-m*w);
|
||||
yb(3)=sin(ip-2*m*w);
|
||||
end;
|
||||
|
||||
ya(1) = ba1*ya(2) - ya(3);
|
||||
ya(3) = ya(2);
|
||||
ya(2) = ya(1);
|
||||
|
||||
yb(1) = bb1*yb(2) - yb(3);
|
||||
yb(3) = yb(2);
|
||||
yb(2) = yb(1);
|
||||
|
||||
b = fraq*yb(1);
|
||||
a = ya(1);
|
||||
if (a ~= 0)
|
||||
blit = b/a * Hw(fix((x+0.5)*nhw)+1);
|
||||
else
|
||||
blit = fraq;
|
||||
end
|
||||
|
||||
saw = saw + blit;
|
||||
sqr = sqr + z*blit;
|
||||
vsaw(n) = 2*(saw - c3);
|
||||
vsqr(n) = 2*(sqr - 0.5);
|
||||
vblit(n) = blit;
|
||||
vc3(n) = b;
|
||||
x = x + fraq;
|
||||
c3 = c3 + fraq;
|
||||
f = f + df;
|
||||
c4 = c4 + fraq - blit;
|
||||
vc4(n) = a;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
|
||||
figure;
|
||||
plot(1:L, vsaw, 1:L, vc4, 1:L, vblit); grid;
|
||||
|
||||
figure;
|
||||
plot(vc4); grid;
|
||||
|
||||
figure;
|
||||
plot(vc3); grid;
|
||||
|
||||
wavwrite(0.5*vc4, fs, 'c4.wav');
|
||||
wavwrite(0.5*vc3, fs, 'c3.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblit, fs, 'blit.wav');
|
||||
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
function eval_hardsync()
|
||||
|
||||
nsin = 4096;
|
||||
nharm_max = 1800;
|
||||
L = 48000/2;
|
||||
fs = 48000;
|
||||
fstart = 440;
|
||||
fend = 440;
|
||||
df = (fend-fstart)/L
|
||||
f2start = 400;
|
||||
f2end = 400;
|
||||
df2 = (f2end-f2start)/L
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/min(fstart, f2start)/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
|
||||
for mm=1:nharm
|
||||
blitm(mm,:) = sin(xx*(mm-1)*pi)./sin(xx*pi).*Hw';
|
||||
blitm(mm,(find(isnan(blitm(mm,:))))) = (mm-1);
|
||||
end;
|
||||
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
x = 0.0;
|
||||
x2 = 0.0;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
f2 = f2start;
|
||||
tri = 0;
|
||||
saw = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
startup = 1;
|
||||
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 1 || startup;
|
||||
x = 0;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
blep_offset = 0;
|
||||
blep_gain = 1;
|
||||
z = ~z;
|
||||
end;
|
||||
if x2 >= 1 || startup,
|
||||
x2 =0;
|
||||
p2 = fs/f2;
|
||||
dx2 = 1.0/p2;
|
||||
m2 = min(fix((p2/2.0)) + 1, nharm_max);
|
||||
% blep_offset = -(1-(x+0.5));
|
||||
% blep_gain = x+0.5;
|
||||
% x = 0;
|
||||
% z = ~z;
|
||||
end;
|
||||
startup = 0;
|
||||
|
||||
% BLEP 1
|
||||
nn = (x+0.0)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
h = lagrange(NLG, nf);
|
||||
blep = h(NLG+1)*blepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*blepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
|
||||
% BLEP 2
|
||||
nn = (x2+0.0)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
h = lagrange(NLG, nf);
|
||||
blep2 = h(NLG+1)*blepm(m2, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep2 = blep2 + h(i)*blepm(m2, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
|
||||
saw = x - blep;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n,1) = sqr;
|
||||
vtri(n,2) = tri;
|
||||
vsaw(n,1) = x;
|
||||
vsaw(n,2) = tri;
|
||||
vblep(n,1) = x;
|
||||
vblep(n,2) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
x2 = x2 + dx2;
|
||||
f2 = f2 + df2;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+107
@@ -0,0 +1,107 @@
|
||||
function eval_hardsync()
|
||||
|
||||
nsin = 4096;
|
||||
nharm_max = 1800;
|
||||
L = 48000/2;
|
||||
fs = 48000;
|
||||
fstart = 440;
|
||||
fend = 440;
|
||||
df = (fend-fstart)/L
|
||||
f2start = 200;
|
||||
f2end = 600;
|
||||
df2 = (f2end-f2start)/L
|
||||
|
||||
% Calc blep table
|
||||
nharm = min(fix((fs/fstart/2.0)) + 1, nharm_max)
|
||||
Hw = kaiser(nsin, 8);
|
||||
xx = (0:nsin-1)/nsin - 0.5;
|
||||
|
||||
for mm=1:nharm
|
||||
blitm(mm,:) = sin(xx*(mm-1)*pi)./sin(xx*pi).*Hw';
|
||||
blitm(mm,(find(isnan(blitm(mm,:))))) = (mm-1);
|
||||
end;
|
||||
|
||||
for mm=1:nharm
|
||||
blepm(mm, :) = cumsum(blitm(mm, :))/nsin;
|
||||
end;
|
||||
|
||||
x = 0.0;
|
||||
x2 = 0.0;
|
||||
z = 0;
|
||||
f = fstart;
|
||||
f2 = f2start;
|
||||
tri = 0;
|
||||
NLG = 2;
|
||||
h = lagrange(1, 0.35)
|
||||
startup = 1;
|
||||
|
||||
for n = 1:L,
|
||||
|
||||
if x >= 1 || startup;
|
||||
x = x - 1;
|
||||
p = fs/f;
|
||||
dx = 1.0/p;
|
||||
m = min(fix((p/2.0)) + 1, nharm_max);
|
||||
z = ~z;
|
||||
end;
|
||||
if x2 >= 1 || startup,
|
||||
x2 =0;
|
||||
p2 = fs/f2;
|
||||
dx2 = 1.0/p2;
|
||||
m2 = min(fix((p2/2.0)) + 1, nharm_max);
|
||||
% blep_offset = -(1-x);
|
||||
% blep_gain = x;
|
||||
% x = 0;
|
||||
% z = ~z;
|
||||
end;
|
||||
startup = 0;
|
||||
nn = (x+0.0)*(nsin-1) + 1;
|
||||
ni = fix(nn);
|
||||
nf = nn - ni;
|
||||
|
||||
h = lagrange(NLG, nf);
|
||||
blep = h(NLG+1)*blepm(m, max(1, ni));
|
||||
j = 1;
|
||||
for i = NLG:-1:1
|
||||
blep = blep + h(i)*blepm(m, max(1, ni-j));
|
||||
j = j + 1;
|
||||
end;
|
||||
saw = x - blep;
|
||||
if (z == 0)
|
||||
sqr = blep;
|
||||
else
|
||||
sqr = 1-blep;
|
||||
end
|
||||
vsqr(n) = 0.5*(sqr-0.5);
|
||||
tri = tri + 2*(sqr-0.5)*dx;
|
||||
vtri(n) = tri;
|
||||
vsaw(n,1) = x;
|
||||
vsaw(n,2) = saw+0.5;
|
||||
vblep(n,1) = x;
|
||||
vblep(n,2) = blep;
|
||||
x = x + dx;
|
||||
f = f + df;
|
||||
x2 = x2 + dx2;
|
||||
f2 = f2 + df2;
|
||||
end;
|
||||
|
||||
|
||||
close all
|
||||
plot(1:nsin, blepm(fix(nharm/2), :)); grid;
|
||||
|
||||
wavwrite(0.5*vtri, fs, 'tri.wav');
|
||||
wavwrite(0.5*vsqr, fs, 'sqr.wav');
|
||||
wavwrite(0.5*vsaw, fs, 'saw.wav');
|
||||
wavwrite(0.5*vblep, fs, 'blep.wav');
|
||||
|
||||
function h = lagrange(N, delay)
|
||||
%LAGRANGE h=lagrange(N,delay) returns order N FIR
|
||||
% filter h which implements given delay
|
||||
% (in samples). For best results,
|
||||
% delay should be near N/2 +/- 1.
|
||||
n = 0:N;
|
||||
h = ones(1,N+1);
|
||||
for k = 0:N
|
||||
index = find(n ~= k);
|
||||
h(index) = h(index) * (delay-k)./ (n(index)-k);
|
||||
end
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
function eval_harmonics(name)
|
||||
|
||||
base = exp(1);
|
||||
ke = 10;
|
||||
|
||||
[x, fs, nbits] = wavread(name);
|
||||
|
||||
y = (1 - base.^(-ke*fabs(x))) * sign(x);
|
||||
|
||||
|
||||
wavrite(y, fs, nbits,
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
function eval_harmonics(name)
|
||||
|
||||
base = exp(1);
|
||||
ke = 2;
|
||||
|
||||
[x, fs, nbits] = wavread(name);
|
||||
|
||||
y = (1 - base.^(-ke*abs(x))) .* sign(x);
|
||||
|
||||
yname = sprintf('out.wav', name);
|
||||
wavwrite(y*0.8, fs, nbits, yname);
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
+66
@@ -0,0 +1,66 @@
|
||||
function paramscale(base, kexp, scenter, pcenter, pmin, pmax)
|
||||
|
||||
N = 1000;
|
||||
slider = (0:N)/N;
|
||||
|
||||
p = toParam(base, kexp, scenter, pcenter, pmin, pmax, slider);
|
||||
s = toSlider(base, kexp, scenter, pcenter, pmin, pmax, p);
|
||||
|
||||
subplot (2, 1, 1)
|
||||
plot (0:N, p); grid; xlabel('param');
|
||||
subplot (2, 1, 2)
|
||||
plot (0:N, s); grid; xlabel('slider');
|
||||
|
||||
function param = toParam(base, kexp, scenter, pcenter, pmin, pmax, slider)
|
||||
|
||||
% pcenter = pmax*scenter + pmin*(1-scenter)
|
||||
|
||||
for i=1:length(slider),
|
||||
s = min(1, max(0, slider(i)));
|
||||
|
||||
if (s < scenter)
|
||||
if (base == 1)
|
||||
p = pcenter-(pcenter-pmin)*(scenter-s)/scenter;
|
||||
% p = pcenter-(pcenter-pmin)*(scenter-s)/scenter;
|
||||
else
|
||||
p = pcenter-(pcenter-pmin)*(base^(kexp*(scenter-s)/scenter)-1)/(base^kexp-1);
|
||||
% p = pcenter-(pcenter-pmin)*(pow(base,kexp*(scenter-s)/scenter)-1)/(pow(base,kexp)-1);
|
||||
end
|
||||
else
|
||||
if (base == 1)
|
||||
p = pcenter+(pmax-pcenter)*(-scenter+s)/(1-scenter);
|
||||
% p = pcenter+(pmax-pcenter)*(-scenter+s)/(1-scenter);
|
||||
else
|
||||
p = pcenter+(pmax-pcenter)*(base^(kexp*(-scenter+s)/(1-scenter))-1)/(base^kexp-1);
|
||||
% p = pcenter+(pmax-pcenter)*(pow(base,kexp*(-scenter+s)/(1-scenter))-1)/(pow(base,kexp)-1);
|
||||
end;
|
||||
end
|
||||
param(i) = p;
|
||||
end
|
||||
|
||||
function slider = toSlider(base, kexp, scenter, pcenter, pmin, pmax, param)
|
||||
|
||||
%pcenter = pmax*scenter + pmin*(1-scenter);
|
||||
|
||||
for i=1:length(param),
|
||||
p = param(i);
|
||||
|
||||
if (p < pcenter)
|
||||
if (base == 1)
|
||||
s = (-pmin+p)*scenter/(pcenter-pmin);
|
||||
% s = (-pmin+p)*scenter/(pcenter-pmin);
|
||||
else
|
||||
s = scenter*(log(base)*kexp-log(-(-pcenter*base^kexp+pmin+p*base^kexp-p)/(pcenter-pmin)))/log(base)/kexp;
|
||||
% s = scenter*(log(base)*kexp-log(-(-pcenter*pow(base,kexp)+pmin+p*pow(base,kexp)-p)/(pcenter-pmin)))/log(base)/kexp;
|
||||
end
|
||||
else
|
||||
if (base == 1)
|
||||
s = (pcenter-scenter*pmax-p+scenter*p)/(-pmax+pcenter);
|
||||
% s = (pcenter-scenter*pmax-p+scenter*p)/(-pmax+pcenter);
|
||||
else
|
||||
s = (kexp*log(base)*scenter+log(-(-pcenter*base^kexp+pmax+p*base^kexp-p)/(-pmax+pcenter))-log(-(-pcenter*base^kexp+pmax+p*base^kexp-p)/(-pmax+pcenter))*scenter)/log(base)/kexp;
|
||||
% s = (kexp*log(base)*scenter+log(-(-pcenter*pow(base,kexp)+pmax+p*pow(base,kexp)-p)/(-pmax+pcenter))-log(-(-pcenter*pow(base,kexp)+pmax+p*pow(base,kexp)-p)/(-pmax+pcenter))*scenter)/log(base)/kexp;
|
||||
end
|
||||
end
|
||||
slider(i) = s;
|
||||
end
|
||||
Executable
+13
@@ -0,0 +1,13 @@
|
||||
function paramscale(base, kexp, scenter, pcenter, pmin, pmax)
|
||||
|
||||
N = 1000;
|
||||
slider = (0:N)/N;
|
||||
|
||||
p = toParam(base, kexp, scenter, pcenter, pmin, pmax, slider);
|
||||
s = toSlider(base, kexp, scenter, pcenter, pmin, pmax, p);
|
||||
|
||||
subplot (2, 1, 1)
|
||||
plot (slider, p); grid; xlabel('toParam(s)');
|
||||
subplot (2, 1, 2)
|
||||
plot (slider, s); grid; xlabel('toSlider(p)');
|
||||
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
function param = toParam(base, kexp, scenter, pcenter, pmin, pmax, slider)
|
||||
|
||||
if ((pcenter < pmin) || (pcenter > pmax))
|
||||
pcenter = pmax*scenter + pmin*(1-scenter);
|
||||
end
|
||||
|
||||
for i=1:length(slider),
|
||||
s = min(1, max(0, slider(i)));
|
||||
|
||||
if (s < scenter)
|
||||
if (base == 1)
|
||||
p = pcenter-(pcenter-pmin)*(scenter-s)/scenter;
|
||||
% p = pcenter-(pcenter-pmin)*(scenter-s)/scenter;
|
||||
else
|
||||
p = pcenter-(pcenter-pmin)*(base^(kexp*(scenter-s)/scenter)-1)/(base^kexp-1);
|
||||
% p = pcenter-(pcenter-pmin)*(pow(base,kexp*(scenter-s)/scenter)-1)/(pow(base,kexp)-1);
|
||||
end
|
||||
else
|
||||
if (base == 1)
|
||||
p = pcenter+(pmax-pcenter)*(-scenter+s)/(1-scenter);
|
||||
% p = pcenter+(pmax-pcenter)*(-scenter+s)/(1-scenter);
|
||||
else
|
||||
p = pcenter+(pmax-pcenter)*(base^(kexp*(-scenter+s)/(1-scenter))-1)/(base^kexp-1);
|
||||
% p = pcenter+(pmax-pcenter)*(pow(base,kexp*(-scenter+s)/(1-scenter))-1)/(pow(base,kexp)-1);
|
||||
end;
|
||||
end
|
||||
param(i) = p;
|
||||
end
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
function slider = toSlider(base, kexp, scenter, pcenter, pmin, pmax, param)
|
||||
|
||||
if ((pcenter < pmin) || (pcenter > pmax))
|
||||
pcenter = pmax*scenter + pmin*(1-scenter);
|
||||
end
|
||||
|
||||
for i=1:length(param),
|
||||
p = param(i);
|
||||
|
||||
if (p < pcenter)
|
||||
if (base == 1)
|
||||
s = (-pmin+p)*scenter/(pcenter-pmin);
|
||||
% s = (-pmin+p)*scenter/(pcenter-pmin);
|
||||
else
|
||||
s = scenter*(log(base)*kexp-log(-(-pcenter*base^kexp+pmin+p*base^kexp-p)/(pcenter-pmin)))/log(base)/kexp;
|
||||
% s = scenter*(log(base)*kexp-log(-(-pcenter*pow(base,kexp)+pmin+p*pow(base,kexp)-p)/(pcenter-pmin)))/log(base)/kexp;
|
||||
end
|
||||
else
|
||||
if (base == 1)
|
||||
s = (pcenter-scenter*pmax-p+scenter*p)/(-pmax+pcenter);
|
||||
% s = (pcenter-scenter*pmax-p+scenter*p)/(-pmax+pcenter);
|
||||
else
|
||||
s = (kexp*log(base)*scenter+log(-(-pcenter*base^kexp+pmax+p*base^kexp-p)/(-pmax+pcenter))-log(-(-pcenter*base^kexp+pmax+p*base^kexp-p)/(-pmax+pcenter))*scenter)/log(base)/kexp;
|
||||
% s = (kexp*log(base)*scenter+log(-(-pcenter*pow(base,kexp)+pmax+p*pow(base,kexp)-p)/(-pmax+pcenter))-log(-(-pcenter*pow(base,kexp)+pmax+p*pow(base,kexp)-p)/(-pmax+pcenter))*scenter)/log(base)/kexp;
|
||||
end
|
||||
end
|
||||
slider(i) = s;
|
||||
end
|
||||
@@ -0,0 +1,3 @@
|
||||
build
|
||||
JUCE-3.1.1
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ DEFINES += -D${TARGET}
|
||||
|
||||
${OBJS}: $(BUILD_DIR)/%.o : ${PKG_ROOT}/%.cpp
|
||||
mkdir -p $(dir $@)
|
||||
g++ ${CXXFLAGS_${CONFIG}} ${DEFINES} ${INCLUDES} -o $@ -c $<
|
||||
$(CXX) ${CXXFLAGS_${CONFIG}} ${DEFINES} ${INCLUDES} -o $@ -c $<
|
||||
|
||||
${BUILD_DIR}/${BUILD_TARGET}: ${OBJS}
|
||||
ar -q $@ ${OBJS}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
vstsdk2.4
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
CONFIG ?= release
|
||||
PKG_VER := vst_sdk2_4_rev2
|
||||
PKG_VER := vstsdk2.4
|
||||
|
||||
include pkg.mk
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
diff -ruN temp/vstsdk2.4/public.sdk/source/vst2.x/audioeffect.cpp ./vstsdk2.4/public.sdk/source/vst2.x/audioeffect.cpp
|
||||
--- temp/vstsdk2.4/public.sdk/source/vst2.x/audioeffect.cpp 2006-06-07 09:22:02.000000000 +0200
|
||||
+++ ./vstsdk2.4/public.sdk/source/vst2.x/audioeffect.cpp 2025-08-02 10:01:34.082302630 +0200
|
||||
@@ -7,7 +7,7 @@
|
||||
// Created by : Steinberg Media Technologies
|
||||
// Description : Class AudioEffect (VST 1.0)
|
||||
//
|
||||
-// © 2006, Steinberg Media Technologies, All Rights Reserved
|
||||
+// � 2006, Steinberg Media Technologies, All Rights Reserved
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
|
||||
#include "audioeffect.h"
|
||||
@@ -507,9 +507,10 @@
|
||||
{
|
||||
VstInt32 digit = value / div;
|
||||
value -= digit * div;
|
||||
+ const char digit8 = (char)(digit & 0xFF) + '0';
|
||||
if (state || digit > 0)
|
||||
{
|
||||
- char temp[2] = {'0' + (char)digit, '\0'};
|
||||
+ char temp[2] = {digit8, '\0'};
|
||||
vst_strncat (text, temp, maxLen);
|
||||
state = true;
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
#define __JUCER_HEADER_BUTTONLED_BUTTONLED_B8510B75__
|
||||
|
||||
//[Headers] -- You can add your own extra header files here --
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include <JuceHeader.h>
|
||||
//[/Headers]
|
||||
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
|
||||
//#######################################################################################
|
||||
//Class to insert Midiclock messages into a given MidiBuffer, suitable for block
|
||||
//based audio callbacks. The class can act on position jumps e.g. "Loops" by sending a
|
||||
//positioning message.
|
||||
//NOTE: No ballistik came in to play so I wouldn't recommend using it to drive a mechanical
|
||||
//tape deck!!!
|
||||
//
|
||||
//solar3d-software, April- 10- 2012
|
||||
//#######################################################################################
|
||||
|
||||
*/
|
||||
#include "JK_MidiClock.h"
|
||||
#include "juce_core/maths/juce_MathsFunctions.h"
|
||||
#include <cmath>
|
||||
#include <synth/synth_defs.h>
|
||||
|
||||
|
||||
JK_MidiClock::JK_MidiClock()
|
||||
:m_wasPlaying(false),
|
||||
m_syncPpqPosition(-999.0),
|
||||
m_posChangeThreshold(0.001),
|
||||
m_ppqToStartSyncAt(0.0),
|
||||
m_followSongPosition(true),
|
||||
m_syncFlag(0),
|
||||
m_ppqOffset(0)
|
||||
{
|
||||
//Prepare Midi messages to avoid blocking the caller of generateMidiclock()!
|
||||
m_continueMessage = new MidiMessage(MidiMessage::midiContinue());
|
||||
m_stopMessage = new MidiMessage(MidiMessage::midiStop());
|
||||
m_clockMessage = new MidiMessage(MidiMessage::midiClock());
|
||||
m_songPositionMessage = new MidiMessage(MidiMessage::songPositionPointer(0));
|
||||
m_sample_remain = 0;
|
||||
}
|
||||
//=================================================================================================
|
||||
void JK_MidiClock::generateMidiclock(const AudioPlayHead::CurrentPositionInfo &posInfo,
|
||||
MidiBuffer* midiBuffer, const int bufferSize, const double sampleRate)
|
||||
{
|
||||
//###################################Some explanation about musical tempo #################
|
||||
//A Time Signature, is two numbers, one on top of the other. The numerator describes the #
|
||||
//number of Beats in a Bar, while the denominator describes of what note value a Beat is. #
|
||||
//So 4/4 would be four quarter-notes per Bar, while 4/2 would be four half-notes per Bar, #
|
||||
//4/8 would be four eighth-notes per Bar, and 2/4 would be two quarter-notes per Bar. #
|
||||
//#########################################################################################
|
||||
|
||||
if (midiBuffer == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//PPQ value of one sample
|
||||
const double ppqPerSample = (posInfo.bpm / 60.0) / sampleRate;
|
||||
|
||||
//PPQ offset to compensate Midi interface latency
|
||||
double hostPpqPosition = posInfo.ppqPosition + m_ppqOffset * ppqPerSample;
|
||||
|
||||
const double clockDistanceInSamples_f = (60.0 * sampleRate) / (posInfo.bpm * 24.0);
|
||||
const int clockDistanceInSamples = roundToInt(clockDistanceInSamples_f);
|
||||
|
||||
const double quant = (60.0 / posInfo.bpm) * sampleRate;
|
||||
|
||||
if (posInfo.isPlaying || posInfo.isRecording)
|
||||
{
|
||||
if (! m_wasPlaying)
|
||||
{
|
||||
//set the point where to start the slave
|
||||
m_ppqToStartSyncAt = getNearestSixteenthInPPQ(hostPpqPosition);
|
||||
SynthDebug("m_ppqToStartSyncAt %f", m_ppqToStartSyncAt);
|
||||
|
||||
//Special case: Master is set to always start playback from the previous start position...
|
||||
if (positionJumped(m_syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
||||
{
|
||||
//Cue Midiclock slave to the nearest sixteenth note to new start position
|
||||
//because the one calculated in stop mode isn't valid anymore.
|
||||
sendSongPositionPointerMessage(m_ppqToStartSyncAt, 0, midiBuffer);
|
||||
SynthDebug("m_ppqToStartSyncAt %f", m_ppqToStartSyncAt);
|
||||
}
|
||||
m_sample_remain = clockDistanceInSamples;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//Position jump (loop or manually position change while playing)
|
||||
if (positionJumped(m_syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
||||
{
|
||||
SynthDebug("positionJumped");
|
||||
//set the point where to start the slave
|
||||
m_ppqToStartSyncAt = getNearestSixteenthInPPQ(hostPpqPosition);
|
||||
|
||||
//User has changed position manually while playing
|
||||
if (m_syncFlag == 0)
|
||||
{
|
||||
SynthDebug("Sync=0");
|
||||
midiBuffer->addEvent(*m_stopMessage, 0);
|
||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
||||
|
||||
m_syncFlag = startSlave_;
|
||||
}
|
||||
else
|
||||
{
|
||||
SynthDebug("Sync=1");
|
||||
if (m_followSongPosition)
|
||||
{
|
||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
||||
|
||||
m_syncFlag = startSlave_;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_syncFlag = 0;
|
||||
}
|
||||
}
|
||||
m_sample_remain = clockDistanceInSamples;
|
||||
}
|
||||
}
|
||||
|
||||
for (int posInBuffer = 0; posInBuffer < bufferSize; ++posInBuffer)
|
||||
{
|
||||
m_syncPpqPosition = hostPpqPosition + (posInBuffer * ppqPerSample);
|
||||
|
||||
//Some hosts like Cubase come up with a wacky ppqPosition
|
||||
//that could break the timing! Best is to "wait"
|
||||
//here for the right ppqPosition to jump on.
|
||||
if (m_syncPpqPosition >= m_ppqToStartSyncAt)
|
||||
{
|
||||
if ((m_syncFlag & startSlave_) == startSlave_)
|
||||
{
|
||||
midiBuffer->addEvent(*m_continueMessage, posInBuffer);
|
||||
|
||||
m_syncFlag &= cycleEnd_;
|
||||
}
|
||||
|
||||
//Cycle mode on
|
||||
if (posInfo.isLooping && posInfo.ppqLoopStart != posInfo.ppqLoopEnd)
|
||||
{
|
||||
const double ppqToCycleEnd = fabs(posInfo.ppqLoopEnd - m_syncPpqPosition);
|
||||
const int64 samplesToCycleEnd = roundToInt64(ppqToCycleEnd * (60.0 / posInfo.bpm) * sampleRate);
|
||||
|
||||
if ((m_syncFlag & cycleEnd_) == 0)
|
||||
{
|
||||
if (samplesToCycleEnd <= clockDistanceInSamples) //For fine tuning tweak here
|
||||
{
|
||||
//We have reached the cycle- end position
|
||||
//and must stop the Midiclock slave here
|
||||
if (m_followSongPosition)
|
||||
midiBuffer->addEvent(*m_stopMessage, posInBuffer);
|
||||
|
||||
m_syncFlag |= cycleEnd_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//For best timing we should never interupt Midiclock messages!
|
||||
//Seems that some slaves constantly adjusting their internal clock
|
||||
//to Midiclock even if they are in stop mode.
|
||||
int buffer_remain = bufferSize;
|
||||
int posInBuffer = 0;
|
||||
int buffer_consume = 0;
|
||||
while(buffer_remain)
|
||||
{
|
||||
if (m_sample_remain > buffer_remain)
|
||||
{
|
||||
m_sample_remain -= buffer_remain;
|
||||
buffer_consume = buffer_remain;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_consume = m_sample_remain;
|
||||
m_sample_remain = 0;
|
||||
}
|
||||
|
||||
buffer_remain -= buffer_consume;
|
||||
posInBuffer += buffer_consume;
|
||||
|
||||
if (buffer_remain == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_sample_remain == 0)
|
||||
{
|
||||
m_sample_remain = clockDistanceInSamples;
|
||||
#if 0
|
||||
SynthDebug("posInBuffer : %d", posInBuffer);
|
||||
SynthDebug("clockDistanceInSamples_f (soll) : %f", clockDistanceInSamples_f);
|
||||
SynthDebug("hostPpqPosition : %f", hostPpqPosition);
|
||||
SynthDebug("hostPpqPosition (Q) : %f", getNearestInPPQ(hostPpqPosition, 96));
|
||||
SynthDebug("hostSamplePos (Q) : %f", getNearestInPPQ(hostPpqPosition, 96) * quant);
|
||||
#endif
|
||||
midiBuffer->addEvent(*m_clockMessage, roundToInt(posInBuffer));
|
||||
}
|
||||
}
|
||||
|
||||
m_wasPlaying = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Send positioning message if the user has stopped or if he changed the playhead position
|
||||
//manually in stop mode! This will also initially cue slave after loading plugin instance.
|
||||
if (m_wasPlaying || positionJumped(m_syncPpqPosition, hostPpqPosition, sampleRate, ppqPerSample))
|
||||
{
|
||||
midiBuffer->addEvent(*m_stopMessage, 0);
|
||||
sendSongPositionPointerMessage(hostPpqPosition, 0, midiBuffer);
|
||||
}
|
||||
|
||||
m_syncPpqPosition = hostPpqPosition;
|
||||
m_syncFlag = startSlave_;
|
||||
m_wasPlaying = false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
bool JK_MidiClock::positionJumped(const double lastPosInPPQ, const double currentPosInPPQ,
|
||||
const double sampleRate, const double ppqPerSample)
|
||||
{
|
||||
//This returns true if the user has changed the playhead position manually or if
|
||||
//a jump has occured! The comperator's default threshold is lastPosInPPQ +- 10ms.
|
||||
if (currentPosInPPQ < lastPosInPPQ - ((m_posChangeThreshold * sampleRate) * ppqPerSample) ||
|
||||
currentPosInPPQ > lastPosInPPQ + ((m_posChangeThreshold * sampleRate) * ppqPerSample))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
//=================================================================================================
|
||||
void JK_MidiClock::sendSongPositionPointerMessage(const double ppqPosition, const int posInBuffer, MidiBuffer* buffer)
|
||||
{
|
||||
//This will cue the slave to the NEAREST
|
||||
//16th note to the given ppqPosition.
|
||||
int intBeat = int(ceil(ppqPosition * 4));
|
||||
|
||||
uint8* pSongPositionTime((uint8*)(m_songPositionMessage->getRawData()));
|
||||
|
||||
*(pSongPositionTime + 1) = (uint8)(intBeat & 0x7f);
|
||||
*(pSongPositionTime + 2) = (uint8)((intBeat & 0x3f80)>>7);
|
||||
|
||||
buffer->addEvent(*m_songPositionMessage, posInBuffer);
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
#ifndef __JK_MIDICLOCK
|
||||
#define __JK_MIDICLOCK
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include <JuceHeader.h>
|
||||
|
||||
//#include "Includes.h"
|
||||
|
||||
@@ -23,14 +23,14 @@ class JK_MidiClock
|
||||
public:
|
||||
JK_MidiClock();
|
||||
|
||||
void setPositionJumpThreshold(const double ms) {posChangeThreshold = ms / 1000.0;}
|
||||
double getPositionJumpThreshold() {return posChangeThreshold;}
|
||||
void setFollowSongPosition(const bool shouldFollow) {followSongPosition = shouldFollow;}
|
||||
bool getFollowSongPosition() {return followSongPosition;}
|
||||
void setOffset(const int offset) {ppqOffset = offset;}
|
||||
int getOffset() {return ppqOffset;}
|
||||
void setPositionJumpThreshold(const double ms) {m_posChangeThreshold = ms / 1000.0;}
|
||||
double getPositionJumpThreshold() {return m_posChangeThreshold;}
|
||||
void setFollowSongPosition(const bool shouldFollow) {m_followSongPosition = shouldFollow;}
|
||||
bool getFollowSongPosition() {return m_followSongPosition;}
|
||||
void setOffset(const int offset) {m_ppqOffset = offset;}
|
||||
int getOffset() {return m_ppqOffset;}
|
||||
|
||||
void generateMidiclock(AudioPlayHead::CurrentPositionInfo &lastPosInfo,
|
||||
void generateMidiclock(const AudioPlayHead::CurrentPositionInfo &lastPosInfo,
|
||||
MidiBuffer* midiBuffer,
|
||||
const int bufferSize,
|
||||
const double sampleRate);
|
||||
@@ -45,21 +45,21 @@ private:
|
||||
}
|
||||
|
||||
|
||||
bool wasPlaying;
|
||||
double syncPpqPosition;
|
||||
double posChangeThreshold;
|
||||
double ppqToStartSyncAt;
|
||||
bool followSongPosition;
|
||||
uint8 syncFlag;
|
||||
int ppqOffset;
|
||||
|
||||
bool m_wasPlaying;
|
||||
double m_syncPpqPosition;
|
||||
double m_posChangeThreshold;
|
||||
double m_ppqToStartSyncAt;
|
||||
bool m_followSongPosition;
|
||||
uint8 m_syncFlag;
|
||||
int m_ppqOffset;
|
||||
int m_sample_remain;
|
||||
static const int cycleEnd_ = 1;
|
||||
static const int startSlave_ = 2;
|
||||
|
||||
ScopedPointer <MidiMessage> clockMessage;
|
||||
ScopedPointer <MidiMessage> continueMessage;
|
||||
ScopedPointer <MidiMessage> stopMessage;
|
||||
ScopedPointer <MidiMessage> songPositionMessage;
|
||||
ScopedPointer <MidiMessage> m_clockMessage;
|
||||
ScopedPointer <MidiMessage> m_continueMessage;
|
||||
ScopedPointer <MidiMessage> m_stopMessage;
|
||||
ScopedPointer <MidiMessage> m_songPositionMessage;
|
||||
|
||||
void sendSongPositionPointerMessage(const double ppqPosition, const int posInBuffer, MidiBuffer* buffer);
|
||||
|
||||
@@ -67,6 +67,7 @@ private:
|
||||
const double sampleRate, const double ppqPerSample);
|
||||
|
||||
double getNearestSixteenthInPPQ(const double ppqPosition) {return ceil(ppqPosition * 4.0) / 4.0;}
|
||||
double getNearestInPPQ(const double ppqPosition, double quantizer) {return ceil(ppqPosition * quantizer / 4) / (quantizer / 4);}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(JK_MidiClock);
|
||||
};
|
||||
@@ -9,7 +9,16 @@
|
||||
#include <math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "synth/synth_defs.h"
|
||||
#include <synth/synth_defs.h>
|
||||
#include <synth/voice.h>
|
||||
#include <synth/vco.h>
|
||||
#include <synth/noise.h>
|
||||
#include <synth/smooth.h>
|
||||
#include <synth/vcf.h>
|
||||
#include <synth/lfo.h>
|
||||
#include <synth/env.h>
|
||||
#include <synth/param_scale.h>
|
||||
|
||||
#include "JaySynthSound.h"
|
||||
#include "JaySynthVoice.h"
|
||||
#include "JaySynthMonophonicMGR.h"
|
||||
@@ -44,6 +53,7 @@ JaySynth::JaySynth (int num_voices, String pathToWaves)
|
||||
{
|
||||
File wavesFile = File(pathToWaves);
|
||||
ScopedPointer<FileInputStream>pIn = wavesFile.createInputStream();
|
||||
if (pIn != NULL)
|
||||
pIn->read(synth_common.vco.wt.wave_rawdata, sizeof(synth_common.vco.wt.wave_rawdata));
|
||||
}
|
||||
|
||||
@@ -148,7 +158,7 @@ JaySynth::~JaySynth()
|
||||
{
|
||||
delete(m_ppAudioThread[i]);
|
||||
}
|
||||
delete(m_ppAudioThread);
|
||||
delete[] m_ppAudioThread;
|
||||
|
||||
SynthDebug("delete params\n");
|
||||
paramInfoFree(&pb_range[0]);
|
||||
@@ -161,25 +171,25 @@ JaySynth::~JaySynth()
|
||||
{
|
||||
paramInfoFree(&humanize_voice_param[i][j]);
|
||||
}
|
||||
delete (humanize_voice_param[i]);
|
||||
delete (ppHumanizedSliders[i]);
|
||||
delete[] humanize_voice_param[i];
|
||||
delete[] ppHumanizedSliders[i];
|
||||
}
|
||||
delete (humanize_voice_param);
|
||||
delete (ppHumanizedSliders);
|
||||
delete[] humanize_voice_param;
|
||||
delete[] ppHumanizedSliders;
|
||||
|
||||
for (i = max_num_voices; --i >= 0;)
|
||||
{
|
||||
removeVoice(i);
|
||||
}
|
||||
|
||||
delete (pPer_voice_controls);
|
||||
delete (pCurrNoteInfos);
|
||||
delete (m_pVoices);
|
||||
delete[] pPer_voice_controls;
|
||||
delete[] pCurrNoteInfos;
|
||||
delete[] m_pVoices;
|
||||
for (i=0; i < m_num_audiothreads; i++)
|
||||
{
|
||||
delete (m_ppEventAudioThreadRdy[i]);
|
||||
}
|
||||
delete(m_ppEventAudioThreadRdy);
|
||||
delete[] m_ppEventAudioThreadRdy;
|
||||
|
||||
|
||||
}
|
||||
@@ -370,6 +380,8 @@ void JaySynth::humanizeVarianceChanged_ENV(void)
|
||||
//==============================================================================
|
||||
void JaySynth::ClearControls(void)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
memset(controls, 0, SYNTH_NUM_PARAMS*sizeof(synth_float_t));
|
||||
}
|
||||
|
||||
@@ -419,6 +431,8 @@ synth_float_t JaySynth::getParameter(int paramID)
|
||||
|
||||
void JaySynth::setParameter(int paramID, synth_float_t param)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
params[paramID] = param;
|
||||
setParam(paramID, -1);
|
||||
|
||||
@@ -428,6 +442,8 @@ void JaySynth::setParameter(int paramID, synth_float_t param)
|
||||
|
||||
void JaySynth::setControl(int paramID, synth_float_t param)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
controls[paramID] = param;
|
||||
setParam(paramID, -1);
|
||||
|
||||
@@ -437,12 +453,16 @@ void JaySynth::setControl(int paramID, synth_float_t param)
|
||||
|
||||
void JaySynth::setPerVoiceControl(int voice, int channel, int paramID, synth_float_t param)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
pPer_voice_controls[voice].ctrl[channel][paramID] = param;
|
||||
setParam(paramID, voice);
|
||||
}
|
||||
|
||||
void JaySynth::setParam(int paramID, int voice)
|
||||
{
|
||||
const ScopedLock sl (lock);
|
||||
|
||||
int i, j, voice_param_type, voice_min, voice_max;
|
||||
synth_float_t param;
|
||||
|
||||
@@ -887,22 +907,19 @@ void JaySynth::handlePitchWheel (const int midiChannel, const int wheelValue)
|
||||
|
||||
}
|
||||
|
||||
void JaySynth::handleController (const int midiChannel,
|
||||
const int controllerNumber,
|
||||
const int controllerValue)
|
||||
void JaySynth::handleController (const midiCC_info_t &midiCC_info)
|
||||
{
|
||||
midiCC_info.channel = midiChannel;
|
||||
midiCC_info.ID = controllerNumber;
|
||||
midiCC_info.value = (synth_float_t)controllerValue/127;
|
||||
if (midiCC_info.ID < 0 || midiCC_info.ID >= NUM_MIDI_CONTROLLERS)
|
||||
return;
|
||||
|
||||
last_midiCC_info[controllerNumber] = midiCC_info;
|
||||
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_MIDICC, &midiCC_info);
|
||||
last_midiCC_info[midiCC_info.ID] = midiCC_info;
|
||||
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_MIDICC, (midiCC_info_t*)&midiCC_info);
|
||||
|
||||
switch (controllerNumber)
|
||||
switch (midiCC_info.ID)
|
||||
{
|
||||
case 0x40: handleSustainPedal (midiChannel, controllerValue >= 64); break;
|
||||
case 0x42: handleSostenutoPedal (midiChannel, controllerValue >= 64); break;
|
||||
case 0x43: handleSoftPedal (midiChannel, controllerValue >= 64); break;
|
||||
case 0x40: handleSustainPedal (midiCC_info.channel, midiCC_info.value_raw >= 64); break;
|
||||
case 0x42: handleSostenutoPedal (midiCC_info.channel, midiCC_info.value_raw >= 64); break;
|
||||
case 0x43: handleSoftPedal (midiCC_info.channel, midiCC_info.value_raw >= 64); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
@@ -912,8 +929,8 @@ void JaySynth::handleController (const int midiChannel,
|
||||
{
|
||||
JaySynthVoice* const voice = voices.getUnchecked (i);
|
||||
|
||||
if (midiChannel <= 0 || voice->isPlayingChannel (midiChannel))
|
||||
voice->controllerMoved (controllerNumber, controllerValue);
|
||||
if (midiCC_info.channel <= 0 || voice->isPlayingChannel (midiCC_info.channel))
|
||||
voice->controllerMoved (midiCC_info.ID, midiCC_info.value_raw);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -987,15 +1004,27 @@ void JaySynth::renderNextBlock (AudioSampleBuffer& outputBuffer,
|
||||
midiIterator.setNextSamplePosition (startSample);
|
||||
MidiMessage m (0xf4, 0.0);
|
||||
|
||||
bool havePendingEvent = false;
|
||||
int midiEventPos = 0;
|
||||
|
||||
while (numSamples > 0)
|
||||
{
|
||||
int midiEventPos;
|
||||
const bool useEvent = midiIterator.getNextEvent (m, midiEventPos)
|
||||
if (!havePendingEvent)
|
||||
havePendingEvent = midiIterator.getNextEvent (m, midiEventPos);
|
||||
|
||||
const bool useEvent = havePendingEvent
|
||||
&& midiEventPos < startSample + numSamples;
|
||||
|
||||
const int numThisTime = useEvent ? midiEventPos - startSample
|
||||
int numThisTime = useEvent ? midiEventPos - startSample
|
||||
: numSamples;
|
||||
|
||||
// The DSP core's per-block work buffers are fixed at SYNTH_MAX_BUFSIZE
|
||||
// samples, so render oversized ranges in multiple passes rather than
|
||||
// overrunning them. The pending midi event (if any) stays queued until
|
||||
// we actually reach its sample position.
|
||||
if (numThisTime > SYNTH_MAX_BUFSIZE)
|
||||
numThisTime = SYNTH_MAX_BUFSIZE;
|
||||
|
||||
if (numThisTime > 0)
|
||||
{
|
||||
|
||||
@@ -1032,9 +1061,12 @@ void JaySynth::renderNextBlock (AudioSampleBuffer& outputBuffer,
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useEvent)
|
||||
// Only fire the pending event once we've actually rendered up to its
|
||||
// sample position (numThisTime may have been clamped short of it above).
|
||||
if (useEvent && numThisTime == midiEventPos - startSample)
|
||||
{
|
||||
handleMidiEvent (m);
|
||||
havePendingEvent = false;
|
||||
}
|
||||
|
||||
startSample += numThisTime;
|
||||
@@ -1087,9 +1119,30 @@ void JaySynth::handleMidiEvent (const MidiMessage& m)
|
||||
}
|
||||
if (m.isController())
|
||||
{
|
||||
handleController (m.getChannel(),
|
||||
m.getControllerNumber(),
|
||||
m.getControllerValue());
|
||||
midiCC_info_t midiCC_info;
|
||||
midiCC_info.channel = m.getChannel();
|
||||
midiCC_info.ID = m.getControllerNumber();
|
||||
midiCC_info.type = MIDI_CONTROLLER_TYPE_CC;
|
||||
midiCC_info.value_raw = m.getControllerValue();
|
||||
midiCC_info.value = (synth_float_t)midiCC_info.value_raw/127;
|
||||
|
||||
SynthDebug("CC: controllerNumber=%d, controllerValue=%d", midiCC_info.ID, midiCC_info.value_raw);
|
||||
|
||||
bool isNrpnProcessing = mMidiNrpn.process(midiCC_info.ID, midiCC_info.value_raw);
|
||||
if (mMidiNrpn.isValid())
|
||||
{
|
||||
midiCC_info.ID = mMidiNrpn.getId();
|
||||
midiCC_info.type = MIDI_CONTROLLER_TYPE_NRPN;
|
||||
midiCC_info.value_raw = mMidiNrpn.getValue();
|
||||
midiCC_info.value = (synth_float_t)midiCC_info.value_raw/mMidiNrpn.getMaxValue();
|
||||
SynthDebug("NRPN: controllerNumber=%d, controllerValue=%d", midiCC_info.ID, midiCC_info.value_raw);
|
||||
}
|
||||
|
||||
if (!isNrpnProcessing)
|
||||
{
|
||||
handleController (midiCC_info);
|
||||
}
|
||||
|
||||
}
|
||||
if (m.isMidiMachineControlMessage())
|
||||
{
|
||||
@@ -1128,8 +1181,10 @@ void JaySynth::handleMidiEvent (const MidiMessage& m)
|
||||
if (m.isSongPositionPointer())
|
||||
{
|
||||
SynthDebug("SongPositionPointer\n");
|
||||
m_clock_cnt_bar = (6 * m.getSongPositionPointerMidiBeat()) % m_numMidiClocksPerBar;
|
||||
SynthDebug("getSongPositionPointerMidiBeat(): %u -> %u clocks\n", m.getSongPositionPointerMidiBeat(), m_clock_cnt_bar);
|
||||
double bar_16 = 1 + ((double)m.getSongPositionPointerMidiBeat()/4);
|
||||
double bar_4 = (bar_16 - (int)bar_16);
|
||||
m_clock_cnt_bar = (int)(bar_4*4);
|
||||
SynthDebug("getSongPositionPointerMidiBeat(): %f -> %u clocks", bar_4, m_clock_cnt_bar);
|
||||
}
|
||||
if (m.isQuarterFrame())
|
||||
{
|
||||
@@ -1162,10 +1217,17 @@ void JaySynth::handleMidiEvent (const MidiMessage& m)
|
||||
|
||||
info.bpm = (uint32_t)(bpm + 0.5);
|
||||
info.type = 1;
|
||||
|
||||
if (m_clock_cnt_bar == 0)
|
||||
{
|
||||
info.type = 2;
|
||||
SynthDebug("1111111111111111111111111");
|
||||
}
|
||||
else
|
||||
{
|
||||
SynthDebug("-------------------------");
|
||||
}
|
||||
|
||||
listeners.call (&JaySynthListener::synthChanged, SYNTH_CHANGED_MIDICLOCK, &info);
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,14 @@
|
||||
#ifndef _JAYSYNTH_H_
|
||||
#define _JAYSYNTH_H_
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "synth/synth_defs.h"
|
||||
#include "synth/param_scale.h"
|
||||
#include <synth/synth_defs.h>
|
||||
#include <synth/param_scale.h>
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "JaySynthVoice.h"
|
||||
#include "JaySynthMonophonicMGR.h"
|
||||
#include "JaySynthMidiCC.h"
|
||||
#include "MidiNrpn.h"
|
||||
|
||||
#define GET_NUM_VOICES max_num_voices
|
||||
|
||||
@@ -44,6 +47,82 @@ private:
|
||||
class JaySynth : public Synthesiser
|
||||
{
|
||||
public:
|
||||
enum
|
||||
{
|
||||
SYNTH_PER_VOICE_CONTROL_VELKEY = 0,
|
||||
SYNTH_PER_VOICE_CONTROL_SIZE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SYNTH_CHANGED_PARAM = 0,
|
||||
SYNTH_CHANGED_MIDICC,
|
||||
SYNTH_CHANGED_NUM_VOICES_PLAYING,
|
||||
SYNTH_CHANGED_NOTE_PRESSED,
|
||||
SYNTH_CHANGED_NOTE_RELEASED,
|
||||
SYNTH_CHANGED_MIDICLOCK,
|
||||
SYNTH_CHANGED_SIZE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MIDI_CONTROLLER_TYPE_CC = 0,
|
||||
MIDI_CONTROLLER_TYPE_NRPN,
|
||||
MIDI_CONTROLLER_TYPE_RPN,
|
||||
MIDI_CONTROLLER_TYPE_SIZE
|
||||
};
|
||||
|
||||
typedef struct _smidiCC_info_t
|
||||
{
|
||||
int type;
|
||||
int channel;
|
||||
int ID;
|
||||
int value_raw;
|
||||
synth_float_t value;
|
||||
} midiCC_info_t;
|
||||
|
||||
typedef struct _smidi_note_info_t
|
||||
{
|
||||
int note;
|
||||
synth_float_t velocity;
|
||||
|
||||
} midi_note_info_t;
|
||||
|
||||
typedef struct _smidi_quarter_clock_info_t
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t bpm;
|
||||
} midi_quarter_clock_info_t;
|
||||
|
||||
midiCC_info_t* getLastMidiCC_infos(void)
|
||||
{
|
||||
return last_midiCC_info;
|
||||
}
|
||||
|
||||
midiCC_info_t* getLastMidiCC_info(int controllerID)
|
||||
{
|
||||
if (controllerID < NUM_MIDI_CONTROLLERS)
|
||||
return &last_midiCC_info[controllerID];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
midi_note_info_t* getLastMidiNote_infos()
|
||||
{
|
||||
return &last_midi_note_info;
|
||||
}
|
||||
|
||||
void setMidiCC_editBuffer(JaySynthMidiCC *pMidiCC)
|
||||
{
|
||||
pMidCC_editBuffer = pMidiCC;
|
||||
}
|
||||
|
||||
typedef struct _sper_voice_control_t
|
||||
{
|
||||
synth_float_t ctrl[SYNTH_PER_VOICE_CONTROL_SIZE][SYNTH_NUM_PARAMS];
|
||||
|
||||
} per_voice_control_t;
|
||||
|
||||
JaySynth(int num_voices, String pathToWaves);
|
||||
~JaySynth();
|
||||
void setSampleRate (synth_float_t sampleRate);
|
||||
@@ -57,7 +136,8 @@ public:
|
||||
synth_float_t getVolume(void);
|
||||
int lastPitchWheelValue, lastmidiChannel;
|
||||
void handlePitchWheel (int midiChannel, int wheelValue);
|
||||
void handleController (int midiChannel, int controllerNumber, int controllerValue);
|
||||
void handleController (int midiChannel, int controllerNumber, int controllerValue) {}
|
||||
void handleController (const midiCC_info_t &midiCC_info);
|
||||
void updateParameters(void);
|
||||
void updateParameter(int paramID);
|
||||
void humanizeModeChanged(void);
|
||||
@@ -145,72 +225,6 @@ public:
|
||||
listeners.add (listener);
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
SYNTH_PER_VOICE_CONTROL_VELKEY = 0,
|
||||
SYNTH_PER_VOICE_CONTROL_SIZE
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
SYNTH_CHANGED_PARAM = 0,
|
||||
SYNTH_CHANGED_MIDICC,
|
||||
SYNTH_CHANGED_NUM_VOICES_PLAYING,
|
||||
SYNTH_CHANGED_NOTE_PRESSED,
|
||||
SYNTH_CHANGED_NOTE_RELEASED,
|
||||
SYNTH_CHANGED_MIDICLOCK,
|
||||
SYNTH_CHANGED_SIZE
|
||||
};
|
||||
|
||||
typedef struct _smidiCC_info_t
|
||||
{
|
||||
int channel;
|
||||
int ID;
|
||||
synth_float_t value;
|
||||
} midiCC_info_t;
|
||||
|
||||
typedef struct _smidi_note_info_t
|
||||
{
|
||||
int note;
|
||||
synth_float_t velocity;
|
||||
|
||||
} midi_note_info_t;
|
||||
|
||||
typedef struct _smidi_quarter_clock_info_t
|
||||
{
|
||||
uint32_t type;
|
||||
uint32_t bpm;
|
||||
} midi_quarter_clock_info_t;
|
||||
|
||||
midiCC_info_t* getLastMidiCC_infos(void)
|
||||
{
|
||||
return last_midiCC_info;
|
||||
}
|
||||
|
||||
midiCC_info_t* getLastMidiCC_info(int controllerID)
|
||||
{
|
||||
if (controllerID < NUM_MIDI_CONTROLLERS)
|
||||
return &last_midiCC_info[controllerID];
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
midi_note_info_t* getLastMidiNote_infos()
|
||||
{
|
||||
return &last_midi_note_info;
|
||||
}
|
||||
|
||||
void setMidiCC_editBuffer(JaySynthMidiCC *pMidiCC)
|
||||
{
|
||||
pMidCC_editBuffer = pMidiCC;
|
||||
}
|
||||
|
||||
typedef struct _sper_voice_control_t
|
||||
{
|
||||
synth_float_t ctrl[SYNTH_PER_VOICE_CONTROL_SIZE][SYNTH_NUM_PARAMS];
|
||||
|
||||
} per_voice_control_t;
|
||||
|
||||
//==============================================================================
|
||||
|
||||
private:
|
||||
@@ -261,6 +275,7 @@ private:
|
||||
bool m_isMidiClockStarted;
|
||||
|
||||
void updateMidiTiming(const MidiMessage& m);
|
||||
MidiNrpn mMidiNrpn;
|
||||
};
|
||||
|
||||
|
||||
+32
-30
@@ -726,24 +726,6 @@ JaySynthAudioProcessorEditor::JaySynthAudioProcessorEditor (JaySynthAudioProcess
|
||||
m_comboBox_vcf_type->addItem (TRANS("BPF"), 3);
|
||||
m_comboBox_vcf_type->addListener (this);
|
||||
|
||||
addAndMakeVisible (m_slider_lfo1_smooth = new Slider ("LFO 2 smooth"));
|
||||
m_slider_lfo1_smooth->setTooltip (TRANS("LFO 2 smooth [%]"));
|
||||
m_slider_lfo1_smooth->setRange (0, 1, 0);
|
||||
m_slider_lfo1_smooth->setSliderStyle (Slider::RotaryVerticalDrag);
|
||||
m_slider_lfo1_smooth->setTextBoxStyle (Slider::NoTextBox, false, 80, 20);
|
||||
m_slider_lfo1_smooth->setColour (Slider::rotarySliderFillColourId, Colour (0x7fffffff));
|
||||
m_slider_lfo1_smooth->setColour (Slider::rotarySliderOutlineColourId, Colour (0x66ffffff));
|
||||
m_slider_lfo1_smooth->addListener (this);
|
||||
|
||||
addAndMakeVisible (m_label_lfo1_smooth = new Label ("LFO 2 smooth",
|
||||
TRANS("value")));
|
||||
m_label_lfo1_smooth->setFont (Font (14.00f, Font::plain));
|
||||
m_label_lfo1_smooth->setJustificationType (Justification::centred);
|
||||
m_label_lfo1_smooth->setEditable (true, true, false);
|
||||
m_label_lfo1_smooth->setColour (TextEditor::textColourId, Colours::black);
|
||||
m_label_lfo1_smooth->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
|
||||
m_label_lfo1_smooth->addListener (this);
|
||||
|
||||
addAndMakeVisible (m_slider_lfo0_smooth = new Slider ("LFO 1 smooth"));
|
||||
m_slider_lfo0_smooth->setTooltip (TRANS("LFO 1 smooth [%]"));
|
||||
m_slider_lfo0_smooth->setRange (0, 1, 0);
|
||||
@@ -762,6 +744,24 @@ JaySynthAudioProcessorEditor::JaySynthAudioProcessorEditor (JaySynthAudioProcess
|
||||
m_label_lfo0_smooth->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
|
||||
m_label_lfo0_smooth->addListener (this);
|
||||
|
||||
addAndMakeVisible (m_slider_lfo1_smooth = new Slider ("LFO 2 smooth"));
|
||||
m_slider_lfo1_smooth->setTooltip (TRANS("LFO 2 smooth [%]"));
|
||||
m_slider_lfo1_smooth->setRange (0, 1, 0);
|
||||
m_slider_lfo1_smooth->setSliderStyle (Slider::RotaryVerticalDrag);
|
||||
m_slider_lfo1_smooth->setTextBoxStyle (Slider::NoTextBox, false, 80, 20);
|
||||
m_slider_lfo1_smooth->setColour (Slider::rotarySliderFillColourId, Colour (0x7fffffff));
|
||||
m_slider_lfo1_smooth->setColour (Slider::rotarySliderOutlineColourId, Colour (0x66ffffff));
|
||||
m_slider_lfo1_smooth->addListener (this);
|
||||
|
||||
addAndMakeVisible (m_label_lfo1_smooth = new Label ("LFO 2 smooth",
|
||||
TRANS("value")));
|
||||
m_label_lfo1_smooth->setFont (Font (14.00f, Font::plain));
|
||||
m_label_lfo1_smooth->setJustificationType (Justification::centred);
|
||||
m_label_lfo1_smooth->setEditable (true, true, false);
|
||||
m_label_lfo1_smooth->setColour (TextEditor::textColourId, Colours::black);
|
||||
m_label_lfo1_smooth->setColour (TextEditor::backgroundColourId, Colour (0x00000000));
|
||||
m_label_lfo1_smooth->addListener (this);
|
||||
|
||||
addAndMakeVisible (m_slider_lfo2_smooth = new Slider ("LFO 3 smooth"));
|
||||
m_slider_lfo2_smooth->setTooltip (TRANS("LFO 3 smooth [%]"));
|
||||
m_slider_lfo2_smooth->setRange (0, 1, 0);
|
||||
@@ -2497,10 +2497,10 @@ JaySynthAudioProcessorEditor::~JaySynthAudioProcessorEditor()
|
||||
m_label_synth_name = nullptr;
|
||||
m_label_synth_version = nullptr;
|
||||
m_comboBox_vcf_type = nullptr;
|
||||
m_slider_lfo1_smooth = nullptr;
|
||||
m_label_lfo1_smooth = nullptr;
|
||||
m_slider_lfo0_smooth = nullptr;
|
||||
m_label_lfo0_smooth = nullptr;
|
||||
m_slider_lfo1_smooth = nullptr;
|
||||
m_label_lfo1_smooth = nullptr;
|
||||
m_slider_lfo2_smooth = nullptr;
|
||||
m_label_lfo2_smooth = nullptr;
|
||||
m_slider_lfo3_smooth = nullptr;
|
||||
@@ -3535,10 +3535,10 @@ void JaySynthAudioProcessorEditor::resized()
|
||||
m_label_synth_name->setBounds (12, 662, 92, 24);
|
||||
m_label_synth_version->setBounds (978, 662, 132, 24);
|
||||
m_comboBox_vcf_type->setBounds (880, 74, 68, 16);
|
||||
m_slider_lfo1_smooth->setBounds (506, 88, 36, 36);
|
||||
m_label_lfo1_smooth->setBounds (498, 70, 52, 20);
|
||||
m_slider_lfo0_smooth->setBounds (392, 224, 36, 36);
|
||||
m_label_lfo0_smooth->setBounds (384, 206, 52, 20);
|
||||
m_slider_lfo1_smooth->setBounds (506, 88, 36, 36);
|
||||
m_label_lfo1_smooth->setBounds (498, 70, 52, 20);
|
||||
m_slider_lfo2_smooth->setBounds (392, 402, 36, 36);
|
||||
m_label_lfo2_smooth->setBounds (384, 384, 52, 20);
|
||||
m_slider_lfo3_smooth->setBounds (506, 402, 36, 36);
|
||||
@@ -5045,10 +5045,6 @@ void JaySynthAudioProcessorEditor::mouseDown (const MouseEvent &m)
|
||||
if (m.mods.isRightButtonDown() && m.mods.isShiftDown())
|
||||
{
|
||||
// Comboboxes don't support midicontroller assignments
|
||||
if (componentThatWasClicked->getComponentID().contains(String("PARAM_COMBOBOX")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
MidiCC_PopUp_show(componentThatWasClicked);
|
||||
}
|
||||
}
|
||||
@@ -5164,10 +5160,6 @@ void JaySynthAudioProcessorEditor::MidiCC_PopUp_event(int event)
|
||||
switch (event)
|
||||
{
|
||||
case MidiCC_PopUp::POPUP_OK:
|
||||
paramID = m_MidiCC_PopUp->getParamID();
|
||||
getProcessor()->setParam(paramID, m_MidiCC_PopUp->getCurrentParamValue(), false, false);
|
||||
getProcessor()->controlParam(paramID, m_MidiCC_PopUp->getCurrentControlValue());
|
||||
m_label_patch_name->setText(getProcessor()->getCurrentProgramName(), juce::NotificationType::dontSendNotification);
|
||||
deleteAndZero (m_MidiCC_PopUp);
|
||||
break;
|
||||
|
||||
@@ -5366,6 +5358,16 @@ void JaySynthAudioProcessorEditor::paramUpdate(int paramID, bool doHostUpdate)
|
||||
if (pButton)
|
||||
{
|
||||
pButton->setToggleState(getProcessor()->getParameter(paramID) > 0.5, juce::NotificationType::dontSendNotification);
|
||||
|
||||
if (pButton == m_button_humanize_enable)
|
||||
{
|
||||
getProcessor()->humanizeModeChanged();
|
||||
}
|
||||
else if (pButton == m_button_unisono_enable)
|
||||
{
|
||||
getProcessor()->unisonoModeChanged();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
#define __JUCE_HEADER_AD3FF14AD0EF4DBE__
|
||||
|
||||
//[Headers] -- You can add your own extra header files here --
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include <JuceHeader.h>
|
||||
#include "PluginProcessor.h"
|
||||
#include "ButtonLED.h"
|
||||
#include "MidiCC_PopUp.h"
|
||||
@@ -177,10 +177,10 @@ private:
|
||||
ScopedPointer<Label> m_label_synth_name;
|
||||
ScopedPointer<Label> m_label_synth_version;
|
||||
ScopedPointer<ComboBox> m_comboBox_vcf_type;
|
||||
ScopedPointer<Slider> m_slider_lfo1_smooth;
|
||||
ScopedPointer<Label> m_label_lfo1_smooth;
|
||||
ScopedPointer<Slider> m_slider_lfo0_smooth;
|
||||
ScopedPointer<Label> m_label_lfo0_smooth;
|
||||
ScopedPointer<Slider> m_slider_lfo1_smooth;
|
||||
ScopedPointer<Label> m_label_lfo1_smooth;
|
||||
ScopedPointer<Slider> m_slider_lfo2_smooth;
|
||||
ScopedPointer<Label> m_label_lfo2_smooth;
|
||||
ScopedPointer<Slider> m_slider_lfo3_smooth;
|
||||
@@ -7,8 +7,9 @@
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "synth/synth_defs.h"
|
||||
#include <synth/synth_defs.h>
|
||||
|
||||
#include <JuceHeader.h>
|
||||
#include "JaySynthMidiCC.h"
|
||||
|
||||
//==============================================================================
|
||||
@@ -54,11 +55,21 @@ JaySynthMidiCC::JaySynthMidiCC()
|
||||
/*pmax*/pConstraints->pmax,
|
||||
/*pinterval*/pConstraints->pinterval);
|
||||
|
||||
midiCC_containers[i].doChangeParameter = false;
|
||||
midiCC_containers[i].controllerID = 0;
|
||||
midiCC_containers[i].controllerID = 128 + i;
|
||||
midiCC_containers[i].isAssigned = false;
|
||||
midiCC_containers[i].doChangeParameter = false;
|
||||
midiCC_containers[i].doApplyOnPatchInit = false;
|
||||
|
||||
if (pConstraints->midiControllerId >= 0)
|
||||
{
|
||||
if (pConstraints->midiControllerId > 0)
|
||||
{
|
||||
midiCC_containers[i].controllerID = pConstraints->midiControllerId;
|
||||
}
|
||||
midiCC_containers[i].doChangeParameter = true;
|
||||
midiCC_containers[i].isAssigned = true;
|
||||
}
|
||||
|
||||
// Velocity
|
||||
paramInfoInit(&midiCC_containers[i].curve_vel, i, pConstraints->pName);
|
||||
paramInfoSet(&midiCC_containers[i].curve_vel,
|
||||
@@ -173,26 +184,13 @@ int JaySynthMidiCC::findParamID_byName(String name)
|
||||
return (int)paramNamedValueSet.getWithDefault (toParameterNameXML(name), var(-1));
|
||||
}
|
||||
|
||||
void JaySynthMidiCC::exportXML(String name)
|
||||
{
|
||||
File file(name);
|
||||
XmlElement xml ("JSYNTH_MIDICC_TABLE_FILE");
|
||||
|
||||
exportXML(&xml);
|
||||
|
||||
file.create();
|
||||
xml.writeToFile (/*const File &destinationFile*/file, /*const String &dtdToUse*/"JSYNTH_MIDICC_TABLE_FILE" /*, const String &encodingType="UTF-8", int lineWrapLength=60*/);
|
||||
}
|
||||
|
||||
void JaySynthMidiCC::exportXML(XmlElement *pXML_doc) const
|
||||
void JaySynthMidiCC::export_midi_cc(XmlElement *pXML) const
|
||||
{
|
||||
int paramID;
|
||||
midiCC_container_t const *pMidiContainer;
|
||||
XmlElement *pXML, *pXML_root, *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
XmlElement *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
|
||||
// Store controller and slider infos
|
||||
pXML_root = pXML_doc->createNewChildElement (String("JSYNTH_MIDICONTROLLER_TABLE"));
|
||||
pXML = pXML_root->createNewChildElement (String("TARGETS"));
|
||||
for (paramID=0; paramID < SYNTH_NUM_PARAMS; paramID++)
|
||||
{
|
||||
pMidiContainer = &midiCC_containers[paramID];
|
||||
@@ -202,29 +200,34 @@ void JaySynthMidiCC::exportXML(XmlElement *pXML_doc) const
|
||||
pXML_PARAM->setAttribute ("IS_ABSOLUTE", pMidiContainer->doChangeParameter);
|
||||
pXML_PARAM->setAttribute ("DO_APPLY_ON_PATCH_INIT", pMidiContainer->doApplyOnPatchInit);
|
||||
pXML_PARAM->setAttribute ("CONTROLLER_ID", pMidiContainer->controllerID);
|
||||
}
|
||||
}
|
||||
|
||||
void JaySynthMidiCC::export_midi_velkey(XmlElement *pXML) const
|
||||
{
|
||||
int paramID;
|
||||
midiCC_container_t const *pMidiContainer;
|
||||
XmlElement *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
|
||||
// Store controller and slider infos
|
||||
for (paramID=0; paramID < SYNTH_NUM_PARAMS; paramID++)
|
||||
{
|
||||
pMidiContainer = &midiCC_containers[paramID];
|
||||
pXML_PARAM = pXML->createNewChildElement (String("PARAM_") + String(paramID));
|
||||
pXML_PARAM->setAttribute ("NAME", toParameterNameXML(pMidiContainer->constraints.pName));
|
||||
pXML_PARAM->setAttribute ("IS_VELOCITY_ASSIGNED", pMidiContainer->isVelAssigned);
|
||||
pXML_PARAM->setAttribute ("IS_KEYFOLLOW_ASSIGNED", pMidiContainer->isKeyAssigned);
|
||||
pXML_PARAM->setAttribute ("VELKEY_COMBINE_OP", pMidiContainer->velKeyCombineOP);
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("RELATIVE"));
|
||||
// pXML_PARAM_INFO->setAttribute ("BASE", String(pMidiContainer->control.base, 10));
|
||||
// pXML_PARAM_INFO->setAttribute ("KEXP", String(pMidiContainer->control.kexp, 10));
|
||||
// pXML_PARAM_INFO->setAttribute ("SCENTER", String(pMidiContainer->control.scenter, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->control.pmin, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->control.pmax, 10));
|
||||
// pXML_PARAM_INFO->setAttribute ("PINTERVAL", String(pMidiContainer->control.pinterval, 10));
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("ABSOLUTE"));
|
||||
// pXML_PARAM_INFO->setAttribute ("BASE", String(pMidiContainer->param.base, 10));
|
||||
// pXML_PARAM_INFO->setAttribute ("KEXP", String(pMidiContainer->param.kexp, 10));
|
||||
// pXML_PARAM_INFO->setAttribute ("SCENTER", String(pMidiContainer->param.scenter, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->param.pmin, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->param.pmax, 10));
|
||||
// pXML_PARAM_INFO->setAttribute ("PINTERVAL", String(pMidiContainer->param.pinterval, 10));
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("VELOCITY_CURVE"));
|
||||
// pXML_PARAM_INFO->setAttribute ("BASE", String(pMidiContainer->curve_vel.base, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("KEXP", String(pMidiContainer->curve_vel.kexp, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("SCENTER", String(pMidiContainer->curve_vel.scenter, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->curve_vel.pmin, 10));
|
||||
@@ -232,65 +235,137 @@ void JaySynthMidiCC::exportXML(XmlElement *pXML_doc) const
|
||||
pXML_PARAM_INFO->setAttribute ("PINTERVAL", String(pMidiContainer->curve_vel.pinterval, 10));
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->createNewChildElement (String("KEYFOLLOW_CURVE"));
|
||||
// pXML_PARAM_INFO->setAttribute ("BASE", String(pMidiContainer->curve_key.base, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("KEXP", String(pMidiContainer->curve_key.kexp, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("SCENTER", String(pMidiContainer->curve_key.scenter, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMIN", String(pMidiContainer->curve_key.pmin, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PMAX", String(pMidiContainer->curve_key.pmax, 10));
|
||||
pXML_PARAM_INFO->setAttribute ("PINTERVAL", String(pMidiContainer->curve_key.pinterval, 10));
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
int i, controllerID, numDst;
|
||||
XmlElement *pXML_dst, *pXML_MIDICC;
|
||||
|
||||
// todo: romove that in future versions
|
||||
pXML = pXML_root->createNewChildElement (String("CONTROLLERS"));
|
||||
// Store MIDI controller assignments
|
||||
for (controllerID=0; controllerID < NUM_MIDI_CONTROLLERS; controllerID++)
|
||||
{
|
||||
pXML_MIDICC = pXML->createNewChildElement (String("MIDICC_") + String(controllerID));
|
||||
pXML_MIDICC->setAttribute ("ID", controllerID);
|
||||
|
||||
pXML_dst = pXML_MIDICC->createNewChildElement (String("TARGETS"));
|
||||
numDst = getNumDestinations(controllerID);
|
||||
for (i=0; i < numDst; i++)
|
||||
{
|
||||
pMidiContainer = getDestination(controllerID, i);
|
||||
pXML_dst->setAttribute (String("PARAM") + String(pMidiContainer->constraints.id), pMidiContainer->constraints.pName);
|
||||
}
|
||||
}
|
||||
}
|
||||
// todo: romove that in future versions
|
||||
*/
|
||||
}
|
||||
|
||||
int JaySynthMidiCC::importXML(String name)
|
||||
{
|
||||
File file(name);
|
||||
XmlDocument xml(file);
|
||||
|
||||
ScopedPointer<XmlElement> pXML_root (xml.getDocumentElement());
|
||||
|
||||
return importXML(pXML_root);
|
||||
}
|
||||
|
||||
int JaySynthMidiCC::importXML(XmlElement const *pXML_doc)
|
||||
int JaySynthMidiCC::import_midi_velkey(XmlElement const *pXML)
|
||||
{
|
||||
int i, paramID;
|
||||
midiCC_container_t *pMidiContainer;
|
||||
XmlElement *pXML, *pXML_root, *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
XmlElement *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
|
||||
for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
|
||||
removeDestinations(i);
|
||||
|
||||
pXML_root = pXML_doc->getChildByName ("JSYNTH_MIDICONTROLLER_TABLE");
|
||||
// make sure that it's actually our type of XML object..
|
||||
if (pXML_root)
|
||||
if (pXML)
|
||||
{
|
||||
pXML = pXML_root->getFirstChildElement();
|
||||
while(pXML)
|
||||
pXML_PARAM = pXML->getFirstChildElement();
|
||||
while(pXML_PARAM)
|
||||
{
|
||||
paramID = findParamID_byName(pXML_PARAM->getStringAttribute("NAME"));
|
||||
if (paramID < 0)
|
||||
{
|
||||
pXML_PARAM = pXML_PARAM->getNextElement();
|
||||
continue;
|
||||
}
|
||||
|
||||
pMidiContainer = &midiCC_containers[paramID];
|
||||
pMidiContainer->isVelAssigned = pXML_PARAM->getIntAttribute("IS_VELOCITY_ASSIGNED", 0) != 0;
|
||||
pMidiContainer->isKeyAssigned = pXML_PARAM->getIntAttribute("IS_KEYFOLLOW_ASSIGNED", 0) != 0;
|
||||
pMidiContainer->velKeyCombineOP = pXML_PARAM->getIntAttribute("VELKEY_COMBINE_OP", VELKEY_OP_ADD);
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("RELATIVE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
pMidiContainer->control.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
|
||||
pMidiContainer->control.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
|
||||
}
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("ABSOLUTE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
pMidiContainer->param.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
|
||||
pMidiContainer->param.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
|
||||
}
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("VELOCITY_CURVE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
pMidiContainer->curve_vel.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
|
||||
pMidiContainer->curve_vel.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
|
||||
pMidiContainer->curve_vel.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
|
||||
pMidiContainer->curve_vel.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
|
||||
}
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("KEYFOLLOW_CURVE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
pMidiContainer->curve_key.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
|
||||
pMidiContainer->curve_key.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
|
||||
pMidiContainer->curve_key.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
|
||||
pMidiContainer->curve_key.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
|
||||
}
|
||||
|
||||
if (pXML_PARAM->getIntAttribute("IS_ASSIGNED", 0))
|
||||
add(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
|
||||
else
|
||||
remove(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
|
||||
|
||||
pXML_PARAM = pXML_PARAM->getNextElement();
|
||||
}
|
||||
}
|
||||
isModified = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JaySynthMidiCC::import_midi_cc(XmlElement const *pXML)
|
||||
{
|
||||
int i, paramID;
|
||||
midiCC_container_t *pMidiContainer;
|
||||
XmlElement *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
|
||||
for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
|
||||
removeDestinations(i);
|
||||
|
||||
// make sure that it's actually our type of XML object..
|
||||
if (pXML)
|
||||
{
|
||||
pXML_PARAM = pXML->getFirstChildElement();
|
||||
while(pXML_PARAM)
|
||||
{
|
||||
paramID = findParamID_byName(pXML_PARAM->getStringAttribute("NAME"));
|
||||
if (paramID < 0)
|
||||
{
|
||||
pXML_PARAM = pXML_PARAM->getNextElement();
|
||||
continue;
|
||||
}
|
||||
|
||||
pMidiContainer = &midiCC_containers[paramID];
|
||||
pMidiContainer->doChangeParameter = pXML_PARAM->getIntAttribute("IS_ABSOLUTE", pXML_PARAM->getIntAttribute("DO_CHANGE_PARAMETER", 0)) != 0;
|
||||
pMidiContainer->doApplyOnPatchInit = pXML_PARAM->getIntAttribute("DO_APPLY_ON_PATCH_INIT", 0) != 0;
|
||||
|
||||
if (pXML_PARAM->getIntAttribute("IS_ASSIGNED", 0))
|
||||
add(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
|
||||
else
|
||||
remove(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
|
||||
|
||||
pXML_PARAM = pXML_PARAM->getNextElement();
|
||||
}
|
||||
}
|
||||
isModified = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int JaySynthMidiCC::import_midi_cc_legacy(XmlElement const *pXML_MidiCc)
|
||||
{
|
||||
int i, paramID;
|
||||
midiCC_container_t *pMidiContainer;
|
||||
XmlElement *pXML, *pXML_PARAM, *pXML_PARAM_INFO;
|
||||
|
||||
for (i=0; i < NUM_MIDI_CONTROLLERS; i++)
|
||||
removeDestinations(i);
|
||||
|
||||
// make sure that it's actually our type of XML object..
|
||||
if (pXML_MidiCc)
|
||||
{
|
||||
pXML = pXML_MidiCc->getFirstChildElement();
|
||||
if(pXML)
|
||||
{
|
||||
pXML_PARAM = pXML->getFirstChildElement();
|
||||
while(pXML_PARAM)
|
||||
@@ -313,83 +388,42 @@ int JaySynthMidiCC::importXML(XmlElement const *pXML_doc)
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("RELATIVE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
// pMidiContainer->control.base = pXML_PARAM_INFO->getDoubleAttribute("BASE", pMidiContainer->constraints.base);
|
||||
// pMidiContainer->control.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", pMidiContainer->constraints.kexp);
|
||||
// pMidiContainer->control.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", pMidiContainer->constraints.scenter);
|
||||
pMidiContainer->control.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
|
||||
pMidiContainer->control.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
|
||||
// pMidiContainer->control.pinterval = pXML_PARAM_INFO->getDoubleAttribute("PINTERVAL", pMidiContainer->constraints.pinterval);
|
||||
}
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("ABSOLUTE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
// pMidiContainer->param.base = pXML_PARAM_INFO->getDoubleAttribute("BASE", pMidiContainer->constraints.base);
|
||||
// pMidiContainer->param.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", pMidiContainer->constraints.kexp);
|
||||
// pMidiContainer->param.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", pMidiContainer->constraints.scenter);
|
||||
pMidiContainer->param.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", pMidiContainer->constraints.pmin);
|
||||
pMidiContainer->param.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", pMidiContainer->constraints.pmax);
|
||||
// pMidiContainer->param.pinterval = pXML_PARAM_INFO->getDoubleAttribute("PINTERVAL", pMidiContainer->constraints.pinterval);
|
||||
}
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("VELOCITY_CURVE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
// pMidiContainer->curve_vel.base = pXML_PARAM_INFO->getDoubleAttribute("BASE", exp(1.0));
|
||||
pMidiContainer->curve_vel.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
|
||||
pMidiContainer->curve_vel.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
|
||||
pMidiContainer->curve_vel.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
|
||||
pMidiContainer->curve_vel.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
|
||||
// pMidiContainer->curve_vel.pinterval = pXML_PARAM_INFO->getDoubleAttribute("PINTERVAL", 0);
|
||||
}
|
||||
|
||||
pXML_PARAM_INFO = pXML_PARAM->getChildByName(String("KEYFOLLOW_CURVE"));
|
||||
if (pXML_PARAM_INFO)
|
||||
{
|
||||
// pMidiContainer->curve_key.base = pXML_PARAM_INFO->getDoubleAttribute("BASE", exp(1.0));
|
||||
pMidiContainer->curve_key.kexp = pXML_PARAM_INFO->getDoubleAttribute("KEXP", 0);
|
||||
pMidiContainer->curve_key.scenter = pXML_PARAM_INFO->getDoubleAttribute("SCENTER", 0.5);
|
||||
pMidiContainer->curve_key.pmin = pXML_PARAM_INFO->getDoubleAttribute("PMIN", 0);
|
||||
pMidiContainer->curve_key.pmax = pXML_PARAM_INFO->getDoubleAttribute("PMAX", 1);
|
||||
// pMidiContainer->curve_key.pinterval = pXML_PARAM_INFO->getDoubleAttribute("PINTERVAL", 0);
|
||||
}
|
||||
|
||||
if (pXML_PARAM->getIntAttribute("IS_ASSIGNED", 0))
|
||||
add(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
|
||||
else
|
||||
remove(pMidiContainer, pXML_PARAM->getIntAttribute("CONTROLLER_ID", 0));
|
||||
|
||||
pXML_PARAM = pXML_PARAM->getNextElement();
|
||||
}
|
||||
|
||||
// todo: romove that in future versions
|
||||
{
|
||||
int controllerID;
|
||||
XmlElement *pXML_MIDICC, *pXML_DST;
|
||||
|
||||
pXML = pXML->getNextElement();
|
||||
while(pXML)
|
||||
{
|
||||
pXML_MIDICC = pXML->getFirstChildElement();
|
||||
while(pXML_MIDICC)
|
||||
{
|
||||
controllerID = pXML_MIDICC->getIntAttribute("ID");
|
||||
pXML_DST = pXML_MIDICC->getFirstChildElement();
|
||||
if (pXML_DST)
|
||||
{
|
||||
for (i=0; i < pXML_DST->getNumAttributes(); i++)
|
||||
{
|
||||
paramID = findParamID_byName(pXML_DST->getAttributeValue(i));
|
||||
if (paramID < 0)
|
||||
break;
|
||||
pMidiContainer = &midiCC_containers[paramID];
|
||||
add(pMidiContainer, controllerID);
|
||||
}
|
||||
}
|
||||
pXML_MIDICC = pXML_MIDICC->getNextElement();
|
||||
}
|
||||
pXML = pXML->getNextElement();
|
||||
}
|
||||
}
|
||||
// todo: romove that in future versions
|
||||
}
|
||||
}
|
||||
isModified = false;
|
||||
@@ -398,7 +432,7 @@ int JaySynthMidiCC::importXML(XmlElement const *pXML_doc)
|
||||
|
||||
void JaySynthMidiCC::add(midiCC_container_t *pObj, int controllerID)
|
||||
{
|
||||
if (controllerID >= NUM_MIDI_CONTROLLERS)
|
||||
if (controllerID < 0 || controllerID >= NUM_MIDI_CONTROLLERS)
|
||||
return;
|
||||
|
||||
remove(pObj, pObj->controllerID);
|
||||
@@ -414,7 +448,7 @@ void JaySynthMidiCC::add(midiCC_container_t *pObj, int controllerID)
|
||||
|
||||
void JaySynthMidiCC::remove(midiCC_container_t *pObj, int controllerID)
|
||||
{
|
||||
if (controllerID >= NUM_MIDI_CONTROLLERS)
|
||||
if (controllerID < 0 || controllerID >= NUM_MIDI_CONTROLLERS)
|
||||
return;
|
||||
|
||||
pObj->isAssigned = 0;
|
||||
@@ -11,8 +11,17 @@
|
||||
#ifndef _JAYSYNTH_MIDICC_
|
||||
#define _JAYSYNTH_MIDICC_
|
||||
|
||||
#include "../JuceLibraryCode/JuceHeader.h"
|
||||
#include "synth/synth_defs.h"
|
||||
#include <JuceHeader.h>
|
||||
|
||||
#include <synth/synth_defs.h>
|
||||
#include <synth/voice.h>
|
||||
#include <synth/vco.h>
|
||||
#include <synth/noise.h>
|
||||
#include <synth/smooth.h>
|
||||
#include <synth/vcf.h>
|
||||
#include <synth/lfo.h>
|
||||
#include <synth/env.h>
|
||||
#include <synth/param_scale.h>
|
||||
|
||||
//==============================================================================
|
||||
#define MIDICONTROLLER_TYPE_VELKEY 0x00000080
|
||||
@@ -46,10 +55,11 @@ public:
|
||||
bool isMidiCCmodified(void) const;
|
||||
String toParameterNameXML (String const &name) const;
|
||||
int findParamID_byName(String name);
|
||||
void exportXML(String name);
|
||||
void exportXML(XmlElement *pXML_doc) const;
|
||||
int importXML(String name);
|
||||
int importXML(XmlElement const *pXML_doc);
|
||||
void export_midi_cc(XmlElement *pXML_doc) const;
|
||||
int import_midi_cc(XmlElement const *pXML_doc);
|
||||
void export_midi_velkey(XmlElement *pXML_doc) const;
|
||||
int import_midi_velkey(XmlElement const *pXML_doc);
|
||||
int import_midi_cc_legacy(XmlElement const *pXML_doc);
|
||||
void add(midiCC_container_t *pObj, int controllerID);
|
||||
void remove(midiCC_container_t *pObj, int controllerID);
|
||||
struct midiCC_container_t* getAtParamID(int paramID);
|
||||
@@ -11,7 +11,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "synth/synth_defs.h"
|
||||
#include <synth/synth_defs.h>
|
||||
#include "JaySynthMonophonicMGR.h"
|
||||
|
||||
/** A JaySynth voice that just plays incredible sounds.. */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user