commit 775ca9ec283073bbe311f0d86c87e953b8d887af
parent 16f6de680dde96d2df9f02a7364a984922b5aba7
Author: dsp56300 <dsp56300@users.noreply.github.com>
Date: Sat, 8 Mar 2025 03:19:06 +0100
add wav file import
Diffstat:
5 files changed, 134 insertions(+), 2 deletions(-)
diff --git a/source/xtJucePlugin/weGraph.cpp b/source/xtJucePlugin/weGraph.cpp
@@ -1,6 +1,7 @@
#include "weGraph.h"
#include "xtWaveEditor.h"
+
#include "dsp56kEmu/fastmath.h"
namespace xtJucePlugin
@@ -172,6 +173,30 @@ namespace xtJucePlugin
return m_lastMouseEvent.get();
}
+ bool Graph::isInterestedInDragSource(const SourceDetails& _dragSourceDetails)
+ {
+ return false;
+ }
+
+ void Graph::itemDropped(const SourceDetails& _dragSourceDetails)
+ {
+ }
+
+ bool Graph::isInterestedInFileDrag(const juce::StringArray& _files)
+ {
+ if (_files.size() != 1)
+ return false;
+ return _files[0].endsWithIgnoreCase(".wav");
+ }
+
+ void Graph::filesDropped(const juce::StringArray& _files, int _x, int _y)
+ {
+ if (_files.size() != 1)
+ return;
+ if (auto res = m_editor.importWaveFile(_files[0].toStdString()))
+ m_editor.getGraphData().set(*res);
+ }
+
void Graph::onSourceChanged()
{
repaint();
diff --git a/source/xtJucePlugin/weGraph.h b/source/xtJucePlugin/weGraph.h
@@ -10,7 +10,7 @@ namespace xtJucePlugin
class GraphData;
class WaveEditor;
- class Graph : public juce::Component
+ class Graph : public juce::Component, public juce::DragAndDropTarget, public juce::FileDragAndDropTarget
{
public:
static constexpr uint32_t InvalidIndex = ~0;
@@ -57,6 +57,12 @@ namespace xtJucePlugin
m_updateHoveredPositionWhileDragging = _enable;
}
+ bool isInterestedInDragSource(const SourceDetails& _dragSourceDetails) override;
+ void itemDropped(const SourceDetails& _dragSourceDetails) override;
+
+ bool isInterestedInFileDrag(const juce::StringArray& _files) override;
+ void filesDropped(const juce::StringArray& _files, int _x, int _y) override;
+
protected:
virtual void onSourceChanged();
virtual void onHoveredIndexChanged(uint32_t _index);
diff --git a/source/xtJucePlugin/weWaveTreeItem.cpp b/source/xtJucePlugin/weWaveTreeItem.cpp
@@ -114,7 +114,12 @@ namespace xtJucePlugin
if(xt::wave::isReadOnly(m_waveIndex))
return false;
- if(files.size() == 1 && files[0].endsWithIgnoreCase(".mid") || files[0].endsWithIgnoreCase(".syx"))
+ if (files.size() != 1)
+ return false;
+
+ const auto& f = files[0];
+
+ if(f.endsWithIgnoreCase(".mid") || f.endsWithIgnoreCase(".syx") || f.endsWithIgnoreCase(".wav"))
return true;
return TreeItem::isInterestedInFileDrag(files);
@@ -125,6 +130,19 @@ namespace xtJucePlugin
if(xt::wave::isReadOnly(m_waveIndex))
return;
+ if (files.isEmpty())
+ return;
+
+ if (files[0].endsWithIgnoreCase(".wav"))
+ {
+ if (auto wave = m_editor.importWaveFile(files[0].toStdString()))
+ {
+ m_editor.getData().setWave(m_waveIndex, *wave);
+ m_editor.getData().sendWaveToDevice(m_waveIndex);
+ }
+ return;
+ }
+
const auto errorTitle = m_editor.getEditor().getProcessor().getProperties().name + " - Error";
std::map<xt::WaveId, xt::WaveData> waves;
@@ -135,6 +153,7 @@ namespace xtJucePlugin
{
if (tables.size() == 1)
genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, errorTitle, "This file doesn't contain a Wave but a Control Table, please drop on a User Table slot.");
+
return;
}
diff --git a/source/xtJucePlugin/xtWaveEditor.cpp b/source/xtJucePlugin/xtWaveEditor.cpp
@@ -19,6 +19,7 @@
#include "juceUiLib/messageBox.h"
+#include "synthLib/wavReader.h"
#include "synthLib/wavWriter.h"
#include "xtLib/xtState.h"
@@ -443,4 +444,81 @@ namespace xtJucePlugin
genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, productName + " - Error", "Failed to create file\n" + _filename + ".\n\nMake sure that the file is not write protected or opened in another application");
}
}
+
+ std::optional<xt::WaveData> WaveEditor::importWaveFile(const std::string& _filename) const
+ {
+ const auto productName = getEditor().getProcessor().getProperties().name;
+
+ std::vector<uint8_t> fileData;
+
+ if (!baseLib::filesystem::readFile(fileData, _filename))
+ {
+ genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, productName + " - Error", "Failed to open file\n" + _filename + ".\n\nMake sure that the file exists and " + productName + " has read permissions.");
+ return {};
+ }
+
+ constexpr const char* formatDesc = "\n\nMake sure it is an 8 bit mono PCM file with a size of 64 (half-cycle wave) or 128 (full-cycle wave)";
+
+ synthLib::Data wavData;
+ if (!synthLib::WavReader::load(wavData, nullptr, fileData.data(), fileData.size()))
+ {
+ genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, productName + " - Error", "Failed to parse data from file\n" + _filename + "." + formatDesc);
+ return {};
+ }
+
+ std::vector<int8_t> data;
+
+ if (wavData.isFloat || wavData.bitsPerSample != 8 || wavData.channels != 1)
+ {
+ genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, productName + " - Error", "Unsupported wav format in file\n" + _filename + "." + formatDesc);
+ return {};
+ }
+
+ data.resize(wavData.dataByteSize);
+ for (size_t i=0; i<wavData.dataByteSize; ++i)
+ {
+ data[i] = static_cast<int8_t>(static_cast<const uint8_t*>(wavData.data)[i] - 128);
+ }
+
+ if (data.size() == 64)
+ {
+ xt::WaveData d;
+ for (size_t i = 0; i < 64; ++i)
+ {
+ d[i] = data[i];
+ d[127 - i] = static_cast<int8_t>(dsp56k::clamp(-data[i], -128, 127));
+ }
+ return d;
+ }
+
+ if (data.size() == 128)
+ {
+ xt::WaveData d;
+ for (size_t i = 0; i < 128; ++i)
+ d[i] = data[i];
+
+ bool valid = true;
+
+ for (size_t i=0; i<64; ++i)
+ {
+ if (d[i] != -d[127 - i])
+ {
+ valid = false;
+ break;
+ }
+ }
+
+ if (valid)
+ return d;
+
+ genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, productName + " - Error", "Unsupported file\n" + _filename + ".\n\n" +
+ "An imported file with a size of 128 samples needs to be a full-cycle wave, i.e. the second half of the file needs to be the first half reversed and inverted.\n" +
+ "Correct the file or import a file of length 64 only (half-cycle wave). In the latter case, building a full cycle wave is done during import."
+ + formatDesc);
+ return {};
+ }
+
+ genericUI::MessageBox::showOk(juce::AlertWindow::WarningIcon, productName + " - Error", "Unsupported size of file\n" + _filename + "." + formatDesc);
+ return {};
+ }
}
diff --git a/source/xtJucePlugin/xtWaveEditor.h b/source/xtJucePlugin/xtWaveEditor.h
@@ -64,6 +64,8 @@ namespace xtJucePlugin
void exportAsWav(const xt::WaveData& _data);
void exportAsWav(const std::string& _filename, const xt::WaveData& _data) const;
+ std::optional<xt::WaveData> importWaveFile(const std::string& _filename) const;
+
private:
// ComponentMovementWatcher
void componentVisibilityChanged() override { checkFirstTimeVisible(); }
@@ -99,5 +101,7 @@ namespace xtJucePlugin
xt::WaveId m_selectedWave;
WaveEditorStyle m_style;
+
+ std::unique_ptr<juce::FileChooser> m_fileChooser;
};
}