mirror of
https://codeberg.org/armin/monoslicer.git
synced 2026-09-01 04:10:45 +02:00
Add octave offset dropdown, fix waveform sidebar, embed audio in plugin state
- Add octave offset combo (-3 to +3) in title bar for MIDI input transposition - Fix waveform sidebar base note from C3 to C4 - Fix midiNoteToSliceIndex to compare pitch classes instead of raw MIDI notes - Add colored slice indicator dots on waveform sidebar keys - Embed audio data, file path, sample rate, and slices in plugin state for DAW project save/restore (falls back to file path if embedded data missing)
This commit is contained in:
parent
8dc07d2568
commit
73c4095139
6 changed files with 130 additions and 12 deletions
|
|
@ -78,6 +78,9 @@ juce::AudioProcessorValueTreeState::ParameterLayout MonoSlicerProcessor::createP
|
|||
layout.add(std::make_unique<juce::AudioParameterInt>(
|
||||
"keyShift", "Key Shift", -12, 12, 0));
|
||||
|
||||
layout.add(std::make_unique<juce::AudioParameterInt>(
|
||||
"octaveOffset", "Octave Offset", -3, 3, 0));
|
||||
|
||||
juce::StringArray scaleChoices { "50%", "75%", "100%", "125%", "150%", "200%" };
|
||||
layout.add(std::make_unique<juce::AudioParameterChoice>(
|
||||
"uiScale", "UI Scale", scaleChoices, 2));
|
||||
|
|
@ -446,7 +449,8 @@ void MonoSlicerProcessor::processBlock(juce::AudioBuffer<float>& buffer, juce::M
|
|||
|
||||
void MonoSlicerProcessor::handleNoteOn(int noteNumber, float velocity)
|
||||
{
|
||||
int sliceIdx = noteNumber - 60;
|
||||
int offset = static_cast<int>(apvts.getRawParameterValue("octaveOffset")->load()) * 12;
|
||||
int sliceIdx = noteNumber - 60 + offset;
|
||||
sliceIdx = juce::jlimit(0, juce::jmax(0, sliceManager.getNumSlices() - 1), sliceIdx);
|
||||
|
||||
if (apvts.getRawParameterValue("selectViaMidi")->load() > 0.5f)
|
||||
|
|
@ -555,6 +559,36 @@ juce::AudioProcessorEditor* MonoSlicerProcessor::createEditor()
|
|||
void MonoSlicerProcessor::getStateInformation(juce::MemoryBlock& destData)
|
||||
{
|
||||
auto state = apvts.copyState();
|
||||
|
||||
state.setProperty("filePath", currentFile.getFullPathName(), nullptr);
|
||||
state.setProperty("sampleRate", loadedSampleRate, nullptr);
|
||||
|
||||
juce::Array<juce::var> sliceStarts;
|
||||
juce::Array<juce::var> sliceEnds;
|
||||
for (const auto& s : sliceManager.getSlices())
|
||||
{
|
||||
sliceStarts.add(s.startSample);
|
||||
sliceEnds.add(s.endSample);
|
||||
}
|
||||
state.setProperty("sliceStarts", sliceStarts, nullptr);
|
||||
state.setProperty("sliceEnds", sliceEnds, nullptr);
|
||||
|
||||
if (sampleBuffer.getNumSamples() > 0)
|
||||
{
|
||||
int numChannels = sampleBuffer.getNumChannels();
|
||||
int numSamples = sampleBuffer.getNumSamples();
|
||||
|
||||
juce::MemoryOutputStream audioStream;
|
||||
audioStream.writeInt(numChannels);
|
||||
audioStream.writeInt(numSamples);
|
||||
for (int ch = 0; ch < numChannels; ++ch)
|
||||
audioStream.write(sampleBuffer.getReadPointer(ch),
|
||||
static_cast<size_t>(numSamples) * sizeof(float));
|
||||
|
||||
audioStream.flush();
|
||||
state.setProperty("audioData", juce::var(juce::MemoryBlock(audioStream.getData(), audioStream.getDataSize())), nullptr);
|
||||
}
|
||||
|
||||
juce::MemoryOutputStream stream(destData, false);
|
||||
state.writeToStream(stream);
|
||||
}
|
||||
|
|
@ -563,8 +597,60 @@ void MonoSlicerProcessor::setStateInformation(const void* data, int sizeInBytes)
|
|||
{
|
||||
auto stream = juce::MemoryInputStream(data, static_cast<size_t>(sizeInBytes), false);
|
||||
auto state = juce::ValueTree::readFromStream(stream);
|
||||
if (state.isValid())
|
||||
apvts.replaceState(state);
|
||||
if (!state.isValid()) return;
|
||||
|
||||
apvts.replaceState(state);
|
||||
|
||||
loadedSampleRate = static_cast<double>(state.getProperty("sampleRate", 44100.0));
|
||||
|
||||
auto audioVar = state.getProperty("audioData", {});
|
||||
auto* audioBlock = audioVar.getBinaryData();
|
||||
if (audioBlock != nullptr && audioBlock->getSize() > 0)
|
||||
{
|
||||
const auto* buf = static_cast<const uint8_t*>(audioBlock->getData());
|
||||
size_t offset = 0;
|
||||
|
||||
int numChannels = juce::ByteOrder::bigEndianInt(buf + offset); offset += 4;
|
||||
int numSamples = juce::ByteOrder::bigEndianInt(buf + offset); offset += 4;
|
||||
|
||||
sampleBuffer.setSize(numChannels, numSamples, false, false, false);
|
||||
for (int ch = 0; ch < numChannels; ++ch)
|
||||
{
|
||||
std::memcpy(sampleBuffer.getWritePointer(ch), buf + offset,
|
||||
static_cast<size_t>(numSamples) * sizeof(float));
|
||||
offset += static_cast<size_t>(numSamples) * sizeof(float);
|
||||
}
|
||||
|
||||
currentFile = juce::File(state.getProperty("filePath", "").toString());
|
||||
refreshFolderList();
|
||||
sliceManager.setSampleBuffer(&sampleBuffer, loadedSampleRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
juce::String path = state.getProperty("filePath", "").toString();
|
||||
if (path.isNotEmpty())
|
||||
{
|
||||
juce::File file(path);
|
||||
if (file.existsAsFile())
|
||||
loadAudioFile(file);
|
||||
}
|
||||
}
|
||||
|
||||
auto sliceStarts = state.getProperty("sliceStarts", {});
|
||||
if (sliceStarts.isArray())
|
||||
{
|
||||
auto* starts = sliceStarts.getArray();
|
||||
int numSavedSlices = starts->size();
|
||||
|
||||
if (numSavedSlices > 0)
|
||||
{
|
||||
sliceManager.clearSlices();
|
||||
for (int i = 1; i < numSavedSlices; ++i)
|
||||
sliceManager.addSliceManual(static_cast<int>((*starts)[i]));
|
||||
}
|
||||
}
|
||||
|
||||
stateRestored.store(true);
|
||||
}
|
||||
|
||||
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue