From 79e7274ee7b0e574be618c63919b3e0ce3aeefc6 Mon Sep 17 00:00:00 2001 From: Pierre Ranwez <pierre.ranwez.etu@univ-lille.fr> Date: Tue, 15 Feb 2022 12:27:44 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20loop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- background.js | 29 ++++---- content.js | 184 +++++++++++++++++++------------------------------- main.js | 21 ++++-- popup.html | 2 +- 4 files changed, 101 insertions(+), 135 deletions(-) diff --git a/background.js b/background.js index 6da0cbd..eabb704 100644 --- a/background.js +++ b/background.js @@ -7,32 +7,44 @@ h1, h2 { font-size: [chiffre2 20:100]px; color: rgb([chiffre 0:255],255,255); }`; -let parameters = `[[\" \",\"typo\",\"sans-serif;serif;cursive;fantasy\",\"r\",\"note:10\",\"onset:E\"],[\" \",\"chiffre\",\"0:1000\",\"i:100\",\"cc:60\",\"onset:D\"],[\" \",\"chiffre2\",\"0:200\",\"i:10\",\"cc:60\",\"onset:E\"],[\" \",\"couleur\",\"#000:#fff\",\"d\",\"cc:60\",\"loud\"],[\" \",\"opacité\",\"0:1\",\"d\",\"cc:60\",\"hi\"]]`; +let parameters = `[[\" \",\"typo\",\"sans-serif;serif;cursive;fantasy\",\"r\",\"note:10\",\"onset:E\"],[\" \",\"chiffre\",\"0:1000\",\"i:100\",\"cc:60\",\"onset:D\"],[\" \",\"chiffre2\",\"0:200\",\"i:10\",\"cc:60\",\"onset:E\"],[\" \",\"couleur\",\"#000:#fff\",\"d\",\"cc:60\",\"loud\"],[\" \",\"opacité\",\"0:1\",\"d\",\"cc:1\",\"hi\"]]`; let activate = false; -let activateFav = "ressources/icon256.png"; let audioB = false; let midiB = false; let audioI = false; let midiI = false; let popup = false; let loopPlay = false; +let records = [[ + { + time: 1, + data: { value: 0, controller: { number: 1 } }, + type: 'controlchange' + }, + { + time: 200, + data: { value: 1, controller: { number: 1 } }, + type: 'controlchange' + }] +]; let all = ""; //Initialize the CSS storage on startup chrome.runtime.onInstalled.addListener(() => { chrome.storage.sync.set({ css }); chrome.storage.sync.set({ activate }); - chrome.storage.sync.set({ activateFav }); chrome.storage.sync.set({ audioB }); chrome.storage.sync.set({ midiB }); chrome.storage.sync.set({ audioI }); chrome.storage.sync.set({ midiI }); chrome.storage.sync.set({ popup }); chrome.storage.sync.set({ loopPlay }); + chrome.storage.sync.set({ records }); chrome.storage.sync.set({ parameters }); chrome.storage.sync.set({ all }); }); + /** * @description This function is called when popup is opened and add a listener when user close it. */ @@ -42,15 +54,4 @@ chrome.runtime.onConnect.addListener(function (port) { chrome.storage.sync.set({ popup: false }); }); } -}); - -function loopPlay2() { - console.log('looooop'); -} - -chrome.runtime.onMessage.addListener((message, callback) => { - // const tabId = getForegroundTabId(); - // if (message.data === "loop") { - // chrome.scripting.executeScript({ func: loopPlay2, tabId }); - // } }); \ No newline at end of file diff --git a/content.js b/content.js index adcc2e9..6e8d2a5 100644 --- a/content.js +++ b/content.js @@ -1,5 +1,3 @@ -var audioEvents = []; -var midiEvents = []; var audioContext = null; var meter = null; var analyser = null; @@ -9,17 +7,6 @@ var buf = new Float32Array(buflen); var audio = false; var midi = false; var audioSample = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; -var record = false; -var records = []; // array of recorded values -var recording = { - time: new Date().getTime(), - events: [ - [1.0, 'controlchange', '1', 0], - [2.0, 'controlchange', '1', 1], - ] -}; // dict with current record -var time; -var playLoop = false; var parameterSave = {}; function range(template, value) { @@ -177,81 +164,13 @@ function midiEvent(type, data) { }); - var newTime = new Date().getTime(); - bpm = ((1 / ((newTime - time) / 1000)) * 60); - time = newTime; - if (record && bpm == 120) { - switch (type) { - case 'controlchange': - recording['events'].push([(newTime - time) / 1000, type, data.controller.name, data.value]); - break; - case 'noteon': - recording['events'].push([(newTime - time) / 1000, type, data.note.numer, data.value]); - break; - case 'pitchbend': - recording['events'].push([(newTime - time) / 1000, type, type, data.value]); - break; - } + if (isRecording) { + const time = Math.floor(performance.now() - recordingTime); + RECORDED[recordCount].push({ type, data, time }) } } -// Function that play recorded midi loop -function playMidiLoop(loopId) { - chrome.runtime.sendMessage({ data: 'loop' }); - // if (records[loopId]) { - // playLoopInfo = true; - // while (playLoopInfo && playLoop && records[loopId]['events'].length > 0) { - // chrome.storage.sync.get(['loopPlay'], function (result) { - // playLoopInfo = result.loopPlay; - // }); - // console.log(playLoopInfo); - // for (let index = 0; index < records[loopId]['events'].length; index++) { - // const event = records[loopId]['events'][index]; - // const eventNext = records[loopId]['events'][(index + 1) % records[loopId]['events'].length]; - // // !!! TODO: add a way to play midi events - // audioEvents.forEach(event => { - // midiValue = event[3]; - // console.log(event[3]); - // templates = event['templates']; - // templates.forEach(template => { - // midiValue = (template['min'] + ((template['max'] - template['min']) * midiValue)); - // if (event[1] == 'noteon' && template['eventType'] == 'note' && template['eventName'] == event[2]) { - // value = event['value'].replace('[' + template['templateFull'] + ']', midiValue); - // let els = document.querySelectorAll(event['selector']); - // els.forEach(e => { - // e.style[event['property']] = value; // Note velocity - // }); - // } - // if (event[1] == 'controlchange' && template['eventType'] == 'cc' && template['eventName'] == event[2]) { - // value = event['value'].replace('[' + template['templateFull'] + ']', midiValue); - // let els = document.querySelectorAll(event['selector']); - // els.forEach(e => { - // e.style[event['property']] = value; // Control value - // }); - // } - // if (event[1] == 'pitchbend' && template['eventType'] == 'pb') { - // value = event['value'].replace('[' + template['templateFull'] + ']', midiValue); - // let els = document.querySelectorAll(event['selector']); - // els.forEach(e => { - // e.style[event['property']] = value; // Pitchbend value - // }); - // } - // if (event[1] == 'loud' && template['eventType'] == 'loud') { - // value = event['value'].replace('[' + template['templateFull'] + ']', midiValue); - // let els = document.querySelectorAll(event['selector']); - // els.forEach(e => { - // e.style[event['property']] = value; // Pitchbend value - // }); - // } - // }); - // }); - // sleepFor((event[0] - eventNext[0]) * 1000); - // } - // } - // } -} - function createAudioMeter(audioContext, clipLevel, averaging, clipLag) { var processor = audioContext.createScriptProcessor(512); processor.onaudioprocess = volumeAudioProcess; @@ -338,11 +257,6 @@ function audioApi() { }); }; -function sleepFor(sleepDuration) { - var now = new Date().getTime(); - while (new Date().getTime() < now + sleepDuration) { /* Do nothing */ } -} - function didntGetStream() { chrome.storage.sync.set({ audioI: false }); audio = false; @@ -441,7 +355,59 @@ function freqToBin(freq, rounding = 'round') { return bin < max ? bin : max; } +// Allow to store the current note in an index, for duration computation +const CURRENT = {}; +// const RECORDED = []; +chrome.storage.sync.get(['records'], function (result) { + RECORDED = result.records; +}); + +let recordCount = 0; +let isRecording = false; +let isLoop = false; +let recordingTime = 0; +let theLoop; +const record = (status) => { + isRecording = status; + recordingTime = performance.now(); + recordCount = RECORDED.length; +}; + + +// Start loop +const loop = () => { + isLoop = !isLoop; + isRecording = false; + console.log('Loop started', RECORDED); + if (RECORDED[0].length) { + const loopLength = RECORDED[0][RECORDED.length - 1].time; + if (isLoop) { + loopNotes(); + theLoop = setInterval(() => loopNotes(), loopLength); + } else { + clearInterval(theLoop) + } + } +}; + +const loopNotes = () => { + console.log('Looping notes'); + RECORDED[0].forEach(note => { + setTimeout(() => { + // Prevent to keep playing also after stop + if (!isLoop) return; + console.log('Looping :p', note.type); + midiEvent(note.type, note.data) + // setTimeout(() => play(note.note, 0), 200) + }, note.time); + }) +} +const reset = () => { + RECORDED[0].length = 0; + isRecording = false; + isLoop = false; + } function audioEvent() { analyser.getFloatTimeDomainData(buf); @@ -485,7 +451,7 @@ function audioEvent() { }); const sum = audioSample.reduce((a, b) => a + b, 0); - const avg = (sum / audioSample.length) |rgba(16 18 27 / 40%)| 0; + const avg = (sum / audioSample.length) || 0; var attack = false; if (avg + 0.05 < meter.volume) { attack = true; @@ -531,13 +497,6 @@ function audioEvent() { } }); - var newTime = new Date().getTime(); - bpm = ((1 / ((newTime - time) / 1000)) * 60); - time = newTime; - if (record && bpm == 120) { - recording['events'].push([(newTime - time) / 1000, 'loud', 'data.controller.name', meter.volume * 1.4]); - } - // sleepFor(20); if (audio) { rafID = window.requestAnimationFrame(audioEvent); } @@ -592,30 +551,25 @@ function onMessage({ type, data }) { break; } case 'playLoop': { - playLoop = !playLoop; - playMidiLoop(data); + loop(); break; } case 'getLoop': { - chrome.runtime.sendMessage({ type: 'midiRecords', data: records }); + chrome.storage.sync.get(['records'], function (result) { + chrome.runtime.sendMessage({ type: 'midiRecords', data: result.records }); + }); break; } case 'record': { - record = data; - // If record in stopped add it to the list of records. - if (!record) { - recording['time'] = (new Date().getTime() - recording['time']) / 1000; - records.push(recording); - recording = { - time: new Date().getTime(), - events: [] - }; - chrome.runtime.sendMessage({ type: 'midiRecords', data: records }); - } - else { - time = new Date().getTime(); - recording['time'] = new Date().getTime(); - } + record(data); + break; + } + case 'resetLoop': { + reset(); + chrome.storage.sync.set({ 'records': RECORDED }); + chrome.storage.sync.get(['records'], function (result) { + chrome.runtime.sendMessage({ type: 'midiRecords', data: result.records }); + }); break; } } diff --git a/main.js b/main.js index 6d25ebb..328cff7 100644 --- a/main.js +++ b/main.js @@ -325,7 +325,7 @@ function onMessage({ type, data }) { case 'midiRecords': { $('#loopList').text(''); for (let i = 0; i < data.length; i++) { - $('#loopList').append('<tr><td>Loop ' + i + '</td><td>' + data[i]['time'] + 'ms</td><td>' + data[i]['events'].length + '</td><td><button id="loopPlay" value="' + i + '">▶️ Play</button></td></tr>'); + $('#loopList').append('<tr><td>Loop ' + i + '</td><td>' + data[i][data[i].length - 1].time + 'ms</td><td>' + data[i].length + '</td><td><button id="loopPlay" value="' + i + '">Play</button><button id="loopReset" value="' + i + '">Reset</button></td></tr>'); } break; } @@ -402,12 +402,13 @@ $('#record').on('click', function () { }); }); + $(document).on('click', '#loopPlay', function () { if ($(this).text().includes('Play')) { - $(this).text('⏸️ Pause'); + $(this).text('Pause'); chrome.storage.sync.set({ loopPlay: true }); } else { - $(this).text('▶️ Play'); + $(this).text('Play'); chrome.storage.sync.set({ loopPlay: false }); } @@ -415,7 +416,16 @@ $(document).on('click', '#loopPlay', function () { chrome.tabs.query({ active: true, currentWindow: true }, function (tabs) { chrome.tabs.sendMessage(tabs[0].id, { type: 'playLoop', data: id }); - }); + } + ); +}); +$(document).on('click', '#loopReset', function () { + id = $(this).val(); + chrome.tabs.query({ active: true, currentWindow: true }, + function (tabs) { + chrome.tabs.sendMessage(tabs[0].id, { type: 'resetLoop', data: id }); + } + ); }); $(document).on('click', '.icon-tabler-plus', function () { @@ -438,7 +448,8 @@ $(document).on('click', '.icon-tabler-minus', function () { // Wait messages from content script chrome.runtime.onMessage.addListener(onMessage); // Indicate to Background that the popup is ready -chrome.runtime.connect({ name: "popup" }); +var port = chrome.runtime.connect({ name: "popup" }); + // Load the editor loadEditor(); // Initialize the settings UI diff --git a/popup.html b/popup.html index 3ffc1fb..29f3fc8 100644 --- a/popup.html +++ b/popup.html @@ -100,7 +100,7 @@ <path d="M20 12v3a3 3 0 0 1 -3 3h-13m3 3l-3 -3l3 -3"></path> </svg> LOOPS </summary> - <button id="record">Record</button> BPM : <input id="bpm" type="number" value="120" disabled> + <button id="record">Record</button> <table width="100%"> <thead> <tr> -- GitLab