Chlvnrz commited on
Commit
d4cef17
·
verified ·
1 Parent(s): 2babccb

Upload 9 files

Browse files
Files changed (9) hide show
  1. Dockerfile +45 -0
  2. constants.py +127 -0
  3. gitignore +1 -0
  4. main.py +96 -0
  5. model.py +25 -0
  6. pyproject.toml +6 -0
  7. requirements.txt +8 -0
  8. string_to_notes.py +124 -0
  9. utils.py +173 -0
Dockerfile ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:20.04
2
+
3
+ WORKDIR /code
4
+
5
+ ENV SYSTEM=spaces
6
+ ENV SPACE_ID=juancopi81/multitrack-midi-music-generator
7
+
8
+ COPY ./requirements.txt /code/requirements.txt
9
+
10
+ # Preconfigure tzdata
11
+ RUN DEBIAN_FRONTEND="noninteractive" apt-get -qq update && \
12
+ DEBIAN_FRONTEND="noninteractive" apt-get install -y tzdata
13
+
14
+ RUN apt-get update -qq && \
15
+ apt-get install -qq python3-pip build-essential libasound2-dev libjack-dev wget cmake pkg-config libglib2.0-dev ffmpeg
16
+
17
+ # Download libfluidsynth source
18
+ RUN wget https://github.com/FluidSynth/fluidsynth/archive/refs/tags/v2.3.3.tar.gz && \
19
+ tar xzf v2.3.3.tar.gz && \
20
+ cd fluidsynth-2.3.3 && \
21
+ mkdir build && \
22
+ cd build && \
23
+ cmake .. && \
24
+ make && \
25
+ make install && \
26
+ cd ../../ && \
27
+ rm -rf fluidsynth-2.3.3 v2.3.3.tar.gz
28
+
29
+ ENV LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
30
+ RUN ldconfig
31
+
32
+ RUN pip3 install --no-cache-dir --upgrade -r /code/requirements.txt
33
+
34
+ RUN useradd -m -u 1000 user
35
+
36
+ USER user
37
+
38
+ ENV HOME=/home/user \
39
+ PATH=/home/user/.local/bin:$PATH
40
+
41
+ WORKDIR $HOME/app
42
+
43
+ COPY --chown=user . $HOME/app
44
+
45
+ CMD ["python3", "main.py"]
constants.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SAMPLE_RATE = 44100
2
+
3
+
4
+ GM_INSTRUMENTS = [
5
+ "Acoustic Grand Piano",
6
+ "Bright Acoustic Piano",
7
+ "Electric Grand Piano",
8
+ "Honky-tonk Piano",
9
+ "Electric Piano 1",
10
+ "Electric Piano 2",
11
+ "Harpsichord",
12
+ "Clavi",
13
+ "Celesta",
14
+ "Glockenspiel",
15
+ "Music Box",
16
+ "Vibraphone",
17
+ "Marimba",
18
+ "Xylophone",
19
+ "Tubular Bells",
20
+ "Dulcimer",
21
+ "Drawbar Organ",
22
+ "Percussive Organ",
23
+ "Rock Organ",
24
+ "Church Organ",
25
+ "Reed Organ",
26
+ "Accordion",
27
+ "Harmonica",
28
+ "Tango Accordion",
29
+ "Acoustic Guitar (nylon)",
30
+ "Acoustic Guitar (steel)",
31
+ "Electric Guitar (jazz)",
32
+ "Electric Guitar (clean)",
33
+ "Electric Guitar (muted)",
34
+ "Overdriven Guitar",
35
+ "Distortion Guitar",
36
+ "Guitar Harmonics",
37
+ "Acoustic Bass",
38
+ "Electric Bass (finger)",
39
+ "Electric Bass (pick)",
40
+ "Fretless Bass",
41
+ "Slap Bass 1",
42
+ "Slap Bass 2",
43
+ "Synth Bass 1",
44
+ "Synth Bass 2",
45
+ "Violin",
46
+ "Viola",
47
+ "Cello",
48
+ "Contrabass",
49
+ "Tremolo Strings",
50
+ "Pizzicato Strings",
51
+ "Orchestral Harp",
52
+ "Timpani",
53
+ "String Ensemble 1",
54
+ "String Ensemble 2",
55
+ "Synth Strings 1",
56
+ "Synth Strings 2",
57
+ "Choir Aahs",
58
+ "Voice Oohs",
59
+ "Synth Choir",
60
+ "Orchestra Hit",
61
+ "Trumpet",
62
+ "Trombone",
63
+ "Tuba",
64
+ "Muted Trumpet",
65
+ "French Horn",
66
+ "Brass Section",
67
+ "Synth Brass 1",
68
+ "Synth Brass 2",
69
+ "Soprano Sax",
70
+ "Alto Sax",
71
+ "Tenor Sax",
72
+ "Baritone Sax",
73
+ "Oboe",
74
+ "English Horn",
75
+ "Bassoon",
76
+ "Clarinet",
77
+ "Piccolo",
78
+ "Flute",
79
+ "Recorder",
80
+ "Pan Flute",
81
+ "Blown Bottle",
82
+ "Shakuhachi",
83
+ "Whistle",
84
+ "Ocarina",
85
+ "Lead 1 (square)",
86
+ "Lead 2 (sawtooth)",
87
+ "Lead 4 (chiff)",
88
+ "Lead 5 (charang)",
89
+ "Lead 6 (voice)",
90
+ "Lead 7 (fifths)",
91
+ "Lead 8 (bass + lead)",
92
+ "Pad 1 (new age)",
93
+ "Pad 2 (warm)",
94
+ "Pad 3 (polysynth)",
95
+ "Pad 4 (choir)",
96
+ "Pad 5 (bowed)",
97
+ "Pad 6 (metallic)",
98
+ "Pad 7 (halo)",
99
+ "Pad 8 (sweep)",
100
+ "FX 1 (rain)",
101
+ "FX 2 (soundtrack)",
102
+ "FX 3 (crystal)",
103
+ "FX 4 (atmosphere)",
104
+ "FX 5 (brightness)",
105
+ "FX 7 (echoes)",
106
+ "FX 8 (sci-fi)",
107
+ "Koto",
108
+ "Kalimba",
109
+ "Bagpipe",
110
+ "Fiddle",
111
+ "Shanai",
112
+ "Tinkle Bell",
113
+ "Agogo",
114
+ "Steel Drums",
115
+ "Woodblock",
116
+ "Taiko Drum",
117
+ "Melodic Tom",
118
+ "Synth Drum",
119
+ "Reverse Cymbal",
120
+ "Guitar Fret Noise",
121
+ "Breath Noise",
122
+ "Seashore",
123
+ "Bird Tweet",
124
+ "Telephone Ring",
125
+ "Helicopter",
126
+ "Applause",
127
+ ]
gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ env/
main.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ import gradio as gr
4
+
5
+ from utils import (
6
+ generate_song,
7
+ change_tempo,
8
+ )
9
+
10
+
11
+ os.environ["PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION"] = "python"
12
+
13
+ DESCRIPTION = """
14
+ <h1>🎵 Multitrack Music Generator 🎶</h1>
15
+ """
16
+
17
+ genres = ["ROCK", "POP", "JAZZ", "RANDOM"]
18
+
19
+ demo = gr.Blocks()
20
+
21
+
22
+ def run():
23
+ with demo:
24
+ gr.HTML(DESCRIPTION)
25
+ with gr.Row():
26
+ with gr.Column():
27
+ temp = gr.Slider(
28
+ minimum=0, maximum=1, step=0.05, value=0.85, label="Temperature"
29
+ )
30
+ genre = gr.Dropdown(
31
+ choices=genres, value="POP", label="Select the genre"
32
+ )
33
+ with gr.Row():
34
+ btn_from_scratch = gr.Button("Start")
35
+ btn_continue = gr.Button("Generate More")
36
+ with gr.Column():
37
+ with gr.Group():
38
+ audio_output = gr.Video(show_share_button=True)
39
+ midi_file = gr.File()
40
+ with gr.Row():
41
+ qpm = gr.Slider(
42
+ minimum=60, maximum=140, step=10, value=120, label="Tempo"
43
+ )
44
+ btn_qpm = gr.Button("Change Tempo")
45
+ with gr.Row():
46
+ with gr.Column():
47
+ plot_output = gr.Plot()
48
+ with gr.Column():
49
+ instruments_output = gr.Markdown("# List of generated instruments")
50
+ with gr.Row():
51
+ text_sequence = gr.Text()
52
+ empty_sequence = gr.Text(visible=False)
53
+ with gr.Row():
54
+ num_tokens = gr.Text(visible=False)
55
+ btn_from_scratch.click(
56
+ fn=generate_song,
57
+ inputs=[genre, temp, empty_sequence, qpm],
58
+ outputs=[
59
+ audio_output,
60
+ midi_file,
61
+ plot_output,
62
+ instruments_output,
63
+ text_sequence,
64
+ num_tokens,
65
+ ],
66
+ )
67
+ btn_continue.click(
68
+ fn=generate_song,
69
+ inputs=[genre, temp, text_sequence, qpm],
70
+ outputs=[
71
+ audio_output,
72
+ midi_file,
73
+ plot_output,
74
+ instruments_output,
75
+ text_sequence,
76
+ num_tokens,
77
+ ],
78
+ )
79
+ btn_qpm.click(
80
+ fn=change_tempo,
81
+ inputs=[text_sequence, qpm],
82
+ outputs=[
83
+ audio_output,
84
+ midi_file,
85
+ plot_output,
86
+ instruments_output,
87
+ text_sequence,
88
+ num_tokens,
89
+ ],
90
+ )
91
+
92
+ demo.launch(server_name="0.0.0.0", server_port=7860)
93
+
94
+
95
+ if __name__ == "__main__":
96
+ run()
model.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from typing import Tuple
3
+ from transformers import AutoTokenizer, AutoModelForCausalLM
4
+
5
+ tokenizer = None
6
+ model = None
7
+
8
+
9
+ def get_model_and_tokenizer() -> Tuple[AutoModelForCausalLM, AutoTokenizer]:
10
+
11
+ global model, tokenizer
12
+ if model is None or tokenizer is None:
13
+ # Set device
14
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
15
+
16
+ # Load the tokenizer and the model
17
+ tokenizer = AutoTokenizer.from_pretrained("juancopi81/lmd_8bars_tokenizer")
18
+ model = AutoModelForCausalLM.from_pretrained(
19
+ "juancopi81/lmd-8bars-2048-epochs40_v4"
20
+ )
21
+
22
+ # Move model to device
23
+ model = model.to(device)
24
+
25
+ return model, tokenizer
pyproject.toml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ [tool.black]
2
+ exclude = '''
3
+ (
4
+ /env
5
+ )
6
+ '''
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ gradio
2
+ bokeh==2.4.3
3
+ note-seq==0.0.5
4
+ matplotlib
5
+ transformers
6
+ pyfluidsynth==1.3.0
7
+ torch
8
+ pydantic==2.0.3
string_to_notes.py ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Optional
2
+
3
+ from note_seq.protobuf.music_pb2 import NoteSequence
4
+ from note_seq.constants import STANDARD_PPQ
5
+
6
+
7
+ def token_sequence_to_note_sequence(
8
+ token_sequence: str,
9
+ qpm: float = 120.0,
10
+ use_program: bool = True,
11
+ use_drums: bool = True,
12
+ instrument_mapper: Optional[dict] = None,
13
+ only_piano: bool = False,
14
+ ) -> NoteSequence:
15
+
16
+ if isinstance(token_sequence, str):
17
+ token_sequence = token_sequence.split()
18
+
19
+ note_sequence = empty_note_sequence(qpm)
20
+
21
+ # Compute note and bar lengths based on the provided QPM
22
+ note_length_16th = 0.25 * 60 / qpm
23
+ bar_length = 4.0 * 60 / qpm
24
+
25
+ # Render all notes.
26
+ current_program = 1
27
+ current_is_drum = False
28
+ current_instrument = 0
29
+ track_count = 0
30
+ for _, token in enumerate(token_sequence):
31
+ if token == "PIECE_START":
32
+ pass
33
+ elif token == "PIECE_END":
34
+ break
35
+ elif token == "TRACK_START":
36
+ current_bar_index = 0
37
+ track_count += 1
38
+ pass
39
+ elif token == "TRACK_END":
40
+ pass
41
+ elif token == "KEYS_START":
42
+ pass
43
+ elif token == "KEYS_END":
44
+ pass
45
+ elif token.startswith("KEY="):
46
+ pass
47
+ elif token.startswith("INST"):
48
+ instrument = token.split("=")[-1]
49
+ if instrument != "DRUMS" and use_program:
50
+ if instrument_mapper is not None:
51
+ if instrument in instrument_mapper:
52
+ instrument = instrument_mapper[instrument]
53
+ current_program = int(instrument)
54
+ current_instrument = track_count
55
+ current_is_drum = False
56
+ if instrument == "DRUMS" and use_drums:
57
+ current_instrument = 0
58
+ current_program = 0
59
+ current_is_drum = True
60
+ elif token == "BAR_START":
61
+ current_time = current_bar_index * bar_length
62
+ current_notes = {}
63
+ elif token == "BAR_END":
64
+ current_bar_index += 1
65
+ pass
66
+ elif token.startswith("NOTE_ON"):
67
+ pitch = int(token.split("=")[-1])
68
+ note = note_sequence.notes.add()
69
+ note.start_time = current_time
70
+ note.end_time = current_time + 4 * note_length_16th
71
+ note.pitch = pitch
72
+ note.instrument = current_instrument
73
+ note.program = current_program
74
+ note.velocity = 80
75
+ note.is_drum = current_is_drum
76
+ current_notes[pitch] = note
77
+ elif token.startswith("NOTE_OFF"):
78
+ pitch = int(token.split("=")[-1])
79
+ if pitch in current_notes:
80
+ note = current_notes[pitch]
81
+ note.end_time = current_time
82
+ elif token.startswith("TIME_DELTA"):
83
+ delta = float(token.split("=")[-1]) * note_length_16th
84
+ current_time += delta
85
+ elif token.startswith("DENSITY="):
86
+ pass
87
+ elif token == "[PAD]":
88
+ pass
89
+ else:
90
+ pass
91
+
92
+ # Make the instruments right.
93
+ instruments_drums = []
94
+ for note in note_sequence.notes:
95
+ pair = [note.program, note.is_drum]
96
+ if pair not in instruments_drums:
97
+ instruments_drums += [pair]
98
+ note.instrument = instruments_drums.index(pair)
99
+
100
+ if only_piano:
101
+ for note in note_sequence.notes:
102
+ if not note.is_drum:
103
+ note.instrument = 0
104
+ note.program = 0
105
+
106
+ return note_sequence
107
+
108
+
109
+ def empty_note_sequence(qpm: float = 120.0, total_time: float = 0.0) -> NoteSequence:
110
+ """
111
+ Creates an empty note sequence.
112
+
113
+ Args:
114
+ qpm (float, optional): The quarter notes per minute. Defaults to 120.0.
115
+ total_time (float, optional): The total time. Defaults to 0.0.
116
+
117
+ Returns:
118
+ NoteSequence: The empty note sequence.
119
+ """
120
+ note_sequence = NoteSequence()
121
+ note_sequence.tempos.add().qpm = qpm
122
+ note_sequence.ticks_per_quarter = STANDARD_PPQ
123
+ note_sequence.total_time = total_time
124
+ return note_sequence
utils.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List, Tuple
2
+
3
+ import gradio as gr
4
+ from transformers import AutoTokenizer, AutoModelForCausalLM
5
+ import note_seq
6
+ from matplotlib.figure import Figure
7
+ from numpy import ndarray
8
+ import torch
9
+
10
+ from constants import GM_INSTRUMENTS, SAMPLE_RATE
11
+ from string_to_notes import token_sequence_to_note_sequence
12
+ from model import get_model_and_tokenizer
13
+
14
+
15
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
16
+
17
+ # Load the tokenizer and the model
18
+ model, tokenizer = get_model_and_tokenizer()
19
+
20
+
21
+ def create_seed_string(genre: str = "OTHER") -> str:
22
+
23
+ if genre == "RANDOM":
24
+ seed_string = "PIECE_START"
25
+ else:
26
+ seed_string = f"PIECE_START GENRE={genre} TRACK_START"
27
+ return seed_string
28
+
29
+
30
+ def get_instruments(text_sequence: str) -> List[str]:
31
+ """
32
+ Extracts the list of instruments from a text sequence.
33
+
34
+ Args:
35
+ text_sequence (str): The text sequence.
36
+
37
+ Returns:
38
+ List[str]: The list of instruments.
39
+ """
40
+ instruments = []
41
+ parts = text_sequence.split()
42
+ for part in parts:
43
+ if part.startswith("INST="):
44
+ if part[5:] == "DRUMS":
45
+ instruments.append("Drums")
46
+ else:
47
+ index = int(part[5:])
48
+ instruments.append(GM_INSTRUMENTS[index])
49
+ return instruments
50
+
51
+
52
+ def generate_new_instrument(seed: str, temp: float = 0.75) -> str:
53
+
54
+ seed_length = len(tokenizer.encode(seed))
55
+
56
+ while True:
57
+ # Encode the conditioning tokens.
58
+ input_ids = tokenizer.encode(seed, return_tensors="pt")
59
+
60
+ # Move the input_ids tensor to the same device as the model
61
+ input_ids = input_ids.to(model.device)
62
+
63
+ # Generate more tokens.
64
+ eos_token_id = tokenizer.encode("TRACK_END")[0]
65
+ generated_ids = model.generate(
66
+ input_ids,
67
+ max_new_tokens=2048,
68
+ do_sample=True,
69
+ temperature=temp,
70
+ eos_token_id=eos_token_id,
71
+ )
72
+ generated_sequence = tokenizer.decode(generated_ids[0])
73
+
74
+ # Check if the generated sequence contains "NOTE_ON" beyond the seed
75
+ new_generated_sequence = tokenizer.decode(generated_ids[0][seed_length:])
76
+ if "NOTE_ON" in new_generated_sequence:
77
+ return generated_sequence
78
+
79
+
80
+ def get_outputs_from_string(
81
+ generated_sequence: str, qpm: int = 120
82
+ ) -> Tuple[ndarray, str, Figure, str, str]:
83
+
84
+ instruments = get_instruments(generated_sequence)
85
+ instruments_str = "\n".join(f"- {instrument}" for instrument in instruments)
86
+ note_sequence = token_sequence_to_note_sequence(generated_sequence, qpm=qpm)
87
+
88
+ synth = note_seq.fluidsynth
89
+ array_of_floats = synth(note_sequence, sample_rate=SAMPLE_RATE)
90
+ int16_data = note_seq.audio_io.float_samples_to_int16(array_of_floats)
91
+ fig = note_seq.plot_sequence(note_sequence, show_figure=False)
92
+ num_tokens = str(len(generated_sequence.split()))
93
+ audio = gr.make_waveform((SAMPLE_RATE, int16_data))
94
+ note_seq.note_sequence_to_midi_file(note_sequence, "midi_ouput.mid")
95
+ return audio, "midi_ouput.mid", fig, instruments_str, num_tokens
96
+
97
+
98
+ def remove_last_instrument(
99
+ text_sequence: str, qpm: int = 120
100
+ ) -> Tuple[ndarray, str, Figure, str, str, str]:
101
+
102
+ # We split the song into tracks by splitting on 'TRACK_START'
103
+ tracks = text_sequence.split("TRACK_START")
104
+ # We keep all tracks except the last one
105
+ modified_tracks = tracks[:-1]
106
+ # We join the tracks back together, adding back the 'TRACK_START' that was removed by split
107
+ new_song = "TRACK_START".join(modified_tracks)
108
+
109
+ if len(tracks) == 2:
110
+ # There is only one instrument, so start from scratch
111
+ audio, midi_file, fig, instruments_str, new_song, num_tokens = generate_song(
112
+ text_sequence=new_song
113
+ )
114
+ elif len(tracks) == 1:
115
+ # No instrument so start from empty sequence
116
+ audio, midi_file, fig, instruments_str, new_song, num_tokens = generate_song(
117
+ text_sequence=""
118
+ )
119
+ else:
120
+ audio, midi_file, fig, instruments_str, num_tokens = get_outputs_from_string(
121
+ new_song, qpm
122
+ )
123
+
124
+ return audio, midi_file, fig, instruments_str, new_song, num_tokens
125
+
126
+
127
+ def regenerate_last_instrument(
128
+ text_sequence: str, qpm: int = 120
129
+ ) -> Tuple[ndarray, str, Figure, str, str, str]:
130
+
131
+ last_inst_index = text_sequence.rfind("INST=")
132
+ if last_inst_index == -1:
133
+ # No instrument so start from empty sequence
134
+ audio, midi_file, fig, instruments_str, new_song, num_tokens = generate_song(
135
+ text_sequence="", qpm=qpm
136
+ )
137
+ else:
138
+ # Take it from the last instrument and continue generation
139
+ next_space_index = text_sequence.find(" ", last_inst_index)
140
+ new_seed = text_sequence[:next_space_index]
141
+ audio, midi_file, fig, instruments_str, new_song, num_tokens = generate_song(
142
+ text_sequence=new_seed, qpm=qpm
143
+ )
144
+ return audio, midi_file, fig, instruments_str, new_song, num_tokens
145
+
146
+
147
+ def change_tempo(
148
+ text_sequence: str, qpm: int
149
+ ) -> Tuple[ndarray, str, Figure, str, str, str]:
150
+
151
+ audio, midi_file, fig, instruments_str, num_tokens = get_outputs_from_string(
152
+ text_sequence, qpm=qpm
153
+ )
154
+ return audio, midi_file, fig, instruments_str, text_sequence, num_tokens
155
+
156
+
157
+ def generate_song(
158
+ genre: str = "OTHER",
159
+ temp: float = 0.75,
160
+ text_sequence: str = "",
161
+ qpm: int = 120,
162
+ ) -> Tuple[ndarray, str, Figure, str, str, str]:
163
+
164
+ if text_sequence == "":
165
+ seed_string = create_seed_string(genre)
166
+ else:
167
+ seed_string = text_sequence
168
+
169
+ generated_sequence = generate_new_instrument(seed=seed_string, temp=temp)
170
+ audio, midi_file, fig, instruments_str, num_tokens = get_outputs_from_string(
171
+ generated_sequence, qpm
172
+ )
173
+ return audio, midi_file, fig, instruments_str, generated_sequence, num_tokens