mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-13 22:35:15 -04:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5887d4b999 | |||
| 6211be5cc7 | |||
| 6855311d2d | |||
| d2b060a057 | |||
| 3f550cc6ce | |||
| 727fc2c049 | |||
| c0f006eef1 | |||
| adb13cc458 | |||
| 1ab3c16045 |
@@ -10,7 +10,7 @@ import Hap from './hap.mjs';
|
||||
import State from './state.mjs';
|
||||
import { unionWithObj } from './value.mjs';
|
||||
|
||||
import { isNote, toMidi, compose, removeUndefineds, flatten, id, listRange, curry, mod } from './util.mjs';
|
||||
import { isNote, toMidi, compose, removeUndefineds, flatten, id, listRange, curry, mod, objectify } from './util.mjs';
|
||||
import drawLine from './drawLine.mjs';
|
||||
|
||||
/** @class Class representing a pattern. */
|
||||
@@ -546,6 +546,10 @@ export class Pattern {
|
||||
return this._fromBipolar().range(min, max);
|
||||
}
|
||||
|
||||
union(other) {
|
||||
return this._opleft(other, (a) => (b) => Object.assign({}, objectify(a), objectify(b)));
|
||||
}
|
||||
|
||||
_bindWhole(choose_whole, func) {
|
||||
const pat_val = this;
|
||||
const query = function (state) {
|
||||
|
||||
@@ -6,7 +6,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
|
||||
import { strict as assert } from 'assert';
|
||||
import { pure } from '../pattern.mjs';
|
||||
import { isNote, tokenizeNote, toMidi, fromMidi, mod, compose, getFrequency } from '../util.mjs';
|
||||
import { isNote, tokenizeNote, toMidi, fromMidi, mod, compose, getFrequency, dirtify } from '../util.mjs';
|
||||
|
||||
describe('isNote', () => {
|
||||
it('should recognize notes without accidentals', () => {
|
||||
@@ -118,3 +118,31 @@ describe('compose', () => {
|
||||
assert.equal(compose(addS('a'), addS('b'))('x'), 'xab');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dirtify', () => {
|
||||
it('should offset plain number', () => {
|
||||
assert.deepStrictEqual(dirtify(61), { n: 1 });
|
||||
assert.deepStrictEqual(dirtify(60), { n: 0 });
|
||||
});
|
||||
it('should parse note string as midi number', () => {
|
||||
assert.deepStrictEqual(dirtify('c4'), { n: 0 });
|
||||
assert.deepStrictEqual(dirtify('c5'), { n: 12 });
|
||||
});
|
||||
it('should use non note string as sound', () => {
|
||||
assert.deepStrictEqual(dirtify('bd'), { s: 'bd' });
|
||||
});
|
||||
it('should keep objects as is', () => {
|
||||
assert.deepStrictEqual(dirtify({ s: 'bd' }), { s: 'bd' });
|
||||
assert.deepStrictEqual(dirtify({ n: 7 }), { n: 7 });
|
||||
assert.deepStrictEqual(dirtify({ n: 7, room: 0.5 }), { n: 7, room: 0.5 });
|
||||
});
|
||||
it('should parse notes inside object', () => {
|
||||
assert.deepStrictEqual(dirtify({ n: 'c3', s: 'supersaw' }), { n: -12, s: 'supersaw' });
|
||||
});
|
||||
it('should dirtify .value', () => {
|
||||
assert.deepStrictEqual(dirtify({ value: 61 }), { n: 1 });
|
||||
assert.deepStrictEqual(dirtify({ value: 'c5' }), { n: 12 });
|
||||
assert.deepStrictEqual(dirtify({ value: 'bd' }), { s: 'bd' });
|
||||
assert.deepStrictEqual(dirtify({ value: 61, room: 0.5 }), { n: 1, room: 0.5 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,3 +119,43 @@ export function curry(func, overload) {
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
// this functions just makes sure non objects values are wrapped in { value }
|
||||
export function objectify(value) {
|
||||
if (typeof value === 'object' && !Array.isArray(value)) {
|
||||
return value; // do nothing if its already an object
|
||||
}
|
||||
return { value };
|
||||
}
|
||||
|
||||
// maybe move this to osc?
|
||||
// turns value into object that is the right format superdirt osc
|
||||
export function dirtify(val) {
|
||||
const obj = objectify(val);
|
||||
|
||||
if (obj.n && typeof obj.n === 'string') {
|
||||
return { ...obj, ...dirtify(obj.n) };
|
||||
}
|
||||
const { value, ...rest } = obj;
|
||||
if (typeof value === 'undefined') {
|
||||
return rest;
|
||||
}
|
||||
const isNumber = typeof value === 'number';
|
||||
const isMidi = typeof value === 'string' && isNote(value);
|
||||
if (isNumber || isMidi) {
|
||||
const numberOffset = -60; // tidal 0 = midi 60
|
||||
const n = (isNumber ? value : toMidi(value)) + numberOffset;
|
||||
if (rest.n) {
|
||||
console.warn(`dirtify: n is already defined as "${rest.n}", overwriting with value "${n}"`);
|
||||
}
|
||||
return { ...rest, n };
|
||||
}
|
||||
if (typeof value === 'string') {
|
||||
// not a note => should be a sound
|
||||
return { ...rest, s: value };
|
||||
}
|
||||
console.warn(`dirtify: ignored value "${value}"`);
|
||||
// what lands here?
|
||||
// throw new Error('cannot objectify: ' + value);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ This program is free software: you can redistribute it and/or modify it under th
|
||||
*/
|
||||
|
||||
import OSC from 'osc-js';
|
||||
import { Pattern } from '@strudel.cycles/core';
|
||||
import { Pattern, objectify } from '@strudel.cycles/core';
|
||||
|
||||
const comm = new OSC();
|
||||
comm.open();
|
||||
@@ -30,7 +30,7 @@ Pattern.prototype.osc = function () {
|
||||
if (startedAt < 0) {
|
||||
startedAt = Date.now() - currentTime * 1000;
|
||||
}
|
||||
const controls = Object.assign({}, { cps, cycle, delta }, hap.value);
|
||||
const controls = Object.assign({}, { cps, cycle, delta }, objectify(hap.value));
|
||||
const keyvals = Object.entries(controls).flat();
|
||||
const ts = Math.floor(startedAt + (time + latency) * 1000);
|
||||
const message = new OSC.Message('/dirt/play', ...keyvals);
|
||||
@@ -41,3 +41,7 @@ Pattern.prototype.osc = function () {
|
||||
return hap.setContext({ ...hap.context, onTrigger });
|
||||
});
|
||||
};
|
||||
|
||||
Pattern.prototype.superdirt = function () {
|
||||
return this.withValue(dirtify).osc();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user