您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

166 行
5.8KB

  1. /*
  2. TODO
  3. u-law encoding
  4. browser feature check (offlineaudiocontext might still be a bit niche)
  5. */
  6. var audioFileChooser = document.getElementById('audioFileChooser');
  7. audioFileChooser.addEventListener('change', readFile);
  8. function readFile() {
  9. for(var i = 0; i < audioFileChooser.files.length; i++) {
  10. var fileReader = new FileReader();
  11. var sampleRateChoice = document.getElementById('sampleRate').value;
  12. if(sampleRateChoice == "auto") sampleRateChoice = null;
  13. else sampleRateChoice = parseInt(sampleRateChoice);
  14. fileReader.readAsArrayBuffer(audioFileChooser.files[i]);
  15. fileReader.addEventListener('load', function(fileName, ev) {
  16. processFile(ev.target.result, fileName, sampleRateChoice);
  17. }.bind(null, audioFileChooser.files[i].name));
  18. }
  19. }
  20. function processFile(file, fileName, sampleRateChoice) {
  21. // determine sample rate
  22. // ideas borrowed from https://github.com/ffdead/wav.js
  23. var sampleRate = 0;
  24. if(!sampleRateChoice) {
  25. try {
  26. var sampleRateBytes = new Uint8Array(file, 24, 4);
  27. for(var i = 0; i < sampleRateBytes.length; i ++) {
  28. sampleRate |= sampleRateBytes[i] << (i*8);
  29. }
  30. } catch(err) {
  31. console.log('problem reading sample rate');
  32. }
  33. if([44100, 22050, 11025].indexOf(sampleRate) == -1) {
  34. sampleRate = 44100;
  35. }
  36. } else {
  37. console.log('using chosen sample rate of ' + sampleRateChoice);
  38. sampleRate = sampleRateChoice;
  39. }
  40. var context = new OfflineAudioContext(1, 100*sampleRate, sampleRate); // 100 seconds for now
  41. context.decodeAudioData(file, function(buffer) {
  42. console.log(buffer.sampleRate);
  43. var monoData = [];
  44. if(buffer.numberOfChannels == 1) {
  45. monoData = buffer.getChannelData(0);
  46. } else if(buffer.numberOfChannels == 2) {
  47. var leftData = buffer.getChannelData(0);
  48. var rightData = buffer.getChannelData(1);
  49. for(var i=0;i<buffer.length;i++) {
  50. monoData[i] = (leftData[i] + rightData[i]) / 2;
  51. }
  52. } else {
  53. alert("ONLY MONO AND STEREO FILES ARE SUPPORTED");
  54. // NB - would be trivial to add support for n channels, given that everything ends up mono anyway
  55. }
  56. var outputData = [];
  57. for(var i=0;i<monoData.length;i+=2) {
  58. var a = toUint16(monoData[i]*0x7fff);
  59. var b = toUint16(monoData[i+1]*0x7fff);
  60. var out = (65536*b + a).toString(16);
  61. while(out.length < 8) out = '0' + out;
  62. out = '0x' + out;
  63. outputData.push(out);
  64. }
  65. var padLength;
  66. var compressionCode = '0';
  67. var sampleRateCode;
  68. if(true) compressionCode = '8'; // add u-law support here later
  69. if(sampleRate == 44100) {
  70. padLength = padding(outputData.length/2, 128);
  71. sampleRateCode = '1';
  72. } else if(sampleRate == 22050) {
  73. padLength = padding(outputData.length/2, 64);
  74. sampleRateCode = '2';
  75. } else if(sampleRate == 11025) {
  76. padLength = padding(outputData.length/2, 32);
  77. sampleRateCode = '3';
  78. }
  79. var statusInt = (outputData.length*2).toString(16);
  80. while(statusInt.length < 6) statusInt = '0' + statusInt;
  81. if(outputData.length*2>0xFFFFFF) alert("DATA TOO LONG");
  82. statusInt = '0x' + compressionCode + sampleRateCode + statusInt;
  83. outputData.unshift(statusInt);
  84. for(var i=0;i<padLength/2;i++) {
  85. outputData.push('0x00000000');
  86. }
  87. var outputFileHolder = document.getElementById('outputFileHolder');
  88. var downloadLink1 = document.createElement('a');
  89. var downloadLink2 = document.createElement('a');
  90. var formattedName = fileName.split('.')[0];
  91. formattedName = formattedName.charAt(0).toUpperCase() + formattedName.slice(1).toLowerCase();
  92. downloadLink1.href = generateOutputFile(generateCPPFile(fileName, formattedName, outputData));
  93. downloadLink1.setAttribute('download', 'AudioSample' + formattedName + '.cpp');
  94. downloadLink1.innerHTML = 'Download AudioSample' + formattedName + '.cpp';
  95. downloadLink2.href = generateOutputFile(generateHeaderFile(formattedName, outputData));
  96. downloadLink2.setAttribute('download', 'AudioSample' + formattedName + '.h');
  97. downloadLink2.innerHTML = 'Download AudioSample' + formattedName + '.h';
  98. outputFileHolder.appendChild(downloadLink1);
  99. outputFileHolder.appendChild(document.createElement('br'));
  100. outputFileHolder.appendChild(downloadLink2);
  101. outputFileHolder.appendChild(document.createElement('br'));
  102. });
  103. }
  104. // http://2ality.com/2012/02/js-integers.html
  105. function toInteger(x) {
  106. x = Number(x);
  107. return Math.round(x);
  108. //return x < 0 ? Math.ceil(x) : Math.floor(x);
  109. }
  110. function modulo(a, b) {
  111. return a - Math.floor(a/b)*b;
  112. }
  113. function toUint16(x) {
  114. return modulo(toInteger(x), Math.pow(2, 16));
  115. }
  116. // compute the extra padding needed
  117. function padding(sampleLength, block) {
  118. var extra = sampleLength % block;
  119. if (extra == 0) return 0;
  120. return block - extra;
  121. }
  122. function generateOutputFile(fileContents) {
  123. var textFileURL = null;
  124. var blob = new Blob([fileContents], {type: 'text/plain'});
  125. textFileURL = window.URL.createObjectURL(blob);
  126. return textFileURL;
  127. }
  128. function formatAudioData(audioData) {
  129. var outputString = '';
  130. for(var i = 0; i < audioData.length; i ++) {
  131. if(i%8==0 && i>0) outputString += '\n';
  132. outputString += audioData[i] + ',';
  133. }
  134. return outputString;
  135. }
  136. function generateCPPFile(fileName, formattedName, audioData) {
  137. var out = "";
  138. out += '// Audio data converted from audio file by wav2sketch_js\n\n';
  139. out += '#include "AudioSample' + formattedName + '.h"\n\n';
  140. out += 'const unsigned int AudioSample' + formattedName + '[' + audioData.length + '] = {\n';
  141. out += formatAudioData(audioData) + '\n};';
  142. return out;
  143. }
  144. function generateHeaderFile(formattedName, audioData) {
  145. var out = "";
  146. out += '// Audio data converted from audio file by wav2sketch_js\n\n';
  147. out += 'extern const unsigned int AudioSample' + formattedName + '[' + audioData.length + '];';
  148. return out;
  149. }