update?
This commit is contained in:
@@ -1 +1 @@
|
|||||||
h1.svelte-1mwh9qx{color:red}
|
h1.svelte-pvij5y{text-align:center;color:red}.main-contianer.svelte-pvij5y{width:100%}h1.svelte-1f51h8v.svelte-1f51h8v{color:red;text-align:center}.unit.svelte-1f51h8v.svelte-1f51h8v{display:block}.lines.landscape.svelte-1f51h8v .unit.svelte-1f51h8v{display:inline-block}.bar.svelte-1f51h8v.svelte-1f51h8v{display:block;margin-bottom:1em}.lines.landscape.svelte-1f51h8v .bar.svelte-1f51h8v{display:inline-block;margin-bottom:0;margin-right:1em}.drum-line.svelte-1f51h8v.svelte-1f51h8v{display:block;overflow-x:scroll}.drum-line.svelte-1f51h8v h3.svelte-1f51h8v{display:inline-block;width:3em}.lines.landscape.svelte-1f51h8v .drum-line.svelte-1f51h8v{display:inline-block}.lines.svelte-1f51h8v.svelte-1f51h8v{width:100%;justify-content:center;display:flex;flex-direction:row;margin:auto}.lines.landscape.svelte-1f51h8v.svelte-1f51h8v{flex-direction:column}.unit.svelte-1lue60t{height:2em;width:2em;background-color:white;border:solid black 1px}.active.svelte-1lue60t{background-color:#d97474}.ghost.svelte-1lue60t{background-color:#bc8787}
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
146
src/Beat.ts
146
src/Beat.ts
@@ -1,46 +1,160 @@
|
|||||||
import BeatUnit from "./BeatUnit";
|
import BeatUnit, {BeatUnitType} from "./BeatUnit";
|
||||||
|
|
||||||
type BeatInitOptions = {
|
export type BeatInitOptions = {
|
||||||
timeSig: {
|
timeSig: {
|
||||||
up: number,
|
up: number,
|
||||||
down: number,
|
down: number,
|
||||||
},
|
},
|
||||||
bars: number,
|
bars: number,
|
||||||
|
drumSchema: string[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class Beat {
|
export default class Beat {
|
||||||
private timeSigUp: number = 4;
|
private timeSigUp: number = 4;
|
||||||
private timeSigDown: number = 4;
|
private timeSigDown: number = 4;
|
||||||
private units: BeatUnit[] = [];
|
private readonly unitRecords: Record<string, BeatUnit[]>;
|
||||||
|
private readonly drumSchema: string[];
|
||||||
|
private notify: () => void = () => {};
|
||||||
|
private barCount: number = 1;
|
||||||
|
|
||||||
constructor(options?: BeatInitOptions) {
|
constructor(options?: BeatInitOptions) {
|
||||||
this.setTimeSignature(options.timeSig.up, options.timeSig.down);
|
this.unitRecords = {};
|
||||||
this.setBars(options.bars);
|
if (options) {
|
||||||
|
this.drumSchema = [...options.drumSchema];
|
||||||
|
this.initUnitRecords();
|
||||||
|
this.setTimeSignature(options.timeSig.up, options.timeSig.down);
|
||||||
|
this.setBars(options.bars);
|
||||||
|
} else {
|
||||||
|
this.drumSchema = ['LF', 'LH', 'RH', 'RF'];
|
||||||
|
this.initUnitRecords();
|
||||||
|
this.setTimeSignature(4, 4);
|
||||||
|
this.setBars(48);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private initUnitRecords(): void {
|
||||||
|
for (const drumSchemaTag of this.drumSchema) {
|
||||||
|
this.unitRecords[drumSchemaTag] = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeSignature(up: number, down: number): void {
|
setTimeSignature(up: number, down: number): void {
|
||||||
if (Beat.isValidTimeSigRange(up)) {
|
if (Beat.isValidTimeSigRange(up)) {
|
||||||
this.timeSigUp = up | 0;
|
if (Beat.isValidTimeSigRange(down)) {
|
||||||
}
|
this.timeSigUp = up | 0;
|
||||||
if (Beat.isValidTimeSigRange(down)) {
|
this.timeSigDown = down | 0;
|
||||||
this.timeSigDown = down | 0;
|
this.updateBeatUnitLength();
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setBars(barCount: number): void {
|
setBars(barCount: number): void {
|
||||||
if (barCount*this.timeSigUp < this.units.length) {
|
const isPosInt = (barCount > 0 && (barCount | 0) === barCount);
|
||||||
this.units.splice(barCount, this.units.length - barCount);
|
if (!isPosInt || barCount == this.barCount) {
|
||||||
} else if (barCount > this.bars) {
|
return;
|
||||||
for (let i = 0; i < barCount; i++) {
|
}
|
||||||
this.units.push(new BeatUnit());
|
this.barCount = barCount;
|
||||||
|
this.updateBeatUnitLength();
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateBeatUnitLength() {
|
||||||
|
const newBarCount = this.barCount * this.timeSigUp;
|
||||||
|
for (const drumSchemaTag of this.drumSchema) {
|
||||||
|
const unitRecord = this.unitRecords[drumSchemaTag];
|
||||||
|
if (newBarCount < unitRecord.length) {
|
||||||
|
unitRecord.splice(this.barCount, unitRecord.length - newBarCount);
|
||||||
|
} else if (newBarCount > unitRecord.length) {
|
||||||
|
const barsToAdd = newBarCount - unitRecord.length;
|
||||||
|
for (let i = 0; i < barsToAdd; i++) {
|
||||||
|
unitRecord.push(new BeatUnit());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.bars = barCount;
|
getTimeSigUp(): number {
|
||||||
|
return this.timeSigUp;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTimeSigDown(): number {
|
||||||
|
return this.timeSigDown;
|
||||||
}
|
}
|
||||||
|
|
||||||
stringify(): string {
|
stringify(): string {
|
||||||
return "I am a Beat";
|
let stringified = this.drumSchema.join(" ");
|
||||||
|
stringified += "\n";
|
||||||
|
for (let i = 0; i < this.unitRecords[this.drumSchema[0]].length; i++) {
|
||||||
|
for (const drumSchemaTag of this.drumSchema) {
|
||||||
|
stringified += this.unitRecords[drumSchemaTag][i].stringify() + " ";
|
||||||
|
}
|
||||||
|
if (i % this.timeSigUp === 2) {
|
||||||
|
stringified += "\n";
|
||||||
|
}
|
||||||
|
stringified += "\n";
|
||||||
|
}
|
||||||
|
return stringified;
|
||||||
|
}
|
||||||
|
|
||||||
|
swapSchemaOrder(index1: number, index2: number): void {
|
||||||
|
if (this.drumSchema[index1] && this.drumSchema[index2]) {
|
||||||
|
const temp = this.drumSchema[index1];
|
||||||
|
this.drumSchema[index1] = this.drumSchema[index2];
|
||||||
|
this.drumSchema[index2] = temp;
|
||||||
|
}
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
turnUnitOn(schemaKey: string, index: number): void {
|
||||||
|
if (Math.abs(index | 0) !== index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.unitRecords[schemaKey] && this.unitRecords[schemaKey][index]) {
|
||||||
|
this.unitRecords[schemaKey][index].setOn(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
turnUnitOff(schemaKey: string, index: number): void {
|
||||||
|
if (Math.abs(index | 0) !== index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.unitRecords[schemaKey] && this.unitRecords[schemaKey][index]) {
|
||||||
|
this.unitRecords[schemaKey][index].setOn(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
toggleUnit(schemaKey: string, index: number): void {
|
||||||
|
if (Math.abs(index | 0) !== index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.unitRecords[schemaKey] && this.unitRecords[schemaKey][index]) {
|
||||||
|
this.unitRecords[schemaKey][index].toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setUnitType(schemaKey: string, index: number, type: BeatUnitType): void {
|
||||||
|
if (Math.abs(index | 0) !== index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.unitRecords[schemaKey]?.[index]?.setType(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate(updateCallback: () => void) {
|
||||||
|
this.notify = updateCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
getUnit(schemaKey: string, index: number): BeatUnit | null {
|
||||||
|
return this.unitRecords[schemaKey]?.[index] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDrumSchema(): string[] {
|
||||||
|
return this.drumSchema.slice();
|
||||||
|
}
|
||||||
|
|
||||||
|
getBarCount(): number {
|
||||||
|
return this.barCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static isValidTimeSigRange(sig: number): boolean {
|
private static isValidTimeSigRange(sig: number): boolean {
|
||||||
|
|||||||
@@ -1,8 +1,59 @@
|
|||||||
|
export enum BeatUnitType {
|
||||||
|
Normal,
|
||||||
|
GhostNote,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export default class BeatUnit {
|
export default class BeatUnit {
|
||||||
constructor() {
|
private on: boolean = false;
|
||||||
|
private type: BeatUnitType = BeatUnitType.Normal;
|
||||||
|
private onUpdateCallbacks: ((on: boolean, type: BeatUnitType) => void)[] = [];
|
||||||
|
|
||||||
|
constructor(on: boolean = false) {
|
||||||
|
this.on = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
stringify(): string {
|
stringify(): string {
|
||||||
return "U";
|
if (!this.on) {
|
||||||
|
return "O";
|
||||||
|
}
|
||||||
|
if (this.type === BeatUnitType.GhostNote) {
|
||||||
|
return "G";
|
||||||
|
} else {
|
||||||
|
return "#";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(): void {
|
||||||
|
this.on = !this.on;
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
setOn(on: boolean): void {
|
||||||
|
this.on = on;
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
setType(type: BeatUnitType) {
|
||||||
|
this.type = type;
|
||||||
|
this.notify();
|
||||||
|
}
|
||||||
|
|
||||||
|
getType(): BeatUnitType {
|
||||||
|
return this.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
isOn(): boolean {
|
||||||
|
return this.on;
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdate(callback: (on: boolean, type: BeatUnitType) => void) {
|
||||||
|
this.onUpdateCallbacks.push(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
notify(): void {
|
||||||
|
for (const cb of this.onUpdateCallbacks) {
|
||||||
|
cb(this.on, this.type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/Store.ts
Normal file
20
src/Store.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import type BeatUnit from "./BeatUnit";
|
||||||
|
import Beat, {BeatInitOptions} from "./Beat";
|
||||||
|
|
||||||
|
export default class Store {
|
||||||
|
private beat: Beat;
|
||||||
|
constructor(options: BeatInitOptions) {
|
||||||
|
this.beat = new Beat(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBeat() {
|
||||||
|
return this.beat;
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribeBeatUnit(schemaKey: string, index: number, callback: (unit: BeatUnit) => void): BeatUnit {
|
||||||
|
this.beat.onUnitUpdate(() => {
|
||||||
|
callback(this.beat.getUnit(schemaKey, index));
|
||||||
|
});
|
||||||
|
return this.beat.getUnit(schemaKey, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main.ts
12
src/main.ts
@@ -1,8 +1,18 @@
|
|||||||
import App from './ui/App.svelte';
|
import App from './ui/App.svelte';
|
||||||
|
import Store from "./Store";
|
||||||
|
|
||||||
const app = new App({
|
const app = new App({
|
||||||
target: document.body,
|
target: document.body,
|
||||||
props: {},
|
props: {
|
||||||
|
store: new Store({
|
||||||
|
bars: 10,
|
||||||
|
timeSig: {
|
||||||
|
down: 4,
|
||||||
|
up: 4,
|
||||||
|
},
|
||||||
|
drumSchema: ['LF', 'LH', 'RH', 'RF'],
|
||||||
|
}),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
13
src/tests.ts
13
src/tests.ts
@@ -0,0 +1,13 @@
|
|||||||
|
import {BeatUnitType} from "./BeatUnit";
|
||||||
|
import Beat from "./Beat";
|
||||||
|
|
||||||
|
const beat = new Beat({
|
||||||
|
drumSchema: ["LH", "RH", "LF", "LR"],
|
||||||
|
timeSig: {
|
||||||
|
up: 3,
|
||||||
|
down: 4,
|
||||||
|
},
|
||||||
|
bars: 10,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(beat.stringify());
|
||||||
@@ -1,12 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import Store from "../Store";
|
||||||
|
import Beat from "./Beat.svelte";
|
||||||
|
|
||||||
|
export let store: Store;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<h1>
|
<h1>
|
||||||
Hello, world!
|
ArneDrums
|
||||||
</h1>
|
</h1>
|
||||||
|
<div class="main-contianer">
|
||||||
|
<Beat beat="{store.getBeat()}"/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h1 {
|
h1 {
|
||||||
|
text-align: center;
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
.main-contianer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
104
src/ui/Beat.svelte
Normal file
104
src/ui/Beat.svelte
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type Beat from "../Beat";
|
||||||
|
import BeatUnit from "./BeatUnit.svelte";
|
||||||
|
|
||||||
|
export let beat: Beat;
|
||||||
|
export let landscape: boolean = true;
|
||||||
|
|
||||||
|
beat.onUpdate(() => {
|
||||||
|
beat = beat;
|
||||||
|
});
|
||||||
|
|
||||||
|
$: timeSigUp = beat.getTimeSigUp();
|
||||||
|
$: barCount = beat.getBarCount();
|
||||||
|
$: drumSchema = beat.getDrumSchema();
|
||||||
|
|
||||||
|
function addBar() {
|
||||||
|
beat.setBars(beat.getBarCount() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeBar() {
|
||||||
|
beat.setBars(beat.getBarCount() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInputTimeSigUp(e: InputEvent) {
|
||||||
|
const sigUp = Number((e.target as HTMLInputElement).value);
|
||||||
|
beat.setTimeSignature(sigUp, beat.getTimeSigDown());
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleInputTimeSigDown(e: InputEvent) {
|
||||||
|
const sigDown = Number((e.target as HTMLInputElement).value);
|
||||||
|
beat.setTimeSignature(beat.getTimeSigUp(), sigDown);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h1>Beat</h1>
|
||||||
|
|
||||||
|
<button on:click={addBar}>Add Bar</button>
|
||||||
|
<button on:click={removeBar}>Remove Bar</button>
|
||||||
|
<button on:click={() => landscape = !landscape}>Toggle View</button>
|
||||||
|
<h3>Time Sig</h3>
|
||||||
|
<input type="text" value="{beat.getTimeSigUp()}" on:input={handleInputTimeSigUp}/>
|
||||||
|
<p>---</p>
|
||||||
|
<input type="text" value="{beat.getTimeSigDown()}" on:input={handleInputTimeSigDown}/>
|
||||||
|
|
||||||
|
<div class="lines" class:landscape={landscape}>
|
||||||
|
{#each drumSchema as drumSchemaKey}
|
||||||
|
<div class="drum-line">
|
||||||
|
<h3>{drumSchemaKey}</h3>
|
||||||
|
{#each {length: barCount} as _, barIndex}
|
||||||
|
<div class="bar">
|
||||||
|
{#each {length: timeSigUp} as _, noteIndex}
|
||||||
|
<div class="unit">
|
||||||
|
<BeatUnit unit="{beat.getUnit(drumSchemaKey, timeSigUp*barIndex + noteIndex)}"/>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<style>
|
||||||
|
h1 {
|
||||||
|
color: red;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.unit {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.lines.landscape .unit {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.bar {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
.lines.landscape .bar {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: 0;
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
.drum-line {
|
||||||
|
display: block;
|
||||||
|
overflow-x: scroll;
|
||||||
|
}
|
||||||
|
.drum-line h3 {
|
||||||
|
display: inline-block;
|
||||||
|
width: 3em;
|
||||||
|
}
|
||||||
|
.lines.landscape .drum-line {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.lines {
|
||||||
|
width: 100%;
|
||||||
|
justify-content: center;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
.lines.landscape {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
35
src/ui/BeatUnit.svelte
Normal file
35
src/ui/BeatUnit.svelte
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type BeatUnit from "../BeatUnit";
|
||||||
|
import {BeatUnitType} from "../BeatUnit";
|
||||||
|
|
||||||
|
export let unit: BeatUnit;
|
||||||
|
|
||||||
|
unit.onUpdate(() => {
|
||||||
|
unit = unit;
|
||||||
|
});
|
||||||
|
|
||||||
|
$: ghost = unit.getType() === BeatUnitType.GhostNote;
|
||||||
|
$: active = unit.isOn();
|
||||||
|
|
||||||
|
function onClick() {
|
||||||
|
unit.toggle();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="unit" class:ghost={ghost} class:active={active} on:click={onClick}>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.unit {
|
||||||
|
height: 2em;
|
||||||
|
width: 2em;
|
||||||
|
background-color: white;
|
||||||
|
border: solid black 1px;
|
||||||
|
}
|
||||||
|
.active {
|
||||||
|
background-color: #d97474;
|
||||||
|
}
|
||||||
|
.ghost {
|
||||||
|
background-color: #bc8787;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user