Guest User

NISTON Experimental Stream Player

a guest
Sep 22nd, 2014
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 16.70 KB | None | 0 0
  1. using System;
  2. using Un4seen.Bass;
  3. using Un4seen.Bass.AddOn.Tags;
  4. using System.Runtime.InteropServices;
  5.  
  6. // NISTON Portable Streaming Audio Player for Windows Unix and, perhaps Mac OSX
  7. //
  8. // A practical example on cross-platform development or how to use the
  9. // BASS Audio Library via BASS.NET under mono on ARM6 Linux.
  10. // This works on a Raspberry Pi! =)
  11. //
  12. // Homepage: http://niston.wordpress.com
  13. //
  14. // BASS.NET API: http://bass.radio42.com/
  15. // BASS for ARM Linux: http://www.un4seen.com/forum/?topic=13804.msg95617#msg95617
  16.  
  17.  
  18. namespace sndchk
  19. {
  20.     class Program
  21.     {
  22.         // some global variables for SYNCing (not working yet, but doesn't really matter)
  23.         static SYNCPROC _mySync;
  24.         static SYNCPROC _StallProc;
  25.        
  26.         // some strings for the bar graph displays
  27.         static string barBckg = "---------|---------|---------|---------|---------";
  28.         static string barScale = "|0      20|       40|       60|       80|      100|";
  29.  
  30.         // true if we have floating point channel support from BASS, will always be false on ARM
  31.         static bool flpSupport = true;
  32.  
  33.         static void Main(string[] args)
  34.         {
  35.             // clear the console and write application title
  36.             Console.Clear();
  37.             Console.Write("NISTON Experimental Stream Player");
  38.  
  39.             // Setup BASS Audio Lib
  40.             SetupBassLibrary();
  41.  
  42.             // check float
  43.             flpSupport = CheckFloatSupport();
  44.  
  45.             // init BASS using the default output device
  46.             if (Bass.BASS_Init(-1, 44100, BASSInit.BASS_DEVICE_DEFAULT, IntPtr.Zero))
  47.             {
  48.                 if (flpSupport) { Console.Write(" (32bit DSP)"); }
  49.  
  50.                 // check for command line arguments supplied
  51.                 if (args.Length == 0)
  52.                 {
  53.                     // add line feed and empty line
  54.                     Console.WriteLine();
  55.                     Console.WriteLine();
  56.                    
  57.                     // see if we are running on wintendo
  58.                     if (System.Environment.OSVersion.Platform.ToString().ToUpper().Contains("WIN32NT"))
  59.                     {
  60.                         // mono not required on windows
  61.                         Console.WriteLine("usage: sndchk.exe <Shoutcast URL>");
  62.                     }
  63.                     else
  64.                     {
  65.                         // mono required on other platforms
  66.                         Console.WriteLine("usage: mono sndchk.exe <Shoutcast URL>");
  67.                     }
  68.                        
  69.                     // additional line feed
  70.                     Console.SetCursorPosition(0, 17);
  71.  
  72.                     // terminate
  73.                     System.Environment.Exit(1);
  74.                 }
  75.  
  76.                 // get url from args
  77.                 string url = args[0];
  78.  
  79.                 // show status
  80.                 Console.SetCursorPosition(67, 0);
  81.                 Console.Write("Connecting...");
  82.  
  83.                 // show URL line
  84.                 Console.SetCursorPosition(0, 3);
  85.                 Console.Write("           URL : " + url);
  86.  
  87.                 // attempt to create stream from URL, use SAMPLE_FLOAT if system is capable
  88.                 int stream = Bass.BASS_StreamCreateURL(url, 0, BASSFlag.BASS_STREAM_STATUS | BASSFlag.BASS_STREAM_BLOCK | (flpSupport ? BASSFlag.BASS_SAMPLE_FLOAT : 0), null, System.IntPtr.Zero);
  89.                 Console.SetCursorPosition(67, 0);
  90.                 Console.Write("   Connected.");
  91.                
  92.                
  93.                 if (stream != 0)
  94.                 {
  95.                     // stream created, set SYNCs (it appears that SYNCs don't work in console application?)
  96.                     _mySync = new SYNCPROC(MetaSync);
  97.                     var res = Bass.BASS_ChannelSetSync(stream, BASSSync.BASS_SYNC_META, 0, _mySync, IntPtr.Zero);
  98.                     _StallProc = new SYNCPROC(StallSync);
  99.                     Bass.BASS_ChannelSetSync(stream, BASSSync.BASS_SYNC_STALL, 0, _StallProc, IntPtr.Zero);
  100.  
  101.                     // play the stream channel
  102.                     Bass.BASS_ChannelPlay(stream, false);
  103.                 }
  104.                 else
  105.                 {
  106.                     // error creating the stream, print message and terminate
  107.                     Bass.BASS_Free();
  108.                     Console.WriteLine("Stream error: {0}", Bass.BASS_ErrorGetCode());
  109.                     System.Environment.Exit(1);
  110.                 }
  111.  
  112.                 // playing now, so show Exit Hint.
  113.                 Console.SetCursorPosition(58, 0);
  114.                 Console.Write("Press any key to quit.");
  115.  
  116.                 // hide cursor
  117.                 Console.CursorVisible = false;
  118.  
  119.                 // main loop until key press
  120.                 while (!Console.KeyAvailable)
  121.                 {
  122.                     // check for stream EOF
  123.                     if (Bass.BASS_ChannelIsActive(stream) == BASSActive.BASS_ACTIVE_STOPPED)
  124.                     {
  125.                         // free ended stream / BASS library
  126.                         Bass.BASS_StreamFree(stream);
  127.                         Bass.BASS_Free();
  128.                        
  129.                         // clear status info
  130.                         // playing now, so show Exit Hint.
  131.                         // show goodbye message
  132.                         Console.SetCursorPosition(58, 0);
  133.                         Console.Write("     Stream has ended.");
  134.                         Console.SetCursorPosition(0, 17);
  135.                         Console.CursorVisible = true;
  136.                        
  137.                         // terminate
  138.                         System.Environment.Exit(0);
  139.                     }
  140.  
  141.                     // read stream information.........................................................
  142.  
  143.                     // get channel information
  144.                     BASS_CHANNELINFO chanInf = Bass.BASS_ChannelGetInfo(stream);
  145.  
  146.                     // determine channel bits
  147.                     int chanBits = 16;
  148.                     if (chanInf != null)
  149.                     {
  150.                         // get chanel bits
  151.                         if (chanInf.Is32bit) { chanBits = 32; }
  152.                         if (chanInf.Is8bit) { chanBits = 8; }
  153.                     }
  154.  
  155.                     // get audio levels
  156.                     int level = Bass.BASS_ChannelGetLevel(stream);
  157.                     int left = Utils.LowWord32(level);
  158.                     int right = Utils.HighWord32(level);
  159.  
  160.                     // get buffer level
  161.                     long len = Bass.BASS_StreamGetFilePosition(stream, BASSStreamFilePosition.BASS_FILEPOS_END);
  162.                     long down = Bass.BASS_StreamGetFilePosition(stream, BASSStreamFilePosition.BASS_FILEPOS_DOWNLOAD);
  163.                     long dec = Bass.BASS_StreamGetFilePosition(stream, BASSStreamFilePosition.BASS_FILEPOS_CURRENT);
  164.                     int progress = (int)(Math.Floor(((float)(down) - (float)(dec)) * (float)(100) / (float)(len)));
  165.                     if (progress > 100) { progress = 100; }; if (progress < 0) { progress = 0; }
  166.                    
  167.                     // get tags
  168.                     TAG_INFO myTags = new TAG_INFO();
  169.                     var tags = Bass.BASS_ChannelGetTags(stream, BASSTag.BASS_TAG_META);
  170.                     if (tags != null) { myTags.UpdateFromMETA(tags, true, true); }
  171.  
  172.                     // check http info
  173.                     var httpInfo = Bass.BASS_ChannelGetTagsHTTP(stream);
  174.                     var httpInfo2 = Bass.BASS_ChannelGetTags(stream, BASSTag.BASS_TAG_HTTP);
  175.  
  176.                     // get station and bitrate from ICY tags
  177.                     string bitRate = "";
  178.                     string stationName = "NO INFORMATION";
  179.                     string[] icyInfo = Bass.BASS_ChannelGetTagsICY(stream);
  180.                     if (icyInfo !=  null)
  181.                     {
  182.                         for (int n = 0; n <= icyInfo.GetUpperBound(0); n++)
  183.                         {
  184.                             // debug.print all ICY tags to output window
  185.                             System.Diagnostics.Debug.Print("ICY_TAG[" + n + "] : " + icyInfo[n]);
  186.                             // found bit rate
  187.                             if (icyInfo[n].Contains("icy-br")) { bitRate = icyInfo[n].Replace("icy-br:", ""); }
  188.                             // found station name
  189.                             if (icyInfo[n].Contains("icy-name")) { stationName = icyInfo[n].Replace("icy-name:", ""); }
  190.                         }
  191.                     }
  192.                    
  193.                     // update display..................................................................
  194.  
  195.                     // home cursor
  196.                     Console.SetCursorPosition(0, 5);
  197.                    
  198.                     // write station name
  199.                     Console.WriteLine("       Station : " + stationName);
  200.                    
  201.                     // write song information (artist, title)
  202.                     Console.SetCursorPosition(10, 6);
  203.                     Console.Write("Song : " + FormatSongLine(myTags.artist, myTags.title).Left(61));
  204.  
  205.                     // write channel information
  206.                     Console.SetCursorPosition(2, 7);
  207.                     Console.Write("Audio Format : " + ((bitRate != "") ? bitRate + "kbps " : "") + GetChannelFormatString(chanInf.chans) + " " + GetStreamFormatString(chanInf.ctype) + " (" + chanBits + "bits@" + Math.Round((float)((float)(chanInf.freq) / (float)(1000)), 1) + "kHz) ");
  208.  
  209.                     // write level meter backgrounds
  210.                     Console.SetCursorPosition(4, 9);
  211.                     Console.Write("Levels (L) : [" + barBckg + "]");
  212.                     Console.SetCursorPosition(11, 10);
  213.                     Console.Write("(R) : [" + barBckg + "]");
  214.                     // write scale bar
  215.                     Console.SetCursorPosition(17, 11);
  216.                     Console.Write(barScale + " %");
  217.                     // write buffer meter background
  218.                     Console.SetCursorPosition(8, 12);
  219.                     Console.WriteLine("Buffer : [" + barBckg + "]");
  220.                     // write left level meter
  221.                     Console.SetCursorPosition(18,9);
  222.                     Console.Write(GetLevelBar(left));
  223.                     // write right level meter
  224.                     Console.SetCursorPosition(18, 10);
  225.                     Console.Write(GetLevelBar(right));
  226.                     // write buffer meter
  227.                     Console.SetCursorPosition(18, 12);
  228.                     Console.Write(GetPercentBar(progress));
  229.  
  230.                     // ### DEBUG ONLY
  231.                     //Console.SetCursorPosition(5, 15);
  232.                     //if (httpInfo != null) { Console.Write("HTTP Info : " + httpInfo[0].ToString()); }
  233.  
  234.                     //Console.SetCursorPosition(5, 16);
  235.                     //if (httpInfo2 != IntPtr.Zero)
  236.                     //{
  237.                     //    Console.Write("HTTPInfo2 : ");
  238.                     //    Console.Write(Marshal.PtrToStringAnsi(httpInfo2).ToString());
  239.                     //} else
  240.                     //{ Console.Write("IntPtr.Zero"); }
  241.                    
  242.                    
  243.                     // hold refresh for a while
  244.                     System.Threading.Thread.Sleep(50);
  245.                 }
  246.                
  247.                 // free the stream
  248.                 Bass.BASS_StreamFree(stream);
  249.                 // free BASS
  250.                 Bass.BASS_Free();
  251.  
  252.                 Console.SetCursorPosition(58, 0);
  253.                 Console.Write("            User quit.");
  254.                 Console.SetCursorPosition(0, 17);
  255.            
  256.             }
  257.             else
  258.             {
  259.                 // init call failed
  260.                 Bass.BASS_Free();
  261.                 Console.WriteLine("BASS initialization failed.");
  262.             }
  263.  
  264.             // enable cursor on quit
  265.             Console.CursorVisible = true;
  266.         }
  267.  
  268.         // execution ends here
  269.  
  270. #region "Functions"
  271.  
  272.         // BASS Setup
  273.         static void SetupBassLibrary()
  274.         {
  275.             // we must register BASS.NET to be able to run in console, otherwise will crash on Unix without X Server
  276.             BassNet.Registration("<email>", "<regkey>");  // go to http:// bass.radio42.com and get a regkey for free
  277.  
  278.             // load BASS plugins from current directory
  279.             Bass.BASS_PluginLoadDirectory(Environment.CurrentDirectory);
  280.  
  281.             // set BASS configuration
  282.             Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_NET_READTIMEOUT, 60000);             // network read timeout 60000ms
  283.             Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_NET_BUFFER, 10000);                  // network buffer size 10000ms
  284.             Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_NET_PREBUF, 30);                     // prebuffer 30% of buffer size
  285.             Bass.BASS_SetConfig(BASSConfig.BASS_CONFIG_NET_PLAYLIST, true);                 // enable playlist processing
  286.         }
  287.  
  288.         static bool CheckFloatSupport()
  289.         {
  290.             // check FLOAT support                
  291.             int streamChk = Bass.BASS_StreamCreate(44100, 1, BASSFlag.BASS_SAMPLE_FLOAT, null, IntPtr.Zero);
  292.             if (streamChk == 0) { return false; } else { Bass.BASS_StreamFree(streamChk); return true; }
  293.         }
  294.  
  295.         // format Song Line
  296.         static string FormatSongLine(string artist, string title)
  297.         {
  298.             var a = !(string.IsNullOrWhiteSpace(artist));
  299.             var t = !(string.IsNullOrWhiteSpace(title));
  300.  
  301.             return (a ? (t ? artist + " - " + title : artist) : (t ? title : "NO INFORMATION"));
  302.         }
  303.        
  304.         // draw bar graph by chanLevel (0...32767)
  305.         static string GetLevelBar(int chanLevel)
  306.         {
  307.             int percent = (int)Math.Floor((float)(chanLevel) / (float)(32767) * (float)(100));
  308.             return GetPercentBar(percent);
  309.         }
  310.  
  311.         // draw bar graph by percentage (0...100)
  312.         static string GetPercentBar(int percent)
  313.         {
  314.             int maxBarLen = barBckg.Length;
  315.             int scaledLevel = (int)Math.Floor((float)(maxBarLen) / (float)(100) * (float)(percent));          
  316.             string display = new string('#', scaledLevel);
  317.             display = barBckg.ReplaceAt(0, display.Length, display);
  318.             return display;
  319.         }
  320.  
  321.         // get channel format name from number of channels
  322.         static string GetChannelFormatString(int channels)
  323.         {
  324.             string fStr;
  325.             switch (channels)
  326.             {
  327.                 case 0:
  328.                     fStr = "N/A";
  329.                     break;
  330.                 case 1:
  331.                     fStr = "MONO";
  332.                     break;
  333.                 case 2:
  334.                     fStr = "STEREO";
  335.                     break;
  336.                 case 4:
  337.                     fStr = "QUADRO";
  338.                     break;
  339.                 case 6:
  340.                     fStr = "5.1 SURROUND";
  341.                     break;
  342.                 case 8:
  343.                     fStr = "7.1 SURROUND";
  344.                     break;
  345.                 default:
  346.                     fStr = "MULTICHANNEL (" + channels + ")";
  347.                     break;
  348.             }
  349.             return fStr;
  350.         }
  351.    
  352.         // get stream type name from BASSChannelType
  353.         static string GetStreamFormatString(Un4seen.Bass.BASSChannelType ctype)
  354.         {
  355.             switch (ctype)
  356.             {
  357.                 case BASSChannelType.BASS_CTYPE_STREAM_AAC:
  358.                     return "AAC";
  359.                 case BASSChannelType.BASS_CTYPE_STREAM_MP3:
  360.                     return "MP3";
  361.                 case BASSChannelType.BASS_CTYPE_STREAM_OGG:
  362.                     return "OGG";
  363.                 default:
  364.                     return ctype.ToString();
  365.             }
  366.         }
  367.  
  368.         // SYNC procs (Not working in console ???)
  369.         public static void MetaSync(int syncHandle, int channel, int data, IntPtr user)
  370.         {
  371.             //_tags.UpdateFromMETA(Bass.BASS_ChannelGetTags(channel, BASSTag.BASS_TAG_META), false, false);
  372.         }
  373.         public static void StallSync(int syncHandle, int channel, int data, IntPtr user)
  374.         {
  375.             //if (data == 0) { System.Diagnostics.Debug.Print("STALL"); }
  376.         }
  377.     }
  378.  
  379. #endregion
  380.  
  381. #region "Extenders"
  382.  
  383.  
  384.     // some string extenders
  385.     static class Extend
  386.     {
  387.         public static string ReplaceAt(this string str, int index, int length, string replace)
  388.         {
  389.             return str.Remove(index, Math.Min(length, str.Length - index))
  390.                     .Insert(index, replace);
  391.         }
  392.  
  393.         public static string Left(this string s, int left)
  394.         {
  395.             if (left > s.Length) { left = s.Length; }
  396.             return s.Substring(0, left);
  397.         }
  398.     }
  399.  
  400. #endregion
  401. }
Add Comment
Please, Sign In to add comment