Android Open Source - SensorDataCollector Audio Sensor






From Project

Back to project page SensorDataCollector.

License

The source code is released under:

GNU General Public License

If you think the Android project SensorDataCollector 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

/*
 * Copyright (c) 2014, the SenSee authors.  Please see the AUTHORS file
 * for details. /* w w  w  .  ja  v  a 2 s.  c  om*/
 * 
 * Licensed under the GNU Public License, Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.gnu.org/copyleft/gpl.html
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */

package sysnetlab.android.sdc.sensor.audio;

import java.nio.ByteBuffer;
import java.util.Calendar;
import java.util.Date;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

import android.media.AudioFormat;
import android.media.AudioRecord;
import android.text.TextUtils;
import android.util.Log;
import sysnetlab.android.sdc.datacollector.DeviceInformation;
import sysnetlab.android.sdc.datacollector.ExperimentTime;
import sysnetlab.android.sdc.datastore.AbstractStore;
import sysnetlab.android.sdc.datastore.AbstractStore.Channel;
import sysnetlab.android.sdc.sensor.AbstractSensor;

public class AudioSensor extends AbstractSensor {
    public final static int AUDIOSENSOR_MICROPHONE = 1;

    private static AudioSensor instance = null;

    private String mName = "Audio Sensor (Microphone)";

    private AbstractStore.Channel mChannel;

    private AudioRecord mAudioRecord;
    private AudioRecordParameter mAudioRecordParameter;

    private Date mDateStart;
    private Date mDateEnd;
    private ExperimentTime mExperimentTimeStart;
    private ExperimentTime mExperimentTimeEnd;

    // formulate in a producer/consumer paradigm
    private boolean mHavingDataLoss;
    private boolean mIsRecording;
    private Thread mDataCollectionThread;
    private Thread mDataProcessingThread;

    public static AudioSensor getInstance() {
        if (instance == null) {
            instance = new AudioSensor();
        }

        return instance;
    }

    protected AudioSensor() {
        super.setMajorType(AbstractSensor.AUDIO_SENSOR);
        super.setMinorType(AUDIOSENSOR_MICROPHONE);
    }

    public AudioSensor(AudioRecordParameter params, AbstractStore.Channel channel) {
        super.setMajorType(AbstractSensor.AUDIO_SENSOR);
        super.setMinorType(AUDIOSENSOR_MICROPHONE);
        mAudioRecordParameter = params;
        mChannel = channel;
    }

    public void start() {
        if (mIsRecording) {
            return;
        }

        mAudioRecord = new AudioRecord(mAudioRecordParameter.getSource().getSourceId(),
                mAudioRecordParameter.getSamplingRate(),
                mAudioRecordParameter.getChannel().getChannelId(), mAudioRecordParameter
                        .getEncoding().getEncodingId(),
                mAudioRecordParameter.getBufferSize());

        Log.d("SensorDataCollector", "getValidRecordingParameters(): " +
                "new AudioRecord(source, rate, channl, encoding, buffersize) = " +
                "new AudioRecord(" + mAudioRecordParameter.getSource().getSourceId() + ", " +
                mAudioRecordParameter.getSamplingRate() + ", " +
                mAudioRecordParameter.getChannel().getChannelId() + ", " +
                mAudioRecordParameter.getEncoding().getEncodingId() + ", " +
                mAudioRecordParameter.getBufferSize() + ")");

        if (!passedSanitationCheck()) {
            throw new RuntimeException(
                    "AudioRecord in SensorDataCollector is not properly released or initialized.");
        }

        mDateStart = Calendar.getInstance().getTime();
        mExperimentTimeStart = new ExperimentTime();

        BlockingQueue<DataBuffer> audioDataQueue = new LinkedBlockingQueue<DataBuffer>();
        Producer p = new Producer(audioDataQueue);
        Consumer c = new Consumer(audioDataQueue);
        mDataCollectionThread = new Thread(p);
        mDataProcessingThread = new Thread(c);

        mIsRecording = true;

        mDataCollectionThread.start();
        mDataProcessingThread.start();

        Log.d("SensorDataCollector", "AudioSensor::start(): audio record started");
    }

    public void stop() {
        if (!mIsRecording || mAudioRecord == null) {
            return;
        }

        mIsRecording = false;
        mExperimentTimeEnd = new ExperimentTime();
        mDateEnd = Calendar.getInstance().getTime();
    }

    @Override
    public int getMajorType() {
        return super.getMajorType();
    }

    @Override
    public int getMinorType() {
        return super.getMinorType();
    }

    @Override
    public String getName() {
        return mName;
    }

    @Override
    public String getVendor() {
        return (new DeviceInformation()).getManufacturer();
    }

