2014年5月28日水曜日

Changes in SoundCloud JavaScript SDK version 2

Previously, SoundCloud announced 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.

What? July 1? We must hurry up! And the upgrade guide is here.

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.

I was relieved a little because they said "Everything should work as it did before". But actually, it's NOT TRUE!

First, mute() and unmute() are deleted. These methods are SoundManager2 methods. SDK ver.1 uses SoundManger2 internally.

SoundManager 2: Documentation

Second, SDK2 doesn't fire event callback like onplay, onfinish.

And I looked at it why. Finally, I found that it's because SDK2 now uses "AudioManager" rather than SoundManager2.

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

http://connect.soundcloud.com/sdk-2.0.0.js
SC.Helper.loadJavascript(audioManagerURL + "/audiomanager.js"...

What is AudioManager? I could not find docs... There is no way other than analyzing it (ノ∀`)

First of all, what's happend to SoundManager2?

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

Here is the results of "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)}

We can see soundManager2 has not so many functions, and mute() and unmute() are deleted.

Lookin at the functions, it seems "_player" is the AudioManager itself. I tried to "console.dir" this.

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

AudioManager(_player) has setMute(boolean). I tried to use setMute(), it works.

So we can throw it away the wrapper object. I think this is enough.

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

Okay then, what about event callback like onfinish?  Use stateChange() like this.

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

The "evt" object is a String. I observed what events were coming.

loading
playing
paused
ended

So we can rewrite the above code like this.

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();
          }
         );

It works.

These are the other changes as far as I noticed.

setVolume():0-100 to 0-1.0
unmute():setMute(false)
mute():setMute(true)
stop():pause() + seek(0)
position:getCurrentPosition() returns msec
duration:getDuration() returns msec
bytesLoaded:substitute : getLoadedPosition() returns msec
bytesTotal:substitute : getDuration() returns msec
setPosition():substitute : seek() when seeking AudioManager fires "seeking" event to stateChange()

That's it for now.

More detailed analysis.
琴線探査: Analysis : Why SoundCloud JavaScript SDK version 2 doesn't accept options(autoplay, onfinish etc.) ?