Skip to content

Commit a52fa9d

Browse files
committed
Make sure prematurely interrupted envelopes get garbage collected
Fixes #29
1 parent 7515c65 commit a52fa9d

File tree

3 files changed

+47
-21
lines changed

3 files changed

+47
-21
lines changed

src/processing/sound/Engine.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -396,14 +396,18 @@ protected void disconnectFromOutput(int channel, UnitSource source, int part) {
396396
}
397397

398398
protected void play(UnitSource source) {
399-
// TODO check if unit is already connected
400-
// source.getOutput().isConnected()
401-
for (int i = 0; i < source.getOutput().getNumParts(); i++) {
402-
this.connectToOutput((this.outputChannel + i) % this.synth.getAudioDeviceManager().getMaxOutputChannels(this.outputDevice), source, i);
403-
// source.getOutput().connect(i, this.volume[(this.outputChannel + i) % this.synth.getAudioDeviceManager().getMaxOutputChannels(this.outputDevice)].inputA, 0);
404-
if (this.multiChannelMode) {
405-
// only add the first (left) channel
406-
break;
399+
// check if unit is already connected
400+
UnitGenerator generator = source.getUnitGenerator();
401+
if (!this.addedUnits.contains(generator)) {
402+
this.synth.add(generator);
403+
this.addedUnits.add(generator);
404+
for (int i = 0; i < source.getOutput().getNumParts(); i++) {
405+
this.connectToOutput((this.outputChannel + i) % this.synth.getAudioDeviceManager().getMaxOutputChannels(this.outputDevice), source, i);
406+
// source.getOutput().connect(i, this.volume[(this.outputChannel + i) % this.synth.getAudioDeviceManager().getMaxOutputChannels(this.outputDevice)].inputA, 0);
407+
if (this.multiChannelMode) {
408+
// only add the first (left) channel
409+
break;
410+
}
407411
}
408412
}
409413
}
@@ -415,6 +419,7 @@ protected void stop(UnitSource source) {
415419
for (int i : IntStream.range(0, source.getOutput().getNumParts()).toArray()) {
416420
source.getOutput().disconnectAll(i);
417421
}
422+
// removal happens inside
418423
this.remove(source.getUnitGenerator());
419424
}
420425
}

src/processing/sound/Env.java

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package processing.sound;
22

33
import com.jsyn.data.SegmentedEnvelope;
4+
import com.jsyn.ports.QueueDataCommand;
5+
import com.jsyn.ports.QueueDataEvent;
6+
import com.jsyn.ports.UnitDataQueueCallback;
47
import com.jsyn.unitgen.VariableRateMonoReader;
5-
import com.softsynth.shared.time.TimeStamp;
68

79
import processing.core.PApplet;
810

@@ -35,23 +37,43 @@ public void play(SoundObject input, float attackTime, float sustainTime, float s
3537
sustainTime, sustainLevel * input.amp, // sustain
3638
releaseTime, 0.0 });
3739

38-
// TODO re-use player from fixed or dynamic pool
40+
// fire-and-forget envelope player
3941
VariableRateMonoReader player = new VariableRateMonoReader();
40-
41-
// this would make sense to me but breaks the envelope for some reason
42-
// input.amplitude.disconnectAll();
43-
player.output.connect(input.amplitude);
4442
Engine.getEngine().add(player);
43+
// we need to start the player explicitly, otherwise if it gets disconnected
44+
// by another envelope kicking in before it has completed, it would stop
45+
// prematurely and the callback (which removes it from the synth for garbage
46+
// collection) would never get called!
47+
player.start();
4548

46-
player.dataQueue.queue(env);
49+
input.amplitude.disconnectAll();
50+
player.output.connect(input.amplitude);
4751
if (!input.isPlaying()) {
4852
input.play();
4953
}
5054

51-
// disconnect player from amplitude port after finished and set amplitude to 0
52-
TimeStamp envFinished = Engine.getEngine().synth.createTimeStamp().makeRelative(attackTime + sustainTime + releaseTime);
53-
player.output.disconnect(0, input.amplitude, 0, envFinished);
54-
// TODO better: trigger unit stop() so that isPlaying() is set to false as well?
55-
input.amplitude.set(0, envFinished);
55+
QueueDataCommand cmd = player.dataQueue.createQueueDataCommand(env, 0, env.getNumFrames());
56+
// need to set auto stop for the remove() inside the callback to work
57+
cmd.setAutoStop(true);
58+
cmd.setCallback(new UnitDataQueueCallback() {
59+
public void finished(QueueDataEvent event) {
60+
// check if this output has maybe already been disconnected (by a new
61+
// envelope that has taken over)
62+
if (player.output.isConnected()) {
63+
player.output.disconnectAll();
64+
// TODO what to do with the input soundobject after the envelope is
65+
// finished? just silence it, but then it isn't automatically garbage
66+
// collected? should we trigger the object's stop() as well?
67+
input.amplitude.set(0);
68+
}
69+
Engine.getEngine().remove(player);
70+
}
71+
public void looped(QueueDataEvent event) {
72+
}
73+
public void started(QueueDataEvent event) {
74+
}
75+
});
76+
77+
player.getSynthesizer().queueCommand(cmd);
5678
}
5779
}

src/processing/sound/SoundObject.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,6 @@ public void pan(float pos) {
9898
public void play() {
9999
// TODO print info message if it's already playing?
100100
if (!this.isPlaying) {
101-
Engine.getEngine().add(this.circuit);
102101
Engine.getEngine().play(this.circuit);
103102
this.setAmplitude();
104103
this.isPlaying = true;

0 commit comments

Comments
 (0)