diff --git a/BassBoom.Basolia/File/FileTools.cs b/BassBoom.Basolia/File/FileTools.cs index 4c2922c..77b87e8 100644 --- a/BassBoom.Basolia/File/FileTools.cs +++ b/BassBoom.Basolia/File/FileTools.cs @@ -24,6 +24,7 @@ using BassBoom.Native.Interop.Init; using BassBoom.Native.Interop.Play; using SpecProbe.Software.Platform; +using System.IO; using System.Linq; using System.Net.Http; using System.Threading.Tasks; @@ -184,6 +185,50 @@ public static async Task OpenUrlAsync(BasoliaMedia? basolia, string path) PlaybackTools.FeedRadio(basolia); } + /// + /// Opens a stream + /// + /// Basolia instance that contains a valid handle + /// MPEG audio stream + public static void OpenFrom(BasoliaMedia? basolia, Stream? audioStream) => + Task.Run(() => OpenFromAsync(basolia, audioStream)).Wait(); + + /// + /// Opens a stream + /// + /// Basolia instance that contains a valid handle + /// MPEG audio stream + public static async Task OpenFromAsync(BasoliaMedia? basolia, Stream? audioStream) + { + InitBasolia.CheckInited(); + if (basolia is null) + throw new BasoliaException("Basolia instance is not provided", mpg123_errors.MPG123_BAD_HANDLE); + + // Check to see if the file is open + if (IsOpened(basolia)) + throw new BasoliaException("Can't open this URL while the current file or a radio station is still open", mpg123_errors.MPG123_BAD_FILE); + + // Check to see if we provided a stream + if (audioStream is null) + throw new BasoliaException("Audio stream is not provided", mpg123_errors.MPG123_BAD_FILE); + + // We're now entering the dangerous zone + unsafe + { + // Open the stream + var handle = basolia._mpg123Handle; + var @delegate = MpgNative.GetDelegate(MpgNative.libManagerMpg, nameof(NativeInput.mpg123_open_feed)); + int openStatus = @delegate.Invoke(handle); + if (openStatus == (int)mpg123_errors.MPG123_ERR) + throw new BasoliaException("Can't open stream", mpg123_errors.MPG123_ERR); + basolia.isOpened = true; + } + basolia.currentFile = new(false, "", audioStream, null, ""); + + // If necessary, feed. + await PlaybackTools.FeedStream(basolia); + } + /// /// Closes a currently opened media file /// @@ -214,6 +259,7 @@ public static void CloseFile(BasoliaMedia? basolia) throw new BasoliaException("Can't close file", mpg123_errors.MPG123_ERR); basolia.isOpened = false; basolia.isRadioStation = false; + basolia.currentFile?.Stream?.Close(); basolia.currentFile = null; } } diff --git a/BassBoom.Basolia/Playback/PlaybackTools.cs b/BassBoom.Basolia/Playback/PlaybackTools.cs index c694dd2..7367034 100644 --- a/BassBoom.Basolia/Playback/PlaybackTools.cs +++ b/BassBoom.Basolia/Playback/PlaybackTools.cs @@ -110,9 +110,6 @@ public static void Play(BasoliaMedia? basolia) // We're now entering the dangerous zone unsafe { - var handle = basolia._mpg123Handle; - var outHandle = basolia._out123Handle; - // Reset the format. Orders here matter. var (rate, channels, encoding) = FormatTools.GetFormatInfo(basolia); FormatTools.NoFormat(basolia); @@ -487,8 +484,6 @@ internal static void FeedRadio(BasoliaMedia? basolia) unsafe { - var handle = basolia._mpg123Handle; - // Get the MP3 frame length first string metaIntStr = currentRadio.Headers.GetValues("icy-metaint").First(); int metaInt = int.Parse(metaIntStr); @@ -515,6 +510,41 @@ internal static void FeedRadio(BasoliaMedia? basolia) basolia.radioIcy = icy; Debug.WriteLine($"{basolia.radioIcy}"); + // Copy the data to MPG123 + CopyBuffer(basolia, buffer); + } + } + + internal static async Task FeedStream(BasoliaMedia? basolia) + { + if (!FileTools.IsOpened(basolia) || FileTools.IsRadioStation(basolia)) + return; + var currentStream = FileTools.CurrentFile(basolia); + if (currentStream is null) + return; + if (currentStream.Stream is null) + return; + if (basolia is null) + throw new BasoliaException("Basolia instance is not provided", mpg123_errors.MPG123_BAD_HANDLE); + + // Now, get the MP3 frame + byte[] buffer = new byte[currentStream.Stream.Length]; + await currentStream.Stream.ReadAsync(buffer, 0, (int)currentStream.Stream.Length); + + // Copy the data to MPG123 + CopyBuffer(basolia, buffer); + } + + internal static void CopyBuffer(BasoliaMedia? basolia, byte[]? buffer) + { + if (buffer is null) + return; + if (basolia is null) + throw new BasoliaException("Basolia instance is not provided", mpg123_errors.MPG123_BAD_HANDLE); + unsafe + { + var handle = basolia._mpg123Handle; + // Copy the data to MPG123 IntPtr data = Marshal.AllocHGlobal(buffer.Length); Marshal.Copy(buffer, 0, data, buffer.Length); diff --git a/BassBoom.Cli/CliBase/PlayerControls.cs b/BassBoom.Cli/CliBase/PlayerControls.cs index b48bbf8..4f85d10 100644 --- a/BassBoom.Cli/CliBase/PlayerControls.cs +++ b/BassBoom.Cli/CliBase/PlayerControls.cs @@ -439,19 +439,13 @@ internal static void PlayTest() InfoBoxNonModalColor.WriteInfoBox("Playing test sound...", false); // Extract the test sound asset to a temporary file - string path = PlatformHelper.IsOnWindows() ? $"{Environment.GetEnvironmentVariable("TEMP")}" : $"/tmp/"; - string fullPath = $"{path}/{DateTime.Now:ddMMyyyyHHmmssfff}.mp3"; var stream = typeof(PlayerControls).Assembly.GetManifestResourceStream("BassBoom.Cli.sample.mp3") ?? throw new Exception("Missing test sound data."); - var target = File.OpenWrite(fullPath); - stream.CopyTo(target); // Now, close the file and play it - target.Close(); - FileTools.OpenFile(BassBoomCli.basolia, fullPath); + FileTools.OpenFrom(BassBoomCli.basolia, stream); PlaybackTools.Play(BassBoomCli.basolia); FileTools.CloseFile(BassBoomCli.basolia); - File.Delete(fullPath); // Ask the user if everything is OK. int answer = InfoBoxButtonsColor.WriteInfoBoxButtons("Sound test", [new InputChoiceInfo("Yes", "Yes"), new InputChoiceInfo("No", "No")], "Is everything OK in this current configuration?");