add a timer generator

This commit is contained in:
Kelvin Sherlock 2021-10-23 20:15:34 -04:00
parent 075b578cc8
commit 92f0f00f95
4 changed files with 319 additions and 4 deletions

View File

@ -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 \

View File

@ -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

View File

@ -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
View 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>
</>
);
}
}