Upload files to "Source"
This commit is contained in:
@@ -1,43 +1,79 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "SynthVoice.h"
|
|
||||||
#include <JuceHeader.h>
|
#include <JuceHeader.h>
|
||||||
|
#include "SynthVoice.h"
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
// MPESynthesiser wrapper that owns voices, installs a catch-all Sound,
|
||||||
|
// forwards prepare() to each voice, and renders MIDI blocks.
|
||||||
class NeuralAudioEngine : public juce::MPESynthesiser
|
class NeuralAudioEngine : public juce::MPESynthesiser
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr auto maxNumVoices = 4;
|
static constexpr int maxNumVoices = 8;
|
||||||
|
|
||||||
//==============================================================================
|
explicit NeuralAudioEngine (NeuralSharedParams& sp)
|
||||||
NeuralAudioEngine(NeuralSharedParams &sp)
|
|
||||||
{
|
{
|
||||||
for (auto i = 0; i < maxNumVoices; ++i)
|
// Call Synthesiser base API explicitly via a base pointer (portable on MSVC).
|
||||||
|
auto* base = static_cast<juce::Synthesiser*>(this);
|
||||||
|
base->clearVoices();
|
||||||
|
base->clearSounds();
|
||||||
|
|
||||||
|
// Create voices (MPESynthesiser takes ownership)
|
||||||
|
for (int i = 0; i < maxNumVoices; ++i)
|
||||||
addVoice (new NeuralSynthVoice (sp));
|
addVoice (new NeuralSynthVoice (sp));
|
||||||
|
|
||||||
|
// Catch-all Sound so any MIDI note/channel is accepted.
|
||||||
|
struct AnySound final : public juce::SynthesiserSound
|
||||||
|
{
|
||||||
|
bool appliesToNote (int) override { return true; }
|
||||||
|
bool appliesToChannel (int) override { return true; }
|
||||||
|
};
|
||||||
|
// Use raw pointer for this JUCE API on your version.
|
||||||
|
base->addSound (new AnySound());
|
||||||
|
|
||||||
|
// Respond to standard MIDI in non-MPE hosts
|
||||||
|
enableLegacyMode();
|
||||||
|
|
||||||
setVoiceStealingEnabled (true);
|
setVoiceStealingEnabled (true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
// Called from processor::prepareToPlay()
|
||||||
void prepare (const juce::dsp::ProcessSpec& spec) noexcept
|
void prepare (const juce::dsp::ProcessSpec& spec) noexcept
|
||||||
{
|
{
|
||||||
setCurrentPlaybackSampleRate (spec.sampleRate);
|
setCurrentPlaybackSampleRate (spec.sampleRate);
|
||||||
|
|
||||||
for (auto* v : voices)
|
for (auto* v : voices)
|
||||||
dynamic_cast<NeuralSynthVoice*> (v)->prepare(spec);
|
if (auto* nv = dynamic_cast<NeuralSynthVoice*> (v))
|
||||||
|
nv->prepare (spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
// Forward the MIDI-buffer overload so MIDI is consumed.
|
||||||
|
void renderNextBlock (juce::AudioBuffer<float>& outputAudio,
|
||||||
|
juce::MidiBuffer& midiMessages,
|
||||||
|
int startSample,
|
||||||
|
int numSamples)
|
||||||
|
{
|
||||||
|
juce::MPESynthesiser::renderNextBlock (outputAudio, midiMessages,
|
||||||
|
startSample, numSamples);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utility: apply a lambda to all voices
|
||||||
template <typename VoiceFunc>
|
template <typename VoiceFunc>
|
||||||
void applyToVoices (VoiceFunc&& fn) noexcept
|
void applyToVoices (VoiceFunc&& fn) noexcept
|
||||||
{
|
{
|
||||||
for (auto* v : voices)
|
for (auto* v : voices)
|
||||||
fn(dynamic_cast<NeuralSynthVoice*> (v));
|
if (auto* nv = dynamic_cast<NeuralSynthVoice*> (v))
|
||||||
|
fn (nv);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
// Keep base rendering behaviour for the sub-block overload.
|
||||||
void renderNextSubBlock(juce::AudioBuffer<float>& outputAudio, int startSample, int numSamples) override
|
using juce::MPESynthesiser::renderNextSubBlock;
|
||||||
|
|
||||||
|
void renderNextSubBlock (juce::AudioBuffer<float>& buffer,
|
||||||
|
int startSample,
|
||||||
|
int numSamples) override
|
||||||
{
|
{
|
||||||
MPESynthesiser::renderNextSubBlock(outputAudio, startSample, numSamples);
|
juce::MPESynthesiser::renderNextSubBlock (buffer, startSample, numSamples);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
80
Source/BlepOsc.h
Normal file
80
Source/BlepOsc.h
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <JuceHeader.h>
|
||||||
|
|
||||||
|
enum class BlepWave : int { Sine = 0, Saw, Square, Triangle };
|
||||||
|
|
||||||
|
class BlepOsc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void prepare (double sampleRate) { sr = sampleRate; resetPhase(); }
|
||||||
|
void setWave (BlepWave w) { wave = w; }
|
||||||
|
void setFrequency (float f) { freq = juce::jmax (0.0f, f); inc = freq / (float) sr; }
|
||||||
|
void resetPhase (float p = 0.0f) { phase = juce::jlimit (0.0f, 1.0f, p); }
|
||||||
|
|
||||||
|
inline float process()
|
||||||
|
{
|
||||||
|
// phase in [0..1)
|
||||||
|
float out = 0.0f;
|
||||||
|
float t = phase;
|
||||||
|
phase += inc;
|
||||||
|
if (phase >= 1.0f) phase -= 1.0f;
|
||||||
|
|
||||||
|
switch (wave)
|
||||||
|
{
|
||||||
|
case BlepWave::Sine: out = std::sin (2.0f * juce::MathConstants<float>::pi * t); break;
|
||||||
|
|
||||||
|
case BlepWave::Saw:
|
||||||
|
{
|
||||||
|
// naive saw in [-1..1]
|
||||||
|
float s = 2.0f * t - 1.0f;
|
||||||
|
// apply BLEP at the discontinuity crossing t=0
|
||||||
|
s -= polyBlep (t, inc);
|
||||||
|
out = s;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case BlepWave::Square:
|
||||||
|
{
|
||||||
|
float s = (t < 0.5f ? 1.0f : -1.0f);
|
||||||
|
// rising edge at 0.0, falling at 0.5
|
||||||
|
s += polyBlep (t, inc) - polyBlep (std::fmod (t + 0.5f, 1.0f), inc);
|
||||||
|
out = s;
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case BlepWave::Triangle:
|
||||||
|
{
|
||||||
|
// integrate the BLEP square for band-limited tri
|
||||||
|
float sq = (t < 0.5f ? 1.0f : -1.0f);
|
||||||
|
sq += polyBlep (t, inc) - polyBlep (std::fmod (t + 0.5f, 1.0f), inc);
|
||||||
|
// leaky integrator to keep DC under control
|
||||||
|
z1 = z1 + (sq - z1) * inc;
|
||||||
|
out = 2.0f * z1; // scale
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// PolyBLEP as in Valimäki/Huovilainen
|
||||||
|
static inline float polyBlep (float t, float dt)
|
||||||
|
{
|
||||||
|
// t in [0..1)
|
||||||
|
if (t < dt)
|
||||||
|
{
|
||||||
|
t /= dt;
|
||||||
|
return t + t - t * t - 1.0f;
|
||||||
|
}
|
||||||
|
else if (t > 1.0f - dt)
|
||||||
|
{
|
||||||
|
t = (t - 1.0f) / dt;
|
||||||
|
return t * t + t + t + 1.0f;
|
||||||
|
}
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
double sr = 44100.0;
|
||||||
|
float freq = 440.0f, inc = 440.0f / 44100.0f;
|
||||||
|
float phase = 0.0f;
|
||||||
|
float z1 = 0.0f;
|
||||||
|
BlepWave wave = BlepWave::Sine;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user