provide installation instructions
[extjs.git] / air / src / MusicPlayer.js
1 /*\r
2  * Ext JS Library 0.30\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 Ext.ns('Ext.air');\r
10 \r
11 Ext.air.MusicPlayer = Ext.extend(Ext.util.Observable, {\r
12         /**\r
13          * The currently active Sound. Read-only.\r
14          * @type air.Sound\r
15          * @property activeSound\r
16          */\r
17         activeSound: null,\r
18         /**\r
19          * The currently active SoundChannel. Read-only.\r
20          * @type air.SoundChannel\r
21          * @property activeChannel\r
22          */\r
23         activeChannel: null,\r
24         /**\r
25          * The currently active Transform. Read-only.\r
26          * @type air.SoundTransform\r
27          * @property activeTransform\r
28          */\r
29         activeTransform: new air.SoundTransform(1, 0),\r
30         // private \r
31         pausePosition: 0,\r
32         /**\r
33          * @cfg {Number} progressInterval\r
34          * How often to fire the progress event when playing music in milliseconds\r
35          * Defaults to 500.\r
36          */\r
37         progressInterval: 500,\r
38         \r
39         constructor: function(config) {\r
40                 config = config || {};\r
41                 Ext.apply(this, config);\r
42                 \r
43                 this.addEvents(\r
44                         /**\r
45                          * @event stop\r
46                          */\r
47                         'stop',\r
48                         /**\r
49                          * @event pause\r
50                          */\r
51                         'pause',\r
52                         /**\r
53                          * @event play\r
54                          */\r
55                         'play',\r
56                         /**\r
57                          * @event load\r
58                          */\r
59                         'load',\r
60                         /**\r
61                          * @event id3info\r
62                          */\r
63                         'id3info',\r
64                         /**\r
65                          * @event complete\r
66                          */\r
67                         'complete',\r
68                         /**\r
69                          * @event progress\r
70                          */\r
71                         'progress',\r
72                         /**\r
73                          * @event skip\r
74                          */\r
75                         'skip'\r
76                 );\r
77                 \r
78                 Ext.air.MusicPlayer.superclass.constructor.call(this, config);\r
79                 this.onSoundFinishedDelegate = this.onSoundFinished.createDelegate(this);\r
80                 this.onSoundLoadDelegate = this.onSoundLoad.createDelegate(this);\r
81                 this.onSoundID3LoadDelegate = this.onSoundID3Load.createDelegate(this);\r
82 \r
83                 Ext.TaskMgr.start({\r
84                         run: this.notifyProgress,\r
85                         scope: this,\r
86                         interval: this.progressInterval\r
87                 });             \r
88         },      \r
89 \r
90         /**\r
91          * Adjust the volume\r
92          * @param {Object} percent\r
93          * Ranges from 0 to 1 specifying volume of sound.\r
94          */\r
95         adjustVolume: function(percent) {\r
96                 this.activeTransform.volume = percent;\r
97                 if (this.activeChannel) {               \r
98                         this.activeChannel.soundTransform = this.activeTransform;               \r
99                 }               \r
100         },\r
101         /**\r
102          * Stop the player\r
103          */\r
104         stop: function() {\r
105                 this.pausePosition = 0;         \r
106                 if (this.activeChannel) {\r
107                         this.activeChannel.stop();                      \r
108                         this.activeChannel = null;                      \r
109                 }               \r
110                 if (this.activeSound) {\r
111                         this.activeSound.removeEventListener(air.Event.COMPLETE, this.onSoundLoadDelegate);\r
112                         this.activeSound.removeEventListener(air.Event.ID3, this.onSoundID3LoadDelegate);\r
113                         this.activeSound.removeEventListener(air.Event.SOUND_COMPLETE, this.onSoundFinishedDelegate);                                           \r
114                 }\r
115         },\r
116         /**\r
117          * Pause the player if there is an activeChannel\r
118          */\r
119         pause: function() {\r
120                 if (this.activeChannel) {\r
121                         this.pausePosition = this.activeChannel.position;\r
122                         this.activeChannel.stop();                      \r
123                 }               \r
124         },\r
125         /**\r
126          * Play a sound, if no url is specified will attempt to resume the activeSound\r
127          * @param {String} url (optional)\r
128          * Url resource to play\r
129          */\r
130         play: function(url) {\r
131                 if (url) {                      \r
132                         this.stop();                    \r
133                         var req = new air.URLRequest(url);\r
134                         this.activeSound = new air.Sound();\r
135                         this.activeSound.addEventListener(air.Event.SOUND_COMPLETE, this.onSoundFinishedDelegate);                                              \r
136                         this.activeSound.addEventListener(air.Event.COMPLETE, this.onSoundLoadDelegate);                        \r
137                         this.activeSound.addEventListener(air.Event.ID3, this.onSoundID3LoadDelegate);\r
138                         this.activeSound.load(req);                                             \r
139                 } else {\r
140                         this.onSoundLoad();     \r
141                 }       \r
142         },\r
143         \r
144         /**\r
145          * Skip to a specific position in the song currently playing.\r
146          * @param {Object} pos\r
147          */\r
148         skipTo: function(pos) {\r
149                 if (this.activeChannel) {\r
150                         this.activeChannel.stop();              \r
151                         this.activeChannel = this.activeSound.play(pos);        \r
152                         this.activeChannel.soundTransform = this.activeTransform;               \r
153                         this.fireEvent('skip', this.activeChannel, this.activeSound, pos);\r
154                 }\r
155         },\r
156         \r
157         /**\r
158          * Returns whether or not there is an active SoundChannel.\r
159          */\r
160         hasActiveChannel: function() {\r
161                 return !!this.activeChannel;\r
162         },\r
163         \r
164         // private\r
165         onSoundLoad: function(event) {\r
166                 if (this.activeSound) {\r
167                         if (this.activeChannel) {\r
168                                 this.activeChannel.stop();\r
169                         }\r
170                         this.activeChannel = this.activeSound.play(this.pausePosition);\r
171                         this.activeChannel.soundTransform = this.activeTransform;\r
172                         this.fireEvent('load', this.activeChannel, this.activeSound);\r
173                 }               \r
174         },\r
175         // private\r
176         onSoundFinished: function(event) {\r
177                 // relay AIR event\r
178                 this.fireEvent('complete', event);\r
179         },\r
180         // private\r
181         onSoundID3Load: function(event) {\r
182                 this.activeSound.removeEventListener(air.Event.ID3, this.onSoundID3LoadDelegate);               \r
183                 var id3 = event.target.id3;             \r
184                 this.fireEvent('id3info', id3);\r
185         },\r
186         // private\r
187         notifyProgress: function() {\r
188                 if (this.activeChannel && this.activeSound) {\r
189                         var playbackPercent = 100 * (this.activeChannel.position / this.activeSound.length);                    \r
190                         // SOUND_COMPLETE does not seem to work consistently.\r
191                         if (playbackPercent > 99.7) {\r
192                                 this.onSoundFinished();                         \r
193                         } else {\r
194                                 this.fireEvent('progress', this.activeChannel, this.activeSound);\r
195                         }       \r
196                 }               \r
197         }               \r
198 });