Android Open Source - Backyard-Brains-Android-App Audio Service






From Project

Back to project page Backyard-Brains-Android-App.

License

The source code is released under:

GNU General Public License

If you think the Android project Backyard-Brains-Android-App 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

/*
 * Backyard Brains Android App/* ww w. j a  v a 2  s  . c om*/
 * Copyright (C) 2011 Backyard Brains
 * by Nathan Dotz <nate (at) backyardbrains.com>
 *
 * 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 com.backyardbrains.audio;

import java.nio.ByteBuffer;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;

import com.backyardbrains.BackyardAndroidActivity;
import com.backyardbrains.R;

/**
 * Manages a thread which monitors default audio input and pushes raw audio data
 * to bound activities.
 * 
 * @author Nathan Dotz <nate@backyardbrains.com>
 * @version 1
 * 
 */



public class AudioService extends Service implements ReceivesAudio {
  static final String TAG = AudioService.class.getCanonicalName();
  private int NOTIFICATION = R.string.mic_thread_running;
  public boolean running;

  /**
   * Provides a reference to {@link AudioService} to all bound clients.
   */
public class AudioServiceBinder extends Binder {
    public AudioService getService() {
      return AudioService.this;
    }
  }

  private ToggleRecordingListener toggleRecorder;

  private final IBinder mBinder = new AudioServiceBinder();
  //private int mBindingsCount;

  private MicListener micThread;
  private RingBuffer audioBuffer;
  private RecordingSaver mRecordingSaverInstance;

  private NotificationManager mNM;
  private TriggerAverager triggerAverager;
  private boolean triggerMode;
  private ToggleTriggerListener toggleTrigger;
  private SetSampleSizeListener sampleSizeListener;
  private long lastSamplesReceivedTimestamp;
  private int micListenerBufferSizeInSamples;

  /**
   * @return the micListenerBufferSizeInSamples
   */
  public int getMicListenerBufferSizeInSamples() {
    return micListenerBufferSizeInSamples;
  }

  /**
   * @return the lastSamplesReceivedTimestamp
   */
  public long getLastSamplesReceivedTimestamp() {
    return lastSamplesReceivedTimestamp;
  }

  /**
   * return a byte array with in the appropriate order representing the last
   * 1.5 seconds of audio or so
   * 
   * @return a ordinate-corrected version of the audio buffer
   */
  public short[] getAudioBuffer() {
    return audioBuffer.getArray();
  }
  
  public short[] getTriggerBuffer() {
    return triggerAverager.getAveragedSamples();
  }

  public void setMicListenerBufferSizeInSamples(int i) {
    micListenerBufferSizeInSamples = i;
  }

  /**
   * Register a receiver for toggling recording funcionality, then instantiate
   * our ringbuffer and turn on mic thread
   * 
   * @see android.app.Service#onCreate()
   */
  @Override
  public void onCreate() {
    super.onCreate();

    registerRecordingToggleReceiver(true);

    audioBuffer = new RingBuffer(131072);
    audioBuffer.zeroFill();
    
    registerTriggerToggleReceiver(true);
    triggerAverager = new TriggerAverager(50);
    triggerMode = false;

    registerSetSampleSizeReceiver(true);
    
    turnOnMicThread();
  }
  
