ddf / Minim

A Java audio library, designed to be used with Processing.
http://code.compartmental.net/tools/minim
GNU Lesser General Public License v3.0
668 stars 136 forks source link

Calling .close on one of two simultaneously loaded AudioPlayer instances of the same file also closes the other #77

Closed markkorput closed 5 years ago

markkorput commented 6 years ago

I ran into this issue while simultaneously loading the same audio file twice (in separate threads); when the first instance calls the close() method on the loaded AudioPlayer, this will affect the second instance; which will never play.

In our project we're using our own threaded asynchronous audio loader which at some point uses minim.loadFile(filename) to do the actual file loading and passes the AudioPlayer instance to the caller's callback (see -simplified- code below):

// load instance1; this call starts a separate thread where the loading is done
loadAudioAsync("path/to/my/soundtrack.mp3").whenAvailable((AudioPlayer player1) -> {
  player1.close(); // <-- because of this the second instance never plays
});

// DELAY1

// load instance2; this starts when the thread of the first call is not finished yet
loadAudioAsync("path/to/my/soundtrack.mp3").whenAvailable((AudioPlayer player2) -> {
  // DELAY2
  player2.play(); // <-- this never plays
});

NOTE1 if I put a bit of delay before loading the second instance (see DELAY1) there is no problem so it seems like the problem occurs only if the second instance is already (partially) loaded when the first instance calls the close() method.

NOTE2 the problem still occurs (no sound plays) when I put a delay AFTER loading the 2nd instance, but BEFORE calling the AudioPlayer.play() method (see DELAY2), which seems to indicate that both AudioPlayer instances are pointed at some shared resource during initialisation, not when playback starts.

I had a quick-look at the Minim code but couldn't find the exact location of the issue. We're using processing (2 and 3) so my guess is that the PApplet.createInput method might be doing some internal caching.

ddf commented 5 years ago

Yes, I'd agree it is most likely a shared resource, possibly down at the InputStream level, which would put it outside the ability of Minim to deal with, I think, since Minim relies on Processing to provide an InputStream for files.

ddf commented 5 years ago

I tested with the v2.2.2 distribution and I wasn't able to reproduce this in Processing 3.3.3 or 3.3.7. Here's the sketch I wrote, which is a modification of the PlayAFile example, based on your description above. Does this look close enough to your async loader code?

import ddf.minim.*;

Minim minim;

class LoadAndClose implements Runnable
{
  public void run()
  {
    minim.loadFile("groove.mp3").close();
  }
}

class LoadAndPlay implements Runnable
{
  public void run()
  {
    minim.loadFile("groove.mp3").play();
  }
}

void setup()
{
  size(512, 200, P3D);

  // we pass this to Minim so that it can load files from the data directory
  minim = new Minim(this);

  new Thread(new LoadAndClose()).start();
  new Thread(new LoadAndPlay()).start();
}

void draw()
{
  background(0);
  stroke(255); 
}
markkorput commented 5 years ago

Hey Damien, thanks for looking into this, but this is all my bad; I didn't take my async loader out of the equation and I just figured out that it is actually there where active requests are recycled so both async requests receive the same AudioPlayer instance. Sorry for wasting your time '-.-

ddf commented 5 years ago

No worries, I don't consider it a waste. Thanks for letting me know!