package io.github.dstrekelj.toolkit.audio;

import android.media.AudioTrack;
import android.os.Process;
import android.util.Log;

import java.nio.ShortBuffer;

/**
 * TODO: Comment.
 */
public abstract class PcmPlayerRunnable implements Runnable {
    public static final String TAG = "PcmPlayerRunnable";

    private int audioFormat;
    private int channelConfiguration;
    private int sampleRate;
    private int streamType;

    private boolean isPlaying;

    public PcmPlayerRunnable(int audioFormat, int channelConfiguration, int sampleRate, int streamType) {
        this.audioFormat = audioFormat;
        this.channelConfiguration = channelConfiguration;
        this.sampleRate = sampleRate;
        this.streamType = streamType;
    }

    public boolean isPlaying() {
        return isPlaying;
    }

    public void stop() {
        Log.d(TAG, "Stopping playback");
        isPlaying = false;
    }

    @Override
    public void run() {
        isPlaying = true;

        android.os.Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO);

        int minBufferSize = AudioTrack.getMinBufferSize(
                sampleRate,
                channelConfiguration,
                audioFormat
        );

        if (minBufferSize == AudioTrack.ERROR || minBufferSize == AudioTrack.ERROR_BAD_VALUE) {
            minBufferSize = sampleRate * 2;
        }

        AudioTrack audioTrack = new AudioTrack(
                streamType,
                sampleRate,
                channelConfiguration,
                audioFormat,
                minBufferSize,
                AudioTrack.MODE_STREAM
        );

        audioTrack.play();
        ShortBuffer samples = onPlayerStart();

        short[] buffer = new short[minBufferSize];
        samples.rewind();
        int limit = samples.capacity();
        while (samples.position() < limit && isPlaying) {
            int numberOfLeftoverSamples = limit - samples.position();
            int numberOfSamplesToWrite;
            if (numberOfLeftoverSamples >= buffer.length) {
                samples.get(buffer);
                numberOfSamplesToWrite = buffer.length;
            } else {
                for (int i = numberOfLeftoverSamples; i < buffer.length; i++) {
                    buffer[i] = 0;
                }
                samples.get(buffer, 0, numberOfLeftoverSamples);
                numberOfSamplesToWrite = numberOfLeftoverSamples;
            }
            audioTrack.write(buffer, 0, numberOfSamplesToWrite);
            onPlay();
        }

        if (!isPlaying) {
            audioTrack.release();
            onPlayerStop();
        }
    }

    protected abstract void onPlay();
    protected abstract ShortBuffer onPlayerStart();
    protected abstract void onPlayerStop();
}

@@ -238,16 +238,61 @@
 Runnable
+, AudioTrack.OnPlaybackPositionUpdateListener
  %7B%0A    p
@@ -1675,35 +1675,8 @@
 );%0A%0A
-        audioTrack.play();%0A
     
@@ -1715,24 +1715,250 @@
 erStart();%0A%0A
+        audioTrack.setPlaybackPositionUpdateListener(this);%0A        audioTrack.setPositionNotificationPeriod(sampleRate / 30);%0A        audioTrack.setNotificationMarkerPosition(samples.capacity());%0A%0A        audioTrack.play();%0A%0A
         shor
@@ -2881,24 +2881,242 @@
    %7D%0A    %7D%0A%0A
+    @Override%0A    public void onMarkerReached(AudioTrack audioTrack) %7B%0A        audioTrack.release();%0A        onPlayerStop();%0A    %7D%0A%0A    @Override%0A    public void onPeriodicNotification(AudioTrack audioTrack) %7B%0A%0A    %7D%0A%0A
     protecte

Stop playback when last sample is reached