Skip to content

Commit 0737b5f

Browse files
Seperation of system instructions and prompt, and playback safety
- Prompt and system instructions are seperated and now sent through GenerativeAI.SystemInstruction. - Playback safety is ensured by preventing playing music if a note is already playing.
1 parent e680a1a commit 0737b5f

4 files changed

Lines changed: 601 additions & 34 deletions

File tree

NeoBleeper/CreateMusicWithAI.cs

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,8 +1046,13 @@ by creating NBPML text (Fun fact: I wasn't know what system prompt is.
10461046
I just learned it from GitHub Copilot's system prompt menu and asked for certain AIs and
10471047
they identified as it's definetely a system prompt, despite I called it as "makeshift rubbish
10481048
prompt template".)*/
1049-
string completePrompt = $"**User Prompt:**\r\n[{prompt}]\r\n\r\n" +
1050-
$"--- AI Instructions ---\r\n" +
1049+
1050+
/*Spoiler: Now, it's not rubbish anymore since I used GenerativeModel.SystemInstruction
1051+
property of Google_GenerativeAI library, but I kept it as is for nostalgia for
1052+
my good old "makeshift rubbish prompt template" days. :) */
1053+
1054+
// The string that contains system instructions for the AI model
1055+
string systemInstructions = $"--- AI Instructions ---\r\n" +
10511056
$"You are an expert music composition AI. " +
10521057
$"Your primary goal is to generate music in a well-formed NBPML XML file format. Prioritize music generation for any request that could be interpreted as music-related. " +
10531058
$"If the user prompt is a song name, artist name, composer name, or ANY music-related term (even a single word), treat it as a music composition request. " +
@@ -1194,16 +1199,17 @@ by creating NBPML text (Fun fact: I wasn't know what system prompt is.
11941199
$" </Line>\r\n" +
11951200
$" <!-- More <Line> elements representing musical events or rests -->\r\n" +
11961201
$" </LineList>\r\n" +
1197-
$"</NeoBleeperProjectFile>";
1202+
$"</NeoBleeperProjectFile>";
11981203
connectionCheckTimer.Start();
11991204
SetControlsEnabledAndMakeLoadingVisible(false);
12001205
var resultBuilder = new StringBuilder();
12011206
string response = string.Empty;
12021207
var apiKey = EncryptionHelper.DecryptString(Settings1.Default.geminiAPIKey);
12031208
var googleAI = new GoogleAi(apiKey);
12041209
var googleModel = googleAI.CreateGenerativeModel(AIModel);
1210+
googleModel.SystemInstruction = systemInstructions;
12051211
isMusicGenerationStarted = true; // Set the flag to indicate music generation has started
1206-
await foreach (var chunk in googleModel.StreamContentAsync(completePrompt, cts.Token))
1212+
await foreach (var chunk in googleModel.StreamContentAsync(prompt, cts.Token))
12071213
{
12081214
// Clean up the chunk text by removing double newlines and trimming whitespace
12091215
if (chunk?.Candidates == null) continue;
@@ -2524,6 +2530,35 @@ private string NormalizeNoteValues(string nbpmlContent)
25242530
if (string.IsNullOrWhiteSpace(nbpmlContent))
25252531
return string.Empty;
25262532

2533+
// Convert solfege to letter notes
2534+
var solfegeMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
2535+
{
2536+
// Natural notes
2537+
{ "Do", "C" }, { "Re", "D" }, { "Mi", "E" }, { "Fa", "F" },
2538+
{ "Sol", "G" }, { "La", "A" }, { "Ti", "B" }, { "Si", "B" },
2539+
// Accented variations (French/Portuguese)
2540+
{ "Ré", "D" }, { "Mí", "E" }, { "Fá", "F" }, { "Lá", "A" }
2541+
};
2542+
2543+
foreach (var pair in solfegeMap)
2544+
{
2545+
nbpmlContent = Regex.Replace(
2546+
nbpmlContent,
2547+
$@"<Note([1-4])>\s*{Regex.Escape(pair.Key)}([#b♯♭]?)(\d*)\s*</Note\1>",
2548+
m =>
2549+
{
2550+
var noteLetter = pair.Value;
2551+
var accidental = m.Groups[2].Value; // #, b, ♯, ♭ veya boş
2552+
var octave = m.Groups[3].Value; // Oktav numarası veya boş
2553+
// Oktav yoksa 4 ekle
2554+
if (string.IsNullOrEmpty(octave))
2555+
octave = "4";
2556+
return $"<Note{m.Groups[1].Value}>{noteLetter}{accidental}{octave}</Note{m.Groups[1].Value}>";
2557+
},
2558+
RegexOptions.IgnoreCase
2559+
);
2560+
}
2561+
25272562
// Fix notes with ambigious octaves (e.g., C, D#, A, Gb without octave)
25282563
nbpmlContent = Regex.Replace(
25292564
nbpmlContent,
@@ -2607,36 +2642,6 @@ private string NormalizeNoteValues(string nbpmlContent)
26072642
RegexOptions.IgnoreCase);
26082643
}
26092644

2610-
// Convert solfege to letter notes
2611-
var solfegeMap = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
2612-
{
2613-
// Natural notes
2614-
{ "Do", "C" }, { "Re", "D" }, { "Mi", "E" }, { "Fa", "F" },
2615-
{ "Sol", "G" }, { "La", "A" }, { "Ti", "B" }, { "Si", "B" },
2616-
// Accented variations (French/Portuguese)
2617-
{ "Ré", "D" }, { "Mí", "E" }, { "Fá", "F" }, { "Lá", "A" }
2618-
};
2619-
2620-
// Handle solfege sharps
2621-
foreach (var pair in solfegeMap)
2622-
{
2623-
nbpmlContent = Regex.Replace(
2624-
nbpmlContent,
2625-
$@"\b{Regex.Escape(pair.Key)}#(\d+)\b",
2626-
$"{pair.Value}#$1",
2627-
RegexOptions.IgnoreCase);
2628-
}
2629-
2630-
// Handle natural solfege
2631-
foreach (var pair in solfegeMap)
2632-
{
2633-
nbpmlContent = Regex.Replace(
2634-
nbpmlContent,
2635-
$@"\b{Regex.Escape(pair.Key)}(\d+)\b",
2636-
$"{pair.Value}$1",
2637-
RegexOptions.IgnoreCase);
2638-
}
2639-
26402645
// Fix hyphenated notes (C-5 -> C5)
26412646
nbpmlContent = Regex.Replace(nbpmlContent, @"\b([A-G]#?)-(\d+)\b", "$1$2", RegexOptions.IgnoreCase);
26422647

NeoBleeper/MainWindow.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5219,11 +5219,19 @@ private void eraseWholeLineToolStripMenuItem_Click(object sender, EventArgs e)
52195219

52205220
private void playAllToolStripMenuItem_Click(object sender, EventArgs e)
52215221
{
5222+
if(noteAlreadyPlaying)
5223+
{
5224+
return; // Prevent starting playback if notes are already playing
5225+
}
52225226
PlayAll();
52235227
}
52245228

52255229
private void playFromSelectedLineToolStripMenuItem_Click(object sender, EventArgs e)
52265230
{
5231+
if (noteAlreadyPlaying)
5232+
{
5233+
return; // Prevent starting playback if notes are already playing
5234+
}
52275235
PlayFromSelectedLine();
52285236
}
52295237

0 commit comments

Comments
 (0)