    @Override
    public int getVersion() {
        return 0;
    }

    public AbstractStore.Channel getChannel() {
        return mChannel;
    }

    public AudioRecordParameter getAudioRecordParameter() {
        return mAudioRecordParameter;
    }

    public void setChannel(Channel channel) {
        mChannel = channel;
    }

    public void setAudioRecordParameter(AudioRecordParameter param) {
        mAudioRecordParameter = param;
    }

    public Date getTimeStart() {
        return mDateStart;
    }

    public void setTimeStart(Date d) {
        mDateStart = d;
    }

    public Date getTimeEnd() {
        return mDateEnd;
    }

    public void setTimeEnd(Date d) {
        mDateEnd = d;
    }

    public ExperimentTime getExperimentTimeStart() {
        return mExperimentTimeStart;
    }

    public void setExperimentTimeStart(ExperimentTime experimentTimeStart) {
        this.mExperimentTimeStart = experimentTimeStart;
    }

    public ExperimentTime getExperimentTimeEnd() {
        return mExperimentTimeEnd;
    }

    public void setExperimentTimeEnd(ExperimentTime experimentTimeEnd) {
        this.mExperimentTimeEnd = experimentTimeEnd;
    }

    public boolean havingDataLoss() {
        return mHavingDataLoss;
    }

    @Override
    public boolean isSameSensor(AbstractSensor sensor) {
        // same physical sensor, may have different settings
        return TextUtils.equals(mName, sensor.getName());
    }

    @Override
    public boolean equals(Object rhs) {
        if (this == rhs)
            return true;

        if (!(rhs instanceof AudioSensor))
            return false;

        AudioSensor sensor = (AudioSensor) rhs;

        if (mAudioRecordParameter == null) {
            if (sensor.mAudioRecordParameter != null) {
                return false;
            } else {
                return true;
            }
        } else {
            if (!mAudioRecordParameter.equals(sensor.mAudioRecordParameter)) {
                return false;
            } else {
                return true;
            }
        }
    }

    @Override
    public String toString() {
        return mName;
    }

    private boolean passedSanitationCheck() {
        if (mAudioRecord == null)
            return false;
        if (mAudioRecordParameter == null)
            return false;

        if (mAudioRecord.getState() != AudioRecord.STATE_INITIALIZED)
            return false;

        if (mAudioRecord.getAudioFormat() != mAudioRecordParameter.getEncoding().getEncodingId())
            return false;

        if (mAudioRecord.getAudioSource() != mAudioRecordParameter.getSource().getSourceId())
            return false;

        if (mAudioRecord.getSampleRate() != mAudioRecordParameter.getSamplingRate())
            return false;

        // how about source?
        return true;
    }

    private class DataBuffer {
        private boolean mPoisonPill;
        private ByteBuffer mByteBuffer;

        DataBuffer(boolean pill, ByteBuffer buf) {
            mPoisonPill = pill;
            mByteBuffer = buf;
        }

        boolean isPoisonPill() {
            return mPoisonPill;
        }

        ByteBuffer getBuffer() {
            return mByteBuffer;
        }
    }

    private class Producer implements Runnable {
        private final BlockingQueue<DataBuffer> mQueue;

        Producer(BlockingQueue<DataBuffer> q) {
            mQueue = q;
        }

        public void run() {
            initializeAudioData(mChannel, mAudioRecord);

            try {
                while (mIsRecording) {
                    /*
                     * produce() may return null, I think it might be the result
                     * of a race condition. when the AudioRecord is stopped,
                     * produce() may still be running and the thread may attempt
                     * to read a buffer, which is an exception and produce()
                     * returns null. alternatively, we can AudioRecord::stop()
                     * after this while loop. however, it makes the code less
                     * readable. a 3rd option, is to deliver an event when it is
                     * an opportune time to AudioRecord::stop(). The event
                     * handler invokes AudioRecord:;stop()
                     */
                    ByteBuffer b = produce();
                    if (b != null) {
                        mQueue.put(new DataBuffer(false, b));
                    }
                }

                mQueue.put(new DataBuffer(true, null));
            } catch (InterruptedException e) {
                Log.d("SensorDataCollector",
                        "AudioSensor::Producer::run(): interrupted: " + e.toString());
            }
        }

        ByteBuffer produce() {
            return recordToByteBuffer(mAudioRecordParameter.getMinBufferSize());
        }
    }

    private class Consumer implements Runnable {
        private final BlockingQueue<DataBuffer> mQueue;
        private int mNumberOfBytes;

        Consumer(BlockingQueue<DataBuffer> q) {
            mQueue = q;
            mNumberOfBytes = 0;
        }

