2014年5月15日木曜日

SoundCloud「JavaScript SDK version 2」の変更点

Update 14.05.28: I wrote English version.
琴線探査: Changes in SoundCloud「JavaScript SDK version 2」

先日SoundCloudから「JavaScript SDK version 2」をリリースするよーというアナウンスがあった。

Backstage Blog - Introducing JavaScript SDK version 2 - SoundCloud Developers

JavaScript SDK version 1 is now deprecated and will be permanently replaced by version 2 on July 1, 2014.

7月1日にver.1はver.2で永久に置き換わるとあるので、急がなきゃなと思っていた。

アップグレードガイドも出ていた。

Upgrading to JavaScript SDK 2.0.0 - SoundCloud Developers

We've done our utmost to make the upgrade to version 2.0.0 as seamless as possible. Everything should work as it did before, but please be sure to test before deploying your application.

「全て以前のように動くはずだから」というので少し安心したが、実際にやってみたらトンでもない!開発者は急いだ方がいい。

まずおかしいなと気づいたのは、これまであったmute()とunmute()が無くなっていたこと。これらはver.1のSDKが内部的に使っていたSoundManager2のメソッドだ。

SoundManager 2: Documentation

さらにおかしいなと思ったのは、onplayとかonfinishなどのイベントコールバックが効かなくなっていたこと。

色々と調べていくと、どうやらver.2はSoundManager2を使うのをやめて「AudioManager」というのを使うようになったようだ。

javascript - Options passed to Soundmanager (via Soundcloud JS SDK) not being used - Stack Overflow

実際に「http://connect.soundcloud.com/sdk-2.0.0.js」の中を見てみると、以前あったSoundManagerの記述はなく、AudioManagerが使われていることが確認できた。こんな感じで。

SC.Helper.loadJavascript(audioManagerURL + "/audiomanager.js"

このAudioManagerというのは何なのか?ドキュメントが見当たらないので解析するしか無い(ノ∀`)

まず、これまでSoundManager2だったものはどのようになったのか?

SC.stream("/tracks/" + id,
          {
            onfinish:onPlayerPlayComplete,
            onpause:onPlayerPause,
            onplay:onPlayerPlay,
          },
          function(soundManager2) {
            playerSC = soundManager2;
            playerSC.play();
          }
         );

ここで返ってくるsoundManager2をconsole.dirしてみると、ほとんど機能が無いことに気づく。

getCurrentPosition: function (){return this._player.getCurrentPosition()}

getDuration: function (){return this._player.getDuration()}

getLoadedPosition: function (){return this._player.getLoadedPosition()}

getState: function (){return this._player.getState()}

getType: function (){return this._player.getType()}

getVolume: function (){return this._player.getVolume()}

pause: function (){return this._player.pause()}

play: function (position){if(this._player.getState()==="loading"||this._player.getState()==="initialize"){return this._player.on("stateChange",function(state){if(state==="idle"){return this.play()}})}else{return this._player.play()}}

seek: function (ms){return this._player.seek(ms)}

setVolume: function (volume){return this._player.setVolume(volume)}

stop: function (){this._player.pause();return this._player.seek(0)}

確かにmute()とかunmute()とか無いわぁ〜(^^);

それぞれのメソッドの中を見ると、「_player」というのがAudioManagerのようだ。これをconsole.dirしてみる。あまりに長すぎるのでメソッド名だけ。

bind
getCurrentPosition
getDuration
getErrorID
getErrorMessage
getId
getLoadedPosition
getMute
getState
getType
getVolume
isInOneOfStates
kill
listenTo
listenToOnce
off
on
once
pause
play
preload
resume
seek
setMute
setVolume
stopListening
toggleEventListeners
trigger
unbind
updatePositions

こちらの方にはsetMute(boolean)というメソッドがある。実際に使ってみたら効いた。なのでラッパーオブジェクトは捨てちゃって、こうしていいと思う。

SC.stream("/tracks/" + id,
          function(player) {
            playerSC = player._player;
            playerSC.play();
          }
         );

残る大きな問題はonfinishとかのイベントコールバックをどうするかだけど、playerSC.on("stateChange", function(evt){});でいけた。

SC.stream("/tracks/" + id,
          function(player) {
            playerSC = player._player;
            playerSC.on("stateChange", function(evt){
              console.log(evt);
            });
            playerSC.play();
          }
         );

このevtオブジェクトは文字列。一曲流して動きを観察してみたところ、こんな感じのイベントが来てた。

loading
playing
paused
ended

つまり、こういうことだろう。

SC.stream("/tracks/" + id,
          function(player) {
            playerSC = player._player;
            playerSC.on("stateChange", function(evt){
              console.log(evt);
              switch(evt) {
                case "ended":
                  onPlayerPlayComplete();
                  break;
                case "paused":
                  onPlayerPause();
                  break;
                case "playing":
                  onPlayerPlay();
                  break;
              }
            });
            playerSC.play();
          }
         );

うまくいった。

その他、気づいた範囲で変更点を挙げていこう。

setVolume():0〜100だったのが0〜1.0に
unmute():setMute(false)
mute():setMute(true)
stop():pause() + seek(0)
position:getCurrentPosition() msecで返ってくる。
duration:getDuration() msecで返ってくる。
bytesLoaded:無し。getLoadedPosition()で代用。msecで返ってくる。
bytesTotal:無し。getDuration()で代用。
setPosition():seek() seek中には「seeking」イベントがstateChangeで飛んでくる

今のところ以上!ふぅ(´-`)

追記14.05.26:さらに詳しい分析
琴線探査: なぜSoundCloud「JavaScript SDK version 2」ではautoplayやonfinishなどのオプションを受け付けなくなったのか?の分析