Skip to content

Commit

Permalink
Change tempo (#108)
Browse files Browse the repository at this point in the history
* create bpm input component

* add style to the bpm input component and adjust sequencer

* modify input value with input or buttons

* change bpm while playing beat
  • Loading branch information
Babali42 authored Jan 6, 2025
1 parent f0b2ad6 commit 5317daa
Show file tree
Hide file tree
Showing 8 changed files with 178 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div class="number-control">
<div class="number-left" (click)="decrementBpm()"></div>
<input type="number" name="number" class="number-quantity" [value]="bpm" (change)="updateNumber($event)">
<div class="number-right" (click)="incrementBpm()"></div>
</div>
39 changes: 39 additions & 0 deletions frontend/src/app/components/bpm-input/bpm-input.component.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/* From Uiverse.io by Cybercom682 */
.number-control {
display: flex;
align-items: center;

.number-left::before,
.number-right::after {
content: attr(data-content);
background-color: var(--backgroundColor);;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--borderColor);;
width: 20px;
color: var(--textColor);
cursor: pointer;
}

.number-left::before {
content: "-";
}

.number-right::after {
content: "+";
}

.number-quantity {
padding: 0.25rem;
width: 50px;
height: 12px;
-moz-appearance: textfield;
border: 0;
border-top: 1px solid var(--borderColor);
border-bottom: 1px solid var(--borderColor);
background-color: var(--backgroundColor);
color: var(--textColor);
}
}

56 changes: 56 additions & 0 deletions frontend/src/app/components/bpm-input/bpm-input.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { BpmInputComponent } from './bpm-input.component';
import {By} from "@angular/platform-browser";

describe('BpmInputComponent', () => {
let component: BpmInputComponent;
let fixture: ComponentFixture<BpmInputComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [BpmInputComponent]
})
.compileComponents();

fixture = TestBed.createComponent(BpmInputComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should increment the bpm in the desired range', () => {
component.maxBpm = 130;
component.bpm = 128;
component.incrementBpm();
component.incrementBpm();
component.incrementBpm();
expect(component.bpm).toEqual(component.maxBpm);
});

it('should decrement the bpm in the desired range', () => {
component.minBpm = 126;
component.bpm = 128;
component.decrementBpm();
component.decrementBpm();
component.decrementBpm();
expect(component.bpm).toEqual(component.minBpm);
});

it('should update the value', () => {
component.bpm = 128;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const inputElement = fixture.debugElement.query(By.css('.number-quantity')).nativeElement;

// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
inputElement.value = '120';
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
inputElement.dispatchEvent(new Event('change'));
fixture.detectChanges();

expect(component.bpm).toBe(120);
});
});
37 changes: 37 additions & 0 deletions frontend/src/app/components/bpm-input/bpm-input.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {SoundService} from "../../services/sound/sound.service";

@Component({
selector: 'app-bpm-input',
standalone: true,
imports: [],
templateUrl: './bpm-input.component.html',
styleUrl: './bpm-input.component.scss'
})
export class BpmInputComponent {
maxBpm = SoundService.maxBpm;
minBpm = SoundService.minBpm;
@Input() bpm : number = SoundService.minBpm;
@Output() bpmChange = new EventEmitter<number>();

incrementBpm() {
this.updateBpm(Math.min(this.bpm+1, this.maxBpm));
}

decrementBpm() {
this.updateBpm(Math.max(this.bpm-1, this.minBpm));
}

updateNumber(event: Event): void {
const inputElement = event.target as HTMLInputElement;
let value = Number(inputElement.value);
value = Math.min(value, this.maxBpm);
value = Math.max(value, this.minBpm);
this.updateBpm(value);
}

private updateBpm(value: number): void {
this.bpm = value;
this.bpmChange.emit(this.bpm);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
{{ this.soundService.isPlaying ? "⏸" : "▶" }}
</button>
</div>
<div class="sequencer-header flex">
<div>
<div class="sequencer-header">
<div class="label">
<span class="small">Genre : </span>
<span class="large">{{ genre.label }}</span>
Expand All @@ -14,12 +13,11 @@
<span class="small">Beat : </span>
<span class="large">{{ beat.label }}</span>
</div>
<div class="label">
<div class="flex label" style="align-items: center; gap: 5px; margin-top: 2px">
<span class="small">Tempo : </span>
<span class="large">{{ beat.bpm }}</span>
<app-bpm-input [bpm]="beat.bpm" (bpmChange)="changeBeatBpm($event)"></app-bpm-input>
<span class="small"> bpm</span>
</div>
</div>
</div>
<div class="tracks-name">
<h3 *ngFor="let track of beat.tracks" class="track-name">{{ track.name }}</h3>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ $gap: 4px;
font-weight: bold;
}
}

div + div {
margin-top: 3px;
}
}

.play-button-container {
Expand Down
18 changes: 16 additions & 2 deletions frontend/src/app/components/sequencer/sequencer.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import {Beat} from '../../domain/beat';
import {NgFor} from '@angular/common';
import {StepLengths} from './models/step-lengths';
import {Genre} from "../../domain/genre";
import { ActivatedRoute } from '@angular/router';
import {ActivatedRoute} from '@angular/router';
import IManageGenres, {IManageGenresToken} from "../../domain/ports/secondary/i-manage-genres";
import {Subject} from "rxjs";
import {BpmInputComponent} from "../bpm-input/bpm-input.component";

@Component({
selector: 'sequencer',
templateUrl: './sequencer.component.html',
styleUrls: ['./sequencer.component.scss'],
standalone: true,
imports: [NgFor]
imports: [NgFor, BpmInputComponent]
})
export class SequencerComponent implements OnInit {
beat = {} as Beat;
Expand Down Expand Up @@ -78,5 +79,18 @@ export class SequencerComponent implements OnInit {

protected readonly StepLengths = StepLengths;
protected readonly Math = Math;

changeBeatBpm($event: number) {
const isPlaying = this.soundService.isPlaying;
this.soundService.setBpm($event);
this.soundService.generateLoopBuffer().then(
() => {
if(isPlaying)
this.soundService.play();
},
() => {
}
);
}
}

19 changes: 18 additions & 1 deletion frontend/src/app/services/sound/sound.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {LoadingBarService} from '@ngx-loading-bar/core';
providedIn: 'root'
})
export class SoundService {
static maxBpm = 300;
static minBpm = 30;
bpm: number = 120;
isPlaying: boolean = false;
index: number = 0;
Expand Down Expand Up @@ -39,7 +41,7 @@ export class SoundService {
this.stepNumber
);
}
this.playSound(this.loopBuffer);
this.play();
} else {
this.pause();
}
Expand Down Expand Up @@ -118,4 +120,19 @@ export class SoundService {
setStepNumber(length: number) {
this.stepNumber = length;
}

async generateLoopBuffer(): Promise<void> {
this.loopBuffer = await this.soundGeneratorService.getRenderedBuffer(
this.tracks,
this.samples,
this.bpm,
this.stepNumber
);
}

play() : void {
if(!this.loopBuffer)
return;
this.playSound(this.loopBuffer);
}
}

0 comments on commit 5317daa

Please sign in to comment.