Go Down

Topic: Gobetwino Music Server for iTunes (Read 2323 times) previous topic - next topic

Jaaptina

Jul 16, 2011, 11:46 pm Last Edit: Sep 10, 2011, 09:52 pm by Jaaptina Reason: 1
Building a music server the hard way having fun with Arduino.
Using Arduino, LCD screen and SC-card reader to start a selected album in iTunes on a pc.

First step is to control iTunes with Arduino. I tried to use Gobetwino but this doesn't always make the right selections. I ended up using AAC-Keys to translate serial data to key presses and Auto Hot Key (AHK) to control iTunes.
The most difficult part was to select the iTunes search field.

Second step is to make al list of albums. In iTunes export a playlist to Excel. In Excel remove all info except artist and album name. Save as csv-file. Rename to txt-file and copy to SD-card. This creates a list for Arduino to read like:

Giant Sand;Selections Circa 1990 - 2000
Joan Baez;Play Me Backwards
Room Eleven;Mmm... Gumbo?
Nina Simone;The Very Best Of Nina Simone
The Walkabouts;Nighttown
Calexico;The Black Light

Third step is to make Arduino scroll up and down this list. To make Arduino read this list all at once is explained in the SD-card examples.
The scolling function is based on line feeds. During setup al line feed positions are stored in an array. Now we know the starting points of all next lines (albums). The ";" is used to display the album title on the 2nd row.
Setup ends with displaying the first album on the LCD screen. I use a Nuelectronics LCD screen with buttons like this:


Use up and down buttons to display the next or previous album on the LCD screen (see next()). Pushing key 0 sends an AHK command to the pc, adds the title of the selected album and starts the first song in iTunes (see start()).

All works fine on my laptop for now:


Next is to set up a second hand pc as iTunes server with digital out (toslink or coax). This I will connect through a dac to my stereo. I'd like to convert an old AKAI digital tuner into the music server by exchanging the screen with the Arduino LCD screen. But this will take some time.

The code works well and certainly fast enough. Improvements and suggestions on how to use a NAS instead of a pc are welcome.

Code: [Select]
#include <SD.h>
#include <PString.h>
#include <LCD4Bit_mod.h>

LCD4Bit_mod lcd = LCD4Bit_mod(2);
int keyarray[5] = {30, 150, 360, 535, 760 }; //see LCD button shield
int NUM_KEYS = 5;
int keypad;
int key = -1;
int oldkey = -1;

const int chipSelect = 10;
File dataFile;
char x;
int myLines[24];  // array of 24 albums based on line feed positions
int n = 0;        // album counter
int pos = 0;      // album position variable
char album[60];   // "artist name;album name" variable
PString myPstring(album, sizeof(album)); // to built a string out of characters


void setup(){
 Serial.begin(9600);
 pinMode(10, OUTPUT);    
 SD.begin(chipSelect);
 dataFile = SD.open("SAVONDS2.TXT");
 if (dataFile) {  
   while (dataFile.available()) {
     x = dataFile.read();         //characters read one by one
     if (x == 10){                //line feed is 10
       myLines[n] = dataFile.position(); //array of line feed positions
       n++;                      
   } }
   dataFile.close();
   myLines[23] = 0;               //change last pos into first album position
   n = 23;
 }
 lcd.init();
 next();                          //print first album to LCD
 delay(200);
}  

void loop(){
 keypad = analogRead(0);          //detect key press
 key = getkey(keypad);
 if (key != oldkey){
   delay(25);        
   keypad = analogRead(0);  
   key = getkey(keypad);  
   if (key != oldkey){
     oldkey = key;
     if (key >=0){buttons();}    // LCD shield with 5 buttons  
   }
 }
 delay(25);
}

void buttons(){              
 if (key == 0){start();}         // itunes starts displayed album
 if (key == 1){n=n-1; next();}   // display next album
 if (key == 2){n=n+1; next();}   // display previous album
 if (key == 3){Serial.println("\033,hold,alt.s");}  // AHK start/pause itunes
 if (key == 4){Serial.println("\033,hold,alt.s");}
 delay(100);
}

void next(){        //read artist and album name (n) from SD-card
 if(n<0){n=23;}    //scroll from first to last album
 if(n>23){n=0;}    //scroll from last to first album
 pos = myLines[n]; //position of line feed preceding ablum (n)
 myPstring = "";
 dataFile = SD.open("SAVONDS2.TXT");
 if (dataFile) {  
   lcd.clear();
   lcd.cursorTo(1, 0);           //artist name on first row
   dataFile.seek(pos);           //position to read
   int q = 0;  
   while (dataFile.available()) {
     x=dataFile.read();          //read next character 'x'
     q++;                        //character counter
     if (x == ';'){
       lcd.cursorTo(2, 0);       //album name on second row
       q=0;                      //reset character counter for next row
       myPstring.print(' ');}    //space between artist and album
     else{
       if (x != 10 && x != 13){
         if(q<17){lcd.print(x);} //send 'x' to LCD screen
         myPstring.print(x);    //add 'x' to album
       }
       else{
         dataFile.close();       //stop while loop
       }                         //myPstring contains artist and album name
     }
   }
 }
}

