// Path: app/src/mixedbit/speechtrainer/controller/AudioBufferAllocator.java
// final class AudioBuffer {
//     private static final int DECIBEL_MULTIPLIER = 10;
//     // State is kept only for contract violation checks and is not exposed
//     // to the user.
//     private BufferState bufferState;
//     private final short[] audioData;
//     private int audioDataLengthInShorts;
//     private double soundLevel;
//     private int audioBufferId;
// 
//     /**
//      * Only {@link AudioBufferAllocator} can create AudioBuffers.
//      */
//     private AudioBuffer(int bufferSize) {
//         audioData = new short[bufferSize];
//         bufferState = BufferState.AVAILABLE;
//     }
// 
//     /**
//      * @return buffer for audio data. After content of the buffer is
//      *         modified, audioDataStored must be called.
//      */
//     public short[] getAudioData() {
//         checkStateIs(BufferState.ALLOCATED);
//         return audioData;
//     }
// 
//     /**
//      * Must be always called after audio data is modified.
//      * 
//      * @param audioDataLengthInShorts
//      *            Number of samples stored in the audio data buffer, can not
//      *            be larger than length of audio data array.
//      */
//     public void audioDataStored(int audioDataLengthInShorts) {
//         Assertions.illegalStateIfFalse(audioDataLengthInShorts <= audioData.length,
//         "Audio data length too long.");
//         checkStateIs(BufferState.ALLOCATED);
//         this.audioDataLengthInShorts = audioDataLengthInShorts;
//         computeSoundLevel();
//     }
// 
//     /**
//      * @return Unique id of the allocated buffer. The id is guaranteed to
//      *         increase for buffers returned by subsequent
//      *         allocateAudioBuffer calls. Once buffer is released, its id is
//      *         forgotten and a new id is assigned when the buffer is
//      *         allocated again.
//      */
//     public int getAudioBufferId() {
//         checkStateIs(BufferState.ALLOCATED);
//         return audioBufferId;
//     }
// 
//     /**
//      * @return Length of audio data stored in the buffer.
//      */
//     public int getAudioDataLengthInShorts() {
//         checkStateIs(BufferState.ALLOCATED);
//         return audioDataLengthInShorts;
//     }
// 
//     /**
//      * @return Sound level of audio data stored in the buffer. See
//      *         {@link http://en.wikipedia.org/wiki/Sound_pressure}. The
//      *         returned sound level is not an absolute, device independent
//      *         value.
//      */
//     public double getSoundLevel() {
//         checkStateIs(BufferState.ALLOCATED);
//         return this.soundLevel;
//     }
// 
//     private void changeStateTo(BufferState state) {
//         this.bufferState = state;
//     }
// 
//     private void checkStateIs(BufferState state) {
//         if (this.bufferState != state) {
//             throw new IllegalStateException("Audio buffer is in illegal state "
//                     + this.bufferState.name());
//         }
//     }
// 
//     private void computeSoundLevel() {
//         long sum = 0;
//         for (int i = 0; i < audioDataLengthInShorts; ++i) {
//             sum += audioData[i] * audioData[i];
//         }
//         if (sum != 0) {
//             this.soundLevel = DECIBEL_MULTIPLIER
//             * Math.log10(1.0 * sum / audioDataLengthInShorts);
//         } else {
//             this.soundLevel = 0.0;
//         }
//     }
// 
//     private void setAudioBufferId(int audioBufferId) {
//         this.audioBufferId = audioBufferId;
//     }
// }


import mixedbit.speechtrainer.controller.AudioBufferAllocator.AudioBuffer;
import android.media.AudioRecord;
/**
 * This file is part of Speech Trainer.
 * Copyright (C) 2011 Jan Wrobel <wrr@mixedbit.org>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package mixedbit.speechtrainer.controller;


/**
 * Wrapper over AudioRecord that exposes minimal interface for recording
 * AudioBuffers. Informs AudioEventListener about each executed action
 * (recording started, audio buffer recorded, recording of audio buffer failed,
 * recording stopped). As in case of the Player interface, this interface is
 * extracted from the RecorderImpl to allow mocking with the EasyMock.
 */
interface Recorder {

    /**
     * Starts recording. Calls to readAudioBuffer are allowed only after
     * recording was started.
     */
    public abstract void startRecording();

    /**
     * Reads recorded audio buffer. Requires recording to be started
     * (startRecording called). Can block until there is enough audio data
     * recorded. Returns false if recording failed (this happens for instance
     * when microphone is used by some other application).
     */
    public abstract boolean readAudioBuffer(AudioBuffer audioBuffer);

    /**
     * Stops recording. Recording can be started again with the startRecording
     * method.
     */
    public abstract void stopRecording();

}

class RecorderImpl implements Recorder {
    private final AudioRecord audioRecord;
    private final AudioEventListener audioEventListener;

    /**
     * @param audioRecord
     *            AudioRecord object configured by a caller.
     * @param audioEventListener
     *            Listener that is informed about each action executed by the
     *            recorder.
     */
    public RecorderImpl(AudioRecord audioRecord, AudioEventListener audioEventListener) {
        this.audioRecord = audioRecord;
        this.audioEventListener = audioEventListener;
    }

    @Override
    public void startRecording() {
        audioRecord.startRecording();
        audioEventListener.recordingStarted();
    }

