마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

AudioTrack에서 SeekTo 기능 사용하기

0 추천
안녕하세요. 현재 AudioTrack을 사용하여 Player 기능을 구현하고 있습니다.

MediaPlayer 관련한 Example들은 많은데 AudioTrack을 사용하여 Player 구현한 Example들은 많이 없어서 고생중입니다..

현재 AudioTrack을 AsyncTask를 사용하여 Wav파일을 재생하고 있는데요..

SeekBar를 이용하여 현재 Play되는 시점(현재플레이시간, 총플레이시간)까지 표현해 주고 있습니다.

이제 여기서...

SeekBar를 이동하여 원하는 시점부터 Play되게 하고 싶은데요..

찾아보니 setPlaybackHeadPosition() 메소드를 이용하는 것 같은데..암만해도 되질 않네요....

답변 부탁드립니다..ㅠㅠㅠ

 

또 혹시 AudioTrack으로 MP3 Player 만드신 경험이 있으신분도 답변 달아주시면 감사하겠습니닿!!

부..부탁해요~
뿅쵹 (120 포인트) 님이 2013년 10월 1일 질문

1개의 답변

0 추천
AudioTrack에서 seek을 하실 때에는 flush를 호출하여 기존 wrtie한 pcm 데이터를 삭제하고, pcm 파일이 seek 된 위치의 데이터를 넣어주시면 됩니다.

pcm의 경우  bitrate가 정해져있으니  bitrate * 시간으로 나온 숫자만 큼 seek한 위치의 pcm파일을 읽어 AudioTrack에 넣으면 됩니다. .

wav의 경우 헤더 + PCM으로 구성되어 있으니  헤더 사이즈만큼 seek을  보정하셔야 할 수 있습니다.

mp3 플레이어도 AudioTrack을 사용하기 위해서는 wav로 변환하는 과정을 수행 해 주어야 합니다. mp3 단독만 하실거면 libmad 같은 라이브러리도 좋으며, ffmpeg같은 것을 사용하셔도 됩니다.

jni로 mp3를 pcm로 변환하여 마찬가지 방법으로 AudioTrack write에 변환한 데이터를 적어주시면 플레이가 될 겁니다.
사악미소 (65,330 포인트) 님이 2013년 10월 1일 답변
Seek된 데이터는 Seek한 시간 * SampleRate(44100) 으로 넣어주고 있는데요..
데이터를 넣어준다는게 setPlaybackHeadPosition(Seek) 를 말씀하시는 건가요?

예를들어
mAudioTrack.flush();
                    mAudioTrack.setPlaybackHeadPosition(seekBar.getProgress() * sampleFreq);

답변감사합니다:)
setPlaybackHeadPosition 를 사용하실 필요가 없다는 겁니다.
AudioTrack은 들어오는 PCM 데이터를 재생하는 역활만 합니다. seek하던 말던 write로 입력해준 데이터만 재생하는거죠..
따라서 write 할 때 넣는 PCM 데이터를  seek 했을 때의 데이터로만  넣어주시면 됩니다. flush를 호출하지 않으면, 기존에 write한 내용을 다 플레이 한 이후 seek한 위치의 데이터가 재생되는거구요.
wrtie 할 때 seek한 데이터를
mAudioTrack.write(byteData, "이부분", minSize); 에 넣으면 되는건가요..?

답변감사합니다..:)
네 거기에 PCM 데이터를 넣으시면 됩니다.
임의이 PCM 데이터를 write할때 넣으면 재생이 안되고 바로 플레이를 종료해버리네요..

--------------------- 아래와 같이 Play 하고 있는데요..
playbackPosition = 0;
@Override
protected Void doInBackground(Void... params) {
    while (minSize < inputStream.available() - 44) {
        if (isCancelled()) {
            break;
        }

        if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            inputStream.read(byteData, 0, minSize);
            mAudioTrack.write(byteData, playbackPosition, minSize);

            // Progress Bar Update
            if (!seekPosition.isPressed() && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
                onProgressUpdate(mAudioTrack.getPlaybackHeadPosition() / sampleFreq);
            }
        }
    }
}
------------------------ 아래와 같이 SeekBar Stop시 playbackPosition 값을 넘겨줍니다..
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
    if (mAudioTrack != null) {
        mAudioTrack.flush();
        playbackPosition = seekBar.getProgress() * (byteRate * bitPerSample);
    }
}


이때 바로 플레이를 종료시키고 onPostExcute()를 호출하네요..
답변갑사합니다!:)
write api를 잘 못 쓰신듯 한데요...=ㅇ=  
들어갈 파라메터가 . 버퍼 , 오프셋, 사이즈 형식인데
playbackPosition 를 주셨으니. byteData 로 잡은 사이즈보다 더 큰 오프셋 값이 들어가게되면  IndexOutOfBoundsException이 날 수 밖에 없죠..
int readLen =  inputStream.read(byteData, 0, minSize); 로 읽었으면,
mAudioTrack.write(byteData, 0, readLen); 같은 씩으로 주셔야 하고
 inputStream.read 전 inputStream.skip 같은 것으로 seek처리를 해주셔야  되어야 할 듯 합니다. 그리고 죽으면 가급적 로그를 첨부해주시는게 좋을 듯 합니다.
또한 inputStream 을 사용할 경우 skip 같은 것으로 뒤로는  seek을 구현하는게 쉽지만  앞으로는 mark 같은 것을 이용해야하는데 복잡하니  RandomAccessFile 같은 것을 이용하시는 것을 권장합니다.

마지막으로 inputStream 에서 데이터를 못 읽었을 때의 처리도 해주셔야 할 듯 하네요.
...