void start(){
 Serial.println("\033,hold,alt.z"); //AHK open itunes search field
 delay(500);                        //wait for blank search field
 Serial.println(album);             //copy album from myPstring
 delay(1500);                       //wait for itunes
 Serial.println("\033,hold,alt.s"); //itunes start album
}

int getkey(unsigned int input){
 int k;
 for (k=0; k<NUM_KEYS; k++){
   if (input < keyarray[k]){return k;}
 }
 if (k >= NUM_KEYS){k = -1; return k;}
}


AHK script:
Code: [Select]
; based on code snippets from the iTunes Anywhere topic
; thanks to daorc for activating the search control
; thanks to brohan for ComObjCreate function

#SingleInstance, Force

itunes := ComObjCreate("iTunes.Application")

ControlSend, ahk_parent,{Tab}, iTunes ahk_class iTunes
ControlSend, ahk_parent,{Tab}, iTunes ahk_class iTunes
DetectHiddenWindows, On

!s::
itunes.PlayPause()
return

!z::
IfWinExist,ahk_class iTunes
  {
  itunes.Pause()
  winset,top
  winactivate
  controlclick,Edit1,iTunes ahk_class iTunes ;activate the "search" control
  controlfocus,Edit1,iTunes ahk_class iTunes ;activate the "search" control
  ControlSend, ahk_parent,{Esc}, iTunes ahk_class iTunes
  }
return







HugoPT

Debian,Mint,Ubuntu
Arduino Mega 2560
Arduino Nano
Arduino Duemilanove
MAC OS Montain Lion
Raspberry PI Model B

Jaaptina

#2
Aug 24, 2011, 05:17 pm Last Edit: Sep 29, 2011, 10:19 pm by Jaaptina Reason: 1
Using Gobetwino it is possible to read a file with album names from the computer and start and control iTunes. So the SD-card is not needed anymore.

An old AKAI digital tuner is used as housing. Behind the dial is a 10-turns potentiometer. Turning the dial one of a hundred albums can be selected.


Jaaptina

#3
Sep 10, 2011, 10:12 pm Last Edit: Sep 17, 2011, 02:57 pm by Jaaptina Reason: 1
The arduino is placed in the old digital tuner. The buttons are wired. The tuning knob is used te scroll through the albums. Gobetwino is used to read the album names from a file on the pc. The button next to the knob is used to start playing the selected album. Gobetwino sends the artist and album name to the iTunes search box and starts the album. Pressing the button again, without changing an album, functions as pause/start for the current song. The button left of the screen shuts down iTunes.



The pc is an Asus Cube with an digital coax out for the highest sound quality.



Future enhancements:

  • remote control (IR)

  • playlists select function (small lists in favor of one big list)

  • relocate the on/off function of the pc to the tuner

  • move the pc system into the tuner housing

  • move the cd's to the attic


Jaaptina

#4
Sep 29, 2011, 10:18 pm Last Edit: Dec 14, 2011, 09:26 pm by Jaaptina Reason: 1
With some heavy tuning of the code the Music Server now functions well. I used a lot off sub/sub routines because Gobetwino seems to be responding quicker that way. On the PC-side Gobetwino can activate shutdown.exe now. The main power button (wired to Arduino) now shuts down the PC. I moved the PC to the cellar and used a long USB cable to connect to the music server. The music is send wirelessly to an Apple Airport Express.

Code: [Select]

#include <PString.h>
#include <LCD4Bit_mod.h>

LCD4Bit_mod lcd = LCD4Bit_mod(2);
int iAnVal = A2; int val; int oldval = 1050;
int pot; int oldpot = -1;
int button1;
int button2; int oldbutton2 = LOW;
int buttonPin1 = 13; int buttonPin2 = 12;
int state = 1;
int n;                                      // number of albums
int p;                                      // album position (2 tot n+1)
int y = 0;                                  // playlists no. 0 to 3
int steps = 80;                             // potmeter instances
char* myPlaylists[4]= {"SAVONDS","STEVIG","DIVERSEN","KLASSIEK"};
char x; char buffer[5]; char album[33];     // "artist name;album name" variable
PString myPstring(album, sizeof(album));    // put characters into a string
boolean newalbum = false;
boolean play = false;

