Read Song Title/Artist from a live audio stream with Silverlight 4? - c#

I have a SL4 project that is successfully streaming a great sounding WMA audio stream from a remote location. All of the MediaElement actions are straight forward.
What I want to do is read the attributes that are passed as text along with the Audio stream. For instance the encoder of the stream embeds the title of the stream, the title of the song playing and the name of the artist for the current song.
How would I pick this out using Silverlight 4 and then display it in a Label to the user?
It sure would be easier than writing a bunch of web services to do the same thing. Windows Media Player and WinAmp all get the information I am just not seeing it in the MediaElement object collection.

I found the answer after searchting the web as well as fiddling with Expression 3 a little as well.
It turns out that a live audio stream has markers that are sent across as well as the audio. Markers can contain almost anything but one is called a "Caption". The caption is basically a free-form string field that you can read. With my stream the encoder sends a lot of information across as a caption that can then be broken down. So here is the code I am using:
Starts with registering a few events, the last one is the important one.
public MainPage()
{
InitializeComponent();
this.mediaElement1.BufferingProgressChanged += new RoutedEventHandler(mediaElement1_BufferingProgressChanged);
this.mediaElement1.MarkerReached += new TimelineMarkerRoutedEventHandler(mediaElement1_MarkerReached);
}
Then the actual marker handler does the following:
private void mediaElement1_MarkerReached(object sender, TimelineMarkerRoutedEventArgs e)
{
Dictionary<string, string> songAttribs = new Dictionary<string, string>();
string playerFeed = HttpUtility.UrlDecode(e.Marker.Text);
char[] delims = { '&' };
string[] Attribs = playerFeed.Split(delims);
foreach (String attrib in Attribs)
{
string[] keypair = attrib.Split('=');
string key = "";
string value = "";
try
{
key = keypair[0];
}
catch
{
key = null;
}
if (key != null)
{
try
{
value = keypair[1];
}
catch
{
value = "";
}
songAttribs.Add(keypair[0], keypair[1]);
}
}
nowplaying.Title = songAttribs["title"];
nowplaying.Artist = songAttribs["artist"];
nowplaying.Duration = 0;
this.label2.Content = "Artist: " + nowplaying.Artist;
this.label3.Content = "Title: " + nowplaying.Title;
this.label1.Content = playerFeed;
}
Still working on some of the code but so far things seem to be working.

Mayba WMP and WinAmp get the Informations from a Website or something like this, and dont read it out of the stream...
Werewolve

Related

C# - Saving and Loading data to file

