diff options
Diffstat (limited to 'moc_library.c')
-rw-r--r-- | moc_library.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/moc_library.c b/moc_library.c new file mode 100644 index 0000000..1e382aa --- /dev/null +++ b/moc_library.c @@ -0,0 +1,541 @@ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> //Header file for sleep(). man 3 sleep for details. +#include <errno.h> + +#include <pthread.h> +#include <sys/socket.h> +#include <sys/un.h> + +// A normal C function that is executed as a thread +// when its name is specified in pthread_create() +// + +char *moc_socket_path = "/home/calvin/.moc/socket2"; + +/* State of the server. */ +#define STATE_PLAY 0x01 +#define STATE_STOP 0x02 +#define STATE_PAUSE 0x03 +#define CMD_GET_STATE 0x13 /* get the state */ +#define CMD_GET_CTIME 0x0d /* get the current song time */ +#define CMD_GET_TAGS 0x2c /* get tags for the currently played file */ +#define CMD_GET_FILE_TAGS 0x2f /* get tags for the specified file */ +#define CMD_GET_SNAME 0x0f /* get the stream file name */ +#define MAX_SEND_STRING 4096 +#define RANGE(min, val, max) ((val) >= (min) && (val) <= (max)) + + +/* Definition of events sent by server to the client. */ +#define EV_STATE 0x01 /* server has changed the state */ +#define EV_CTIME 0x02 /* current time of the song has changed */ +#define EV_SRV_ERROR 0x04 /* an error occurred */ +#define EV_BUSY 0x05 /* another client is connected to the server */ +#define EV_DATA 0x06 /* data in response to a request will arrive */ +#define EV_BITRATE 0x07 /* the bitrate has changed */ +#define EV_RATE 0x08 /* the rate has changed */ +#define EV_CHANNELS 0x09 /* the number of channels has changed */ +#define EV_EXIT 0x0a /* the server is about to exit */ +#define EV_PONG 0x0b /* response for CMD_PING */ +#define EV_OPTIONS 0x0c /* the options has changed */ +#define EV_SEND_PLIST 0x0d /* request for sending the playlist */ +#define EV_TAGS 0x0e /* tags for the current file have changed */ +#define EV_STATUS_MSG 0x0f /* followed by a status message */ +#define EV_MIXER_CHANGE 0x10 /* the mixer channel was changed */ +#define EV_FILE_TAGS 0x11 /* tags in a response for tags request */ +#define EV_AVG_BITRATE 0x12 /* average bitrate has changed (new song) */ +#define EV_AUDIO_START 0x13 /* playing of audio has started */ +#define EV_AUDIO_STOP 0x14 /* playing of audio has stopped */ + +/* Events caused by a client that wants to modify the playlist (see + * CMD_CLI_PLIST* commands). */ +#define EV_PLIST_ADD 0x50 /* add an item, followed by the file name */ +#define EV_PLIST_DEL 0x51 /* delete an item, followed by the file name */ +#define EV_PLIST_MOVE 0x52 /* move an item, followed by 2 file names */ +#define EV_PLIST_CLEAR 0x53 /* clear the playlist */ + +/* These events, though similar to the four previous are caused by server + * which takes care of clients' queue synchronization. */ +#define EV_QUEUE_ADD 0x54 +#define EV_QUEUE_DEL 0x55 +#define EV_QUEUE_MOVE 0x56 +#define EV_QUEUE_CLEAR 0x57 +/* Flags for the info decoder function. */ +enum tags_select +{ + TAGS_COMMENTS = 0x01, /* artist, title, etc. */ + TAGS_TIME = 0x02 /* time of the file. */ +}; + +enum moc_state { + STOPPED, + PLAYING, + PAUSED +}; + +enum moc_status { + INITIALIZING, + CONNECTED, + FAILED_TO_CONNECT, + ERROR +}; + +struct moc { + pthread_mutex_t lock; + enum moc_status status; + enum moc_state state; + + char *filename; + char *title; + char *album; + char *artist; + int track; + int time; + int filled; +}; + +extern const char * moc_str_state(enum moc_state s) { + static const char *strings[] = { "STOPPED", "PLAYING", "PAUSED"}; + return strings[s]; + +} + +static char *get_curr_file(int sock); +static char *get_data_str(int sock); +static void *get_event_data (const int sock, const int type); +int send_str (int sock, const char *str); +char *get_str (int sock); +int send_int (int sock, int i); +int get_int (int sock, int *i); + +static char *get_curr_file (const int sock) { + send_int (sock, CMD_GET_SNAME); + int ev; + while(ev != EV_DATA) { + get_int(sock, &ev); + fprintf(stderr, "EVENT: 0x%02x\n", ev); + } + return get_str(sock); +} +static void wait_for_data (const int sock) { + int event = -1; + do { + fprintf(stderr, "waiting for data last was %d\n", event); + event = get_int(sock, &event); + if (event == EV_EXIT) { + fprintf(stderr, "The server exited!"); + pthread_exit(NULL); + } + if(event == EV_STATE) { + char *skip; + skip = get_str(sock); + fprintf(stderr, "got str%s\n", skip); + } + if (event != EV_DATA) + get_event_data(sock, event); + } while (event != EV_DATA); +} + +static void *get_event_data (const int sock, const int type) +{ + switch (type) { + case EV_PLIST_ADD: + case EV_QUEUE_ADD: + break; + // return recv_item_from_srv (); + case EV_PLIST_DEL: + case EV_QUEUE_DEL: + case EV_STATUS_MSG: + case EV_SRV_ERROR: + return get_str(sock); + case EV_FILE_TAGS: + // return recv_tags_data_from_srv (); + break; + case EV_PLIST_MOVE: + break; + } + + return NULL; +} +static char *get_data_str(int sock) { + wait_for_data (sock); + return get_str(sock); +} + + +int send_str (int sock, const char *str) +{ + int len; + + len = strlen (str); + if (!send_int (sock, len)) + return 0; + + if (send (sock, str, len, 0) != len) + return 0; + + return 1; +} +char *get_str (int sock) { + int len; + int res, nread = 0; + char *str; + + if (!get_int(sock, &len)) + return NULL; + + if (!RANGE(0, len, MAX_SEND_STRING)) { + printf("Bad string length."); + return NULL; + } + + str = (char *)malloc (sizeof(char) * (len + 1)); + while (nread < len) { + res = recv (sock, str + nread, len - nread, 0); + if (res == -1) { + printf("recv() failed when getting string: %s\n", + strerror(errno)); + free (str); + return NULL; + } + if (res == 0) { + printf("Unexpected EOF when getting string\n"); + free (str); + return NULL; + } + nread += res; + } + str[len] = 0; + + return str; +} +int send_int (int sock, int i) +{ + int res; + + res = send (sock, &i, sizeof(int), 0); + if (res == -1) + printf("send() failed: %s", strerror(errno)); + + return res == sizeof(int) ? 1 : 0; +} + +int get_int (int sock, int *i) +{ + int res; + + res = recv (sock, i, sizeof(int), 0); + if (res == -1) + printf("recv() failed when getting int: %s", strerror(errno)); + + return res == sizeof(int) ? 1 : 0; +} + +static int moc_server_connect () +{ + char *path = moc_socket_path; + struct sockaddr_un sock_name; + int sock; + + /* Create a socket */ + if ((sock = socket (PF_LOCAL, SOCK_STREAM, 0)) == -1) + return -1; + + sock_name.sun_family = AF_LOCAL; + strcpy (sock_name.sun_path, moc_socket_path); + + if (connect(sock, (struct sockaddr *)&sock_name, + SUN_LEN(&sock_name)) == -1) { + close (sock); + fprintf(stderr, "%s at (%s)\n", strerror(errno), sock_name); + return -1; + } + + return sock; +} + + +void update_state(struct moc *data, int state) { + fprintf(stderr, "moc: state %d, state\n"); + if(state == STATE_PLAY) { + data->state = PLAYING; + fprintf(stderr, "moc: playing\n"); + } + if(state == STATE_PAUSE) { + data->state = PAUSED; + fprintf(stderr, "moc: paused\n"); + } + if(state == STATE_STOP) { + data->state = STOPPED; + fprintf(stderr, "moc: state stopped\n"); + } +} +void *moc_loop(void *input) { + + + struct moc *data = (struct moc*)input; + + int srv_sock = -1; + + srv_sock = moc_server_connect(); + if(srv_sock == -1) { + pthread_mutex_lock(&data->lock); + data->status = FAILED_TO_CONNECT; + pthread_mutex_unlock(&data->lock); + pthread_exit(NULL); + } + + data->status = CONNECTED; + + int last_cmd = -1; + int event = -1; + + send_int(srv_sock, CMD_GET_STATE); + last_cmd = EV_STATE; + while(get_int(srv_sock, &event)) { + switch (event) { + // handle our data. we MUST know what the last request was otherwise we + // don't know how to parse it. + case EV_DATA: + fprintf(stderr, "moc: EV_DATA\n"); + if(last_cmd == EV_CTIME) { + get_int(srv_sock, &data->time); + fprintf(stderr, "moc: update ctime\n"); + } + // on state change, we need to figure out what file we have and what + // tags it has. + else if(last_cmd == EV_STATE) { + int state; + get_int(srv_sock, &state); + update_state(data, state); + + char *file = get_curr_file(srv_sock); + fprintf(stderr, "moc: current file %s\n", file); + // if it's the same file as we had, don't update. + if(data->filename == NULL || strcmp(file, data->filename) != 0) { + fprintf(stderr, "moc: new file, asking for tags\n"); + data->filename = file; + // get tags. + send_int(srv_sock, CMD_GET_FILE_TAGS); + send_str(srv_sock, file); + send_int(srv_sock,TAGS_COMMENTS | TAGS_TIME); + last_cmd = EV_FILE_TAGS; + } + } + else { + fprintf(stderr, "moc: shit i have data IDK what it is\n"); + } + last_cmd = -1; + + break; + case EV_BUSY: + fprintf(stderr, "moc: EV_BUSY\n"); + break; + case EV_CTIME: + fprintf(stderr, "moc: EV_CTIME\n"); + last_cmd = EV_CTIME; + send_int(srv_sock, CMD_GET_CTIME); + break; + case EV_STATE: + fprintf(stderr, "moc: EV_STATE\n"); + last_cmd = EV_STATE; + send_int(srv_sock, CMD_GET_STATE); + break; + case EV_EXIT: + fprintf(stderr,"moc: EV_EXIT\n"); + break; + case EV_BITRATE: + fprintf(stderr, "moc: EV_BITRATE\n"); + break; + case EV_RATE: + fprintf(stderr, "moc: EV_RATE\n"); + break; + case EV_CHANNELS: + printf("moc: EV_CHANNELS\n"); + break; + case EV_SRV_ERROR: + printf("moc: EV_SRV_ERROR\n"); + break; + case EV_OPTIONS: + printf("moc: EV_OPTIONS\n"); + break; + case EV_SEND_PLIST: + case EV_PLIST_ADD: + case EV_PLIST_CLEAR: + case EV_PLIST_DEL: + case EV_PLIST_MOVE: + printf("moc: EV_PLAYLIST\n"); + case EV_TAGS: + last_cmd = EV_TAGS; + printf("moc: EV_TAGS\n"); + break; + case EV_STATUS_MSG: + printf("moc: EV_STATUS_MSG\n"); + printf("moc: status update: %s\n", get_str(srv_sock)); + break; + case EV_MIXER_CHANGE: + printf("moc: EV_MIXER_CHANGE\n"); + break; + case EV_FILE_TAGS: + printf("moc: EV_FILE_TAGS\n"); + + if (!(data->filename = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving filename\n"); + } + + if (!(data->title = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving title\n"); + } + + if (!(data->artist = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving artist\n"); + } + + if (!(data->album = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving album\n"); + } + + if (!get_int(srv_sock, &data->track)) { + fprintf(stderr, "Error while receiving track\n"); + } + + if (!get_int(srv_sock, &data->time)) { + fprintf(stderr, "Error while receiving time\n"); + } + + if (!get_int(srv_sock, &data->filled)) { + fprintf(stderr, "Error while receiving filled\n"); + } + + break; + case EV_AVG_BITRATE: + printf("moc: EV_AVG_BITRATE\n"); + break; + case EV_AUDIO_START: + printf("moc: AUDIO START\n"); + break; + case EV_AUDIO_STOP: + printf("moc: AUDIO STOP\n"); + break; + default: + fprintf(stderr, "Unknown event: 0x%02x!\n", event); + break; + } + } +} +void *moc_loop2(void *input) { + + struct moc *data = (struct moc*)input; + + int srv_sock = -1; + + srv_sock = moc_server_connect(); + if(srv_sock == -1) { + pthread_mutex_lock(&data->lock); + data->status = FAILED_TO_CONNECT; + pthread_mutex_unlock(&data->lock); + pthread_exit(NULL); + } + data->status = CONNECTED; + + while(1) { + + int state = 0; + int ev = 0; + + while(ev != EV_DATA) { + send_int(srv_sock, CMD_GET_STATE); + if (!get_int(srv_sock, &ev)) { + fprintf(stderr, "Can't get data from the server.\n"); + } + fprintf(stderr, "EVENT: 0x%02x\n", ev); + } + + if(!get_int(srv_sock, &state)) { + fprintf(stderr, "Can't get state from the server!\n"); + } + fprintf(stderr, "Got state%d\n", state); + + pthread_mutex_lock(&data->lock); + if(state == STATE_PLAY) { + data->state = PLAYING; + fprintf(stderr, "moc: playing\n"); + } + if(state == STATE_PAUSE) { + data->state = PAUSED; + fprintf(stderr, "moc: paused\n"); + } + if(state == STATE_STOP) { + data->state = STOPPED; + fprintf(stderr, "moc: stopped\n"); + } + pthread_mutex_unlock(&data->lock); + continue; + + char *file = get_curr_file(srv_sock); + fprintf(stderr, "moc: filename: %s\n", file); + + send_int(srv_sock, CMD_GET_FILE_TAGS); + send_str(srv_sock, file); + send_int(srv_sock,TAGS_COMMENTS | TAGS_TIME); + + // filename first. + if (!(data->filename = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving filename\n"); + } + + if (!(data->title = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving title\n"); + } + + if (!(data->artist = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving artist\n"); + } + + if (!(data->album = get_str(srv_sock))) { + fprintf(stderr, "Error while receiving album\n"); + } + + if (!get_int(srv_sock, &data->track)) { + fprintf(stderr, "Error while receiving track\n"); + } + + if (!get_int(srv_sock, &data->time)) { + fprintf(stderr, "Error while receiving time\n"); + } + + if (!get_int(srv_sock, &data->filled)) { + fprintf(stderr, "Error while receiving filled\n"); + } + + + + fprintf(stderr, "moc: end switch\n"); + + pthread_mutex_unlock(&data->lock); + fprintf(stderr, "end lock\n"); + } + + fprintf(stderr, "moc: end of loop\n"); + return NULL; +} + +extern struct moc *moc_init() { + + struct moc *moc = (struct moc*)malloc(sizeof(struct moc)); + moc->filename = NULL; + moc->title = NULL; + moc->album = NULL; + moc->artist = NULL; + moc->time = -1; + pthread_t thread_id; + + if (pthread_mutex_init(&moc->lock, NULL) != 0) { + printf("\n mutex init has failed\n"); + return NULL; + } + moc->status = INITIALIZING; + + pthread_create(&thread_id, NULL, moc_loop, (void *)moc); + return moc; +} |