#pragma once #include #include #include #include #include class SliceManager { public: struct Slice { int startSample; int endSample; int midiNote; // halftone offset from C3 (0 = C3, 1 = C#3, etc.) }; SliceManager(); void setSampleBuffer(juce::AudioBuffer* buffer, double sampleRate); void setSensitivity(float s); void setBassGain(float g); void setTrebleGain(float g); void autoSlice(); void clearSlices(); void addSliceManual(int samplePos); void removeSliceAt(int samplePos, int tolerance = 5); void moveSlice(int fromSample, int toSample); void trimStart(int sample); void trimEnd(int sample); void undo(); void redo(); bool canUndo() const; bool canRedo() const; const std::vector& getSlices() const { return slices; } int getNumSlices() const { return static_cast(slices.size()); } const Slice& getSlice(int index) const { return slices[static_cast(index)]; } int findSliceIndexForSample(int samplePos) const; juce::AudioBuffer* getMutableBuffer() { return sampleBuffer; } private: void sortSlices(); void assignMidiNotes(); void pushHistory(); void truncateHistory(); std::vector slices; std::vector> undoStack; std::vector> redoStack; static constexpr size_t maxHistory = 50; juce::AudioBuffer* sampleBuffer = nullptr; double currentSampleRate = 44100.0; float sensitivity = 0.5f; float bassGain = 1.0f; float trebleGain = 1.0f; std::vector computeEnvelope(const float* channelData, int numSamples); std::vector lowpassFilter(const std::vector& input, float cutoffHz); std::vector highpassFilter(const std::vector& input, float cutoffHz); std::vector detectOnsets(const std::vector& envelope, float threshold); };