diff options
author | sternenseemann <git@lukasepple.de> | 2017-09-10 16:13:07 +0200 |
---|---|---|
committer | sternenseemann <git@lukasepple.de> | 2017-09-10 16:22:51 +0200 |
commit | d1377b5c82184f4c5c667b49012cb3a26aa0204e (patch) | |
tree | 14d2bd4ca82717f81c7b5d5caad83f5d27eed033 /web | |
parent | f1c95a7753f1d33a2c15d8311b094ac59868b852 (diff) |
Lots of design improvements & feature additions
- Seed storage - added an endpoint for receiving a random seed - added an input for manually entering a seed - seeds get saved to local storage - param storage - params (maxhops, seed, starting_node) get saved to local storage - restored upon reload - added in browser player which generates a wav based on the seed - renamed midi format to mid - improve overall look and usability of the sidebar - introduced issues in the dialogs -> TODO
Diffstat (limited to 'web')
-rw-r--r-- | web/source/custom.css | 84 | ||||
-rw-r--r-- | web/source/index.html | 41 | ||||
-rw-r--r-- | web/source/main.js | 164 |
3 files changed, 207 insertions, 82 deletions
diff --git a/web/source/custom.css b/web/source/custom.css index 9e7c17c..6fc3605 100644 --- a/web/source/custom.css +++ b/web/source/custom.css @@ -17,6 +17,7 @@ body { color: white; background-color: black; box-shadow: 0px 0px 20px #111; + font-size: 1.2rem; } #sidebar > * { @@ -26,27 +27,15 @@ body { padding-left: 0px; padding-right: 0px; margin: 0; - display: inline-block; -} - -#sidebar button, #sidebar input { - height: 3rem; -} - -#sidebar *:nth-child(odd) { - background-color: #111; -} - -#sidebar ::placeholder { - color:#aaa; } -#sidebar *:nth-child(even) { - background-color: #000; +#sidebar button:hover, #sidebar input:hover, +#sidebar .custom-file:hover, #sidebar select:hover { + background-color: #563d7c; } -#sidebar button:hover, #sidebar input:hover, #sidebar .custom-file:hover { - background-color: #563d7c; +#sidebar button, #sidebar input, #sidebar .custom-file, #sidebar select { + background-color: #000; } #sidebar h1 { @@ -54,6 +43,7 @@ body { padding-top: 0.75rem; padding-bottom: 0.75rem; text-align: center; + background-color: #111; } #sidebar h2 { @@ -61,6 +51,15 @@ body { padding-top: 0.9rem; padding-bottom: 0.9rem; text-align: center; + background-color: #222; +} + +#sidebar select { + color: white; + border: none; + padding: 0.75rem; + font-size: 1.2rem; + width: auto; } button { @@ -69,7 +68,7 @@ button { background-color:black; font-size: 1.2rem; margin:0; - padding:0; + padding:0.75rem; } input[type="number"] { @@ -78,6 +77,7 @@ input[type="number"] { border: none; text-align: center; font-size: 1.2rem; + padding:0.75rem; } .custom-file { @@ -88,7 +88,6 @@ input[type="number"] { height: 3rem; } - .custom-file input[type="file"] { position: relative; top:0; @@ -103,6 +102,7 @@ input[type="number"] { } .custom-file span { + text-align: center; position: absolute; top: 0; left: 0; @@ -112,7 +112,6 @@ input[type="number"] { height: 3rem; pointer-events: none; background-color: transparent !important; - text-align: center; font-size: 1.2rem; line-height: 1.5rem; padding-top: 0.75rem; @@ -148,50 +147,69 @@ input[type="number"] { font-size: 1.5rem; } -.dialog button.cancel { +button.cancel { background-color: #a23a30; } -.dialog button.save { +button.save { background-color: #0ea92f; } -.label-input { +.dialog .multi-inputs { + font-size: 1.5rem; +} + +.multi-inputs { display: inline-flex; flex-direction: row; + flex-wrap: nowrap; + justify-content: flex-start; + align-items: baseline; width: 100%; } -.label-input > * { - flex: auto; +.multi-inputs > * { + flex-grow: 1; + flex-basis: auto; transition: width 0.7s ease-out; + max-height: 100%; + text-align: center; } -.label-input label { +.multi-inputs :nth-child(1) { + text-align: left; +} + +.multi-inputs label { display: inline-block; background-color: #333; - font-size: 1.5rem; padding: 0.75rem; } -.label-input input { +.multi-inputs input { display: inline-block; color: white; background-color: #111; - font-size: 1.5rem; padding: 0.75rem; border: none; min-width: 0px; } -.label-input span { +.multi-inputs span { display: inline-block; - font-size: 1.5rem; padding: 0.75rem; background-color: #222; } -.label-input button { - font-size: 1.5rem; +.multi-inputs button { padding: 0.75rem; } + +#player-container { + display: inline-flex; + align-items: center; +} + +#player-container > * { + flex: auto; +} diff --git a/web/source/index.html b/web/source/index.html index 14c08dc..0e21fcd 100644 --- a/web/source/index.html +++ b/web/source/index.html @@ -17,46 +17,65 @@ <button id="set-starting-node">Set starting node</button> <button id="show-starting-node">Show starting node</button> <h2>Generate an interpretation</h2> - <input type="number" min="0" id="hop-count" placeholder="Max. song length (in notes)"> - <button id="play">Play</button> - <button id="gen-midi">Download MIDI</button> + <div class="multi-inputs"> + <label for="seed">Seed:</label> + <input type="number" id="seed"> + </div> + <div class="multi-inputs"> + <label for="hop-count">Length:</label> + <input type="number" min="0" id="hop-count" placeholder="Max. note count"> + </div> + <div id="player-container"> + <button id="reload-player">↺</button> + <audio id="player" controls></audio> + </div> + <div class="multi-inputs"> + <button id="download-audio">Download</button> + <label for="format"> + as + </label> + <select id="format"> + <option value="mid">MIDI</option> + <option value="wav">WAV</option> + </select> + </div> <h2>Load or Save Work</h2> - <button id="gen-score">Save</button> + <button id="gen-score" class="save">Save</button> <label for="upload-score" class="custom-file"> <input type="file" id="upload-score" > <span>Load</span> </label> - <button id="clear-score">Clear</button> + <button id="clear-score" class="cancel">Clear</button> </div> <div id="edge-overlay" class="hidden dialog"> <h2><span id="edge-operation"></span> edge</h2> - <div class="label-input"> + <div class="multi-inputs"> <label for="prob">Probability:</label> <input id="prob" type="number" min="0.0" max="100"> <span>%</span> </div> - <div class="label-input"> + <div class="multi-inputs"> <button class="save" id="edge-save">Save</button> <button class="cancel" id="edge-cancel">Cancel</button> </div> </div> <div id="node-overlay" class="hidden dialog"> <h2><span id="node-operation"></span> node</h2> - <div class="label-input"> + <div class="multi-inputs"> <label for="pitch">Pitch:</label> <select id="pitch"></select> </div> - <div class="label-input"> + <div class="multi-inputs"> <label for="octave">Octave:</label> <input id="octave" type="number" step="1"> </div> - <div class="label-input"> + <div class="multi-inputs"> <label>Duration:</label> <input min="0" id="numerator" type="number" step="1"> <span>/</span> <input min="0" id="denominator" type="number" step="1"> </div> - <div class="label-input"> + <div class="multi-inputs"> <button class="save" id="node-save">Save</button> <button class="cancel" id="node-cancel">Cancel</button> </div> diff --git a/web/source/main.js b/web/source/main.js index 2dd126b..ab9823e 100644 --- a/web/source/main.js +++ b/web/source/main.js @@ -3,6 +3,7 @@ import { Map } from 'immutable'; // types / internals const valid_pitches = [ + 'Rest', 'Cff', 'Cf', 'C', 'Dff', 'Cs', 'Df', 'Css', 'D', 'Eff', @@ -14,10 +15,11 @@ const valid_pitches = [ 'Gs', 'Af', 'Gss', 'A', 'Bff', 'As', 'Bf', 'Ass', 'B', - 'Bs', 'Bss', 'Rest' + 'Bs', 'Bss' ]; const display_pitches = [ + 'Rest', 'C♯♯', 'C♯', 'C', 'D♯♯', 'C♭', 'D♯', 'C𝄫', 'D', 'E♯♯', @@ -29,7 +31,7 @@ const display_pitches = [ 'G♭', 'A♯', 'G𝄫', 'A', 'B♯♯', 'A♭', 'B♯', 'A𝄫', 'B', - 'B♭', 'B𝄫', 'Rest' + 'B♭', 'B𝄫' ]; function displayPitch(pitch) { @@ -254,7 +256,7 @@ function downloadFile(content_type, filename, content) { var nodeData = Map(); var edgeData = Map(); var network = null; -var starting_node_id = undefined; +var starting_node_id = null; function showOverlay(id) { @@ -378,9 +380,11 @@ function handleImport() { } } -function saveGraphToLocalStorage() { +function saveDataToLocalStorage() { const json = JSON.stringify(collectGraphData(nodeData, edgeData)); + const params = JSON.stringify(gatherParams()); localStorage.setItem("score", json) + localStorage.setItem("params", params) } function showStartingNode() { @@ -402,45 +406,118 @@ function setStartingNode() { } } -function genInterpretation(format) { - try { - var starting_node_entry = nodeData.get(starting_node_id); +function fetchInterpretation(params, format) { + var jsonRequest = JSON.stringify({ + graph: collectGraphData(nodeData, edgeData), + params: params + }); + + var myHeaders = new Headers(); + myHeaders.set('Content-Type', 'application/json'); + + var myInit = { + method: 'POST', + headers: myHeaders, + mode: 'cors', + body: jsonRequest + }; + + var myRequest = new Request(`http://localhost:8081/interpretation/${format}`, myInit); + + return fetch(myRequest).then(res => res.blob()); +} + +function gatherParams() { + var starting_node_entry = nodeData.get(starting_node_id); + if(starting_node_entry !== undefined && starting_node_entry !== null) { var starting_node = { id: starting_node_entry.nodeData.id, music: starting_node_entry.music }; - } catch(e) { - alert('Set a starting node first!'); - return; + } else { + var starting_node = null } - try { - var maxhops = document.getElementById('hop-count').value; + var maxhops = document.getElementById('hop-count').value; + if(maxhops === "" || Number(maxhops) === NaN) { + maxhops = null; + } else { + maxhops = Number(maxhops); + } - var jsonRequest = JSON.stringify({ - graph: collectGraphData(nodeData, edgeData), - params: { maxhops: 100, starting_node: starting_node } - }); + var seed = document.getElementById('seed').value; + if(seed === "" || Number(seed) === NaN) { + seed = null; + } else { + seed = Number(seed); + } + return { + maxhops: maxhops, + starting_node: starting_node, + seed: seed + }; +} - var myHeaders = new Headers(); - myHeaders.set('Content-Type', 'application/json'); +function completeGatherParams() { + var p = gatherParams(); + if(p.starting_node === null) { + alert('Set a starting node first!'); + return null; + } - var myInit = { - method: 'POST', - headers: myHeaders, - mode: 'cors', - body: jsonRequest - }; + if(p.maxhops === null) { + alert('Set the maximum amount of hops to a valid number'); + return null; + } + + if(p.seed === null) { + // TODO auto generate a random one, let the user confirm before + alert('Set the seed to a valid number!'); + return null; + } - var myRequest = new Request(`http://localhost:8081/interpretation/${format}`, myInit); + return p; +} - fetch(myRequest).then(res => res.blob()).then(file => { - var url = URL.createObjectURL(file); - download(url, 'export.midi'); - }); - } catch(e) { - alert('An error occured while contacting the API: ' + e); +function importParams(p) { + if(p.starting_node !== null) { + starting_node_id = p.starting_node.id; + } + if(p.seed !== null) { + document.getElementById('seed').value = p.seed; + } + if(p.maxhops !== null) { + document.getElementById('hop-count').value = p.maxhops; + } +} + +function downloadInterpretation(format) { + var params = completeGatherParams(); + if(params != null) { + try { + fetchInterpretation(params, format).then(file => { + var url = URL.createObjectURL(file); + download(url, `export.${format}`); + }); + } catch(e) { + alert('An error occured while contacting the API: ' + e); + } + } +} + +function reloadPlayer() { + var params = completeGatherParams(); + if(params !== null) { + document.getElementById('player').src = null; + try { + fetchInterpretation(params, 'wav').then(file => { + var url = URL.createObjectURL(file); + document.getElementById('player').src = url; + }); + } catch(e) { + alert('An error occured while contacting the API: ' + e); + } } } @@ -518,6 +595,14 @@ function init() { localStorage.removeItem('score'); } + try { + const params = localStorage.getItem('params') + if(params !== null) { + importParams(JSON.parse(params)); + } + } catch(e) { + localStorage.removeItem('params'); + } const pitch_selector = valid_pitches.map((p, i) => `<option value="${p}">${display_pitches[i]}</option>`) @@ -525,21 +610,24 @@ function init() { acc + v, ''); document.getElementById('pitch').innerHTML = pitch_selector; - document.getElementById('gen-midi').onclick = - genInterpretation.bind(this, ('midi')); + /* event handling, order as in sidebar */ + document.getElementById('set-starting-node').onclick = setStartingNode; + document.getElementById('show-starting-node').onclick = showStartingNode; + + document.getElementById('reload-player').onclick = reloadPlayer; + document.getElementById('download-audio').onclick = () => { + var format = document.getElementById('format').value; + downloadInterpretation(format); + }; document.getElementById('gen-score').onclick = () => downloadFile('application/json', 'score.likely.json', JSON.stringify(collectGraphData(nodeData, edgeData))); - document.getElementById('upload-score').addEventListener('change',handleImport); document.getElementById('clear-score').onclick = () => importGraphData({ nodes: [], edges: []}); - document.getElementById('show-starting-node').onclick = showStartingNode; - document.getElementById('set-starting-node').onclick = setStartingNode; - - window.setInterval(saveGraphToLocalStorage, 5000); + window.setInterval(saveDataToLocalStorage, 5000); } document.addEventListener('DOMContentLoaded', () => init()); |