Skip to content

Commit 60dc0be

Browse files
committed
Added WaveformView component
1 parent 6ba59a6 commit 60dc0be

19 files changed

+567
-65
lines changed

package-lock.json

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"@ng-bootstrap/ng-bootstrap": "^13.0.0",
2323
"@popperjs/core": "^2.10.2",
2424
"bootstrap": "^5.2.0",
25+
"konva": "^8.3.11",
2526
"peaks.js": "^2.0.5",
2627
"rxjs": "~7.5.0",
2728
"tslib": "^2.3.0",
@@ -40,4 +41,4 @@
4041
"karma-jasmine-html-reporter": "~2.0.0",
4142
"typescript": "~4.7.2"
4243
}
43-
}
44+
}

src/app/app.component.html

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,24 @@
1-
<style>
2-
#zoomview-container {
3-
box-shadow: 3px 3px 20px #919191;
4-
-moz-box-shadow: 3px 3px 20px #919191;
5-
-webkit-box-shadow: 3px 3px 20px #919191;
6-
margin: 24px 0 24px 0;
7-
line-height: 0;
8-
width: 1000px;
9-
height: 200px;
10-
}
1+
<div class="container">
2+
<h1>
3+
Peaks.js Angular Example
4+
</h1>
115

12-
#overview-container {
13-
box-shadow: 3px 3px 20px #919191;
14-
-moz-box-shadow: 3px 3px 20px #919191;
15-
-webkit-box-shadow: 3px 3px 20px #919191;
16-
margin: 0 0 24px 0;
17-
line-height: 0;
18-
width: 1000px;
19-
height: 85px;
20-
}
21-
</style>
6+
<p>
7+
This is a simple example of how to use <a href="https://github.com/bbc/peaks.js">Peaks.js</a> in an Angular application.
8+
</p>
229

23-
<h1>{{ title }}</h1>
10+
<p>
11+
Audio content is copyright BBC, from the <a href="http://bbcsfx.acropolis.org.uk/?q=07023003">BBC Sound Effects</a> library,
12+
used under the terms of the <a href="https://github.com/bbcarchdev/Remarc/blob/master/doc/2016.09.27_RemArc_Content%20licence_Terms%20of%20Use_final.pdf">RemArc Licence</a>.
13+
</p>
2414

25-
<p>Hello</p>
15+
<h3>Select audio</h3>
2616

27-
<div id="zoomview-container"></div>
28-
<div id="overview-container"></div>
17+
<div class="btn-group" role="group" *ngFor="let audio of exampleAudio">
18+
<button class="btn btn-primary" [class.active]="audio.id === selectedAudio.id" type="button" (click)="onSelect(audio)">
19+
{{audio.name}}
20+
</button>&nbsp;
21+
</div>
2922

30-
<audio id="audio" controls>
31-
<source src="assets/07030039.mp3" type="audio/mpeg">
32-
</audio>
23+
<waveform-view [selectedAudio]="selectedAudio"></waveform-view>
24+
</div>

src/app/app.component.ts

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,36 @@
11
import { Component } from '@angular/core';
2-
import Peaks, { PeaksInstance, PeaksOptions } from 'peaks.js';
2+
3+
import ExampleAudio from './example-audio';
4+
5+
const audioUrls: ExampleAudio[] = [
6+
{
7+
id: 1,
8+
name: 'Bird song',
9+
audioUrl: 'assets/07030039.mp3',
10+
audioContentType: 'audio/mpeg',
11+
waveformDataUrl: 'assets/07030039.dat'
12+
},
13+
14+
{
15+
id: 2,
16+
name: 'Car passing',
17+
audioUrl: 'assets/07023003.mp3',
18+
audioContentType: 'audio/mpeg',
19+
waveformDataUrl: 'assets/07023003-2channel.dat'
20+
}
21+
];
322

