republicofgavin / PauseResumeAudioRecorder

An audio recorder that supports pause/resume functionality (WAV files only for now)
Apache License 2.0
21 stars 12 forks source link

Remove last 1 second of record #3

Open dajver opened 7 years ago

dajver commented 7 years ago

Hi again :)

It could be that you won't be able to help me with this, but I will try to ask you :) Maybe you know how to remove last second of record when we tap on stop button? Because I have some issue. When we click on stop button very loudly it hearly on the record on last second. Can we remove this last second? Or maybe make less sesitivity of microphone?

Thank you advance!

dajver commented 7 years ago

Heeeey =( Could you help me?

republicofgavin commented 7 years ago

The best I could find in terms of making it less sensitive was this answer: http://stackoverflow.com/questions/26317772/increase-volume-of-recording-android-audiorecord

republicofgavin commented 7 years ago

In regards to removing the last second, I think it is possible. I had to press hard on my phone for me to even hear the press. So, I don't think I should add this to the recorder itself. It's just recording the loud press. Feel free to fork the library to add changes you think would make better. With that being said, I think I can get you started in the right direction. I wrote a crude and untested alteration to the thread to demonstrate the approach I think would work. I put in a log statement that counted the number of times the loop repeated in the span of a second (my estimate was around 15). So, I created a list that "holds" the audio record recording and only adds to the file was the size has reached its limit. In other words, it just adds runs on a second delay. This approach is similar to a CircularFifoQueue(http://commons.apache.org/proper/commons-collections/javadocs/api-release/org/apache/commons/collections4/queue/CircularFifoQueue.html), but I didn't want to pull in Apache commons just for one collection. ` private class AudioRecorderThread extends Thread{ private AudioRecord currentAudioRecording; private int bufferSizeInBytes; private long threadMaxFileSizeInBytes; private String threadAudioFile; private int threadChannelConfig; private int threadAudioEncoding; private int threadSampleRateHertz; private List<short[]>audioCache; private static final int NUMBER_OF_READS_PER_SECOND=15;

    /**
     * Default constructor. Parameters are passed into the thread to keep the recorder(ultimately the user) from changing the values and thus altering the state of the thread.
     * @param threadAudioFile The file path where the {@link AudioRecord} writes data to. Ultimately it will deleted when the data is converted.
     * @param threadAudioSource The source of the audio data. Currently, only MIC is supported.
     * @param threadSampleRateHertz The sample rate in Hz
     * @param threadChannelConfig The channel config (MONO or STEREO).
     * @param threadAudioEncoding The audio encoding (8 bit or 16 bit).
     * @param maxFileSizeInBytes Maximum file size in bytes.
     */
    AudioRecorderThread(final String threadAudioFile,final int threadAudioSource, final int threadSampleRateHertz, final int threadChannelConfig, final int threadAudioEncoding,final long maxFileSizeInBytes){
        this.threadAudioFile=threadAudioFile;
        bufferSizeInBytes=AudioRecord.getMinBufferSize(threadSampleRateHertz,threadChannelConfig,threadAudioEncoding);
        currentAudioRecording=new AudioRecord(threadAudioSource,threadSampleRateHertz,threadChannelConfig,threadAudioEncoding,bufferSizeInBytes);

        this.threadSampleRateHertz=threadSampleRateHertz;
        this.threadChannelConfig=threadChannelConfig;
        this.threadAudioEncoding=threadAudioEncoding;
        this.threadMaxFileSizeInBytes=maxFileSizeInBytes;
        audioCache=new LinkedList<>();
    }
    @Override
    public void run(){
        currentAudioRecording.startRecording();
        final short[] readingBuffer = new short[bufferSizeInBytes];
        DataOutputStream dataOutputStream=null;
        final short waveHeaderChannelConfig=(short)((threadChannelConfig==AudioFormat.CHANNEL_IN_MONO)?1:2);
        final short waveHeaderBitrateConfig=(short)((AudioFormat.ENCODING_PCM_8BIT==threadAudioEncoding)?8:16);
        try {
            dataOutputStream = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(threadAudioFile)));
            int currentState = currentAudioState.getAndSet(currentAudioState.get());//This, unlike the normal get, does it atomically.
            //we add a fake header to be a place holder for the real header, once the recording is complete.
            PcmWavConverter.addWavHeader(new PcmWavConverter.WaveHeader(threadSampleRateHertz, waveHeaderChannelConfig, waveHeaderBitrateConfig),threadAudioFile);
            long currentFileSizeInBytes=0;
            while (currentState == RECORDING_STATE || currentState == PAUSED_STATE) {
                if (currentState == PAUSED_STATE) {
                    sleep(100);
                }
                else {
                    final int length = currentAudioRecording.read(readingBuffer, 0, bufferSizeInBytes);
                    audioCache.add(readingBuffer);
                    if (audioCache.size()>=NUMBER_OF_READS_PER_SECOND) {
                        for (int i = 0; i < audioCache.size(); i++) {
                            //write the data in Little Endian format;
                            dataOutputStream.writeByte(audioCache.get(0)[i] & 0xFF);
                            dataOutputStream.writeByte((audioCache.get(0)[i] >> 8) & 0xFF);
                        }
                        audioCache.remove(0);
                        currentFileSizeInBytes = currentFileSizeInBytes + bufferSizeInBytes;
                        //If the next input clip goes over, just stop the thread now.
                        if (currentFileSizeInBytes + bufferSizeInBytes > threadMaxFileSizeInBytes) {
                            Log.d(TAG, "Max file size has been reached. Stopping recording thread.");
                            currentAudioState.getAndSet(STOPPED_STATE);
                            new Thread(new MaxSizeReachedRunnable()).run();
                        }
                    } 
                }
                currentState = currentAudioState.getAndSet(currentAudioState.get());
            }
            currentAudioRecording.stop();
            currentAudioRecording.release();
        }
        catch(IOException ex){
            currentAudioState.getAndSet(ERROR_STATE);
            throw new RuntimeException("IOException has occurred while recording file: "+threadAudioFile,ex);
        }
        catch (InterruptedException ex){
            currentAudioState.getAndSet(ERROR_STATE);
            Log.d(TAG,"InterruptedException occurred for audioFile: "+ threadAudioFile);
        }
        finally{
            try {
                if (dataOutputStream !=null) {
                    dataOutputStream.flush();
                    dataOutputStream.close();
                    PcmWavConverter.addWavHeader(new PcmWavConverter.WaveHeader(threadSampleRateHertz, waveHeaderChannelConfig, waveHeaderBitrateConfig), threadAudioFile);

                    if(!(new File(threadAudioFile).renameTo(new File(threadAudioFile.replace(".temp", ".wav"))))){
                        Log.e(TAG,"PCM file was not renamed.");
                        currentAudioState.getAndSet(ERROR_STATE);
                    }
                }
            }
            catch (IOException ex){
                Log.e(TAG,"IOException occurred for audioFile"+audioFile);
                currentAudioState.getAndSet(ERROR_STATE);
            }
        }
    }
}

