1

I need to synchronize audio file using MediaPlayer and streaming audio from Youtube, using YoutubePlayer.

I've encountered an error with starting MediaPlayer. It starts after some delay, and audio tracks are not synchronized. Using SoundPool is not good, because audio files are heavy.

Is it possible to manage starting of MediaPlayer, or pre buffer the audio(decode audio to PCM and play?)?

Thanks in advance.

Taras
  • 2,526
  • 3
  • 33
  • 63

1 Answers1

1

I am not familiar with YoutubePlayer, but as far as reduced latency when starting the audio file, you might be better off using AudioTrack (or if you are comfortable with openSL -- check this video out).

A simple fix I have used for syncing audio with decent results is to set up an AudioTrack and read PCM data from the audio file into a byte array. Then create a thread with AudioTrack play and write commands inside. When you are ready to begin playing the audio file, just call start on the thread. Obviously the sampling rate, channels, and format are dependent on the audio file you are streaming.

int minBufferSize = AudioTrack.getMinBufferSize(441000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT);
at = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);
filepath = Environment.getExternalStorageDirectory().getPath() + File.separator + "audiofile.wav";
fp = new File(Environment.getExternalStorageDirectory().getPath() + File.separator + "audiofile.wav");

        count = 0;
        data = new byte[(int) fp.length()];
        try {
            fis = new FileInputStream(filepath);
            dis = new DataInputStream(fis); 
            while((count = dis.read(data, 0, (int) fp.length())) > -1){}
            data = null;
            dis.close();
            fis.close();
        } catch (FileNotFoundException e) { 
            e.printStackTrace();
        } catch (IOException e) { 
            e.printStackTrace();
        }

        atThread = new Thread(new Runnable() {
            public void run() { 
                    at.play();
                    at.write(data, 0, (int) fp.length());
                    data = null; at.stop(); at.flush(); at.release();

            }
            });

Hope this helps!

EDIT:

BIG thanks to @WilliamMorrison for the help! After modifying @inazaruk's answer here, I was able to get separate wave files to play without writing their entire files to memory. Phew!

AudioTrack song_1, song_2;
boolean stop = false;
Button start_btn, stop_btn;
DataInputStream dis_1, dis_2;
File file_1 = new File(Environment.getExternalStorageDirectory().toString() + "/song_1.wav");
File file_2 = new File(Environment.getExternalStorageDirectory().toString() + "/song_2.wav");
FileInputStream fis_1, fis_2;
int minBufferSize;
Thread thread_1, thread_2;

@Override
protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    start_btn = (Button)findViewById(R.id.start);
    start_btn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            start();  
        }
    });

    stop_btn = (Button)findViewById(R.id.stop);
    stop_btn.setOnClickListener(new OnClickListener() {
        @Override
        public void onClick(View v) {
            stop();  
        }
    });      
}  

Runnable read_song1 = new Runnable() {       
    public void run() {

        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        byte [] audio_data = new byte[minBufferSize];
        try {
            while(!stop) {    
                dis_1.read(audio_data, 0, minBufferSize);
                song_1.write(audio_data, 0, audio_data.length);
            }
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    } 
};

Runnable read_song2 = new Runnable()
{       
    public void run() {
        Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
        byte [] audio_data = new byte[minBufferSize];
        try {
            while(!stop) {    
                dis_2.read(audio_data, 0, minBufferSize);
                song_2.write(audio_data, 0, audio_data.length);
            }
        } 
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } 
        catch (IOException e) {
            e.printStackTrace();
        }
    } 
};

void start() {

    try {
        fis_1 = new FileInputStream(file_1); fis_2 = new FileInputStream(file_2);
        dis_1 = new DataInputStream(fis_1); dis_2 = new DataInputStream(fis_2); 

        stop = false;

        minBufferSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT);

        song_1 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);            
        song_2 = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, AudioTrack.MODE_STREAM);

        song_1.play(); song_2.play();        
        thread_1 = new Thread(read_song1); thread_2 = new Thread(read_song2);
        thread_1.start(); thread_2.start();
    } 
    catch (FileNotFoundException e) {
        e.printStackTrace();
    }
}

void stop() {

    stop = true;          
    song_1.stop(); song_2.stop();
    song_1.flush(); song_2.flush();
    song_1.release(); song_2.release();

    try {
        dis_1.close(); dis_2.close();
        fis_1.close(); fis_2.close();
    } 
    catch (IOException e) {
        e.printStackTrace();
    }
}   
Community
  • 1
  • 1
jch000
  • 421
  • 3
  • 11
  • 1
    Good example. Readers be aware this code reads the entirety of a PCM audio file into memory. This means it will work well for short files, but maybe not longer files. – William Morrison Apr 21 '14 at 18:33
  • Agh, great point! That actually helps me a lot -- thanks William! – jch000 Apr 23 '14 at 00:19
  • @WilliamMorrison do you have any advice on how to write only a portion of the PCM audio file into memory at a time? I am probably missing something obvious, but can't seem to figure out the proper way to do it. Any direction would be much appreciated! =] – jch000 May 21 '14 at 18:29
  • 1
    You need to stream bytes from the file to your audio track. So, rather than reading the entire file into a byte array, you read a small chunk of the file from the stream, write it to the audiotrack, and repeat till the stream is empty. – William Morrison May 21 '14 at 19:14
  • 1
    Think I finally got it (see edit) -- thanks @WilliamMorrison! – jch000 May 22 '14 at 18:34