package interdroid.swan.sensors.impl;
import interdroid.swan.R;
import interdroid.swan.sensors.AbstractConfigurationActivity;
import interdroid.swan.sensors.AbstractSwanSensor;
import java.util.HashMap;
import java.util.Map;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import android.os.Bundle;
public class SoundSensor extends AbstractSwanSensor {
public static final String TAG = "Sound";
/**
* The configuration activity for this sensor.
*
* @author nick <palmer@cs.vu.nl>
*
*/
public static class ConfigurationActivity extends
AbstractConfigurationActivity {
@Override
public final int getPreferencesXML() {
return R.xml.sound_preferences;
}
}
public static final String RMS_FIELD = "rms";
public static final String DB_FIELD = "db";
public static final String PEAK_DB = "peak_db";
public static final String SAMPLE_INTERVAL = "sample_interval";
public static final String SAMPLE_RATE = "sample_rate";
public static final String AUDIO_SOURCE = "audio_source";
public static final String CHANNEL_CONFIG = "channel_config";
public static final String AUDIO_FORMAT = "audio_format";
public static final int DEFAULT_PEAK_DB = 70;
public static final int DEFAULT_SAMPLE_INTERVAL = 10 * 1000;
public static final int DEFAULT_SAMPLE_RATE = 8000;
public static final int DEFAULT_AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
public static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
public static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
protected static final int HISTORY_SIZE = 3000;
private Map<String, SoundPoller> activeThreads = new HashMap<String, SoundPoller>();
@Override
public String[] getValuePaths() {
return new String[] { RMS_FIELD, DB_FIELD };
}
@Override
public void initDefaultConfiguration(Bundle DEFAULT_CONFIGURATION) {
DEFAULT_CONFIGURATION.putDouble(PEAK_DB, DEFAULT_PEAK_DB);
DEFAULT_CONFIGURATION.putInt(SAMPLE_INTERVAL, DEFAULT_SAMPLE_INTERVAL);
DEFAULT_CONFIGURATION.putInt(SAMPLE_RATE, DEFAULT_SAMPLE_RATE);
DEFAULT_CONFIGURATION.putInt(AUDIO_SOURCE, DEFAULT_AUDIO_SOURCE);
DEFAULT_CONFIGURATION.putInt(AUDIO_FORMAT, DEFAULT_AUDIO_FORMAT);
DEFAULT_CONFIGURATION.putInt(CHANNEL_CONFIG, DEFAULT_CHANNEL_CONFIG);
}
@Override
public void onConnected() {
SENSOR_NAME = "Sound Sensor";
}
@Override
public final void register(String id, String valuePath, Bundle configuration) {
SoundPoller soundPoller = new SoundPoller(id, valuePath, configuration);
activeThreads.put(id, soundPoller);
soundPoller.start();
}
@Override
public final void unregister(String id) {
activeThreads.remove(id).shouldStop();
}
class SoundPoller extends Thread {
private String id;
private Bundle configuration;
private AudioRecord audioRecord;
private String valuePath;
private int bufferSize;
private double peakDb;
private boolean shouldStop = false;
SoundPoller(String id, String valuePath, Bundle configuration) {
this.id = id;
this.configuration = configuration;
this.valuePath = valuePath;
this.bufferSize = 8 * AudioRecord.getMinBufferSize(
configuration.getInt(SAMPLE_RATE,
mDefaultConfiguration.getInt(SAMPLE_RATE)),
configuration.getInt(CHANNEL_CONFIG,
mDefaultConfiguration.getInt(CHANNEL_CONFIG)),
configuration.getInt(AUDIO_FORMAT,
mDefaultConfiguration.getInt(AUDIO_FORMAT)));
audioRecord = new AudioRecord(configuration.getInt(AUDIO_SOURCE,
mDefaultConfiguration.getInt(AUDIO_SOURCE)),
configuration.getInt(SAMPLE_RATE,
mDefaultConfiguration.getInt(SAMPLE_RATE)),
configuration.getInt(CHANNEL_CONFIG,
mDefaultConfiguration.getInt(CHANNEL_CONFIG)),
configuration.getInt(AUDIO_FORMAT,
mDefaultConfiguration.getInt(AUDIO_FORMAT)),
bufferSize);
peakDb = configuration.getDouble(PEAK_DB,
mDefaultConfiguration.getDouble(PEAK_DB));
}
private void shouldStop() {
shouldStop = true;
}
public void run() {
while (!shouldStop) {
long start = System.currentTimeMillis();
double sample = sample(audioRecord, bufferSize,
DB_FIELD.equals(valuePath) ? peakDb : -1);
putValueTrimSize(valuePath, id, start, sample);
long sleepTime = configuration.getInt(SAMPLE_INTERVAL,
mDefaultConfiguration.getInt(SAMPLE_INTERVAL))
+ start
- System.currentTimeMillis();
if (sleepTime > 0) {
try {
sleep(sleepTime);
} catch (InterruptedException e) {
}
}
}
}
/**
* Sample rms.
*
* @return the RMS of the sample
*/
private double sample(AudioRecord audioRecord, int sampleLength,
double peakDb) {
short[] buffer = new short[sampleLength];
int position = 0;
// take the samples
audioRecord.startRecording();
while (position < sampleLength) {
position += audioRecord.read(buffer, position, sampleLength
- position);
}
audioRecord.stop();
double sumOfSquares = 0;
for (int i = 0; i < sampleLength; i++) {
sumOfSquares += buffer[i] * buffer[i];
}
double rms = Math.sqrt(sumOfSquares / sampleLength);
if (peakDb < 0) {
return rms;
}
double db = peakDb + 20 * Math.log10(rms / Short.MAX_VALUE);
return db;
}
}
@Override
public void onDestroySensor() {
for (SoundPoller soundPoller : activeThreads.values()) {
soundPoller.interrupt();
}
super.onDestroySensor();
};
}