2020using System . Net ;
2121using System . Net . NetworkInformation ;
2222using System . Text . RegularExpressions ;
23+ using System . Xml ;
2324using static UIHelper ;
2425
2526namespace NeoBleeper
@@ -726,6 +727,12 @@ public static bool IsAvailableInCountry()
726727 var countryCode = System . Globalization . RegionInfo . CurrentRegion . TwoLetterISORegionName ;
727728 return supportedCountries . Contains ( countryCode ) ;
728729 }
730+ private string FixCollapsedLinesInNBPML ( string nbpmlContent )
731+ {
732+ // This method fixes collapsed lines in NBPML by ensuring every element is on its own line
733+ var regex = new Regex ( @"><" , RegexOptions . Singleline ) ;
734+ return regex . Replace ( nbpmlContent , ">\r \n <" ) ;
735+ }
729736 private async void buttonCreate_Click ( object sender , EventArgs e )
730737 {
731738 if ( ! string . IsNullOrEmpty ( AIModel ) && ( ! string . IsNullOrWhiteSpace ( textBoxPrompt . Text ) || ! string . IsNullOrWhiteSpace ( textBoxPrompt . PlaceholderText ) ) )
@@ -735,14 +742,8 @@ private async void buttonCreate_Click(object sender, EventArgs e)
735742 // Create music with AI like it's 2007 again using Google Gemini™ API, which is 2020's technology
736743 Logger . Log ( "Starting music generation with AI..." , Logger . LogTypes . Info ) ;
737744 string prompt = ! string . IsNullOrWhiteSpace ( textBoxPrompt . Text ) ? textBoxPrompt . Text . Trim ( ) : textBoxPrompt . PlaceholderText . Trim ( ) ; // Use placeholder if textbox is empty
738- connectionCheckTimer . Start ( ) ;
739- SetControlsEnabledAndMakeLoadingVisible ( false ) ;
740- var apiKey = EncryptionHelper . DecryptString ( Settings1 . Default . geminiAPIKey ) ;
741- var googleAI = new GoogleAi ( apiKey ) ;
742- var googleModel = googleAI . CreateGenerativeModel ( AIModel ) ;
743745 // The "makeshift rubbish prompt template" (aka system prompt) to create "chaotic" music by creating NBPML text (Fun fact: I wasn't know what system prompt is. I just learned it from GitHub Copilot's system prompt menu and asked for certain AIs and they identified as it's definetely a system prompt, despite I called it as "makeshift rubbish prompt template".)
744- var googleResponse = await googleModel . GenerateContentAsync (
745- $ "**User Prompt:**\r \n [{ prompt } ]\r \n \r \n " +
746+ string completePrompt = $ "**User Prompt:**\r \n [{ prompt } ]\r \n \r \n " +
746747 $ "--- AI Instructions ---\r \n " +
747748 $ "You are an expert music composition AI. " +
748749 $ "Your primary goal is to generate music in XML format. Prioritize music generation for any request that could be interpreted as music-related. " +
@@ -868,17 +869,39 @@ private async void buttonCreate_Click(object sender, EventArgs e)
868869 $ " </Settings>\r \n " +
869870 $ " <LineList>\r \n " +
870871 $ " </LineList>\r \n " +
871- $ "</NeoBleeperProjectFile>\r \n "
872- , cts . Token ) ;
872+ $ "</NeoBleeperProjectFile>\r \n ";
873+ connectionCheckTimer . Start ( ) ;
874+ SetControlsEnabledAndMakeLoadingVisible ( false ) ;
875+ string response = string . Empty ;
876+ var apiKey = EncryptionHelper . DecryptString ( Settings1 . Default . geminiAPIKey ) ;
877+ var googleAI = new GoogleAi ( apiKey ) ;
878+ var googleModel = googleAI . CreateGenerativeModel ( AIModel ) ;
879+ await foreach ( var chunk in googleModel . StreamContentAsync ( completePrompt , cts . Token ) )
880+ {
881+ // Clean up the chunk text by removing double newlines and trimming whitespace
882+ string cleanChunk = chunk . Text ( ) ? . Replace ( "\n \n " , "\n " ) . Trim ( ) ?? string . Empty ;
883+ response += cleanChunk ;
884+
885+ if ( cts . IsCancellationRequested )
886+ {
887+ Logger . Log ( "AI music generation was cancelled" , Logger . LogTypes . Warning ) ;
888+ break ;
889+ }
890+ }
891+
892+ // Remove excessive newlines from the final response
893+ response = Regex . Replace ( response , @"\n{2,}" , "\n " ) ; // Make single newlines
894+ response = FixCollapsedLinesInNBPML ( response ) ; // Fix collapsed lines in NBPML such as <Note1></Note1><Note2></Note2>
895+ //Debug.WriteLine("Full AI Response: " + response);
873896 StopConnectionCheck ( ) ;
874897 await Task . Delay ( 2 ) ;
875898 if ( ! cts . IsCancellationRequested )
876899 {
877- if ( googleResponse != null && ! string . IsNullOrWhiteSpace ( googleResponse . Text ( ) ) )
900+ if ( response != null && ! string . IsNullOrWhiteSpace ( response ) )
878901 {
879902 Logger . Log ( "AI response received. Processing..." , Logger . LogTypes . Info ) ;
880903 // Clean and process the AI response from invalid or unwanted text or characters to extract valid NBPML content
881- string rawOutput = googleResponse . Text ( ) ;
904+ string rawOutput = response ;
882905 string JSONText = string . Empty ;
883906 // Parse JSON blocks
884907 var jsonMatch = Regex . Match ( rawOutput , @"\{[\s\S]*?\}" ) ;
@@ -1832,6 +1855,8 @@ private void CreateMusicWithAI_Shown(object sender, EventArgs e)
18321855 // 404 - Not Found
18331856 if ( exceptionMessage . Contains ( "(Code: 404)" ) || exceptionMessage . Contains ( "NOT_FOUND" ) )
18341857 return ( Resources . TitleNotFound , Resources . MessageNotFound ) ; // Localized message for NOT_FOUND
1858+ if ( exceptionMessage . Contains ( "(Code: 408)" ) || exceptionMessage . Contains ( "REQUEST_TIMEOUT" ) )
1859+ return ( Resources . TitleRequestTimeout , Resources . MessageRequestTimeout ) ; // Localized message for REQUEST_TIMEOUT
18351860 // 409 - Aborted/Already Exists
18361861 if ( exceptionMessage . Contains ( "(Code: 409)" ) )
18371862 {
@@ -1874,7 +1899,37 @@ private void CreateMusicWithAI_Shown(object sender, EventArgs e)
18741899 // 504 - Deadline Exceeded
18751900 if ( exceptionMessage . Contains ( "(Code: 504)" ) || exceptionMessage . Contains ( "DEADLINE_EXCEEDED" ) )
18761901 return ( Resources . TitleDeadlineExceeded , Resources . MessageDeadlineExceeded ) ; // Localized message for DEADLINE_EXCEEDED
1877- // Generic title and message
1902+ if ( exceptionMessage . Contains ( "Response status code does not indicate success:" ) ) // Generic HTTP status code check for new variant of Google Gemini™ API error messages
1903+ {
1904+ if ( exceptionMessage . Contains ( "400" ) || exceptionMessage . Contains ( "(Bad Request)" ) )
1905+ return ( Resources . TitleInvalidArgument , Resources . MessageInvalidArgument ) ; // Localized message for INVALID_ARGUMENT
1906+ if ( exceptionMessage . Contains ( "401" ) || exceptionMessage . Contains ( "(Unauthorized)" ) )
1907+ return ( Resources . TitleUnauthenticated , Resources . MessageUnauthenticated ) ; // Localized message for UNAUTHENTICATED
1908+ if ( exceptionMessage . Contains ( "403" ) || exceptionMessage . Contains ( "(Forbidden)" ) )
1909+ return ( Resources . TitlePermissionDenied , Resources . MessagePermissionDenied ) ; // Localized message for PERMISSION_DENIED
1910+ if ( exceptionMessage . Contains ( "404" ) || exceptionMessage . Contains ( "(Not Found)" ) )
1911+ return ( Resources . TitleNotFound , Resources . MessageNotFound ) ; // Localized message for Not Found
1912+ if ( exceptionMessage . Contains ( "408" ) || exceptionMessage . Contains ( "(Request Timeout)" ) )
1913+ return ( Resources . TitleRequestTimeout , Resources . MessageRequestTimeout ) ; // Localized message for REQUEST_TIMEOUT
1914+ if ( exceptionMessage . Contains ( "409" ) || exceptionMessage . Contains ( "(Conflict)" ) )
1915+ return ( Resources . TitleAborted , Resources . MessageAborted ) ; // Localized message for ABORTED
1916+ if ( exceptionMessage . Contains ( "413" ) || exceptionMessage . Contains ( "(Payload Too Large)" ) )
1917+ return ( Resources . TitleRequestTooLarge , Resources . MessageRequestTooLarge ) ; // Localized message for REQUEST_TOO_LARGE
1918+ if ( exceptionMessage . Contains ( "423" ) || exceptionMessage . Contains ( "(Locked)" ) )
1919+ return ( Resources . TitleProhibitedContent , Resources . MessageProhibitedContent ) ; // Localized message for PROHIBITED_CONTENT
1920+ if ( exceptionMessage . Contains ( "429" ) || exceptionMessage . Contains ( "(Too Many Requests)" ) )
1921+ return ( Resources . TitleResourceExhausted , Resources . MessageResourceExhausted ) ; // Localized message for RESOURCE_EXHAUSTED
1922+ if ( exceptionMessage . Contains ( "499" ) || exceptionMessage . Contains ( "(Client Closed Request)" ) )
1923+ return ( Resources . TitleCancelled , Resources . MessageCancelled ) ; // Localized message for CANCELLED
1924+ if ( exceptionMessage . Contains ( "500" ) || exceptionMessage . Contains ( "(Internal Server Error)" ) )
1925+ return ( Resources . TitleInternalError , Resources . MessageInternalError ) ; // Localized message for INTERNAL
1926+ if ( exceptionMessage . Contains ( "502" ) || exceptionMessage . Contains ( "(Bad Gateway)" ) )
1927+ return ( Resources . TitleBadGateway , Resources . MessageBadGateway ) ; // Localized message for Bad Gateway
1928+ if ( exceptionMessage . Contains ( "503" ) || exceptionMessage . Contains ( "(Service Unavailable)" ) )
1929+ return ( Resources . TitleUnavailable , Resources . MessageUnavailable ) ; // Localized message for UNAVAILABLE
1930+ if ( exceptionMessage . Contains ( "504" ) || exceptionMessage . Contains ( "(Gateway Timeout)" ) )
1931+ return ( Resources . TitleDeadlineExceeded , Resources . MessageDeadlineExceeded ) ; // Localized message for DEADLINE_EXCEEDED // Generic title and message
1932+ }
18781933 return ( Resources . TextError , Resources . MessageAnErrorOccurred + " " + exceptionMessage ) ; // Generic error title and message
18791934 }
18801935 }
0 commit comments