  /**
   * 
   */
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    return super.onStartCommand(intent, Service.START_STICKY, startId);
  }

  /**
   * unregister our recording listener, then kill {@link MicListener}
   * 
   * @see android.app.Service#onDestroy()
   */
  @Override
  public void onDestroy() {
    registerRecordingToggleReceiver(false);
    registerTriggerToggleReceiver(false);
    registerSetSampleSizeReceiver(false);
    turnOffMicThread();
    super.onDestroy();
  }

  /**
   * return a binding pointer for GL threads to reference this object
   * 
   * @see android.app.Service#onBind(android.content.Intent)
   * @return binding reference to this object
   */
  @Override
  public IBinder onBind(Intent arg0) {
    //mBindingsCount++;
    //Log.d(TAG, "Bound to service: " + mBindingsCount + " instances");
    return mBinder;
  }

  @Override
  public boolean onUnbind(Intent intent) {
    //mBindingsCount--;
    //Log.d(TAG, "Unbound from service: " + mBindingsCount + " instances");
    return super.onUnbind(intent);
  }

  /**
   * Instantiate {@link MicListener} thread, tell it to start, and put up the
   * notification via {@link AudioService#showNotification()}
   */
  public void turnOnMicThread() {
    micThread = null;
    micThread = new MicListener();
    micThread.start(AudioService.this);
    mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    showNotification(true);
    Log.d(TAG, "Mic thread started");
  }

  /**
   * Clean up {@link MicListener} resources and remove notification
   */
  public void turnOffMicThread() {
    if (micThread != null) {
      micThread.requestStop();
      micThread = null;
      Log.d(TAG, "Mic Thread Shut Off");
    }
    showNotification(false);
  }

  /**
   * Toggle our receiver. If true, register a receiver for intents with the
   * action "BYBToggleRecording", otherwise, unregister the same receiver.
   * 
   * @param reg register if true, unregister otherwise.
   */
  private void registerRecordingToggleReceiver(boolean reg) {
    if (reg) {
      IntentFilter intentFilter = new IntentFilter("BYBToggleRecording");
      toggleRecorder = new ToggleRecordingListener();
      registerReceiver(toggleRecorder, intentFilter);
    } else {
      unregisterReceiver(toggleRecorder);
    }
  }

  private void registerTriggerToggleReceiver(boolean reg) {
    if (reg) {
      IntentFilter intentFilter = new IntentFilter("BYBToggleTrigger");
      toggleTrigger = new ToggleTriggerListener();
      registerReceiver(toggleTrigger, intentFilter);
    } else {
      unregisterReceiver(toggleTrigger);
    }
  }

  private void registerSetSampleSizeReceiver(boolean reg) {
    if (reg) {
      IntentFilter intentFilter = new IntentFilter("setSampleSize");
      sampleSizeListener = new SetSampleSizeListener();
      registerReceiver(sampleSizeListener, intentFilter);
    } else {
      unregisterReceiver(sampleSizeListener);
    }
  }
  /**
   * Toggle a notification that this service is running.
   * 
   * @param show show if true, hide otherwise.
   * @see android.app.Notification
   * @see NotificationManager#notify()
   */
  private void showNotification(boolean show) {
    if (show) {
      CharSequence text = getText(R.string.mic_thread_running);
      Notification not = new Notification(R.drawable.ic_launcher_byb,
          text, System.currentTimeMillis());
      PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
          new Intent(this, BackyardAndroidActivity.class), 0);
      not.setLatestEventInfo(this, "Backyard Brains", text, contentIntent);
      mNM.notify(NOTIFICATION, not);
    } else {
      if (mNM != null) {
        mNM.cancel(NOTIFICATION);
      }
    }
  }

  /**
   * On receiving audio, add it to the RingBuffer. If we're recording, also
   * dispatch it to the RecordingSaver instance.
   * 
   * @see com.backyardbrains.audio.RecievesAudio#receiveAudio(byte[])
   */
  @Override
  public void receiveAudio(ByteBuffer audioInfo) {
    audioBuffer.add(audioInfo);
    lastSamplesReceivedTimestamp = System.currentTimeMillis();
    if(triggerMode) {
//      Log.d(TAG, "Pushing audio to triggerAverager, length: "+audioInfo.capacity());
      triggerAverager.push(audioInfo);
    }
    if (mRecordingSaverInstance != null) {
      recordAudio(audioInfo);
    }

  }

  /**
   * dispatch audio to the active RecordingSaver instance
   * 
   * @param audioInfo
   */
  private void recordAudio(ByteBuffer audioInfo) {
    audioInfo.clear();
    try {
      mRecordingSaverInstance.receiveAudio(audioInfo);
    } catch (IllegalStateException e) {
      Log.w(getClass().getCanonicalName(),
          "Ignoring bytes received while not synced: "
              + e.getMessage());
    }
  }

  public boolean startRecording() {
    if (mRecordingSaverInstance != null) {
      return false;
    }
    Long theTime = (Long) System.currentTimeMillis();
    try {
      mRecordingSaverInstance = new RecordingSaver(theTime.toString());
    } catch (IllegalStateException e) {
      Toast.makeText(getApplicationContext(),
          "No SD Card is available. Recording is disabled",
          Toast.LENGTH_LONG).show();
    }
    return true;
  }

  public boolean stopRecording() {
    if (mRecordingSaverInstance != null) {
      mRecordingSaverInstance.finishRecording();
      mRecordingSaverInstance = null;
      return true;
    }
    return false;
  }
  
  public Handler getTriggerHandler() {
    return triggerAverager.getHandler();
  }

  private class ToggleRecordingListener extends BroadcastReceiver {
    @Override
    public void onReceive(android.content.Context context,
        android.content.Intent intent) {
      if (!startRecording()) {
        if (!stopRecording()) {
          Log.w(TAG, "There was an error recording properly");
        }
      }
    };
  }

  private class ToggleTriggerListener extends BroadcastReceiver {

    @Override
    public void onReceive(android.content.Context context,
        android.content.Intent intent) {
      triggerMode = intent.getBooleanExtra("triggerMode", false);
      Log.d(TAG, "Switched triggerMode to "+triggerMode);
    };
    
  }
  
  private class SetSampleSizeListener extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
      triggerAverager.setMaxsize(intent.getIntExtra("newSampleSize", 1));
      Log.d(TAG, "Set triggeraverager sample size to "+triggerAverager.getMaxsize()); 
    }
    
  }

}




Java Source Code List

com.backyardbrains.BackyardAndroidActivity.java
com.backyardbrains.BackyardBrainsApplication.java
com.backyardbrains.BackyardBrainsConfigurationActivity.java
com.backyardbrains.FileListActivity.java
com.backyardbrains.TriggerActivity.java
com.backyardbrains.audio.AudioService.java
com.backyardbrains.audio.MicListener.java
com.backyardbrains.audio.ReceivesAudio.java
com.backyardbrains.audio.RecordingSaver.java
com.backyardbrains.audio.RingBuffer.java
com.backyardbrains.audio.TriggerAverager.java
com.backyardbrains.drawing.ContinuousGLSurfaceView.java
com.backyardbrains.drawing.OscilloscopeRenderer.java
com.backyardbrains.drawing.ThresholdGlSurfaceView.java
com.backyardbrains.drawing.ThresholdRenderer.java
com.backyardbrains.view.ScaleListener.java
com.backyardbrains.view.TwoDimensionScaleGestureDetector.java
com.backyardbrains.view.UIFactory.java