MP3の一部分再生がうまくいかない

1,185 views
Skip to first unread message

Shintaro Iwasaki

unread,
Dec 10, 2013, 6:36:03 AM12/10/13
to android-g...@googlegroups.com

初めまして。

現在、MediaPlayerで、大きなmp3のファイル(3分ほどの長さ)の一部だけ(10秒ぐらい)を再生する
プログラムを書いております。
しかし、きちんと「再生したい部分ぴったり」に再生することができず、
実際の音の再生長が、期待される箇所ではありません。

音の開始時に、再生したい箇所より先のところから再生が始まってしまったり、
あるいは音の終了時に、再生したい箇所まで再生されずに、途中で再生が終わってしまったりします。

以下が、startミリ秒からendミリ秒までmp3を再生する現在のコードです。アイデアとしては、
1.MediaPlayerでファイルを読み込む
2.seekToで再生位置をstartに変える
3.シークが完了したら、再生を始める
4.(end - start)ミリ秒待って再生を止めるHandlerを発行する
というものです。

ひとまず、手元の環境(実機202K Android 4.2.2)で試しております。
また、音声のstartやendは波形ソフトを使って得た時間です。

もし、何か知っている方がいらっしゃれば、ご教授願います。
また、別の方法を知っている方がいらっしゃれば、教えてくださるとうれしいです。

よろしくお願いします。

import java.io.IOException;

import android.media.MediaPlayer;
import android.media.MediaPlayer.OnSeekCompleteListener;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.util.Log;

public class MainActivity extends Activity {
    //音声再生クラス
    public class MusicPlayer {
        private MediaPlayer mp;
   
        public MusicPlayer(String path) throws IllegalArgumentException, SecurityException, IllegalStateException, IOException{
            mp.setDataSource(path);
            mp.prepare();
        }
   
