diff --git a/src/App.vue b/src/App.vue
index 22e4d4d..716c2c5 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -2,7 +2,7 @@
-
+
@@ -21,16 +21,41 @@ import Channel from "@/components/Channel.vue";
})
export default class App extends Vue {
private readonly N_CHANNELS = 6;
+ private readonly LFO_PERIOD = 30_000;
+ private readonly LFO_DEPTH = 33;
+ private readonly LFO_OFFSET = 66;
+
+ private volumes = Array(this.N_CHANNELS).fill(50);
+ private animateVolumeStart?: Date;
+ private animateVolumeInterval?: number;
private defaultMedia = [
- "https://www.youtube.com/watch?v=q76bMs-NwRk"
+ "https://www.youtube.com/watch?v=q76bMs-NwRk",
];
private start() {
(this.$refs.channels as Channel[]).forEach((channel) => {
channel.start();
+ this.animateVolume();
});
}
+
+ private animateVolume() {
+ clearInterval(this.animateVolumeInterval);
+ this.animateVolumeStart = new Date();
+ this.animateVolumeInterval = setInterval(() => {
+ if (this.animateVolumeStart) {
+ const delta = new Date().getTime() - this.animateVolumeStart.getTime();
+
+ this.volumes = [...Array(this.N_CHANNELS).keys()]
+ .map((idx) => {
+ const offset = idx * (1 / this.N_CHANNELS);
+ const progress = delta / this.LFO_PERIOD + offset;
+ return Math.sin(progress * 2 * Math.PI) * this.LFO_DEPTH + this.LFO_OFFSET;
+ });
+ }
+ }, 1000 / 60);
+ }
}
diff --git a/src/components/Channel.vue b/src/components/Channel.vue
index 6c13664..6b48ccb 100644
--- a/src/components/Channel.vue
+++ b/src/components/Channel.vue
@@ -32,14 +32,11 @@ enum ChannelState {
@Component
export default class Channel extends Vue {
- @Prop() private url: string | undefined;
+ @Prop() public volume: number = 50;
+ @Prop() public url: string | undefined;
private youtubePlayer?: YouTubePlayer;
- private animateVolumeStart?: Date;
- private animateVolumeInterval?: number;
private state = ChannelState.UNLOADED;
private title = "";
- private volume: number = 50;
-
private get source() {
if (this.url) {
@@ -63,7 +60,6 @@ export default class Channel extends Vue {
this.youtubePlayer.setVolume(this.volume);
this.youtubePlayer.playVideo();
this.state = ChannelState.PLAYING;
- this.animateVolume();
}
}
@@ -117,18 +113,6 @@ export default class Channel extends Vue {
this.onUrlChange();
}
- private animateVolume() {
- clearInterval(this.animateVolumeInterval);
- const LFO_PERIOD = 3000;
- this.animateVolumeStart = new Date();
- this.animateVolumeInterval = setInterval(() => {
- if (this.animateVolumeStart) {
- const delta = new Date().getTime() - this.animateVolumeStart.getTime();
- this.volume = Math.sin(((delta % LFO_PERIOD) / LFO_PERIOD) * 2 * Math.PI) * 50 + 50;
- }
- }, 1000 / 60);
- }
-
private static fetchYoutubeTitle(videoId: string): Promise
{
return new Promise((resolve, reject) => {
let url = `https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${videoId}`;