Reading from multiple streams and play them together

Hi all!

I am trying to read multiple streams from the locustream Soundmap (oldie but goldie) and play them all at once using this quark (it has cURL, vorbis-tools and lame as dependencies) and it works pretty well as long as I am reading only one stream. As soon as I load more than one, the stream seems not to refresh anymore and I hear only a loop until the process freezes (while the server is still playing sound and accepting CmdPeriod) and I get a bunch of LAME errors like these:

big_values too large! 339
hip: Can't rewind stream by 383 bits!
hip: bitstream problem, resyncing skipping 44903 bytes...
hip: bitstream problem, resyncing skipping 104568 bytes...

It looks like it tries to use the same buffer or something like that…

So without waiting for a finite answer from you (because it could be a problem from the Quark or from the mkfifo mechanism it uses under the hood), could someone please try the code and tell me if the behavior can be reproduced? (UNIX only - I am on Pop_OS and would be curious to know if it works on MacOS…)

It is the only solution I found to do it without reinventing the wheel. Does anyone know if some other Quarks, or some built-in ways I am not aware of, allow reading from mp3/ogg streams?

Maybe there is something in the code that is utterly bad or some logic mistakes I can’t see because I am still trying to get my head around confusing SC…
Some checks are missing ((if buffer == nil) for instance. I remove them for the sake of clarity.

Here is my code:

(
Server.default.quit;
Server.default.waitForBoot({

  var api_list_url = "https://locusonus.org/soundmap/list/active/json/name";

  ~sources = [];
  
  ~streams = ( 
    query: {|self|
      self.raw_active = "curl %".format(api_list_url).unixCmdGetStdOut(9999999999).parseJSON;
      self.raw_active;
    },
    get_items: {|self ... keys|
      if (keys.size == 0, {
        self.raw_active;
      },{
        self.raw_active.collect({|data|
          var tmp_dict = Dictionary.new;
          keys.do {|key|
            tmp_dict.put(key.asSymbol, data[key.asString]);
          };
          tmp_dict;
        });
      });
    },
    get_item: {|self, idx ... keys|
      var tmp_data = self.raw_active[idx];
      if (keys.size == 0,
      {
        tmp_data;
      },
      {
        var tmp_dict = Dictionary.new;
        keys.do {|key| tmp_dict.put(key.asSymbol, tmp_data[key.asString])};
        tmp_dict;
      });
    }
  );

  SynthDef(\source, {
    var mtx_bus = \mtx_bus.kr(0);
    var stream_data = DiskIn.ar(2, \buffer.kr(0));
    Out.ar(mtx_bus, stream_data[0]);
    Out.ar(\out.kr(0), stream_data[1]);
  }).add;

  ~source_proto = (
    init: {| self, stream_obj |
      self.mp3 = MP3(stream_obj["url"], \readurl, stream_obj["ext"]);
      self.buffer = Buffer.cueSoundFile(s, self.mp3.fifo, 0, 2);
      self.audio = Synth.newPaused(\source, [\buffer, self.buffer.bufnum], addAction: \addToTail);
    },
    activate: {| self |
      self.mp3.start;
      self.audio.run(true);
    },
    deactivate: {| self |
      self.audio.run(false);
      self.mp3.stop;
    },
    change: {| self, stream_obj |
        self.audio.run(false);
        self.mp3.stop;
        self.buffer.free;
        self.mp3 = MP3(stream_obj["url"], \readurl, stream_obj["ext"]);
        self.buffer = Buffer.cueSoundFile(s, self.mp3.fifo, 0, 2);
        self.audio.set(\buffer, self.buffer.bufnum);
        self.activate();
    },
    set_hw_out_channel: {|self, out|
      self.audio.set(\out, out);
    }
  );

  ~streams.query();
});
)

// create the sources
(
// cheap random mechanism
var available_idx = (0..~streams.get_items.size - 1);

// works with 1, not with >1
1.do {
  var tmp_idx = available_idx.removeAt(rrand(0, available_idx.size -1));
  var stream_obj = ~streams.get_item(tmp_idx);
  var source = Event.new(proto: ~source_proto);
  tmp_idx.postln;
  source.init(stream_obj);
  ~sources = ~sources.add(source);
};
)
// starting them
~sources[0].activate;
~sources[0].mp3.path;
// hear the second channel on the right channel. I need it for later...
~sources[0].set_hw_out_channel(1)
~sources[0].mp3.hash

Thank you very much in advance for your time and answers!

PS: I also tried to recompile LAME against libsndfile 1.1 as mentioned in the issues, but no luck.

Edit: I realized that the last stream of the list works. Only the first ones don’t. I am trying to check why cURL is not grabbing new data and/or why LAME is not writing. It looks like a parallelism problem.