Stream Ripper This program will download MP3 data from SHOUTcast stream and save mp3 file in microSD card. Metadata also will be saved as another file when stream includes metadata.
Dependencies: EthernetNetIf mbed SDFileSystem
main.cpp
- Committer:
- xshige
- Date:
- 2010-09-20
- Revision:
- 0:5490b791ee3d
File content as of revision 0:5490b791ee3d:
// Stream Ripper // // This program will download MP3 data from SHOUTcast stream // and save mp3 file in microSD card. // Metadata also will be saved as another file // when stream includes metadata. // // written by: xshige // 2010/9/20 version 0.5 // Set Recording Size for Downloading // 1MB means around 1 minutes play (assume 128kbps) #define RECSIZE (15*1000000) // define this if you want metadata stream #define METADATA #include "mbed.h" #include "EthernetNetIf.h" #include "TCPSocket.h" #include "NTPClient.h" // define this if you use DHCP #define DHCP // define this if you use time&date as output file name #define TDFILE #define WRTFILE #ifdef WRTFILE #include "SDFileSystem.h" SDFileSystem sd(p5, p6, p7, p8, "sd"); #endif // maybe we need 4KB at least #define SBUFSIZE (1024*4) //#define SBUFSIZE (1024*8) //#define SBUFSIZE (1024*16) //NG //#define SBUFSIZE (1024*10) //NG //#define TICK_DRIVEN NTPClient ntp; time_t ctTime; /* ---------------- Message Smaples */ // Client Side: // GET /live HTTP/1.0 // Host: gw // Accept: */* // User-Agent: mbed // Icy-MetaData: 1 // Connection: close /* Server#0 Resopose: HTTP/1.0 200 OK Content-Type: audio/mpeg icy-br:96 ice-audio-info: ice-samplerate=44100;ice-bitrate=96;ice-channels=2 icy-br:96 icy-description:www.SoloPianoRadio.com icy-genre:Classical icy-name:Whisperings: Solo Piano Radio icy-pub:1 icy-url:http://www.solopianoradio.com Server: Icecast 2.3.1 icy-metaint:16000 Server#1 Resopose: ICY 200 OK icy-notice1:<BR>This stream requires <a href="http://www.winamp.com/">Winamp</a><BR> icy-notice2:SHOUTcast Distributed Network Audio Server/Linux v1.9.8<BR> icy-name:SMOOTHJAZZ.COM - The Internet's Original Smooth Jazz Radio Station - Live from the Monterey Bay icy-genre:Smooth Jazz icy-url:http://www.smoothjazz.com content-type:audio/mpeg icy-pub:1 icy-metaint:32768 icy-br:128 ------------------------ Notes: * icy-metadata:1 a client can request to have metadata (information about the music - ) included in the stream, by adding this. * ICY [CODE] whereas code refers to http status code (200 - ok, 404 - resource not found, 401 - service unavailable, etc.). * icy-pub - Not sure, believe it indicates if the stream is public or private * icy-metaint - an interval (in bytes) that specifies how often metadata updates will be sent to the client. * icy-br - BitRate, seems mostly informational as most clients encountered seem to support VBR (Variable BitRate). ------------------------- */ #ifdef DHCP EthernetNetIf eth; #else EthernetNetIf eth( IpAddr(192,168,0,25), //IP Address IpAddr(255,255,255,0), //Network Mask IpAddr(192,168,0,1), //Gateway IpAddr(192,168,0,1) //DNS ); #endif DigitalOut led1(LED1, "led1"); DigitalOut led2(LED2, "led2"); DigitalOut led3(LED3, "led3"); DigitalOut led4(LED4, "led4"); TCPSocket sock; char inbuf[2048]; // IP input buffer char* cp; // stream int slen; // stream length unsigned long int tlen=0; // total lenth of downloaded file //------------------------------------------------- class RingBuffer { protected: unsigned char *buffer; int buffersize; int rp; // read index int wp; // write index public: RingBuffer(int bufsiz) { buffersize = bufsiz; buffer = new unsigned char[buffersize]; // memset(buffer, 0, sizeof(char) * buffersize); // reset pointer to make empty rp = 0; wp = 0; }; ~RingBuffer(void) { delete[] buffer; }; int GetByte(unsigned char *cp); int PutByte(unsigned char c); int IsEmpty(void); }; int RingBuffer::GetByte(unsigned char *cp ) { if( rp != wp ) { // not empty *cp = buffer[rp]; rp = (rp + 1) % buffersize; return 0; } else { // empty return -1; } } int RingBuffer::PutByte(unsigned char c ) { int next = (wp + 1) % buffersize; if( next == rp ) { printf("*** Buffer Full ***\r\n" ); // printf("*");//debug return -1; } buffer[wp] = c; wp = next; return 0; } int RingBuffer::IsEmpty(void) { return (rp==wp)?(true):(false); } //----------------- // create RingBuffer RingBuffer sbuf(SBUFSIZE); // char sResponse[800]; // keeps Stream Response char sMetadata[200]; // keeps Stream Metadata char sPrevMetadata[200]; // keeps Previous Stream Metadata // client state enum wSTATE{NOP,PUT_HEADER} WriteState; enum rSTATE{GET_RESPONSE,GET_STREAM} ReadState; // metadata char mbuf[512]; int mbyte; // lenth byte, -1 means this value is not effective int mlen; // metadata length int metaint=0; // metadate interval void initDec(void); // prototype void onTCPSocketEvent(TCPSocketEvent e) { int i,len; switch (e) { case TCPSOCKET_CONNECTED: printf("TCP Socket Connected\r\n"); // this printf disturb socket work correctly // break; // we must comment out ? case TCPSOCKET_WRITEABLE: //Can now write some data... // printf("TCP Socket Writable\r\n"); // debug switch(WriteState) { case PUT_HEADER: const char* str = // "GET /live HTTP/1.0\r\n" // use this if solopiano "GET / HTTP/1.0\r\n" "Host: gw\r\n" "Accept: */*\r\n" "User-Agent: mbed\r\n" #ifdef METADATA "Icy-MetaData: 1\r\n" // send this if you want Metadata #endif // "Connection: close\r\n" "\r\n" ; sock.send(str, strlen(str)); printf("\r\nHEADER:\r\n%s\r\n", str); // display PUT_HEADER WriteState=NOP; ReadState=GET_RESPONSE; break; case NOP: break; } // switch(WriteState) break; case TCPSOCKET_READABLE: //Can now read some data... // printf("TCP Socket Readable\r\n"); // debug switch(ReadState) { case GET_RESPONSE: len=sock.recv(inbuf, sizeof inbuf); cp=strstr(inbuf,"\r\n\r\n"); if (cp==NULL) { inbuf[len]=0; // printf("%s",inbuf); // debug sprintf(sResponse,"%s",inbuf); return; // still read response again } // *cp=0; sprintf(sResponse+strlen(sResponse),"%s\r\n\r\n",inbuf); printf("RESPONSE:\r\n%s",sResponse); // get metaint value cp=strstr(sResponse,"icy-metaint:"); if (cp==NULL) metaint=0; else sscanf(cp+strlen("icy-metaint:"),"%d",&metaint); printf("\r\nmetaint: %d\r\n\r\n",metaint); //debug // i=strlen(inbuf)+4; // bump bitstream right after response ReadState=GET_STREAM; initDec(); // while(i<len) { // write one bye sbuf.PutByte(inbuf[i]);i++; }; return; break; // case GET_STREAM: // receive data ****repeatedly**** while(len=sock.recv(inbuf, sizeof inbuf)) { i=0; // save len bytes while (i<len) { sbuf.PutByte(inbuf[i]);i++; } } // while (len=sock...) return; // get more stream break; // } // switch (ReadState) break;// case TCPSOCKET_CONTIMEOUT: printf("TCP Socket Timeout\r\n"); break; case TCPSOCKET_CONRST: printf("TCP Socket CONRST\r\n"); break; case TCPSOCKET_CONABRT: printf("TCP Socket CONABRT\r\n"); printf("Maybe Server Down...\r\n"); break; case TCPSOCKET_ERROR: printf("TCP Socket Error\r\n"); break; case TCPSOCKET_DISCONNECTED: //Close socket... printf("TCP Socket Disconnected\r\n"); sock.close(); break; }// switch(e) } //-------------------------------- #ifdef WRTFILE FILE *fp, *fp2; char outfile1[32]; char outfile2[32]; #endif void initDec(void) { slen=0;mbyte=-1;mlen=0; #ifdef WRTFILE #ifdef TDFILE // char outfile[32]; // make output file for MP3 ctTime=time(NULL); struct tm *t=localtime(&ctTime); sprintf(outfile1,"/sd/%02d%02d%02d%02d.mp3", // t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min); // printf("%s\r\n",outfile); //debug fp = fopen(outfile1, "wb"); #else fp = fopen("/sd/stream.mp3", "wb"); #endif if(fp == NULL) { error("Could not open file for download\n"); } #ifdef METADATA #ifdef TDFILE // make output file for Metadata sprintf(outfile2,"/sd/%02d%02d%02d%02d.met", // t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min); // printf("%s\r\n",outfile); //debug fp2 = fopen(outfile2, "w"); #else fp2 = fopen("/sd/stream.met", "w"); #endif if(fp2 == NULL) { error("Could not open file for metadata\n"); } ctTime=time(NULL); // get seconds fprintf(fp2,"%s\n%s\n---------------\n", sResponse,ctime(&ctTime)); // write stream information on file #endif // display time&date ctTime = time(NULL); // get seconds printf("%s\r\n", ctime(&ctTime)); // float edt=(RECSIZE)/1000000; if (metaint==0) { printf("Now Downloading(Metadata-less Stream)...\r\n"); printf("Estimated Donwload Time: %3.2f minutes\r\n\r\n",edt); } else { printf("Now Downloading(Stream with Metadata)...\r\n"); printf("Estimated Donwload Time: %3.2f minutes\r\n\r\n",edt); } #endif return; } void sendDec(unsigned char sbyte) { unsigned char wc=sbyte; if (metaint==0) { // we have no metadata in a stream // put one byte to decoder #ifdef WRTFILE fwrite(&wc,1,1,fp); // fputc(sbyte,fp); ///// tlen++; if (tlen==RECSIZE) { fclose(fp); printf("\r\n*** Download Complete.***\r\n"); while(true); // dynamic stop } #endif return; } // if (slen==metaint) { mbyte=sbyte; mlen=0; slen++; return; } // if (mbyte==-1) { // put one byte to decoder #ifdef WRTFILE fwrite(&wc,1,1,fp); // fputc(sbyte,fp); ///// #endif #if 1 if (0!=strcmp(sMetadata,sPrevMetadata)) { printf("Metadata: %s\r\n",sMetadata); // display metadata // printf("mbyte:%d slen:%d tlen:%d\r\n\r\n",mbyte,slen,tlen); // debug printf("tlen:%d\r\n\r\n",tlen); // display total length sprintf(sPrevMetadata,"%s",sMetadata); // copy to previous ///// fclose(fp);fp=fopen(outfile1,"ab"); // switch append mode ///// fclose(fp2);fp2=fopen(outfile2,"a"); fprintf(fp2,"%d\n%s\n",tlen,sMetadata); // save metadata on TXT file } #endif slen++; tlen++; return; } // // comming here when we are in reading metadata if (slen==(metaint+16*mbyte+1)) { // we have all bytes for metadata mbuf[mlen]=0; // make terminator if (0<mbyte) { sprintf(sMetadata,"%s",mbuf); // coy new metadata } #ifdef WRTFILE fwrite(&wc,1,1,fp); // fputc(sbyte,fp); //// tlen++; if (tlen>RECSIZE) { printf("\r\n*** Download Complete.***\r\n"); fclose(fp);fclose(fp2); // display time&date ctTime = time(NULL); // get seconds in UTC printf("%s\r\n", ctime(&ctTime)); while(true); // dynamic stop } #endif // printf("metaint: %d slen:%d\r\n",metaint,slen); //debug mbyte=-1;mlen=0; slen=1; //OK return; } // if (slen==(metaint+16*mbyte+1)) // // coming here still reading metadata // we still read one byte of metadata mbuf[mlen]=sbyte;mlen++; slen++; return; } #ifdef TICK_DRIVEN void TickProcess(void) { int x; for(x=0;x<(SBUFSIEZ/2);x++) { unsigned char sbyte; if (-1==sbuf.GetByte(&sbyte)) { // Net::poll(); return; } sendDec(sbyte); } return; } //----------------------- Ticker tick; #endif int main() { // set_time(); // make debug port Fast Serial pc(USBTX, USBRX); // pc.baud(9600); pc.baud(115200); // pc.baud(230400); // init string sprintf(sResponse,""); // init medatadata string sprintf(sMetadata,"StreamTitle='Currently, Unknown';"); sprintf(sPrevMetadata,""); printf("\r\n"); printf("Setting up...\r\n"); EthernetErr ethErr = eth.setup(); if (ethErr) { printf("Error %d in setup.\r\n", ethErr); return -1; }; // Init State for Read/Write ReadState=GET_RESPONSE; WriteState=PUT_HEADER; //---------------------------------- // setup clock for time stamp // NTPClient ntp; // time_t ctTime; Host timeServer(IpAddr(), 123, "0.uk.pool.ntp.org"); ntp.setTime(timeServer); ctTime = time(NULL); // get seconds in UTC ctTime = ctTime+(3600*9); // convert JST (please change to fit your localtime) set_time(ctTime); // re-setup RTC printf("\r\nTime is setup now (JST): %s\r\n", ctime(&ctTime)); #if 0 // test program for time ctTime=time(NULL); struct tm *t=localtime(&ctTime); printf("%4d %02d/%02d %02d:%02d:%02d\r\n\r\n", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec); #endif //----------------------------- printf("Setup OK\r\n"); // ************** STATION DEFINITIONS ************** //// http://pianosolo.streamguys.net:80/live //Host server(IpAddr(216,246,105,11), 80); //solo piano //Host server(IpAddr(), 80,"pianosolo.streamguys.net"); //solo piano // SMOOTHJAZZ.COM - The Internet's Original Smooth Jazz Radio Station // - Live from the Monterey Bay // Host server(IpAddr(67,213,217,212), 80); //smooth jazz //Folk Alley (( All Folk. All The Time. )) // http://66.225.205.8:8000 Host server(IpAddr(66,225,205,8), 8000); // 1-ONE NATION FM.COM GOSPEL RADIO // http://208.85.240.2:8094 // Host server(IpAddr(208,85,240,2), 8094); // GotRadio - Celti //http://64.62.164.211:3000 // Host server(IpAddr(64,62,164,211), 3000); // AnimeNfo Radio | Serving you the best Anime music! //http://216.18.227.252:8000 // Host server(IpAddr(216,18,227,252), 8000); // ************** end of STATION DEFINITIONS ************** // display IP address and port# of server IpAddr serverIp = server.getIp(); int port = server.getPort(); printf("Connecting... %d.%d.%d.%d:%d\r\n", serverIp[0],serverIp[1],serverIp[2],serverIp[3],port); TCPSocketErr bindErr = sock.connect(server); sock.setOnEvent(&onTCPSocketEvent); Timer tmr; tmr.start(); #ifdef TICK_DRIVEN tick.attach(&TickProcess,0.1); // 100ms tick, this interval depens on SBUFSIZE #endif while (true) { Net::poll(); #ifndef TICK_DRIVEN int x; for(x=0;x<(SBUFSIZE/2);x++) { unsigned char sbyte; if (-1==sbuf.GetByte(&sbyte)) { // Net::poll(); break; } sendDec(sbyte); } #endif if (tmr.read() > 0.05) { tmr.reset(); if (metaint>0) { led4=!led4; //Show that we are alive if ((metaint/4)<slen) led3=1; else led3=0; if ((metaint*2/4)<slen) led2=1; else led2=0; if ((metaint*3/4)<slen) led1=1; else led1=0; } else { led1=!led1; // indicate no metadata led4=0; } } } }