        public void run() {
            try {
                while (mIsRecording) {
                    DataBuffer buf = mQueue.take();
                    if (!buf.isPoisonPill()) {
                        consume(mChannel, buf.getBuffer());
                    } else {
                        break;
                    }
                }
            } catch (InterruptedException e) {
                Log.i("SensorDataCollector", "AudioSensor::Consumer::run(): " + e.toString());
            }
            finalizeAudioData(mChannel, mAudioRecord, mNumberOfBytes);
        }

        void consume(AbstractStore.Channel c, ByteBuffer b) {
            int n = processBufferedAudioData(c, b);
            mNumberOfBytes += n;
        }
    }

    private ByteBuffer recordToByteBuffer(int capacity) {
        // Log.d("SensorDataCollector",
        // "AudioSensor::recordToByteBuffer(): entered with capacity = " +
        // capacity);

        ByteBuffer buf = ByteBuffer.allocateDirect(capacity);

        int totalBytesRead = 0;
        int numBytesToRead = capacity;
        while (numBytesToRead > 0) {
            int numBytesRead = mAudioRecord.read(buf, numBytesToRead);

            if (numBytesRead > 0) {
                totalBytesRead += numBytesRead;
                numBytesToRead = capacity - totalBytesRead;
                buf.position(totalBytesRead);
            } else if (numBytesRead == AudioRecord.ERROR_INVALID_OPERATION) {
                Log.d("SensorDataCollector",
                        "AudioSensor::recordToByteBuffer(): AudioRecord.ERROR_INVALID_OPERATION");
                buf = null;
                break;
            } else if (numBytesRead == AudioRecord.ERROR_BAD_VALUE) {
                Log.d("SensorDataCollector",
                        "AudioSensor::recordToByteBuffer(): AudioRecord.ERROR_BAD_VALUE");
                buf = null;
                break;
            } else if (numBytesRead == AudioRecord.ERROR) {
                Log.d("SensorDataCollector", "AudioSensor::recordToByteBuffer(): AudioRecord.ERROR");
                buf = null;
                break;
            }
        }

        // Log.d("SensorDataCollector",
        // "AudioSensor::recordToByteBuffer():buf = " + buf);
        return buf;
    }

    private int processBufferedAudioData(AbstractStore.Channel c, ByteBuffer bb) {
        int sizeInBytes = bb.capacity();
        /*
         * Log.d("SensorDataCollector",
         * "AudioSensor::processBufferedAudioData():sizeInBytes = " +
         * sizeInBytes);
         */

        byte[] b = new byte[sizeInBytes];
        bb.clear();
        bb.get(b);

        c.write(b, 0, sizeInBytes);

        return sizeInBytes;
    }

    private void initializeAudioData(AbstractStore.Channel c, AudioRecord r) {
        Log.d("SensorDataCollector", "AudioSensor::initializeAudioData(): start audio record");

        Log.d("SensorDataCollector", "initializeAudioData(): "
                +
                "new AudioRecord(source, rate, channl, encoding, buffersize) = "
                +
                "new AudioRecord("
                + mAudioRecordParameter.getSource().getSourceId()
                + ", "
                +
                r.getSampleRate()
                + ", "
                +
                "[ count = "
                + r.getChannelCount()
                + " config = "
                + r.getChannelConfiguration()
                + "], "
                +
                "[ format = "
                + r.getAudioFormat()
                + " format = "
                +
                (r.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT ? "ENCODING_PCM_16BIT"
                        : "ENCODING_PCM_8BIT") + "], " +
                mAudioRecordParameter.getBufferSize() + ")");

        r.startRecording();

        if (c.getType() == Channel.CHANNEL_TYPE_WAV) {
            WaveHeader header = new WaveHeader((short) 0, 0, (short) 0, 0L);
            c.write(header.getHeader(), 0, header.getLength());
            c.setDeferredClosing(true);
        }
    }

    private void finalizeAudioData(AbstractStore.Channel c, AudioRecord r, int numberOfBytes) {
        Log.d("SensorDataCollector", "entering finalizeAudioData() ...");
        if (c.getType() == Channel.CHANNEL_TYPE_WAV) {
            short bitsPerSample;
            if (r.getAudioFormat() == AudioFormat.ENCODING_PCM_16BIT) {
                bitsPerSample = 16;
            } else if (r.getAudioFormat() == AudioFormat.ENCODING_PCM_8BIT) {
                bitsPerSample = 8;
            } else {
                bitsPerSample = 16;
            }
            long numberOfSamples;
            numberOfSamples = numberOfBytes / (bitsPerSample / 8);
            WaveHeader header = new WaveHeader((short) r.getChannelCount(), r.getSampleRate(),
                    bitsPerSample, numberOfSamples);
            c.write(header.getHeader(), 0, header.getLength(), 0);
            c.setReadyToClose();
        }
        r.stop();
        r.release();
        r = null; /*
                   * set null according to documentation at
                   * http://developer.android
                   * .com/reference/android/media/AudioRecord.html#release%28%29
                   */
    }
}