`

dajver commented 7 years ago

Thank you for your response :) But I don't understand what you did in this code, what would help me remove last second in record...

I understood that you changed this part of code:

audioCache.add(readingBuffer);
                    if (audioCache.size()>=NUMBER_OF_READS_PER_SECOND) {
                        for (int i = 0; i < audioCache.size(); i++) {
                            //write the data in Little Endian format;
                            dataOutputStream.writeByte(audioCache.get(0)[i] & 0xFF);
                            dataOutputStream.writeByte((audioCache.get(0)[i] >> 8) & 0xFF);
                        }
                        audioCache.remove(0);
                        currentFileSizeInBytes = currentFileSizeInBytes + bufferSizeInBytes;
                        //If the next input clip goes over, just stop the thread now.
                        if (currentFileSizeInBytes + bufferSizeInBytes > threadMaxFileSizeInBytes) {
                            Log.d(TAG, "Max file size has been reached. Stopping recording thread.");
                            currentAudioState.getAndSet(STOPPED_STATE);
                            new Thread(new MaxSizeReachedRunnable()).run();
                        }
                    } 

but I realy don't understand what heppening there, except that you put audioCache to dataOutputStream with small delay. Maybe I didn't understood something?

I'm sorry for that, just want to understand.

dajver commented 7 years ago

I already cloned your library and changed it for myself, that is why, I can change it more, I just need understand where and how :D

republicofgavin commented 7 years ago

It's no problem. It's a crude example, so it probably wasn't clear. Because there is a delay in writing to the file AND the list isn't completely added after the user presses stop, the delayed audio never gets written. Hence, it is a "second" off.

dajver commented 7 years ago

I tried your example, but it record just last second of record, and don't save all previous. Maybe I need to change something?

while (currentState == RECORDING_STATE || currentState == PAUSED_STATE) {
                    if (currentState == PAUSED_STATE) {
                        onAmplitudeRunning.getAmplitude(0, currentState);
                        sleep(100);
                    } else {
                        double sum = 0;
                        final int length = currentAudioRecording.read(readingBuffer, 0, bufferSizeInBytes);
                        audioCache.add(readingBuffer);
                        if (audioCache.size() >= NUMBER_OF_READS_PER_SECOND) {
                            for (int i = 0; i < audioCache.size(); i++) {
                                //write the data in Little Endian format;
                                dataOutputStream.writeByte(audioCache.get(0)[i] & 0xFF);
                                dataOutputStream.writeByte((audioCache.get(0)[i] >> 8) & 0xFF);
                                sum += audioCache.get(0)[i] * audioCache.get(0)[i];
                            }
                            audioCache.remove(0);
                            if (length > 0) {
                                final double amplitude = sum / length;
                                onAmplitudeRunning.getAmplitude((int) Math.sqrt(amplitude) / 2, currentState);
                            }
                            currentFileSizeInBytes = currentFileSizeInBytes + bufferSizeInBytes;
                            //If the next input clip goes over, just stop the thread now.
                            if (currentFileSizeInBytes + bufferSizeInBytes > threadMaxFileSizeInBytes) {
                                Log.d(TAG, "Max file size has been reached. Stopping recording thread.");
                                currentAudioState.getAndSet(STOPPED_STATE);
                                new Thread(new MaxSizeReachedRunnable()).run();
                            }
                        }
                    }
                    currentState = currentAudioState.getAndSet(currentAudioState.get());
                }

Maybe I wrote something wring here?

dajver commented 7 years ago

Heeey. Do you have any idea? :(