1717 */
1818package 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 ;
2223import java .util .concurrent .Executors ;
2324import java .util .concurrent .ThreadPoolExecutor ;
2425import java .util .concurrent .atomic .AtomicInteger ;
2526
2627public 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