refactor: Moving to svelteless, added Jenkinsfile
Some checks failed
Gitea djledda.de/arne-drums/pipeline/head There was a failure building this commit

This commit is contained in:
Daniel Ledda
2021-08-28 20:57:20 +02:00
parent b1e65acda6
commit 87b9078072
20 changed files with 8409 additions and 1064 deletions

View File

@@ -1,163 +1,162 @@
import BeatUnit, {BeatUnitType} from "./BeatUnit";
export type BeatInitOptions = {
timeSig: {
up: number,
down: number,
},
bars: number,
drumSchema: string[],
};
export default class Beat {
private timeSigUp: number = 4;
private timeSigDown: number = 4;
private readonly unitRecords: Record<string, BeatUnit[]>;
private readonly drumSchema: string[];
private notify: () => void = () => {};
private barCount: number = 1;
constructor(options?: BeatInitOptions) {
this.unitRecords = {};
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 {
if (Beat.isValidTimeSigRange(up)) {
if (Beat.isValidTimeSigRange(down)) {
this.timeSigUp = up | 0;
this.timeSigDown = down | 0;
this.updateBeatUnitLength();
this.notify();
}
}
}
setBars(barCount: number): void {
const isPosInt = (barCount > 0 && (barCount | 0) === barCount);
if (!isPosInt || barCount == this.barCount) {
return;
}
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());
}
}
}
}
getTimeSigUp(): number {
return this.timeSigUp;
}
getTimeSigDown(): number {
return this.timeSigDown;
}
stringify(): string {
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 {
return sig >= 2 && sig <= 64;
}
import BeatUnit, {BeatUnitType} from "./BeatUnit";
export type BeatInitOptions = {
timeSig: {
up: number,
down: number,
},
name: string,
bars: number,
};
export default class Beat {
private static count = 0;
private readonly key: string;
private name: string;
private timeSigUp = 4;
private timeSigDown = 4;
private readonly unitRecord: BeatUnit[] = [];
private observers: (() => void)[] = [];
private barCount = 1;
constructor(options: BeatInitOptions) {
this.key = `Beat-${Beat.count}`;
if (options.timeSig) {
this.name = options.name;
this.setTimeSignature(options.timeSig.up, options.timeSig.down);
this.setBars(options.bars);
} else {
this.name = this.key;
this.setTimeSignature(4, 4);
this.setBars(48);
}
Beat.count++;
}
setTimeSignature(up: number, down: number): void {
if (Beat.isValidTimeSigRange(up)) {
if (Beat.isValidTimeSigRange(down)) {
this.timeSigUp = up | 0;
this.timeSigDown = down | 0;
this.updateBeatUnitLength();
this.notify();
}
}
}
setBars(barCount: number): void {
const isPosInt = (barCount > 0 && (barCount | 0) === barCount);
if (!isPosInt || barCount == this.barCount) {
return;
}
this.barCount = barCount;
this.updateBeatUnitLength();
this.notify();
}
private updateBeatUnitLength() {
const newBarCount = this.barCount * this.timeSigUp;
if (newBarCount < this.unitRecord.length) {
this.unitRecord.splice(this.barCount * this.timeSigUp, this.unitRecord.length - newBarCount);
} else if (newBarCount > this.unitRecord.length) {
const barsToAdd = newBarCount - this.unitRecord.length;
for (let i = 0; i < barsToAdd; i++) {
this.unitRecord.push(new BeatUnit());
}
}
}
getTimeSigUp(): number {
return this.timeSigUp;
}
getTimeSigDown(): number {
return this.timeSigDown;
}
turnUnitOn(index: number): void {
if (Math.abs(index | 0) !== index) {
return;
}
const unit = this.getUnit(index);
if (unit) {
unit.setOn(true);
this.notify();
}
}
turnUnitOff(index: number): void {
if (Math.abs(index | 0) !== index) {
return;
}
const unit = this.getUnit(index);
if (unit) {
unit.setOn(false);
this.notify();
}
}
toggleUnit(index: number): void {
if (Math.abs(index | 0) !== index) {
return;
}
const unit = this.getUnit(index);
if (unit) {
unit.toggle();
this.notify();
}
}
setUnitType(index: number, type: BeatUnitType): void {
if (Math.abs(index | 0) !== index) {
return;
}
this.getUnit(index).setType(type);
this.notify();
}
unitIsOn(index: number): boolean {
return this.getUnit(index)?.isOn();
}
unitType(index: number): BeatUnitType {
return this.getUnit(index)?.getType();
}
onUpdate(updateCallback: () => void): void {
this.observers.push(updateCallback);
}
private getUnit(index: number): BeatUnit {
if (!this.unitRecord[index]) {
throw new Error(`Invalid beat unit index! - ${index}`);
}
return this.unitRecord[index];
}
getBarCount(): number {
return this.barCount;
}
getKey(): string {
return this.key;
}
private static isValidTimeSigRange(sig: number): boolean {
return sig >= 2 && sig <= 64;
}
private notify(): void {
this.observers.forEach(observer => observer());
}
setName(newName: string): void {
this.name = newName;
this.notify();
}
getName(): string {
return this.name;
}
}

102
src/BeatGroup.ts Normal file
View File

@@ -0,0 +1,102 @@
import Beat, {BeatInitOptions} from "./Beat";
type BeatGroupInitOptions = {
beats: BeatInitOptions[],
}
export default class BeatGroup {
private beats: Beat[] = [];
private beatKeyMap: Record<string, number> = {};
private subscribers: (() => void)[] = [];
constructor(options: BeatGroupInitOptions) {
for (const beatOptions of options.beats) {
const newBeat = new Beat(beatOptions);
this.beats.push(newBeat);
this.beatKeyMap[newBeat.getKey()] = this.beats.length - 1;
}
}
getBeatByKey(beatKey: string): Beat {
if (typeof this.beatKeyMap[beatKey] === "undefined") {
throw new Error(`Could not find the beat with key: ${beatKey}`);
}
return this.getBeatByIndex(this.beatKeyMap[beatKey]);
}
getBeatByIndex(beatIndex: number): Beat {
if (!this.beats[beatIndex]) {
throw new Error(`Could not find the beat with index: ${beatIndex}`);
}
return this.beats[beatIndex];
}
getBeatCount(): number {
return this.beats.length;
}
getBeatKeys(): string[] {
return this.beats.map(beat => beat.getKey());
}
swapBeatsByIndices(beatIndex1: number, beatIndex2: number): void {
const beat1 = this.getBeatByIndex(beatIndex1);
const beat2 = this.getBeatByIndex(beatIndex2);
this.beats[beatIndex1] = beat2;
this.beats[beatIndex2] = beat1;
this.beatKeyMap[beat1.getKey()] = beatIndex2;
this.beatKeyMap[beat2.getKey()] = beatIndex1;
this.notify();
}
swapBeatsByKeys(beatKey1: string, beatKey2: string): void {
const index1 = this.beatKeyMap[this.getBeatByKey(beatKey1).getKey()];
const index2 = this.beatKeyMap[this.getBeatByKey(beatKey2).getKey()];
this.swapBeatsByIndices(index1, index2);
}
private notify(): void {
this.subscribers.forEach(subscriber => subscriber());
}
onBeatChangeByKey(beatKey: string, subscriber: (beatKey: string) => void): void {
this.getBeatByKey(beatKey).onUpdate(() => subscriber(beatKey));
}
onBeatChangeByIndex(beatIndex: number, subscriber: (beatIndex: number) => void): void {
this.getBeatByIndex(beatIndex).onUpdate(() => subscriber(beatIndex));
}
onBeatsChange(subscriber: () => void): void {
this.subscribers.push(subscriber);
}
moveBeatBack(beatKey: string): void {
const index = this.beatKeyMap[beatKey];
if (typeof index !== "undefined" && index > 0) {
this.swapBeatsByIndices(index, index - 1);
}
this.notify();
}
moveBeatForward(beatKey: string): void {
const index = this.beatKeyMap[beatKey];
if (typeof index !== "undefined" && index < this.getBeatCount()) {
this.swapBeatsByIndices(index, index + 1);
}
this.notify();
}
canMoveBeatBack(beatKey: string): boolean {
return this.beatKeyMap[beatKey] > 0;
}
canMoveBeatForward(beatKey: string): boolean {
return this.beatKeyMap[beatKey] < this.beats.length - 1;
}
setBeatName(beatKey: string, newName: string): void {
this.getBeatByKey(beatKey).setName(newName);
this.notify();
}
}

View File

@@ -1,59 +1,48 @@
export enum BeatUnitType {
Normal,
GhostNote,
}
export default class BeatUnit {
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 {
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);
}
}
}
export enum BeatUnitType {
Normal,
GhostNote,
}
export default class BeatUnit {
private on = false;
private type: BeatUnitType = BeatUnitType.Normal;
private onUpdateCallbacks: ((on: boolean, type: BeatUnitType) => void)[] = [];
constructor(on = false) {
this.on = on;
}
toggle(): void {
this.on = !this.on;
this.notify();
}
setOn(on: boolean): void {
this.on = on;
this.notify();
}
setType(type: BeatUnitType): void {
this.type = type;
this.notify();
}
getType(): BeatUnitType {
return this.type;
}
isOn(): boolean {
return this.on;
}
onUpdate(callback: (on: boolean, type: BeatUnitType) => void): void {
this.onUpdateCallbacks.push(callback);
}
notify(): void {
for (const cb of this.onUpdateCallbacks) {
cb(this.on, this.type);
}
}
}

