Friday, January 1, 2010

iTunes & Music code Redux

It has been brought to my attention on my first iTunes COM API post that some of my information in that post is currently invalid:
  1. Lyrics: LyricWiki decided to cut off their API because of copywright concerns, so that function will no longer work. Link
  2. Album Art: Amazon has implemented authentication in their album art API, if you would still like to use this service, I recommend this post


Wednesday, August 26, 2009

My Segway Works!

Its not 100% yet, but I got my Segway ride-able. Here are some videos:

Riding my Segway! from Kerry Snyder on Vimeo.


Riding My Segway - With Steering from Kerry Snyder on Vimeo.



I should be able to post more details and code soon.

Saturday, August 8, 2009

Segway Video and TI Pong

Yes, its been over a month, but I have made quite a it of progress on my Segway, and, without further adieu:
Item #1:

Initial Segway Test from Kerry Snyder on Vimeo.


It balances itself! Now all I need to do is adjust the PID constants. And put in a big red E-Stop switch.


Item #2: TI calculator Pong

I made this while I was bored in math class last year, all by myself, programmed in a BASIC-like language on our school's graphing calculators, which I believe are TI-83+'s.

Here is the calculator manual, with programming information: Chapter 16

And here is the code:

Lbl F
ClrHome
Menu("PLAY PONG","PLAY",Z,"HIGH SCORE",K,"QUIT",S)
Pause
Lbl Z
1»B
0»G
1»O
1»P
0»J
Lbl U
getKey»A
If A=34
Then
B+1»B
Then
8»B
End
ClrHome
Output(B,1,"[")
End
If A=25
Then
B-1»B
If B<1
Then
1»B
End ClrHome
Output(B,1,"[")
End
If G=0
Then
randInt(3,6)»X
randInt(6,10)»Y
Output(X,Y,"+")
G+1»G
Else
Output(X,Y,"+")
If P=1
Then
X+1»X
Else
X-1»X
End
If O=1
Then
Y+1»Y
Else
Y-1»Y
End
If X>8
Then
8»X
0»P
End
If X,1
Then
1»X
1»P
End
If Y>16
Then
16»Y
0»O
End
If Y<2
Then
If X=B or B-1=X or B+1=X
Then
2»Y
1»O
J+1=J
Else
ClrHome
Disp "GAME OVER"
Pause
Output(5,4,"SCORE:")
Output(5,11,J)
Pause
If J>E
Then
J»E
ClrHome
Output(5,2,"NEW HIGH SCORE")
Pause
End
Pause
Menu("GAME OVER","PLAY AGAIN",H,"QUIT",S)
End
End
ClrHome
Output(B,1,"[")
Output(X,Y,"+")
End
Goto U
Lbl H
1»B
0»G
1»O
1»P
0»J
Goto F
Lbl S
0»J
ClrHome
Stop
Lbl K
Output(5,2,"HIGH SCORE:")
Output(5,14,E)
Pause
Goto F




Saturday, June 27, 2009

iTunes COM API C#: Playlists

After a comment on my original iTunes COM API post inquiring about Playlists, I decided to revisit that part of the project. I had originally intended to include this in the first post, but it was more difficult than I had first though. Here are some code snippets to get you started:

1. Create a ComboBox listing the names of all the playlists:

foreach (IITPlaylist pl in app.LibrarySource.Playlists)
            {
                comboBox1.Items.Add(pl.Name);
            }
Notes:
Retrieves all playlists in iTunes

2. Playing One:
private void button1_Click(object sender, EventArgs e)
        {
            IITPlaylist pl = app.LibrarySource.Playlists[comboBox1.SelectedIndex + 1];
            pl.PlayFirstTrack();
        }
Notes:
Could also be done on ComboBox1.SelectedIndexChanged()
+1 Accounts for differences in indexes


