Feature request: extract XYZ data metadata
-
Could we please get more helpers for getting the xyz (samplemap) metadata in c++?
Actual Data:
Extracted Data:
[FileReader] Mode: XYZ multisample provider: SampleMap items: 2 item 0 map : key=[60..91] vel=[0..128] rr=1 root=69.00 audio: ch=2 samples=159976 sr=48000.000 loop : [45..149976] item 1 map : key=[23..60] vel=[0..128] rr=1 root=48.00 audio: ch=2 samples=159976 sr=48000.000 loop : [45..149976]
^ Here is the data I was able to pull out of a samplemap / monolyth using the xyz data in c++
I noticed that some data is missing and incorrect!
We are missing:- SampleStart
- XfadeLength
- LoopStart.
Additionally 'loopstart' that xyz data gives us, seems to actually be (loopstart - samplestart) rather than the actual loopstart!
Here is the code I used to extract the data:
// FILE: FileReader.h /* ExternalData XYZ samplemap inspector */ #pragma once #include <JuceHeader.h> #if JUCE_WINDOWS #include <windows.h> #endif namespace project { using namespace juce; using namespace hise; using namespace scriptnode; template <int NV> struct FileReader : public data::base { SNEX_NODE(FileReader); struct MetadataClass { SN_NODE_ID("FileReader"); }; static constexpr bool isModNode() { return false; } static constexpr bool isPolyphonic() { return NV > 1; } static constexpr bool hasTail() { return false; } static constexpr bool isSuspendedOnSilence() { return false; } static constexpr int getFixChannelAmount() { return 2; } static constexpr int NumTables = 0; static constexpr int NumSliderPacks = 0; static constexpr int NumAudioFiles = 1; static constexpr int NumFilters = 0; static constexpr int NumDisplayBuffers = 0; void prepare(PrepareSpecs) {} void reset() {} void handleHiseEvent(HiseEvent&) {} template <typename T> void process(T& d) { static_cast<void>(d); } template <typename T> void processFrame(T&) {} int handleModulation(double&) { return 0; } void setExternalData(const ExternalData& data, int index) { if (index != 0) return; extData = data; dumpExternal(extData); } template <int P> void setParameter(double v) { static_cast<void>(v); if (P == 0) {} } void createParameters(ParameterDataList& list) { parameter::data p("Dummy", { 0.0, 1.0 }); registerCallback<0>(p); p.setDefaultValue(0.0); list.add(std::move(p)); } private: ExternalData extData; /* Dumps all accessible metadata for the bound ExternalData. */ void dumpExternal(const ExternalData& ed) { if (ed.obj == nullptr) { logLine("[FileReader] No ExternalData object set."); return; } if (!SimpleReadWriteLock::ScopedTryReadLock(ed.obj->getDataLock())) { logLine("[FileReader] Could not lock ExternalData for reading."); return; } auto* m = static_cast<MultiChannelAudioBuffer*>(ed.obj); if (!ed.isXYZ()) { logLine("[FileReader] Mode: Single sample"); const auto& buf = m->getBuffer(); const int ch = buf.getNumChannels(); const int ns = buf.getNumSamples(); const double sr = m->sampleRate; const auto total = m->getTotalRange(); const auto current = m->getCurrentRange(); const auto loopAbs = m->getLoopRange(false); const auto loopRel = m->getLoopRange(true); logLine(" audio: ch=" + String(ch) + " samples=" + String(ns) + " sr=" + String(sr, 3)); logLine(" range: total=" + rangeStr(total) + " current=" + rangeStr(current)); logLine(" loop : abs=" + rangeStr(loopAbs) + " rel=" + rangeStr(loopRel)); return; } logLine("[FileReader] Mode: XYZ multisample"); logLine(" provider: " + m->getCurrentXYZId().toString()); logLine(" items: " + String(ed.numSamples)); auto* items = static_cast<MultiChannelAudioBuffer::XYZItem*>(ed.data); for (int i = 0; i < ed.numSamples; ++i) { logLine(" item " + String(i)); const auto& it = items[i]; logLine(" map : key=" + rangeStr(it.keyRange) + " vel=" + rangeStr(it.veloRange) + " rr=" + String(it.rrGroup) + " root=" + String(it.root, 2)); int ch = 0, ns = 0; double sr = 0.0; Range<int> lr; String ref; if (it.data != nullptr) { ch = it.data->buffer.getNumChannels(); ns = it.data->buffer.getNumSamples(); sr = it.data->sampleRate; lr = it.data->loopRange; ref = it.data->reference; } logLine(" audio: ch=" + String(ch) + " samples=" + String(ns) + " sr=" + String(sr, 3)); logLine(" loop : " + (lr.getLength() > 0 ? rangeStr(lr) : String("-"))); if (ref.isNotEmpty()) logLine(" ref : " + ref); } } String rangeStr(const Range<int>& r) const { return "[" + String(r.getStart()) + ".." + String(r.getEnd()) + "]"; } void logLine(const String& s) { #if JUCE_WINDOWS const auto msg = (s + "\r\n").toStdString(); OutputDebugStringA(msg.c_str()); #else Logger::writeToLog(s); #endif } }; } // namespace project
-
G griffinboy marked this topic as a question
-
Definitely a question for @Christoph-Hart this one!
Following on from our discussion about the XYZ data. -
Womp womp bump
-
@griffinboy Ha you're impatient today
-
Yeah it's naughty of me
The timeline of this project I'm working on became really stretched, so now that I'm finally approaching the end I'm too excited to resolve the final barriers.
-
@griffinboy that's not so easy, the buffer that is passed with the ExternalData is already truncated with the sample-start and sample-end values. But why do you need this, just playback the part you want to play?
-
Wait, does the xfade get baked into the sound file then?
My client was hoping to take some sample maps that have already been created, and load them into a custom c++ sampler.
And then mess around with the loop points etc.
But if what you're saying is true, then I can't do that
-
G griffinboy marked this topic as a regular topic
-
-
Does your project really have different xfade values for samplemaps or can you just use a project wide constant for this?
-
I think we could maybe get away with having a default length.
Knowing the real loop start is important though, which I can't extract it seems -
@griffinboy but the real loop start is correct in your first post, no?
-
Oh you're correct, because it's chopped off the start of the sample
-
G griffinboy marked this topic as a question
-
G griffinboy has marked this topic as solved
-
Seperate but related question:
How does one load a samplemap (xyz) into a c++ node from hise script?
And can the hisescript read the sample map metadata?I'm wanting the hise script to be able to read out the saved loop points, so that as we load in a samplemap into the c++ sampler node, we can set the loop point parameters correctly.
else I'll have to do a funny loop where we use global cables to send the loop points extracted from the xyz data inside the node, into hise, which then uses that info to set the parameters on the c++ node.
-
-
@griffinboy
@Christoph-Hart
@DanHSorry to bother you! Important! : )
-
@griffinboy sorry I'm on vacation right now so my ability to write code is limited.
In order to load a sample map dynamically, you can use the
{XYZ::SampleMap}
wildcard, followed by the samplemap ID (without the .xml extension). So if your samplemap is called Piano.xml, then the string you pass intoloadFile()
is"{XYZ::SampleMap}Piano"
.so that as we load in a samplemap into the c++ sampler node, we can set the loop point parameters correctly.
Remind me again, what's the problem with the loop points? They should be passed on correctly with the sample start applied to the range already, no?
-
edit This issue is solved. Read my message below this post.
okay the issue is that when we load a sample map into the c++ sampler, we need the Loop metadata to set the Loop Parameters of the c++ node.
Sure, I can just load in a sample map and internally use the loop values, the result will sound correct. However, the actual parameters will be in the wrong locations.
-
Update
You can forget it! Client decided that they didn't need this feature. Thanks for your help thus far. I no longer need to solve this!
-
@Christoph-Hart client was me