├── AudioDrop.js ├── README.md ├── audiodrop.png └── tests ├── index.html ├── test.css └── test.js /AudioDrop.js: -------------------------------------------------------------------------------- 1 | function AudioDrop(options) { 2 | 3 | if(!options.context) { 4 | return console.error('Please supply AudioDrop with a `context` option.'); 5 | } 6 | if(!options.elements) { 7 | return console.error('Please supply AudioDrop with an `elements` option.'); 8 | } 9 | if(!options.drop) { 10 | return console.error('Please supply AudioDrop with a `drop` option.'); 11 | } 12 | 13 | if(!Array.isArray(options.elements)) { 14 | options.elements = [options.elements]; 15 | } 16 | 17 | // ========================================== 18 | // Event Binding 19 | // ========================================== 20 | 21 | // we want to prevent the default behavior for window drops 22 | window.addEventListener('dragleave', preventDefault, false); 23 | window.addEventListener('dragover', preventDefault, false); 24 | window.addEventListener('drop', preventDefault, false); 25 | 26 | // create events for each element passed in 27 | options.elements.forEach( function(element) { 28 | element.addEventListener('dragenter', dragEnter, false); 29 | element.addEventListener('dragover', dragOver, false); 30 | element.addEventListener('dragleave', dragLeave, false); 31 | element.addEventListener('drop', drop, false); 32 | }); 33 | 34 | // ========================================== 35 | // Events 36 | // ========================================== 37 | 38 | function drop(e) { 39 | e.stopPropagation(); 40 | e.preventDefault(); 41 | // turn off global dragging flag 42 | dragging = false; 43 | // start looping to find audio files & folders 44 | var droppedFiles = Array.prototype.slice.call(e.dataTransfer.files); 45 | droppedFiles.forEach( function(file) { 46 | if(isSupportedFormat(file.type)) { 47 | decodeBuffer(file); 48 | } 49 | }); 50 | return false; 51 | } 52 | 53 | function dragEnter(e) { 54 | options.dragEnter && options.dragEnter(e); 55 | } 56 | 57 | function dragOver(e) { 58 | e.stopPropagation(); 59 | e.preventDefault(); 60 | options.dragOver && options.dragOver(e); 61 | } 62 | 63 | function dragLeave(e) { 64 | e.stopPropagation(); 65 | e.preventDefault(); 66 | options.dragLeave && options.dragLeave(e); 67 | } 68 | 69 | function preventDefault(e) { 70 | e.preventDefault(); 71 | } 72 | 73 | // ========================================== 74 | // Audio 75 | // ========================================== 76 | 77 | function decodeBuffer(file) { 78 | var fileReader = new FileReader(); 79 | 80 | fileReader.onload = function(fileEvent) { 81 | var data = fileEvent.target.result; 82 | options.context.decodeAudioData(data, function(buffer) { 83 | options.drop(buffer, file); 84 | }, function(e) { 85 | console.error('There was an error decoding ' + file.name); 86 | }); 87 | }; 88 | 89 | fileReader.readAsArrayBuffer(file); 90 | } 91 | 92 | function isSupportedFormat(type) { 93 | return type.indexOf('audio') > -1; 94 | } 95 | } 96 | 97 | AudioDrop.isValidVariableName = function(str) { 98 | return !str.match(/^(?:do|if|in|for|let|new|try|var|case|else|enum|eval|null|this|true|void|with|break|catch|class|const|false|super|throw|while|yield|delete|export|import|public|return|static|switch|typeof|default|extends|finally|package|private|continue|debugger|function|arguments|interface|protected|implements|instanceof|undefined)$/); 99 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AudioDrop 2 | 3 | 4 | 5 | `AudioDrop` is a utility that enables drag-and-drop loading of Audio Buffers. Simply specify an element to be designated as a drop zone and provide a callback that determines how to handle the newly-created audio buffer. 6 | 7 | *For use with the Web Audio API*: If you're looking for a general drag-and-drop utility you may want to check out [dropzone](http://www.dropzonejs.com/). Audiodrop makes use of the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) to decode audio files into [`AudioBuffer`](https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer) objects. 8 | 9 | ## API 10 | 11 | #### `AudioDrop(options)` 12 | 13 | Call `AudioDrop` with an options object. Required options are `context`, `elements`, and `drop`. 14 | 15 | ```javascript 16 | AudioDrop({ 17 | 18 | // the web audio context (required) 19 | context: myAudioContext, 20 | 21 | // a DOM Element or an array of DOM Elements (required) 22 | elements: document.querySelector('#dropzone'), 23 | 24 | // the callback to handle each file (required) 25 | drop: function(buffer, file) { 26 | window[file.name] = buffer; 27 | console.log('Added the buffer ' + file.name + ' to the window.'); 28 | }, 29 | 30 | // DOM Events 31 | 32 | // called when there is a file being dragged into the dropzone 33 | dragEnter: function(e) { }, 34 | 35 | // called repeatedly while a file is being dragged on the dropzone 36 | dragOver: function(e) { }, 37 | 38 | // called when there is a file being dragged out of the dropzone 39 | dragLeave: function(e) { }, 40 | }) 41 | ``` 42 | 43 | #### `AudioDrop.isValidVariableName(String)` 44 | 45 | A convenience function for determining whether or not a string, if turned into a variable name, would violate a reserved keyword. This is useful if you are planning to attach the variable to the `window`, as in the example below. 46 | 47 | ## Examples 48 | 49 | #### Attach the audio buffer to the window. 50 | 51 | ```javascript 52 | AudioDrop({ 53 | context: new AudioContext(), 54 | elements: window.document.body, 55 | drop: function(buffer, file) { 56 | var name = file.name.replace(/\.[^/.]+$/, ""); 57 | if( AudioDrop.isValidVariableName(name) ) { 58 | window[name] = buffer; 59 | console.log('Added the variable "' + name + '"" to the window.'); 60 | } else { 61 | window[name + '-sample'] = buffer; 62 | console.log('Added the variable window["' + name + '-sample"] to the window.'); 63 | } 64 | } 65 | }); 66 | ``` 67 | 68 | ----------------------- 69 | 70 | `AudioDrop` was created for [`Lissajous`](https://github.com/kylestetz/lissajous). -------------------------------------------------------------------------------- /audiodrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kylestetz/AudioDrop/0a5110175f86ec36ea92effcd2f3a38ba4b953d0/audiodrop.png -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | AudioDrop.js Tests 4 | 5 | 6 | 7 | 8 | 9 |
10 |

Simple Test

11 |

Drag and drop a file over me & check the console.

12 |
13 |
14 |

Advanced Test

15 |

This box should highlight when files are dragged over it.

16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /tests/test.css: -------------------------------------------------------------------------------- 1 | body,html 2 | { 3 | font-family: 'Andale Mono', 'Inconsolata', 'Courier New', monospace; 4 | text-align: center; 5 | } 6 | 7 | * 8 | { 9 | box-sizing: border-box; 10 | } 11 | 12 | .box 13 | { 14 | display: inline-block; 15 | vertical-align: top; 16 | width: 280px; 17 | height: 280px; 18 | padding: 20px; 19 | 20 | text-align: center; 21 | } 22 | 23 | /* prevent duplicate drag enter/leave events for child elements */ 24 | .box * 25 | { 26 | pointer-events: none; 27 | } 28 | 29 | #simple-test 30 | { 31 | background-color: #79BC8A; 32 | border: 8px solid transparent; 33 | } 34 | 35 | #advanced-test 36 | { 37 | background-color: #72A2BC; 38 | border: 8px dashed transparent; 39 | 40 | transition: background-color 0.2s, border-color 0.2s; 41 | } 42 | 43 | #advanced-test.over 44 | { 45 | background-color: #A2CEE0; 46 | border-color: #72A2BC; 47 | } -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | document.addEventListener("DOMContentLoaded", function(event) { 2 | 3 | var context = new AudioContext(); 4 | 5 | // ================================================== 6 | // SIMPLE TEST 7 | // ================================================== 8 | 9 | AudioDrop({ 10 | context: context, 11 | elements: document.querySelector('#simple-test'), 12 | 13 | drop: function(buffer, file) { 14 | var name = file.name.replace(/\.[^/.]+$/, ""); 15 | if( AudioDrop.isValidVariableName(name) ) { 16 | window[name] = buffer; 17 | console.log('simple test: Added the variable "' + name + '" to the window.'); 18 | } else { 19 | window[name + '-sample'] = buffer; 20 | console.log('simple test: Added the variable window["' + name + '-sample"] to the window.'); 21 | } 22 | }, 23 | 24 | dragEnter: function(e) { 25 | console.log('simple test: dragEnter'); 26 | }, 27 | dragOver: function(e) { 28 | console.log('simple test: dragOver'); 29 | }, 30 | dragLeave: function(e) { 31 | console.log('simple test: dragLeave'); 32 | } 33 | }); 34 | 35 | // ================================================== 36 | // ADVANCED TEST 37 | // ================================================== 38 | 39 | var advancedDropzone = document.querySelector('#advanced-test'); 40 | 41 | AudioDrop({ 42 | context: context, 43 | elements: advancedDropzone, 44 | 45 | drop: function(buffer, file) { 46 | advancedDropzone.className = advancedDropzone.className.replace(' over', ''); 47 | var name = file.name.replace(/\.[^/.]+$/, ""); 48 | if( AudioDrop.isValidVariableName(name) ) { 49 | window[name] = buffer; 50 | console.log('advanced test: Added the variable "' + name + '" to the window.'); 51 | } else { 52 | window[name + '-sample'] = buffer; 53 | console.log('advanced test: Added the variable window["' + name + '-sample"] to the window.'); 54 | } 55 | }, 56 | 57 | dragEnter: function(e) { 58 | advancedDropzone.className += ' over'; 59 | }, 60 | dragLeave: function(e) { 61 | advancedDropzone.className = advancedDropzone.className.replace(' over', ''); 62 | } 63 | }); 64 | 65 | }); --------------------------------------------------------------------------------