This is just some basic examples, for tons more information, check out this link. You have to login in with your iTunes account, but look for "iTunes COM for Windows SDK (ZIP)" If you have trouble locating this file, email me and I can get it to you. It includes iTunesCOM.chm, which is the de-facto resource for all of this information. Between my example code (Here and here), you should be able to preform many of the iTunes functions. As always, comment if you have any troubles.


Tuesday, June 16, 2009

Getting an Angle

So probably the biggest part of constructing a balancing robot, or scooter, is knowing what angle it is in relation to the ground. From this, you can determine how much you need to compensate. The balancing system in my Segway consists of two sensors:

ADXRS614 Gyro 50 °/s

ADXL203CE Accelerometer +/- 1.5g

As I have explained in a previous post, both of these sensors are required to determine an accurate angle measurement. In my code so far, these are combined using a Complementary Filter. A great resource for coding a complementary filter on the Arduino can be found here. The hardest part that I found using this code is the Coefficients, which are not explained. I have determined, however, how to calculate these and get a working angle output. The 2 Coefficients in the program are 28.783 for the Gyro and 10.78 for the Accelerometer.

To calculate this for the Gyro:
1. Get the mv/°/s value for your Gyro from the Datasheet
-----25
2. Divide by 1000 (Milli volts in a volt)
-----0.025
3. Divide by 5 (Voltage of the Analog Reference)
-----.005
4. Divide by .0174532925 (Radians in a Degree)
-----0.2864789
5. Take the Inverse of that (1/x)
-----3.49
And there is your answer, 3.49. You would replace the 28.783 in the formula for this.

To calculate this for the Accelerometer:
1. Get the mv/g value for your Accelerometer from the Datasheet
-----1000
2. Divide by 1000 (Milli volts in a volt)
-----1
3. Divide by 5 (Voltage of the Analog Reference)
-----0.2
4. Take the Inverse
-----5
I'm not so sure on this one, but it seems to work. You would replace the 10.78 with this.

You can also replace the Voltage, in this case 5v, or the 1024 in the Formula, depending on your supply voltage or Analog Port Resolution.

When I put this all together in my test code, I get a solid, seemingly accurate angle measurement.

Next up - PID!

Thursday, April 2, 2009

iTunes COM API in C#

I decided that I would post a little guide to some of the things that I have learned in using the iTunes COM API in C#. It's not very well documented, and I though I could help.

Initialization:
First, you have to add a Reference and Using
Right click on References, and click "Add Refrence"
Switch to the COM tab and find "iTunes *.** Type Library, and click add.
At the top of your code, add "Using iTunesLib"
Finally, add these two lines of code under the class of the form, outside of any function:

delegate void Router(object arg);
iTunesApp app = new iTunesAppClass();


The Router helps in handling the iTunes Events, and "app" is what you will use to do anything with iTunes.

Events:
You'll need to attach at least one event for a program that controls iTunes, to see when something changes in iTunes, and here is some code for that:

app.OnPlayerPlayEvent += new _IiTunesEvents_OnPlayerPlayEventEventHandler(delegate(object o)
                {
                    this.Invoke(new Router(app_OnPlayerPlayEvent), o);
                });

This should be put under InitializeComponent(), and will call app_OnPlayerPlayEvent when it is activated.

Controls:
These are pretty simple, just call them to control iTunes:

app.Play();
app.Pause();
app.NextTrack();
app.PreviousTrack();
app.Stop():


There are a few others, but these are the big ones.

Track Information:
iTunes provides pretty much all of the information about a playing track, there is a lot more than I mention here, I reccomend experimentation.

IITTrack track = app.CurrentTrack;
Albumlbl.Text = track.Album;
Artistlbl.Text = track.Artist;
Tracklbl.Text = track.Name;

These will populate the labels with the track (at the time) that is playing or paused. It must be refreshed to update the information.

IITArtworkCollection Art1 = track.Artwork;
IITArtwork Art2 = Art1[1];
Art2.SaveArtworkToFile("c:\\temp\\Album.jpg");
Stream r = File.Open("c:\\temp\\Album.jpg", FileMode.Open);
Image temp = Image.FromStream(r);
r.Close();
pictureBox1.Image = temp;
SetImage(pictureBox1);