I decided to get into coding and am learning c#, after making a few small projects, I decided to step it up a little and make a text adventure game, with saving and loading, and if I get to feeling zany I'll try to add some multiplayer. While I haven't really hit a road block because of it, I can't help but feel that I am doing load function REALLY sub-optimally. The save is fine, I feel like it works for me, but the load I feel can be really simplified, I just don't know what to use.
I also wouldn't really mind, but with this way, if I add other attributes/skills or whatever else that needs to be saved, I will have to add everything to the load function as well, and it will be even longer.
I have tried to search around on here, the c# documentation, and other sites, but can't find a solution that works for this case. can anyone help me find a better way of doing this? Or is this the best I can really do since it's varying data types?
Edit: To simplify and clarify what answer I am searching for, I am trying to find a simpler and more scalable way to save and load the data to a file.
static void LoadGame(CharData PlayerData)
{
Console.WriteLine("Enter the name of the character to load as shown below.");
//getting current directory info, setting to di
DirectoryInfo di = new DirectoryInfo(Directory.GetCurrentDirectory());
//need to initialize these outside of a loop
int SaveFiles = 0;
string DisplayName = " ";
int DisplayNameLength = 0;
//looks through files in working directory ending in '.fasv', displays them in format '{x}. John Smith'
foreach (var fi in di.GetFiles("*.fasv"))
{
SaveFiles++;
DisplayNameLength = fi.Name.Length;
//remove .fasv from displayed name to make it look nicer
DisplayName = fi.Name.Remove(DisplayNameLength - 5, 5);
Console.WriteLine(SaveFiles.ToString() + ". " + DisplayName);
}
string toLoad = Console.ReadLine();
using StreamReader sr = new StreamReader(toLoad + ".fasv");
//the name is easy to get since it's a string. but integers...
PlayerData.Name = sr.ReadLine();
//... not so much. i hate all of this and i feel like it's gross, but i don't know how else to do it
int hp, xp, level, toughness, innovation, mind, empathy, spryness;
Int32.TryParse(sr.ReadLine(), out hp);
Int32.TryParse(sr.ReadLine(), out xp);
Int32.TryParse(sr.ReadLine(), out level);
Int32.TryParse(sr.ReadLine(), out toughness);
Int32.TryParse(sr.ReadLine(), out innovation);
Int32.TryParse(sr.ReadLine(), out mind);
Int32.TryParse(sr.ReadLine(), out empathy);
Int32.TryParse(sr.ReadLine(), out spryness);
PlayerData.Health = hp;
PlayerData.Level = level;
PlayerData.XP = xp;
PlayerData.Toughness = toughness;
PlayerData.Innovation = innovation;
PlayerData.Mind = mind;
PlayerData.Empathy = empathy;
PlayerData.Spryness = spryness;
sr.Close();
InGame(PlayerData);
}
static void SaveGame(CharData PlayerData)
{
using (StreamWriter sw = new StreamWriter(PlayerData.Name + ".fasv"))
{
foreach (System.Reflection.PropertyInfo stat in PlayerData.GetType().GetProperties())
{
//write player data properties to file line by line, using stat to iterate through the player data properties
sw.WriteLine(stat.GetValue(PlayerData));
}
sw.Close();
}
}
If you aren't set on a particular data format for the file data, I would recommend using a serializer such as JSON.NET. You can use NuGet to add newtonsoft.json to your project, and that would allow you to just do something similar to:
using (StreamWriter file = File.CreateText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
serializer.Serialize(file, playerData);
}
And then your code to read from the file would be pretty similar:
using (var file = File.OpenText(pathToPlayerFile))
{
var serializer = new JsonSerializer();
return (CharData)serializer.Deserialize(file, typeof(CharData));
}
I borrowed those code snippets from newtonsoft.com. CreateText will create (or overwrite) the file and write the object as a JSON object.

Play audio at two different Audiodevices simultaneously with Naudio

My question is similar to one here :
Play audio to two different Audiodevices simultaneously with Naudio
But i ask it here again since it has not been answered clearly in the link above.
I also have it at:
Play sound in both speaker and headset wpf
Inspiration from :
Play a sound in a specific device with C#
I am adding source code and adding the NAudio tag here as well.
I have an wpf application and i am using the soundPlayer class to play sound (for eg ringtone). Currently the tone plays either on speakers or on the headset (if its plugged in). I would like the application to play the tone on speaker even when the headsets are plugged in. I know there are ways to do this in android, but couldn't find any in wpf. I would also like an UI for the user to choose the devices in which he would like to hear sound. Any help is appreciated. Thanks !
public void detectDevices()
{
int waveOutDevices = WaveOut.DeviceCount;
switch (waveOutDevices)
{
case 1:
var wave1 = new WaveOut();
wave1.DeviceNumber = 0;
playSound(0);
break;
case 2:
var wave2 = new WaveOut();
wave2.DeviceNumber = 0;
playSound(0);
var wave3 = new WaveOut();
wave3.DeviceNumber = 1;
playSound(1);
break;
}
}
public void playSound(int deviceNumber)
{
disposeWave();// stop previous sounds before starting
waveReader = new NAudio.Wave.WaveFileReader(fileName);
var waveOut = new NAudio.Wave.WaveOut();
waveOut.DeviceNumber = deviceNumber;
output = waveOut;
output.Init(waveReader);
output.Play();
}
public void disposeWave()
{
if (output != null)
{
if (output.PlaybackState == NAudio.Wave.PlaybackState.Playing)
{
output.Stop();
output.Dispose();
output = null;
}
}
if (wave != null)
{
wave.Dispose();
wave = null;
}
}
case eSelector.startIncomingRinging:
fileName = ("Ring.wav");
detectDevices();
I still hear ringtone just in one device (either in headset or speakers) using the code above.
You need two instances of WaveOut, one for each soundcard. And then the simplest way if you are playing from file, is to also have two instances of WaveFileReader. You can't easily synchronize them I'm afraid, you'll just have to start them both playing together and hope for the best.
The wave1, wave2 and wave3 classes in your code above do abosolutely nothing. The audio will be played with the WaveOut device you create in playSound. You seem to have a single class property called output and another called waveReader, when you need two of each.

