@next2d/media 1.14.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/Sound.js ADDED
@@ -0,0 +1,501 @@
1
+ import { SoundMixer } from "./SoundMixer";
2
+ import { URLRequest } from "@next2d/net";
3
+ import { EventDispatcher, Event as Next2DEvent, IOErrorEvent, ProgressEvent as Next2DProgressEvent } from "@next2d/events";
4
+ import { $Math, $performance, $requestAnimationFrame, $clamp, $getArray } from "@next2d/share";
5
+ import { $ajax, $audioContext, $audios, $currentPlayer, $decodeAudioData } from "@next2d/util";
6
+ /**
7
+ * Sound クラスを使用すると、アプリケーション内のサウンドを処理することができます。
8
+ * Sound クラスを使用すると、Sound オブジェクトの作成や、外部 MP3 ファイルのオブジェクトへのロードと再生ができます。
9
+ *
10
+ * The Sound class lets you work with sound in an application.
11
+ * The Sound class lets you create a Sound object,
12
+ * load and play an external MP3 file into that object.
13
+ *
14
+ * @class
15
+ * @memberOf next2d.media
16
+ * @extends EventDispatcher
17
+ */
18
+ export class Sound extends EventDispatcher {
19
+ /**
20
+ * @constructor
21
+ * @public
22
+ */
23
+ constructor() {
24
+ super();
25
+ /**
26
+ * @type {number}
27
+ * @default 0
28
+ * @private
29
+ */
30
+ this._$bytesLoaded = 0;
31
+ /**
32
+ * @type {number}
33
+ * @default 0
34
+ * @private
35
+ */
36
+ this._$bytesTotal = 0;
37
+ /**
38
+ * @type {AudioBuffer}
39
+ * @default null
40
+ * @private
41
+ */
42
+ this._$arrayBuffer = null;
43
+ /**
44
+ * @type {Uint8Array}
45
+ * @default null
46
+ * @private
47
+ */
48
+ this._$audioBuffer = null;
49
+ /**
50
+ * @type {object}
51
+ * @default null
52
+ * @private
53
+ */
54
+ this._$character = null;
55
+ /**
56
+ * @type {array}
57
+ * @private
58
+ */
59
+ this._$sources = $getArray();
60
+ /**
61
+ * @type {number}
62
+ * @default 1
63
+ * @private
64
+ */
65
+ this._$volume = 1;
66
+ /**
67
+ * @type {number}
68
+ * @default 0
69
+ * @private
70
+ */
71
+ this._$currentCount = 0;
72
+ /**
73
+ * @type {number}
74
+ * @default 0
75
+ * @private
76
+ */
77
+ this._$loopCount = 0;
78
+ /**
79
+ * @type {boolean}
80
+ * @default true
81
+ * @private
82
+ */
83
+ this._$stopFlag = true;
84
+ /**
85
+ * @type {string}
86
+ * @default ""
87
+ * @private
88
+ */
89
+ this._$src = "";
90
+ }
91
+ /**
92
+ * @description 指定されたクラスのストリングを返します。
93
+ * Returns the string representation of the specified class.
94
+ *
95
+ * @return {string}
96
+ * @default [class Sound]
97
+ * @method
98
+ * @static
99
+ */
100
+ static toString() {
101
+ return "[class Sound]";
102
+ }
103
+ /**
104
+ * @description 指定されたクラスの空間名を返します。
105
+ * Returns the space name of the specified class.
106
+ *
107
+ * @return {string}
108
+ * @default next2d.media.Sound
109
+ * @const
110
+ * @static
111
+ */
112
+ static get namespace() {
113
+ return "next2d.media.Sound";
114
+ }
115
+ /**
116
+ * @description 指定されたオブジェクトのストリングを返します。
117
+ * Returns the string representation of the specified object.
118
+ *
119
+ * @return {string}
120
+ * @default [object Sound]
121
+ * @method
122
+ * @public
123
+ */
124
+ toString() {
125
+ return "[object Sound]";
126
+ }
127
+ /**
128
+ * @description 指定されたオブジェクトの空間名を返します。
129
+ * Returns the space name of the specified object.
130
+ *
131
+ * @return {string}
132
+ * @default next2d.media.Sound
133
+ * @const
134
+ * @public
135
+ */
136
+ get namespace() {
137
+ return "next2d.media.Sound";
138
+ }
139
+ /**
140
+ * @description 既にアプリケーションにロードされているデータのバイト数です。
141
+ * The number of bytes of data that have been loaded into the application.
142
+ *
143
+ * @member {number}
144
+ * @default 0
145
+ * @readonly
146
+ * @public
147
+ */
148
+ get bytesLoaded() {
149
+ return this._$bytesLoaded;
150
+ }
151
+ /**
152
+ * @description アプリケーションにロードされるファイルの総バイト数。
153
+ * The total size in bytes of the file being loaded into the application.
154
+ *
155
+ * @member {number}
156
+ * @default 0
157
+ * @readonly
158
+ * @public
159
+ */
160
+ get bytesTotal() {
161
+ return this._$bytesTotal;
162
+ }
163
+ /**
164
+ * @description ループ回数の設定
165
+ * Loop count setting.
166
+ *
167
+ * @member {number}
168
+ * @default 0
169
+ * @public
170
+ */
171
+ get loopCount() {
172
+ return this._$loopCount;
173
+ }
174
+ set loopCount(loop_count) {
175
+ this._$loopCount = loop_count;
176
+ }
177
+ /**
178
+ * @description 外部サウンドのURL
179
+ * URL for external sound.
180
+ *
181
+ * @member {string}
182
+ * @default ""
183
+ * @public
184
+ */
185
+ get src() {
186
+ return this._$src;
187
+ }
188
+ set src(url) {
189
+ this.load(new URLRequest(url));
190
+ }
191
+ /**
192
+ * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。
193
+ * The volume, ranging from 0 (silent) to 1 (full volume).
194
+ *
195
+ * @member {number}
196
+ * @default 1
197
+ * @public
198
+ */
199
+ get volume() {
200
+ return this._$volume;
201
+ }
202
+ set volume(volume) {
203
+ this._$volume = $Math.min(SoundMixer.volume, $clamp(volume, 0, 1, 1));
204
+ const length = this._$sources.length;
205
+ if (length && $audioContext) {
206
+ for (let idx = 0; idx < length; ++idx) {
207
+ const source = this._$sources[idx];
208
+ if (source._$gainNode) {
209
+ source._$gainNode.gain.value = this._$volume;
210
+ source._$volume = this._$volume;
211
+ }
212
+ }
213
+ }
214
+ }
215
+ /**
216
+ * @description Sound クラスを複製します。
217
+ * Duplicate the Sound class.
218
+ *
219
+ * @return {Sound}
220
+ * @method
221
+ * @public
222
+ */
223
+ clone() {
224
+ const sound = new Sound();
225
+ sound.volume = this.volume;
226
+ sound._$loopCount = this._$loopCount;
227
+ if (this._$character) {
228
+ sound._$character = this._$character;
229
+ }
230
+ else {
231
+ sound._$audioBuffer = this._$audioBuffer;
232
+ }
233
+ return sound;
234
+ }
235
+ /**
236
+ * @description 指定した URL から外部 MP3 ファイルのロードを開始します。
237
+ * Initiates loading of an external MP3 file from the specified URL.
238
+ *
239
+ * @param {URLRequest} request
240
+ * @return {void}
241
+ * @method
242
+ * @public
243
+ */
244
+ load(request) {
245
+ this._$src = request.url;
246
+ $ajax({
247
+ "format": "arraybuffer",
248
+ "url": request.url,
249
+ "method": request.method,
250
+ "data": request.data,
251
+ "headers": request.headers,
252
+ "withCredentials": request.withCredentials,
253
+ "event": {
254
+ "loadstart": (event) => {
255
+ this._$loadStart(event);
256
+ },
257
+ "progress": (event) => {
258
+ this._$progress(event);
259
+ },
260
+ "loadend": (event) => {
261
+ this._$loadEnd(event);
262
+ }
263
+ }
264
+ });
265
+ }
266
+ /**
267
+ * @param {ProgressEvent} event
268
+ * @return {void}
269
+ * @method
270
+ * @private
271
+ */
272
+ _$loadStart(event) {
273
+ this._$bytesLoaded = event.loaded;
274
+ this._$bytesTotal = event.total;
275
+ if (this.willTrigger(Next2DEvent.OPEN)) {
276
+ this.dispatchEvent(new Next2DEvent(Next2DEvent.OPEN));
277
+ }
278
+ if (this.willTrigger(Next2DProgressEvent.PROGRESS)) {
279
+ this.dispatchEvent(new Next2DProgressEvent(Next2DProgressEvent.PROGRESS, false, false, event.loaded, event.total));
280
+ }
281
+ }
282
+ /**
283
+ * @param {ProgressEvent} event
284
+ * @return {void}
285
+ * @method
286
+ * @private
287
+ */
288
+ _$progress(event) {
289
+ this._$bytesLoaded = event.loaded;
290
+ this._$bytesTotal = event.total;
291
+ if (this.willTrigger(Next2DProgressEvent.PROGRESS)) {
292
+ this.dispatchEvent(new Next2DProgressEvent(Next2DProgressEvent.PROGRESS, false, false, event.loaded, event.total));
293
+ }
294
+ }
295
+ /**
296
+ * @param {ProgressEvent} event
297
+ * @return {void}
298
+ * @method
299
+ * @private
300
+ */
301
+ _$loadEnd(event) {
302
+ this._$bytesLoaded = event.loaded;
303
+ this._$bytesTotal = event.total;
304
+ if (this.willTrigger(Next2DProgressEvent.PROGRESS)) {
305
+ this.dispatchEvent(new Next2DProgressEvent(Next2DProgressEvent.PROGRESS, false, false, event.loaded, event.total));
306
+ }
307
+ const target = event.target;
308
+ if (!target) {
309
+ throw new Error("the Sound target is null.");
310
+ }
311
+ if (199 < target.status && 400 > target.status) {
312
+ this._$arrayBuffer = target.response;
313
+ if ($audioContext) {
314
+ $decodeAudioData(this)
315
+ .then((sound) => {
316
+ if (sound.hasEventListener(Next2DEvent.INIT)
317
+ || sound.hasEventListener(Next2DEvent.COMPLETE)) {
318
+ const player = $currentPlayer();
319
+ player._$loaders.push(sound);
320
+ }
321
+ });
322
+ }
323
+ else {
324
+ $audios.push(this);
325
+ }
326
+ }
327
+ else {
328
+ if (this.willTrigger(IOErrorEvent.IO_ERROR)) {
329
+ this.dispatchEvent(new IOErrorEvent(IOErrorEvent.IO_ERROR, false, false, target.statusText));
330
+ }
331
+ }
332
+ }
333
+ /**
334
+ * @description サウンドを再生します。
335
+ * Play a sound.
336
+ *
337
+ * @param {number} [start_time=0]
338
+ * @return {void}
339
+ * @method
340
+ * @public
341
+ */
342
+ play(start_time = 0) {
343
+ const buffer = this._$character
344
+ ? this._$character.audioBuffer
345
+ : this._$audioBuffer;
346
+ // execute
347
+ if (!$audioContext || !buffer) {
348
+ const now = $performance.now();
349
+ const wait = () => {
350
+ const buffer = this._$character
351
+ ? this._$character.audioBuffer
352
+ : this._$audioBuffer;
353
+ if (buffer !== null && $audioContext !== null) {
354
+ const offset = ($performance.now() - now) / 1000;
355
+ this._$createBufferSource(start_time, offset);
356
+ return;
357
+ }
358
+ $requestAnimationFrame(wait);
359
+ };
360
+ $requestAnimationFrame(wait);
361
+ }
362
+ else {
363
+ this._$createBufferSource(start_time);
364
+ }
365
+ }
366
+ /**
367
+ * @description チャンネルで再生しているサウンドを停止します。
368
+ * Stops the sound playing in the channel.
369
+ *
370
+ * @return {void}
371
+ * @method
372
+ * @public
373
+ */
374
+ stop() {
375
+ this._$stopFlag = true;
376
+ const length = this._$sources.length;
377
+ if (length) {
378
+ const player = $currentPlayer();
379
+ if ($audioContext) {
380
+ for (let idx = 0; idx < length; ++idx) {
381
+ const source = this._$sources[idx];
382
+ if (source._$gainNode) {
383
+ source._$gainNode.gain.value = 0;
384
+ source._$gainNode.disconnect();
385
+ source._$gainNode = null;
386
+ }
387
+ source.onended = null;
388
+ source.disconnect();
389
+ }
390
+ }
391
+ player._$sources.splice(player._$sources.indexOf(this), 1);
392
+ this._$currentCount = 0;
393
+ this._$sources.length = 0;
394
+ }
395
+ }
396
+ /**
397
+ * @param {object} tag
398
+ * @param {MovieClip} parent
399
+ * @return {void}
400
+ * @method
401
+ * @private
402
+ */
403
+ _$build(tag, parent) {
404
+ const loaderInfo = parent.loaderInfo;
405
+ if (!loaderInfo || !loaderInfo._$data) {
406
+ throw new Error("the loaderInfo or data is null.");
407
+ }
408
+ this._$character = loaderInfo
409
+ ._$data
410
+ .characters[tag.characterId];
411
+ if (!this._$character) {
412
+ throw new Error("character is null.");
413
+ }
414
+ // load AudioBuffer
415
+ if (!this._$character.audioBuffer) {
416
+ if ($audioContext) {
417
+ $decodeAudioData(this)
418
+ .then((sound) => {
419
+ if (sound.hasEventListener(Next2DEvent.INIT)
420
+ || sound.hasEventListener(Next2DEvent.COMPLETE)) {
421
+ const player = $currentPlayer();
422
+ player._$loaders.push(sound);
423
+ }
424
+ });
425
+ }
426
+ else {
427
+ $audios.push(this);
428
+ }
429
+ }
430
+ this._$loopCount = tag.loopCount | 0;
431
+ this._$volume = $Math.min(SoundMixer.volume, tag.volume);
432
+ }
433
+ /**
434
+ * @param {number} [start_time=0]
435
+ * @param {number} [offset=0]
436
+ * @return {void}
437
+ * @method
438
+ * @private
439
+ */
440
+ _$createBufferSource(start_time = 0, offset = 0) {
441
+ if (!$audioContext) {
442
+ throw new Error("the Audio Context is null.");
443
+ }
444
+ // setup
445
+ const source = $audioContext.createBufferSource();
446
+ source.onended = (event) => {
447
+ return this._$endEventHandler(event);
448
+ };
449
+ // main
450
+ source.buffer = this._$character
451
+ ? this._$character.audioBuffer
452
+ : this._$audioBuffer;
453
+ source._$gainNode = $audioContext.createGain();
454
+ source._$gainNode.connect($audioContext.destination);
455
+ const volume = $Math.min(SoundMixer.volume, this._$volume);
456
+ source._$gainNode.gain.value = volume;
457
+ source._$volume = volume;
458
+ source.connect(source._$gainNode);
459
+ source.start(start_time | 0, offset);
460
+ const player = $currentPlayer();
461
+ if (player._$sources.indexOf(this) === -1) {
462
+ player._$sources.push(this);
463
+ }
464
+ this._$sources.push(source);
465
+ this._$stopFlag = false;
466
+ }
467
+ /**
468
+ * @param {Event} event
469
+ * @return {void}
470
+ * @method
471
+ * @private
472
+ */
473
+ _$endEventHandler(event) {
474
+ const source = event.target;
475
+ this._$sources.splice(this._$sources.indexOf(source), 1);
476
+ if (!this._$stopFlag && this._$loopCount > this._$currentCount) {
477
+ this._$createBufferSource();
478
+ this._$currentCount++;
479
+ }
480
+ else {
481
+ this._$currentCount = 0;
482
+ if ($audioContext) {
483
+ if (source._$gainNode) {
484
+ source._$gainNode.gain.value = 0;
485
+ source._$gainNode.disconnect();
486
+ source._$gainNode = null;
487
+ }
488
+ // Firefoxにて、disconnectした時にonendedが呼び出されるのを回避
489
+ source.onended = null;
490
+ source.disconnect();
491
+ }
492
+ if (!this._$sources.length) {
493
+ const player = $currentPlayer();
494
+ player._$sources.splice(player._$sources.indexOf(this), 1);
495
+ }
496
+ if (this.willTrigger(Next2DEvent.SOUND_COMPLETE)) {
497
+ this.dispatchEvent(new Next2DEvent(Next2DEvent.SOUND_COMPLETE));
498
+ }
499
+ }
500
+ }
501
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * SoundMixer クラスには、静的プロパティやアプリケーションのグローバルサウンドコントロールのメソッドが含まれます。
3
+ * SoundMixer クラスは、アプリケーションの埋め込みおよびストリーミングサウンド、及び、Video クラスの音声を制御します。
4
+ *
5
+ * @class
6
+ * @memberOf next2d.media
7
+ */
8
+ export declare class SoundMixer {
9
+ /**
10
+ * @description 指定されたクラスのストリングを返します。
11
+ * Returns the string representation of the specified class.
12
+ *
13
+ * @return {string}
14
+ * @default [class SoundMixer]
15
+ * @method
16
+ * @static
17
+ */
18
+ static toString(): string;
19
+ /**
20
+ * @description 指定されたクラスの空間名を返します。
21
+ * Returns the space name of the specified class.
22
+ *
23
+ * @return {string}
24
+ * @default next2d.media.SoundMixer
25
+ * @const
26
+ * @static
27
+ */
28
+ static get namespace(): string;
29
+ /**
30
+ * @description 指定されたオブジェクトのストリングを返します。
31
+ * Returns the string representation of the specified object.
32
+ *
33
+ * @return {string}
34
+ * @default [object SoundMixer]
35
+ * @method
36
+ * @public
37
+ */
38
+ toString(): string;
39
+ /**
40
+ * @description 指定されたオブジェクトの空間名を返します。
41
+ * Returns the space name of the specified object.
42
+ *
43
+ * @return {string}
44
+ * @default next2d.media.SoundMixer
45
+ * @const
46
+ * @public
47
+ */
48
+ get namespace(): string;
49
+ /**
50
+ * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。
51
+ * The volume, ranging from 0 (silent) to 1 (full volume).
52
+ *
53
+ * @member {number}
54
+ * @default 1
55
+ * @static
56
+ */
57
+ static get volume(): number;
58
+ static set volume(volume: number);
59
+ /**
60
+ * @description 再生中のサウンドとビデオをすべて停止します。
61
+ * Stops all sound and video that is playing.
62
+ *
63
+ * @return {void}
64
+ * @method
65
+ * @static
66
+ */
67
+ static stopAll(): void;
68
+ }
@@ -0,0 +1,114 @@
1
+ import { $currentPlayer, $getSoundMixerVolume, $setSoundMixerVolume } from "@next2d/util";
2
+ import { $clamp, $Math } from "@next2d/share";
3
+ /**
4
+ * SoundMixer クラスには、静的プロパティやアプリケーションのグローバルサウンドコントロールのメソッドが含まれます。
5
+ * SoundMixer クラスは、アプリケーションの埋め込みおよびストリーミングサウンド、及び、Video クラスの音声を制御します。
6
+ *
7
+ * @class
8
+ * @memberOf next2d.media
9
+ */
10
+ export class SoundMixer {
11
+ /**
12
+ * @description 指定されたクラスのストリングを返します。
13
+ * Returns the string representation of the specified class.
14
+ *
15
+ * @return {string}
16
+ * @default [class SoundMixer]
17
+ * @method
18
+ * @static
19
+ */
20
+ static toString() {
21
+ return "[class SoundMixer]";
22
+ }
23
+ /**
24
+ * @description 指定されたクラスの空間名を返します。
25
+ * Returns the space name of the specified class.
26
+ *
27
+ * @return {string}
28
+ * @default next2d.media.SoundMixer
29
+ * @const
30
+ * @static
31
+ */
32
+ static get namespace() {
33
+ return "next2d.media.SoundMixer";
34
+ }
35
+ /**
36
+ * @description 指定されたオブジェクトのストリングを返します。
37
+ * Returns the string representation of the specified object.
38
+ *
39
+ * @return {string}
40
+ * @default [object SoundMixer]
41
+ * @method
42
+ * @public
43
+ */
44
+ toString() {
45
+ return "[object SoundMixer]";
46
+ }
47
+ /**
48
+ * @description 指定されたオブジェクトの空間名を返します。
49
+ * Returns the space name of the specified object.
50
+ *
51
+ * @return {string}
52
+ * @default next2d.media.SoundMixer
53
+ * @const
54
+ * @public
55
+ */
56
+ get namespace() {
57
+ return "next2d.media.SoundMixer";
58
+ }
59
+ /**
60
+ * @description ボリュームです。範囲は 0(無音)~ 1(フルボリューム)です。
61
+ * The volume, ranging from 0 (silent) to 1 (full volume).
62
+ *
63
+ * @member {number}
64
+ * @default 1
65
+ * @static
66
+ */
67
+ static get volume() {
68
+ return $getSoundMixerVolume();
69
+ }
70
+ static set volume(volume) {
71
+ $setSoundMixerVolume($clamp(volume, 0, 1, 1));
72
+ const soundMixerVolume = $getSoundMixerVolume();
73
+ const player = $currentPlayer();
74
+ const sounds = player._$sources;
75
+ for (let idx = 0; idx < sounds.length; ++idx) {
76
+ const sound = sounds[idx];
77
+ for (let idx = 0; idx < sound._$sources.length; ++idx) {
78
+ const source = sound._$sources[idx];
79
+ if (source._$gainNode) {
80
+ source._$gainNode.gain.value = $Math.min(soundMixerVolume, source._$volume);
81
+ }
82
+ }
83
+ }
84
+ const videos = player._$videos;
85
+ for (let idx = 0; idx < videos.length; ++idx) {
86
+ const video = videos[idx];
87
+ if (video._$video) {
88
+ video._$video.volume = $Math.min(soundMixerVolume, video.volume);
89
+ }
90
+ }
91
+ }
92
+ /**
93
+ * @description 再生中のサウンドとビデオをすべて停止します。
94
+ * Stops all sound and video that is playing.
95
+ *
96
+ * @return {void}
97
+ * @method
98
+ * @static
99
+ */
100
+ static stopAll() {
101
+ const player = $currentPlayer();
102
+ // sounds
103
+ const sources = player._$sources;
104
+ for (let idx = 0; idx < sources.length; ++idx) {
105
+ sources[idx].stop();
106
+ }
107
+ const videos = player._$videos;
108
+ for (let idx = 0; idx < videos.length; ++idx) {
109
+ videos[idx].pause();
110
+ }
111
+ player._$sources.length = 0;
112
+ player._$videos.length = 0;
113
+ }
114
+ }