void setup(){
  Serial.begin(9600);
  pinMode(buttonPin1, INPUT);
  lcd.init(); lcd.cursorTo(1, 0);
  lcd.printIn("Start");
  starttunes();             


void starttunes(){                           
  readline(1);
  while(!Serial.available()){lcd.printIn("Waiting ..."); delay(3000); readline(1);}
  number();            delay(100);
  tunes();             delay(5000);     
}

void number(){
  myPstring = "";
  while(Serial.available()){
    x = Serial.read();                   //read character from Gobetwino
    if(x!=10 && x!=13 && x!=48 && x!=59){
      myPstring.print(x);}}              //add character to string(album)
  delay(100);
  n = atoi(album);                       //string to int (first line states the number of albums)
  lcd.cursorTo(2, 0);
  lcd.printIn(itoa((n), buffer, 10));    //display number of albums on LCD
}

void tunes(){
  Serial.println("#S|TUNES|[]#");        //start iTunes (Gobetwino command)
  delay(200);
}

void loop(){                           
  button1 = digitalRead(buttonPin1);       
  button2 = digitalRead(buttonPin2);         
  if(button2 != oldbutton2){                  //select playlist
    if(button2 == HIGH){selectlist();}
    if(button2 == LOW){selectalbum();}
    oldbutton2 = button2; return;}
  if(button2 == LOW && button1 == HIGH){      //button1 pressed
    if(newalbum == true){start(); return;}    //start selected album
    else{
      Serial.println("#S|SENDK|[0& ]#");
      play = !play;
      delay(1000); return;}}
  val = analogRead(iAnVal);                   //read potentiometer
  if(val>oldval-5 && val<oldval+5){delay(100); return;}
  delay(10);
  val = analogRead(iAnVal);                 //read potentiometer again
  if(val>oldval-5 && val<oldval+5){delay(100); return;}
  oldval = val;
  pot = map(val, 0, 1023, 0, steps);          //map number of steps
  if(pot != oldpot){
    if(button2 == HIGH){selectlist();}
    if(button2 == LOW){selectalbum();}
    oldpot = pot;}
  else{delay(100);}   
}

void selectlist(){
  if(y == 3){y = -1;}
  y = y+1;
  readline(1);
  lcd.printIn(myPlaylists[y]);
  number();
}

void selectalbum(){
  if(pot == steps || pot == 0 || oldpot == -1){
    if(pot == steps){p = n+1;}
    if(pot == 0){p = 2;}
    if(oldpot == -1){p = pot;}}
  else{
    if(pot>oldpot){                     //rotate right
      int i = pot-oldpot;
      if(i < 3){p = p+i;}
      else{p = p+5*(pot-oldpot);}}      //skip some albums
    if(pot<oldpot){                     //rotate left
      int i = oldpot-pot;
      if(i < 3){p = p-i;}
      else{p = p-5*(oldpot-pot);}}}     //skip some albums         
  while (p > n+1){p = p-n;}           
  if(p<2){p = n+1;}
  newalbum = true;                      //wait for button1 to start album
  displayAlbum(); 
}

void displayAlbum(){                    //state 2; read album p from file
  readline(p);
  if(!Serial.available()){lcd.printIn("Time out");return;}
  myPstring = "";
  int q = 0; while (Serial.available() && q < 60){
    x = Serial.read(); q++;             //read character from Gobetwino
    if(x == ';'){                       //artist;album seperator
      lcd.cursorTo(2, 0); q = 0;        //album name on second row
      myPstring.print(' ');}            //reset character counter for next row
    if(x != ';' && x !=10 && x != 13 && x != 48 && q < 17){ //discard line feed etc.
      lcd.print(x);                     //send 'x' to LCD screen
      myPstring.print(x);}}             //add 'x' album
  delay(50);
}

void readline(int r){                    //Gobetwino command
  lcd.clear(); lcd.cursorTo(1, 0);
  int q = 0; while (Serial.available() && q < 200){
    x = Serial.read(); q++;}             //empty serial buffer
  Serial.print("#S|");
  Serial.print(myPlaylists[y]);
  Serial.print("|[");
  Serial.print(itoa((r), buffer, 10));   //read myPlaylist[y] line p
  Serial.println("]#");                  //from gobetwino-defined file
  q = 0; while(!Serial.available() && q < 200){delay(10); q++;} // wait for serial return
}

void start(){                            //start selected album
  if(play == true){
    Serial.println("#S|SENDK|[0& ]#");
    play = false; delay(50);}
  Serial.println("#S|SENDK|[0&^i]#"); delay(50);
  Serial.println("#S|SENDK|[0&^i]#"); delay(50);
  Serial.println("#S|SENDK|[0&^i]#"); delay(50);
  Serial.print("#S|SENDK|[0&");
  Serial.print(album);                   //copy album from myPstring
  Serial.println("]#");                  //to iTunes
  delay(1000);                           //wait for itunes 1800
  Serial.println("#S|SENDK|[0&{TAB}]#"); delay(50);
  Serial.println("#S|SENDK|[0& ]#");
  play = true; newalbum = false;
  delay(100);


D4p0up

Impressive job.

I would love to have such a Hifi system, but with the audio output, and that would dig the music data onto a local ethernet samba server.

I already found some equivalent equipments, but all of them are based on TV for the man machine interface.
Arduino sound Reseach - http://bit.ly/fullmaj [fr][en] | Homemade Free VST - http://zomg.zxq.net [en]

MikMo

My little heart gets all warm and tender when i see GoBetwino beeing used for cool projects  :)

Jaaptina


My little heart gets all warm and tender when i see GoBetwino beeing used for cool projects  :)


Thanks MikMo!
You have written the only program I could work with that can read files on a PC.

Could not have done it without you!  ;)

Go Up