423
@Component({
524
selector: 'app-root',
625
templateUrl: './app.component.html',
726
styleUrls: ['./app.component.css']
827
})
928
export class AppComponent {
10-
title = 'Peaks.js Angular Example';
11-
12-
ngOnInit() {
13-
const options: PeaksOptions = {
14-
zoomview: {
15-
container: document.getElementById('zoomview-container')
16-
},
17-
overview: {
18-
container: document.getElementById('overview-container')
19-
},
20-
mediaElement: document.getElementById('audio')!,
21-
dataUri: {
22-
arraybuffer: 'assets/07030039.dat'
23-
}
24-
};
29+
exampleAudio: ExampleAudio[] = audioUrls;
30+
audio: ExampleAudio = audioUrls[0];
31+
selectedAudio: ExampleAudio = audioUrls[0];
2532

26-
Peaks.init(options, function(err: Error, peaks?: PeaksInstance) {
27-
console.log('Peaks ready');
28-
});
33+
onSelect(audio: ExampleAudio): void {
34+
this.selectedAudio = audio;
2935
}
3036
}

src/app/app.module.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { BrowserModule } from '@angular/platform-browser';
33

44
import { AppComponent } from './app.component';
55
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
6+
import { WaveformViewComponent } from './waveform-view/waveform-view.component';
67

78
@NgModule({
89
declarations: [
9-
AppComponent
10+
AppComponent,
11+
WaveformViewComponent
1012
],
1113
imports: [
1214
BrowserModule,
@@ -15,4 +17,5 @@ import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
1517
providers: [],
1618
bootstrap: [AppComponent]
1719
})
18-
export class AppModule { }
20+
21+
export class AppModule { };

src/app/example-audio.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default interface ExampleAudio {
2+
id: number,
3+
name: string,
4+
audioUrl: string,
5+
audioContentType: string,
6+
waveformDataUrl: string
7+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import Konva from 'konva';
2+
import { Label, Tag } from 'konva/lib/shapes/Label';
3+
import { Line } from 'konva/lib/shapes/Line';
4+
import { Text } from 'konva/lib/shapes/Text';
5+
6+
import { CreatePointMarkerOptions, PointMarker } from 'peaks.js';
7+
8+
class CustomPointMarker implements PointMarker {
9+
private _options: CreatePointMarkerOptions;
10+
private _group?: Konva.Group;
11+
private _label?: Label;
12+
private _tag?: Tag;
13+
private _text?: Text;
14+
private _line?: Line;
15+
16+
constructor(options: CreatePointMarkerOptions) {
17+
this._options = options;
18+
}
19+
20+
init(group: object) {
21+
this._group = group as Konva.Group;
22+
23+
this._label = new Label({
24+
x: 0.5,
25+
y: 0.5
26+
});
27+
28+
this._tag = new Tag({
29+
fill: this._options!.color,
30+
stroke: this._options!.color,
31+
strokeWidth: 1,
32+
pointerDirection: 'down',
33+
pointerWidth: 10,
34+
pointerHeight: 10,
35+
lineJoin: 'round',
36+
shadowColor: 'black',
37+
shadowBlur: 10,
38+
shadowOffsetX: 3,
39+
shadowOffsetY: 3,
40+
shadowOpacity: 0.3
41+
});
42+
43+
this._label.add(this._tag);
44+
45+
this._text = new Text({
46+
text: this._options!.point.labelText,
47+
fontFamily: 'Calibri',
48+
fontSize: 14,
49+
padding: 5,
50+
fill: 'white'
51+
});
52+
53+
this._label.add(this._text);
54+
55+
// Vertical Line - create with default y and points, the real values
56+
// are set in fitToView().
57+
this._line = new Line({
58+
x: 0,
59+
y: 0,
60+
stroke: this._options!.color,
61+
strokeWidth: 1
62+
});
63+
64+
this._group.add(this._label);
65+
this._group.add(this._line);
66+
67+
this.fitToView();
68+
69+
this.bindEventHandlers();
70+
};
71+
72+
bindEventHandlers() {
73+
this._group!.on('mouseenter', () => {
74+
document.body.style.cursor = 'move';
75+
});
76+
77+
this._group!.on('mouseleave', () => {
78+
document.body.style.cursor = 'default';
79+
});
80+
};
81+
82+
fitToView() {
83+
const height = this._options!.layer.getHeight();
84+
85+
const labelHeight = this._text!.height() + 2 * this._text!.padding();
86+
const offsetTop = 14;
87+
const offsetBottom = 26;
88+
89+
this._group!.y(offsetTop + labelHeight + 0.5);
90+
91+
this._line!.points([0.5, 0, 0.5, height - labelHeight - offsetTop - offsetBottom]);
92+
};
93+
}
94+
95+
export default CustomPointMarker;
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import Konva from 'konva';
2+
import { Label, Tag } from 'konva/lib/shapes/Label';
3+
import { Line } from 'konva/lib/shapes/Line';
4+
import { Text } from 'konva/lib/shapes/Text';
5+
6+
import { CreateSegmentMarkerOptions, SegmentMarker } from 'peaks.js';
7+
8+
class CustomSegmentMarker implements SegmentMarker {
9+
private _options: CreateSegmentMarkerOptions;
10+
private _group?: Konva.Group;
11+
private _label?: Label;
12+
private _tag?: Tag;
13+
private _text?: Text;
14+
private _line?: Line;
15+
16+
constructor(options: CreateSegmentMarkerOptions) {
17+
this._options = options;
18+
}
19+
20+
init(group: object) {
21+
this._group = group as Konva.Group;
22+
23+
this._label = new Label({
24+
x: 0.5,
25+
y: 0.5
26+
});
27+
28+
const color = this._options!.segment.color;
29+
30+
this._tag = new Tag({
31+
fill: color as string,
32+
stroke: color as string,
33+
strokeWidth: 1,
34+
pointerDirection: 'down',
35+
pointerWidth: 10,
36+
pointerHeight: 10,
37+
lineJoin: 'round',
38+
shadowColor: 'black',
39+
shadowBlur: 10,
40+
shadowOffsetX: 3,
41+
shadowOffsetY: 3,
42+
shadowOpacity: 0.3
43+
});
44+
45+
this._label.add(this._tag);
46+
47+
let labelText = this._options.segment.labelText;
48+
49+
if (labelText) {
50+
labelText += ' ';
51+
}
52+
53+
labelText += this._options.startMarker ? 'Start' : 'End';
54+
55+
this._text = new Text({
56+
text: labelText,
57+
fontFamily: 'Calibri',
58+
fontSize: 14,
59+
padding: 5,
60+
fill: 'white'
61+
});
62+
63+
this._label.add(this._text);
64+
65+
// Vertical Line - create with default y and points, the real values
66+
// are set in fitToView().
67+
this._line = new Line({
68+
x: 0,
69+
y: 0,
70+
stroke: color as string,
71+
strokeWidth: 1
72+
});
73+
74+
this._group.add(this._label);
75+
this._group.add(this._line);
76+
77+
this.fitToView();
78+
79+
this.bindEventHandlers();
80+
}
81+
82+
bindEventHandlers() {
83+
this._group!.on('mouseenter', () => {
84+
document.body.style.cursor = 'move';
85+
});
86+
87+
this._group!.on('mouseleave', () => {
88+
document.body.style.cursor = 'default';
89+
});
90+
};
91+
92+
fitToView() {
93+
const height = this._options.layer.getHeight();
94+
95+
const labelHeight = this._text!.height() + 2 * this._text!.padding();
96+
const offsetTop = 14;
97+
const offsetBottom = 26;
98+
99+
this._group!.y(offsetTop + labelHeight + 0.5);
100+
101+
this._line!.points([0.5, 0, 0.5, height - labelHeight - offsetTop - offsetBottom]);
102+
}
103+
}
104+
105+
export default CustomSegmentMarker;

0 commit comments

Comments
 (0)