Question

I am using the AudioRecord class from https://github.com/nonameentername/soundtouch-android/blob/master/src/org/tecunhuman/ExtAudioRecorder.java but with it being slightly modified.

In the start() method, I start recording with the AudioRecord class. I also start a MediaPlayer to play a instrumental. To get the vocals and the instrumental to mix at the right positions on the server, I send the instrumental delay to pad the instrumental at the beginning with silence (beatDelayInSeconds - MediaPlayer delay from when start is called to it actually being played since there is latency when starting media player).

My logic must be wrong because the timing on the playback of the vocals and the instrumental is off on the device. Some times the recording will be too fast, sometimes it will be too slow in comparison to when the instrumental is playing. Any suggestions. Any help would be greatly appreciated.

For better explanation:

Voice Recording Started

|----------------------------------------|

Beat/Instrumental Starts playing afterwards

[X]|-----------------------------------------------|

I am trying to find X so I can send that to startPlayback() method as beatDelayInSeconds and to the server. X is the time between when the voice starts recording until the beat actually starts playing. Currently in my startPlayback() method, plays both beat and vocals back, they are not in sync like it was recorded. Hope this makes sense.

Note: I had to put the delay handler in there for some Jelly Bean devices because the AudioRecord listener was not being called.

public void start()
{
    if (state == State.READY)
    {
        if (rUncompressed)
        {
            payloadSize = 0;
            RecordReadDelayInSeconds = 0;
            RecordDelayInSeconds = 0;

            mPlayer = new MediaPlayer();

            try {

                mPlayer.setDataSource(mp3Path);

                mPlayer.prepare();

            } catch (IllegalArgumentException e) {e.printStackTrace();} 
              catch (SecurityException e) {e.printStackTrace();} 
              catch (IllegalStateException e) {e.printStackTrace();} 
              catch (IOException e) { e.printStackTrace();}

            final long recordstarted = System.nanoTime() + 1500000000; //handler delay

            audioRecorder.startRecording();

            //Fix for recording issue with Samsung s3 Sprint phones.Doing a delayed first read
              new Handler().postDelayed(new Runnable() {                    
                   @Override
                    public void run() {

                       long recordstopped = System.nanoTime();

                        long recordDelay = recordstopped - recordstarted;

                        double RecordDelayInSeconds = recordDelay / 1000000.0;

                        Log.i("StartRecording() Delay in seconds",
                                String.valueOf(RecordDelayInSeconds));

                        long recordreadstarted = System.nanoTime();

                        int bytesReceived = audioRecorder.read(buffer, 0, buffer.length);
                        Log.d(TAG,"Delayed first read: bytes recieved "+ bytesReceived);

                        long recordreadstopped = System.nanoTime();

                        long recordreadDelay = recordreadstopped - recordreadstarted;

                        RecordReadDelayInSeconds = recordreadDelay / 1000000.0;

                        Log.i("Record read() Delay in seconds",
                                String.valueOf(RecordReadDelayInSeconds));

                        long mediastarted = System.nanoTime();

                        mPlayer.start();

                        long mediastopped = System.nanoTime();

                        long beatDelay = mediastopped - mediastarted;

                        beatDelayInSeconds = 0;

                        beatDelayInSeconds = (beatDelay) / 1000000000.0;

                        Log.i("Beat Delay in seconds",
                                String.valueOf(beatDelayInSeconds));

                    }
                }, 1500);
}

This is the playback code that the user listens to before sending info to server. It first starts the playback of the wav the user just recorded and then plays the instrumental according to the beatDelayInSeconds variable from the other method.

private void startPlayback() {

    wavPlayer.start();
    RefreshHandler mp3PlaybackHandler = new RefreshHandler();
            mp3PlaybackHandler.sleep((long) (beatDelayInSeconds * 1000));

}

class RefreshHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        mp3Player.start();
    }

    public void sleep(long delayMillis) {
        this.removeMessages(0);
        Log.i(TAG, "Beat delayed for " + delayMillis + " milliseconds");
        sendMessageDelayed(obtainMessage(0), delayMillis);
    }
};
Was it helpful?

Solution

I don't understand "the timing is always off"? what does it mean?

I am not so clear about your question and your description, but Is it true: You want to postpone the execution of a code block after some period of time?

If it's the case, you must consider whether your new Handler().postDelayed(..) is called inside another UI thread or not. Try this if it's an activity's process:

your_activity_instance.runOnUiThread(new Runnable() 
{
            @Override
            public void run() 
            {
               //delay with thread
               try{
                   Thread.sleep(1500);
               }catch(.....)
               {..}

               //your work load here
            }
});

NOTE:

  • Handler.postDelayed() does NOT create a separated thread for your code block, but only append a new message (related to your Runnable instance) with timer sticker into current thread's message queue.

  • activity.runOnUiThread() do the similar thing but the Runnable.run() has to wait until the system comes back to activity UI thread then be excuted. This's helpful when you want to do some activity's job in its child thread.

Update for modified question:

I may understand your intention: trying to execute wavPlayer.start() and mp3Player.start() at the same time; but your current solution is not nice or even it is impossible:

  • In the same thread which you are using handlers, you will hardly sync the 2 functions (as I mention, handler.run() doesn't spawn separated thread).

  • you can not pin down the exact time stamp of hanler.run() because the system controls when the message is appended to the queue, the post delayed is a relative value.

My suggested solution:

  • You spawn a separated thread for each player.

  • Before each thread start(), you mark the time stamp (may use millisecs, not such of accuracy to use nano)

  • In the second thread which is called later, you also pass to it the DELTA of time stamps (diff between markers). Then use Thread.Sleep() or Handler.postDelayed() for your adjustment of sync.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top