// Path: app/src/mixedbit/speechtrainer/Assertions.java
// public class Assertions {
// 
//     public static void check(boolean shouldBeTrue) {
//         if (shouldBeTrue == false) {
//             throw new AssertionError();
//         }
//     }
// 
//     public static void illegalStateIfFalse(boolean shouldBeTrue) {
//         if (shouldBeTrue == false) {
//             throw new IllegalStateException();
//         }
//     }
// 
//     public static void illegalStateIfFalse(boolean shouldBeTrue, String errorMessage) {
//         if (shouldBeTrue == false) {
//             throw new IllegalStateException(errorMessage);
//         }
//     }
// }
// 
// Path: app/src/mixedbit/speechtrainer/SpeechTrainerConfig.java
// public class SpeechTrainerConfig {
//     // The only rate that is guaranteed to work for recording and playing on all
//     // devices.
//     public static final int SAMPLE_RATE_HZ = 44100;
// 
//     // The memory required for audio data is SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS
//     // * 2 * NUMBER_OF_AUDIO_BUFFERS = ~6MB. The maximum time of recording is
//     // SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS * NUMBER_OF_AUDIO_BUFFERS /
//     // SAMPLE_RATE_HZ seconds = ~68 seconds. Should be more than enough for
//     // the speech training purposes.
//     public static final int SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS = 1000;
//     public static final int NUMBER_OF_AUDIO_BUFFERS = 3000;
// 
//     // Approximate number of audio buffers needed to store a second of sound.
//     public static int numberOfBuffersPerSecond() {
//         return SAMPLE_RATE_HZ / SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS;
//     }
// 
// }
// 
// Path: app/src/mixedbit/speechtrainer/controller/AudioEventListener.java
// public interface AudioEventListener {
// 
//     /**
//      * Informs the listener that recording has started. Listener can assume it
//      * is always called before calls to audioBufferRecorded are made. Also
//      * recordingStarted is never called if recording or playing are already in
//      * progress.
//      */
//     public void recordingStarted();
// 
//     /**
//      * Informs the listener that an audio buffer with a given id and sound level
//      * was recorded.
//      */
//     public void audioBufferRecorded(int audioBufferId, double soundLevel);
// 
//     /**
//      * Informs the listener that recording of an audio buffer failed. This
//      * usually happens when some other application starts using the microphone.
//      * Listener should not assume that recording has stopped when this callback
//      * is invoked, recordingStopped() will still be called.
//      */
//     public void audioBufferRecordingFailed();
// 
//     /**
//      * Informs the listener that recording has stopped. Listener can assume no
//      * more calls to audioBufferRecorded will be made until recordingStarted is
//      * called again.
//      */
//     public void recordingStopped();
// 
//     /**
//      * Informs the listener that playing has started. Listener can assume it is
//      * always called before calls to audioBufferPlayed are made. Also
//      * playingStarted is never called if recording or playing are already in
//      * progress.
//      */
//     public void playingStarted();
// 
//     /**
//      * Informs the listener that an audio buffer with a given id and sound level
//      * was played. Buffer id is unique, so listener can compare ids to identify
//      * which recorded buffer was played.
//      */
//     public void audioBufferPlayed(int audioBufferId, double soundLevel);
// 
//     /**
//      * Informs the listener that playing has stopped. Listener can assume no
//      * more calls to audioBufferPlayed will be made until playingStarted is
//      * called again.
//      */
//     public void playingStopped();
// }


import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.SortedSet;
import java.util.TreeSet;
import mixedbit.speechtrainer.Assertions;
import mixedbit.speechtrainer.SpeechTrainerConfig;
import mixedbit.speechtrainer.controller.AudioEventListener;
                && audioBufferId <= lastBufferPlayed.getAudioBufferId();
            }
        }
    }

    // How many most recent AudioBufferInfo to keep.
    public static final int HISTORY_SIZE = SpeechTrainerConfig.NUMBER_OF_AUDIO_BUFFERS;
    // A chained listener to which all audio events are also passed (In
    // production code this is the GUI component that displays audio events).
    private final AudioEventListener nextListener;

    // The two sorted sets are kept in sync. They could be replaced with a
    // single NavigableSet that supports iteration in a reverse order.
    // Unfortunately the NavigableSet is available only from Android API
    // level 9.
    private final SortedSet<AudioBufferInfo> recordedBuffersLastRecordedLast;
    private final SortedSet<AudioBufferInfo> recordedBuffersLastRecordedFirst;

    // Recently recorded buffers are not added directly to recordedBuffers in
    // order not to invalidate iterator that can be in use by the GUI.
    private final Queue<AudioBufferInfo> recentlyRecordedBuffers = new LinkedList<AudioBufferInfo>();
    private double maxSoundLevel;
    private double minSoundLevel;
    private AudioBufferInfoImpl firstBufferPlayed = null;
    private AudioBufferInfoImpl lastBufferPlayed = null;

    /**
     * @param nextListener
     *            A chained listener to which all audio events are passed.
     */