How to control the youtube flash player with c#?

My goal is to make a open source YouTube player that can be controlled via global media keys.
The global key issue I got it covered but the communication between the YouTube player and my Windows Forms application just doesn't work for some reason.
So far this is what I have:
private AxShockwaveFlashObjects.AxShockwaveFlash player;
player.movie = "http://youtube.googleapis.com/v/9bZkp7q19f0"
...
private void playBtn_Click(object sender, EventArgs e)
{
player.CallFunction("<invoke name=\"playVideo\" returntype=\"xml\"></invoke>");
}
Unfortunately this returns:
"Error HRESULT E_FAIL has been returned from a call to a COM component."
What am I missing? Should I load a different URL?
The documentation states that YouTube player uses ExternalInterface class to control it from JavaScript or AS3 so it should work with c#.
UPDATED:
Method used to embed the player: http://www.youtube.com/watch?v=kg-z8JfOIKw
Also tried to use the JavaScript-API in the WebBrowser control but no luck (player just didn't respond to JavaScript commands, tried even to set WebBrowser.url to a working demo, all that I succeeded is to get the onYouTubePlayerReady() to fire using the simple embedded object version )
I think there might be some security issues that I'm overseeing, don't know.
UPDATE 2:
fond solution, see my answer below.
It sounds like your trying to use Adobe Flash as your interface; then pass certain variables back into C#.
An example would be this:
In Flash; create a button... Actionscript:
on (press) {
fscommand("Yo","dude");
}
Then Visual Studio you just need to add the COM object reference: Shockwave Flash Object
Then set the embed to true;
Then inside Visual Studio you should be able to go to Properties; find fscommand. The fscommand will allow you to physically connect the value from the Flash movie.
AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEvent
That collects; then just use e.command and e.arg for example to have the collected item do something.
Then add this to the EventHandler;
lbl_Result.Text="The "+e.args.ToString()+" "+e.command.ToString()+" was clicked";
And boom it's transmitting it's data from Flash into Visual Studio. No need for any crazy difficult sockets.
On a side note; if you have Flash inside Visual Studio the key is to ensure it's "embed is set to true." That will hold all the path references within the Flash Object; to avoid any miscalling to incorrect paths.
I'm not sure if that is the answer your seeking; or answers your question. But without more details on your goal / error. I can't assist you.
Hope this helps. The first portion should actually show you the best way to embed your Shockwave into Visual Studio.
Make sure you add the correct reference:
Inside your project open 'Solution Explorer'
Right-Click to 'Add Reference'
Go to 'COM Object'
Find Proper object;
COM Objects:
Shockwave ActiveX
Flash Accessibility
Flash Broker
Shockwave Flash
Hope that helps.
It sounds like you aren't embedding it correctly; so you can make the call to it. If I'm slightly mistaken; or is this what you meant:
If your having difficulty Ryk had a post awhile back; with a method to embed YouTube videos:
<% MyYoutubeUtils.ShowEmebddedVideo("<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/gtNlQodFMi8&hl=en&fs=1"></param><param name="allowFullScreen" value="true"></param><embed src="http://www.youtube.com/v/gtNlQodFMi8&hl=en&fs=1" type="application/x-shockwave-flash" allowfullscreen="true" width="425" height="344"></embed></object>") %>
Or...
public static string ShowEmbeddedVideo(string youtubeObject)
{
var xdoc = XDocument.Parse(youtubeObject);
var returnObject = string.Format("<object type=\"{0}\" data=\{1}\"><param name=\"movie\" value=\"{1}\" />",
xdoc.Root.Element("embed").Attribute("type").Value,
xdoc.Root.Element("embed").Attribute("src").Value);
return returnObject;
}
Which you can find the thread here: https://stackoverflow.com/questions/2547101/purify-embedding-youtube-videos-method-in-c-sharp
I do apologize if my post appears fragmented; but I couldn't tell if it was the reference, the variable, the method, or embed that was causing you difficulties. Truly hope this helps; or give me more details and I'll tweak my response accordingly.
C# to ActionScript Communication:
import flash.external.ExternalInterface;
ExternalInterface.addCallback("loadAndPlayVideo", null, loadAndPlayVideo);
function loadAndPlayVideo(uri:String):void
{
videoPlayer.contentPath = uri;
}
Then in C#; add an instance of the ActiveX control and add the content into a Constructor.
private AxShockwaveFlash flashPlayer;
public FLVPlayer ()
{
// Add Error Handling; to condense I left out.
flashPlayer.LoadMovie(0, Application.StartupPath + "\\player.swf");
}
fileDialog = new OpenFileDialog();
fileDialog.Filter = "*.flv|*.flv";
fileDialog.Title = "Select a Flash Video File...";
fileDialog.Multiselect = false;
fileDialog.RestoreDirectory = true;
if (fileDialog.ShowDialog() == DialogResult.OK)
{
flashPlayer.CallFunction("<invoke" + " name=\"loadAndPlayVideo\" returntype=\"xml"> <arguements><string>" + fileDialog.FileName + "</string></arguements></invoke>");
}
ActionScript Communication to C#:
import flash.external.ExternalInterface;
ExternalInterface.call("ResizePlayer", videoPlayer.metadata.width, videoPlayer.metadata.height);
flashPlayer.FlashCall += new _IShockwaveFlashEvents_FlashCallEventHandler(flashPlayer_FlashCall);
Then the XML should appear:
<invoke name="ResizePlayer" returntype="xml">
<arguements>
<number> 320 </number>
<number> 240 </number>
</arguments>
</invoke>
Then parse the XML in the event handler and invoke the C# function locally.
XmlDocument document = new XmlDocument();
document.LoadXML(e.request);
XmlNodeList list = document.GetElementsByTagName("arguements");
ResizePlayer(Convert.ToInt32(list[0].FirstChild.InnerText), Convert.ToInt32(list[0].ChildNodes[1].InnerText));
Now they are both passing data back and forth. That is a basic example; but by utilizing the ActionScript Communication you shouldn't have any issues utilizing the native API.
Hope that is more helpful. You can expand on that idea by a utility class for reuse. Obviously the above code has some limitations; but hopefully it points you in the right direction. Was that direction you were attempting to go? Or did I still miss the point?
Create a new Flash Movie; in ActionScript 3. Then on the initial first frame; apply the below:
Security.allowDomain("www.youtube.com");
var my_player:Object;
var my_loader:Loader = new Loader();
my_loader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3"))
my_loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);
function onLoaderInit(e:Event):void{
addChild(my_loader);
my_player = my_loader.content;
my_player.addEventListener("onReady", onPlayerReady);
}
function onPlayerReady(e:Event):void{
my_player.setSize(640,360);
my_player.loadVideoById("_OBlgSz8sSM",0);
}
So what exactly is that script doing? It is utilizing the native API and using ActionScript Communication. So below I'll break down each line.
Security.allowDomain("www.youtube.com");
Without that line YouTube won't interact with the object.
var my_player:Object;
You can't just load a movie into the movie; so we will create a variable Object. You have to load a special .swf that will contain access to those codes. The below; does just that. So you can access the API.
var my_loader:Loader = new Loader();
my_loader.load(new URLRequest("http://www.youtube.com/apiplayer?version=3"));
We now reference the Google API per their documentation.
my_loader.contentLoaderInfo.addEventListener(Event.INIT, onLoaderInit);
But in order to actually work with our object; we need to wait for it to be fully initialized. So the Event Listener will wait; so we know when we can pass commands to it.
The onLoaderInit function will be triggered upon initialization. Then it's first task will be my_loader to display the list so that the video appears.
The addChild(my_loader); is what will load one; the my_player = my_loader.content; will store a reference for easy access to the object.
Though it has been initialized; you have to wait even further... You use my_player.addEventListener("onReady", onPlayerReady); to wait and listen for those custom events. Which will allow a later function to handle.
Now the player is ready for basic configuration;
function onPlayerReady(e:Event):void{
my_player.setSize(640,360);
}
The above function starts very basic manipulation. Then the last line my_player.loadVideoById("_OBlgSz8sSM",0); is referencing the particular video.
Then on your stage; you could create two buttons and apply:
play_btn.addEventListener(MouseEvent.CLICK, playVid);
function playVid(e:MouseEvent):void {
my_player.playVideo();
}
pause_btn.addEventListener(MouseEvent.CLICK, pauseVid);
function pauseVid(e:MouseEvent):void {
my_player.pauseVideo();
}
Which would give you a play and pause functionality. Some additional items you could use our:
loadVideoById()
cueVideoById()
playVideo()
pauseVideo()
stopVideo()
mute()
unMute()
Keep in mind those can't be used or called until it has been fully initialized. But using that; with the earlier method should allow you to layout the goal and actually pass variables between the two for manipulation.
Hopefully that helps.
I'd start by making sure that javascript can talk to your flash app.
make sure you have: allowScriptAccess="sameDomain" set in the embed (from http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/external/ExternalInterface.html#includeExamplesSummary).
you should validate that html->flash works; then C->html; and gradually work up to C->you-tube-component. you have a lot of potential points of failure between C and the you-tube-component right now and it's hard to address all of them at the same time.
After a lot of tries and head-hammering, I've found a solution:
Seems that the Error HRESULT E_FAIL... happens when the flash dosen't understand the requested flash call. Also for the youtube external api to work, the js api needs to be enabled:
player.movie = "http://www.youtube.com/v/VIDEO_ID?version=3&enablejsapi=1"
As I said in the question the whole program is open source, so you will find the full code at bitbucket. Any advice, suggestions or collaborators are highly appreciated.
The complete solution:
Here is the complete guide for embedding and interacting with the YouTube player or any other flash object.
After following the video tutorial
, set the flash player's FlashCall event to the function that will handle the flash->c# interaction (in my example it's YTplayer_FlashCall )
the generated `InitializeComponent()` should be:
...
this.YTplayer = new AxShockwaveFlashObjects.AxShockwaveFlash();
this.YTplayer.Name = "YTplayer";
this.YTplayer.Enabled = true;
this.YTplayer.OcxState = ((System.Windows.Forms.AxHost.State)(resources.GetObject("YTplayer.OcxState")));
this.YTplayer.FlashCall += new AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEventHandler(this.YTplayer_FlashCall);
...
the FlashCall event handler
private void YTplayer_FlashCall(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEvent e)
{
Console.Write("YTplayer_FlashCall: raw: "+e.request.ToString()+"\r\n");
// message is in xml format so we need to parse it
XmlDocument document = new XmlDocument();
document.LoadXml(e.request);
// get attributes to see which command flash is trying to call
XmlAttributeCollection attributes = document.FirstChild.Attributes;
String command = attributes.Item(0).InnerText;
// get parameters
XmlNodeList list = document.GetElementsByTagName("arguments");
List<string> listS = new List<string>();
foreach (XmlNode l in list){
listS.Add(l.InnerText);
}
Console.Write("YTplayer_FlashCall: \"" + command.ToString() + "(" + string.Join(",", listS) + ")\r\n");
// Interpret command
switch (command)
{
case "onYouTubePlayerReady": YTready(listS[0]); break;
case "YTStateChange": YTStateChange(listS[0]); break;
case "YTError": YTStateError(listS[0]); break;
default: Console.Write("YTplayer_FlashCall: (unknownCommand)\r\n"); break;
}
}
this will resolve the flash->c# communication
calling the flash external functions (c#->flash):
private string YTplayer_CallFlash(string ytFunction){
string flashXMLrequest = "";
string response="";
string flashFunction="";
List<string> flashFunctionArgs = new List<string>();
Regex func2xml = new Regex(#"([a-z][a-z0-9]*)(\(([^)]*)\))?", RegexOptions.Compiled | RegexOptions.IgnoreCase);
Match fmatch = func2xml.Match(ytFunction);
if(fmatch.Captures.Count != 1){
Console.Write("bad function request string");
return "";
}
flashFunction=fmatch.Groups[1].Value.ToString();
flashXMLrequest = "<invoke name=\"" + flashFunction + "\" returntype=\"xml\">";
if (fmatch.Groups[3].Value.Length > 0)
{
flashFunctionArgs = pars*emphasized text*eDelimitedString(fmatch.Groups[3].Value);
if (flashFunctionArgs.Count > 0)
{
flashXMLrequest += "<arguments><string>";
flashXMLrequest += string.Join("</string><string>", flashFunctionArgs);
flashXMLrequest += "</string></arguments>";
}
}
flashXMLrequest += "</invoke>";
try
{
Console.Write("YTplayer_CallFlash: \"" + flashXMLrequest + "\"\r\n");
response = YTplayer.CallFunction(flashXMLrequest);
Console.Write("YTplayer_CallFlash_response: \"" + response + "\"\r\n");
}
catch
{
Console.Write("YTplayer_CallFlash: error \"" + flashXMLrequest + "\"\r\n");
}
return response;
}
private static List<string> parseDelimitedString (string arguments, char delim = ',')
{
bool inQuotes = false;
bool inNonQuotes = false;
int whiteSpaceCount = 0;
List<string> strings = new List<string>();
StringBuilder sb = new StringBuilder();
foreach (char c in arguments)
{
if (c == '\'' || c == '"')
{
if (!inQuotes)
inQuotes = true;
else
inQuotes = false;
whiteSpaceCount = 0;
}else if (c == delim)
{
if (!inQuotes)
{
if (whiteSpaceCount > 0 && inQuotes)
{
sb.Remove(sb.Length - whiteSpaceCount, whiteSpaceCount);
inNonQuotes = false;
}
strings.Add(sb.Replace("'", string.Empty).Replace("\"", string.Empty).ToString());
sb.Remove(0, sb.Length);
}
else
{
sb.Append(c);
}
whiteSpaceCount = 0;
}
else if (char.IsWhiteSpace(c))
{
if (inNonQuotes || inQuotes)
{
sb.Append(c);
whiteSpaceCount++;
}
}
else
{
if (!inQuotes) inNonQuotes = true;
sb.Append(c);
whiteSpaceCount = 0;
}
}
strings.Add(sb.Replace("'", string.Empty).Replace("\"", string.Empty).ToString());
return strings;
}
adding Youtube event handlers:
private void YTready(string playerID)
{
YTState = true;
//start eventHandlers
YTplayer_CallFlash("addEventListener(\"onStateChange\",\"YTStateChange\")");
YTplayer_CallFlash("addEventListener(\"onError\",\"YTError\")");
}
private void YTStateChange(string YTplayState)
{
switch (int.Parse(YTplayState))
{
case -1: playState = false; break; //not started yet
case 1: playState = true; break; //playing
case 2: playState = false; break; //paused
//case 3: ; break; //buffering
case 0: playState = false; if (!loopFile) mediaNext(); else YTplayer_CallFlash("seekTo(0)"); break; //ended
}
}
private void YTStateError(string error)
{
Console.Write("YTplayer_error: "+error+"\r\n");
}
usage ex:
YTplayer_CallFlash("playVideo()");
YTplayer_CallFlash("pauseVideo()");
YTplayer_CallFlash("loadVideoById(KuNQgln6TL0)");
string currentVideoId = YTplayer_CallFlash("getPlaylist()");
string currentDuration = YTplayer_CallFlash("getDuration()");
The functions YTplayer_CallFlash, YTplayer_FlashCall should work for any flash-C# communication with minor adjustments like the YTplayer_CallFlash's switch (command).
This stumped me for a number of hours.
Just add enable JS to your URL:
http://www.youtube.com/v/9bZkp7q19f0?version=3&enablejsapi=1
CallFunction works fine for me now! Also remove unrequired space in the call.

WMPLib: player.mediaCollection.getAll().count is always 0

I am attempting to write code that reads each item from the user's Windows Media Player library. This code works for the majority of users, but for some users, getAll() will return an empty list when they clearly have hundreds or thousands of items in their Windows Media Player library.
var player = new WindowsMediaPlayer();
var collection = player.mediaCollection;
var list = collection.getAll();
int total = list.count;
I am referencing the WMPLib namespace by adding a COM reference to wmp.dll. My application ships with Interop.WMPLib.dll. How would some users' machines be configured in such a way that they run Windows Media Player with many songs in their library, but WMPLib fails to function correctly? Furthermore, what workarounds exist to reliably read the user's Windows Media Player library in all cases?
Try this snippet and see if it works for you.
public List<MusicEntry> GetMusicLibrary()
{
List<MusicEntry> entries;
IWMPPlaylist mediaList = null;
IWMPMedia mediaItem;
try
{
// get the full audio media list
mediaList = media.getByAttribute("MediaType", "Audio");
entries = new List<MusicEntry>(mediaList.count);
for (int i = 0; i < mediaList.count; i++)
{
mediaItem = mediaList.get_Item(i);
// create the new entry and populate its properties
entry = new MusicEntry()
{
Title = GetTitle(mediaItem),
Album = GetAlbum(mediaItem),
Artist = GetArtist(mediaItem),
TrackNumber = GetTrackNumber(mediaItem),
Rating = GetRating(mediaItem),
FileType = GetFileType(mediaItem)
};
entries.Add(entry);
}
}
finally
{
// make sure we clean up as this is COM
if (mediaList != null)
{
mediaList.clear();
}
}
return entries;
}
For more information refer to this excellent article on Code Project.
http://www.codeproject.com/Articles/36338/Export-Windows-Media-Player-Music-Metadata-to-XML

How can I use google text to speech api in windows form?

I want to use google text to speech in my windows form application, it will read a label. I added System.Speech reference. How can it read a label with a button click event?
http://translate.google.com/translate_tts?q=testing+google+speech This is the google text to speech api, or how can I use microsoft's native text to speech?
UPDATE Google's TTS API is no longer publically available. The notes at the bottom about Microsoft's TTS are still relevant and provide equivalent functionality.
You can use Google's TTS API from your WinForm application by playing the response using a variation of this question's answer (it took me a while but I have a real solution):
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.FormClosing += (sender, e) =>
{
if (waiting)
stop.Set();
};
}
private void ButtonClick(object sender, EventArgs e)
{
var clicked = sender as Button;
var relatedLabel = this.Controls.Find(clicked.Tag.ToString(), true).FirstOrDefault() as Label;
if (relatedLabel == null)
return;
var playThread = new Thread(() => PlayMp3FromUrl("http://translate.google.com/translate_tts?q=" + HttpUtility.UrlEncode(relatedLabel.Text)));
playThread.IsBackground = true;
playThread.Start();
}
bool waiting = false;
AutoResetEvent stop = new AutoResetEvent(false);
public void PlayMp3FromUrl(string url)
{
using (Stream ms = new MemoryStream())
{
using (Stream stream = WebRequest.Create(url)
.GetResponse().GetResponseStream())
{
byte[] buffer = new byte[32768];
int read;
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
}
ms.Position = 0;
using (WaveStream blockAlignedStream =
new BlockAlignReductionStream(
WaveFormatConversionStream.CreatePcmStream(
new Mp3FileReader(ms))))
{
using (WaveOut waveOut = new WaveOut(WaveCallbackInfo.FunctionCallback()))
{
waveOut.Init(blockAlignedStream);
waveOut.PlaybackStopped += (sender, e) =>
{
waveOut.Stop();
};
waveOut.Play();
waiting = true;
stop.WaitOne(10000);
waiting = false;
}
}
}
}
}
NOTE: The above code requires NAudio to work (free/open source) and using statements for System.Web, System.Threading, and NAudio.Wave.
My Form1 has 2 controls on it:
A Label named label1
A Button named button1 with a Tag of label1 (used to bind the button to its label)
The above code can be simplified slightly if a you have different events for each button/label combination using something like (untested):
private void ButtonClick(object sender, EventArgs e)
{
var clicked = sender as Button;
var playThread = new Thread(() => PlayMp3FromUrl("http://translate.google.com/translate_tts?q=" + HttpUtility.UrlEncode(label1.Text)));
playThread.IsBackground = true;
playThread.Start();
}
There are problems with this solution though (this list is probably not complete; I'm sure comments and real world usage will find others):
Notice the stop.WaitOne(10000); in the first code snippet. The 10000 represents a maximum of 10 seconds of audio to be played so it will need to be tweaked if your label takes longer than that to read. This is necessary because the current version of NAudio (v1.5.4.0) seems to have a problem determining when the stream is done playing. It may be fixed in a later version or perhaps there is a workaround that I didn't take the time to find. One temporary workaround is to use a ParameterizedThreadStart that would take the timeout as a parameter to the thread. This would allow variable timeouts but would not technically fix the problem.
More importantly, the Google TTS API is unofficial (meaning not to be consumed by non-Google applications) it is subject to change without notification at any time. If you need something that will work in a commercial environment I'd suggest either the MS TTS solution (as your question suggests) or one of the many commercial alternatives. None of which tend to be even this simple though.
To answer the other side of your question:
The System.Speech.Synthesis.SpeechSynthesizer class is much easier to use and you can count on it being available reliably (where with the Google API, it could be gone tomorrow).
It is really as easy as including a reference to the System.Speech reference and:
public void SaySomething(string somethingToSay)
{
var synth = new System.Speech.Synthesis.SpeechSynthesizer();
synth.SpeakAsync(somethingToSay);
}
This just works.
Trying to use the Google TTS API was a fun experiment but I'd be hard pressed to suggest it for production use, and if you don't want to pay for a commercial alternative, Microsoft's solution is about as good as it gets.
I know this question is a bit out of date but recently Google published Google Cloud Text To Speech API.
.NET Client version of Google.Cloud.TextToSpeech can be found here:
https://github.com/jhabjan/Google.Cloud.TextToSpeech.V1
Here is short example how to use the client:
GoogleCredential credentials =
GoogleCredential.FromFile(Path.Combine(Program.AppPath, "jhabjan-test-47a56894d458.json"));
TextToSpeechClient client = TextToSpeechClient.Create(credentials);
SynthesizeSpeechResponse response = client.SynthesizeSpeech(
new SynthesisInput()
{
Text = "Google Cloud Text-to-Speech enables developers to synthesize natural-sounding speech with 32 voices"
},
new VoiceSelectionParams()
{
LanguageCode = "en-US",
Name = "en-US-Wavenet-C"
},
new AudioConfig()
{
AudioEncoding = AudioEncoding.Mp3
}
);
string speechFile = Path.Combine(Directory.GetCurrentDirectory(), "sample.mp3");
File.WriteAllBytes(speechFile, response.AudioContent);

Categories