mirror of
https://codeberg.org/armin/monoslicer.git
synced 2026-09-01 04:10:45 +02:00
146 lines
4.8 KiB
C++
146 lines
4.8 KiB
C++
#pragma once
|
|
#include <juce_audio_processors/juce_audio_processors.h>
|
|
#include <juce_audio_formats/juce_audio_formats.h>
|
|
#include <juce_dsp/juce_dsp.h>
|
|
#include "SliceManager.h"
|
|
|
|
class MonoslicerProcessor : public juce::AudioProcessor
|
|
{
|
|
public:
|
|
MonoslicerProcessor();
|
|
~MonoslicerProcessor() override;
|
|
|
|
void prepareToPlay(double sampleRate, int samplesPerBlock) override;
|
|
void releaseResources() override;
|
|
void processBlock(juce::AudioBuffer<float>&, juce::MidiBuffer&) override;
|
|
|
|
juce::AudioProcessorEditor* createEditor() override;
|
|
bool hasEditor() const override { return true; }
|
|
|
|
const juce::String getName() const override { return JucePlugin_Name; }
|
|
|
|
bool acceptsMidi() const override { return true; }
|
|
bool producesMidi() const override { return true; }
|
|
bool isMidiEffect() const override { return false; }
|
|
double getTailLengthSeconds() const override { return 0.0; }
|
|
|
|
int getNumPrograms() override { return 1; }
|
|
int getCurrentProgram() override { return 0; }
|
|
void setCurrentProgram(int) override {}
|
|
const juce::String getProgramName(int) override { return {}; }
|
|
void changeProgramName(int, const juce::String&) override {}
|
|
|
|
void getStateInformation(juce::MemoryBlock& destData) override;
|
|
void setStateInformation(const void* data, int sizeInBytes) override;
|
|
|
|
void loadAudioFile(const juce::File& file);
|
|
void clearSample();
|
|
void loadNextSample();
|
|
void loadPreviousSample();
|
|
void triggerSlice(int sliceIndex, float velocity = 0.8f);
|
|
void releaseSlice();
|
|
void stretchToBeats(int numBeats, float bpm);
|
|
void psolaStretch(int targetLength);
|
|
float getHostBpm() const;
|
|
|
|
void undo();
|
|
void redo();
|
|
bool canUndo() const;
|
|
bool canRedo() const;
|
|
|
|
juce::AudioProcessorValueTreeState& getAPVTS() { return apvts; }
|
|
SliceManager& getSliceManager() { return sliceManager; }
|
|
|
|
const juce::AudioBuffer<float>& getSampleBuffer() const { return sampleBuffer; }
|
|
double getSampleRateLoaded() const { return loadedSampleRate; }
|
|
const juce::File& getCurrentFile() const { return currentFile; }
|
|
int getCurrentFileIndex() const { return currentFileIndex; }
|
|
int getNumFilesInFolder() const { return static_cast<int>(folderFiles.size()); }
|
|
|
|
std::atomic<bool> stateRestored { false };
|
|
std::atomic<int> currentPlaybackSample { -1 };
|
|
std::atomic<int> currentActiveSlice { -1 };
|
|
std::atomic<int> selectedSlice { -1 };
|
|
std::atomic<float> outputLevelL { 0.0f };
|
|
std::atomic<float> outputLevelR { 0.0f };
|
|
|
|
private:
|
|
juce::AudioProcessorValueTreeState apvts;
|
|
juce::AudioProcessorValueTreeState::ParameterLayout createParameterLayout();
|
|
|
|
SliceManager sliceManager;
|
|
|
|
juce::AudioFormatManager formatManager;
|
|
juce::AudioBuffer<float> sampleBuffer;
|
|
double loadedSampleRate = 44100.0;
|
|
double currentSampleRate = 44100.0;
|
|
|
|
juce::File currentFile;
|
|
juce::Array<juce::File> folderFiles;
|
|
int currentFileIndex = -1;
|
|
void refreshFolderList();
|
|
|
|
struct UndoState
|
|
{
|
|
juce::File file;
|
|
std::vector<SliceManager::Slice> slices;
|
|
};
|
|
std::vector<UndoState> undoStack;
|
|
std::vector<UndoState> redoStack;
|
|
static constexpr int maxUndoDepth = 10;
|
|
void pushUndoState();
|
|
bool restoringFromUndo = false;
|
|
|
|
enum class AmpStage { Idle, Attack, Decay, Sustain, Release };
|
|
enum class FilterType { LP12, LP24, HP, BP, Notch };
|
|
|
|
struct BiquadState
|
|
{
|
|
float x1 = 0.0f, x2 = 0.0f, y1 = 0.0f, y2 = 0.0f;
|
|
float b0 = 0.0f, b1 = 0.0f, b2 = 0.0f;
|
|
float a1 = 0.0f, a2 = 0.0f;
|
|
|
|
void reset() { x1 = x2 = y1 = y2 = 0.0f; }
|
|
|
|
float process(float input)
|
|
{
|
|
float output = b0 * input + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;
|
|
x2 = x1; x1 = input;
|
|
y2 = y1; y1 = output;
|
|
return output;
|
|
}
|
|
};
|
|
|
|
struct Voice
|
|
{
|
|
bool active = false;
|
|
int sliceIndex = -1;
|
|
float position = 0.0f;
|
|
float velocity = 0.0f;
|
|
bool midiNoteSent = false;
|
|
|
|
AmpStage ampStage = AmpStage::Idle;
|
|
float ampEnv = 0.0f;
|
|
int ampSamplesToNext = 0;
|
|
|
|
AmpStage filterStage = AmpStage::Idle;
|
|
float filterEnv = 0.0f;
|
|
int filterSamplesToNext = 0;
|
|
|
|
BiquadState filterL, filterR;
|
|
BiquadState filterL2, filterR2;
|
|
};
|
|
|
|
static constexpr int maxVoices = 16;
|
|
std::array<Voice, maxVoices> voices;
|
|
|
|
void handleNoteOn(int noteNumber, float velocity);
|
|
void handleNoteOff();
|
|
|
|
void advanceAmpEnvelope(Voice& voice, int samples);
|
|
void advanceFilterEnvelope(Voice& voice, int samples);
|
|
void computeBiquadCoeffs(FilterType type, float cutoff, float resonance, float sampleRate,
|
|
float& b0, float& b1, float& b2, float& a1, float& a2);
|
|
|
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(MonoslicerProcessor)
|
|
};
|