midi notes avaiable on ui

This commit is contained in:
michalcourson
2025-10-30 21:39:06 -04:00
parent 3645e38dd5
commit 55e80b4c74
7 changed files with 604 additions and 303 deletions

View File

@ -67,14 +67,17 @@ static inline bool float_equal(float one, float two) {
return abs(one - two) < 1e-5f;
}
void Shifter::Init()
void Shifter::Init(float samplerate, int samplesPerBlock)
{
sample_rate_ = samplerate;
blocksize = samplesPerBlock;
out_midi_smoother.SetFrameTime((float)samplesPerBlock / samplerate);
volume = 1;
helm.setframesize(1024);
helm.setoverlap(1);
for (int i = 0; i < MAX_VOICES; ++i)
{
voices[i].Init(48000);
voices[i].Init(samplerate);
}
for (int i = 0; i < BUFFER_SIZE; ++i)
{
@ -148,13 +151,13 @@ void Shifter::DetectPitch(const float* const* in, float** out, size_t size)
// Smoothly filter in_period changes
in_period = in_period * in_period_filter_amount + period * (1.0f - in_period_filter_amount);
}
float in_freq = 48000 / in_period;
float in_freq = sample_rate_ / in_period;
float midi = (12 * log2f(in_freq / 440) + 69.0f);
//target_out_period = in_period * out_period_filter_amount + target_out_period * (1 - out_period_filter_amount);
out_midi = out_midi_smoother.update(midi, (int)(midi+.5));
out_period = 48000.0f / mtof(out_midi);
out_period = sample_rate_ / mtof(out_midi);
}
void Shifter::SetRates() {}
@ -275,8 +278,8 @@ void Shifter::GetSamples(float** output, const float* input, size_t size)
//add new samples if necessary
for (int out_p = 0; out_p < MAX_VOICES; ++out_p)
{
voices[out_p].Process();
if (!voices[out_p].IsActive()) continue;
voices[out_p].Process();
if (voices[out_p].PeriodOverflow())
{
float resampling_period = GetOutputEnvelopePeriod(out_p);
@ -328,12 +331,12 @@ void Shifter::GetSamples(float** output, const float* input, size_t size)
void Shifter::AddMidiNote(int note) {
for (int i = 0; i < MAX_VOICES; ++i) {
if (voices[i].IsActive() && voices[i].GetMidiNote() == note) {
return;
if (voices[i].onoff_ && voices[i].GetMidiNote() == note) {
voices[i].Release();
}
}
for (int i = 0; i < MAX_VOICES; ++i) {
if (!voices[i].IsActive()) {
if (!voices[i].onoff_) {
voices[i].Trigger(note);
return;
}
@ -343,9 +346,8 @@ void Shifter::AddMidiNote(int note) {
void Shifter::RemoveMidiNote(int note) {
for (int i = 0; i < MAX_VOICES; ++i) {
if (voices[i].IsActive() && voices[i].GetMidiNote() == note) {
if (voices[i].GetMidiNote() == note) {
voices[i].Release();
return;
}
}
}

View File

@ -75,7 +75,7 @@ public:
class Shifter {
public:
void Init();
void Init(float samplerate, int samplesPerBlock);
void Process(const float* const* in,
float** out,
size_t size);
@ -91,6 +91,7 @@ public:
}
float out_midi = 40;
ShifterVoice voices[MAX_VOICES];
private:
void DetectPitch(const float* const* in, float** out, size_t size);
@ -140,11 +141,13 @@ private:
float out_period_filter_amount = 0.7f; // You can expose this as a parameter
ShifterVoice voices[MAX_VOICES];
float out_period = 0; //C3
float in_period = 366.936;
float out_period_counter = 0;
float cos_lookup[8192];
float sample_rate_;
int blocksize;
};
#endif

View File

@ -145,26 +145,26 @@ private:
int64 writeIx = 0;
};
class SpectralBars
{
public:
//template <typename T>
void push(int data)
{
testQueue.push(data);
}
void compute(Span<int> output) {
int index = 0;
for (auto it = output.begin(); it != output.end(); ++it) {
*it = testQueue.get(index++);
}
}
private:
circ_queue<int, 256> testQueue;
};
//class SpectralBars
//{
//public:
// //template <typename T>
// void push(int data)
// {
// testQueue.push(data);
// }
//
// void compute(Span<int> output) {
// int index = 0;
// for (auto it = output.begin(); it != output.end(); ++it) {
// *it = testQueue.get(index++);
// }
// }
//
//
//private:
// circ_queue<int, 256> testQueue;
//};
//==============================================================================
class WebViewPluginAudioProcessor : public AudioProcessor
@ -200,6 +200,7 @@ public:
//==============================================================================
void getStateInformation(MemoryBlock& destData) override;
void setStateInformation(const void* data, int sizeInBytes) override;
bool new_midi = false;
struct Parameters
{
@ -261,18 +262,20 @@ public:
Parameters parameters;
AudioProcessorValueTreeState state;
SpinLock midiLock;
std::vector<int> spectrumData = [] { return std::vector<int>(256, 0.0f); }();
/*std::vector<int> spectrumData = [] { return std::vector<int>(256, 0.0f); }();
SpinLock spectrumDataLock;
SpectralBars spectralBars;
SpectralBars spectralBars;*/
dsp::LadderFilter<float> filter;
Shifter shifter;
private:
//==============================================================================
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(WebViewPluginAudioProcessor)
Shifter shifter;
};
//==============================================================================
@ -288,14 +291,14 @@ WebViewPluginAudioProcessor::WebViewPluginAudioProcessor(AudioProcessorValueTree
parameters(layout),
state(*this, nullptr, "STATE", std::move(layout))
{
shifter.Init();
shifter.Init(48000.0f, 48);
}
//==============================================================================
void WebViewPluginAudioProcessor::prepareToPlay(double sampleRate, int samplesPerBlock)
{
const auto channels = std::max(getTotalNumInputChannels(), getTotalNumOutputChannels());
shifter.Init();
shifter.Init((float)sampleRate, samplesPerBlock);
if (channels == 0)
return;
@ -335,8 +338,17 @@ void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer,
for (const auto metadata : midi)
{
const auto msg = metadata.getMessage();
if (msg.isNoteOn()) shifter.AddMidiNote(msg.getNoteNumber());
else if (msg.isNoteOff()) shifter.RemoveMidiNote(msg.getNoteNumber());
if (msg.isNoteOn()) {
shifter.AddMidiNote(msg.getNoteNumber());
new_midi = true;
//editor.webComponent.emitEventIfBrowserIsVisible("midNoteData", var{});
}
else if (msg.isNoteOff()) {
shifter.RemoveMidiNote(msg.getNoteNumber());
new_midi = true;
//editor.webComponent.emitEventIfBrowserIsVisible("midNoteData", var{});
}
}
@ -344,12 +356,11 @@ void WebViewPluginAudioProcessor::processBlock(juce::AudioBuffer<float>& buffer,
//DBG(shifter.out_midi[MAX_VOICES]);
//push midi note
//spectralBars.push(shifter.out_midi[MAX_VOICES]);
const SpinLock::ScopedTryLockType lock(spectrumDataLock);
const SpinLock::ScopedTryLockType lock(midiLock);
if (!lock.isLocked())
return;
spectralBars.compute({ spectrumData.data(), spectrumData.size() });
}
@ -446,9 +457,9 @@ public:
{
static constexpr size_t numFramesBuffered = 5;
SpinLock::ScopedLockType lock{ processorRef.spectrumDataLock };
SpinLock::ScopedLockType lock{ processorRef.midiLock };
Array<var> frame;
/*Array<var> frame;
for (size_t i = 1; i < processorRef.spectrumData.size(); ++i)
frame.add(processorRef.spectrumData[i]);
@ -458,14 +469,18 @@ public:
spectrumDataFrames.push_back(std::move(frame));
while (spectrumDataFrames.size() > numFramesBuffered)
spectrumDataFrames.pop_front();
spectrumDataFrames.pop_front();*/
static int64 callbackCounter = 0;
/*if ( spectrumDataFrames.size() == numFramesBuffered
&& callbackCounter++ % (int64) numFramesBuffered)
{*/
webComponent.emitEventIfBrowserIsVisible("spectrumData", var{});
if (processorRef.new_midi) {
processorRef.new_midi = false;
webComponent.emitEventIfBrowserIsVisible("midNoteData", var{});
}
//}
}
@ -595,16 +610,22 @@ std::optional<WebBrowserComponent::Resource> WebViewPluginAudioProcessorEditor::
return WebBrowserComponent::Resource{ streamToVector(stream), String { "text/html" } };
}
if (urlToRetrive == "spectrumData.json")
if (urlToRetrive == "midNoteData.json")
{
Array<var> frames;
for (const auto& frame : spectrumDataFrames)
frames.add(frame);
juce::Array<var> notes;
int voice_num = 0;
for (auto& voice : processorRef.shifter.voices) {
if (voice.onoff_) {
auto obj = new DynamicObject();
obj->setProperty("voice", voice_num);
obj->setProperty("midi", voice.GetMidiNote());
notes.add(var(obj));
}
voice_num++;
}
DynamicObject::Ptr d(new DynamicObject());
d->setProperty("timeResolutionMs", getTimerInterval());
d->setProperty("frames", std::move(frames));
d->setProperty("notes", notes);
const auto s = JSON::toString(d.get());
MemoryInputStream stream{ s.getCharPointer(), s.getNumBytesAsUTF8(), false };
@ -652,7 +673,7 @@ WebViewPluginAudioProcessorEditor::WebViewPluginAudioProcessorEditor(WebViewPlug
setSize(500, 500);
startTimerHz(20);
startTimerHz(60);
}
//==============================================================================
@ -675,5 +696,7 @@ public:
}
bool hasEditor() const override { return true; }
AudioProcessorEditor* createEditor() override { return new WebViewPluginAudioProcessorEditor(*this); }
AudioProcessorEditor* createEditor() override {
return new WebViewPluginAudioProcessorEditor(*this);
}
};

View File

@ -18,18 +18,23 @@ static inline float mtof(float m)
void ShifterVoice::Init(float sample_rate) {
portamento_.Init(sample_rate, 0.05f); //default portamento time
amplitude_envelope_.Init(sample_rate);
sample_rate_ = sample_rate;
portamento_.Init(sample_rate_, 0.05f); //default portamento time
amplitude_envelope_.Init(sample_rate_);
amplitude_envelope_.SetAttackTime(0.2f);
amplitude_envelope_.SetDecayTime(0.1f);
amplitude_envelope_.SetReleaseTime(.1f);
amplitude_envelope_.SetReleaseTime(.05f);
onoff_ = false;
overflow_ = false;
current_midi = 60;
current_period_ = 48000.0f / mtof((float)current_midi);
current_period_ = sample_rate_ / mtof((float)current_midi);
current_amplitude = 0.0f;
period_counter = 0.0f;
panning = 0.5f;
//on reset, make sure envelope is not running
while (amplitude_envelope_.IsRunning()) {
amplitude_envelope_.Process(false);
}
}
@ -49,7 +54,7 @@ void ShifterVoice::Release() {
void ShifterVoice::Process() {
current_amplitude = amplitude_envelope_.Process(onoff_);
current_period_ = 48000.0f / mtof(portamento_.Process((float)current_midi));
current_period_ = sample_rate_ / mtof(portamento_.Process((float)current_midi));
period_counter++;
overflow_ = period_counter >= current_period_;
if (overflow_) {

View File

@ -32,12 +32,14 @@ public:
void SetAdsrTimes(float attack, float decay, float release);
float GetPanning(int channel) const;
int GetMidiNote() const { return current_midi; }
bool onoff_;
private:
Port portamento_;
Adsr amplitude_envelope_;
bool onoff_;
bool overflow_;
float sample_rate_;
int current_midi;
float current_period_;
float current_amplitude;