HISE Logo Forum
    • Categories
    • Register
    • Login

    Feature request: extract XYZ data metadata

    Scheduled Pinned Locked Moved Solved Feature Requests
    19 Posts 4 Posters 876 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • griffinboyG
      griffinboy
      last edited by griffinboy

      Could we please get more helpers for getting the xyz (samplemap) metadata in c++?

      Actual Data:
      0ce5f17e-a901-4533-a52f-1e5f0849918b-image.png

      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
      
      
      griffinboyG 1 Reply Last reply Reply Quote 1
      • griffinboyG griffinboy marked this topic as a question
      • griffinboyG
        griffinboy @griffinboy
        last edited by

        @griffinboy

        Definitely a question for @Christoph-Hart this one!
        Following on from our discussion about the XYZ data.

        griffinboyG 1 Reply Last reply Reply Quote 0
        • griffinboyG
          griffinboy @griffinboy
          last edited by

          @griffinboy

          Womp womp bump

          d.healeyD 1 Reply Last reply Reply Quote 0
          • d.healeyD
            d.healey @griffinboy
            last edited by

            @griffinboy Ha you're impatient today 😆

            Libre Wave - Freedom respecting instruments and effects
            My Patreon - HISE tutorials
            YouTube Channel - Public HISE tutorials

            griffinboyG 1 Reply Last reply Reply Quote 1
            • griffinboyG
              griffinboy @d.healey
              last edited by griffinboy

              @d-healey

              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.

              Christoph HartC 1 Reply Last reply Reply Quote 0
              • Christoph HartC
                Christoph Hart @griffinboy
                last edited by

                @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?

                griffinboyG 1 Reply Last reply Reply Quote 1
                • griffinboyG
                  griffinboy @Christoph Hart
                  last edited by griffinboy

                  @Christoph-Hart

                  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 😆

                  DanHD 1 Reply Last reply Reply Quote 0
                  • griffinboyG griffinboy marked this topic as a regular topic
                  • DanHD
                    DanH @griffinboy
                    last edited by

                    @griffinboy @Christoph-Hart 🙏

                    DHPlugins / DC Breaks | Artist / Producer / DJ / Developer
                    https://dhplugins.com/ | https://dcbreaks.com/
                    London, UK

                    Christoph HartC 1 Reply Last reply Reply Quote 0
                    • Christoph HartC
                      Christoph Hart @DanH
                      last edited by

                      Does your project really have different xfade values for samplemaps or can you just use a project wide constant for this?

                      griffinboyG 1 Reply Last reply Reply Quote 0
                      • griffinboyG
                        griffinboy @Christoph Hart
                        last edited by

                        @Christoph-Hart

                        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

                        Christoph HartC 1 Reply Last reply Reply Quote 0
                        • Christoph HartC
                          Christoph Hart @griffinboy
                          last edited by

                          @griffinboy but the real loop start is correct in your first post, no?

                          griffinboyG 2 Replies Last reply Reply Quote 0
                          • griffinboyG
                            griffinboy @Christoph Hart
                            last edited by

                            @Christoph-Hart

                            Oh you're correct, because it's chopped off the start of the sample

                            1 Reply Last reply Reply Quote 0
                            • griffinboyG griffinboy marked this topic as a question
                            • griffinboyG griffinboy has marked this topic as solved
                            • griffinboyG
                              griffinboy @Christoph Hart
                              last edited by

                              @Christoph-Hart

                              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.

                              griffinboyG 1 Reply Last reply Reply Quote 0
                              • griffinboyG
                                griffinboy @griffinboy
                                last edited by

                                @griffinboy

                                @Christoph-Hart

                                Bump! Sorry

                                griffinboyG 1 Reply Last reply Reply Quote 0
                                • griffinboyG
                                  griffinboy @griffinboy
                                  last edited by

                                  @griffinboy
                                  @Christoph-Hart
                                  @DanH

                                  Sorry to bother you! Important! : )

                                  Christoph HartC 1 Reply Last reply Reply Quote 0
                                  • Christoph HartC
                                    Christoph Hart @griffinboy
                                    last edited by Christoph Hart

                                    @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 into loadFile() 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?

                                    griffinboyG 2 Replies Last reply Reply Quote 2
                                    • griffinboyG
                                      griffinboy @Christoph Hart
                                      last edited by griffinboy

                                      @Christoph-Hart

                                      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.

                                      0bea0a38-8bf5-4a94-aef5-218b2d39e383-image.png

                                      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.

                                      1 Reply Last reply Reply Quote 0
                                      • griffinboyG
                                        griffinboy @Christoph Hart
                                        last edited by

                                        @Christoph-Hart

                                        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!

                                        DanHD 1 Reply Last reply Reply Quote 0
                                        • DanHD
                                          DanH @griffinboy
                                          last edited by

                                          @Christoph-Hart client was me 😬

                                          DHPlugins / DC Breaks | Artist / Producer / DJ / Developer
                                          https://dhplugins.com/ | https://dcbreaks.com/
                                          London, UK

                                          1 Reply Last reply Reply Quote 2
                                          • First post
                                            Last post

                                          12

                                          Online

                                          1.9k

                                          Users

                                          12.5k

                                          Topics

                                          108.6k

                                          Posts