Skip to content

Commit efd79ca

Browse files
committed
Improved MultithreadedJavaxSoundSystem concurrency
1 parent b9ac3c2 commit efd79ca

File tree

1 file changed

+50
-17
lines changed

1 file changed

+50
-17
lines changed

src/main/java/net/raphimc/noteblocktool/audio/soundsystem/impl/MultithreadedJavaxSoundSystem.java

Lines changed: 50 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,59 @@
1717
*/
1818
package net.raphimc.noteblocktool.audio.soundsystem.impl;
1919

20-
import java.util.concurrent.ArrayBlockingQueue;
21-
import java.util.concurrent.BlockingQueue;
20+
import java.util.Arrays;
21+
import java.util.Queue;
22+
import java.util.concurrent.ConcurrentLinkedQueue;
2223
import java.util.concurrent.Executors;
2324
import java.util.concurrent.ThreadPoolExecutor;
2425
import java.util.concurrent.atomic.AtomicInteger;
2526

2627
public class MultithreadedJavaxSoundSystem extends JavaxSoundSystem {
2728

28-
private final ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(Math.max(1, Runtime.getRuntime().availableProcessors() / 2));
29-
private final BlockingQueue<SoundInstance> soundsToRender = new ArrayBlockingQueue<>(8192);
30-
private final AtomicInteger renderLock = new AtomicInteger(0);
29+
private final ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(Math.max(2, Runtime.getRuntime().availableProcessors() - 4));
30+
private final Queue<SoundInstance> soundsToRender = new ConcurrentLinkedQueue<>();
31+
private final Queue<SoundInstance> soundsToMerge = new ConcurrentLinkedQueue<>();
32+
private final AtomicInteger syncLock = new AtomicInteger(0);
33+
private final long[][] threadSamples;
3134

3235
public MultithreadedJavaxSoundSystem(final int maxSounds, final float playbackSpeed) {
3336
super(maxSounds, playbackSpeed);
3437

35-
for (int i = 0; i < this.threadPool.getCorePoolSize(); i++) {
38+
final int mergingThreads = Math.max(1, this.threadPool.getCorePoolSize() / 3);
39+
final int renderingThreads = this.threadPool.getCorePoolSize() - mergingThreads;
40+
this.threadSamples = new long[mergingThreads][];
41+
for (int i = 0; i < mergingThreads; i++) {
42+
this.threadSamples[i] = new long[this.samplesPerTick];
43+
}
44+
45+
for (int i = 0; i < renderingThreads; i++) {
46+
this.threadPool.submit(() -> {
47+
try {
48+
while (!Thread.currentThread().isInterrupted()) {
49+
final SoundInstance soundInstance = this.soundsToRender.poll();
50+
if (soundInstance == null) {
51+
Thread.sleep(1);
52+
continue;
53+
}
54+
soundInstance.render();
55+
this.soundsToMerge.add(soundInstance);
56+
}
57+
} catch (InterruptedException ignored) {
58+
}
59+
});
60+
}
61+
for (int i = 0; i < mergingThreads; i++) {
62+
final int finalI = i;
3663
this.threadPool.submit(() -> {
3764
try {
38-
while (true) {
39-
this.soundsToRender.take().render();
40-
this.renderLock.decrementAndGet();
65+
while (!Thread.currentThread().isInterrupted()) {
66+
final SoundInstance soundInstance = this.soundsToMerge.poll();
67+
if (soundInstance == null) {
68+
Thread.sleep(1);
69+
continue;
70+
}
71+
soundInstance.write(this.threadSamples[finalI]);
72+
this.syncLock.decrementAndGet();
4173
}
4274
} catch (InterruptedException ignored) {
4375
}
@@ -49,18 +81,19 @@ public MultithreadedJavaxSoundSystem(final int maxSounds, final float playbackSp
4981
public void writeSamples() {
5082
final long[] samples = new long[this.samplesPerTick];
5183
for (SoundInstance playingSound : this.playingSounds) {
52-
if (playingSound.hasDataToRender()) {
53-
this.soundsToRender.offer(playingSound);
54-
this.renderLock.incrementAndGet();
55-
}
84+
this.soundsToRender.add(playingSound);
85+
this.syncLock.incrementAndGet();
5686
}
5787

58-
while (this.renderLock.get() != 0 && !Thread.currentThread().isInterrupted()) {
59-
// Wait for all sounds to be rendered
88+
while (this.syncLock.get() != 0 && !Thread.currentThread().isInterrupted()) {
89+
// Wait for all sounds to be rendered and merged
6090
}
6191

62-
for (SoundInstance playingSound : this.playingSounds) {
63-
playingSound.write(samples);
92+
for (long[] threadSamples : this.threadSamples) {
93+
for (int i = 0; i < samples.length; i++) {
94+
samples[i] += threadSamples[i];
95+
}
96+
Arrays.fill(threadSamples, 0);
6497
}
6598
this.dataLine.write(this.writeNormalized(samples), 0, samples.length * 2);
6699
this.playingSounds.removeIf(SoundInstance::isFinished);

0 commit comments

Comments
 (0)