    @Override
// Path: app/src/mixedbit/speechtrainer/controller/AudioBufferAllocator.java
// final class AudioBuffer {
//     private static final int DECIBEL_MULTIPLIER = 10;
//     // State is kept only for contract violation checks and is not exposed
//     // to the user.
//     private BufferState bufferState;
//     private final short[] audioData;
//     private int audioDataLengthInShorts;
//     private double soundLevel;
//     private int audioBufferId;
// 
//     /**
//      * Only {@link AudioBufferAllocator} can create AudioBuffers.
//      */
//     private AudioBuffer(int bufferSize) {
//         audioData = new short[bufferSize];
//         bufferState = BufferState.AVAILABLE;
//     }
// 
//     /**
//      * @return buffer for audio data. After content of the buffer is
//      *         modified, audioDataStored must be called.
//      */
//     public short[] getAudioData() {
//         checkStateIs(BufferState.ALLOCATED);
//         return audioData;
//     }
// 
//     /**
//      * Must be always called after audio data is modified.
//      * 
//      * @param audioDataLengthInShorts
//      *            Number of samples stored in the audio data buffer, can not
//      *            be larger than length of audio data array.
//      */
//     public void audioDataStored(int audioDataLengthInShorts) {
//         Assertions.illegalStateIfFalse(audioDataLengthInShorts <= audioData.length,
//         "Audio data length too long.");
//         checkStateIs(BufferState.ALLOCATED);
//         this.audioDataLengthInShorts = audioDataLengthInShorts;
//         computeSoundLevel();
//     }
// 
//     /**
//      * @return Unique id of the allocated buffer. The id is guaranteed to
//      *         increase for buffers returned by subsequent
//      *         allocateAudioBuffer calls. Once buffer is released, its id is
//      *         forgotten and a new id is assigned when the buffer is
//      *         allocated again.
//      */
//     public int getAudioBufferId() {
//         checkStateIs(BufferState.ALLOCATED);
//         return audioBufferId;
//     }
// 
//     /**
//      * @return Length of audio data stored in the buffer.
//      */
//     public int getAudioDataLengthInShorts() {
//         checkStateIs(BufferState.ALLOCATED);
//         return audioDataLengthInShorts;
//     }
// 
//     /**
//      * @return Sound level of audio data stored in the buffer. See
//      *         {@link http://en.wikipedia.org/wiki/Sound_pressure}. The
//      *         returned sound level is not an absolute, device independent
//      *         value.
//      */
//     public double getSoundLevel() {
//         checkStateIs(BufferState.ALLOCATED);
//         return this.soundLevel;
//     }
// 
//     private void changeStateTo(BufferState state) {
//         this.bufferState = state;
//     }
// 
//     private void checkStateIs(BufferState state) {
//         if (this.bufferState != state) {
//             throw new IllegalStateException("Audio buffer is in illegal state "
//                     + this.bufferState.name());
//         }
//     }
// 
//     private void computeSoundLevel() {
//         long sum = 0;
//         for (int i = 0; i < audioDataLengthInShorts; ++i) {
//             sum += audioData[i] * audioData[i];
//         }
//         if (sum != 0) {
//             this.soundLevel = DECIBEL_MULTIPLIER
//             * Math.log10(1.0 * sum / audioDataLengthInShorts);
//         } else {
//             this.soundLevel = 0.0;
//         }
//     }
// 
//     private void setAudioBufferId(int audioBufferId) {
//         this.audioBufferId = audioBufferId;
//     }
// }


// Path: app/src/mixedbit/speechtrainer/controller/Recorder.java
import mixedbit.speechtrainer.controller.AudioBufferAllocator.AudioBuffer;
import android.media.AudioRecord;

/**
 * This file is part of Speech Trainer.
 * Copyright (C) 2011 Jan Wrobel <wrr@mixedbit.org>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package mixedbit.speechtrainer.controller;


/**
 * Wrapper over AudioRecord that exposes minimal interface for recording
 * AudioBuffers. Informs AudioEventListener about each executed action
 * (recording started, audio buffer recorded, recording of audio buffer failed,
 * recording stopped). As in case of the Player interface, this interface is
 * extracted from the RecorderImpl to allow mocking with the EasyMock.
 */
interface Recorder {

    /**
     * Starts recording. Calls to readAudioBuffer are allowed only after
     * recording was started.
     */
    public abstract void startRecording();

    /**
     * Reads recorded audio buffer. Requires recording to be started
     * (startRecording called). Can block until there is enough audio data
     * recorded. Returns false if recording failed (this happens for instance
     * when microphone is used by some other application).
     */
    public abstract boolean readAudioBuffer(AudioBuffer audioBuffer);

    /**
     * Stops recording. Recording can be started again with the startRecording
     * method.
     */
    public abstract void stopRecording();

}

class RecorderImpl implements Recorder {
    private final AudioRecord audioRecord;
    private final AudioEventListener audioEventListener;

    /**
     * @param audioRecord
     *            AudioRecord object configured by a caller.
     * @param audioEventListener
     *            Listener that is informed about each action executed by the
     *            recorder.
     */
    public RecorderImpl(AudioRecord audioRecord, AudioEventListener audioEventListener) {
        this.audioRecord = audioRecord;
        this.audioEventListener = audioEventListener;
    }

    @Override
    public void startRecording() {
        audioRecord.startRecording();
        audioEventListener.recordingStarted();
    }

    @Override
    public boolean readAudioBuffer(AudioBuffer audioBuffer) {