diff --git a/src/about.html b/src/about.html index 357438b..0f6a086 100755 --- a/src/about.html +++ b/src/about.html @@ -104,13 +104,13 @@

Introducing AudioMass (https://audiomass.co) an open-source web based audio and waveform editing tool.

- +Screenshot of AudioMass with parametric EQ options visible

AudioMass lets you record, or use your existing audio tracks, and modify them by trimming, cutting, pasting or applying a plethora of effects, from compression and paragraphic equalizers to reverb, delay and distortion fx. AudioMass also supports more than 20 hotkeys combinations and a dynamic responsive interface to ensure ease of use and that your producivity remains high. it is written solely in plain old-school javascript, weights approximately 65kb and has no backend or framework dependencies.

- +AudioMass shown running on multiple devices

It also has very good browser and device support.

@@ -146,21 +146,21 @@

Getting Started

To get started, drag and drop an audio file, or try the included sample.
Once the file is loaded and you can view the waveform, zoom in, pan around, or select a region.

- +Screenshot of selected audio

Recording Audio

To record audio, simply press the Recording button, or the [R] key.

- +Screenshot of recording in progress

Exporting to mp3

In order to export back to mp3, click on 'File', then 'Export to mp3', and follow the modal's instructions.

- +Export MP3 screenshot @@ -177,11 +177,11 @@

The story behind AudioMass. And a short rant on web interfaces.

In general I am a big fan of the interfaces of DAWs (Digital Audio Workstations), they are extremely, complex, intricate, versatile, and they manage to remain visually pleasant even through their infinite options and knobs. Many times I feel the web has taken a very wrong turn, as amazing interfaces such as...

Sonar

- +Sonar screenshot

Fruity loops

- +Fruity loops screenshot
diff --git a/src/fx-pg-eq.js b/src/fx-pg-eq.js index 8c5cea6..6e906bd 100755 --- a/src/fx-pg-eq.js +++ b/src/fx-pg-eq.js @@ -1615,8 +1615,10 @@ }; if (!PKAudioEditor.engine.wavesurfer.isReady) { - play_btn.className += ' pk_inact'; - both_btn.className += ' pk_inact'; + // play_btn.className += ' pk_inact'; + _setButtonState(play_btn,'inactive'); + // both_btn.className += ' pk_inact'; + _setButtonState(both_btn,'inactive'); } if (PKAudioEditor.engine.wavesurfer.isPlaying()) { @@ -1865,8 +1867,10 @@ if (!PKAudioEditor.engine.wavesurfer.isReady) { - play_btn.className += ' pk_inact'; - loop_btn.className += ' pk_inact'; + // play_btn.className += ' pk_inact'; + _setButtonState(play_btn,'inactive'); + // loop_btn.className += ' pk_inact'; + _setButtonState(loop_btn,'inactive'); } if (PKAudioEditor.engine.wavesurfer.isPlaying()) { play_btn.className += ' pk_act'; @@ -2877,14 +2881,16 @@ } is_ready = true; - btn_start.classList.remove ('pk_inact'); + // btn_start.classList.remove ('pk_inact'); + _setButtonState(btn_start,'active'); }); } else { devices_sel.parentNode.style.display = 'none'; has_devices = false; is_ready = true; - btn_start.classList.remove ('pk_inact'); + // btn_start.classList.remove ('pk_inact'); + _setButtonState(btn_start,'active'); } }; @@ -2934,7 +2940,8 @@ if (is_active) { stop (); - btn_pause.classList.add ('pk_inact'); + // btn_pause.classList.add ('pk_inact'); + _setButtonState(btn_pause,'inactive'); btn_start.innerText = 'START RECORDING'; return ; @@ -2969,7 +2976,8 @@ script_processor.connect ( audio_context.destination ); is_active = true; - btn_pause.classList.remove ('pk_inact'); + // btn_pause.classList.remove ('pk_inact'); + _setButtonState(btn_pause,'active'); btn_start.innerText = 'FINISH RECORDING'; script_processor.onaudioprocess = fetchBufferFunction; diff --git a/src/index-cache.html b/src/index-cache.html index b69183b..d5ac293 100755 --- a/src/index-cache.html +++ b/src/index-cache.html @@ -5,7 +5,7 @@ - + @@ -29,7 +29,10 @@ -
+ + + +
diff --git a/src/index.html b/src/index.html index a1edbce..3af18b1 100755 --- a/src/index.html +++ b/src/index.html @@ -5,7 +5,7 @@ - + @@ -30,7 +30,10 @@ -
+ + + +
diff --git a/src/main.css b/src/main.css index f7bd7d8..8cede87 100755 --- a/src/main.css +++ b/src/main.css @@ -73,7 +73,8 @@ body,html{height:100%;background:#111; white-space:nowrap } -.pk_hdr .pk_btn:hover{ +.pk_hdr .pk_btn:hover, +.pk_hdr .pk_btn:focus-within{ background:#eee; color:#111 } @@ -630,7 +631,7 @@ a{cursor:pointer;color:#99c2c6;-webkit-tap-highlight-color:rgba(0,0,0,0)} box-shadow: 0 2px 12px rgba(138, 58, 138, 0.5); background-clip: padding-box; } -.pk_contextMenu a, .pk_contextMenu a:hover{ +.pk_contextMenu a, .pk_contextMenu a:hover, .pk_contextMenu a:focus{ clear: both; color: #eee; display: block; @@ -645,7 +646,8 @@ a{cursor:pointer;color:#99c2c6;-webkit-tap-highlight-color:rgba(0,0,0,0)} font-weight:normal; transition: all 110ms; } -.pk_contextMenu a:hover{ +.pk_contextMenu a:hover, +.pk_contextMenu a:focus{ color:#99c2c6; line-height: 18px; text-decoration: none; @@ -936,7 +938,8 @@ a{cursor:pointer;color:#99c2c6;-webkit-tap-highlight-color:rgba(0,0,0,0)} -webkit-user-select:none; user-select:none; } -.pk_modal_a_bottom:hover{ +.pk_modal_a_bottom:hover, +.pk_modal_a_bottom:focus{ /*background: #eee; color: #111; border-color: #eee;*/ @@ -958,7 +961,8 @@ a{cursor:pointer;color:#99c2c6;-webkit-tap-highlight-color:rgba(0,0,0,0)} transition: background 120ms; } -.pk_modal_cancel:hover{ +.pk_modal_cancel:hover, +.pk_modal_cancel:focus{ box-shadow: 0 0 6px rgba(158, 39, 39, 0.6); border-color: unset; background: #280a0ac7; @@ -970,7 +974,8 @@ a{cursor:pointer;color:#99c2c6;-webkit-tap-highlight-color:rgba(0,0,0,0)} transition: border-color 120ms, color 120ms; } -.pk_modal_a_accpt:hover{ +.pk_modal_a_accpt:hover, +.pk_modal_a_accpt:focus{ color: #99c2c6; } @@ -1153,7 +1158,8 @@ a{cursor:pointer;color:#99c2c6;-webkit-tap-highlight-color:rgba(0,0,0,0)} letter-spacing: normal; } -.pk_sel_edt:hover > span{ +.pk_sel_edt:hover > span, +.pk_sel_edt:focus > span{ visibility:visible; opacity: 1; } @@ -1666,7 +1672,8 @@ input.pk_horiz.pk_w180{ transition:background 120ms; } -.pk_pgeq_els:hover{ +.pk_pgeq_els:hover, +.pk_pgeq_els:focus{ background:#3b413f; } @@ -1821,7 +1828,8 @@ body.pk_stndln{ background:#303030; } -.pk_tbsa:hover{ +.pk_tbsa:hover, +.pk_tbsa:focus{ box-shadow: inset 0 -2px 6px #181818; } @@ -1894,7 +1902,8 @@ body.pk_stndln{ transition: background 70ms; } -#pk_tmp_tap3:hover{ +#pk_tmp_tap3:hover, +#pk_tmp_tap3:focus{ background:#363636; } @@ -1930,7 +1939,8 @@ body.pk_stndln{ padding-right:5px; } -.pk_lcldrf:hover{ +.pk_lcldrf:hover, +.pk_lcldrf:focus{ background:#292929; } @@ -1987,3 +1997,47 @@ body.pk_stndln{ .pk_aut{ box-shadow: 0px 0px 20px #eee, inset 0px 0px 4px gold; } + +/* a11y fixes */ + +/*for skip links*/ +.skip { + height: 1px; + overflow: hidden; + position: absolute; + white-space: nowrap; + width: 1px; + left:-10px; +} +a.skip:focus { + clip: none; + height: auto; + overflow: none; + width: auto; + background: black; + color:white; + font-weight: bold; + font-size: 22px; + padding: 10px; + z-index: 1000; + top: 30px; + left: 15px; + border: 2px solid white; + border-radius: 3px; + outline: none; +} +#skipToolbar:focus { + outline: 2px solid white; +} + +* .pk_btn, .pk_pan_btn { + cursor: pointer; + transition-duration: 0.2s; +} +* .pk_btn:focus, +.pk_pan_btn:focus { + border:3px solid black; + color:black; + background: white; + box-shadow: 0 0 0 2px #fff; +} \ No newline at end of file diff --git a/src/modal.js b/src/modal.js index 856616c..dae73c1 100755 --- a/src/modal.js +++ b/src/modal.js @@ -24,6 +24,10 @@ // var centerer var el_cont = d.createElement ('div'); el_cont.className = 'pk_modal_cnt'; + el_cont.setAttribute('role','dialog'); + el_cont.setAttribute('aria-modal','true'); + el_cont.setAttribute('aria-labelledby','dialogHeading'); + el_cont.setAttribute('aria-describedby','dialogContent'); this.el_cont = el_cont; // title diff --git a/src/ui.js b/src/ui.js index 80d85db..49bc656 100755 --- a/src/ui.js +++ b/src/ui.js @@ -1291,6 +1291,8 @@ function _makeUITopHeader ( menu_tree, UI ) { var header = d.createElement ( 'div' ); header.className = 'pk_hdr pk_noselect'; + header.setAttribute('role','region'); + header.setAttribute('aria-label','Application menu'); var _name = 'TopHeader', _default_class = 'pk_btn pk_noselect'; @@ -1648,6 +1650,8 @@ var audio_container = d.createElement ('div'); audio_container.className = 'pk_av_cont'; + audio_container.setAttribute('role','region'); + audio_container.setAttribute('aria-label','Audio waveform'); UI.el.appendChild( audio_container ); @@ -1659,6 +1663,8 @@ var footer = d.createElement ( 'div' ); footer.className = 'pk_ftr pk_noselect'; + footer.setAttribute('role','region'); + footer.setAttribute('aria-label','Volume information'); UI.el.appendChild( footer ); // make panner buttons @@ -1672,8 +1678,6 @@ var btn_panner_left = d.createElement ('button'); var btn_panner_right = d.createElement ('button'); - btn_panner_left.setAttribute ('tabIndex', -1); - btn_panner_right.setAttribute ('tabIndex', -1); btn_panner_left.className = 'pk_pan_btn'; btn_panner_right.className = 'pk_pan_btn'; @@ -1728,7 +1732,6 @@ var btn_zoom_in_h = d.createElement ('button'); btn_zoom_in_h.className = 'pk_btn pk_zoom_in_h'; btn_zoom_in_h.innerHTML = '+Zoom In Horiz (+)'; - btn_zoom_in_h.setAttribute ('tabIndex', -1); btn_zoom_in_h.onclick = function () { app.fireEvent ('RequestZoomUI', 'h', -1); this.blur(); @@ -1737,7 +1740,6 @@ var btn_zoom_out_h = d.createElement ('button'); btn_zoom_out_h.className = 'pk_btn pk_zoom_out_h pk_inact'; btn_zoom_out_h.innerHTML = '–Zoom Out Horiz (-)'; - btn_zoom_out_h.setAttribute ('tabIndex', -1); btn_zoom_out_h.onclick = function () { app.fireEvent ('RequestZoomUI', 'h', 1); this.blur(); @@ -1746,7 +1748,6 @@ var btn_zoom_reset = d.createElement ('button'); btn_zoom_reset.className = 'pk_btn pk_zoom_reset pk_inact'; btn_zoom_reset.innerHTML = '[R] Reset Zoom (0)'; - btn_zoom_reset.setAttribute ('tabIndex', -1); btn_zoom_reset.onclick = function () { app.fireEvent ('RequestZoomUI', 0); this.blur(); @@ -1768,7 +1769,6 @@ var btn_zoom_in_v = d.createElement ('button'); btn_zoom_in_v.className = 'pk_btn pk_zoom_in_v'; btn_zoom_in_v.innerHTML = '↕ +Zoom In Vertically'; - btn_zoom_in_v.setAttribute ('tabIndex', -1); btn_zoom_in_v.onclick = function () { app.fireEvent ('RequestZoomUI', 'v', -1); this.blur(); @@ -1777,7 +1777,6 @@ var btn_zoom_out_v = d.createElement ('button'); btn_zoom_out_v.className = 'pk_btn pk_zoom_out_v'; btn_zoom_out_v.innerHTML = '↕ –Zoom Out Vertically'; - btn_zoom_out_v.setAttribute ('tabIndex', -1); btn_zoom_out_v.onclick = function () { app.fireEvent ('RequestZoomUI', 'v', 1); this.blur(); @@ -2023,9 +2022,9 @@ // change temp message, it's pretty ugly #### TODO var ttmp = d.createElement('div'); ttmp.className = 'pk_tmpMsg'; - ttmp.innerHTML = 'Drag n drop an Audio File in this window, or click ' + - 'here to use a sample'; + ttmp.innerHTML = 'Drag n drop an Audio File in this window, or ' + + 'load a sample audio file'; main_audio_view.appendChild( ttmp ); var ttmp2 = d.createElement('div'); @@ -2055,20 +2054,26 @@ function _makeUIToolbar (UI) { var container = d.createElement ( 'div' ); container.className = 'pk_tbc'; + container.setAttribute('role','region'); + container.setAttribute('aria-label','Main toolbar'); var toolbar = d.createElement ( 'div' ); toolbar.className = 'pk_tb pk_noselect'; + toolbar.setAttribute('tabindex','-1'); + toolbar.setAttribute('id','skipToolbar'); var btn_groups = d.createElement( 'div' ); btn_groups.className = 'pk_btngroup'; var transport = d.createElement( 'div' ); transport.className = 'pk_transport'; + transport.setAttribute('role','group'); + transport.setAttribute('aria-label','Transport controls'); // play button var btn_stop = d.createElement ('button'); - btn_stop.setAttribute ('tabIndex', -1); btn_stop.innerHTML = 'Stop Playback (Space)'; + btn_stop.setAttribute('aria-label','Stop Playback (Space)'); btn_stop.className = 'pk_btn pk_stop icon-stop2'; btn_stop.onclick = function() { UI.fireEvent('RequestStop'); @@ -2076,9 +2081,9 @@ transport.appendChild ( btn_stop ); var btn_play = d.createElement ('button'); - btn_play.setAttribute ('tabIndex', -1); btn_play.className = 'pk_btn pk_play icon-play3'; btn_play.innerHTML = 'Play (Space)'; + btn_play.setAttribute('aria-label','Play (Space)'); transport.appendChild ( btn_play ); btn_play.onclick = function() { UI.fireEvent('RequestPlay'); @@ -2092,9 +2097,9 @@ }); var btn_pause = d.createElement ('button'); - btn_pause.setAttribute('tabIndex', -1); btn_pause.className = 'pk_btn pk_pause icon-pause2'; btn_pause.innerHTML = 'Pause (Shift+Space)'; + btn_pause.setAttribute('aria-label','Pause (Shift+Space)'); transport.appendChild ( btn_pause ); btn_pause.onclick = function() { UI.fireEvent('RequestPause'); @@ -2102,9 +2107,9 @@ }; var btn_loop = d.createElement ('button'); - btn_loop.setAttribute('tabIndex', -1); btn_loop.className = 'pk_btn pk_loop icon-loop'; btn_loop.innerHTML = 'Toggle Loop (L)'; + btn_loop.setAttribute('aria-label','Toggle Loop (L)'); transport.appendChild ( btn_loop ); btn_loop.onclick = function() { UI.fireEvent('RequestSetLoop'); @@ -2116,9 +2121,9 @@ }); var btn_back_jump = d.createElement ('button'); - btn_back_jump.setAttribute('tabIndex', -1); btn_back_jump.className = 'pk_btn pk_back_jump icon-backward2'; btn_back_jump.innerHTML = 'Seek (left arrow)'; + btn_back_jump.setAttribute('aria-label','Seek (left arrow)'); transport.appendChild ( btn_back_jump ); /////////////////////////////////////////////////////////// @@ -2196,9 +2201,9 @@ //////////////////////// var btn_front_jump = d.createElement ('button'); - btn_front_jump.setAttribute('tabIndex', -1); btn_front_jump.className = 'pk_btn pk_front_jump icon-forward3'; btn_front_jump.innerHTML = 'Seek (right arrow)'; + btn_front_jump.setAttribute('aria-label','Seek (right arrow)'); transport.appendChild ( btn_front_jump ); var btn_frnt_focus = false; @@ -2388,9 +2393,9 @@ }, [27]); var btn_back_total = d.createElement ('button'); - btn_back_total.setAttribute('tabIndex', -1); btn_back_total.className = 'pk_btn icon-previous2'; btn_back_total.innerHTML = 'Seek Start (Shift + left arrow)'; + btn_back_total.setAttribute('aria-label','Seek Start (Shift + left arrow)'); transport.appendChild ( btn_back_total ); btn_back_total.onclick = function() { UI.fireEvent( 'RequestRegionClear'); @@ -2399,9 +2404,9 @@ }; var btn_front_total = d.createElement ('button'); - btn_front_total.setAttribute('tabIndex', -1); btn_front_total.className = 'pk_btn icon-next2'; btn_front_total.innerHTML = 'Seek End (Shift + right arrow)'; + btn_front_total.setAttribute('aria-label','Seek End (Shift + right arrow)'); btn_front_total.onclick = function() { UI.fireEvent( 'RequestRegionClear'); UI.fireEvent( 'RequestSeekTo', 0.996); @@ -2411,9 +2416,9 @@ var btn_rec = d.createElement ('button'); - btn_rec.setAttribute('tabIndex', -1); btn_rec.className = 'pk_btn icon-rec'; btn_rec.innerHTML = 'Record (R)'; + btn_rec.setAttribute('aria-label','Record (R)'); btn_rec.onclick = function() { if (this.getAttribute('disabled') === 'disabled') { this.blur (); return ; @@ -2449,6 +2454,8 @@ var timing = d.createElement( 'div' ); timing.className = 'pk_timecontainer'; + timing.setAttribute('role','group'); + timing.setAttribute('aria-label','Time information') var timingspan = d.createElement( 'span' ); timingspan.innerText = '00:00:000'; @@ -2682,11 +2689,13 @@ var actions = d.createElement( 'div' ); actions.className = 'pk_ctns'; + actions.setAttribute('role','group'); + actions.setAttribute('aria-label','Copy/Paste controls'); var copy_btn = d.createElement ('button'); - copy_btn.setAttribute('tabIndex', -1); copy_btn.className = 'pk_btn icon-files-empty pk_inact'; copy_btn.innerHTML = 'Copy Selection (Shift + C)'; + copy_btn.setAttribute('aria-label','Copy Selection (Shift + C)'); actions.appendChild ( copy_btn ); copy_btn.onclick = function() { @@ -2705,6 +2714,7 @@ paste_btn.setAttribute('focusable', 'false'); paste_btn.className = 'pk_btn icon-file-text2 pk_inact'; paste_btn.innerHTML = 'Paste Selection (Shift + V)'; + paste_btn.setAttribute('aria-label','Paste Selection (Shift + V)'); actions.appendChild ( paste_btn ); paste_btn.onclick = function() { @@ -2713,9 +2723,10 @@ }; var cut_btn = d.createElement ('button'); - cut_btn.setAttribute('tabIndex', -1); - cut_btn.className = 'pk_btn icon-scissors pk_inact'; + cut_btn.className = 'pk_btn icon-scissors'; + _setButtonActiveState(cut_btn,'inactive'); cut_btn.innerHTML = 'Cut Selection (Shift + X)'; + cut_btn.setAttribute('aria-label','Cut Selection (Shift + X)'); actions.appendChild ( cut_btn ); cut_btn.onclick = function() { @@ -2724,9 +2735,9 @@ }; var silence_btn = d.createElement ('button'); - silence_btn.setAttribute('tabIndex', -1); silence_btn.className = 'pk_btn icon-silence'; silence_btn.innerHTML = 'Insert Silence (Shift + N)'; + silence_btn.setAttribute('aria-label','Insert Silence (Shift + N)'); actions.appendChild ( silence_btn ); UI.KeyHandler.addCallback ('KeyShiftN', function( k ) { @@ -2744,6 +2755,8 @@ var selection = d.createElement( 'div' ); selection.className = 'pk_selection'; + selection.setAttribute('role','group'); + selection.setAttribute('aria-label','Selected audio time properties'); selection.innerHTML = '
' + 'Selection:' + '
Start:-
' + @@ -2752,9 +2765,9 @@ '
'; var btn_clear_selection = d.createElement ('button'); - btn_clear_selection.setAttribute('tabIndex', -1); btn_clear_selection.className = 'pk_btn icon-clearsel pk_inact'; btn_clear_selection.innerHTML = 'Clear Selection (~ tilda)'; + btn_clear_selection.setAttribute('aria-label','Clear Selection (~ tilda)'); var sel_spans = selection.getElementsByClassName('pk_dat'); UI.listenFor ('DidCreateRegion', function ( region ) { @@ -2830,6 +2843,18 @@ // - }; + function _setButtonActiveState(button,state) { + if (state==="active") { + button.removeAttribute('tabindex'); + button.classList.remove('pk_inact'); + } else { + button.setAttribute('tabindex','-1'); + button.classList.add('pk_inact'); + } + } + + + function _makeMobileScroll (UI) { var getFactor = function () { diff --git a/src/welcome.js b/src/welcome.js index e9a5206..cc32262 100755 --- a/src/welcome.js +++ b/src/welcome.js @@ -10,22 +10,22 @@ setTimeout(function () { if (PKAE.isMobile) { change -= 15; body_str = 'Tips:
Please make sure your device is not in silent mode. You might need to physically flip the silent switch. '+ - ''+ + 'Phone silent mode switch'+ '

'; } else { body_str = 'Tips:
Please keep in mind that most key shortcuts rely on the Shift + key combo. (eg Shift+Z for undo, Shift+C copy, Shift+X cut... etc )

'; - body_str2 = 'Check out the codebase on Github

'; // checkout the code on github + body_str2 = 'Check out the codebase on Github

'; // checkout the code on github } // Welcome to AudioMass, var md = new PKSimpleModal({ - title: 'Welcome to AudioMass', + title: 'Welcome to AudioMass', ondestroy: function( q ) { PKAE.ui.InteractionHandler.on = false; PKAE.ui.KeyHandler.removeCallback ('modalTemp'); }, - body:'
'+ + body:'
'+ 'AudioMass is a free, open source, web-based Audio and Waveform Editor.
It runs entirely in the browser with no backend and no plugins required!'+ '


'+ body_str+ @@ -52,7 +52,9 @@ setTimeout(function () { } }); md.Show (); + document.getElementsByClassName('pk_modal_cancel')[0].innerHTML = '      OK      '; + document.getElementsByClassName('pk_modal_cancel')[0].focus(); }; var change = 96;