Ignotus-mago / PixelAudio

PixelAudio maps arrays of audio samples onto arrays of pixel values.
Other
0 stars 0 forks source link

Concurrency for Audio and Animation #17

Open Ignotus-mago opened 1 month ago

Ignotus-mago commented 1 month ago

This is going to be a large issue. I have mostly handled it with Timer and TimerTask for calls that run in comparatively short periods of time.

shiftTimer = new java.util.Timer();
shiftTimer.schedule(
  new java.util.TimerTask() {
    public void run() {
      runShiftTracker();
    }
  },
  32, shiftInterval
);

Concurrency became a problem in WaveSynth when opening a JSON file to load new values. The file i/o is handled in its own thread while animation continues, which is desirable in a performance. Once a file is selected, there may be an ArrayIndexOutOfBoundsException -- or maybe not. That seems to happen when the JSON data changes the size of an array precisely in the middle of the animation loop. My solution was to add the keyword synchronized so that the loop would complete before any variables were changed:

public synchronized void renderFrame(int frame) {
  // load variables with prepareAnimation() at start of animation loop
  if (frame == 0)
    prepareAnimation();
  // loop through the image/signal, calculating a value for each pixel/sample
  for (int i = 0; i < this.mapSize; i++) {
    this.colorSignal[i] = this.renderPixel(frame, i);
  }
  // write colorSignal's pixel color values to mapImage.pixels
  this.mapper.plantPixels(colorSignal, mapImage.pixels, 0, mapSize);
  this.mapImage.updatePixels();
  // set our internal step variable, just a tracker for now
  this.setStep(frame);
}

// render one pixel, return its RGB value
public int renderPixel(int frame, int pos) {
  float freqShift = 1.0f;
  for (int j = 0; j < dataLength; j++) {
    WaveData wd = waveDataList.get(j);
    if (wd.isMuted || wd.waveState == WaveData.WaveState.SUSPENDED)
      continue;
    float val = (wd.waveValue(frame, pos, freqShift, mapInc) + woff) * wscale + wd.dc;
    weights[j] = val * wd.amp * this.gain;
  }
  return this.weightedColor(waveColors, weights);
}

Now I am getting a warning in the console:

2024-08-02 10:49:57.739 java[39825:8591584] +[CATransaction synchronize] called within transaction

A little research reveals this to be a MacOS event from Core Animation. I am not sure is this is a problem or not. For the moment, everything seems to work as desired. Should I be concerned?

Ignotus-mago commented 1 month ago

I've been handling concurrency so far with Timer objects:

case 'c': 
  isAutoShift = !isAutoShift;
  if (isAutoShift) {
    shiftTimer = new java.util.Timer();
    shiftTimer.schedule(
      new java.util.TimerTask() {
        public void run() {
          runShiftTracker();
        }
      },
      32, shiftInterval
    );
  }
  else {
    if (shiftTimer != null) {
      shiftTimer.cancel();
    }
  }
  println("----->>> autoShift is "+ isAutoShift);
  break;

See line 427 in PixelAudio.pde, the application that suggested the name for the PixelAudio library. This anonymous method works fine for simple Timer events.