Skip to content

Commit ce02327

Browse files
committed
Fix sampler failing to locate embedded sample files during save as
1 parent f1dbc90 commit ce02327

4 files changed

Lines changed: 84 additions & 1 deletion

File tree

CHANGELOG

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ Bug fixes:
3131

3232
* Fix song position clamping when song length decreases
3333

34+
* Fix sampler failing to locate embedded sample files during save as
35+
3436
Other:
3537

3638
* Update user manual with pan column documentation

src/domain/devices/sampler_device.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,11 @@ void SamplerDevice::loadSample(uint8_t note, const std::string & filePath)
491491
m_audioFileReader->close();
492492

493493
auto sample = std::make_unique<Sample>();
494-
sample->filePath = filePath;
494+
if (QString::fromStdString(filePath).startsWith(Constants::NahdXml::embeddedDataPathPrefix())) {
495+
sample->filePath = filePath;
496+
} else {
497+
sample->filePath = absolutePath.toStdString();
498+
}
495499
sample->channels = info.channels;
496500
sample->sampleRate = info.samplerate;
497501
sample->data = std::move(data);

src/unit_tests/xml_serialization_test/xml_serialization_test.cpp

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,82 @@ void XmlSerializationTest::test_toXmlFromXml_samplerDevice_relativePath_shouldLo
878878
QCOMPARE(samplerIn->absoluteFilePath(60), absolutePath);
879879
}
880880

881+
void XmlSerializationTest::test_toXmlFromXml_samplerDevice_saveAs_shouldPreserveEmbeddedData()
882+
{
883+
const std::string projectPath { "/tmp/noteahead_test" };
884+
const std::string relativePath { "samples/kick.wav" };
885+
const std::string absolutePath { QDir(QString::fromStdString(projectPath)).absoluteFilePath(QString::fromStdString(relativePath)).toStdString() };
886+
887+
// Create the dummy directory and file on disk so serializeDataToXml can open it
888+
QDir().mkpath(QFileInfo(QString::fromStdString(absolutePath)).absolutePath());
889+
QFile dummyFile { QString::fromStdString(absolutePath) };
890+
QVERIFY(dummyFile.open(QIODevice::WriteOnly));
891+
dummyFile.write("dummy-wav-data");
892+
dummyFile.close();
893+
894+
const auto samplerName = "Noteahead Internal Device 1";
895+
896+
const auto engine = std::make_shared<AudioEngine>();
897+
const auto dataService = std::make_shared<DataService>();
898+
DeviceService deviceServiceOut { engine, dataService };
899+
deviceServiceOut.setProjectPath(projectPath);
900+
901+
const auto samplerOut = std::make_shared<SamplerDevice>(samplerName, std::make_unique<MockAudioFileReader>());
902+
samplerOut->loadSample(60, absolutePath);
903+
samplerOut->setEmbedWaveData(true);
904+
deviceServiceOut.setDevice(0, samplerOut);
905+
906+
EditorService editorServiceOut { std::make_shared<SelectionService>(), std::make_shared<SettingsService>(), std::make_shared<AutomationService>(std::make_shared<PropertyService>()), dataService };
907+
connect(&editorServiceOut, &EditorService::devicesSerializationRequested, &deviceServiceOut, &DeviceService::serializeToXml);
908+
connect(&editorServiceOut, &EditorService::dataSerializationRequested, [&deviceServiceOut, dataService](ProjectWriter & writer) {
909+
const auto files = deviceServiceOut.getFilesToEmbed();
910+
dataService->serializeDataToXml(writer, files);
911+
});
912+
913+
const auto xml = editorServiceOut.toXml();
914+
915+
// Now load it in a new setup
916+
const auto engine2 = std::make_shared<AudioEngine>();
917+
const auto dataService2 = std::make_shared<DataService>();
918+
DeviceService deviceServiceIn { engine2, dataService2 };
919+
deviceServiceIn.setProjectPath(projectPath);
920+
921+
const auto samplerIn = std::make_shared<SamplerDevice>(samplerName, std::make_unique<MockAudioFileReader>());
922+
deviceServiceIn.setDevice(0, samplerIn);
923+
924+
EditorService editorServiceIn { std::make_shared<SelectionService>(), std::make_shared<SettingsService>(), std::make_shared<AutomationService>(std::make_shared<PropertyService>()), dataService2 };
925+
connect(&editorServiceIn, &EditorService::devicesDeserializationRequested, &deviceServiceIn, &DeviceService::deserializeFromXml);
926+
connect(&editorServiceIn, &EditorService::devicesSerializationRequested, &deviceServiceIn, &DeviceService::serializeToXml);
927+
connect(&editorServiceIn, &EditorService::dataSerializationRequested, [&deviceServiceIn, dataService2](ProjectWriter & writer) {
928+
const auto files = deviceServiceIn.getFilesToEmbed();
929+
dataService2->serializeDataToXml(writer, files);
930+
});
931+
932+
editorServiceIn.fromXml(xml);
933+
934+
// Verify it is loaded and its path in memory is absolute (our fix!)
935+
QVERIFY(samplerIn->sample(60));
936+
const auto expectedMemoryPath = samplerIn->sample(60)->filePath;
937+
// It should start with nahd:// because it was deserialized as embedded
938+
QVERIFY(QString::fromStdString(expectedMemoryPath).startsWith(Constants::NahdXml::embeddedDataPathPrefix()));
939+
940+
// Now simulate "Save As" by changing project path to a new location
941+
const std::string newProjectPath { "/tmp/noteahead_test_new" };
942+
deviceServiceIn.setProjectPath(newProjectPath);
943+
944+
// Serialize again! If the bug exists, this will fail or serialize empty data
945+
// because absoluteFilePath(60) would be resolved relative to the new path and point to a non-existent file
946+
const auto xml2 = editorServiceIn.toXml();
947+
948+
// Verify the second XML has the embedded data block
949+
QVERIFY(xml2.contains("<Data"));
950+
QVERIFY(xml2.contains("nahd://kick.wav"));
951+
952+
// Cleanup
953+
QFile::remove(QString::fromStdString(absolutePath));
954+
QDir().rmdir(QFileInfo(QString::fromStdString(absolutePath)).absolutePath());
955+
}
956+
881957
void XmlSerializationTest::test_toXmlFromXml_synthDevice_shouldPreserveValuesAndDiscreteFlags()
882958
{
883959
const auto synthName = "Noteahead Internal Device 1";

src/unit_tests/xml_serialization_test/xml_serialization_test.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ private slots:
6060
void test_toXmlFromXml_trackDrumTrack_shouldLoadTrackDrumTrack();
6161
void test_toXmlFromXml_samplerDevice_shouldLoadSamplerDevice();
6262
void test_toXmlFromXml_samplerDevice_relativePath_shouldLoadCorrectly();
63+
void test_toXmlFromXml_samplerDevice_saveAs_shouldPreserveEmbeddedData();
6364
void test_toXmlFromXml_synthDevice_shouldPreserveValuesAndDiscreteFlags();
6465
void test_toXmlFromXml_synthUserPresets_shouldSaveAndLoad();
6566
void test_toXmlFromXml_synthUserPresets_discreteValues_shouldSaveAndLoad();

0 commit comments

Comments
 (0)