mirror of
https://github.com/ksherlock/ensoniq-buddy.git
synced 2024-06-09 23:29:28 +00:00
add a timer generator
This commit is contained in:
parent
075b578cc8
commit
92f0f00f95
3
Makefile
3
Makefile
|
@ -2,7 +2,8 @@
|
|||
|
||||
all: js/application.js js/preact.min.js | js
|
||||
|
||||
SRC = src/main.jsx src/application.jsx src/note_input.jsx src/wave_data.jsx src/utils.js src/input.jsx
|
||||
SRC = src/main.jsx src/application.jsx src/note_input.jsx src/wave_data.jsx src/utils.js \
|
||||
src/input.jsx src/duration_input.jsx
|
||||
|
||||
js/application.js : $(SRC)
|
||||
esbuild --bundle --jsx-factory=preact.h --jsx-fragment=preact.Fragment --format=esm \
|
||||
|
|
|
@ -244,6 +244,100 @@ function CheckBox(props) {
|
|||
});
|
||||
}
|
||||
|
||||
// src/duration_input.jsx
|
||||
function split_value2(x) {
|
||||
if (x === void 0 || x === null)
|
||||
return ["", 0];
|
||||
if (typeof x == "number")
|
||||
return [x, 0];
|
||||
var xx = x.split(":");
|
||||
if (xx.length == 2)
|
||||
return xx;
|
||||
if (xx.length == 1)
|
||||
return [x, 0];
|
||||
return ["", 0];
|
||||
}
|
||||
function DurationToSeconds(x) {
|
||||
let [time, unit] = split_value2(x);
|
||||
switch (+unit) {
|
||||
case 0:
|
||||
return +time;
|
||||
case 1:
|
||||
return +time / 1e3;
|
||||
case 2:
|
||||
return +time / 60;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
var DurationInput = class extends preact.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._amtChange = this.amtChange.bind(this);
|
||||
this._unitChange = this.unitChange.bind(this);
|
||||
}
|
||||
amtChange(e) {
|
||||
e.preventDefault();
|
||||
let { value } = this.props;
|
||||
var [time, unit] = split_value2(value);
|
||||
var new_time = e.target.value.replace(/^\s+|\s+$/g, "");
|
||||
var n = Number(new_time);
|
||||
if (Number.isNaN(n)) {
|
||||
e.target.value = time;
|
||||
return;
|
||||
}
|
||||
this.change(new_time, unit);
|
||||
}
|
||||
unitChange(e) {
|
||||
e.preventDefault();
|
||||
let { value } = this.props;
|
||||
var [time, unit] = split_value2(value);
|
||||
var new_unit = +e.target.value;
|
||||
let s = DurationToSeconds(value);
|
||||
if (new_unit == unit)
|
||||
return;
|
||||
var new_time = 0;
|
||||
switch (new_unit) {
|
||||
case 0:
|
||||
new_time = s;
|
||||
break;
|
||||
case 1:
|
||||
new_time = s * 1e3;
|
||||
break;
|
||||
case 2:
|
||||
new_time = s * 60;
|
||||
break;
|
||||
}
|
||||
this.change(new_time, new_unit);
|
||||
}
|
||||
change(time, unit) {
|
||||
let { onChange } = this.props;
|
||||
if (onChange) {
|
||||
onChange(time + ":" + unit);
|
||||
}
|
||||
}
|
||||
render() {
|
||||
var { value, disabled } = this.props;
|
||||
var [amt, unit] = split_value2(value);
|
||||
var options = ["Seconds", "Milliseconds", "Ticks"].map((x, ix) => {
|
||||
return /* @__PURE__ */ preact.h("option", {
|
||||
key: ix,
|
||||
value: ix
|
||||
}, x);
|
||||
});
|
||||
return /* @__PURE__ */ preact.h(preact.Fragment, null, /* @__PURE__ */ preact.h("input", {
|
||||
type: "text",
|
||||
value: amt,
|
||||
disabled,
|
||||
onChange: this._amtChange
|
||||
}), " ", /* @__PURE__ */ preact.h("select", {
|
||||
value: unit,
|
||||
disabled,
|
||||
onChange: this._unitChange
|
||||
}, options));
|
||||
}
|
||||
};
|
||||
|
||||
// src/application.jsx
|
||||
var C4 = 4 * 12;
|
||||
function nmultiply(x) {
|
||||
|
@ -324,6 +418,28 @@ function HyperDisplay(props) {
|
|||
const relative = offset < 0 ? -offset + 32768 : offset;
|
||||
return /* @__PURE__ */ preact.h("div", null, "Relative: ", relative);
|
||||
}
|
||||
function TimerDisplay(props) {
|
||||
var { osc, time } = props;
|
||||
const sr = calc_sr(osc);
|
||||
const cycles = time * sr;
|
||||
const size = 0;
|
||||
var best_res = 0;
|
||||
var best_freq = 0;
|
||||
for (var res = 0; res < 8; ++res) {
|
||||
var shift = 1 << calc_shift(res, size);
|
||||
var f = Math.round(cycles * shift / 256);
|
||||
if (f >= 65536)
|
||||
break;
|
||||
best_res = res;
|
||||
best_freq = f;
|
||||
}
|
||||
[best_res, best_freq] = simplify(best_res, best_freq);
|
||||
var best_shift = calc_shift(best_res, size);
|
||||
return /* @__PURE__ */ preact.h(preact.Fragment, null, /* @__PURE__ */ preact.h("div", null, "Resolution: ", best_res ? best_res : "N/A"), /* @__PURE__ */ preact.h("div", null, "Frequency: ", best_freq ? best_freq : "N/A"), /* @__PURE__ */ preact.h(SampleDisplay, {
|
||||
freq: best_freq,
|
||||
shift: best_shift
|
||||
}));
|
||||
}
|
||||
var Application = class extends preact.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -332,6 +448,7 @@ var Application = class extends preact.Component {
|
|||
this._resChange = this.resChange.bind(this);
|
||||
this._freqChange = this.freqChange.bind(this);
|
||||
this._noteChange = this.noteChange.bind(this);
|
||||
this._durationChange = this.durationChange.bind(this);
|
||||
this._tabChange = this.tabChange.bind(this);
|
||||
this._asmChange = this.asmChange.bind(this);
|
||||
this._shapeChange = this.shapeChange.bind(this);
|
||||
|
@ -398,6 +515,9 @@ var Application = class extends preact.Component {
|
|||
noteChange(v) {
|
||||
this.setState({ note: v });
|
||||
}
|
||||
durationChange(v) {
|
||||
this.setState({ duration: v });
|
||||
}
|
||||
asmChange(e) {
|
||||
e.preventDefault();
|
||||
var v = +e.target.value;
|
||||
|
@ -477,6 +597,19 @@ var Application = class extends preact.Component {
|
|||
freq: in_freq
|
||||
}));
|
||||
}
|
||||
timerChildren() {
|
||||
var { osc, duration } = this.state;
|
||||
return /* @__PURE__ */ preact.h(preact.Fragment, null, /* @__PURE__ */ preact.h("div", null, /* @__PURE__ */ preact.h("label", null, "Oscillators"), " ", /* @__PURE__ */ preact.h(Oscillators, {
|
||||
value: osc,
|
||||
onChange: this._oscChange
|
||||
})), /* @__PURE__ */ preact.h("div", null, /* @__PURE__ */ preact.h("label", null, "Duration"), " ", /* @__PURE__ */ preact.h(DurationInput, {
|
||||
value: duration,
|
||||
onChange: this._durationChange
|
||||
})), /* @__PURE__ */ preact.h(TimerDisplay, {
|
||||
osc,
|
||||
time: DurationToSeconds(duration)
|
||||
}));
|
||||
}
|
||||
hyperChildren() {
|
||||
var { in_freq, note, indeterminate } = this.state;
|
||||
if (indeterminate)
|
||||
|
@ -518,10 +651,13 @@ var Application = class extends preact.Component {
|
|||
children = this.waveChildren();
|
||||
break;
|
||||
case 4:
|
||||
children = this.timerChildren();
|
||||
break;
|
||||
case 5:
|
||||
children = this.hyperChildren();
|
||||
break;
|
||||
}
|
||||
var options = ["Sample", "Resample", "Note", "Wave", "HyperCard Pitch"].map((o, ix) => {
|
||||
var options = ["Sample", "Resample", "Note", "Wave", "Timer", "HyperCard Pitch"].map((o, ix) => {
|
||||
return /* @__PURE__ */ preact.h("option", {
|
||||
key: ix,
|
||||
value: ix
|
||||
|
|
|
@ -8,6 +8,7 @@ import { WaveData } from './wave_data';
|
|||
|
||||
import { Oscillators, WaveSize, Resolution, Frequency, Assembler, WaveShape, CheckBox } from './input';
|
||||
|
||||
import { DurationInput, DurationToSeconds } from './duration_input';
|
||||
|
||||
const C4 = 4*12;
|
||||
|
||||
|
@ -149,6 +150,8 @@ function HyperDisplay(props) {
|
|||
// 261.63 = C4
|
||||
// 3072 = 12 * 256 (12 = octave)
|
||||
|
||||
// "The high byte of this word is a semitone value; the low byte is a fractional semitone."
|
||||
|
||||
const r = (freq * 261.63 )/ (26_320 * pitch);
|
||||
|
||||
const offset = Math.round(3072 * Math.log2(r));
|
||||
|
@ -160,6 +163,47 @@ function HyperDisplay(props) {
|
|||
);
|
||||
}
|
||||
|
||||
|
||||
function TimerDisplay(props) {
|
||||
var {osc, time } = props;
|
||||
|
||||
const sr = calc_sr(osc);
|
||||
|
||||
const cycles = time * sr;
|
||||
|
||||
|
||||
// (f * 256) / shift = cycles
|
||||
// f * 256 = cycles * shift
|
||||
// f = (cycles * shift) / 256
|
||||
|
||||
// should calculate min. wave size. eg, 256k sample has min. shift / 512, 32768 has min shift of / 2
|
||||
|
||||
const size = 0; // 256
|
||||
|
||||
var best_res = 0;
|
||||
var best_freq = 0;
|
||||
for (var res = 0; res < 8; ++res) {
|
||||
var shift = 1 << calc_shift(res, size);
|
||||
var f = Math.round(cycles * shift / 256);
|
||||
if (f >= 0x10000) break;
|
||||
best_res = res;
|
||||
best_freq = f;
|
||||
}
|
||||
|
||||
[best_res, best_freq] = simplify(best_res, best_freq);
|
||||
var best_shift = calc_shift(best_res, size);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>Resolution: {best_res ? best_res : "N/A"}</div>
|
||||
<div>Frequency: {best_freq ? best_freq : "N/A"}</div>
|
||||
<SampleDisplay freq={best_freq} shift={best_shift} />
|
||||
</>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// oscillators generate addresses, not samples.
|
||||
// accumulator is 24-bit.
|
||||
// frequency is 16-bit.
|
||||
|
@ -176,6 +220,7 @@ export class Application extends preact.Component {
|
|||
this._resChange = this.resChange.bind(this);
|
||||
this._freqChange = this.freqChange.bind(this);
|
||||
this._noteChange = this.noteChange.bind(this);
|
||||
this._durationChange = this.durationChange.bind(this);
|
||||
this._tabChange = this.tabChange.bind(this);
|
||||
this._asmChange = this.asmChange.bind(this);
|
||||
this._shapeChange = this.shapeChange.bind(this);
|
||||
|
@ -243,6 +288,10 @@ export class Application extends preact.Component {
|
|||
this.setState({ note: v });
|
||||
}
|
||||
|
||||
durationChange(v) {
|
||||
this.setState({ duration: v });
|
||||
}
|
||||
|
||||
asmChange(e) {
|
||||
e.preventDefault();
|
||||
var v = +e.target.value;
|
||||
|
@ -261,6 +310,8 @@ export class Application extends preact.Component {
|
|||
this.setState({ indeterminate: v });
|
||||
}
|
||||
|
||||
|
||||
|
||||
sampleChildren() {
|
||||
|
||||
var { osc, wave, res, freq } = this.state;
|
||||
|
@ -348,6 +399,24 @@ export class Application extends preact.Component {
|
|||
);
|
||||
}
|
||||
|
||||
timerChildren() {
|
||||
var { osc, duration } = this.state;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<label>Oscillators</label> <Oscillators value={osc} onChange={this._oscChange} />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Duration</label> <DurationInput value={duration} onChange={this._durationChange} />
|
||||
</div>
|
||||
|
||||
<TimerDisplay osc={osc} time={DurationToSeconds(duration)} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
hyperChildren() {
|
||||
|
||||
var { in_freq, note, indeterminate } = this.state;
|
||||
|
@ -396,10 +465,11 @@ export class Application extends preact.Component {
|
|||
case 1: children = this.resampleChildren(); break;
|
||||
case 2: children = this.noteChildren(); break;
|
||||
case 3: children = this.waveChildren(); break;
|
||||
case 4: children = this.hyperChildren(); break;
|
||||
case 4: children = this.timerChildren(); break;
|
||||
case 5: children = this.hyperChildren(); break;
|
||||
}
|
||||
|
||||
var options = ["Sample", "Resample", "Note", "Wave", "HyperCard Pitch"].map( (o, ix) => {
|
||||
var options = ["Sample", "Resample", "Note", "Wave", "Timer", "HyperCard Pitch",].map( (o, ix) => {
|
||||
return <option key={ix} value={ix}>{o}</option>;
|
||||
});
|
||||
|
||||
|
|
108
src/duration_input.jsx
Normal file
108
src/duration_input.jsx
Normal file
|
@ -0,0 +1,108 @@
|
|||
|
||||
function split_value(x) {
|
||||
|
||||
if (x === undefined || x === null) return ["", 0];
|
||||
|
||||
if (typeof(x) == "number") return [x, 0];
|
||||
|
||||
var xx = x.split(':');
|
||||
|
||||
if (xx.length == 2) return xx;
|
||||
if (xx.length == 1) return [x, 0];
|
||||
|
||||
return [ "", 0];
|
||||
}
|
||||
|
||||
export function DurationToSeconds(x) {
|
||||
|
||||
let [ time, unit ] = split_value(x);
|
||||
switch (+unit) {
|
||||
case 0: return +time; // seconds;
|
||||
case 1: return +time / 1000 ; // milliseconds;
|
||||
case 2: return +time / 60; // ticks
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
export class DurationInput extends preact.Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this._amtChange = this.amtChange.bind(this);
|
||||
this._unitChange = this.unitChange.bind(this);
|
||||
|
||||
}
|
||||
|
||||
amtChange(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let { value } = this.props;
|
||||
var [ time, unit ] = split_value(value);
|
||||
|
||||
var new_time = e.target.value.replace(/^\s+|\s+$/g,"");
|
||||
|
||||
var n = Number(new_time);
|
||||
if (Number.isNaN(n)) {
|
||||
e.target.value = time;
|
||||
return; // error.
|
||||
}
|
||||
|
||||
|
||||
this.change(new_time, unit);
|
||||
}
|
||||
|
||||
unitChange(e) {
|
||||
e.preventDefault();
|
||||
|
||||
let { value } = this.props;
|
||||
var [ time, unit ] = split_value(value);
|
||||
var new_unit = +e.target.value;
|
||||
|
||||
|
||||
let s = DurationToSeconds(value);
|
||||
|
||||
|
||||
if (new_unit == unit) return;
|
||||
var new_time = 0;
|
||||
switch(new_unit) {
|
||||
case 0: new_time = s; break;
|
||||
case 1: new_time = s * 1000; break;
|
||||
case 2: new_time = s * 60; break;
|
||||
}
|
||||
|
||||
this.change(new_time, new_unit);
|
||||
}
|
||||
|
||||
change(time, unit) {
|
||||
let { onChange } = this.props;
|
||||
|
||||
if (onChange) {
|
||||
onChange(time + ":" + unit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
|
||||
var { value,disabled } = this.props;
|
||||
|
||||
var [ amt, unit ] = split_value(value);
|
||||
|
||||
var options = ["Seconds", "Milliseconds", "Ticks"].map( (x, ix) => {
|
||||
|
||||
return <option key={ix} value={ix}>{x}</option>
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<input type="text" value={amt} disabled={disabled} onChange={this._amtChange} />
|
||||
{' '}
|
||||
<select value={unit} disabled={disabled} onChange={this._unitChange}>{options}</select>
|
||||
</>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user