        public synchronized void start(final int start, final int end) {
            Log.d("", "Start Sound : " + start + " > " + end);
           
            mp.seekTo(start);
           
            mp.setOnSeekCompleteListener(new OnSeekCompleteListener(){
                @Override
                public void onSeekComplete(MediaPlayer arg0) {
                    mp.start();
                    Handler h= new Handler(){
                        @Override
                        public void dispatchMessage(Message msg) {
                            if (msg.what == 1) {
                                if (mp.isPlaying()) {
                                    mp.pause();
                                }
                            } else {
                                super.dispatchMessage(msg);
                            }
                        }
                    };
                    h.sendEmptyMessageDelayed(1, end - start);
                }
            });
        }
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MusicPlayer mp;
        try {
            //mp3ファイルを読み込む。
            mp=new MusicPlayer("a.mp3");
            //a.mp3の1200ミリ秒から3250ミリ秒までを再生しようとしている。
            mp.start(1200, 3250);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}





Hiroaki GOTO as GORRY

unread,
Dec 10, 2013, 7:30:56 AM12/10/13
to android-g...@googlegroups.com

後藤 浩昭(GORRY)です。

今時の音声再生環境の場合、時間方向には2つの誤差を考えておく必要があります。

1. 再生遅延。再生APIを発行後、すぐ発声が行われるとは限りません。
音声データはある程度バッファへ蓄積されてから発声されるので、
バッファリングにかかる時間分だけ発声が遅れたり、停止時に
バッファリングされている分が発声されず尻切れになったりします。

Androidの場合、JellyBeanより前の世代の端末は特にこのバッファ長が
長めにとられている機種が多く、ゲームなどではしばしばこの遅延が
問題となっています。

2. フレーム長。MP3などの圧縮音声は、元データをある程度のサンプル数で
分割して圧縮を行っています。一般的なMP3は1フレーム1152サンプルです。
つまり、44.1KHzの場合は44100÷1152≒26.1ミリ秒ごとにしか分割できず、
余りの分が誤差となります。


In message <c621c0a1-92c7-4c21...@googlegroups.com>
"[android-group-japan: 25576] MP3の一部分再生がうまくいかない"
> --
> このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
> このグループから退会し、メールの受信を停止するには、android-group-j...@googlegroups.com にメールを送信します。
> このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
> http://groups.google.com/group/android-group-japan からこのグループにアクセスしてください。
> その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。



--
Hiroaki GOTO as "GORRY" : 後藤 浩昭
EMAIL: gorr...@gmail.com

Shintaro Iwasaki

unread,
Dec 10, 2013, 8:22:12 PM12/10/13
to android-g...@googlegroups.com

後藤さん、ご回答ありがとうございます。


> Androidの場合、JellyBeanより前の世代の端末は特にこのバッファ長が
> 長めにとられている機種が多く、ゲームなどではしばしばこの遅延が
> 問題となっています。

とのことですが、皆さんはこの問題をどのように解決されているのでしょうか。

ohisamallc

unread,
Dec 10, 2013, 9:05:07 PM12/10/13
to android-g...@googlegroups.com
山形のohisamaです。
山形は、まだ寝雪ではありません。
がんばろう、東北。

oggにして、soundpoolで再生する。
どうしてもmp3なら、ndkでffmpeg使って
生pcmを取得してmediaplayerに流し込む。

Shintaro Iwasaki

unread,
Dec 10, 2013, 11:41:22 PM12/10/13
to android-g...@googlegroups.com

ohisamaさん、具体的な方法について、ご回答ありがとうございます。

> どうしてもmp3なら、ndkでffmpeg使って
> 生pcmを取得してmediaplayerに流し込む。

現状mp3を使わざるを得ないため、ffmpegを使う方向で検討します。

ひとまず、ffmpegの使用について調べてみます。
また分からないことがあったときには、相談させてください。

皆さん、ありがとうございました。

Hiroaki GOTO as GORRY

unread,
Dec 11, 2013, 12:39:19 AM12/11/13
to android-g...@googlegroups.com

後藤 浩昭(GORRY)です。

ohisama氏が提示している解決法は、「音声データをストレージから
読み込む際のタイムラグ」と、「圧縮音声のデコード開始時のタイム
ラグ」を改善するにとどまります。

当方が問題にしている発声遅延は、(遅延が最も短いといわれる)NDKで
OpenSL ESを使ったとしても解決しません。
当方が調査した限りでは、これらの方法をとったとしても、多くの機種で
1/30秒程度、(中華Padなどで)下手すると1/4秒程度の遅延が発生して
きていました。

これを改善するには、OSとサウンドデバイスドライバのチューニングしか
方法がなく、事実上は解決不可能です。
# iOSに比べてAndroidに楽器系のアプリが少ない理由はこれです。
# iOSでは最悪でも1/60秒程度、設定次第ではもっと短くできます。

遅延の少ない機種もありますので、リリース対象をこれらに限定すると
いうのが解決方法のひとつになります。
OS上で明確に遅延が考慮されたのはJellyBeanからですが、ICSの頃には
酷いチューニングの機種はかなり減りましたし、旧い機種でも
PS Certified deviceを取得しているXperiaなどは、遅延が比較的少なく
なるよう設計されています。


In message <NJEOKONKLGEPCBLFHJ...@mui.biglobe.ne.jp>
"RE: Re: [android-group-japan: 25579] MP3の一部分再生がうまくいかない"

Shintaro Iwasaki

unread,
Dec 11, 2013, 5:12:41 AM12/11/13
to android-g...@googlegroups.com

後藤さん、回答ありがとうございます

機種依存の問題については、全く知りませんでした。
貴重なご回答ありがとうございます。


> 当方が問題にしている発声遅延は、(遅延が最も短いといわれる)
> NDKで OpenSL ESを使ったとしても解決しません。

ということで、ひとまずは再生遅延の最も小さいというNDKでの
OpenSL ESについて調べてみたいと思います。

> 遅延の少ない機種もありますので、リリース対象をこれらに限定すると
> いうのが解決方法のひとつになります。

確かに、一つの解決方法になりそうですが、出来る限り多機種に対応させたいので、
最後の方法の一つとして検討してみます。

ohisamallc

unread,
Dec 11, 2013, 7:38:44 PM12/11/13
to android-g...@googlegroups.com
山形のohisamaです。
向学の為、教えて下さい。
pcmの再生に、AudioTrackの使用では、どの程度
改善されますか。
> Hiroaki GOTO as "GORRY" : 後藤 浩昭
> EMAIL: gorr...@gmail.com
>

Hiroaki GOTO as GORRY

unread,
Dec 13, 2013, 3:05:38 AM12/13/13
to android-g...@googlegroups.com

後藤 浩昭(GORRY)です。

本質的には、AudioTrack/SoundPool/OpenSL ESのどれも
大差ありません。遅延の大元は、これらの下位にいる
オーディオデバイスドライバの性能や設定に由来する
ものだからです。

SoundPoolはストレージ型(メモリにサウンドデータを直接
配置してこのエリアをデバイスドライバが扱う)、AudioTrackは
主にストリーミング型(サウンドデータをデバイスドライバへ
動的に送信して扱う)を想定したJavaフレームワークです。
OpenSL ESはこの両方を想定したネイティブ(主にC/C++向け)
フレームワークです。

--

なお、ストリーミングの場合はその性格上、デバイスドライバとは
別にバッファリングが必要になり、これが別に遅延の元となります。

AudioTrackのストリーミングは比較的大きめのバッファを持って
音声の途切れを起こさない方針で作られているためか、遅延は長めに
なります。このバッファサイズはAudioTrack.getMinBufferSize()で
得ることができますが、数十ms~数百msと機種によりかなり
差があります。

OpenSL ESのストリーミングモードは自前でバッファを作成して
遅延量を制御しなければなりません。手許で試した限りでは10ms程度でも
特に問題はありませんでした。この程度になると、ストリーミングの
バッファによる遅延はさほど問題ではなく、デバイスドライバ
レベルの遅延が大きな問題となってきます。

--

ちなみに、これらはAndroidだけでなく、PCでサウンドを扱うときに
おいての一般的な問題です。Windowsなどでも、サウンドドライバに
よっては遅延量(レイテンシ)を設定できたりします。


In message <NJEOKONKLGEPCBLFHJ...@mui.biglobe.ne.jp>
"RE: Re: [android-group-japan: 25591] MP3の一部分再生がうまくいかない"
> このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
> http://groups.google.com/group/android-group-japan からこのグループにアクセスしてください。
> その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。


Reply all
Reply to author
Forward
0 new messages