Spaces:
Running
Running
Upload 6 files
Browse files- Blob.js +197 -0
- FileSaver.min.js +7 -0
- animalese.js +64 -0
- animalese.wav +0 -0
- index.html +60 -17
- riffwave.js +132 -0
Blob.js
ADDED
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/* Blob.js
|
2 |
+
* A Blob implementation.
|
3 |
+
* 2014-07-24
|
4 |
+
*
|
5 |
+
* By Eli Grey, http://eligrey.com
|
6 |
+
* By Devin Samarin, https://github.com/dsamarin
|
7 |
+
* License: X11/MIT
|
8 |
+
* See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md
|
9 |
+
*/
|
10 |
+
|
11 |
+
/*global self, unescape */
|
12 |
+
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
|
13 |
+
plusplus: true */
|
14 |
+
|
15 |
+
/*! @source http://purl.eligrey.com/github/Blob.js/blob/master/Blob.js */
|
16 |
+
|
17 |
+
(function (view) {
|
18 |
+
"use strict";
|
19 |
+
|
20 |
+
view.URL = view.URL || view.webkitURL;
|
21 |
+
|
22 |
+
if (view.Blob && view.URL) {
|
23 |
+
try {
|
24 |
+
new Blob;
|
25 |
+
return;
|
26 |
+
} catch (e) {}
|
27 |
+
}
|
28 |
+
|
29 |
+
// Internally we use a BlobBuilder implementation to base Blob off of
|
30 |
+
// in order to support older browsers that only have BlobBuilder
|
31 |
+
var BlobBuilder = view.BlobBuilder || view.WebKitBlobBuilder || view.MozBlobBuilder || (function(view) {
|
32 |
+
var
|
33 |
+
get_class = function(object) {
|
34 |
+
return Object.prototype.toString.call(object).match(/^\[object\s(.*)\]$/)[1];
|
35 |
+
}
|
36 |
+
, FakeBlobBuilder = function BlobBuilder() {
|
37 |
+
this.data = [];
|
38 |
+
}
|
39 |
+
, FakeBlob = function Blob(data, type, encoding) {
|
40 |
+
this.data = data;
|
41 |
+
this.size = data.length;
|
42 |
+
this.type = type;
|
43 |
+
this.encoding = encoding;
|
44 |
+
}
|
45 |
+
, FBB_proto = FakeBlobBuilder.prototype
|
46 |
+
, FB_proto = FakeBlob.prototype
|
47 |
+
, FileReaderSync = view.FileReaderSync
|
48 |
+
, FileException = function(type) {
|
49 |
+
this.code = this[this.name = type];
|
50 |
+
}
|
51 |
+
, file_ex_codes = (
|
52 |
+
"NOT_FOUND_ERR SECURITY_ERR ABORT_ERR NOT_READABLE_ERR ENCODING_ERR "
|
53 |
+
+ "NO_MODIFICATION_ALLOWED_ERR INVALID_STATE_ERR SYNTAX_ERR"
|
54 |
+
).split(" ")
|
55 |
+
, file_ex_code = file_ex_codes.length
|
56 |
+
, real_URL = view.URL || view.webkitURL || view
|
57 |
+
, real_create_object_URL = real_URL.createObjectURL
|
58 |
+
, real_revoke_object_URL = real_URL.revokeObjectURL
|
59 |
+
, URL = real_URL
|
60 |
+
, btoa = view.btoa
|
61 |
+
, atob = view.atob
|
62 |
+
|
63 |
+
, ArrayBuffer = view.ArrayBuffer
|
64 |
+
, Uint8Array = view.Uint8Array
|
65 |
+
|
66 |
+
, origin = /^[\w-]+:\/*\[?[\w\.:-]+\]?(?::[0-9]+)?/
|
67 |
+
;
|
68 |
+
FakeBlob.fake = FB_proto.fake = true;
|
69 |
+
while (file_ex_code--) {
|
70 |
+
FileException.prototype[file_ex_codes[file_ex_code]] = file_ex_code + 1;
|
71 |
+
}
|
72 |
+
// Polyfill URL
|
73 |
+
if (!real_URL.createObjectURL) {
|
74 |
+
URL = view.URL = function(uri) {
|
75 |
+
var
|
76 |
+
uri_info = document.createElementNS("http://www.w3.org/1999/xhtml", "a")
|
77 |
+
, uri_origin
|
78 |
+
;
|
79 |
+
uri_info.href = uri;
|
80 |
+
if (!("origin" in uri_info)) {
|
81 |
+
if (uri_info.protocol.toLowerCase() === "data:") {
|
82 |
+
uri_info.origin = null;
|
83 |
+
} else {
|
84 |
+
uri_origin = uri.match(origin);
|
85 |
+
uri_info.origin = uri_origin && uri_origin[1];
|
86 |
+
}
|
87 |
+
}
|
88 |
+
return uri_info;
|
89 |
+
};
|
90 |
+
}
|
91 |
+
URL.createObjectURL = function(blob) {
|
92 |
+
var
|
93 |
+
type = blob.type
|
94 |
+
, data_URI_header
|
95 |
+
;
|
96 |
+
if (type === null) {
|
97 |
+
type = "application/octet-stream";
|
98 |
+
}
|
99 |
+
if (blob instanceof FakeBlob) {
|
100 |
+
data_URI_header = "data:" + type;
|
101 |
+
if (blob.encoding === "base64") {
|
102 |
+
return data_URI_header + ";base64," + blob.data;
|
103 |
+
} else if (blob.encoding === "URI") {
|
104 |
+
return data_URI_header + "," + decodeURIComponent(blob.data);
|
105 |
+
} if (btoa) {
|
106 |
+
return data_URI_header + ";base64," + btoa(blob.data);
|
107 |
+
} else {
|
108 |
+
return data_URI_header + "," + encodeURIComponent(blob.data);
|
109 |
+
}
|
110 |
+
} else if (real_create_object_URL) {
|
111 |
+
return real_create_object_URL.call(real_URL, blob);
|
112 |
+
}
|
113 |
+
};
|
114 |
+
URL.revokeObjectURL = function(object_URL) {
|
115 |
+
if (object_URL.substring(0, 5) !== "data:" && real_revoke_object_URL) {
|
116 |
+
real_revoke_object_URL.call(real_URL, object_URL);
|
117 |
+
}
|
118 |
+
};
|
119 |
+
FBB_proto.append = function(data/*, endings*/) {
|
120 |
+
var bb = this.data;
|
121 |
+
// decode data to a binary string
|
122 |
+
if (Uint8Array && (data instanceof ArrayBuffer || data instanceof Uint8Array)) {
|
123 |
+
var
|
124 |
+
str = ""
|
125 |
+
, buf = new Uint8Array(data)
|
126 |
+
, i = 0
|
127 |
+
, buf_len = buf.length
|
128 |
+
;
|
129 |
+
for (; i < buf_len; i++) {
|
130 |
+
str += String.fromCharCode(buf[i]);
|
131 |
+
}
|
132 |
+
bb.push(str);
|
133 |
+
} else if (get_class(data) === "Blob" || get_class(data) === "File") {
|
134 |
+
if (FileReaderSync) {
|
135 |
+
var fr = new FileReaderSync;
|
136 |
+
bb.push(fr.readAsBinaryString(data));
|
137 |
+
} else {
|
138 |
+
// async FileReader won't work as BlobBuilder is sync
|
139 |
+
throw new FileException("NOT_READABLE_ERR");
|
140 |
+
}
|
141 |
+
} else if (data instanceof FakeBlob) {
|
142 |
+
if (data.encoding === "base64" && atob) {
|
143 |
+
bb.push(atob(data.data));
|
144 |
+
} else if (data.encoding === "URI") {
|
145 |
+
bb.push(decodeURIComponent(data.data));
|
146 |
+
} else if (data.encoding === "raw") {
|
147 |
+
bb.push(data.data);
|
148 |
+
}
|
149 |
+
} else {
|
150 |
+
if (typeof data !== "string") {
|
151 |
+
data += ""; // convert unsupported types to strings
|
152 |
+
}
|
153 |
+
// decode UTF-16 to binary string
|
154 |
+
bb.push(unescape(encodeURIComponent(data)));
|
155 |
+
}
|
156 |
+
};
|
157 |
+
FBB_proto.getBlob = function(type) {
|
158 |
+
if (!arguments.length) {
|
159 |
+
type = null;
|
160 |
+
}
|
161 |
+
return new FakeBlob(this.data.join(""), type, "raw");
|
162 |
+
};
|
163 |
+
FBB_proto.toString = function() {
|
164 |
+
return "[object BlobBuilder]";
|
165 |
+
};
|
166 |
+
FB_proto.slice = function(start, end, type) {
|
167 |
+
var args = arguments.length;
|
168 |
+
if (args < 3) {
|
169 |
+
type = null;
|
170 |
+
}
|
171 |
+
return new FakeBlob(
|
172 |
+
this.data.slice(start, args > 1 ? end : this.data.length)
|
173 |
+
, type
|
174 |
+
, this.encoding
|
175 |
+
);
|
176 |
+
};
|
177 |
+
FB_proto.toString = function() {
|
178 |
+
return "[object Blob]";
|
179 |
+
};
|
180 |
+
FB_proto.close = function() {
|
181 |
+
this.size = 0;
|
182 |
+
delete this.data;
|
183 |
+
};
|
184 |
+
return FakeBlobBuilder;
|
185 |
+
}(view));
|
186 |
+
|
187 |
+
view.Blob = function(blobParts, options) {
|
188 |
+
var type = options ? (options.type || "") : "";
|
189 |
+
var builder = new BlobBuilder();
|
190 |
+
if (blobParts) {
|
191 |
+
for (var i = 0, len = blobParts.length; i < len; i++) {
|
192 |
+
builder.append(blobParts[i]);
|
193 |
+
}
|
194 |
+
}
|
195 |
+
return builder.getBlob(type);
|
196 |
+
};
|
197 |
+
}(typeof self !== "undefined" && self || typeof window !== "undefined" && window || this.content || this));
|
FileSaver.min.js
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
|
2 |
+
var saveAs=saveAs||"undefined"!==typeof navigator&&navigator.msSaveOrOpenBlob&&navigator.msSaveOrOpenBlob.bind(navigator)||function(a){"use strict";if("undefined"===typeof navigator||!/MSIE [1-9]\./.test(navigator.userAgent)){var k=a.document,n=k.createElementNS("http://www.w3.org/1999/xhtml","a"),w="download"in n,x=function(c){var e=k.createEvent("MouseEvents");e.initMouseEvent("click",!0,!1,a,0,0,0,0,0,!1,!1,!1,!1,0,null);c.dispatchEvent(e)},q=a.webkitRequestFileSystem,u=a.requestFileSystem||q||a.mozRequestFileSystem,
|
3 |
+
y=function(c){(a.setImmediate||a.setTimeout)(function(){throw c;},0)},r=0,s=function(c){var e=function(){"string"===typeof c?(a.URL||a.webkitURL||a).revokeObjectURL(c):c.remove()};a.chrome?e():setTimeout(e,10)},t=function(c,a,d){a=[].concat(a);for(var b=a.length;b--;){var l=c["on"+a[b]];if("function"===typeof l)try{l.call(c,d||c)}catch(f){y(f)}}},m=function(c,e){var d=this,b=c.type,l=!1,f,p,k=function(){t(d,["writestart","progress","write","writeend"])},g=function(){if(l||!f)f=(a.URL||a.webkitURL||
|
4 |
+
a).createObjectURL(c);p?p.location.href=f:void 0==a.open(f,"_blank")&&"undefined"!==typeof safari&&(a.location.href=f);d.readyState=d.DONE;k();s(f)},h=function(a){return function(){if(d.readyState!==d.DONE)return a.apply(this,arguments)}},m={create:!0,exclusive:!1},v;d.readyState=d.INIT;e||(e="download");if(w)f=(a.URL||a.webkitURL||a).createObjectURL(c),n.href=f,n.download=e,x(n),d.readyState=d.DONE,k(),s(f);else{a.chrome&&b&&"application/octet-stream"!==b&&(v=c.slice||c.webkitSlice,c=v.call(c,0,
|
5 |
+
c.size,"application/octet-stream"),l=!0);q&&"download"!==e&&(e+=".download");if("application/octet-stream"===b||q)p=a;u?(r+=c.size,u(a.TEMPORARY,r,h(function(a){a.root.getDirectory("saved",m,h(function(a){var b=function(){a.getFile(e,m,h(function(a){a.createWriter(h(function(b){b.onwriteend=function(b){p.location.href=a.toURL();d.readyState=d.DONE;t(d,"writeend",b);s(a)};b.onerror=function(){var a=b.error;a.code!==a.ABORT_ERR&&g()};["writestart","progress","write","abort"].forEach(function(a){b["on"+
|
6 |
+
a]=d["on"+a]});b.write(c);d.abort=function(){b.abort();d.readyState=d.DONE};d.readyState=d.WRITING}),g)}),g)};a.getFile(e,{create:!1},h(function(a){a.remove();b()}),h(function(a){a.code===a.NOT_FOUND_ERR?b():g()}))}),g)}),g)):g()}},b=m.prototype;b.abort=function(){this.readyState=this.DONE;t(this,"abort")};b.readyState=b.INIT=0;b.WRITING=1;b.DONE=2;b.error=b.onwritestart=b.onprogress=b.onwrite=b.onabort=b.onerror=b.onwriteend=null;return function(a,b){return new m(a,b)}}}("undefined"!==typeof self&&
|
7 |
+
self||"undefined"!==typeof window&&window||this.content);"undefined"!==typeof module&&null!==module?module.exports=saveAs:"undefined"!==typeof define&&null!==define&&null!=define.amd&&define([],function(){return saveAs});
|
animalese.js
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// animalese.js
|
2 |
+
// (C) 2014 Josh Simmons
|
3 |
+
// http://github.com/acedio/animalese.js
|
4 |
+
|
5 |
+
var Animalese = function(letters_file, onload) {
|
6 |
+
this.Animalese = function(script, shorten, pitch) {
|
7 |
+
function shortenWord(str) {
|
8 |
+
if (str.length > 1) {
|
9 |
+
return str[0] + str[str.length - 1];
|
10 |
+
}
|
11 |
+
return str;
|
12 |
+
}
|
13 |
+
|
14 |
+
var processed_script = script;
|
15 |
+
if (shorten) {
|
16 |
+
processed_script =
|
17 |
+
script.replace(/[^a-z]/gi, ' ').split(' ').map(shortenWord).join('');
|
18 |
+
}
|
19 |
+
|
20 |
+
var data = [];
|
21 |
+
|
22 |
+
var sample_freq = 44100;
|
23 |
+
var library_letter_secs = 0.15;
|
24 |
+
var library_samples_per_letter =
|
25 |
+
Math.floor(library_letter_secs * sample_freq);
|
26 |
+
var output_letter_secs = 0.075;
|
27 |
+
var output_samples_per_letter =
|
28 |
+
Math.floor(output_letter_secs * sample_freq);
|
29 |
+
|
30 |
+
for (var c_index = 0; c_index < processed_script.length; c_index++) {
|
31 |
+
var c = processed_script.toUpperCase()[c_index];
|
32 |
+
if (c >= 'A' && c <= 'Z') {
|
33 |
+
var library_letter_start =
|
34 |
+
library_samples_per_letter * (c.charCodeAt(0) - 'A'.charCodeAt(0));
|
35 |
+
|
36 |
+
for (var i = 0; i < output_samples_per_letter; i++) {
|
37 |
+
data[c_index * output_samples_per_letter + i] =
|
38 |
+
this.letter_library[44 + library_letter_start + Math.floor(i * pitch)];
|
39 |
+
}
|
40 |
+
} else { // non pronouncable character or space
|
41 |
+
for (var i = 0; i < output_samples_per_letter; i++) {
|
42 |
+
data[c_index * output_samples_per_letter + i] = 127;
|
43 |
+
}
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
var wave = new RIFFWAVE();
|
48 |
+
wave.header.sampleRate = sample_freq;
|
49 |
+
wave.header.numChannels = 1;
|
50 |
+
wave.Make(data);
|
51 |
+
|
52 |
+
return wave;
|
53 |
+
}
|
54 |
+
|
55 |
+
var xhr = new XMLHttpRequest();
|
56 |
+
xhr.open('GET', letters_file);
|
57 |
+
xhr.responseType = 'arraybuffer';
|
58 |
+
var req = this;
|
59 |
+
xhr.onload = function(e) {
|
60 |
+
req.letter_library = new Uint8Array(this.response);
|
61 |
+
onload();
|
62 |
+
}
|
63 |
+
xhr.send();
|
64 |
+
}
|
animalese.wav
ADDED
Binary file (172 kB). View file
|
|
index.html
CHANGED
@@ -1,19 +1,62 @@
|
|
1 |
-
<!doctype html>
|
2 |
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
</html>
|
|
|
|
|
1 |
<html>
|
2 |
+
<head>
|
3 |
+
<title>animalese.js Demo</title>
|
4 |
+
</head>
|
5 |
+
<script src="riffwave.js"></script>
|
6 |
+
<script src="Blob.js"></script>
|
7 |
+
<script src="FileSaver.min.js"></script>
|
8 |
+
<script src="animalese.js"></script>
|
9 |
+
<script>
|
10 |
+
function dataURItoBlob(dataURI) {
|
11 |
+
// convert base64/URLEncoded data component to raw binary data held in a string
|
12 |
+
var byteString;
|
13 |
+
if (dataURI.split(',')[0].indexOf('base64') >= 0)
|
14 |
+
byteString = atob(dataURI.split(',')[1]);
|
15 |
+
else
|
16 |
+
byteString = unescape(dataURI.split(',')[1]);
|
17 |
+
|
18 |
+
// separate out the mime component
|
19 |
+
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
|
20 |
+
|
21 |
+
// write the bytes of the string to a typed array
|
22 |
+
var ia = new Uint8Array(byteString.length);
|
23 |
+
for (var i = 0; i < byteString.length; i++) {
|
24 |
+
ia[i] = byteString.charCodeAt(i);
|
25 |
+
}
|
26 |
+
|
27 |
+
return new Blob([ia], {type:mimeString});
|
28 |
+
}
|
29 |
+
|
30 |
+
var synth = new Animalese('animalese.wav', function() {
|
31 |
+
document.getElementById("preview").disabled = false;
|
32 |
+
document.getElementById("download").disabled = false;
|
33 |
+
});
|
34 |
+
|
35 |
+
function generateWav() {
|
36 |
+
return synth.Animalese(document.getElementById("text").value,
|
37 |
+
document.getElementById("shorten").checked,
|
38 |
+
document.getElementById("pitch").value).dataURI;
|
39 |
+
}
|
40 |
+
|
41 |
+
function preview() {
|
42 |
+
var audio = new Audio();
|
43 |
+
audio.src = generateWav();
|
44 |
+
audio.play();
|
45 |
+
}
|
46 |
+
|
47 |
+
function download() {
|
48 |
+
var wave = generateWav();
|
49 |
+
saveAs(dataURItoBlob(wave), "animalese.wav");
|
50 |
+
}
|
51 |
+
|
52 |
+
</script>
|
53 |
+
<body>
|
54 |
+
<h2>animalese.js Demo</h2>
|
55 |
+
<p>More information and the source at <a href="https://github.com/Acedio/animalese.js">https://github.com/Acedio/animalese.js</a>.</p>
|
56 |
+
<textarea id="text" rows"4" cols="50">Testing out animalese.js. Did it work?</textarea><br/>
|
57 |
+
<label>Shorten words<input id="shorten" type="checkbox"/></label><br/>
|
58 |
+
<label>Grump<input id="pitch" type="range" min="0.2" max="2.0" value="1.0" step="0.1"/>Isabelle</label><br/>
|
59 |
+
<button id="preview" type="button" disabled="true" onclick="preview()">Preview!</button>
|
60 |
+
<button id="download" type="button" disabled="false" onclick="download()">Download!</button>
|
61 |
+
</body>
|
62 |
</html>
|
riffwave.js
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/*
|
2 |
+
* RIFFWAVE.js v0.03 - Audio encoder for HTML5 <audio> elements.
|
3 |
+
* Copyleft 2011 by Pedro Ladaria <pedro.ladaria at Gmail dot com>
|
4 |
+
*
|
5 |
+
* Public Domain
|
6 |
+
*
|
7 |
+
* Changelog:
|
8 |
+
*
|
9 |
+
* 0.01 - First release
|
10 |
+
* 0.02 - New faster base64 encoding
|
11 |
+
* 0.03 - Support for 16bit samples
|
12 |
+
*
|
13 |
+
* Notes:
|
14 |
+
*
|
15 |
+
* 8 bit data is unsigned: 0..255
|
16 |
+
* 16 bit data is signed: −32,768..32,767
|
17 |
+
*
|
18 |
+
*/
|
19 |
+
|
20 |
+
var FastBase64 = {
|
21 |
+
|
22 |
+
chars: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
|
23 |
+
encLookup: [],
|
24 |
+
|
25 |
+
Init: function() {
|
26 |
+
for (var i=0; i<4096; i++) {
|
27 |
+
this.encLookup[i] = this.chars[i >> 6] + this.chars[i & 0x3F];
|
28 |
+
}
|
29 |
+
},
|
30 |
+
|
31 |
+
Encode: function(src) {
|
32 |
+
var len = src.length;
|
33 |
+
var dst = '';
|
34 |
+
var i = 0;
|
35 |
+
while (len > 2) {
|
36 |
+
n = (src[i] << 16) | (src[i+1]<<8) | src[i+2];
|
37 |
+
dst+= this.encLookup[n >> 12] + this.encLookup[n & 0xFFF];
|
38 |
+
len-= 3;
|
39 |
+
i+= 3;
|
40 |
+
}
|
41 |
+
if (len > 0) {
|
42 |
+
var n1= (src[i] & 0xFC) >> 2;
|
43 |
+
var n2= (src[i] & 0x03) << 4;
|
44 |
+
if (len > 1) n2 |= (src[++i] & 0xF0) >> 4;
|
45 |
+
dst+= this.chars[n1];
|
46 |
+
dst+= this.chars[n2];
|
47 |
+
if (len == 2) {
|
48 |
+
var n3= (src[i++] & 0x0F) << 2;
|
49 |
+
n3 |= (src[i] & 0xC0) >> 6;
|
50 |
+
dst+= this.chars[n3];
|
51 |
+
}
|
52 |
+
if (len == 1) dst+= '=';
|
53 |
+
dst+= '=';
|
54 |
+
}
|
55 |
+
return dst;
|
56 |
+
} // end Encode
|
57 |
+
|
58 |
+
}
|
59 |
+
|
60 |
+
FastBase64.Init();
|
61 |
+
|
62 |
+
var RIFFWAVE = function(data) {
|
63 |
+
|
64 |
+
this.data = []; // Array containing audio samples
|
65 |
+
this.wav = []; // Array containing the generated wave file
|
66 |
+
this.dataURI = ''; // http://en.wikipedia.org/wiki/Data_URI_scheme
|
67 |
+
|
68 |
+
this.header = { // OFFS SIZE NOTES
|
69 |
+
chunkId : [0x52,0x49,0x46,0x46], // 0 4 "RIFF" = 0x52494646
|
70 |
+
chunkSize : 0, // 4 4 36+SubChunk2Size = 4+(8+SubChunk1Size)+(8+SubChunk2Size)
|
71 |
+
format : [0x57,0x41,0x56,0x45], // 8 4 "WAVE" = 0x57415645
|
72 |
+
subChunk1Id : [0x66,0x6d,0x74,0x20], // 12 4 "fmt " = 0x666d7420
|
73 |
+
subChunk1Size: 16, // 16 4 16 for PCM
|
74 |
+
audioFormat : 1, // 20 2 PCM = 1
|
75 |
+
numChannels : 1, // 22 2 Mono = 1, Stereo = 2...
|
76 |
+
sampleRate : 8000, // 24 4 8000, 44100...
|
77 |
+
byteRate : 0, // 28 4 SampleRate*NumChannels*BitsPerSample/8
|
78 |
+
blockAlign : 0, // 32 2 NumChannels*BitsPerSample/8
|
79 |
+
bitsPerSample: 8, // 34 2 8 bits = 8, 16 bits = 16
|
80 |
+
subChunk2Id : [0x64,0x61,0x74,0x61], // 36 4 "data" = 0x64617461
|
81 |
+
subChunk2Size: 0 // 40 4 data size = NumSamples*NumChannels*BitsPerSample/8
|
82 |
+
};
|
83 |
+
|
84 |
+
function u32ToArray(i) {
|
85 |
+
return [i&0xFF, (i>>8)&0xFF, (i>>16)&0xFF, (i>>24)&0xFF];
|
86 |
+
}
|
87 |
+
|
88 |
+
function u16ToArray(i) {
|
89 |
+
return [i&0xFF, (i>>8)&0xFF];
|
90 |
+
}
|
91 |
+
|
92 |
+
function split16bitArray(data) {
|
93 |
+
var r = [];
|
94 |
+
var j = 0;
|
95 |
+
var len = data.length;
|
96 |
+
for (var i=0; i<len; i++) {
|
97 |
+
r[j++] = data[i] & 0xFF;
|
98 |
+
r[j++] = (data[i]>>8) & 0xFF;
|
99 |
+
}
|
100 |
+
return r;
|
101 |
+
}
|
102 |
+
|
103 |
+
this.Make = function(data) {
|
104 |
+
if (data instanceof Array) this.data = data;
|
105 |
+
this.header.blockAlign = (this.header.numChannels * this.header.bitsPerSample) >> 3;
|
106 |
+
this.header.byteRate = this.header.blockAlign * this.sampleRate;
|
107 |
+
this.header.subChunk2Size = this.data.length * (this.header.bitsPerSample >> 3);
|
108 |
+
this.header.chunkSize = 36 + this.header.subChunk2Size;
|
109 |
+
|
110 |
+
this.wav = this.header.chunkId.concat(
|
111 |
+
u32ToArray(this.header.chunkSize),
|
112 |
+
this.header.format,
|
113 |
+
this.header.subChunk1Id,
|
114 |
+
u32ToArray(this.header.subChunk1Size),
|
115 |
+
u16ToArray(this.header.audioFormat),
|
116 |
+
u16ToArray(this.header.numChannels),
|
117 |
+
u32ToArray(this.header.sampleRate),
|
118 |
+
u32ToArray(this.header.byteRate),
|
119 |
+
u16ToArray(this.header.blockAlign),
|
120 |
+
u16ToArray(this.header.bitsPerSample),
|
121 |
+
this.header.subChunk2Id,
|
122 |
+
u32ToArray(this.header.subChunk2Size),
|
123 |
+
(this.header.bitsPerSample == 16) ? split16bitArray(this.data) : this.data
|
124 |
+
);
|
125 |
+
this.dataURI = 'data:audio/wav;base64,'+FastBase64.Encode(this.wav);
|
126 |
+
};
|
127 |
+
|
128 |
+
if (data instanceof Array) this.Make(data);
|
129 |
+
|
130 |
+
}; // end RIFFWAVE
|
131 |
+
|
132 |
+
|