|
638 | 638 | self._preload = (typeof o.preload === 'boolean' || o.preload === 'metadata') ? o.preload : true; |
639 | 639 | self._rate = o.rate || 1; |
640 | 640 | self._sprite = o.sprite || {}; |
641 | | - self._src = (typeof o.src !== 'string') ? o.src : [o.src]; |
| 641 | + self._src = (o.src instanceof Array) ? o.src : [o.src]; |
642 | 642 | self._volume = o.volume !== undefined ? o.volume : 1; |
643 | 643 | self._xhr = { |
644 | 644 | method: o.xhr && o.xhr.method ? o.xhr.method : 'GET', |
|
706 | 706 | load: function() { |
707 | 707 | var self = this; |
708 | 708 | var url = null; |
| 709 | + var arraybuffer = null; |
| 710 | + var audiobuffer = null; |
| 711 | + |
| 712 | + if (self._state === 'loaded' || self._state === 'loading') { |
| 713 | + return; |
| 714 | + } |
709 | 715 |
|
710 | 716 | // If no audio is available, quit immediately. |
711 | 717 | if (Howler.noAudio) { |
|
714 | 720 | } |
715 | 721 |
|
716 | 722 | // Make sure our source is in an array. |
717 | | - if (typeof self._src === 'string') { |
| 723 | + if (!(self._src instanceof Array)) { |
718 | 724 | self._src = [self._src]; |
719 | 725 | } |
720 | 726 |
|
721 | 727 | // Loop through the sources and pick the first one that is compatible. |
722 | 728 | for (var i=0; i<self._src.length; i++) { |
723 | | - var ext, str; |
| 729 | + var src = self._src[i]; |
| 730 | + var ext; |
| 731 | + |
| 732 | + var srcIsString = typeof src === 'string'; |
| 733 | + if (!srcIsString) { |
| 734 | + if (ArrayBuffer.isView(src)) { |
| 735 | + arraybuffer = src.buffer; |
| 736 | + break; |
| 737 | + } else if (src instanceof ArrayBuffer) { |
| 738 | + arraybuffer = src; |
| 739 | + break; |
| 740 | + } else if (src instanceof AudioBuffer) { |
| 741 | + audiobuffer = src; |
| 742 | + break; |
| 743 | + } |
| 744 | + } |
724 | 745 |
|
725 | 746 | if (self._format && self._format[i]) { |
726 | 747 | // If an extension was specified, use that instead. |
727 | 748 | ext = self._format[i]; |
728 | | - } else { |
729 | | - // Make sure the source is a string. |
730 | | - str = self._src[i]; |
731 | | - if (typeof str !== 'string') { |
732 | | - self._emit('loaderror', null, 'Non-string found in selected audio sources - ignoring.'); |
733 | | - continue; |
734 | | - } |
735 | | - |
| 749 | + } else if (srcIsString) { |
736 | 750 | // Extract the file extension from the URL or base64 data URI. |
737 | | - ext = /^data:audio\/([^;,]+);/i.exec(str); |
| 751 | + ext = /^data:audio\/([^;,]+);/i.exec(src); |
738 | 752 | if (!ext) { |
739 | | - ext = /\.([^.]+)$/.exec(str.split('?', 1)[0]); |
| 753 | + ext = /\.([^.]+)$/.exec(src.split('?', 1)[0]); |
740 | 754 | } |
741 | 755 |
|
742 | 756 | if (ext) { |
743 | 757 | ext = ext[1].toLowerCase(); |
744 | 758 | } |
| 759 | + } else { |
| 760 | + self._emit('loaderror', null, 'Non-string found in selected audio sources - ignoring.'); |
| 761 | + continue; |
745 | 762 | } |
746 | 763 |
|
747 | 764 | // Log a warning if no extension was found. |
|
756 | 773 | } |
757 | 774 | } |
758 | 775 |
|
759 | | - if (!url) { |
760 | | - self._emit('loaderror', null, 'No codec support for selected audio sources.'); |
761 | | - return; |
| 776 | + if (url) { |
| 777 | + self._src = url; |
| 778 | + } else { |
| 779 | + if (!self._webAudio) { |
| 780 | + self._emit('loaderror', null, 'AudioBuffer or ArrayBuffer is incompatible with html5 audio.'); |
| 781 | + return; |
| 782 | + } else if (arraybuffer) { |
| 783 | + self._src = arraybuffer; |
| 784 | + } else if (audiobuffer) { |
| 785 | + self._src = audiobuffer; |
| 786 | + } else { |
| 787 | + self._emit('loaderror', null, 'No codec support for selected audio sources.'); |
| 788 | + return; |
| 789 | + } |
762 | 790 | } |
763 | 791 |
|
764 | | - self._src = url; |
765 | 792 | self._state = 'loading'; |
766 | 793 |
|
767 | 794 | // If the hosting page is HTTPS and the source isn't, |
768 | 795 | // drop down to HTML5 Audio to avoid Mixed Content errors. |
769 | | - if (window.location.protocol === 'https:' && url.slice(0, 5) === 'http:') { |
| 796 | + if (window.location.protocol === 'https:' && url && url.slice(0, 5) === 'http:') { |
770 | 797 | self._html5 = true; |
771 | 798 | self._webAudio = false; |
772 | 799 | } |
773 | 800 |
|
774 | | - // Create a new sound object and add it to the pool. |
775 | | - new Sound(self); |
776 | | - |
777 | 801 | // Load and decode the audio data for playback. |
778 | 802 | if (self._webAudio) { |
779 | | - loadBuffer(self); |
| 803 | + loadBuffer(self); |
780 | 804 | } |
781 | 805 |
|
| 806 | + // Create a new sound object and add it to the pool. |
| 807 | + new Sound(self); |
| 808 | + |
782 | 809 | return self; |
783 | 810 | }, |
784 | 811 |
|
|
1285 | 1312 |
|
1286 | 1313 | // Update the volume or return the current volume. |
1287 | 1314 | var sound; |
1288 | | - if (typeof vol !== 'undefined' && vol >= 0 && vol <= 1) { |
| 1315 | + if (typeof vol !== 'undefined') { |
| 1316 | + vol = Math.max(vol, 0); |
| 1317 | + |
1289 | 1318 | // If the sound hasn't loaded, add it to the load queue to change volume when capable. |
1290 | 1319 | if (self._state !== 'loaded'|| self._playLock) { |
1291 | 1320 | self._queue.push({ |
|
1508 | 1537 | } else if (args.length === 2) { |
1509 | 1538 | loop = args[0]; |
1510 | 1539 | id = parseInt(args[1], 10); |
| 1540 | + } else if (args.length === 3) { |
| 1541 | + id = parseInt(args[2], 10); |
| 1542 | + sound = self._soundById(id); |
| 1543 | + if (sound) { |
| 1544 | + sound._loop = true; |
| 1545 | + sound._start = parseFloat(args[0]) || 0; |
| 1546 | + sound._stop = parseFloat(args[1]) || self._duration; |
| 1547 | + if (self._webAudio && sound._node && sound._node.bufferSource) { |
| 1548 | + sound._node.bufferSource.loop = true; |
| 1549 | + sound._node.bufferSource.loopStart = sound._start; |
| 1550 | + sound._node.bufferSource.loopEnd = sound._stop; |
| 1551 | + |
| 1552 | + // If playing, restart playback to ensure looping updates. |
| 1553 | + if (self.playing(id)) { |
| 1554 | + self.pause(id, true); |
| 1555 | + self.play(id, true); |
| 1556 | + } |
| 1557 | + } |
| 1558 | + } |
| 1559 | + return; |
1511 | 1560 | } |
1512 | 1561 |
|
1513 | 1562 | // If no id is passed, get all ID's to be looped. |
|
1835 | 1884 | // Delete this sound from the cache (if no other Howl is using it). |
1836 | 1885 | var remCache = true; |
1837 | 1886 | for (i=0; i<Howler._howls.length; i++) { |
1838 | | - if (Howler._howls[i]._src === self._src || self._src.indexOf(Howler._howls[i]._src) >= 0) { |
| 1887 | + if (Howler._howls[i]._src === self._src || (typeof self._src === 'string' && self._src.indexOf(Howler._howls[i]._src) >= 0)) { |
1839 | 1888 | remCache = false; |
1840 | 1889 | break; |
1841 | 1890 | } |
|
2188 | 2237 |
|
2189 | 2238 | // Setup the buffer source for playback. |
2190 | 2239 | sound._node.bufferSource = Howler.ctx.createBufferSource(); |
2191 | | - sound._node.bufferSource.buffer = cache[self._src]; |
| 2240 | + sound._node.bufferSource.buffer = self._buffer; |
2192 | 2241 |
|
2193 | 2242 | // Connect to the correct node. |
2194 | 2243 | if (sound._panner) { |
|
2431 | 2480 | * @param {Howl} self |
2432 | 2481 | */ |
2433 | 2482 | var loadBuffer = function(self) { |
2434 | | - var url = self._src; |
2435 | | - |
2436 | | - // Check if the buffer has already been cached and use it instead. |
2437 | | - if (cache[url]) { |
2438 | | - // Set the duration from the cache. |
2439 | | - self._duration = cache[url].duration; |
| 2483 | + var src = self._src; |
2440 | 2484 |
|
2441 | | - // Load the sound into this Howl. |
2442 | | - loadSound(self); |
| 2485 | + // Check if the src is arraybuffer or audiobuffer. |
| 2486 | + // Disable caching if its not a url. |
| 2487 | + if (src instanceof ArrayBuffer) { |
| 2488 | + decodeAudioData(src, self); |
| 2489 | + return; |
| 2490 | + } else if (src instanceof AudioBuffer) { |
| 2491 | + loadSound(self, src); |
| 2492 | + return; |
| 2493 | + } |
2443 | 2494 |
|
| 2495 | + // Check if the buffer has already been cached and use it instead. |
| 2496 | + if (cache[src]) { |
| 2497 | + loadSound(self, cache[src]); |
2444 | 2498 | return; |
2445 | 2499 | } |
2446 | 2500 |
|
2447 | | - if (/^data:[^;]+;base64,/.test(url)) { |
| 2501 | + if (/^data:[^;]+;base64,/.test(src)) { |
2448 | 2502 | // Decode the base64 data URI without XHR, since some browsers don't support it. |
2449 | | - var data = atob(url.split(',')[1]); |
| 2503 | + var data = atob(src.split(',')[1]); |
2450 | 2504 | var dataView = new Uint8Array(data.length); |
2451 | 2505 | for (var i=0; i<data.length; ++i) { |
2452 | 2506 | dataView[i] = data.charCodeAt(i); |
|
2456 | 2510 | } else { |
2457 | 2511 | // Load the buffer from the URL. |
2458 | 2512 | var xhr = new XMLHttpRequest(); |
2459 | | - xhr.open(self._xhr.method, url, true); |
| 2513 | + xhr.open(self._xhr.method, src, true); |
2460 | 2514 | xhr.withCredentials = self._xhr.withCredentials; |
2461 | 2515 | xhr.responseType = 'arraybuffer'; |
2462 | 2516 |
|
|
2483 | 2537 | self._html5 = true; |
2484 | 2538 | self._webAudio = false; |
2485 | 2539 | self._sounds = []; |
2486 | | - delete cache[url]; |
| 2540 | + //delete cache[src]; |
2487 | 2541 | self.load(); |
2488 | 2542 | } |
2489 | 2543 | }; |
|
2516 | 2570 |
|
2517 | 2571 | // Load the sound on success. |
2518 | 2572 | var success = function(buffer) { |
2519 | | - if (buffer && self._sounds.length > 0) { |
| 2573 | + if (buffer) { |
2520 | 2574 | cache[self._src] = buffer; |
2521 | 2575 | loadSound(self, buffer); |
2522 | 2576 | } else { |
|
2539 | 2593 | */ |
2540 | 2594 | var loadSound = function(self, buffer) { |
2541 | 2595 | // Set the duration. |
2542 | | - if (buffer && !self._duration) { |
| 2596 | + if (buffer) { |
| 2597 | + self._buffer = buffer; |
2543 | 2598 | self._duration = buffer.duration; |
2544 | 2599 | } |
2545 | 2600 |
|
|
0 commit comments