Andorid Studio每30秒记录一次WAV文件

时间:2019-07-04 18:19:38

标签: java android-studio timer wav audiorecord

我正在尝试制作一个连续记录的应用程序,每隔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崩溃,我不确定这是否是解决此问题的正确方法。

任何有关如何更好地实现这一目标的建议将不胜感激。

0 个答案:

没有答案