// Path: app/src/mixedbit/speechtrainer/Assertions.java
// public class Assertions {
// 
//     public static void check(boolean shouldBeTrue) {
//         if (shouldBeTrue == false) {
//             throw new AssertionError();
//         }
//     }
// 
//     public static void illegalStateIfFalse(boolean shouldBeTrue) {
//         if (shouldBeTrue == false) {
//             throw new IllegalStateException();
//         }
//     }
// 
//     public static void illegalStateIfFalse(boolean shouldBeTrue, String errorMessage) {
//         if (shouldBeTrue == false) {
//             throw new IllegalStateException(errorMessage);
//         }
//     }
// }
// 
// Path: app/src/mixedbit/speechtrainer/SpeechTrainerConfig.java
// public class SpeechTrainerConfig {
//     // The only rate that is guaranteed to work for recording and playing on all
//     // devices.
//     public static final int SAMPLE_RATE_HZ = 44100;
// 
//     // The memory required for audio data is SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS
//     // * 2 * NUMBER_OF_AUDIO_BUFFERS = ~6MB. The maximum time of recording is
//     // SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS * NUMBER_OF_AUDIO_BUFFERS /
//     // SAMPLE_RATE_HZ seconds = ~68 seconds. Should be more than enough for
//     // the speech training purposes.
//     public static final int SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS = 1000;
//     public static final int NUMBER_OF_AUDIO_BUFFERS = 3000;
// 
//     // Approximate number of audio buffers needed to store a second of sound.
//     public static int numberOfBuffersPerSecond() {
//         return SAMPLE_RATE_HZ / SINGLE_AUDIO_BUFFER_SIZE_IN_SHORTS;
//     }
// 
// }
// 
// Path: app/src/mixedbit/speechtrainer/controller/AudioEventListener.java
// public interface AudioEventListener {
// 
//     /**
//      * Informs the listener that recording has started. Listener can assume it
//      * is always called before calls to audioBufferRecorded are made. Also
//      * recordingStarted is never called if recording or playing are already in
//      * progress.
//      */
//     public void recordingStarted();
// 
//     /**
//      * Informs the listener that an audio buffer with a given id and sound level
//      * was recorded.
//      */
//     public void audioBufferRecorded(int audioBufferId, double soundLevel);
// 
//     /**
//      * Informs the listener that recording of an audio buffer failed. This
//      * usually happens when some other application starts using the microphone.
//      * Listener should not assume that recording has stopped when this callback
//      * is invoked, recordingStopped() will still be called.
//      */
//     public void audioBufferRecordingFailed();
// 
//     /**
//      * Informs the listener that recording has stopped. Listener can assume no
//      * more calls to audioBufferRecorded will be made until recordingStarted is
//      * called again.
//      */
//     public void recordingStopped();
// 
//     /**
//      * Informs the listener that playing has started. Listener can assume it is
//      * always called before calls to audioBufferPlayed are made. Also
//      * playingStarted is never called if recording or playing are already in
//      * progress.
//      */
//     public void playingStarted();
// 
//     /**
//      * Informs the listener that an audio buffer with a given id and sound level
//      * was played. Buffer id is unique, so listener can compare ids to identify
//      * which recorded buffer was played.
//      */
//     public void audioBufferPlayed(int audioBufferId, double soundLevel);
// 
//     /**
//      * Informs the listener that playing has stopped. Listener can assume no
//      * more calls to audioBufferPlayed will be made until playingStarted is
//      * called again.
//      */
//     public void playingStopped();
// }


// Path: app/src/mixedbit/speechtrainer/model/AudioEventCollector.java
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Queue;
import java.util.SortedSet;
import java.util.TreeSet;
import mixedbit.speechtrainer.Assertions;
import mixedbit.speechtrainer.SpeechTrainerConfig;
import mixedbit.speechtrainer.controller.AudioEventListener;

                && audioBufferId <= lastBufferPlayed.getAudioBufferId();
            }
        }
    }

    // How many most recent AudioBufferInfo to keep.
    public static final int HISTORY_SIZE = SpeechTrainerConfig.NUMBER_OF_AUDIO_BUFFERS;
    // A chained listener to which all audio events are also passed (In
    // production code this is the GUI component that displays audio events).
    private final AudioEventListener nextListener;

    // The two sorted sets are kept in sync. They could be replaced with a
    // single NavigableSet that supports iteration in a reverse order.
    // Unfortunately the NavigableSet is available only from Android API
    // level 9.
    private final SortedSet<AudioBufferInfo> recordedBuffersLastRecordedLast;
    private final SortedSet<AudioBufferInfo> recordedBuffersLastRecordedFirst;

    // Recently recorded buffers are not added directly to recordedBuffers in
    // order not to invalidate iterator that can be in use by the GUI.
    private final Queue<AudioBufferInfo> recentlyRecordedBuffers = new LinkedList<AudioBufferInfo>();
    private double maxSoundLevel;
    private double minSoundLevel;
    private AudioBufferInfoImpl firstBufferPlayed = null;
    private AudioBufferInfoImpl lastBufferPlayed = null;

    /**
     * @param nextListener
     *            A chained listener to which all audio events are passed.
     */
    public AudioEventCollector(AudioEventListener nextListener) {