3

I am trying to implement a pause/resume functionality for my audio recording module in Java. So far all I have found on SO is this, which does not give a definitive answer - I have tried the answerer's suggestion of calling start() and stop() on the TargetDataLine to achieve this, and am aware that stop() (from Oracle):

"Stops the line. A stopped line should cease I/O activity. If the line is open and running, however, it should retain the resources required to resume activity. A stopped line should retain any audio data in its buffer instead of discarding it, so that upon resumption the I/O can continue where it left off, if possible."

However I am finding that when I call stop on my TargetDataLine, then sleep for five seconds and call start() to reopen it, the recording never resumes. Here is the code:

import java.io.*;
import java.util.Timer;
import java.util.TimerTask;

import javax.sound.sampled.*;
import javax.sound.sampled.AudioFileFormat.Type;

public class AudioRecorder
{
    static TargetDataLine targetLine = null;
    static final long maxRecordingTime = 3600000; // One hour in milliseconds
    static Thread recordThread;

    public static void main(String[] args)
    {
        try
        {
            // Initialise audio format settings and setup data line matching format specification

            initialiseAudioSettings();
        }
        catch (LineUnavailableException e)
        {
            e.printStackTrace();
        }

        // TEST

        startRecording();

        try
        {
            System.out.println("Sleeping for 5s...");
            Thread.sleep(5000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        System.out.println("About to pause recording...");

        pauseRecording();

        try
        {
            System.out.println("Sleeping for 5s...");
            Thread.sleep(5000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        System.out.println("About to resume recording...");

        resumeRecording();

        try
        {
            System.out.println("Sleeping for 5s...");
            Thread.sleep(5000);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }

        System.out.println("About to stop recording...");

        stopRecording();

        System.out.println("Recording stopped...(in theory)");

        // /TEST
    }

    private static void initialiseAudioSettings() throws LineUnavailableException
    {
        // Define audio format as:
        // Encoding: Linear PCM
        // Sample Rate: 44.1 kHz
        // Bit Depth: 16-bit
        // Channel Format: Stereo
        // Data Storage: Signed & Big-Endian

        final AudioFormat audioFormat = new AudioFormat(44100, 16, 2, true, true);

        // Store format metadata in an Info variable 

        final DataLine.Info audioFormatInfo = new DataLine.Info(TargetDataLine.class, audioFormat);

        if (!AudioSystem.isLineSupported(Port.Info.MICROPHONE))
        {
            throw new LineUnavailableException("No microphone has been detected. Please reconnect the microphone and try again.");
        } else
        {
            System.out.println("Microphone detected. Querying target data line...");
        }

        // Use metadata to ascertain whether audio format is supported by default input device

        if (AudioSystem.isLineSupported(audioFormatInfo) == false)
        {
            throw new LineUnavailableException("The default input device does not support the specified audio output format");
        }

        // Get a line matching the specified audio format 

        targetLine = (TargetDataLine)AudioSystem.getLine(audioFormatInfo);

        // Instruct the system to allocate resources to the targetLine and switch it on

        targetLine.open();

        // Prepare line for audio input

        targetLine.start();
    }

    private static void startRecording()
    {
        TimerTask scheduleRecordingEnd = new TimerTask()
                {
                    public void run()
                    {
                        stopRecording();
                    }
                };

        Timer recordingTimer = new Timer("Recording Timer");

        recordingTimer.schedule(scheduleRecordingEnd, maxRecordingTime);

        // Setup recording thread

        recordThread = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                {
                    // Route audio input stream to target data line

                    AudioInputStream audioInputStream = new AudioInputStream(targetLine);

                    // Instantiate output filepath & filename

                    File outputFile = new File("C:/temp/test.wav");

                    // Write input audio to output .wav file

                    try
                    {
                        AudioSystem.write(audioInputStream, AudioFileFormat.Type.WAVE, outputFile);
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        });

        // Start recording

        recordThread.start();
    }

    private static void pauseRecording()
    {
        targetLine.stop();
    }

    private static void resumeRecording()
    {   
        targetLine.start();
    }

    private static void stopRecording()
    {
        // Cease all I/O functions of the line

        targetLine.stop();

        // Close the line, deallocating system resources and deleting metadata

        targetLine.close();

        System.out.println("Stopping recording...");

        recordThread.stop();
    }
}

The parts of this code that should be of interest are the tests in main() and the startRecording(), pauseRecording() and resumeRecording() functions, although I have included the code in its entirety for completeness.

Could anyone point me in the right direction as to why this might be happening? Any help would be much appreciated.

Mick McCarthy
  • 428
  • 2
  • 16
  • I guess API doesn't provide any straight forward way to do that. Try to append at the end of an existing file. Check other overloaded `write()` method in `AudioSystem`. You need to re-run your `recordThread`, in your code, it's just executed only once. – Mokarrom Hossain Mar 08 '19 at 13:14
  • I am coming to this conclusion myself. I think writing to several .wav files and concatenating them may be the way to go. Regarding re-running my `recordThread`, why would I need to do this? As `pauseRecording()`, to the best of my knowledge, doesn't actually stop the thread but rather stops the data line - then again, I am not very experienced with threading so am not sure what the exit conditions are? – Mick McCarthy Mar 11 '19 at 12:57
  • Your `recordThread` finishes as soon as `AudioSystem.write()` returns regardless you call `pauseRecording()/resumeRecording()/stopRecording()`. – Mokarrom Hossain Mar 11 '19 at 13:05
  • I see. In that case I think the problem is that for whatever reason, stopping the data line causes `AudioSystem.write()` to return, terminating the `recordThread`. Can you think of a way round this? If I run the `recordThread` again will it continue writing to the same .wav file? – Mick McCarthy Mar 11 '19 at 13:07
  • `If I run the recordThread again will it continue writing to the same .wav file?` In that case, I guess, it will delete the old file and create a new file. Therefore, I suggest you looking for other overloaded methods in `AudioSystem`. – Mokarrom Hossain Mar 11 '19 at 13:10

0 Answers0