どのようにLyricsXメディアプレーヤーの進行状況を追跡


LyricsX MacOSのためのオープンソースソフトウェアをダウンロードし、音楽(以前のiTunes)、Spotify、Auvrvana、Vox、スワジアン、またはOSの現在再生中のトラックの現在の演奏トラックの歌詞を表示します.これは、ローカルストレージまたはインターネットから時間タグの歌詞ファイルを取得し、プレーヤーと同期して歌詞を表示します.
重要なコンポーネントとして、私はリアルタイム歌詞表示プログラムの複数の実装を研究しました、そして、私はLyricsXの後のメカニズムが特に面白いとわかりました.ここでは、私はどのようにLyricsXは、ユニークでリソースを節約する方法でプレーヤーの進捗状況を追跡するあなたと共有したいと思います.

コモンプラクティス
LyricsXに行く前に、最初に、他のオープンソースソフトウェアの大部分がこれに取り組む方法を見てみましょう.要するに、それを行う2つの主要な方法があります:更新イベントを送信するには、プレーヤーに頼るか、定期的に時間間隔で進行状況のためのプレーヤーを照会します.両方の方法は、ユーザーがプレーヤーの進行状況を大いに調整し、極端なケースになるときに理想的ではない、歌詞の短い期間に多くの行を持つ歌詞.
最初の方法は、更新プログラムのイベントを送信するには、プレーヤーに依存して、更新間隔が完全にプラットフォームに依存するため、信頼性がありません.いくつかのプラットフォームは、2回目の更新を発行することがありますが、いくつかの秒数回だけ送信することがあります.最悪の場合、長い更新間隔のためにいくつかの線がスキップされるかもしれません.さらに悪いことに、人々に公開されたそのような特徴が不足しているかもしれません.
第2の方法は、通常の時間間隔で進行中のプレイヤーを問い合わせると、実際には上記の問題を解決することができますが、ただ単に理想的なシステムでは、クエリ自体が変数の読み込みと同じくらい簡単です.それどころか、現実の生活の中ではほとんどの場合ではない.通常、進捗状況を問い合わせるには、RPCのいくつかの種類を通過したり、小さなスクリプトを実行しなければならなかった.いずれにせよ、それはすでに多くの複雑なリソースを消費する、悪いケースでは、いくつかのインターネットを介して再生の進行状況を要求を送信する必要があります.精度によっては、クエリを複数回実行する必要があります.しかし、1つの質問が既に半分の秒を取ることができるならば、このようにより良い精度を成し遂げることはほとんど不可能です.

どのようにLyricsXはそれを行うのですか?
Lyricsxは上記のすべての問題を解決した方法は本当にCleaverです.代わりに、現在の進行状況のためのプレーヤーを求めて、それ自体のタイマーを維持し、いくつかの基本的なイベントだけを使用して、プレーヤーと同期しようとします.


プログラムはプレーヤーのグローバル状態を保持します.

  • プレイヤーのステータス:明らかに、これはいずれかの再生や一時停止することができます.一部のプレーヤーの停止状態については、再生のためのトラックスタンバイ、したがって歌詞のタイムラインはないので、我々の考慮から.
  • When the state is playing:

  • 内部t_0 時間:これはcurrent time in internal clock - player progress , 現在のプレイセッションの基準点として機能します.内部クロックは、実際の世界時間のシステムクロック、またはコードのパフォーマンスを測定するクロックのように取得するのに十分速いものは何でもできます.ここでは相対的な違いしか気にしない.

  • 再生速度:通常の速度のための1.0、半分の速度の0.5、速度の2倍のように、など.
  • When the state is paused:

  • 再生の進行状況:それが一時停止しているときのプレーヤーの進行状況.
  • グローバル状態は、次のプレイヤーイベントで更新されます.
  • ステータスの変更(一時停止、停止時に再生)
  • 手動時間変更
  • レート変更( on rate change )
  • あなたは、我々が持っているイベントセットがかなり基本的であるのを見ることができて、ほとんどそのようなソフトウェアが走るのに必須です.

    同期UI
    手でプレーヤーの状態では、それははるかに簡単なシステムコールと計算を介して現在のプレーヤーの進行状況を得るために簡単です.
    progress = ( t() - t_0 ) / rate
    
    どこt_0 は内部クロックの現在時刻を取得する関数です.
    それとは別に、我々は今、プレーヤーの状態のコピーを維持するので、我々は単にプレーヤーは、状態が変更されない限り、示された速度で再生を継続すると仮定することができます.これにより、アニメーションシーケンスのタイムラインを先に生成するなど、最適化を行うことができます.これらの準備作業だけでは、再生の進行状況の更新頻繁に頻繁には、プレーヤーの状態の変更時に行われる必要があります.

    ジュリエットボックス
    Lyricovaジュークボックスでこのロジックを移植する私の試みではplay , pause , seeked and ratechange イベント、および内部クロックについては、我々performance.now() その高精度と利便性を使用するためにrequestAnimationFrame() .
    タイムラインのために、Lyricsxは一見信頼できる内部の方法を使用しましたDispatcherQueue.schedule() アップル提供.しかし、Webにおけるスケジューリング機能window.setTimeout() – 私たちのための高い精度を持っている名誉を持っていません.我々はその後、Aを持っていたrequestAnimationFrame メディアが再生しているときに実行されるループ.これにより、プレーヤ状態がスタートしたときにループを開始し、停止するとループを停止する.
    requestAnimatioFrame ループ、現在のキーフレームの終了時刻に対して現在時刻をチェックします.ループはブラウザで1フレームごとに実行されるので、ループ内のロジックをシンプルに保ち、現在のフレームが終了したときにのみキーフレームを進める.フレームポインタは、ポインタが常に正しいフレームを指していることを確認するために上記のフックで更新されるだけです.
    実装の詳細に興味があれば、LyricsXのソースコードと、異なるプレイヤーからのイベントの翻訳に責任を持つサブモジュールMusicPlayerを見ることができます.
    あるいは、あなたが反応とtypescriptでより快適であるならば、私はこのロジックを私のLyricovaジュークボックスに移植しました<audio /> ブラウザのタグ.
  • ddddxxx/LyricsX
  • ddddxxx/MusicPlayer
  • blueset/project-lyricova: usePlayerState()

  • 謝辞
    どうもありがとうddddxxx , ソフトウェアの素晴らしいアイデアと実装のためのLyricsXの主なメンテナ.
    郵便How LyricsX keeps track of progress of media players 最初に現れた1A23 Blog .