Android Open Source - speech_trainer Audio Buffer Allocator






From Project

Back to project page speech_trainer.

License

The source code is released under:

GNU General Public License

If you think the Android project speech_trainer listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/**
 * This file is part of Speech Trainer.//ww  w.j  a  v a 2 s. c om
 * 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;

import java.util.concurrent.ConcurrentLinkedQueue;

import mixedbit.speechtrainer.Assertions;

/**
 * Keeps a fixed pool of buffers for audio samples. Allocates memory for the
 * buffers once and then reuses the buffers. This decreases allocation and
 * garbage collection overhead and guarantees that memory usage is bounded. The
 * class is thread safe.
 */
class AudioBufferAllocator {
    /**
     * Buffer for audio samples with additional information about samples
     * (number of samples, combined sound level of all samples kept in the
     * buffer). This class in not thread safe.
     */
    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;
        }
    }

    /**
     * State of an audio buffer.
     */
    private enum BufferState {
        AVAILABLE, ALLOCATED
    }

    ConcurrentLinkedQueue<AudioBuffer> availableBuffers;
    private final int numberOfBuffers;
    private int nextAudioBufferId = 0;

    /**
     * All audio buffers are allocated during the construction of
     * {@link AudioBufferAllocator}.
     * 
     * @param numberOfBuffers
     * @param singleBufferSize
     */
    public AudioBufferAllocator(int numberOfBuffers, int singleBufferSize) {
        this.numberOfBuffers = numberOfBuffers;
        availableBuffers = new ConcurrentLinkedQueue<AudioBuffer>();
        for (int i = 0; i < numberOfBuffers; ++i) {
            availableBuffers.add(new AudioBuffer(singleBufferSize));
        }
    }

    /**
     * Allocates an audio buffer. The ownership of the allocated buffer is
     * passed to the caller. When caller is done using the buffer, it must
     * return it to the pool by calling releaseAudioBuffer.
     * 
     * @return AudioBuffer with empty audio data that can be initialized and
     *         used by the caller, null if there are no buffers available.
     */
    public AudioBuffer allocateAudioBuffer() {
        final AudioBuffer buffer = availableBuffers.poll();
        if (buffer != null) {
            buffer.setAudioBufferId(nextAudioBufferId);
            ++nextAudioBufferId;
            buffer.changeStateTo(BufferState.ALLOCATED);
        }
        return buffer;
    }

    /**
     * Releases an audio buffer. The caller returns the ownership of the buffer
     * to the {@link AudioBufferAllocator} and can not use the buffer anymore.
     * Calling any method on a released buffer is forbidden.
     */
    public void releaseAudioBuffer(AudioBuffer audioBuffer) {
        audioBuffer.checkStateIs(BufferState.ALLOCATED);
        // Clear audio data of the released buffer.
        audioBuffer.audioDataStored(0);
        audioBuffer.changeStateTo(BufferState.AVAILABLE);
        availableBuffers.add(audioBuffer);
    }

    /**
     * Sanity check that can be executed by a user in places where all buffers
     * should be released and available for allocation. Helps to ensure audio
     * buffers do not leak.
     */
    public void assertAllAudioBuffersAvailable() {
        Assertions.check(this.numberOfBuffers == availableBuffers.size());
    }
}




Java Source Code List

mixedbit.speechtrainer.AssertionsTest.java
mixedbit.speechtrainer.Assertions.java
mixedbit.speechtrainer.SpeechTrainerConfig.java
mixedbit.speechtrainer.TrainingApplication.java
mixedbit.speechtrainer.controller.AudioBufferAllocatorTest.java
mixedbit.speechtrainer.controller.AudioBufferAllocator.java
mixedbit.speechtrainer.controller.AudioEventListener.java
mixedbit.speechtrainer.controller.AutomaticTrainingControllerTest.java
mixedbit.speechtrainer.controller.AutomaticTrainingController.java
mixedbit.speechtrainer.controller.ControllerFactory.java
mixedbit.speechtrainer.controller.InteractiveTrainingControllerTest.java
mixedbit.speechtrainer.controller.InteractiveTrainingController.java
mixedbit.speechtrainer.controller.Player.java
mixedbit.speechtrainer.controller.RecordPlayTaskManagerTest.java
mixedbit.speechtrainer.controller.RecordPlayTaskManager.java
mixedbit.speechtrainer.controller.Recorder.java
mixedbit.speechtrainer.controller.SilenceFilterTest.java
mixedbit.speechtrainer.controller.SilenceFilter.java
mixedbit.speechtrainer.controller.SilenceLevelDetectorTest.java
mixedbit.speechtrainer.controller.SilenceLevelDetector.java
mixedbit.speechtrainer.controller.TrainingControllerTest.java
mixedbit.speechtrainer.controller.TrainingController.java
mixedbit.speechtrainer.model.AudioBufferInfo.java
mixedbit.speechtrainer.model.AudioEventCollectorTest.java
mixedbit.speechtrainer.model.AudioEventCollector.java
mixedbit.speechtrainer.model.AudioEventHistory.java
mixedbit.speechtrainer.view.AudioEventView.java
mixedbit.speechtrainer.view.FileViewerActivity.java
mixedbit.speechtrainer.view.TrainingActivity.java
mixedbit.speechtrainer.view.TrainingPreferenceActivity.java