Java Source Code List

sysnetlab.android.sdc.datacollector.AndroidSensorEventListener.java
sysnetlab.android.sdc.datacollector.DataCollectionState.java
sysnetlab.android.sdc.datacollector.DateUtils.java
sysnetlab.android.sdc.datacollector.DeviceInformation.java
sysnetlab.android.sdc.datacollector.DropboxHelper.java
sysnetlab.android.sdc.datacollector.ExperimentManagerSingleton.java
sysnetlab.android.sdc.datacollector.ExperimentManager.java
sysnetlab.android.sdc.datacollector.ExperimentTime.java
sysnetlab.android.sdc.datacollector.Experiment.java
sysnetlab.android.sdc.datacollector.Note.java
sysnetlab.android.sdc.datacollector.StateTag.java
sysnetlab.android.sdc.datacollector.Tag.java
sysnetlab.android.sdc.datacollector.TaggingAction.java
sysnetlab.android.sdc.datacollector.TaggingState.java
sysnetlab.android.sdc.datastore.AbstractStore.java
sysnetlab.android.sdc.datastore.SimpleFileStore.java
sysnetlab.android.sdc.datastore.SimpleXmlFileStore.java
sysnetlab.android.sdc.datastore.StoreSingleton.java
sysnetlab.android.sdc.loaders.ExperimentListLoader.java
sysnetlab.android.sdc.sensor.AbstractSensor.java
sysnetlab.android.sdc.sensor.AndroidSensor.java
sysnetlab.android.sdc.sensor.SensorDiscoverer.java
sysnetlab.android.sdc.sensor.SensorProperty.java
sysnetlab.android.sdc.sensor.audio.AudioChannelIn.java
sysnetlab.android.sdc.sensor.audio.AudioEncoding.java
sysnetlab.android.sdc.sensor.audio.AudioRecordParameter.java
sysnetlab.android.sdc.sensor.audio.AudioRecordSettingDBHelper.java
sysnetlab.android.sdc.sensor.audio.AudioRecordSettingDataSource.java
sysnetlab.android.sdc.sensor.audio.AudioSensorHelper.java
sysnetlab.android.sdc.sensor.audio.AudioSensor.java
sysnetlab.android.sdc.sensor.audio.AudioSource.java
sysnetlab.android.sdc.sensor.audio.WaveHeader.java
sysnetlab.android.sdc.sensor.camera.CameraHelper.java
sysnetlab.android.sdc.sensor.camera.CameraSensor.java
sysnetlab.android.sdc.services.RunExperimentService.java
sysnetlab.android.sdc.ui.AudioSensorProbingActivity.java
sysnetlab.android.sdc.ui.CreateExperimentActivity.java
sysnetlab.android.sdc.ui.FragmentActivityBase.java
sysnetlab.android.sdc.ui.GestureEventListener.java
sysnetlab.android.sdc.ui.SensorDataCollectorActivity.java
sysnetlab.android.sdc.ui.TaggingGridView.java
sysnetlab.android.sdc.ui.UserInterfaceUtils.java
sysnetlab.android.sdc.ui.ViewExperimentActivity.java
sysnetlab.android.sdc.ui.adapters.DataStoreListAdapter.java
sysnetlab.android.sdc.ui.adapters.ExperimentListAdapter.java
sysnetlab.android.sdc.ui.adapters.OperationAdapter.java
sysnetlab.android.sdc.ui.adapters.SensorListAdapter.java
sysnetlab.android.sdc.ui.adapters.SensorPropertyListAdapter.java
sysnetlab.android.sdc.ui.adapters.TagListAdapter.java
sysnetlab.android.sdc.ui.adapters.TaggingTagListAdapter.java
sysnetlab.android.sdc.ui.fragments.AudioSensorSetupDialogFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentDataStoreFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentEditNotesFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentEditTagsFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentListFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentRunFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentRunTaggingFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentSensorListFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentSensorSelectionFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentSensorSetupFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentSetupFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentViewFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentViewNotesFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentViewSensorDataFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentViewSensorListFragment.java
sysnetlab.android.sdc.ui.fragments.ExperimentViewTagsFragment.java
sysnetlab.android.sdc.ui.fragments.FragmentUtils.java