Skip to content
Snippets Groups Projects
Commit 02ba5801 authored by RANWEZ Pierre's avatar RANWEZ Pierre :anchor:
Browse files

:alembic: exp: loops (not working)

parent c7e197be
Branches
Tags
1 merge request!1✨ feat: CSSLSD V2
......@@ -33,7 +33,7 @@ To ***save*** your template, click on `Save` button or use keybind `CTRL`+`S`. T
|-------------|-----------|------------------------------------------|
| note | id | Note on, use case : [note:21] |
| cc | id, range | Control change, use case : [cc:10,0,100] |
| pb | | Pitchbend, use case : [pb,0,100] |
| pb | range | Pitchbend, use case : [pb,0,100] |
### Audio Variables
......@@ -58,6 +58,16 @@ a {
}
```
## Looping
***- - WIP FEATURE - -***
Click record button, play on MIDI device then stop recording.
On click, all tick defined buy a BPM, save midi envent in a disctionnary with a timestamp.
On loop play, execute CSS style in relation with loop.
## Ressources
- Chrome extensions : https://developer.chrome.com/docs/extensions/mv3/getstarted/
......
......@@ -6,6 +6,7 @@ let midiB = false;
let audioI = false;
let midiI = false;
let popup = false;
let loopPlay = false;
//Initialize the CSS storage on startup
chrome.runtime.onInstalled.addListener(() => {
......@@ -17,6 +18,7 @@ chrome.runtime.onInstalled.addListener(() => {
chrome.storage.sync.set({ audioI });
chrome.storage.sync.set({ midiI });
chrome.storage.sync.set({ popup });
chrome.storage.sync.set({ loopPlay });
});
/**
......
......@@ -9,6 +9,17 @@ 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;
/**
* Parse CSS with CSSParser and after add events to midi or audio.
......@@ -179,7 +190,7 @@ function midiEvent(type, data) {
});
midiEvents.forEach(event => {
midiValue = data.value;
templates = templateToDict(event['value']);
templates = event['templates'];
templates.forEach(template => {
midiValue = (template['min'] + ((template['max'] - template['min']) * midiValue));
if (type == 'noteon' && template['eventType'] == 'note' && template['eventName'] == data.note.number) {
......@@ -205,8 +216,80 @@ 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;
}
}
}
// Function that play recorded midi loop
function playMidiLoop(loopId) {
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);
......@@ -283,7 +366,7 @@ function audioApi() {
}
}
else {
console.warn('Audio suspend');
console.info('Audio suspend');
audioContext.suspend();
chrome.storage.sync.set({ audioI: false });
audio = false;
......@@ -320,7 +403,7 @@ function gotStream(stream) {
meter = createAudioMeter(audioContext);
mediaStreamSource.connect(meter);
audioContext.resume();
console.warn('Audio start');
console.info('Audio start');
chrome.storage.sync.set({ audioI: true });
chrome.runtime.sendMessage({ type: 'updateUi', data: true });
// kick off the visual updating
......@@ -481,6 +564,14 @@ 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);
......@@ -524,6 +615,33 @@ function onMessage({ type, data }) {
midiApi();
break;
}
case 'playLoop': {
playLoop = !playLoop;
playMidiLoop(data);
break;
}
case 'getLoop': {
chrome.runtime.sendMessage({ type: 'midiRecords', data: 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();
}
break;
}
}
}
......
......@@ -96,6 +96,11 @@ function initUi() {
$('.midiI').removeClass('active');
}
});
chrome.tabs.query({ active: true, currentWindow: true },
function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { type: 'getLoop', data: false });
}
);
}
......@@ -142,6 +147,13 @@ function onMessage({ type, data }) {
$('#midiDevices').text(data);
break;
}
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>');
}
break;
}
}
}
......@@ -245,6 +257,40 @@ $('.midiI').on('click', function () {
initUi();
});
/**
* Record loop button
*/
var record = false;
$('#record').on('click', function () {
record = !record;
if (record) {
$('#record').text('⏹️ Stop');
}
else {
$('#record').text('⏺️ Record');
}
chrome.tabs.query({ active: true, currentWindow: true },
function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { type: 'record', data: record });
});
});
$(document).on('click', '#loopPlay', function () {
if ($(this).text().includes('Play')) {
$(this).text('⏸️ Pause');
chrome.storage.sync.set({ loopPlay: true });
} else {
$(this).text('▶️ Play');
chrome.storage.sync.set({ loopPlay: false });
}
id = $(this).val();
chrome.tabs.query({ active: true, currentWindow: true },
function (tabs) {
chrome.tabs.sendMessage(tabs[0].id, { type: 'playLoop', data: id });
});
});
// Wait messages from content script
chrome.runtime.onMessage.addListener(onMessage);
......
......@@ -22,6 +22,24 @@
</div>
<div id="editor"></div>
<div>
<h3>Loop Settings</h3>
<button id="record">Record</button> BPM : <input id="bpm" type="number" value="120" disabled>
<table width="100%">
<thead>
<tr>
<th>Nom</th>
<th>Temps</th>
<th>Nombre d'evenements</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="loopList">
</tbody>
</table>
</div>
<script src="jquery-3.6.0.min.js" type="text/javascript" charset="utf-8"></script>
<script src="ace/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="ace/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment