Skip to content

Commit

Permalink
String encoding for events
Browse files Browse the repository at this point in the history
  • Loading branch information
stephband committed Sep 25, 2023
1 parent 7c4b239 commit ab3d6a7
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 56 deletions.
67 changes: 44 additions & 23 deletions modules/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ sequence
**/


import capture from '../../fn/modules/capture.js';
import compose from '../../fn/modules/compose.js';
import get from '../../fn/modules/get.js';
import overload from '../../fn/modules/overload.js';
import Pool from '../../fn/modules/pool.js';
import remove from '../../fn/modules/remove.js';
import toType from '../../fn/modules/to-type.js';
import { bytesToSignedFloat } from '../../midi/modules/maths.js';
import { toType } from '../../midi/modules/data.js';
import { toType as toTypeMIDI } from '../../midi/modules/data.js';
import parseEvent from './parse/parse-event.js';

const assign = Object.assign;
const define = Object.defineProperties;
Expand Down Expand Up @@ -127,13 +130,17 @@ export function Event(time, type) {
throw new Error('Soundstage new Event() called with invalid arguments [' + Array.from(arguments).join(', ') + ']. ' + eventValidationHint(arguments));
}

const length = type === 'param' && arguments[4] === 'target' ?
6 :
(lengths[type] || lengths.default) ;

this[0] = time;
this[1] = type;

const l = this.length;

let n = 1;
while (++n < l) { this[n] = arguments[n]; }
while (++n < length) {
this[n] = arguments[n];
}
}

function reset() {
Expand All @@ -157,13 +164,10 @@ assign(Event, {
},

from: function(data) {
//const event = new Event(...data);
const event = Event.of.apply(Event, data);
/*event.originalEvent = data;*/
return event;
return Event.of.apply(Event, data);
},

fromMIDI: overload(compose(toType, getData), {
fromMIDI: overload(compose(toTypeMIDI, getData), {
pitch: function(e) {
return Event.of(e.timeStamp, 'pitch', pitchToFloat(e.data));
},
Expand All @@ -181,14 +185,32 @@ assign(Event, {
},

default: function(e) {
return Event.of(e.timeStamp, toType(e.data), e.data[1], e.data[2] / 127) ;
return Event.of(e.timeStamp, toTypeMIDI(e.data), e.data[1], e.data[2] / 127) ;
}
})
}),

// "time type ..."
parse: capture(/^\s*([-\d\.e]+)\s+(\w+)\s+/, {
2: (event, captures) => {
// time
event[0] = parseFloat(captures[1]);
// type
event[1] = captures[2];
// parameters
parseEvent(event, captures);
// Convert to event object
return Event.from(event);
}
}, []),

stringify: function(event) {
return Array.prototype.join.call(event, ' ');
}
});

assign(Event.prototype, {
toJSON: function() {
return Array.from(this);
return Event.stringify(this);
},

/**
Expand Down Expand Up @@ -224,14 +246,11 @@ define(Event.prototype, {
Event length.
**/
length: {
get: function() { return lengths[this[1]] || lengths.default; }
},

/**
.originalEvent
Original event this event was cloned from, if cloned via Event.from().
**/
/*originalEvent: { value: undefined, writable: true }*/
get: function() {
return this[1] === 'param' && this[4] === 'target' ? 6 :
(lengths[this[1]] || lengths.default) ;
}
}
});


Expand All @@ -241,9 +260,11 @@ A constructor for event objects for internal use.
**/

export default assign(Pool(Event, reset, isIdle), {
of: Event.of,
from: Event.from,
fromMIDI: Event.fromMIDI
of: Event.of,
from: Event.from,
fromMIDI: Event.fromMIDI,
parse: Event.parse,
stringify: Event.stringify
});


Expand Down
11 changes: 0 additions & 11 deletions modules/events/parse-events.js

This file was deleted.

65 changes: 65 additions & 0 deletions modules/parse/parse-event.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@

import get from '../../../fn/modules/get.js';
import id from '../../../fn/modules/id.js';
import capture from '../../../fn/modules/capture.js';
import overload from '../../../fn/modules/overload.js';
import Event from '../event.js';
import parseFrequency from '../parse/parse-frequency.js';
import parseGain from '../parse/parse-gain.js';

const tuning = 440;

/**
parseEvent(array, string)
Takes a sequence string and outputs an array of events.
**/

export default overload(get(1), {
// name gain duration
'note': capture(/^(\w+)\s+([^\s]+)\s+(\w+)\s*/, {
1: (event, captures) => {
event[2] = parseFrequency(tuning, captures[1]);
event[3] = parseGain(captures[2]);
event[4] = parseFloat(captures[3]);
return event;
}
}),

// name target duration
'sequence': capture(/^(\w+)\s+(\w+)\s+(\w+)\s*/, {
1: (event, captures) => {
event[2] = captures[1];
event[3] = captures[2];
event[4] = parseFloat(captures[3]);
return event;
}
}),

// name value [curve]
'param': capture(/^(\w+)\s+([-\d\.]+)(?:\s+(step|linear|exponential|target|curve))?\s*/, {
1: (event, captures) => {
event[2] = captures[1];
event[3] = parseFloat(captures[2]);
event[4] = 'step';
return event;
},

// curve
3: overload((event, captures) => captures[3], {
// duration
'target': capture(/^(\w+)\s*/, {
1: (event, captures) => {
event[4] = 'target';
event[5] = parseFloat(captures[1]);
return event;
}
}),

// Set curve
default: (event, captures) => {
event[4] = captures[3];
return event;
}
})
})
});
56 changes: 56 additions & 0 deletions modules/parse/parse-events.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

import get from '../../../fn/modules/get.js';
import id from '../../../fn/modules/id.js';
import capture from '../../../fn/modules/capture.js';
import overload from '../../../fn/modules/overload.js';
import Event from '../event.js';
import parseEvent from './parse-event.js';
import parseFrequency from './parse-frequency.js';
import parseGain from './parse-gain.js';

const types = {
1: 'note',
4: 'param',
5: 'sequence'
};

function parseType(value) {
// Crude but sort of effective
return types[value] || value;
}


/**
parseEvents(string)
Takes a sequence string and outputs an array of events.
**/

const event = [];
const parseEvents = capture(/^\s*([-\d\.e]+)\s+(\w+)\s+/, {
2: (data, captures) => {
// time
event[0] = parseFloat(captures[1]);
// type
event[1] = captures[2];
// parameters
parseEvent(event, captures);
// Convert to event object
data.push(Event.from(event));
return data;
},

close: (data, captures) => {
const consumed = (captures.consumed || 0)
+ captures.index
+ captures[0].length ;

return consumed < captures.input.length ?
parseEvents(data, captures) :
data ;
}
});

export default function(string) {
const data = [];
return parseEvents(data, string);
};
17 changes: 17 additions & 0 deletions modules/parse/parse-frequency.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

import id from '../../../fn/modules/id.js';
import overload from '../../../fn/modules/overload.js';
import toType from '../../../fn/modules/to-type.js';
import { toNoteNumber, floatToFrequency } from '../../../midi/modules/data.js';

const rdigit = /^\d/;

export default overload(toType, {
'number': id,
'string': (value, tuning) => (
rdigit.test(value) ?
value.slice(-2) === 'Hz' ? parseFloat(value) :
floatToFrequency(tuning, value) :
floatToFrequency(tuning, toNoteNumber(value))
)
});
8 changes: 8 additions & 0 deletions modules/parse/parse-gain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

import toGain from '../../../fn/modules/to-gain.js';
import parseValue from '../../../fn/modules/parse-value.js';

export default parseValue({
'': parseFloat,
'dB': toGain
});
18 changes: 14 additions & 4 deletions modules/sequencer.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import Playable, { IDLE, PLAYING } from './playable.js';
import { automate, getValueAtTime } from './automate.js';
import { isRateEvent, getDuration, isValidEvent, eventValidationHint } from './event.js';
import { timeAtBeatOfEvents } from './sequencer/location.js';
import parseEvents from './events/parse-events.js';
import parseEvent from './parse/parse-event.js';
import parseEvents from './parse/parse-events.js';

const assign = Object.assign;
const create = Object.create;
Expand Down Expand Up @@ -55,6 +56,13 @@ Sequencer()
```
**/

function sanitiseEvents(sequence) {
sequence.sequences && sequence.sequences.forEach(sanitiseEvents);
sequence.events = sequence.events
.map((data) => typeof data === 'string' ? Event.parse(data) : Event.from(data))
.sort(by0Float32);
}

export default function Sequencer(transport, output, events = [], sequences = []) {
// .context
// .startTime
Expand All @@ -65,15 +73,17 @@ export default function Sequencer(transport, output, events = [], sequences = []
Playable.call(this, transport.context);

this.transport = transport;
this.events = typeof evente === 'string' ?
parseEvents(events) :
events.sort(by0Float32) ;
this.events = typeof events === 'string' ?
parseEvents(events).sort(by0Float32) :
events.map((data) => typeof data === 'string' ? Event.parse(data) : Event.from(data)).sort(by0Float32) ;
this.sequences = sequences;
this.rate = transport.outputs.rate.offset;

const privates = Privates(this);
privates.beat = 0;
privates.output = output;

console.log('SEQUENCER', this.events, this.sequences);
}

assign(Sequencer.prototype, Meter.prototype, {
Expand Down
38 changes: 38 additions & 0 deletions modules/test/events-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@

import run from '../../../fn/modules/test.js';
import context from '../context.js';
import parseEvents from '../parse/parse-events.js';

run('Event.of()', [
[[0, 'note', 440, 1, 1]],
[[0, 'note', 440, 1, 1]],
[[0, 'note', 440, 1, 1]],
[[0, 'note', 440, 0.001, 1]],
[[1, 'sequence', 'name', 'target', 8]],
[[1.1, 'param', 'gain', 0, 'step']],
[[8.012, 'param', 'gain', 0.5, 'target', 3]],
[
[0, 'note', 440, 1, 1],
[0, 'note', 440, 1, 1],
[1, 'sequence', 'name', 'target', 8],
[1.1, 'param', 'gain', 0, 'step'],
[8.012, 'param', 'gain', 0.5, 'target', 3],
]
], function(test, done) {
test(parseEvents('0 note A4 1 1'));
test(parseEvents(' 0 note A4 1 1 '));
test(parseEvents(' 0 note A4 0dB 1 '));
test(parseEvents(' 0 note A4 -60dB 1 '));
test(parseEvents('1 sequence name target 8'));
test(parseEvents('1.1 param gain 0'));
test(parseEvents('8.012 param gain 0.5 target 3'));
test(parseEvents(`
0 note A4 1 1
0 note A4 0dB 1
1 sequence name target 8
1.1 param gain 0
8.012 param gain 0.5 target 3
`));

done();
}, 1);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
},
"author": {
"name": "Stephen Band",
"url": "http://stephen.band",
"url": "https://stephen.band",
"twitter": "stephband"
},
"bugs": {
Expand Down
Loading

0 comments on commit ab3d6a7

Please sign in to comment.