none
正確な時間の計測をするには RRS feed

  • 質問

  • オーディオプレイヤーを作成しています。

    再生中の経過時間「00:01:42 / 00:04:32」の表示をするために,Stopwatchクラスを使用しており,

    Me.SW.ElapsedTicks / Stopwatch.Frequency

    のように経過時間を取得しています。

    しかし,再生開始から40分ほど経過すると再生している音に対して経過時間の表示が2秒近く遅れてしまいます。

    また,再生中はリアルタイムにアップサンプリングしているため,CPU負荷の変動がかなりあります。30%~99%

    そもそも,Stopwatchクラスはこういう用途に向いているのかどうかも良く理解しておりません。

     

    オーディオプレイヤーを作成する際に経過時間を表示するためには,どういった手法があるのかどなたかお教えいただけますと幸いです。

     

     

    2010年12月14日 15:06

すべての返信

  • Stopwatch クラス (System.Diagnostics)

    -- 引用ここから

    Stopwatch は、基になるタイマ機構のタイマ刻みをカウントして、経過時間を計測します。インストールされているハードウェアおよびオペレーティング システムが高解像力のパフォーマンス カウンタをサポートしている場合、Stopwatch クラスはそのカウンタを使用して経過時間を計測します。それ以外の場合、Stopwatch クラスはシステム タイマを使用して経過時間を計測します。Stopwatch タイミング実装の精度および解像力を判断するには、Frequency フィールドおよび IsHighResolution フィールドを使用します。 

    -- 引用ここまで

    とMSDNに記述がありますから、ますはこれら2つのプロパティを調べてみてはどうでしょうか。

    Stopwatch.Frequency フィールド (System.Diagnostics)

    Stopwatch.IsHighResolution フィールド (System.Diagnostics)

     


    Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/
    2010年12月14日 15:24
  • コメントいただき,ありがとうございます。

    Stopwatch.Frequency は 14318180
    Stopwatch.IsHighResolution は True

    でしたので,「高解像力のパフォーマンス カウンタをサポートしている」のではないかと思います。

    そのうえで経過時間がずれるので,なおさら悩みそうです。

     

    2010年12月14日 15:43
  • Stopwatchを使用したシンプルなアプリケーションを作成し、現象が再現するか確認してみてはいかがでしょうか。その結果で原因の切り分けができると思います。
    Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/
    2010年12月14日 20:20
  • Stopwatch クラスは経過時間を知ることができますが、オーディオの再生処理の遅延とは無関係です。
    そのオーディオの現在の再生位置を知るために、Stopwatch クラスを使うことは不適切でしょう。
    (将来的に「シーク」(再生位置を変える)機能を実現するつもりなら、なおさら使えないはずです)

    ちなみに、そのオーディオの再生という処理には何を使っているのですか?
    コントロールなどを使っているのであれば、そのコントロールは再生位置を提供するプロパティなどありませんか?
    (あるのであれば、タイマーで定期的にそのプロパティを見れば良い)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年12月14日 22:07
    モデレータ
  • J.Hashimoto様

    >Stopwatchを使用したシンプルなアプリケーションを作成し、現象が再現するか確認してみてはいかがでしょうか。その結果で原因の切り分けができると思います。

    確かにそうなのですが,Stopwatchと何を比較するべきでしょうか? ちょっと思いつきません。

    現在は再生している音がものさしになっていますので,遅れていると判断できるのですが・・・

     

    Azulean 様

    mikeo_410さんの「WPF用音声入出力ライブラリ(WaveIO7)」というものを使用しています。

    カーネルストリーミング再生を目的に使用していますが,現在の再生位置を示す情報は持っていないようです。

    世の中にはオーディオプレーヤーがたくさんありますが,どのソフトも経過時間の表示はしっかりしていますので,何らかの定石があると思うのですが・・・

     

    >Stopwatch クラスは経過時間を知ることができますが、オーディオの再生処理の遅延とは無関係です。
    >そのオーディオの現在の再生位置を知るために、Stopwatch クラスを使うことは不適切でしょう。

    上記の文面からは読み取れなかったのですが,Stopwatchクラスを使うのはなぜ不適切なのでしょう?

    やはりStopwatch クラスでは正確な経過時間を計測する事はできないのでしょうか?

     

    >(将来的に「シーク」(再生位置を変える)機能を実現するつもりなら、なおさら使えないはずです)

    ちなみにシークは実装済みです。Stopwatchをリセットして新しいデータ開始位置を足せば簡単に求める事ができます。

     

    2010年12月15日 16:01
  • 外池と申します。何も資料を確かめたわけではないのですが・・・、StopWatchクラスは、秒程度の時間を測るのには便利かもしれませんが、何十分もの時間を測るのにはあまり適さないように思います。時刻刻みにムラが少ないということで高精度かもしれませんが、絶対的な精度はどうなっているのか・・・、私は資料を見たことがありません。

    今、DateTimeで採れる時刻情報に基づく計時と、StopWatchクラスの計時を分単位で比較していますが、確かに、StopWatchクラスの方が遅れ気味ですね。

    ところで、デジタルで音楽や画像を再生する機器は、送出するデータの量そのものが経時の情報を担っているのではありませんか? サンプリングレートが既知であれば、送り出したデータの量から演奏時間がわかるわけですよね?


    (ホームページを再開しました)
    2010年12月15日 16:42
  • 世の中にはオーディオプレーヤーがたくさんありますが,どのソフトも経過時間の表示はしっかりしていますので,何らかの定石があると思うのですが・・・

    Media Player など、高レベルなインターフェースでは現在位置を示すプロパティを提供していますので、そういったものを利用しているのだと思います。
    今回の低レベル(音階の指定など、自分で組み合わせて出力する?)なインターフェースでは自分で工夫することになるとみられます。

    上記の文面からは読み取れなかったのですが,Stopwatchクラスを使うのはなぜ不適切なのでしょう?
    やはりStopwatch クラスでは正確な経過時間を計測する事はできないのでしょうか?

    私が述べたかったのは、処理の過負荷などで音の出力が遅延した場合、時間の経過と音の再生状況の経過に誤差が出るのではないかと言うことです。
    精度についてはコメントしかねます。(裏とってないので)

     


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年12月15日 22:26
    モデレータ
  • J.Hashimoto様

    >Stopwatchを使用したシンプルなアプリケーションを作成し、現象が再現するか確認してみてはいかがでしょうか。その結果で原因の切り分けができると思います。

    確かにそうなのですが,Stopwatchと何を比較するべきでしょうか? ちょっと思いつきません。

    現在は再生している音がものさしになっていますので,遅れていると判断できるのですが・・・

    混乱させてしまったようなので、発言の意図を書いておきます。

    Stopwatchを使用するだけの、オーディオと無関係なアプリケーションで検証すれば、遅延の原因がStopwatchにあるのかそれ以外にあるのかがはっきりすると思いました。

    他の方からも回答がついていますので、私の発言についてはスルーしてもらってかまいません。

     


    Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/
    2010年12月16日 18:03
  • みなさんからいろいろなコメントをいただき,ありがとうございます。

    Stopwatchを使用せずに

    Dim Start As Date = Now
    Dim CurrentMilliSec As Double

    '現在の経過時刻

    CurrentMilliSec = (Now - Start).TotalMilliseconds

    としてみましたがやはり実際に聴こえる音よりも,CurrentMilliSecが遅れてしまいます。

     

    私が使用しているライブラリは経過時間を表す情報を持っておりませんので,音声とリンクさせる事は難しいと思っております。

    妥協案でもたいへんありがたいので,何かアドバイスをいただけますと幸いです。

     

    2010年12月17日 4:05
  • >音声とリンクさせる事は難しいと思っております。

    >妥協案でもたいへんありがたいので,何かアドバイスをいただけますと幸いです。

     

    音声ライブラリとStopwatchクラスの同期が出来ない以上、どのように誤差が生じていくかを

    判断する基準が何もないため、妥協案はないのではないかと思います。

     

    いっそのことライブラリを使用せずに、ご自身でライブラリに代わる処理を作成してはどうでしょうか。

    「winmm.dll」というMicrosoftのDLLがあり、APIで呼び出し可能です。

    使用できるようになる関数で「mciSendString」というものがあり、これでたいていの情報の取得や

    サウンドに対する指示を行えます。

    [mciSendString]

    http://msdn.microsoft.com/ja-jp/library/cc410496.aspx

    [使用できるコマンド]

    http://msdn.microsoft.com/en-us/library/ms712587(VS.85).aspx

    再生時刻を取得したり、再生位置をシークしたりといった事が可能です。

     

    資料的にはVB6.0での内容になりますが、上記関数を利用する上では参考になると思います。

    以下のサイトも参考にしてください。

    [VisualBasic中学校]

    http://homepage1.nifty.com/rucio/main/technique/teq_1.htm

     

    .NETでのAPI呼び出しの参考としては、

    [DOBON.NET]

    http://dobon.net/vb/dotnet/programing/playmidifile.html#mci

    2010年12月17日 5:19
  • honefai 様

    コメントいただき,ありがとうございます。

    >「winmm.dll」というMicrosoftのDLLがあり、APIで呼び出し可能です。

    具体的に,どういうAPIなのか理解はしておりませんが,これを使用した音声データはカーネルミキサーを通過するのではないでしょうか?

    音質を重要視しておりますので,カーネルミキサーをバイパスして再生する事が私の中では必ず必要なものと位置付けております。申し訳ございません。

    本来であれば自前でWASAPIやASIO,カーネルストリーミングを使用する事ができれば良いのですが,情報が少ないことや私の技術レベルの低さもあいまって,それが困難な状況です。

     

    2010年12月17日 12:10
  • CurrentMilliSec = (Now - Start).TotalMilliseconds
    としてみましたがやはり実際に聴こえる音よりも,CurrentMilliSecが遅れてしまいます。

    これは再生開始(Start)から今(Now)までの経過時間を得られるはずですよね。
    それでも「経過時間(差分)が遅れる」というのであれば、「再生が速すぎる」のでは?
    # Start をとるタイミングが遅すぎる可能性もありますが、正直、第三者からは何とも言いかねます。

    再生時間を取得できるインターフェースがないのであれば、再生の制御で何か工夫して、どこまで再生できたかを把握するしかありません。

    どういったインターフェースで再生されているのかわかりませんが、1 音ずつ鳴らすような仕組みなのであれば、自分で再生速度・再生位置(どこまで再生したか)を管理して、それらを元に計算すればよいでしょう。(この場合、工夫していないと実時間の経過と再生速度は一致しません)
    逆にあるファイルを再生しろと命じるだけで、現在位置をとれないのであれば、時間のずれを補正する術はないと思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2010年12月17日 14:13
    モデレータ
  • 外池です。

    時間計測とは別に、もともと、自身で任意に生成した音データを再生することに興味がありました。で、自分なりに基本的なプログラムを組んで試してみたところ、50分の音データを再生すると、DateTime型で再生開始時と終了時の時刻を採って差を計算する方法で算出した経過時間は1/10秒以下の精度で一致しました。

    プログラムは、WindowsのwaveOut~のAPI群を使っており、2つの0.5秒分のバッファを交互に用いてwaveOutWriteを繰り返しつつ、コールバック関数の呼び出しを6000回数え上げたところで停止させるものです。サンプリング周波数は44100Hzでした。

    参考になれば幸いです。

    ----------追記----------

    WindowsのAPIを使うか、VB User1さんが仰るようにオーディオドライバにもっと直接的にアクセスするかは別にして、オーディオデバイスのD/Aコンバーターが定められた周期(サンプリング周波数)でデジタルデータを読み込んでアナログ信号を送出することは変わりません。

    サンプリング周波数が44100Hzの設定であれば、モノラルであれば44100個のデータがちょうど1秒になるわけです。ステレオなら半分の0.5秒。この関係はかなり正確だと信じて良いと思います。

    で・・・、邪推ですが、実は私も知らなかったのですが、44100Hzのサンプリング周波数を丸めて44000Hzとされていませんでしょうか? この前提で元のデジタルデータの演奏時間を算出してしまうと、40分ほどの音の再生で数秒進行が早すぎるように見えてしまうことになりますが。


    (ホームページを再開しました)
    2010年12月19日 22:40
  • 外池 様

    コメントいただき,ありがとうございます。

    私も何度か自分の時間の算出方法が誤っていないかと思い,ソースを見直しましたが問題はないように思います。

    WAV(PCM)データより以下の方法で算出しています。

    収録時間(秒)=「データサイズ」÷「ブロックサイズ」÷「サンプリング周波数」

    収録時間(秒)はDouble型で格納しています。

     

    約41分経過時点で約2秒ほど計測した時間が遅かったので,

    経過時間+(経過時間×0.000813008130081301)

    としてみたところ,音声と経過時間の表示がほぼ一致しました。

    私のPCの時間のカウントが遅いのか,オーディオデバイスの時間のカウントが速いのかはわかりません。

    ただし,この方法ではPCやオーディオデバイスの環境が変わると通用しないと思いますので,やはりダメでしょうね。

     

    2010年12月20日 4:03
  • 外池です。

    定量的な精度の問題で、しかも、時間という物理量とも関係しますから、基本的なところから、ひとつひとつ確実に潰していくしかないと思います。

    まずは、お手元のパソコンに関して内蔵時計の正確さ(.Net FrameworkのDateTime型で得られる時刻情報の正確さ)は、すぐに判明しますよね? 原始的な方法なら電話の時報で調べてみてもいいし、IT的な方法ならTimeサーバーと比較しても良いし。(ただ、常識的に考えて、1時間で1秒以上もずれることは考えにくいと思いますが・・・)

    オーディオデバイスのD/Aコンバーターが微妙に異なるサンプリング周波数で動いているかもしれない、という疑念は、例えば、基準周波数の音をだしてみて他のマシンの音(楽器用のチューナーの音)と比べてみてはいかがでしょう? 今、問題にしているズレは0.1%程度の話なので、1000Hz程度の音で比較すれば1Hz程度のウナリとして聞こえるでしょうからら、聴力に頼っても判ると思います。

    ところで、具体的に再生されているWAV(PCM)データのトータルのサイズと、ブロックサイズと、サンプリング周波数は具体的にいくつなんでしょう? VB User1さんが、41分の音のデータと考えておられるのは、何バイトですか?


    (ホームページを再開しました)
    2010年12月20日 4:28
  • >ところで、具体的に再生されているWAV(PCM)データのトータルのサイズと、ブロックサイズと、サンプリング周波数は具体的にいくつなんでしょう? VB User1さんが、41分の音のデータと考えておられるのは、何バイトですか?

    約41分経過時点で約2秒程度遅れているという情報しか記憶(記録)していませんでしたので,具体的に何バイトかというのは,すぐにお示しできません。申し訳ございません。

    ただし,普通にこの自作アプリで音楽鑑賞をしていて,「遅れているな」と実感できるのが20~30分ぐらい経過してからです。まだ20曲目の経過時間を表示しているのに,音は21曲目の音が鳴り始めてしまった。という感じです。時間が経過していくにつれて顕著になっていきます。

    よく再生しているのは,32bit×2ch(ブロックサイズ8),サンプリング周波数176400Hzです。

     

    >オーディオデバイスのD/Aコンバーターが微妙に異なるサンプリング周波数で動いているかもしれない、という疑念は、例えば、基準周波数の音をだしてみて 他のマシンの音(楽器用のチューナーの音)と比べてみてはいかがでしょう? 今、問題にしているズレは0.1%程度の話なので、1000Hz程度の音で比 較すれば1Hz程度のウナリとして聞こえるでしょうからら、聴力に頼っても判ると思います。

     

    上記の件ですが,具体的にどうすれば実験した事になるのかよくわかりません。現在の環境は

    PC→USB DDC(同軸デジタル)→単体DAC→プリメインアンプ

    という環境です。どの部分を差し替えれば,比較実験として適切でしょうか?

    2010年12月20日 9:01
  • スゴイ!! スペックですね。ビックリです。

    外池です。いくつか新しい情報を頂いたようですので、思いつくことをいくつか・・・。

    複数の曲を再生されているようですが、それらの曲は1つのPCMデータにまとまっていますか? 複数のPCMデータですか? 曲間の時間はどのように管理されていますでしょうか? 曲間の時間もすべてPCMデータで埋まっているようなものであれば、PCMデータの量だけで経過時間が測れると思いますが、そうでなければ、曲間の時間の扱い方如何で秒単位のズレは在り得そうな気がします。

    176.4kHzという数字で調べてみて感じたことは、そうとう「高級」ですよね。そうすると、そのD/A機材が変な動作をしているとはとても思えません。しいて言えば、ピッチの微調整をできる機能があって、それを変にイジってしまっている可能性ですが、それはないですよね?

    で、ピッチがズレていないかどうかは、オーディオから出てくる音と、別の基準音を一緒にならして聞き比べるだけでわかりますよ? 楽器の調律と同じ方法です。機材の配線を繋ぎ変える必要はまったくありません。自作のソフトで鑑賞されているとのことですので、基準音(440Hzとか、880Hzとか)のPCMデータを作って176.4kHzの機材で再生することはできますよね? あとは、別の機材で、例えば、PC標準の内蔵サウンドカードでも良いのですが、同じ基準音を再生するわけです。同時に音をならせばその周波数の違いはウナリとして聞こえます。

    ------------

    ところで、すいません、失礼ながら、本件はVisual Basicとは関係がなくなってきたように思います。マルチメディア(サウンドデータ)の再生に関するプログラムの方法論だと思いますので、スレッドを仕切りなおした方が良いように思います。


    (ホームページを再開しました)
    2010年12月20日 12:07
  • WaveIO7ですけれど、12/5付けで「PCMRender.CurrentPaddingBytes; 取得した時点の未再生のバイト数」が
    追加されているみたいです。これを使えば現在の再生位置は計算できませんか?

    #なんでPendingじゃなくてPaddingなのか疑問が残るけど


    jzkey
    2010年12月20日 13:32