我正在尝试制作一个连续记录的应用程序,每隔30秒创建一个新的wav文件,将其与另一个存储的wav文件进行比较,如果不相似,则将其删除并继续记录...等等。
我有一些代码,可以通过按下按钮来记录wav文件:
公共类Main2Activity扩展了AppCompatActivity {
public Button next;
public Button play;
public MediaPlayer player;
public File wavFile;
public Context context;
private static final int PERMISSION_RECORD_AUDIO = 0;
public static String fileName = null;
public static String fileDir = null;
public static String name = null;
public static ArrayList<ArrayList<Integer>> significantEventRMSIDCollect = new ArrayList<ArrayList<Integer>>();
private RecordWaveTask recordTask = null;
public void Main3Activity() {
Intent intent = new Intent(this, Main3Activity.class);
startActivity(intent);
}
public void playEvent(){
try {
player.reset();
player.setDataSource("/data/data/com.example.androidaudiorecorder/files/recording_1537098065.wav");
player.prepare();
player.start();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (Exception e) {
System.out.println("Exception of type : " + e.toString());
e.printStackTrace();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
String name = getIntent().getExtras().getString("Name");
String email = getIntent().getExtras().getString("email");
String phone = getIntent().getExtras().getString("phone");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
next= (Button) findViewById(R.id.nextPage);
next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Main3Activity();
}
});
/** File file = new File("/data/data/com.example.androidaudiorecorder/files/recording_DOG.wav");
if (file.exists()) {
play = (Button) findViewById(R.id.btnPlay);
play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playEvent();
}
});
}else{
Toast.makeText(MainActivity.this, "Please create your first recording before pressing play", Toast.LENGTH_SHORT).show();
}
**/
//noinspection ConstantConditions
findViewById(R.id.btnStart).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (ContextCompat.checkSelfPermission(Main2Activity.this, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
// Request permission
ActivityCompat.requestPermissions(Main2Activity.this,
new String[] { Manifest.permission.RECORD_AUDIO },
PERMISSION_RECORD_AUDIO);
return;
}
// Permission already available
launchTask();
}
});
//noinspection ConstantConditions
findViewById(R.id.btnStop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!recordTask.isCancelled() && recordTask.getStatus() == AsyncTask.Status.RUNNING) {
recordTask.cancel(false);
} else {
Toast.makeText(Main2Activity.this, "Task not running.", Toast.LENGTH_SHORT).show();
}
}
});
// Restore the previous task or create a new one if necessary
recordTask = (RecordWaveTask) getLastCustomNonConfigurationInstance();
if (recordTask == null) {
recordTask = new RecordWaveTask(this);
} else {
recordTask.setContext(this);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case PERMISSION_RECORD_AUDIO:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted
launchTask();
} else {
// Permission denied
Toast.makeText(this, "\uD83D\uDE41", Toast.LENGTH_SHORT).show();
}
break;
}
}
private void launchTask() {
switch (recordTask.getStatus()) {
case RUNNING:
Toast.makeText(this, "Task already running...", Toast.LENGTH_SHORT).show();
return;
case FINISHED:
recordTask = new RecordWaveTask(this);
break;
case PENDING:
if (recordTask.isCancelled()) {
recordTask = new RecordWaveTask(this);
}
}
wavFile = new File(getFilesDir(), "recording_DOG.wav");
name = wavFile.getName();
fileName = System.currentTimeMillis() / 1000 + ".wav";
fileDir = getFilesDir().toString();
String path = this.getFilesDir().getAbsolutePath();
Toast.makeText(this , "Directory : " + path , Toast.LENGTH_LONG);
Toast.makeText(this, wavFile.getAbsolutePath(), Toast.LENGTH_LONG).show();
Toast.makeText(this, "name : " + wavFile.getName(), Toast.LENGTH_LONG).show();
Toast.makeText(this, "directory : " + fileDir, Toast.LENGTH_LONG).show();
Toast.makeText(this, "path " + path , Toast.LENGTH_LONG).show();
recordTask.execute(wavFile);
}
@Override
public Object onRetainCustomNonConfigurationInstance() {
recordTask.setContext(null);
return recordTask;
}
private static class RecordWaveTask extends AsyncTask<File, Void, Object[]> {
// Configure me!
private static final int AUDIO_SOURCE = MediaRecorder.AudioSource.MIC;
private static final int SAMPLE_RATE = 44100; // Hz
private static final int ENCODING = AudioFormat.ENCODING_PCM_16BIT;
private static final int CHANNEL_MASK = AudioFormat.CHANNEL_IN_MONO;
//
private static final int BUFFER_SIZE = 2 * AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_MASK, ENCODING);
private Context ctx;
private RecordWaveTask(Context ctx) {
setContext(ctx);
}
private void setContext(Context ctx) {
this.ctx = ctx;
}
/**
* Opens up the given file, writes the header, and keeps filling it with raw PCM bytes from
* <p>
* AudioRecord until it reaches 4GB or is stopped by the user. It then goes back and updates
* <p>
* the WAV header to include the proper final chunk sizes.
*
* @param files Index 0 should be the file to write to
* @return Either an Exception (error) or two longs, the filesize, elapsed time in ms (success)
*/
@Override
protected Object[] doInBackground(File... files) {
AudioRecord audioRecord = null;
FileOutputStream wavOut = null;
long startTime = 0;
long endTime = 0;
try {
// Open our two resources
audioRecord = new AudioRecord(AUDIO_SOURCE, SAMPLE_RATE, CHANNEL_MASK, ENCODING, BUFFER_SIZE);
wavOut = new FileOutputStream(files[0]);
// Write out the wav file header
writeWavHeader(wavOut, CHANNEL_MASK, SAMPLE_RATE, ENCODING);
// Avoiding loop allocations
byte[] buffer = new byte[BUFFER_SIZE];
boolean run = true;
int read;
long total = 0;
// Let's go
startTime = SystemClock.elapsedRealtime();
audioRecord.startRecording();
while (run && !isCancelled()) {
read = audioRecord.read(buffer, 0, buffer.length);
// WAVs cannot be > 4 GB due to the use of 32 bit unsigned integers.
if (total + read > 4294967295L) {
// Write as many bytes as we can before hitting the max size
for (int i = 0; i < read && total <= 4294967295L; i++, total++) {
wavOut.write(buffer[i]);
}
run = false;
} else {
// Write out the entire read buffer
wavOut.write(buffer, 0, read);
total += read;
}
}
} catch (IOException ex) {
return new Object[]{ex};
} finally {
if (audioRecord != null) {
try {
if (audioRecord.getRecordingState() == AudioRecord.RECORDSTATE_RECORDING) {
audioRecord.stop();
endTime = SystemClock.elapsedRealtime();
}
} catch (IllegalStateException ex) {
//
}
if (audioRecord.getState() == AudioRecord.STATE_INITIALIZED) {
audioRecord.release();
}
}
if (wavOut != null) {
try {
wavOut.close();
} catch (IOException ex) {
//
}
}
}
try {
// This is not put in the try/catch/finally above since it needs to run
// after we close the FileOutputStream
updateWavHeader(files[0]);
} catch (IOException ex) {
return new Object[]{ex};
}
return new Object[]{files[0].length(), endTime - startTime};
}
/**
* Writes the proper 44-byte RIFF/WAVE header to/for the given stream
* <p>
* Two size fields are left empty/null since we do not yet know the final stream size
*
* @param out The stream to write the header to
* @param channelMask An AudioFormat.CHANNEL_* mask
* @param sampleRate The sample rate in hertz
* @param encoding An AudioFormat.ENCODING_PCM_* value
* @throws IOException
*/
private static void writeWavHeader(OutputStream out, int channelMask, int sampleRate, int encoding) throws IOException {
short channels;
switch (channelMask) {
case AudioFormat.CHANNEL_IN_MONO:
channels = 1;
break;
default:
throw new IllegalArgumentException("Unacceptable channel mask");
}
short bitDepth;
switch (encoding) {
case AudioFormat.ENCODING_PCM_8BIT:
bitDepth = 8;
break;
case AudioFormat.ENCODING_PCM_16BIT:
bitDepth = 16;
break;
case AudioFormat.ENCODING_PCM_FLOAT:
bitDepth = 32;
break;
default:
throw new IllegalArgumentException("Unacceptable encoding");
}
writeWavHeader(out, channels, sampleRate, bitDepth);
}
/**
* Writes the proper 44-byte RIFF/WAVE header to/for the given stream
* <p>
* Two size fields are left empty/null since we do not yet know the final stream size
*
* @param out The stream to write the header to
* @param channels The number of channels
* @param sampleRate The sample rate in hertz
* @param bitDepth The bit depth
* @throws IOException Throws Exception
*/
private static void writeWavHeader(OutputStream out, short channels, int sampleRate, short bitDepth) throws IOException {
// Convert the multi-byte integers to raw bytes in little endian format as required by the spec
byte[] littleBytes = ByteBuffer
.allocate(14)
.order(ByteOrder.LITTLE_ENDIAN)
.putShort(channels)
.putInt(sampleRate)
.putInt(sampleRate * channels * (bitDepth / 8))
.putShort((short) (channels * (bitDepth / 8)))
.putShort(bitDepth)
.array();
// Not necessarily the best, but it's very easy to visualize this way
out.write(new byte[]{
// RIFF header
'R', 'I', 'F', 'F', // ChunkID
0, 0, 0, 0, // ChunkSize (must be updated later)
'W', 'A', 'V', 'E', // Format
// fmt subchunk
'f', 'm', 't', ' ', // Subchunk1ID
16, 0, 0, 0, // Subchunk1Size
1, 0, // AudioFormat
littleBytes[0], littleBytes[1], // NumChannels
littleBytes[2], littleBytes[3], littleBytes[4], littleBytes[5], // SampleRate
littleBytes[6], littleBytes[7], littleBytes[8], littleBytes[9], // ByteRate
littleBytes[10], littleBytes[11], // BlockAlign
littleBytes[12], littleBytes[13], // BitsPerSample
// data subchunk
'd', 'a', 't', 'a', // Subchunk2ID
0, 0, 0, 0, // Subchunk2Size (must be updated later)
});
}
/**
* Updates the given wav file's header to include the final chunk sizes
*
* @param wav The wav file to update
* @throws IOException
*/
private static void updateWavHeader(File wav) throws IOException {
byte[] sizes = ByteBuffer
.allocate(8)
.order(ByteOrder.LITTLE_ENDIAN)
// There are probably a bunch of different/better ways to calculate
// these two given your circumstances. Cast should be safe since if the WAV is
// > 4 GB we've already made a terrible mistake.
.putInt((int) (wav.length() - 8)) // ChunkSize
.putInt((int) (wav.length() - 44)) // Subchunk2Size
.array();
RandomAccessFile accessWave = null;
//noinspection CaughtExceptionImmediatelyRethrown
try {
accessWave = new RandomAccessFile(wav, "rw");
// ChunkSize
accessWave.seek(4);
accessWave.write(sizes, 0, 4);
// Subchunk2Size
accessWave.seek(40);
accessWave.write(sizes, 4, 4);
} catch (IOException ex) {
// Rethrow but we still close accessWave in our finally
throw ex;
} finally {
if (accessWave != null) {
try {
accessWave.close();
} catch (IOException ex) {
//
}
}
}
}
@Override
protected void onCancelled(Object[] results) {
// Handling cancellations and successful runs in the same way
onPostExecute(results);
}
@Override
protected void onPostExecute(Object[] results) {
Throwable throwable = null;
if (results[0] instanceof Throwable) {
// Error
throwable = (Throwable) results[0];
Log.e(RecordWaveTask.class.getSimpleName(), throwable.getMessage(), throwable);
}
// If we're attached to an activity
if (ctx != null) {
if (throwable == null) {
// Display final recording stats
double size = (long) results[0] / 1000000.00;
long time = (long) results[1] / 1000;
Toast.makeText(ctx, String.format(Locale.getDefault(), "%.2f MB / %d seconds",
size, time), Toast.LENGTH_LONG).show();
} else {
// Error
Toast.makeText(ctx, throwable.getLocalizedMessage(), Toast.LENGTH_LONG).show();
}
}
}
}
这工作正常,但是我尝试使用计时器使其在按下开始按钮后每30秒执行一次:
Timer t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
launchTask();
file = new File(getFilesDir(), "monitoring.wav");
name = wavFile.getName();
fileName = System.currentTimeMillis() / 1000 + ".wav";
fileDir = getFilesDir().toString();
recordTask.execute(wavFile);
if (file.exists()){
for (int loop =0 ; loop < taggedWavs.size(); loop ++){
getMFCC(file , taggedWavs.get(loop) );
}
}
}
}, 0, 30000);
但这会导致appto崩溃,我不确定这是否是解决此问题的正确方法。
任何有关如何更好地实现这一目标的建议将不胜感激。