Ron Au commited on
Commit
72db8a4
·
1 Parent(s): 153471c

feat(api): Connect frontend + backend

Browse files
source/ui/src/config/styles.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "piano": "Piano",
3
- "chamber": "Chamber Music",
4
- "rock_and_metal": "Rock and Metal",
5
- "synth": "Synthesizer",
6
- "church": "Church",
7
- "timpani_strings_harp": "Timpani, Contrabass, Harp",
8
- "country": "Country"
9
- }
 
 
 
 
 
 
 
 
 
 
source/ui/src/lib/ComposeButton.svelte CHANGED
@@ -1,10 +1,45 @@
1
  <script lang="ts">
2
- import { loading } from '$lib/stores';
3
 
4
- $: text = $loading ? 'Composing...' : 'Compose ✨';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  </script>
6
 
7
- <button disabled={$loading}>{text}</button>
 
 
 
 
 
 
8
 
9
  <style>
10
  button {
@@ -26,4 +61,10 @@ button[disabled] {
26
  color: hsl(0 0% 50%);
27
  cursor: initial;
28
  }
 
 
 
 
 
 
29
  </style>
 
1
  <script lang="ts">
2
+ import { density, composing, style, temperature, notesImage, notesTokens, audioBlob } from '$lib/stores';
3
 
4
+ const compose = async (): Promise<void> => {
5
+ try {
6
+ $composing = true;
7
+ const composeResponse = await fetch('compose', {
8
+ method: 'POST',
9
+ headers: {
10
+ 'Content-Type': 'application/json',
11
+ },
12
+ body: JSON.stringify({
13
+ music_style: $style,
14
+ density: $density,
15
+ temperature: $temperature,
16
+ }),
17
+ });
18
+
19
+ if (!composeResponse.ok) {
20
+ throw new Error(`Unable to create composition: [${composeResponse.status}] ${composeResponse.text()}`);
21
+ }
22
+
23
+ const { audio, image, tokens } = await composeResponse.json();
24
+
25
+ $notesImage = image;
26
+ $notesTokens = tokens;
27
+ $audioBlob = audio;
28
+ } catch (err) {
29
+ console.error(err);
30
+ } finally {
31
+ $composing = false;
32
+ }
33
+ };
34
  </script>
35
 
36
+ <button disabled={$composing} on:click={compose}>
37
+ {#if $composing}
38
+ Composing...
39
+ {:else}
40
+ Compose <img src="wand.svg" alt="Magic wand" />
41
+ {/if}
42
+ </button>
43
 
44
  <style>
45
  button {
 
61
  color: hsl(0 0% 50%);
62
  cursor: initial;
63
  }
64
+
65
+ img {
66
+ height: 1.2rem;
67
+ aspect-ratio: 1 / 1;
68
+ vertical-align: bottom;
69
+ }
70
  </style>
source/ui/src/lib/DensityOptions.svelte CHANGED
@@ -1,15 +1,13 @@
1
  <script lang="ts">
2
  import { density } from './stores';
 
3
  import Radio from './Radio.svelte';
4
-
5
- const options = ['Low', 'Medium', 'High'];
6
- const type = 'density';
7
  </script>
8
 
9
  <div>
10
  <fieldset>
11
  <legend>Note density</legend>
12
- <Radio bind:selection={$density} {options} {type} />
13
  </fieldset>
14
  </div>
15
 
 
1
  <script lang="ts">
2
  import { density } from './stores';
3
+ import { densities } from './config.json';
4
  import Radio from './Radio.svelte';
 
 
 
5
  </script>
6
 
7
  <div>
8
  <fieldset>
9
  <legend>Note density</legend>
10
+ <Radio bind:selection={$density} options={densities} />
11
  </fieldset>
12
  </div>
13
 
source/ui/src/lib/NoteTokens.svelte CHANGED
@@ -1,31 +1,18 @@
 
 
 
 
1
  <section>
2
  <h2>Tokenized notes</h2>
3
- <p>
4
- PIECE_START TRACK_START INST=DRUMS DENSITY=6 BAR_START NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42
5
- TIME_DELTA=1 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=38 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=38
6
- NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=35
7
- NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=42 TIME_DELTA=1
8
- NOTE_ON=38 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=38 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1
9
- NOTE_OFF=35 NOTE_OFF=42 BAR_END BAR_START NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1
10
- NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=38 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=38 NOTE_OFF=42
11
- TIME_DELTA=1 NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=35 NOTE_ON=42
12
- TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=38
13
- NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=38 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35
14
- NOTE_OFF=42 BAR_END BAR_START NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=42
15
- TIME_DELTA=1 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=38 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=38 NOTE_OFF=42 TIME_DELTA=1
16
- NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=35 NOTE_ON=42 TIME_DELTA=1
17
- NOTE_OFF=35 NOTE_OFF=42 TIME_DELTA=1 NOTE_ON=42 TIME_DELTA=1 NOTE_OFF=42
18
- </p>
19
  </section>
20
 
21
  <style>
22
- /* h2 {
23
- font-family: 'Italiana', serif;
24
- } */
25
  section {
26
  border: 2px solid hsl(0 0% 80%);
27
  border-radius: 0.375rem;
28
- /* background: hsl(0 0% 20% / 50%); */
29
  padding: 1rem;
30
  }
31
  </style>
 
1
+ <script lang="ts">
2
+ import { notesTokens } from './stores';
3
+ </script>
4
+
5
  <section>
6
  <h2>Tokenized notes</h2>
7
+ {#if $notesTokens}
8
+ <p>{$notesTokens}</p>
9
+ {/if}
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  </section>
11
 
12
  <style>
 
 
 
13
  section {
14
  border: 2px solid hsl(0 0% 80%);
15
  border-radius: 0.375rem;
 
16
  padding: 1rem;
17
  }
18
  </style>
source/ui/src/lib/NoteVisualisation.svelte CHANGED
@@ -1,18 +1,19 @@
 
 
 
 
1
  <section>
2
  <h2>Visualised notes</h2>
3
- <img src="compose.png" alt="MIDI notes of composition" />
4
  </section>
5
 
6
  <style>
7
  section {
8
  border: 2px solid hsl(0 0% 80%);
9
  border-radius: 0.375rem;
10
- /* background: hsl(0 0% 20% / 50%); */
11
  padding: 1rem;
12
  }
13
- /* h2 {
14
- font-family: 'Italiana', serif;
15
- } */
16
  img {
17
  display: block;
18
  margin: auto;
 
1
+ <script lang="ts">
2
+ import { notesImage } from './stores';
3
+ </script>
4
+
5
  <section>
6
  <h2>Visualised notes</h2>
7
+ <img src={$notesImage} alt="MIDI notes of composition" />
8
  </section>
9
 
10
  <style>
11
  section {
12
  border: 2px solid hsl(0 0% 80%);
13
  border-radius: 0.375rem;
 
14
  padding: 1rem;
15
  }
16
+
 
 
17
  img {
18
  display: block;
19
  margin: auto;
source/ui/src/lib/Playback.svelte CHANGED
@@ -1,8 +1,8 @@
1
  <script lang="ts">
2
- import { playing } from '$lib/stores';
3
  </script>
4
 
5
- <audio controls src="download.wav" />
6
 
7
  <style>
8
  audio {
 
1
  <script lang="ts">
2
+ import { audioBlob } from './stores';
3
  </script>
4
 
5
+ <audio src={$audioBlob} controls />
6
 
7
  <style>
8
  audio {
source/ui/src/lib/Radio.svelte CHANGED
@@ -1,14 +1,14 @@
1
  <script lang="ts">
2
- export let options: string[];
3
- export let type: string;
4
- export let selection: string = options[1];
5
  </script>
6
 
7
  <div class="options">
8
- {#each options as option, i}
9
- <label data-selected={option === selection}>
10
- {option}
11
- <input type="radio" bind:group={selection} value={option} />
12
  </label>
13
  {/each}
14
  <input type="radio" checked />
 
1
  <script lang="ts">
2
+ export let options: Record<string, string>;
3
+ const keys: string[] = Object.keys(options);
4
+ export let selection: string = keys[1];
5
  </script>
6
 
7
  <div class="options">
8
+ {#each keys as key}
9
+ <label data-selected={key === selection}>
10
+ {options[key]}
11
+ <input type="radio" bind:group={selection} value={key} />
12
  </label>
13
  {/each}
14
  <input type="radio" checked />
source/ui/src/lib/StyleOptions.svelte CHANGED
@@ -1,17 +1,17 @@
1
  <script lang="ts">
2
  import { style } from './stores';
3
- import stylesConfig from '../config/styles.json';
4
 
5
- const keys: string[] = Object.keys(stylesConfig);
6
  </script>
7
 
8
  <fieldset>
9
- <legend>{stylesConfig[$style] || 'Synthesizer'}</legend>
10
  <div class="grid">
11
  {#each keys as key, i}
12
  <label data-selected={$style === key}>
13
  <div>
14
- <img src={`${key}.svg`} alt={stylesConfig[key]} />
15
  </div>
16
  <input type="radio" bind:group={$style} value={key} />
17
  </label>
 
1
  <script lang="ts">
2
  import { style } from './stores';
3
+ import { styles } from './config.json';
4
 
5
+ const keys: string[] = Object.keys(styles);
6
  </script>
7
 
8
  <fieldset>
9
+ <legend>{styles[$style] || 'Synthesizer'}</legend>
10
  <div class="grid">
11
  {#each keys as key, i}
12
  <label data-selected={$style === key}>
13
  <div>
14
+ <img src={`${key}.svg`} alt={styles[key]} />
15
  </div>
16
  <input type="radio" bind:group={$style} value={key} />
17
  </label>
source/ui/src/lib/TemperatureOptions.svelte CHANGED
@@ -1,15 +1,13 @@
1
  <script lang="ts">
2
  import { temperature } from './stores';
 
3
  import Radio from './Radio.svelte';
4
-
5
- const options = ['Low', 'Medium', 'High', 'Very High'];
6
- const type = 'temperature';
7
  </script>
8
 
9
  <div>
10
  <fieldset>
11
  <legend>Temperature</legend>
12
- <Radio bind:selection={$temperature} {options} {type} />
13
  </fieldset>
14
  </div>
15
 
 
1
  <script lang="ts">
2
  import { temperature } from './stores';
3
+ import { temperatures } from './config.json';
4
  import Radio from './Radio.svelte';
 
 
 
5
  </script>
6
 
7
  <div>
8
  <fieldset>
9
  <legend>Temperature</legend>
10
+ <Radio bind:selection={$temperature} options={temperatures} />
11
  </fieldset>
12
  </div>
13
 
source/ui/src/lib/config.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "styles": {
3
+ "piano": "Piano",
4
+ "chamber": "Chamber Music",
5
+ "rock_and_metal": "Rock and Metal",
6
+ "synth": "Synthesizer",
7
+ "church": "Church",
8
+ "timpani_strings_harp": "Timpani, Contrabass, Harp",
9
+ "country": "Country"
10
+ },
11
+ "densities": {
12
+ "low": "Low",
13
+ "medium": "Medium",
14
+ "high": "High"
15
+ },
16
+ "temperatures": {
17
+ "low": "Low",
18
+ "medium": "Medium",
19
+ "high": "High",
20
+ "very_high": "Very High"
21
+ }
22
+ }
source/ui/src/lib/stores.ts CHANGED
@@ -2,8 +2,16 @@ import { writable } from 'svelte/store';
2
 
3
  import type { Writable } from 'svelte/store';
4
 
5
- export const loading: Writable<boolean> = writable(false);
6
- export const playing: Writable<boolean> = writable(true);
7
  export const style: Writable<string> = writable('synth');
8
- export const density: Writable<string> = writable('Medium');
9
- export const temperature: Writable<string> = writable('Medium');
 
 
 
 
 
 
 
 
 
 
2
 
3
  import type { Writable } from 'svelte/store';
4
 
5
+ /* Input parameters */
 
6
  export const style: Writable<string> = writable('synth');
7
+ export const density: Writable<string> = writable('medium');
8
+ export const temperature: Writable<string> = writable('medium');
9
+
10
+ /* Audio state */
11
+ export const composing: Writable<boolean> = writable(false);
12
+ export const playing: Writable<boolean> = writable(true);
13
+
14
+ /* Composition outputs */
15
+ export const audioBlob: Writable<string> = writable('');
16
+ export const notesImage: Writable<string> = writable('');
17
+ export const notesTokens: Writable<string> = writable('');
source/ui/static/wand.svg ADDED