diff options
author | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-08-27 20:10:35 +0000 |
---|---|---|
committer | blogic <blogic@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-08-27 20:10:35 +0000 |
commit | e1c9572b15974ffa98eff8b8f4bd77444d44dbd9 (patch) | |
tree | e77ccd710367d8139ccc8be6145e15c42600060e /package/fonera-mp3/src/lib | |
parent | 45c87d131f673fa7cfa2abf68b937b50e06145c9 (diff) |
added libjson-c. added driver, webinterface and userspace daemon for the
fonera mp3-hack
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@8509 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/fonera-mp3/src/lib')
20 files changed, 1925 insertions, 0 deletions
diff --git a/package/fonera-mp3/src/lib/mp3.h b/package/fonera-mp3/src/lib/mp3.h new file mode 100644 index 000000000..77db813c9 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3.h @@ -0,0 +1,35 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#include "mp3_file.h" +#include "mp3_stream.h" +#include "mp3_nix_socket.h" +#include "mp3_tcp_socket.h" +#include "mp3_common.h" +#include "mp3_statemachine.h" +#include "mp3_socket_parser.h" +#include "mp3_misc.h" +#include "mp3_playtime.h" +#include "mp3_states.h" + +void state_event(int event, EVENT_PARAM *param); +EVENT_PARAM* state_new_event(unsigned char *text, int numeric); diff --git a/package/fonera-mp3/src/lib/mp3_common.c b/package/fonera-mp3/src/lib/mp3_common.c new file mode 100644 index 000000000..3cdb0b8fc --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_common.c @@ -0,0 +1,121 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <fcntl.h> + +#include "mp3.h" + +static int mp3_fd; +static int mp3_frequency_val = CRYSTAL12288; +unsigned int volume = 0x3030; + +int mp3_open_port(unsigned char *port_name){ + int fd; + if ((fd = open(port_name, O_RDWR)) < 0) { + printf("Error whilst opening %s\n", port_name); + return -1; + } + return fd; +}; + +void mp3_set_frequency(unsigned int crystal_frequency){ + mp3_frequency_val = crystal_frequency; +}; + +int mp3_init(void){ + mp3_fd = mp3_open_port("/dev/mp3"); + if(mp3_fd < 1){ + return 0; + }; + ioctl(mp3_fd, IOCTL_MP3_INIT, mp3_frequency_val); + return 1; +}; + +void mp3_shutdown(void){ + close(mp3_fd); +}; + +void mp3_bass(unsigned char t_freq, unsigned char t_amp, + unsigned char b_freq, unsigned char b_amp){ + unsigned int val; + if(t_amp > 0xf){ + t_amp = 0xf; + }; + if(b_amp > 0xf){ + b_amp = 0xf; + }; + val = t_amp; + val <<= 4; + val += t_freq; + val <<= 4; + val += b_amp; + val <<= 4; + val += b_freq; + ioctl(mp3_fd, IOCTL_MP3_BASS, val); +}; + +void mp3_reset(void){ + ioctl(mp3_fd, IOCTL_MP3_CLEARBUFFER, 0); + ioctl(mp3_fd, IOCTL_MP3_RESET, mp3_frequency_val); + ioctl(mp3_fd, IOCTL_MP3_SETVOLUME, volume); +}; + +unsigned char mp3_send_data_to_buffer(MP3_DATA mp3_data){ + return write(mp3_fd, (void*)&mp3_data, sizeof(MP3_DATA)); +}; + +unsigned char mp3_play(void){ + return ioctl(mp3_fd, IOCTL_MP3_PLAY, 0); +}; + +void mp3_stop(void){ + ioctl(mp3_fd, IOCTL_MP3_CLEARBUFFER, 0); +}; + +void mp3_beep(unsigned char freq, unsigned int ms){ + MP3_BEEP mp3_beep_; + mp3_beep_.freq = freq; + mp3_beep_.ms = ms; + ioctl(mp3_fd, IOCTL_MP3_BEEP, &mp3_beep_); +}; + +void mp3_set_volume(unsigned char left, unsigned char right){ + volume = left; + volume <<= 8; + volume += right; + + ioctl(mp3_fd, IOCTL_MP3_SETVOLUME, volume); +}; + +unsigned char mp3_buffer_finished(void){ + return ioctl(mp3_fd, IOCTL_MP3_END_REACHED, 0); +}; + +unsigned char mp3_get_audio_data(AUDIO_DATA *audio_data){ + return ioctl(mp3_fd, IOCTL_MP3_GETAUDIODATA, audio_data); +}; + + diff --git a/package/fonera-mp3/src/lib/mp3_common.h b/package/fonera-mp3/src/lib/mp3_common.h new file mode 100644 index 000000000..d1eb0fa56 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_common.h @@ -0,0 +1,72 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#define MP3_CHUNK_SIZE 4096 +#define MP3_BUFFERING 0 +#define MP3_PLAYING 1 +#define MP3_BUFFER_FINISHED 2 +#define MP3_PLAY_FINISHED 3 + +typedef struct _MP3_DATA{ + unsigned char mp3[MP3_CHUNK_SIZE]; + unsigned char state; +} MP3_DATA; + + +#define IOCTL_MP3_INIT 0x01 +#define IOCTL_MP3_RESET 0x02 +#define IOCTL_MP3_SETVOLUME 0x03 +#define IOCTL_MP3_GETVOLUME 0x04 +typedef struct _AUDIO_DATA{ + unsigned int bitrate; + unsigned int sample_rate; + unsigned char is_stereo; +}AUDIO_DATA; +#define IOCTL_MP3_GETAUDIODATA 0x05 + +#define IOCTL_MP3_CLEARBUFFER 0x06 +#define IOCTL_MP3_PLAY 0x07 +typedef struct _MP3_BEEP{ + unsigned char freq; + unsigned int ms; +} MP3_BEEP; +#define IOCTL_MP3_BEEP 0x08 +#define IOCTL_MP3_END_REACHED 0x09 + +#define IOCTL_MP3_BASS 0x10 + +#define CRYSTAL12288 0x9800 +#define CRYSTAL24576 0x0 + +#define MP3_OK 0 +#define MP3_ERROR 1 +#define MP3_END 2 + +void mp3_reset(void); +unsigned char mp3_send_data_to_buffer(MP3_DATA mp3_data); +unsigned char mp3_buffer_finished(void); +unsigned char mp3_play(void); +void mp3_stop(void); +int mp3_init(void); +void mp3_bass(unsigned char t_freq, unsigned char t_amp, + unsigned char b_freq, unsigned char b_amp); +void mp3_set_volume(unsigned char left, unsigned char right); diff --git a/package/fonera-mp3/src/lib/mp3_file.c b/package/fonera-mp3/src/lib/mp3_file.c new file mode 100644 index 000000000..2dbed0ffc --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_file.c @@ -0,0 +1,191 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> + +#include "mp3.h" + + +#define MP3_PRE_BUFFER_COUNT ((128 * 1024) / MP3_CHUNK_SIZE) + + +typedef struct _MP3_FILE { + unsigned char filename[2048]; + MP3_DATA mp3_data; + FILE *fd; + unsigned char file_end_found; + MP3_FILE_ID3 *id3; +} MP3_FILE; + +static MP3_FILE mp3_file; + +void mp3_load_id3(FILE *fp){ + unsigned char *buf = malloc(1024); + + mp3_file.id3->album[0] = '\0'; + mp3_file.id3->artist[0] = '\0'; + mp3_file.id3->track[0] = '\0'; + + + fgets(buf, 1024, fp); + if( (buf[0] == 'I') && + (buf[1] == 'D') && + (buf[2] == '3')){ + unsigned int id3_size; + unsigned int i; + unsigned int id3_version = buf[3]; + id3_version <<= 8; + id3_version += buf[4]; + + id3_size = 0; + + for(i = 0; i<4; i++){ + id3_size += buf[5 + i]; + id3_size <<= 7; + }; + if(id3_version>>8 == 3){ + unsigned int id3_pos = 10; + unsigned int id3_tag_size; + unsigned char tag_name[5]; + unsigned char tag_data[257]; + tag_name[4] = '\0'; + tag_data[256] = '\0'; + unsigned int count = 0; + while(count < 10){ + strncpy(tag_name, &buf[id3_pos], 4); + id3_tag_size = buf[id3_pos + 4]; + id3_tag_size <<= 8; + id3_tag_size = buf[id3_pos + 5]; + id3_tag_size <<= 8; + id3_tag_size = buf[id3_pos + 6]; + id3_tag_size <<= 8; + id3_tag_size = buf[id3_pos + 7]; + if(id3_tag_size == 0){ + break; + }; + if(id3_tag_size > 256){ + memcpy(&tag_data[0], &buf[id3_pos + 11] , 256); + } else { + memcpy(&tag_data[0], &buf[id3_pos + 11] , + id3_tag_size -1); + tag_data[id3_tag_size-1] = '\0'; + }; + id3_pos += 10 + id3_tag_size; + if(strcmp(tag_name, "TPE1") == 0){ + strncpy(mp3_file.id3->artist, tag_data, 255); + }; + if(strcmp(tag_name, "TALB") == 0){ + strncpy(mp3_file.id3->album, tag_data, 255); + }; + if(strcmp(tag_name, "TIT2") == 0){ + strncpy(mp3_file.id3->track, tag_data, 255); + }; + if(id3_pos >= id3_size){ + break; + }; + count ++; + }; + }; + printf("ID3 tag found Version 2.%d.%d / size %d\n%s -- %s -- %s\n", + id3_version>>8, + id3_version&0xff, + id3_size, + mp3_file.id3->artist, + mp3_file.id3->album, + mp3_file.id3->track); + } else { + printf("No ID3 Tag was found\n"); + }; + free(buf); +}; + + +int mp3_file_setup(unsigned char *filename, MP3_FILE_ID3 *id3){ + unsigned int i; + mp3_file.id3 = id3; + mp3_file.file_end_found = 0; + strcpy(mp3_file.filename, filename); + mp3_file.fd = fopen(mp3_file.filename, "rb"); + if(!mp3_file.fd){ + mp3_file.fd = 0; + printf("error opening file %s\n", mp3_file.filename); + return MP3_ERROR; + }; + printf("File %s opened Ok\n", mp3_file.filename); + printf("Reading id3 tag\n"); + mp3_load_id3(mp3_file.fd); + fseek(mp3_file.fd, 0, SEEK_SET); + + mp3_reset(); + + printf("Buffering MP3 Data\n"); + mp3_file.mp3_data.state = MP3_BUFFERING; + for(i = 0; i < MP3_PRE_BUFFER_COUNT - 1; i++){ + fread(mp3_file.mp3_data.mp3, MP3_CHUNK_SIZE, 1, mp3_file.fd); + mp3_file.mp3_data.state = MP3_PLAYING; + mp3_send_data_to_buffer(mp3_file.mp3_data); + }; + + printf("Starting to play file : %s\n", mp3_file.filename); + return MP3_OK; +}; + +int mp3_file_handle(void){ + unsigned char transmit_success = 1; + if (!feof(mp3_file.fd)) { + fread(mp3_file.mp3_data.mp3, MP3_CHUNK_SIZE, 1, mp3_file.fd); + transmit_success = 0; + while(!transmit_success){ + if(!mp3_send_data_to_buffer(mp3_file.mp3_data)){ + usleep(1); + transmit_success = 0; + } else { + transmit_success = 1; + }; + }; + return MP3_OK; + } else { + if(!mp3_file.file_end_found){ + mp3_file.mp3_data.state = MP3_BUFFER_FINISHED; + mp3_send_data_to_buffer(mp3_file.mp3_data); + printf("File end reached. Wait till kernel buffer has cleared.\n"); + mp3_file.file_end_found = 1; + }; + if(!mp3_buffer_finished()){ + return MP3_OK; + } else { + return MP3_END; + }; + }; +}; + +int mp3_file_cleanup(void){ + if(mp3_file.fd){ + fclose(mp3_file.fd); + }; + return MP3_OK; +}; diff --git a/package/fonera-mp3/src/lib/mp3_file.h b/package/fonera-mp3/src/lib/mp3_file.h new file mode 100644 index 000000000..1bff35209 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_file.h @@ -0,0 +1,31 @@ +/* +* a.lp_mp3 - Open Source Atmel AVR / Fox Board based MP3 Players +* Copyright (c) 2003-2006 K. John '2B|!2B' Crispin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs.... mail john{AT}phrozen.org +* +*/ + +typedef struct _MP3_FILE_ID3 { + unsigned char album[256]; + unsigned char artist[256]; + unsigned char track[256]; +} MP3_FILE_ID3; + +int mp3_file_setup(unsigned char *filename, MP3_FILE_ID3 *id3); +int mp3_file_handle(void); +int mp3_file_cleanup(void); diff --git a/package/fonera-mp3/src/lib/mp3_misc.c b/package/fonera-mp3/src/lib/mp3_misc.c new file mode 100644 index 000000000..9fba30fbb --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_misc.c @@ -0,0 +1,129 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "mp3.h" + +#define TMP_PLS_NAME "/var/tmp.pls" + +char* mp3_shell_run(char *filename, char **args, char *buffer, int length){ + int fd1[2], fd2[2], l; + if((pipe(fd1) !=0) || (pipe(fd2)!=0)){ + return NULL; + } + if (fork() == 0){ + close(fd1[1]); + close(fd2[0]); + if ((dup2(fd1[0], STDIN_FILENO) == -1) + || (dup2(fd2[1], STDOUT_FILENO) == -1)){ + exit(0); + } + close(fd1[0]); + close(fd2[1]); + execvp(filename, args); + printf("ERROR running : %s\n", filename); + exit(0); + } + memset(buffer,'\0',length); + close(fd1[0]); + close(fd2[1]); + close(fd1[1]); + wait(NULL); + if((l = read(fd2[0], buffer, length -1)) == -1){ + printf("read failed"); + return NULL; + } + buffer[l] = '\0'; + close (fd2[2]); + return buffer; +} + +int mp3_pls_get_info(unsigned char *pls_url, unsigned char *url, + unsigned char *path, unsigned int *port){ + int ret = MP3_ERROR; + char *exec_args[6]; + int i; + remove(TMP_PLS_NAME); + for (i = 0; i < 5; i++){ + exec_args[i] = malloc(2048); + } + exec_args[0][0] = '\0'; + strcpy(exec_args[1], "wget"); + strcpy(exec_args[2], pls_url); + strcpy(exec_args[3], "-O"); + strcpy(exec_args[4], TMP_PLS_NAME); + exec_args[5] = NULL; + printf("Getting pls file --> %s \n", exec_args[2]); + if(mp3_shell_run("wget", &exec_args[1], + exec_args[0], 2048)){ + struct stat s; + stat(TMP_PLS_NAME, &s); + if(s.st_size > 0){ + FILE *fp = fopen(TMP_PLS_NAME, "r"); + if(fp > 0){ + unsigned char *data = malloc(2048); + *url = '\0'; + while((!*url) && (!feof(fp))){ + if(fgets(data, 2048, fp) != NULL){ + if(strstr(data, "File")){ + unsigned char *t = strstr(data, "="); + if(t){ + t++; + if(mp3_stream_parse_url(t, url, + path, port) != MP3_OK){ + *url = '\0'; + } + } + } + } + } + fclose(fp); + free(data); + if(*url){ + ret = MP3_OK; + } + } + } + } else { + printf("WGET error\n"); + } + for (i = 0; i < 5; i++){ + free(exec_args[i]); + } + if(ret == MP3_OK){ + printf("Found file valid file in pls\n"); + } else { + printf("Error whilst parsing pls\n"); + } + return ret; +} + + + diff --git a/package/fonera-mp3/src/lib/mp3_misc.h b/package/fonera-mp3/src/lib/mp3_misc.h new file mode 100644 index 000000000..512192112 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_misc.h @@ -0,0 +1,26 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + + +char* mp3_shell_run(char *filename, char **args, char *buffer, int length); +int mp3_pls_get_info(unsigned char *pls_url, unsigned char *url, + unsigned char *path, unsigned int *port); diff --git a/package/fonera-mp3/src/lib/mp3_nix_socket.c b/package/fonera-mp3/src/lib/mp3_nix_socket.c new file mode 100644 index 000000000..964690d64 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_nix_socket.c @@ -0,0 +1,166 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#include <sys/socket.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/poll.h> +#include <stdarg.h> +#include <fcntl.h> +#include "mp3.h" + +#define SOCKET_PATH "/tmp/foxmp3" + +typedef struct _MP3_NIX_SOCKET { + fd_set master; + fd_set clients; + int max; + int listener; +} MP3_NIX_SOCKET; + +static MP3_NIX_SOCKET mp3_nix_socket; + +int mp3_nix_socket_setup(void){ + struct sockaddr_un myaddr; + int yes=1; + int len; + FD_ZERO(&mp3_nix_socket.master); + FD_ZERO(&mp3_nix_socket.clients); + + if ((mp3_nix_socket.listener = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return MP3_ERROR; + } + if (setsockopt(mp3_nix_socket.listener, SOL_SOCKET, + SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt"); + return MP3_ERROR; + } + myaddr.sun_family = AF_UNIX; + strcpy(myaddr.sun_path, SOCKET_PATH); + unlink(myaddr.sun_path); + len = strlen(myaddr.sun_path) + sizeof(myaddr.sun_family); + if (bind(mp3_nix_socket.listener, (struct sockaddr *)&myaddr, len) == -1) { + perror("bind"); + return MP3_ERROR; + } + if (listen(mp3_nix_socket.listener, 3) == -1) { + perror("listen"); + return MP3_ERROR; + } + FD_SET(mp3_nix_socket.listener, &mp3_nix_socket.master); + mp3_nix_socket.max = mp3_nix_socket.listener; + + return MP3_OK; +}; + +int mp3_nix_socket_handle(void){ + struct sockaddr_un remoteaddr; + socklen_t addrlen; + int i; + int newfd; + char buf[1024]; + int nbytes; + char buf_out[1024]; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 0; + mp3_nix_socket.clients = mp3_nix_socket.master; + + if (select(mp3_nix_socket.max + 1, &mp3_nix_socket.clients, + NULL, NULL, &tv) == -1) { + // sometimes the select is interrupted, because of the alarm signal used by the playtime counter + //perror("error whilst selecting socket"); + return MP3_ERROR; + } + for(i = 0; i <= mp3_nix_socket.max; i++) { + if (FD_ISSET(i, &mp3_nix_socket.clients)) { + if (i == mp3_nix_socket.listener) { + addrlen = sizeof(remoteaddr); + if ((newfd = accept(mp3_nix_socket.listener, + (struct sockaddr *)&remoteaddr, + &addrlen)) == -1) { + perror("error whilst accepting socket"); + return MP3_ERROR; + } else { + FD_SET(newfd, &mp3_nix_socket.master); + if (newfd > mp3_nix_socket.max) { + mp3_nix_socket.max = newfd; + } + fcntl(newfd, F_SETFL, O_NONBLOCK); + printf("New socket client on %d\n", newfd); + } + } else { + if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) { + if (nbytes == 0) { + printf("selectserver: socket hung up %d\n", i); + close(i); + FD_CLR(i, &mp3_nix_socket.master); + } else { + printf("error whilst receiving socket %d\n", i); + } + } else { + buf[nbytes] = '\0'; + printf("Got data : %s\n", buf); + mp3_parser_incoming(buf,buf_out); + if(*buf_out != '\0'){ + send(i, buf_out, strlen(buf_out), 0); + } + } + } + } + } + return MP3_OK; +} + +void mp3_nix_socket_write(unsigned char *data, ...){ + unsigned int i; + unsigned char t[2048]; + va_list ap; + + // clear possible dead sockets + mp3_nix_socket_handle(); + memset(t, 0, 2048); + va_start(ap, data); + vsprintf(t, data, ap); + va_end(ap); + printf("Sending data --> %s\n", t); + for(i = 0; i <= mp3_nix_socket.max; i++) { + if (FD_ISSET(i, &mp3_nix_socket.master)) { + if (i != mp3_nix_socket.listener) { + printf("Sending on socket %d\n", i); + send(i, t, strlen(t), 0); + } + } + } + printf("Finished sending\n"); +} + +int mp3_nix_socket_cleanup(void){ + return MP3_OK; +}; + diff --git a/package/fonera-mp3/src/lib/mp3_nix_socket.h b/package/fonera-mp3/src/lib/mp3_nix_socket.h new file mode 100644 index 000000000..d97f82ce6 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_nix_socket.h @@ -0,0 +1,25 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ +int mp3_nix_socket_setup(void); +int mp3_nix_socket_handle(void); +int mp3_nix_socket_cleanup(void); +void mp3_nix_socket_write(unsigned char *data, ...); diff --git a/package/fonera-mp3/src/lib/mp3_playtime.c b/package/fonera-mp3/src/lib/mp3_playtime.c new file mode 100644 index 000000000..89325943f --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_playtime.c @@ -0,0 +1,61 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + + +#include <unistd.h> +#include <signal.h> +#include <stdio.h> +#include "mp3.h" + + +static MP3_PLAYTIME *mp3_playtime; + +void sig_alarm_handler(int sig){ + alarm(1); + mp3_playtime->playtime++; + mp3_playtime->playtime_secs++; + if(mp3_playtime->playtime_secs > 59){ + mp3_playtime->playtime_mins++; + mp3_playtime->playtime_secs = 0; + } + state_generic_event(MP3_EVENT_GENERIC_PLAYTIME, 0, NULL); +}; + +void mp3_playtime_init(MP3_PLAYTIME *playtime){ + mp3_playtime = playtime; + signal(SIGALRM, sig_alarm_handler); +}; + +void mp3_playtime_start(void){ + mp3_playtime->playtime = 0; + mp3_playtime->playtime_secs = 0; + mp3_playtime->playtime_mins = 0; + alarm(1); + state_generic_event(MP3_EVENT_GENERIC_PLAYTIME, 0, NULL); +}; + +void mp3_playtime_stop(void){ + alarm(0); + mp3_playtime->playtime = 0; + mp3_playtime->playtime_secs = 0; + mp3_playtime->playtime_mins = 0; +}; diff --git a/package/fonera-mp3/src/lib/mp3_playtime.h b/package/fonera-mp3/src/lib/mp3_playtime.h new file mode 100644 index 000000000..618d1224f --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_playtime.h @@ -0,0 +1,33 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + + +typedef struct _MP3_PLAYTIME { + unsigned int playtime; + unsigned int playtime_secs; + unsigned int playtime_mins; +} MP3_PLAYTIME; + +void mp3_playtime_init(MP3_PLAYTIME *playtime); +void mp3_playtime_start(void); +void mp3_playtime_stop(void); + diff --git a/package/fonera-mp3/src/lib/mp3_socket_parser.c b/package/fonera-mp3/src/lib/mp3_socket_parser.c new file mode 100644 index 000000000..1702282e8 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_socket_parser.c @@ -0,0 +1,104 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "mp3.h" + +#define TOKEN_MAX 16 +#define TOKEN_SIZE 256 + +static unsigned char mp3_parser_tokens[TOKEN_MAX][TOKEN_SIZE]; + +int mp3_parser_tokenize(unsigned char *in){ + int i = 0; + char *token = in; + char *tmp; + do { + tmp = strstr(token, " "); + if(tmp){ + *tmp = '\0'; + strcpy(mp3_parser_tokens[i], token); + tmp++; + token = tmp; + } else { + strcpy(mp3_parser_tokens[i], token); + }; + i++; + }while((i < TOKEN_MAX) && (tmp)); + return i; +}; + +extern int state_current; +void mp3_parser_incoming(unsigned char *in, unsigned char *out){ + int c = mp3_parser_tokenize(in); + int ret = 0; + int t1; + if(c){ + printf("Parsing command from frontend app -> %s --- %d tokens\n", in, c); + if((!strcmp(mp3_parser_tokens[0], "PLAY")) && (c == 2)){ + state_event(MP3_EVENT_FILE, state_new_event(mp3_parser_tokens[1], 0)); + ret = 1; + } else if((!strcmp(mp3_parser_tokens[0], "STREAM")) + && (c == 3)){ + if(!strcmp(mp3_parser_tokens[1], "pls")){ + state_event(MP3_EVENT_STREAM, state_new_event(mp3_parser_tokens[2], STREAM_PLS)); + ret = 1; + } else if(!strcmp(mp3_parser_tokens[1], "url")){ + state_event(MP3_EVENT_STREAM, state_new_event(mp3_parser_tokens[2], STREAM_URL)); + ret = 1; + } + } else if((!strcmp(mp3_parser_tokens[0], "VOLUME")) + && (c == 2)){ + t1 = atoi(mp3_parser_tokens[1]); + state_generic_event(MP3_EVENT_GENERIC_VOLUME, t1, NULL); + ret = 1; + } else if((!strcmp(mp3_parser_tokens[0], "STOP")) + && (c == 1)){ + state_event(MP3_EVENT_STOP, NULL); + ret = 1; + } else if((!strcmp(mp3_parser_tokens[0], "STATE")) + && (c == 1)){ + state_generic_event(MP3_EVENT_GENERIC_STATE, 0, out); + return; + } else if((!strcmp(mp3_parser_tokens[0], "BASS")) + && (c == 2)){ + t1 = atoi(mp3_parser_tokens[1]); + state_generic_event(MP3_EVENT_GENERIC_BASS, t1, NULL); + ret = 1; + } + if(ret){ + sprintf(out, "OK\n"); + printf("Command parsed ok.\n"); + } else { + + sprintf(out, "ERROR\n"); + printf("Command parsed with error.\n"); + }; + } else { + printf("Got command from frontend with 0 tokens.\n"); + }; +}; + + diff --git a/package/fonera-mp3/src/lib/mp3_socket_parser.h b/package/fonera-mp3/src/lib/mp3_socket_parser.h new file mode 100644 index 000000000..ebb517939 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_socket_parser.h @@ -0,0 +1,24 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +void mp3_parser_incoming(unsigned char *in, unsigned char *out); + diff --git a/package/fonera-mp3/src/lib/mp3_statemachine.h b/package/fonera-mp3/src/lib/mp3_statemachine.h new file mode 100644 index 000000000..072bb380c --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_statemachine.h @@ -0,0 +1,26 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + + +void mp3_init_statemachine(); +void mp3_statemachine_main_loop(); + diff --git a/package/fonera-mp3/src/lib/mp3_states.c b/package/fonera-mp3/src/lib/mp3_states.c new file mode 100644 index 000000000..f6f03eb87 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_states.c @@ -0,0 +1,224 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/poll.h> +#include <signal.h> +#include "mp3.h" + +typedef struct _MP3_STATE { + struct { + unsigned char volume; + unsigned char bass; + unsigned char treble; + } decoder; + struct { + unsigned char name[2048]; + MP3_FILE_ID3 id3; + } file; + struct { + unsigned char url[2048]; + unsigned char path[256]; + unsigned int port; + } stream; + MP3_PLAYTIME playtime; + } MP3_STATE; + +static MP3_STATE mp3_state; + +void state_startup_enter(int state_last, int event, EVENT_PARAM *param){ + mp3_state.decoder.volume = 0x30; + mp3_state.decoder.bass = 1; + mp3_state.file.name[0] = '\0'; + mp3_state.stream.url[0] = '\0'; + mp3_state.stream.path[0] = '\0'; + mp3_state.stream.port = 0; + mp3_playtime_init(&mp3_state.playtime); + mp3_init(); + mp3_nix_socket_setup(); + mp3_tcp_socket_setup(); +}; + +void state_idle_enter(int state_last, int event, EVENT_PARAM *param){ + mp3_stop(); + mp3_playtime_stop(); +}; + +void state_file_startup_enter(int state_last, int event, EVENT_PARAM *param){ + if(mp3_file_setup(param->text, &mp3_state.file.id3) == MP3_OK){ + strcpy(mp3_state.file.name, param->text); + } else { + state_event(MP3_EVENT_ERROR, NULL); + }; +}; + +void state_file_startup_leave(int state_new, int event){ + if(state_new == MP3_STATE_FILE_HANDLE){ + mp3_play(); + mp3_playtime_start(); + mp3_nix_socket_write("START FILE\n"); + }; +}; + +void state_file_handle_enter(int state_last, int event, EVENT_PARAM *param){ + int ret = mp3_file_handle(); + if(ret == MP3_ERROR){ + state_event(MP3_EVENT_ERROR, NULL); + }; + if(ret == MP3_END){ + state_event(MP3_EVENT_END, NULL); + }; + +}; + +void state_file_handle_leave(int state_new, int event){ + if(state_new != MP3_STATE_FILE_HANDLE){ + mp3_playtime_stop(); + mp3_file_cleanup(); + mp3_nix_socket_write("STOP FILE\n"); + }; +}; + +void state_stream_startup_enter(int state_last, int event, EVENT_PARAM *param){ + if(mp3_stream_setup(param->text, param->numeric, mp3_state.stream.url, + mp3_state.stream.path, &mp3_state.stream.port) != MP3_OK){ + state_event(MP3_EVENT_ERROR, NULL); + }; +}; + +void state_stream_startup_leave(int state_new, int event){ + if(state_new == MP3_STATE_STREAM_HANDLE){ + mp3_play(); + mp3_playtime_start(); + mp3_nix_socket_write("START STREAM\n"); + }; +}; + +void state_stream_handle_enter(int state_last, int event, EVENT_PARAM *param){ + if(mp3_stream_handle() == MP3_ERROR){ + state_event(MP3_EVENT_ERROR, NULL); + } +}; + +void state_stream_handle_leave(int state_new, int event){ + if(state_new != MP3_STATE_STREAM_HANDLE){ + mp3_stream_cleanup(); + mp3_playtime_stop(); + mp3_nix_socket_write("STOP STREAM\n"); + } +}; + +extern STATE states[MAX_STATE_COUNT]; +void state_error_enter(int state_last, int event, EVENT_PARAM *param){ + if(param){ + printf("Error in state %s -> %s\n", states[state_last].name, param->text); + mp3_nix_socket_write("ERROR Error in state %s -> %s\n", states[state_last].name, param->text); + } else { + printf("Unknown error in state %s\n", states[state_last].name); + }; +}; + +void state_shutdown_enter(int state_last, int event, EVENT_PARAM *param){ + printf("Entering state SHUTDOWN ...\n"); + printf("Shutting down player ...\n"); + mp3_nix_socket_cleanup(); + mp3_tcp_socket_cleanup(); + printf("Quitting statemachine ...\n"); + exit(0); +}; +extern int state_current; +void state_generic_event(unsigned int event, unsigned char in_int, + unsigned char *out_uchar){ + switch(event){ + case MP3_EVENT_GENERIC_VOLUME: + mp3_state.decoder.volume = in_int; + mp3_set_volume(mp3_state.decoder.volume, + mp3_state.decoder.volume); + mp3_nix_socket_write("VOLUME %d\n", mp3_state.decoder.volume); + break; + case MP3_EVENT_GENERIC_BASS: + mp3_state.decoder.bass = in_int; + mp3_bass(8, mp3_state.decoder.treble, + 8, mp3_state.decoder.bass); + mp3_nix_socket_write("BASS %d\n", mp3_state.decoder.bass); + break; + case MP3_EVENT_GENERIC_STATE: + out_uchar[0] = '\0'; + sprintf(out_uchar, "%sVOLUME %d\n", + out_uchar, mp3_state.decoder.volume); + sprintf(out_uchar, "%sBASS %d\n", + out_uchar, mp3_state.decoder.bass); + sprintf(out_uchar, "%sTREBLE %d\n", + out_uchar, mp3_state.decoder.treble); + sprintf(out_uchar, "%sPLAYTIME %d\n", + out_uchar, + (mp3_state.playtime.playtime_mins * 60) + mp3_state.playtime.playtime_secs); + switch(state_current){ + case MP3_STATE_IDLE: + sprintf(out_uchar, "%sSTATE MP3_STATE_IDLE\n", out_uchar); + break; + case MP3_STATE_FILE_START: + case MP3_STATE_FILE_HANDLE: + sprintf(out_uchar, "%sFILE %s\n", out_uchar, + mp3_state.file.name); + if(strlen(mp3_state.file.id3.artist)){ + sprintf(out_uchar, "%sID3_ARTIST %s\n", + out_uchar, + mp3_state.file.id3.artist); + }; + if(strlen(mp3_state.file.id3.album)){ + sprintf(out_uchar, "%sID3_ALBUM %s\n", + out_uchar, + mp3_state.file.id3.album); + }; + if(strlen(mp3_state.file.id3.album)){ + sprintf(out_uchar, "%sID3_TRACK %s\n", + out_uchar, + mp3_state.file.id3.track); + }; + sprintf(out_uchar, "%sSTATE MP3_STATE_FILE\n", out_uchar); + break; + case MP3_STATE_STREAM_START: + case MP3_STATE_STREAM_HANDLE: + sprintf(out_uchar, "%sSTREAM %s:%d%s\n", out_uchar, + mp3_state.stream.url, + mp3_state.stream.port, + mp3_state.stream.path); + sprintf(out_uchar, "%sSTATE MP3_STATE_STREAM\n", out_uchar); + break; + default: + sprintf(out_uchar, "STATE Unkonwn\n"); + break; + }; + sprintf(out_uchar, "%sOK\n", out_uchar); + break; + case MP3_EVENT_GENERIC_PLAYTIME: + // printf("%02d:%02d\n", mp3_state.playtime.playtime_mins, + // mp3_state.playtime.playtime_secs); + break; + default: + break; + }; +}; + diff --git a/package/fonera-mp3/src/lib/mp3_states.h b/package/fonera-mp3/src/lib/mp3_states.h new file mode 100644 index 000000000..073c28bb1 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_states.h @@ -0,0 +1,114 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +typedef struct _EVENT { + unsigned char *name; +} EVENT; + +typedef struct _EVENT_PARAM { + int numeric; + unsigned char *text; +} EVENT_PARAM; + +typedef void (*STATE_ENTER)(int state_last, int event, EVENT_PARAM *param); +typedef void (*STATE_LEAVE)(int state_new, int event); + +typedef struct _STATE { + unsigned char *name; + STATE_ENTER enter; + STATE_LEAVE leave; +} STATE; + +typedef struct _TRANSITION { + int old_state; + int event; + int new_state; +} TRANSITION; + +// a = id, b = name, c = enter, d = leave +#define STATE_ADD(a, b, c, d) { \ + if((a > 0) && (a < MAX_STATE_COUNT)){ \ + states[a].name = strdup(b); \ + states[a].enter = c; \ + states[a].leave = d; \ + } \ + } + +// a = old, b = event, c = new +#define TRANSITION_ADD(a, b, c) { \ + if((transition_count >= 0) && \ + (transition_count < MAX_TRANSITION_COUNT)){ \ + transitions[transition_count].old_state = a; \ + transitions[transition_count].event = b; \ + transitions[transition_count].new_state = c; \ + transition_count++; \ + } \ + } + +// a = id, b = name +#define EVENT_ADD(a, b) { \ + if((a > 0) && (a < MAX_EVENT_COUNT)){ \ + events[a].name = strdup(b); \ + } \ + } +#define MAX_EVENT_COUNT 20 +#define MAX_STATE_COUNT 20 +#define MAX_TRANSITION_COUNT 60 + +#define MP3_STATE_NONE 0 +#define MP3_STATE_STARTUP 1 +#define MP3_STATE_IDLE 2 +#define MP3_STATE_FILE_START 3 +#define MP3_STATE_FILE_HANDLE 4 +#define MP3_STATE_STREAM_START 5 +#define MP3_STATE_STREAM_HANDLE 6 +#define MP3_STATE_ERROR 7 +#define MP3_STATE_SHUTDOWN 8 +#define MP3_STATE_DEFAULT 9 + +#define MP3_EVENT_TIMEOUT 1 +#define MP3_EVENT_FILE 2 +#define MP3_EVENT_STREAM 3 +#define MP3_EVENT_STOP 4 +#define MP3_EVENT_ERROR 5 +#define MP3_EVENT_SHUTDOWN 6 +#define MP3_EVENT_END 7 + +#define MP3_EVENT_GENERIC_VOLUME 0 +#define MP3_EVENT_GENERIC_STATE 1 +#define MP3_EVENT_GENERIC_BASS 2 +#define MP3_EVENT_GENERIC_PLAYTIME 3 + +void state_startup_enter(int state_last, int event, EVENT_PARAM *param); +void state_idle_enter(int state_last, int event, EVENT_PARAM *param); +void state_file_startup_enter(int state_last, int event, EVENT_PARAM *param); +void state_file_startup_leave(int state_new, int event); +void state_file_handle_enter(int state_last, int event, EVENT_PARAM *param); +void state_file_handle_leave(int state_new, int event); +void state_stream_startup_enter(int state_last, int event, EVENT_PARAM *param); +void state_stream_startup_leave(int state_last, int event); +void state_stream_handle_enter(int state_last, int event, EVENT_PARAM *param); +void state_stream_handle_leave(int state_new, int event); +void state_error_enter(int state_last, int event, EVENT_PARAM *param); +void state_shutdown_enter(int state_last, int event, EVENT_PARAM *param); +void state_generic_event(unsigned int event, unsigned char in_int, + unsigned char *out_uchar); diff --git a/package/fonera-mp3/src/lib/mp3_stream.c b/package/fonera-mp3/src/lib/mp3_stream.c new file mode 100644 index 000000000..0dd3437e3 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_stream.c @@ -0,0 +1,340 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <netdb.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <sys/time.h> +#include <sys/poll.h> + +#include "mp3.h" + +typedef struct _MP3_STREAM { + unsigned char buf[MAX_PACKET_SIZE + 1]; + int sockfd; + unsigned char mp3_buffer[MAX_BUFFER_SIZE]; + unsigned long int mp3_buffer_write_pos; + unsigned long int mp3_buffer_read_pos; + unsigned long int mp3_data_in_buffer; + unsigned char transmit_success; + unsigned int buffer_error; + MP3_DATA mp3_data; + unsigned int numbytes; + unsigned int metainterval; +} MP3_STREAM; + +static MP3_STREAM mp3_stream; + +int connect_timeout (int sfd, struct sockaddr *addr, int addrlen, + struct timeval *timeout) { + struct timeval sv; + int svlen = sizeof sv; + int ret; + + if (!timeout) { + return connect (sfd, addr, addrlen); + }; + if (getsockopt (sfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&sv, &svlen) < 0) { + return -1; + }; + if (setsockopt (sfd, SOL_SOCKET, SO_SNDTIMEO, timeout,sizeof *timeout) < 0) { + return -1; + }; + ret = connect (sfd, addr, addrlen); + setsockopt (sfd, SOL_SOCKET, SO_SNDTIMEO, (char *)&sv, sizeof sv); + + return ret; +} + +int mp3_stream_parse_url(unsigned char *url, unsigned char *ip, + unsigned char *path, unsigned int *port){ + int len = strlen(url) - 1; + while(((url[len] == '\n')||(url[len] == ' ')) && (len > 0)){ + url[len] = '\0'; + len--; + }; + ip[0] = '\0'; + printf("Parsing stream url : %s\n", url); + unsigned char *http = strstr(url, "http://"); + *port = 80; + if(http){ + url = http + 7; + unsigned char *p = strstr(url, ":"); + if(p){ + *p = '\0'; + p ++; + strcpy(ip, url); + *port = atoi(p); + } + unsigned char *p2 = strstr((p)?(p):(url), "/"); + if(p2){ + strcpy(path, p2); + *p2 = '\0'; + if(!p){ + strcpy(ip, url); + } + + } else { + strcpy(path, "/"); + }; + printf("ip -> %s\nport -> %d\npath -> %s\n", ip, *port, path); + return MP3_OK; + }; + return MP3_ERROR; +}; + +int mp3_stream_get_url(unsigned char *url, unsigned int type, + unsigned char *ip, unsigned int *port, unsigned char *path){ + if(type == STREAM_PLS){ + if(mp3_pls_get_info(url, ip, path, port) == MP3_OK){ + return MP3_OK; + }; + } else if(type == STREAM_URL){ + if(mp3_stream_parse_url(url, ip, path, port) == MP3_OK){ + return MP3_OK; + }; + }; + return MP3_ERROR; +}; + +int mp3_stream_setup(unsigned char *url, unsigned int type, unsigned char *ip, + unsigned char *path, unsigned int *port){ + struct hostent *he; + struct sockaddr_in their_addr; + unsigned int error = 0; + if(mp3_stream_get_url(url, type, ip, port, path) == MP3_ERROR){ + return MP3_ERROR; + }; + + mp3_stream.mp3_buffer_write_pos = 0; + mp3_stream.mp3_buffer_read_pos = 0; + mp3_stream.mp3_data_in_buffer = 0; + mp3_stream.transmit_success = 1; + mp3_stream.buffer_error = 0; + mp3_stream.metainterval = 0; + + mp3_reset(); + + if ((he=gethostbyname(ip)) == NULL) { + perror("Error in gethostbyname. Wrong url/ip ?"); + return MP3_ERROR; + } + if ((mp3_stream.sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + perror("Error opening stream socket"); + return MP3_ERROR; + } + + their_addr.sin_family = AF_INET; + their_addr.sin_port = htons(*port); + their_addr.sin_addr = *((struct in_addr *)he->h_addr); + memset(&(their_addr.sin_zero), '\0', 8); + + struct timeval tv; + tv.tv_sec = 4; + tv.tv_usec = 0; + + if (connect_timeout(mp3_stream.sockfd, (struct sockaddr *)&their_addr, + sizeof(struct sockaddr), &tv) == -1) { + perror("connect"); + return MP3_ERROR; + } + + unsigned char icy_request[1024]; + sprintf(icy_request, + "GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: A.LP-MP3\r\nAccept: */*\r\nicy-metadata:0\r\n\r\n", + path, + ip); + printf("Sending request :\n%s\n", icy_request); + send(mp3_stream.sockfd, icy_request, strlen(icy_request), 0); + //wait 200 ms ??!? some icecast servers seem to not push data to us fast enough ?!?!? + poll(0,0,200); + if ((mp3_stream.numbytes=recv(mp3_stream.sockfd, mp3_stream.buf, MAX_PACKET_SIZE-1, 0)) == -1) { + perror("recv"); + return MP3_ERROR; + } + mp3_stream.buf[mp3_stream.numbytes] = '\0'; + printf("numbytes = %d\n", mp3_stream.numbytes); + printf("------\n%s\n---------\n", mp3_stream.buf); + unsigned char *p = strstr(mp3_stream.buf, "\r\n\r\n"); + if(p) { + *p = '\0'; + p += 4; + } else { + printf("funky p error in stream.c\n"); + } + printf("Received: \n%s\n", mp3_stream.buf); + if(((unsigned char*)strstr(mp3_stream.buf, "ICY 200 OK") != mp3_stream.buf) && + ((unsigned char*)strstr(mp3_stream.buf, "HTTP/1.1 200 OK") != mp3_stream.buf) && + ((unsigned char*)strstr(mp3_stream.buf, "HTTP/1.0 200 OK") != mp3_stream.buf)) { + return MP3_ERROR; + }; + int p_buf = p - mp3_stream.buf; + unsigned char *p2; + p2 = strstr(mp3_stream.buf, "icy-metaint:"); + if(p2){ + p2 = strstr(p2, ":"); + p2++; + unsigned char *p3 = strstr(p2, "\r"); + *p3 = '\0'; + mp3_stream.metainterval = atoi(p2); + printf("META INT == %d\n", mp3_stream.metainterval); + } + + printf("starting to buffer\n"); + memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], + p, p_buf); + mp3_stream.mp3_buffer_write_pos += p_buf; + mp3_stream.mp3_data_in_buffer += p_buf; + + while(mp3_stream.mp3_data_in_buffer + (unsigned long int)MAX_PACKET_SIZE + < (unsigned long int)MAX_BUFFER_SIZE){ + if ((mp3_stream.numbytes=recv(mp3_stream.sockfd, mp3_stream.buf, + MAX_PACKET_SIZE-1, 0)) == -1) { + perror("disconnected"); + printf("disconntected\n"); + return MP3_ERROR; + } + + if(mp3_stream.numbytes == 0){ + sleep(1); + if(++error > 3){ + perror("disconnected"); + printf("disconntected\n"); + return MP3_ERROR; + } + } + + memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], + mp3_stream.buf, mp3_stream.numbytes); + mp3_stream.mp3_buffer_write_pos += mp3_stream.numbytes; + mp3_stream.mp3_data_in_buffer += mp3_stream.numbytes; + printf("%ld ", mp3_stream.mp3_data_in_buffer); + fflush(stdout); + + }; + printf("\n"); + mp3_stream.mp3_data.state = MP3_PLAYING; + while(mp3_stream.mp3_data_in_buffer >= 2 * MP3_CHUNK_SIZE){ + memcpy(mp3_stream.mp3_data.mp3, + &mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_read_pos], + MP3_CHUNK_SIZE); + mp3_send_data_to_buffer(mp3_stream.mp3_data); + mp3_stream.mp3_buffer_read_pos += MP3_CHUNK_SIZE; + mp3_stream.mp3_data_in_buffer -= MP3_CHUNK_SIZE; + }; + + printf("Starting to play stream\n"); + return MP3_OK; +} + +static int max_recv_errors = 10; +int mp3_stream_handle(void){ + if(MAX_BUFFER_SIZE >= mp3_stream.mp3_data_in_buffer + MAX_PACKET_SIZE){ + struct pollfd ufds; + ufds.fd = mp3_stream.sockfd; + ufds.events = POLLIN|POLLHUP; + + if(poll(&ufds, 1, 2000) > 0){ + max_recv_errors = 10; + if ((mp3_stream.numbytes=recv(mp3_stream.sockfd, mp3_stream.buf, MAX_PACKET_SIZE-1, 0)) == -1) { + perror("recv"); + } + if((mp3_stream.numbytes != EAGAIN)&& (mp3_stream.numbytes != -1)){ + if(mp3_stream.mp3_buffer_write_pos + mp3_stream.numbytes <= MAX_BUFFER_SIZE){ + memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], + mp3_stream.buf, mp3_stream.numbytes); + mp3_stream.mp3_buffer_write_pos += mp3_stream.numbytes; + mp3_stream.mp3_data_in_buffer += mp3_stream.numbytes; + if(mp3_stream.mp3_buffer_write_pos == MAX_BUFFER_SIZE){ + mp3_stream.mp3_buffer_write_pos = 0; + }; + } else { + unsigned int buffer_offset = MAX_BUFFER_SIZE - mp3_stream.mp3_buffer_write_pos; + memcpy(&mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_write_pos], + mp3_stream.buf, buffer_offset); + mp3_stream.mp3_buffer_write_pos = + mp3_stream.numbytes - buffer_offset; + memcpy(&mp3_stream.mp3_buffer[0], &mp3_stream.buf[buffer_offset], + mp3_stream.mp3_buffer_write_pos); + mp3_stream.mp3_data_in_buffer += mp3_stream.numbytes; + }; + }; + } else { + max_recv_errors--; + if(max_recv_errors == 0){ + printf("recv error\n"); + return MP3_ERROR; + }; + }; + } + + if(mp3_stream.mp3_data_in_buffer < MP3_CHUNK_SIZE){ + printf("radio_buffer is empty\n"); + mp3_stream.buffer_error ++; + if(mp3_stream.buffer_error > MAX_BUFFER_ERROR){ + return MP3_ERROR; + }; + } else { + mp3_stream.buffer_error = 0; + do{ + if(mp3_stream.transmit_success){ + if(MAX_BUFFER_SIZE >= mp3_stream.mp3_buffer_read_pos + MP3_CHUNK_SIZE){ + memcpy(mp3_stream.mp3_data.mp3, + &mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_read_pos], MP3_CHUNK_SIZE); + mp3_stream.mp3_buffer_read_pos += MP3_CHUNK_SIZE; + mp3_stream.mp3_data_in_buffer -= MP3_CHUNK_SIZE; + if(mp3_stream.mp3_buffer_read_pos == MAX_BUFFER_SIZE){ + mp3_stream.mp3_buffer_read_pos = 0; + }; + + } else { + unsigned int buffer_offset = MAX_BUFFER_SIZE - mp3_stream.mp3_buffer_read_pos; + memcpy(mp3_stream.mp3_data.mp3, + &mp3_stream.mp3_buffer[mp3_stream.mp3_buffer_read_pos], + buffer_offset); + mp3_stream.mp3_buffer_read_pos = MP3_CHUNK_SIZE - buffer_offset; + memcpy(&mp3_stream.mp3_data.mp3[buffer_offset], mp3_stream.mp3_buffer, + mp3_stream.mp3_buffer_read_pos); + }; + } + if(!mp3_send_data_to_buffer(mp3_stream.mp3_data)){ + mp3_stream.transmit_success = 0; + } else { + mp3_stream.transmit_success = 1; + }; + } while((mp3_stream.transmit_success)&&(mp3_stream.mp3_data_in_buffer > MP3_CHUNK_SIZE)); + }; + return MP3_OK; +}; + +int mp3_stream_cleanup(void){ + close(mp3_stream.sockfd); + return MP3_OK; +} diff --git a/package/fonera-mp3/src/lib/mp3_stream.h b/package/fonera-mp3/src/lib/mp3_stream.h new file mode 100644 index 000000000..d8e09ba5e --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_stream.h @@ -0,0 +1,34 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ + +#define MAX_PACKET_SIZE 1500 +#define MAX_BUFFER_SIZE (64*2048) +#define MAX_BUFFER_ERROR 128 + +#define STREAM_URL 0 +#define STREAM_PLS 1 +int mp3_stream_setup(unsigned char *url, unsigned int type, unsigned char *ip, + unsigned char *path, unsigned int *port); +int mp3_stream_handle(void); +int mp3_stream_cleanup(void); +int mp3_stream_parse_url(unsigned char *url, unsigned char *ip, + unsigned char *path, unsigned int *port); diff --git a/package/fonera-mp3/src/lib/mp3_tcp_socket.c b/package/fonera-mp3/src/lib/mp3_tcp_socket.c new file mode 100644 index 000000000..89c8997fe --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_tcp_socket.c @@ -0,0 +1,145 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/wait.h> +#include <signal.h> +#include <sys/poll.h> + +#include "mp3.h" + +#define SOCKET_PORT 369 +#define BACKLOG 10 + +typedef struct _MP3_TCP_SOCKET { + fd_set master; + fd_set clients; + int max; + int listener; +} MP3_TCP_SOCKET; + +static MP3_TCP_SOCKET mp3_tcp_socket; + +int mp3_tcp_socket_setup(void){ + struct sockaddr_in myaddr; + int yes=1; + FD_ZERO(&mp3_tcp_socket.master); + FD_ZERO(&mp3_tcp_socket.clients); + + if ((mp3_tcp_socket.listener = socket(PF_INET, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return MP3_ERROR; + } + if (setsockopt(mp3_tcp_socket.listener, SOL_SOCKET, + SO_REUSEADDR, &yes, sizeof(int)) == -1) { + perror("setsockopt"); + return MP3_ERROR; + } + myaddr.sin_family = AF_INET; + myaddr.sin_port = htons(SOCKET_PORT); + myaddr.sin_addr.s_addr = INADDR_ANY; + memset(&(myaddr.sin_zero), '\0', 8); + if (bind(mp3_tcp_socket.listener, (struct sockaddr *)&myaddr, sizeof(struct sockaddr)) == -1) { + perror("bind"); + return MP3_ERROR; + } + if (listen(mp3_tcp_socket.listener, 3) == -1) { + perror("listen"); + return MP3_ERROR; + } + FD_SET(mp3_tcp_socket.listener, &mp3_tcp_socket.master); + mp3_tcp_socket.max = mp3_tcp_socket.listener; + printf("Started tcp socket on port %d\n", SOCKET_PORT); + return MP3_OK; +}; + +int mp3_tcp_socket_handle(void){ + struct sockaddr_in remoteaddr; + socklen_t addrlen; + int i; + int newfd; + char buf[1024]; + int nbytes; + char buf_out[1024]; + struct timeval tv; + + tv.tv_sec = 0; + tv.tv_usec = 0; + mp3_tcp_socket.clients = mp3_tcp_socket.master; + + if (select(mp3_tcp_socket.max + 1, &mp3_tcp_socket.clients, + NULL, NULL, &tv) == -1) { + return MP3_ERROR; + } + for(i = 0; i <= mp3_tcp_socket.max; i++) { + if (FD_ISSET(i, &mp3_tcp_socket.clients)) { + if (i == mp3_tcp_socket.listener) { + addrlen = sizeof(remoteaddr); + if ((newfd = accept(mp3_tcp_socket.listener, + (struct sockaddr *)&remoteaddr, + &addrlen)) == -1) { + perror("error whilst accepting socket"); + return MP3_ERROR; + } else { + printf("New TCP connection from %s\n", + inet_ntoa(remoteaddr.sin_addr)); + FD_SET(newfd, &mp3_tcp_socket.master); + if (newfd > mp3_tcp_socket.max) { + mp3_tcp_socket.max = newfd; + } + } + } else { + if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) { + if (nbytes == 0) { + perror("selectserver: socket hung up\n"); + } else { + perror("error whilst receiving socket"); + } + close(i); + FD_CLR(i, &mp3_tcp_socket.master); + } else { + buf[nbytes] = '\0'; + printf("Got data : %s\n", buf); + mp3_parser_incoming(buf,buf_out); + if(*buf_out != '\0'){ + send(i, buf_out, strlen(buf_out), 0); + } + close(i); + FD_CLR(i, &mp3_tcp_socket.master); + } + } + } + } + return MP3_OK; +}; + +int mp3_tcp_socket_cleanup(void){ + return MP3_OK; +}; + diff --git a/package/fonera-mp3/src/lib/mp3_tcp_socket.h b/package/fonera-mp3/src/lib/mp3_tcp_socket.h new file mode 100644 index 000000000..f4b5dc422 --- /dev/null +++ b/package/fonera-mp3/src/lib/mp3_tcp_socket.h @@ -0,0 +1,24 @@ +/* +* FOXMP3 +* Copyright (c) 2006 acmesystems.it - john@acmesystems.it +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA02111-1307USA +* +* Feedback, Bugs... info@acmesystems.it +* +*/ +int mp3_tcp_socket_setup(void); +int mp3_tcp_socket_handle(void); +int mp3_tcp_socket_cleanup(void); |