Android MIC からオーディオをキャプチャし、入力に対して FFT を実行してから、グラフをユーザーにグラフ表示する小さなアプリを作成しています。記録とグラフ化を同時に実行しようとしています (明らかに、記録されてからグラフ化されるまでにわずかな遅延があります)。読み取り用と処理用の 2 つのスレッドを起動しようとしています。ただし、処理時に同期の問題が発生し、ゼロしか受信していないように見えます (または受信していないようです)。どんなアドバイスでも大歓迎です。:)
public class Plotter extends Activity {
/* plotting objects */
private static GraphicalView mView;
private LineGraph line = new LineGraph();
private boolean recordAudio = true; // record?
private AudioRecord mRecorder = null; // audio object
private Menu mMenu; // app menu
private static final String LOG_TAG = "Frequency Plotter"; // debug tag
private Mfft mfft = null; // FFT class
private static final int BUF_SIZE = 8192; // amount to read in
private Thread listener = null;
private Thread processor = null;
Stack<Float> items = new Stack<Float>();
/* colors for line */
private int[] colors = {Color.BLUE,Color.CYAN,Color.DKGRAY,Color.GRAY,
Color.GREEN,Color.LTGRAY,Color.MAGENTA,Color.RED,Color.WHITE,Color.YELLOW};
private void processAudio(){
ArrayList<Double> real = new ArrayList<Double>();
try{
Random randomGenerator = new Random();
float[] in = new float[2048];
Arrays.fill(in,1);
while(true){
synchronized(items){
while(items.size() < 2048)
items.wait();
items.notifyAll();
for(int i=0; i < 2048; i++){
in[i] = items.pop();
}
}
double[] ret = mfft.fft(2048,44100,in); // get FFT of data
TimeSeries dataset = new TimeSeries( (real.size()+1)/2048 + "" );
XYSeriesRenderer renderer = new XYSeriesRenderer(); // customized renderer
// Customization time
renderer.setColor(colors[randomGenerator.nextInt(10)]);
renderer.setPointStyle(PointStyle.SQUARE);
renderer.setFillPoints(true);
line.addRenderer(renderer); // add custom renderer
for(int i = 0; i < 2048; i++){
real.add(ret[i]);
dataset.add(real.size()-1,ret[i]); // Add it to our graph
}
line.addDataset(dataset); // add data to line
mView.repaint(); // render lines
}
}catch(Exception e){
Log.e(LOG_TAG, e + " ");
}
}
private void writeToBuffer(short[] in) {
synchronized(items){
for(int i = 0; i < BUF_SIZE; i++){ // copy to create float
items.push((float)in[i]);
}
items.notifyAll();
}
}
private void listen(){
final short[] in = new short[BUF_SIZE];
mRecorder = new AudioRecord(
MediaRecorder.AudioSource.MIC, // source
44100, // frequency (HERTZ)
AudioFormat.CHANNEL_IN_MONO, // channel
AudioFormat.ENCODING_PCM_16BIT, // format
BUF_SIZE // size data packet
);
mRecorder.startRecording();
while(recordAudio){
try{
/* read next part */
mRecorder.read(in,0,BUF_SIZE); // read from device
writeToBuffer(in);
}catch(Exception t){
/* something went horribly wrong!!!*/
recordAudio = false;
Log.e(LOG_TAG, "Failure reading" + t.getMessage());
}
}
}
private void startRecording(){
/* create a new thread that will run the recording in the background */
listener = new Thread(
new Runnable(){
public void run(){
listen();
}
});
listener.start();
/* small delay to produce */
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
/* create a thread to process the audio */
processor = new Thread(
new Runnable(){
public void run(){
processAudio();
}
});
processor.start();
}
private void stopRecording(){
recordAudio = false;
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
/** clear the current chart */
private void clearChart(){
line = new LineGraph();
this.onStart();
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
/* instantiate */
mfft = new Mfft(); // instance of the FFT class
mView = line.getView(this); // get the chart view
/* new horizontal layout */
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.addView(mView); // add chart to layout
setContentView(ll); // set layout
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.record:
startRecording();
item.setEnabled(false); // disable start
mMenu.findItem(R.id.stop).setEnabled(true); // enable stop
return true;
case R.id.stop:
stopRecording();
item.setEnabled(false); // disable stop
mMenu.findItem(R.id.clear).setEnabled(true); // enable stop
return true;
case R.id.clear:
clearChart(); // clear chart
item.setEnabled(false); // disable clear
mMenu.findItem(R.id.record).setEnabled(true); // enable stop
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
mMenu = menu;
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.my_menu, menu);
return true;
}
}
編集:完全な定義を追加しました。