The iPod controller that I submitted for the mbed challenge

Dependencies:   mbed Motordriver PID

ipodcontrol.cpp

Committer:
networker
Date:
2011-05-04
Revision:
0:371773dd3dd1

File content as of revision 0:371773dd3dd1:

#include "mbed.h"
#include "ipodcontrol.h"

char* strdup(const char *s) {
    char *r = new char[strlen(s)+1];
    strcpy(r, s);
    return r;
}

/* There are 6 different paths through the ipod menu structure, they all start at the toplevel and end at the song level.
   The longest path has 5 levels and each type has a variable number of entries, except the toplevel which has always 6 entries (paths).
   When going down the hierarchy, each type starts at entry 0 (the top of the item list).
   When going up the hierarchy, each type starts at the entry it was at when going down, this is remembered in array 'last'.
*/
//enum types { top, playlist, artist, album, genre, song, composer, podcast};
const char *ipodControl::toplevel[] = {"Playlist","Artist","Album","Genre","Song","Composer"};//do not change order!
const types ipodControl::paths[][maxdepth] = {
    {top,playlist,song},
    {top,artist,album,song},
    {top,album,song},
    {top,genre,artist,album,song},//path with the maximum depth
    {top,song},
    {top,composer,album,song}
};

ipodControl::ipodControl(ipod& p): pod(p)  {
    mode = nav;
    path = 0;
    level = 0;
    items = sizeof(toplevel)/sizeof(char*);
    current = 0;
    OnGetNames = 0;
    OnTrackChange = 0;
    OnTime = 0;
    OnTitle = 0;
    OnAlbum = 0;
    OnArtist = 0;
    OnStatus = 0;
    OnError = 0;
    wrap = false;
    pod.SetMode(4);
}

bool ipodControl::readName() {
    name = 0;
    pod.SendAirCmd(get_ipod_name);
    if (pod.waitForReply())
        pod.release();
    if (pod.getError()) {
        if (OnError)
            OnError(pod.getError(), get_ipod_name);
        return false;
    }
    return true;
}

//the menu structure currently has no <All> items
void ipodControl::OK(unsigned item) {//when the user presses 'OK' (rotary encoder), we move down the menu hierarchy until we reach the 'song' level, we then switch to playback mode
    if (mode == nav) {
        if (paths[path][level] != song) { //present level is not yet 'song'
            last[level] = item; //push the current position
            if (level>0) //not at the toplevel
                pod.SendAirCmd(select, paths[path][level], item); //'select' the current item
            level++;
            current = 0;  //start at beginning (no forward history)
            pod.SendAirCmd(get_count, paths[path][level]); //get the number of subitems
            pod.waitForReply();
            items = pod.Arg1();
            pod.release();
            if (items>0)
                pod.SendAirCmd(get_names, paths[path][level],0U,1U);  //display the first subitem
            printf("new level: %s\n", toplevel[paths[path][level]-1]);         //diag. display the new level category
        } else { //pressed OK at song level
            mode = playback;
            pod.SendAirCmd(select, paths[path][level], item); //'select' the current item
            pod.SendAirCmd(play_list, item); //execute the current item, start playing
            Update();
        }
    } else //mode>=play
    { //real ipod will cycle through volume, position(elapsed), cover_art and stars
    }
}

void ipodControl::Menu() {//when the user presses 'menu' we move up the menu structure, we enter navigation mode but playback(if active) continues
    if (mode != nav) {
        mode = nav;
        return;
    }
    if (level > 0) {
        level--;
        if (level > 0) { //still not at top level
            pod.SendAirCmd(get_count, paths[path][level]);  //get number of items at new level
            pod.waitForReply();
            items = pod.Arg1();
            pod.release();
            if (items>0) {
                pod.SendAirCmd(get_names, paths[path][level],last[level],1U);  //display the item at the pushed position, or...
                current = last[level];//pop the last position
            } else {
                current = 0;
            }
            printf("new level: %s\n", toplevel[paths[path][level]-1]);         //diag. display the new level category
        } else { //reached toplevel
            items = sizeof(toplevel)/sizeof(char*);
            current = path;
            printf("new level: Music\n");         //diag. display the new level category
            pod.SendAirCmd(select, 1U); //'select' all  (entire iPod)
            //pod.SendAirCmd(switch_to_main); //switch to main library
        }
    }
    //else was already at top => do nothing
}

void ipodControl::Right() {
    if (mode == nav) {
        Next();
        if (paths[path][level] == top)  //top level
            path = current;
        else //get name of item at new position
            pod.SendAirCmd(get_names, paths[path][level],current,1U);
    } else
        pod.SendAirCmd(command, next);//skip fwd
}

