Skip to content

Commit b391021

Browse files
committed
test: add Lyria music generation tests covering various prompts and modalities.
1 parent 36d6b9d commit b391021

1 file changed

Lines changed: 212 additions & 0 deletions

File tree

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
package io.github.glaforge.gemini.interactions;
2+
3+
import io.github.glaforge.gemini.interactions.model.Content.AudioContent;
4+
import io.github.glaforge.gemini.interactions.model.Content.ImageContent;
5+
import io.github.glaforge.gemini.interactions.model.Content.TextContent;
6+
import io.github.glaforge.gemini.interactions.model.Interaction;
7+
import io.github.glaforge.gemini.interactions.model.InteractionParams.ModelInteractionParams;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
10+
11+
import java.io.IOException;
12+
import java.nio.file.Files;
13+
import java.nio.file.Path;
14+
import java.nio.file.Paths;
15+
import java.util.List;
16+
import java.net.URI;
17+
18+
import static org.junit.jupiter.api.Assertions.*;
19+
20+
@EnabledIfEnvironmentVariable(named = "GEMINI_API_KEY", matches = ".+")
21+
public class LyriaTest {
22+
23+
private final GeminiInteractionsClient client = GeminiInteractionsClient.builder()
24+
.apiKey(System.getenv("GEMINI_API_KEY"))
25+
.build();
26+
27+
private void saveAudio(AudioContent audio, String filename) {
28+
assertNotNull(audio.data());
29+
assertTrue(audio.data().length > 0);
30+
assertEquals("audio", audio.type());
31+
System.out.println("Received audio data of length: " + audio.data().length + " for " + filename);
32+
33+
try {
34+
Path targetPath = Paths.get("target", filename);
35+
Files.createDirectories(targetPath.getParent());
36+
37+
// Lyria audio output natively encoded as MP3 byte stream
38+
byte[] audioData = audio.data();
39+
Files.write(targetPath, audioData);
40+
41+
System.out.println("Saved Lyria generated audio to: " + targetPath.toAbsolutePath());
42+
} catch (IOException e) {
43+
fail("Failed to save audio file: " + e.getMessage());
44+
}
45+
}
46+
47+
private void printLyrics(Interaction interaction) {
48+
interaction.outputs().stream()
49+
.filter(output -> output instanceof TextContent)
50+
.map(output -> (TextContent) output)
51+
.findFirst()
52+
.ifPresent(textContent -> System.out.println("Lyrics / Structure Generated:\n" + textContent.text()));
53+
}
54+
55+
@Test
56+
public void testBasicMusicGeneration() {
57+
System.out.println("Running testBasicMusicGeneration");
58+
ModelInteractionParams request = ModelInteractionParams.builder()
59+
.model("models/lyria-3-clip-preview")
60+
.input("An epic song with opera voices about a quest. Deep synths and a speeding up tempo.")
61+
.responseModalities(
62+
Interaction.Modality.AUDIO,
63+
Interaction.Modality.TEXT)
64+
.build();
65+
66+
Interaction interaction = client.create(request);
67+
68+
assertNotNull(interaction);
69+
assertNotNull(interaction.outputs());
70+
71+
printLyrics(interaction);
72+
73+
boolean hasAudio = interaction.outputs().stream()
74+
.anyMatch(output -> output instanceof AudioContent);
75+
assertTrue(hasAudio, "Response should contain audio content");
76+
77+
interaction.outputs().stream()
78+
.filter(output -> output instanceof AudioContent)
79+
.map(output -> (AudioContent) output)
80+
.findFirst()
81+
.ifPresent(audio -> saveAudio(audio, "lyria-basic-quest.mp3"));
82+
}
83+
84+
@Test
85+
public void testStructuredMusicGeneration() {
86+
System.out.println("Running testStructuredMusicGeneration");
87+
ModelInteractionParams request = ModelInteractionParams.builder()
88+
.model("models/lyria-3-clip-preview")
89+
.input("""
90+
[Intro] Calm piano music setting a sunset scene on the beach
91+
[Verse] Epic rock balade as the storm rages.
92+
[Outro] Opera with choir as the sun reappears again through the black clouds.
93+
""")
94+
.responseModalities(
95+
Interaction.Modality.AUDIO,
96+
Interaction.Modality.TEXT)
97+
.build();
98+
99+
Interaction interaction = client.create(request);
100+
printLyrics(interaction);
101+
102+
interaction.outputs().stream()
103+
.filter(output -> output instanceof AudioContent)
104+
.map(output -> (AudioContent) output)
105+
.findFirst()
106+
.ifPresent(audio -> saveAudio(audio, "lyria-structured-storm.mp3"));
107+
}
108+
109+
@Test
110+
public void testGenerationWithLyrics() {
111+
System.out.println("Running testGenerationWithLyrics");
112+
ModelInteractionParams request = ModelInteractionParams.builder()
113+
.model("models/lyria-3-clip-preview")
114+
.input("""
115+
An uplifting song with guitar rifts about nano banana.
116+
The lyrics should be:
117+
Yellow peel, a tiny sweet, The Nano Banana, a tropical treat. But wait—it
118+
hums, it starts to create, Switching into AI mode. Not a fruit, but a smart
119+
machine, The bananiest model you've ever seen.
120+
""")
121+
.responseModalities(
122+
Interaction.Modality.AUDIO,
123+
Interaction.Modality.TEXT)
124+
.build();
125+
126+
Interaction interaction = client.create(request);
127+
printLyrics(interaction);
128+
129+
interaction.outputs().stream()
130+
.filter(output -> output instanceof AudioContent)
131+
.map(output -> (AudioContent) output)
132+
.findFirst()
133+
.ifPresent(audio -> saveAudio(audio, "lyria-lyrics-banana.mp3"));
134+
}
135+
136+
@Test
137+
public void testInstrumentalMusicGeneration() {
138+
System.out.println("Running testInstrumentalMusicGeneration");
139+
ModelInteractionParams request = ModelInteractionParams.builder()
140+
.model("models/lyria-3-clip-preview")
141+
// Lyria supports instrumental generation implicitly through prompts
142+
// In python notebook, creating instrumental only is mentioned as:
143+
// For background music, soundtracks, or game loops where vocals aren't needed, you can use the instrumental_only parameter.
144+
// Wait, interactions API currently might not expose instrumental_only parameter directly on the top-level.
145+
// Using a strong prompt indicating instrumental only instead, as shown in notebook's example prompt.
146+
.input("Create a looping meditation music that feels like the wind. instrumental only.")
147+
.responseModalities(Interaction.Modality.AUDIO)
148+
.build();
149+
150+
Interaction interaction = client.create(request);
151+
152+
interaction.outputs().stream()
153+
.filter(output -> output instanceof AudioContent)
154+
.map(output -> (AudioContent) output)
155+
.findFirst()
156+
.ifPresent(audio -> saveAudio(audio, "lyria-instrumental-wind.mp3"));
157+
}
158+
159+
@Test
160+
public void testFullSongGeneration() {
161+
System.out.println("Running testFullSongGeneration");
162+
ModelInteractionParams request = ModelInteractionParams.builder()
163+
// Using the Lyria 3 Pro model for full-length song generation
164+
.model("models/lyria-3-pro-preview")
165+
.input("Write a full length epic power metal song about a brave knight fighting a dragon. It should have a guitar solo.")
166+
.responseModalities(List.of(Interaction.Modality.AUDIO, Interaction.Modality.TEXT))
167+
.build();
168+
169+
Interaction interaction = client.create(request);
170+
printLyrics(interaction);
171+
172+
interaction.outputs().stream()
173+
.filter(output -> output instanceof AudioContent)
174+
.map(output -> (AudioContent) output)
175+
.findFirst()
176+
.ifPresent(audio -> saveAudio(audio, "lyria-full-knight-dragon.mp3"));
177+
}
178+
179+
@Test
180+
public void testImageToMusicGeneration() {
181+
System.out.println("Running testImageToMusicGeneration");
182+
try {
183+
// Downloading a sample image directly from the Generative AI cookbook examples
184+
byte[] imageBytes = URI.create("https://storage.googleapis.com/generativeai-downloads/images/groceries.jpeg")
185+
.toURL()
186+
.openStream()
187+
.readAllBytes();
188+
189+
ModelInteractionParams request = ModelInteractionParams.builder()
190+
.model("models/lyria-3-clip-preview")
191+
.input(
192+
new TextContent("An epic song with opera voices about this quest. Deep synths and a speeding up tempo."),
193+
new ImageContent(imageBytes, "image/jpeg")
194+
)
195+
.responseModalities(
196+
Interaction.Modality.AUDIO,
197+
Interaction.Modality.TEXT)
198+
.build();
199+
200+
Interaction interaction = client.create(request);
201+
printLyrics(interaction);
202+
203+
interaction.outputs().stream()
204+
.filter(output -> output instanceof AudioContent)
205+
.map(output -> (AudioContent) output)
206+
.findFirst()
207+
.ifPresent(audio -> saveAudio(audio, "lyria-image-groceries.mp3"));
208+
} catch (IOException e) {
209+
fail("Failed to download image or generate music: " + e.getMessage());
210+
}
211+
}
212+
}

0 commit comments

Comments
 (0)