This is a big one, it will allow you to put the Album Art of the currently playing track in a Picture Box. I recommend checking the Art1[] or doing this in a Try...Catch to avoid errors when a song does not have art. You will also need these, also, for resizing:

Original Link

public Size GenerateImageDimensions(int currW, int currH, int destW, int destH)
        {
            //double to hold the final multiplier to use when scaling the image
            double multiplier = 0;

            //string for holding layout
            string layout;

            //determine if it's Portrait or Landscape
            if (currH > currW) layout = "portrait";
            else layout = "landscape";

            switch (layout.ToLower())
            {
                case "portrait":
                    //calculate multiplier on heights
                    if (destH > destW)
                    {
                        multiplier = (double)destW / (double)currW;
                    }

                    else
                    {
                        multiplier = (double)destH / (double)currH;
                    }
                    break;
                case "landscape":
                    //calculate multiplier on widths
                    if (destH > destW)
                    {
                        multiplier = (double)destW / (double)currW;
                    }

                    else
                    {
                        multiplier = (double)destH / (double)currH;
                    }
                    break;
            }

            //return the new image dimensions
            return new Size((int)(currW * multiplier), (int)(currH * multiplier));
        }

        //Resize the image
        private void SetImage(PictureBox pb)
        {
            try
            {
                //create a temp image
                Image img = pb.Image;

                //calculate the size of the image
                Size imgSize = GenerateImageDimensions(img.Width, img.Height, this.pictureBox1.Width, this.pictureBox1.Height);

                //create a new Bitmap with the proper dimensions
                Bitmap finalImg = new Bitmap(img, imgSize.Width, imgSize.Height);

                //create a new Graphics object from the image
                Graphics gfx = Graphics.FromImage(img);

                //clean up the image (take care of any image loss from resizing)
                gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;

                //empty the PictureBox
                pb.Image = null;

                //center the new image
                pb.SizeMode = PictureBoxSizeMode.CenterImage;

                //set the new image
                pb.Image = finalImg;
            }
            catch (System.Exception e)
            {
                //MessageBox.Show(e.Message);
            }
        }


Another thing that you can do is a progress bar for Track Time. I have Timer1 set for 1 second intervals. This is not totally tested, your mileage may vary:

void Timer1Tick(object sender, EventArgs e)
        {
            IITTrack track = app.CurrentTrack;
            progress.Maximum = track.Duration;
            progress.Value = app.PlayerPosition;
            if (track.Duration != oldtrack)
            {
                if (track.Name != oldname)
                {
                    NewSong();
                }
                else
                {
                    oldname = track.Name;
                }
            }
            else
            {
                oldtrack = track.Duration;
            }
        }


REST Album Art and Lyrics
Finally, I have these few functions that will use Amazon and LyricWiki to find Album Art and Lyrics of a playing song.
I would recommend running them in a BackgroundWorker, to prevent a program from locking up.

Lyrics:

private static string GetLyrics(string Artist, string Title)
        {
            StringBuilder lyric = new StringBuilder();  //Build a new string
            lyric.Append("http://lyricwiki.org/api.php?func=getSong&artist=");   //Add base URL
            lyric.Append(Artist);
            lyric.Append("&song=");
            lyric.Append(Title);
            lyric.Append("&fmt=xml");
            WebRequest requestlyr = WebRequest.Create(lyric.ToString()); //prepare web request
            StreamReader responseStreamlyr = new StreamReader(requestlyr.GetResponse().GetResponseStream()); //prepare responese holder
            string responselyr = responseStreamlyr.ReadToEnd(); //fill up response
            responseStreamlyr.Close(); //Close stream
            string lyrics = XmlParse_general(responselyr.ToString(), "lyrics"); //parse the XML
            return lyrics;
        }