void ipodControl::Left() {
    if (mode == nav) {
        Prev();
        if (paths[path][level] == top) { //top level
            path = current;
        } else {//get name of item at new position
            pod.SendAirCmd(get_names, paths[path][level],current,1U);
        }
    } else
        pod.SendAirCmd(command, prev);//skip back
}

void ipodControl::MoveTo(float pos) {
    newpos = pos;
    switch (mode) {
        case playback:
            if (pos < elapsed) { //reverse
                //newpos = pos;
                mode = fastr;
                printf("moving back to %f sec\n", 0.001*pos);
                pod.SendAirCmd(command, frwd);//assume that polling is active and that the command is accepted
            } else if (pos > elapsed) { //forward
                //newpos = pos;
                mode = fastf;
                printf("moving fwd to %f sec\n", 0.001*pos);
                pod.SendAirCmd(command, ffwd);
            }
            break;
        case fastf:
            //newpos = pos;
            printf("> moving fwd to %f sec\n", 0.001*pos);
            break;
        case fastr:
            //newpos = pos;
            printf("< moving back to %f sec\n", 0.001*pos);
            break;
        case nav:
            printf("N");
            break;
    }
}

void ipodControl::poll() {//this is called as part of the main event loop
    if (pod.ready()) {
        pod.parse();
        switch (pod.cmd()-1) {
            case get_count:
                items = pod.Arg1();
                break;
            case get_names:
                if (OnGetNames)
                    OnGetNames(pod.Arg1(), pod.text());
                break;
            case get_position:
                current = pod.Arg1();
                break;
            case polling:
                if (pod.Arg1()==1) {//track change
                    current = pod.Arg2();
                    mode = playback;
                    if (OnTrackChange)
                        OnTrackChange(current);
                } else {//elapsed time
                    elapsed = pod.Arg2();
                    switch (mode) {
                        case playback:
                            if (OnTime)
                                OnTime(elapsed);
                            break;
                        case fastf:
                            printf(" F ");
                            if (elapsed >= newpos)
                                mode = stopfast;
                            break;
                        case fastr:
                            printf(" R ");
                            if (elapsed <= newpos)
                                mode = stopfast;
                            break;
                        case stopfast:
                            printf(" S ");
                            if (elapsed >= newpos)
                                mode = playback;
                            break;
                    }
                    if (mode == stopfast) {
                        printf("reached position %f sec\n", 0.001*elapsed);
                        pod.SendAirCmd(command, stopf);
                    }
                }
                break;
            case get_ipod_name:
                if (name) delete[] name;
                name = strdup(pod.text());
                break;
            case get_title:
                if (OnTitle)
                    OnTitle(pod.text());
                break;
            case get_artist:
                if (OnArtist)
                    OnArtist(pod.text());
                break;
            case get_album:
                if (OnAlbum)
                    OnAlbum(pod.text());
                break;
            case get_time_status: //arg1=tracklength in ms, arg2=elapsed time, arg3=status (0=stop, 1=play, 2=pause)
                tracklength = pod.Arg1();
                elapsed = pod.Arg2();
                status = pod.Arg3();
                if (OnStatus)
                    OnStatus(tracklength, elapsed, pod.Arg3());
                break;
            case 0: //ack/error
                if (pod.Arg1()>0 && OnError)
                    OnError(pod.Arg1(), pod.Arg2());
                break;
            default:
                printf("Unknown reply from iPod %04X\n", pod.cmd());
                break;
        }
        unsigned reply = pod.cmd();
        pod.release();//reset ready flag and delete the receive buffer, meaning that all returned results should be saved or processed
        updater(reply);
    }
}

void ipodControl::updater(unsigned reply) {
    if (update_state != usIdle)
        printf("\t\tstate=%d, reply = %02X\n", update_state, reply);
    switch (update_state) {
        case usGet_time_status:
            if (reply==get_time_status+1) {
                update_state = usGet_title;
                pod.SendAirCmd(get_title, current);
                printf("updater: getting title\n");
            } else if (reply==polling+1) {//only do this when polling is off
                pod.SendAirCmd(get_time_status); //reissue the same command and stay in the same state
                printf("updater: getting status (again)\n");
            }
            break;
        case usGet_title:
            if (reply==get_title+1) {
                update_state = usGet_artist;
                pod.SendAirCmd(get_artist, current);
                printf("updater: getting artist\n");
            }
            break;
        case usGet_artist:
            if (reply==get_artist+1) {
                update_state = usIdle;
                pod.SendAirCmd(polling, 1);
                printf("updater: going idle\n");
            }
            break;
    }
}