mc support with min-api for

    Dev

    domkirke
    Apr 11 2022 | 4:01 pm
    Hello everyone! We are currently developing the nn~ library with the min-api, that allows to embed any TorchScript model on Max/MSP in a light manner: https://github.com/acids-ircam/nn_tilde
    A multi-channel support would be a great add-on for the usability of our package ; unfortunately, we do not find any information/resource on how to do that with the min-api. Is this even possible and, if it is, where could we find the documentation? Thanks a lot! The ACIDS-IRCAM team

    • Share
    • 👽R∆J∆ The Resident ∆lien👽's icon
      👽R∆J∆ The Resident ∆lien👽's icon
      for the min-api (C++ based), i've not found much either beyond this: https://github.com/Cycling74/min-devkit/blob/main/source/projects/mc.min.info_tilde/mc.min.info_tilde.cpp
      but this project sounds amazing, (i've only learned a bit of 'PyTorch', facebook developer's python extension of the proper C++ based 'Torch'... very impressive as is), wishing you the best of luck!
    • domkirke's icon
      domkirke's icon
      domkirke
      Apr 14 2022 | 3:52 pm
      Thank you for your answers! Unfortunately, max-sdk is not compatible with the min-api, and the given example code analyzes MC inputs but does not show how to produce ones. But thank you anyway! :)
    • testcase's icon
      testcase's icon
      testcase
      Apr 18 2022 | 12:40 am
      first I am just curious - have you wrapped the object using the mc wrapper and that is not the capability you need?
    • domkirke's icon
      domkirke's icon
      domkirke
      Apr 18 2022 | 10:58 am
      We have not tried, because indeed it is not the specific purpose of what we need ; indeed (tell me if I am wrong), wrapping the external for xN channels would require to duplicate the ML model xN times right? However, if you got any documentation on how to perform mc wrapping, it is always welcome :-)
    • Isabel Kaspriskie's icon
      Isabel Kaspriskie's icon
      Isabel Kaspriskie
      Apr 18 2022 | 1:58 pm
      Hi @domkirke,
      You can access max-sdk methods by using the `max::` namespace provided by the max-sdk-base. While it may not be as "clean" as the min API, it opens up to all of the possibilities that the Max SDK offers.
    • testcase's icon
      testcase's icon
      testcase
      Apr 18 2022 | 5:43 pm
      (edited to fix some typos) @domkirke, the information on the wrapper is here.
      https://cycling74.com/sdk/max-sdk-8.2.0/chapter_mc.html
      if that isn't what you need (which is sounds like you don't) I don't think there is any class in the min-api that will work as is *without* mixing the min-api and max-sdk (the non wrapper mc stuff is also explained in link above). while you certainly don't want to arbitrarily mix these two I have found that there are ways you can safely do so. Unfortunately the min-api seems to be missing some essential things and the documentation is sparse so it takes some digging through the code and asking for help.
      I was curious after seeing your question about how one could do this and I came up with a simple bit of code that seems to work to show the concept of what I think you need to do. you will note the use of the maxclass_setup message to call things needed from the max-sdk, also the use of the C function which is getting the number of channels by accessing the chans attribute and how the minwrap casting works. there is no error checking in this and I have not tested it extensively but see if it makes sense. also note multichannelsignal on the outlet definition
      #include "c74_min.h"
      
      using namespace c74::min;
      
      
      
      long simplemc_multichanneloutputs(c74::max::t_object* x, long index, long count);
      
      class simplemc_tilde : public object<simplemc_tilde>, public vector_operator<> {
      public:
          MIN_DESCRIPTION		{ "simple out" };
          MIN_TAGS			{ "misc" };
          MIN_AUTHOR			{ "anonymous" };
          MIN_RELATED			{ "cycle~" };
      
          inlet<>  m_inlet 			{ this, "messages" };
          outlet<> m_outlet		{ this, "(multichannelsignal) output", "multichannelsignal" };
          
          attribute<int> chans {this, "chans", 1,
              range { 1, 1024 }
              
          };
      
          void operator()(audio_bundle input, audio_bundle output) {
              
              for (auto i = 0; i < output.frame_count(); ++i) {
                  
                  for (auto channel = 0; channel < output.channel_count(); ++channel) {
                      auto    out  = output.samples();
                      out[channel][i] = channel; // will just outputs channel number 
                  }
      
      
              }
          }
          
          message<> maxclass_setup {this, "maxclass_setup",
              MIN_FUNCTION {
                  c74::max::t_class* c = args[0];
                  c74::max::class_addmethod(c, (c74::max::method)simplemc_multichanneloutputs, "multichanneloutputs", c74::max::A_CANT, 0);
      
                  return {};
              }
          };
      
      };
      
      MIN_EXTERNAL(simplemc_tilde);
      
      
      
      long simplemc_multichanneloutputs(c74::max::t_object* x, long index, long count) {
          minwrap<simplemc_tilde>* ob = (minwrap<simplemc_tilde>*)(x);
          return ob->m_min_object.chans;
      }
    • domkirke's icon
      domkirke's icon
      domkirke
      Apr 22 2022 | 4:03 pm
      Hello Testcase! Thank you so much for you answer. Indeed, this seems to be an interesting solution, we'll investigate that next week and we'll post here how it came :-)
    • benedikt.sailer's icon
      benedikt.sailer's icon
      benedikt.sailer
      May 24 2022 | 5:34 am
      Just used this approach to automatically sync the number of output channels to the number of input channels my external receives. Works like a charm. It's important to note, that the chans variable from the example above NEEDS to be of type min::attribute<int> and can't just be a long, even though that's the return type of thelong simplemc_multichanneloutputs () function. When I tried just using a long class member max crashed...
    • domkirke's icon
      domkirke's icon
      domkirke
      May 24 2022 | 10:16 am
      Hello folks! We managed to develop a mc~ support thanks to your advices, everything is here : https://github.com/acids-ircam/nn_tilde
      Hope that could help people wanting to develop mc support with the min-api. Thank you so much !
    • Christopher Poovey's icon
      Christopher Poovey's icon
      Christopher Poovey
      Jul 03 2022 | 2:59 pm
      Hello everyone,
      This forum post has been very useful in getting me started, but I am having an issue just passing a multichannel signal through an external. I am quite new to authoring externals and it is likely I am doing something silly. The external adapts to an incoming mc signal fine, but it has issues reading the input channel and outputting it. I was indexing via the output channel before and it was crashing max and when I index from the input channel it does not pass the values correctly. I attached my cpp file and here is the code for the operator function. void operator()(audio_bundle input, audio_bundle output) {
      void operator()(audio_bundle input, audio_bundle output) {
      
              for (auto i = 0; i < input.frame_count(); ++i) {
                  
                  for (auto channel = 0; channel < input.channel_count(); ++channel) {
                      output.samples(channel)[i] = input.samples(channel)[i];
                  }
              }
          }
      mcpass.cpp
      text/plain 1.87 KB
    • domkirke's icon
      domkirke's icon
      domkirke
      Jul 19 2022 | 3:46 pm
      Hello Christopher! I don't know if you fixed the issue or not, but it could be helpful to print the number of channels / inputs / bundle size, you may ask for a channel that does not exist. Our code is here, if it could bring you some interesting insights! Cheers
    • S Sheta's icon
      S Sheta's icon
      S Sheta
      Apr 11 2023 | 5:18 pm
      @BENEDIKT.SAILER Hi, it's been a while since this thread was posted but I was wondering if you could clarify how you adapted Testcase's code to sync the number of output channels to the number of input channels?