You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
4.5KB

  1. /*
  2. TODO
  3. non-44100 sample rates
  4. u-law encoding
  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. fileReader.readAsArrayBuffer(audioFileChooser.files[i]);
  12. fileReader.addEventListener('load', function(fileName, ev) {
  13. processFile(ev.target.result, fileName);
  14. }.bind(null, audioFileChooser.files[i].name));
  15. }
  16. }
  17. function processFile(file, fileName) {
  18. var context = new OfflineAudioContext(1,100*44100,44100); // 100 seconds for now
  19. context.decodeAudioData(file, function(buffer) {
  20. console.log(buffer.sampleRate);
  21. var monoData = [];
  22. if(buffer.numberOfChannels == 1) {
  23. monoData = buffer.getChannelData(0);
  24. } else if(buffer.numberOfChannels == 2) {
  25. var leftData = buffer.getChannelData(0);
  26. var rightData = buffer.getChannelData(1);
  27. for(var i=0;i<buffer.length;i++) {
  28. monoData[i] = (leftData[i] + rightData[i]) / 2;
  29. }
  30. } else {
  31. alert("ONLY MONO AND STEREO FILES ARE SUPPORTED");
  32. }
  33. var outputData = [];
  34. for(var i=0;i<monoData.length;i+=2) {
  35. var a = toUint16(monoData[i]*0x7fff);
  36. var b = toUint16(monoData[i+1]*0x7fff);
  37. var out = (65536*b + a).toString(16);
  38. while(out.length < 8) out = '0' + out;
  39. out = '0x' + out;
  40. outputData.push(out);
  41. }
  42. var padLength = padding(outputData.length, 128);
  43. var statusInt = (outputData.length*2).toString(16);
  44. while(statusInt.length < 6) statusInt = '0' + statusInt;
  45. if(outputData.length*2>0xFFFFFF) alert("DATA TOO LONG");
  46. statusInt = '0x81' + statusInt;
  47. outputData.unshift(statusInt);
  48. for(var i=0;i<padLength;i++) {
  49. outputData.push('0x00000000');
  50. }
  51. var outputFileHolder = document.getElementById('outputFileHolder');
  52. var downloadLink1 = document.createElement('a');
  53. var downloadLink2 = document.createElement('a');
  54. var formattedName = fileName.split('.')[0];
  55. formattedName = formattedName.charAt(0).toUpperCase() + formattedName.slice(1).toLowerCase();
  56. downloadLink1.href = generateOutputFile(generateCPPFile(fileName, formattedName, outputData));
  57. downloadLink1.setAttribute('download', 'AudioSample' + formattedName + '.cpp');
  58. downloadLink1.innerHTML = 'Download AudioSample' + formattedName + '.cpp';
  59. downloadLink2.href = generateOutputFile(generateHeaderFile(formattedName, outputData));
  60. downloadLink2.setAttribute('download', 'AudioSample' + formattedName + '.h');
  61. downloadLink2.innerHTML = 'Download AudioSample' + formattedName + '.h';
  62. outputFileHolder.appendChild(downloadLink1);
  63. outputFileHolder.appendChild(document.createElement('br'));
  64. outputFileHolder.appendChild(downloadLink2);
  65. outputFileHolder.appendChild(document.createElement('br'));
  66. });
  67. }
  68. // http://2ality.com/2012/02/js-integers.html
  69. function toInteger(x) {
  70. x = Number(x);
  71. return Math.round(x);
  72. //return x < 0 ? Math.ceil(x) : Math.floor(x);
  73. }
  74. function modulo(a, b) {
  75. return a - Math.floor(a/b)*b;
  76. }
  77. function toUint16(x) {
  78. return modulo(toInteger(x), Math.pow(2, 16));
  79. }
  80. // compute the extra padding needed
  81. function padding(sampleLength, block) {
  82. var extra = sampleLength % block;
  83. if (extra == 0) return 0;
  84. return block - extra;
  85. }
  86. function generateOutputFile(fileContents) {
  87. var textFileURL = null;
  88. var blob = new Blob([fileContents], {type: 'text/plain'});
  89. textFileURL = window.URL.createObjectURL(blob);
  90. return textFileURL;
  91. }
  92. function formatAudioData(audioData) {
  93. var outputString = '';
  94. for(var i = 0; i < audioData.length; i ++) {
  95. if(i%8==0 && i>0) outputString += '\n'
  96. outputString += audioData[i] + ',';
  97. }
  98. return outputString;
  99. }
  100. function generateCPPFile(fileName, formattedName, audioData) {
  101. var formattedName = fileName.split('.')[0];
  102. formattedName = formattedName.charAt(0).toUpperCase() + formattedName.slice(1).toLowerCase();
  103. var out = "";
  104. out += '// Audio data converted from audio file by wav2sketch_js\n\n';
  105. out += '#include "AudioSample' + formattedName + '.h"\n\n';
  106. out += 'const unsigned int AudioSample' + formattedName + '[' + audioData.length + '] = {\n';
  107. out += formatAudioData(audioData) + '\n};';
  108. return out;
  109. }
  110. function generateHeaderFile(formattedName, audioData) {
  111. var out = "";
  112. out += '// Audio data converted from audio file by wav2sketch_js\n\n';
  113. out += 'extern const unsigned int AudioSample' + formattedName + '[' + audioData.length + '];';
  114. return out;
  115. }