private static string XmlParse_general(string Url, string type)    //XML parse Function
        {

            XmlTextReader xmlrt1 = new XmlTextReader(new StringReader(Url));
            while (xmlrt1.Read())
            {
                //Trace.Write("Node Type", xmlrt1.NodeType.ToString());
                string strNodeType = xmlrt1.NodeType.ToString();
                string strName = xmlrt1.Name;

                if (strNodeType == "Element" && strName == type)
                {
                    xmlrt1.Read();
                    return xmlrt1.Value; //Return output
                } // end if
            } return "";// end while
        }

Album Art
:

private string AlbumURL(string artist, string album)
        {
            StringBuilder art = new StringBuilder();  //Build a new string
            art.Append("http://ecs.amazonaws.com/onca/xml?Service=AWSECommerceService&Version=2005-03-23&Operation=ItemSearch&ContentType=text%2Fxml&SubscriptionId=09XAA8GD0PQVCC8KR5G2&SearchIndex=Music&Artist=");   //Add base URL
            art.Append(artist);
            art.Append("&Keywords=");
            art.Append(album);
            art.Append("&ResponseGroup=Images");
            WebRequest requestart = WebRequest.Create(art.ToString()); //prepare web request
            StreamReader responseStreamart = new StreamReader(requestart.GetResponse().GetResponseStream()); //prepare responese holder
            string responseart = responseStreamart.ReadToEnd(); //fill up response
            responseStreamart.Close(); //Close stream
            string url = XML_image(responseart.ToString()); //parse the XML
            return url;
        }

private static string XML_image(string xml)
        {
            XmlTextReader reader = new XmlTextReader(new StringReader(xml));
            reader.ReadToFollowing("LargeImage");
            reader.ReadToFollowing("URL");
            reader.Read();
            return reader.Value;
        }

If anyone has any questions or needs help getting these going, especially the REST functions, or has any suggested improvements, contact me, I'd be glad to help.

Goodbye, and Good Luck!


Wednesday, March 18, 2009

Bit.ly C# API Update

I decided to update my Bit.ly API DLL in C#, thanks to @codemypantsoff. The only new function that I added was the ability to get the User who shortened a URL from the Short URL or from the Hash. I figured it was also a good time to move all my stuff from my Google Sites page onto a blog post, so here it goes.

 Calls:

MetaH - Retrieves the metadata from a hash

string a = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "epicdiggnation", "MetaH");
Console.WriteLine(a);

MetaU - Retrieves the metadata from a Short Url

string b = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "http://bit.ly/1RmnUT", "MetaU");
Console.WriteLine(b);

ExpandH - Returns the Expanded url from a hash

string c = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "epicdiggnation", "ExpandH");
Console.WriteLine(c);

ExpandU - Returns the Expanded url from a Short Url

string d = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "http://bit.ly/1RmnUT", "ExpandU");
Console.WriteLine(d);

Shorten - Returns a Shortened URL from a long URL
string e = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "http://cnn.com", "Shorten");
Console.WriteLine(e);

ClicksH - Returns number of clicks from a hash

string f = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "epicdiggnation", "ClicksH");
Console.WriteLine(f);

ClicksU - Returns number of clicks from a Short Url

string g = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "http://bit.ly/1RmnUT", "ClicksU");
Console.WriteLine(g);

UserU - Returns the User who shortened the URL from a Short Url

string h = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "http://bit.ly/1RmnUT", "UserU");
Console.WriteLine(h);

UserH - Returns the User who shortened the URL from a Hash

string i = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "epicdiggnation", "UserH");
Console.WriteLine(i);


Console Application sample code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Bitly;
namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string a = API.Bit("bitlyapidemo", "R_0da49e0a9118ff35f52f629d2d71bf07", "http://cnn.com", "Shorten");
            Console.WriteLine(a);
            Console.Read();
        }
    }
}

DLL Link
Source Link

If anyone would like me to update my WPF sample app or has a feature request, go ahead and contact me.