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
|
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)
|
js/application.js : $(SRC)
|
||||||
esbuild --bundle --jsx-factory=preact.h --jsx-fragment=preact.Fragment --format=esm \
|
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
|
// src/application.jsx
|
||||||
var C4 = 4 * 12;
|
var C4 = 4 * 12;
|
||||||
function nmultiply(x) {
|
function nmultiply(x) {
|
||||||
|
@ -324,6 +418,28 @@ function HyperDisplay(props) {
|
||||||
const relative = offset < 0 ? -offset + 32768 : offset;
|
const relative = offset < 0 ? -offset + 32768 : offset;
|
||||||
return /* @__PURE__ */ preact.h("div", null, "Relative: ", relative);
|
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 {
|
var Application = class extends preact.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -332,6 +448,7 @@ var Application = class extends preact.Component {
|
||||||
this._resChange = this.resChange.bind(this);
|
this._resChange = this.resChange.bind(this);
|
||||||
this._freqChange = this.freqChange.bind(this);
|
this._freqChange = this.freqChange.bind(this);
|
||||||
this._noteChange = this.noteChange.bind(this);
|
this._noteChange = this.noteChange.bind(this);
|
||||||
|
this._durationChange = this.durationChange.bind(this);
|
||||||
this._tabChange = this.tabChange.bind(this);
|
this._tabChange = this.tabChange.bind(this);
|
||||||
this._asmChange = this.asmChange.bind(this);
|
this._asmChange = this.asmChange.bind(this);
|
||||||
this._shapeChange = this.shapeChange.bind(this);
|
this._shapeChange = this.shapeChange.bind(this);
|
||||||
|
@ -398,6 +515,9 @@ var Application = class extends preact.Component {
|
||||||
noteChange(v) {
|
noteChange(v) {
|
||||||
this.setState({ note: v });
|
this.setState({ note: v });
|
||||||
}
|
}
|
||||||
|
durationChange(v) {
|
||||||
|
this.setState({ duration: v });
|
||||||
|
}
|
||||||
asmChange(e) {
|
asmChange(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var v = +e.target.value;
|
var v = +e.target.value;
|
||||||
|
@ -477,6 +597,19 @@ var Application = class extends preact.Component {
|
||||||
freq: in_freq
|
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() {
|
hyperChildren() {
|
||||||
var { in_freq, note, indeterminate } = this.state;
|
var { in_freq, note, indeterminate } = this.state;
|
||||||
if (indeterminate)
|
if (indeterminate)
|
||||||
|
@ -518,10 +651,13 @@ var Application = class extends preact.Component {
|
||||||
children = this.waveChildren();
|
children = this.waveChildren();
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
|
children = this.timerChildren();
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
children = this.hyperChildren();
|
children = this.hyperChildren();
|
||||||
break;
|
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", {
|
return /* @__PURE__ */ preact.h("option", {
|
||||||
key: ix,
|
key: ix,
|
||||||
value: ix
|
value: ix
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { WaveData } from './wave_data';
|
||||||
|
|
||||||
import { Oscillators, WaveSize, Resolution, Frequency, Assembler, WaveShape, CheckBox } from './input';
|
import { Oscillators, WaveSize, Resolution, Frequency, Assembler, WaveShape, CheckBox } from './input';
|
||||||
|
|
||||||
|
import { DurationInput, DurationToSeconds } from './duration_input';
|
||||||
|
|
||||||
const C4 = 4*12;
|
const C4 = 4*12;
|
||||||
|
|
||||||
|
@ -149,6 +150,8 @@ function HyperDisplay(props) {
|
||||||
// 261.63 = C4
|
// 261.63 = C4
|
||||||
// 3072 = 12 * 256 (12 = octave)
|
// 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 r = (freq * 261.63 )/ (26_320 * pitch);
|
||||||
|
|
||||||
const offset = Math.round(3072 * Math.log2(r));
|
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.
|
// oscillators generate addresses, not samples.
|
||||||
// accumulator is 24-bit.
|
// accumulator is 24-bit.
|
||||||
// frequency is 16-bit.
|
// frequency is 16-bit.
|
||||||
|
@ -176,6 +220,7 @@ export class Application extends preact.Component {
|
||||||
this._resChange = this.resChange.bind(this);
|
this._resChange = this.resChange.bind(this);
|
||||||
this._freqChange = this.freqChange.bind(this);
|
this._freqChange = this.freqChange.bind(this);
|
||||||
this._noteChange = this.noteChange.bind(this);
|
this._noteChange = this.noteChange.bind(this);
|
||||||
|
this._durationChange = this.durationChange.bind(this);
|
||||||
this._tabChange = this.tabChange.bind(this);
|
this._tabChange = this.tabChange.bind(this);
|
||||||
this._asmChange = this.asmChange.bind(this);
|
this._asmChange = this.asmChange.bind(this);
|
||||||
this._shapeChange = this.shapeChange.bind(this);
|
this._shapeChange = this.shapeChange.bind(this);
|
||||||
|
@ -243,6 +288,10 @@ export class Application extends preact.Component {
|
||||||
this.setState({ note: v });
|
this.setState({ note: v });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
durationChange(v) {
|
||||||
|
this.setState({ duration: v });
|
||||||
|
}
|
||||||
|
|
||||||
asmChange(e) {
|
asmChange(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
var v = +e.target.value;
|
var v = +e.target.value;
|
||||||
|
@ -261,6 +310,8 @@ export class Application extends preact.Component {
|
||||||
this.setState({ indeterminate: v });
|
this.setState({ indeterminate: v });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
sampleChildren() {
|
sampleChildren() {
|
||||||
|
|
||||||
var { osc, wave, res, freq } = this.state;
|
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() {
|
hyperChildren() {
|
||||||
|
|
||||||
var { in_freq, note, indeterminate } = this.state;
|
var { in_freq, note, indeterminate } = this.state;
|
||||||
|
@ -396,10 +465,11 @@ export class Application extends preact.Component {
|
||||||
case 1: children = this.resampleChildren(); break;
|
case 1: children = this.resampleChildren(); break;
|
||||||
case 2: children = this.noteChildren(); break;
|
case 2: children = this.noteChildren(); break;
|
||||||
case 3: children = this.waveChildren(); 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>;
|
return <option key={ix} value={ix}>{o}</option>;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -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