View File

@@ -1,20 +0,0 @@
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);
}
}

3
src/config.json Normal file
View File

@@ -0,0 +1,3 @@
{
"development": true
}

1
src/index.ts Normal file
View File

@@ -0,0 +1 @@
console.log("Hello World!");

View File

@@ -1,18 +1,38 @@
import App from './ui/App.svelte';
import Store from "./Store";
const app = new App({
target: document.body,
props: {
store: new Store({
bars: 10,
timeSig: {
down: 4,
up: 4,
},
drumSchema: ['LF', 'LH', 'RH', 'RF'],
}),
},
});
import App from "./ui/App.svelte";
import Store from "./Store";
const defaultSettings = {
bars: 10,
timeSig: {
down: 4,
up: 4,
},
};
const store = new Store({
beats: [
{
name: "LF",
...defaultSettings,
},
{
name: "LH",
...defaultSettings,
},
{
name: "RH",
...defaultSettings,
},
{
name: "RF",
...defaultSettings,
}
]
});
const app = new App({
target: document.body,
props: {store},
});
export default app;

View File

@@ -1,13 +1,4 @@
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());

View File

@@ -1,23 +0,0 @@
<script lang="ts">
import Store from "../Store";
import Beat from "./Beat.svelte";
export let store: Store;
</script>
<h1>
ArneDrums
</h1>
<div class="main-contianer">
<Beat beat="{store.getBeat()}"/>
</div>
<style>
h1 {
text-align: center;
color: red;
}
.main-contianer {
width: 100%;
}
</style>

View File

@@ -1,104 +0,0 @@
<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>

View File

@@ -1,35 +0,0 @@
<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>