{"id":13439373,"url":"https://github.com/daaain/JSSoundRecorder","last_synced_at":"2025-03-20T08:30:38.875Z","repository":{"id":5536884,"uuid":"6740195","full_name":"daaain/JSSoundRecorder","owner":"daaain","description":"A simple JavaScript sound recorder and editor","archived":false,"fork":false,"pushed_at":"2017-12-29T23:14:30.000Z","size":204,"stargazers_count":349,"open_issues_count":5,"forks_count":85,"subscribers_count":39,"default_branch":"gh-pages","last_synced_at":"2024-10-22T19:20:02.348Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/daaain.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-11-17T21:19:07.000Z","updated_at":"2024-10-13T16:11:04.000Z","dependencies_parsed_at":"2022-08-25T23:13:16.951Z","dependency_job_id":null,"html_url":"https://github.com/daaain/JSSoundRecorder","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daaain%2FJSSoundRecorder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daaain%2FJSSoundRecorder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daaain%2FJSSoundRecorder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/daaain%2FJSSoundRecorder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/daaain","download_url":"https://codeload.github.com/daaain/JSSoundRecorder/tar.gz/refs/heads/gh-pages","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221739700,"owners_count":16872775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T03:01:13.370Z","updated_at":"2024-10-27T22:30:48.029Z","avatar_url":"https://github.com/daaain.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"JSSoundRecorder\n===============\n\nRecord sounds / noises around you and turn them into music.\n\nIt’s a work in progress, at the moment it enables you to record live audio straight from your browser, edit it and save these sounds as a WAV file.\n\nThere's also a sequencer part where you can create small loops using these sounds with a drone synth overlaid on them.\n\nSee it working: http://daaain.github.com/JSSoundRecorder\n\nTechnology\n----------\n\nNo servers involved, only Web Audio API with binary sound Blobs passed around!\n\n### Web Audio API\n\n#### GetUserMedia audio for live recording\n\nExperimental API to record any system audio input (including USB soundcards, musical instruments, etc).\n\n```javascript\n// shim and create AudioContext\nwindow.AudioContext = window.AudioContext || window.webkitAudioContext || window.mozAudioContext;\nvar audio_context = new AudioContext();\n\n// shim and start GetUserMedia audio stream\nnavigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;\nnavigator.getUserMedia({audio: true}, startUserMedia, function(e) {\n  console.log('No live audio input: ' + e);\n});\n```\n\n#### Audio nodes for routing\n\nYou can route audio stream around, with input nodes (microphone, synths, etc), filters (volume / gain, equaliser, low pass, etc) and outputs (speakers, binary streams, etc).\n\n```javascript\nfunction startUserMedia(stream) {\n  // create MediaStreamSource and GainNode\n  var input = audio_context.createMediaStreamSource(stream);\n  var volume = audio_context.createGain();\n  volume.gain.value = 0.7;\n\n  // connect them and pipe output\n  input.connect(volume);\n  volume.connect(audio_context.destination);\n  \n  // connect recorder as well - see below\n  var recorder = new Recorder(input);\n}\n```\n\n### WebWorker\n\nProcessing (interleaving) record buffer is done in the background to not block the main thread and the UI.\n\nAlso WAV conversion for export is also quite heavy for longer recordings, so best left to run in the background.\n\n```javascript\nthis.context = input.context;\nthis.node = this.context.createScriptProcessor(4096, 2, 2);\nthis.node.onaudioprocess = function(e){\n  worker.postMessage({\n   command: 'record',\n   buffer: [\n     e.inputBuffer.getChannelData(0),\n     e.inputBuffer.getChannelData(1)\n   ]\n  });\n}\n```\n\n```javascript\nfunction record(inputBuffer){\n  var bufferL = inputBuffer[0];\n  var bufferR = inputBuffer[1];\n  var interleaved = interleave(bufferL, bufferR);\n  recBuffers.push(interleaved);\n  recLength += interleaved.length;\n}\n\nfunction interleave(inputL, inputR){\n  var length = inputL.length + inputR.length;\n  var result = new Float32Array(length);\n\n  var index = 0,\n      inputIndex = 0;\n\n  while (index \u003c length){\n    result[index++] = inputL[inputIndex];\n    result[index++] = inputR[inputIndex];\n    inputIndex++;\n  }\n  return result;\n}\n```\n\n```javascript\nfunction encodeWAV(samples){\n  var buffer = new ArrayBuffer(44 + samples.length * 2);\n  var view = new DataView(buffer);\n\n  /* RIFF identifier */\n  writeString(view, 0, 'RIFF');\n  /* file length */\n  view.setUint32(4, 32 + samples.length * 2, true);\n  /* RIFF type */\n  writeString(view, 8, 'WAVE');\n  /* format chunk identifier */\n  writeString(view, 12, 'fmt ');\n  /* format chunk length */\n  view.setUint32(16, 16, true);\n  /* sample format (raw) */\n  view.setUint16(20, 1, true);\n  /* channel count */\n  view.setUint16(22, 2, true);\n  /* sample rate */\n  view.setUint32(24, sampleRate, true);\n  /* byte rate (sample rate * block align) */\n  view.setUint32(28, sampleRate * 4, true);\n  /* block align (channel count * bytes per sample) */\n  view.setUint16(32, 4, true);\n  /* bits per sample */\n  view.setUint16(34, 16, true);\n  /* data chunk identifier */\n  writeString(view, 36, 'data');\n  /* data chunk length */\n  view.setUint32(40, samples.length * 2, true);\n\n  floatTo16BitPCM(view, 44, samples);\n\n  return view;\n}\n```\n\n### Binary Blob\n\nInstead of file drag and drop interface this binary blob is passed to editor.\n\nNote: BlobBuilder deprecated (but a lot of examples use it), you should use Blob constructor instead!\n\n```javascript\nvar f = new FileReader();\nf.onload = function(e) {\n  audio_context.decodeAudioData(e.target.result, function(buffer) {\n      $('#audioLayerControl')[0].handleAudio(buffer);\n    }, function(e) {\n      console.warn(e);\n    });\n  };\nf.readAsArrayBuffer(blob);\n```\n\n```javascript\nfunction exportWAV(type){\n  var buffer = mergeBuffers(recBuffers, recLength);\n  var dataview = encodeWAV(buffer);\n  var audioBlob = new Blob([dataview], { type: type });\n\n  this.postMessage(audioBlob);\n}\n```\n\n### Virtual File – URL.createObjectURL\n\nYou can create file download link pointing to WAV blob, but also set it as the source of an Audio element.\n\n```javascript\nvar url = URL.createObjectURL(blob);\nvar audioElement = document.createElement('audio');\nvar downloadAnchor = document.createElement('a');\n\naudioElement.controls = true;\naudioElement.src = url;\n\ndownloadAnchor.href = url;\n```\n\nTODO\n----\n\n* Sequencer top / status row should be radio buttons :)\n* Code cleanup / restructuring\n* Enable open / drag and drop files for editing\n* Visual feedback (levels) for live recording\n* Sequencer UI (and separation to a different module)\n\nCredits / license\n-----------------\n\nLive recording code adapted from: http://www.phpied.com/files/webaudio/record.html\n\nEditor code adapted from: https://github.com/plucked/html5-audio-editor\n\nCopyright (c) 2012 Daniel Demmel\n\nMIT License\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this software and associated documentation files (the\n\"Software\"), to deal in the Software without restriction, including\nwithout limitation the rights to use, copy, modify, merge, publish,\ndistribute, sublicense, and/or sell copies of the Software, and to\npermit persons to whom the Software is furnished to do so, subject to\nthe following conditions:\n\nThe above copyright notice and this permission notice shall be\nincluded in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND,\nEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\nMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\nNONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\nLIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\nOF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\nWITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaaain%2FJSSoundRecorder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdaaain%2FJSSoundRecorder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdaaain%2FJSSoundRecorder/lists"}