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

Revision:
0:5490b791ee3d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Mon Sep 20 06:47:33 2010 +0000
@@ -0,0 +1,645 @@
+
+// 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;
+             }
+        }
+    }
+    
+}