#ifndef __SHOUTCAST_OUTPUT_H__ #define __SHOUTCAST_OUTPUT_H__ #include #include #include #include #include #include #include "c_serial_jobmanager.h" #include "../Components/wac_network/wac_network_connection_api.h" #include #include "../Encoders/c_encoder.h" #include "../Encoders/c_encoder_mp3dll.h" #include "../lame/include/lame.h" #include "../lame/libmp3lame/lame_global_flags.h" #include "../uvAuth21/uvAuth21.h" #define UV_SYNC_BYTE 0x5A #define UV_RESERVED 0x00 #define UV_END 0x00 #define UV_END_LEN 1 #define UV_HEADER_LEN 6 #define UV_META_LEN 6 #define UV_FRAME_LEN 16384 #define UV_MAX_DATA_LEN (UV_FRAME_LEN - UV_HEADER_LEN - UV_END_LEN) #define UV_MAX_META_LEN (UV_FRAME_LEN - UV_HEADER_LEN - UV_META_LEN - UV_END_LEN) #define UV_MAX_META_FRAGMENTS 32 #define UV_MAX_TOTAL_META_LEN (UV_MAX_META_LEN * UV_MAX_META_FRAGMENTS) typedef struct { char* name; bool parent; bool children; } SCgenres; static SCgenres genres[] = {{"Alternative", true, true}, {"Adult Alternative", false}, {"Britpop", false}, {"Classic Alternative", false}, {"College", false}, {"Dancepunk", false}, {"Dream Pop", false}, {"Emo", false}, {"Goth", false}, {"Grunge", false}, {"Hardcore", false}, {"Indie Pop", false}, {"Indie Rock", false}, {"Industrial", false}, {"LoFi", false}, {"Modern Rock", false}, {"New Wave", false}, {"Noise Pop", false}, {"Post Punk", false}, {"Power Pop", false}, {"Punk", false}, {"Ska", false}, {"Xtreme", false}, {"Blues", true, true}, {"Acoustic Blues", false}, {"Cajun and Zydeco", false}, {"Chicago Blues", false}, {"Contemporary Blues", false}, {"Country Blues", false}, {"Delta Blues", false}, {"Electric Blues", false}, {"Classical", true, true}, {"Baroque", false}, {"Chamber", false}, {"Choral", false}, {"Classical Period", false}, {"Early Classical", false}, {"Impressionist", false}, {"Modern", false}, {"Opera", false}, {"Piano", false}, {"Romantic", false}, {"Symphony", false}, {"Country", true, true}, {"Alt Country", false}, {"Americana", false}, {"Bluegrass", false}, {"Classic Country", false}, {"Contemporary Bluegrass", false}, {"Contemporary Country", false}, {"Honky Tonk", false}, {"Hot Country Hits", false}, {"Western", false}, {"Decades", true, true}, {"30s", false}, {"40s", false}, {"50s", false}, {"60s", false}, {"70s", false}, {"80s", false}, {"90s", false}, {"00s", false}, {"Easy Listening", true, true}, {"Exotica", false}, {"Light Rock", false}, {"Lounge", false}, {"Orchestral Pop", false}, {"Polka", false}, {"Space Age Pop", false}, {"Electronic", true, true}, {"Acid House", false}, {"Ambient", false}, {"Big Beat", false}, {"Breakbeat", false}, {"Dance", false}, {"Demo", false}, {"Disco", false}, {"Downtempo", false}, {"Drum and Bass", false}, {"Dubstep", false}, {"Electro", false}, {"Garage", false}, {"Hard House", false}, {"House", false}, {"IDM", false}, {"Jungle", false}, {"Progressive", false}, {"Techno", false}, {"Trance", false}, {"Tribal", false}, {"Trip Hop", false}, {"Folk", true, true}, {"Alternative Folk", false}, {"Contemporary Folk", false}, {"Folk Rock", false}, {"New Acoustic", false}, {"Old Time", false}, {"Traditional Folk", false}, {"World Folk", false}, {"Inspirational", true, true}, {"Christian", false}, {"Christian Metal", false}, {"Christian Rap", false}, {"Christian Rock", false}, {"Classic Christian", false}, {"Contemporary Gospel", false}, {"Gospel", false}, {"Praise and Worship", false}, {"Sermons and Services", false}, {"Southern Gospel", false}, {"Traditional Gospel", false}, {"International", true, true}, {"African", false}, {"Afrikaans", false}, {"Arabic", false}, {"Asian", false}, {"Bollywood", false}, {"Brazilian", false}, {"Caribbean", false}, {"Celtic", false}, {"Creole", false}, {"European", false}, {"Filipino", false}, {"French", false}, {"German", false}, {"Greek", false}, {"Hawaiian and Pacific", false}, {"Hebrew", false}, {"Hindi", false}, {"Indian", false}, {"Islamic", false}, {"Japanese", false}, {"Klezmer", false}, {"Korean", false}, {"Mediterranean", false}, {"Middle Eastern", false}, {"North American", false}, {"Russian", false}, {"Soca", false}, {"South American", false}, {"Tamil", false}, {"Turkish", false}, {"Worldbeat", false}, {"Zouk", false}, {"Jazz", true, true}, {"Acid Jazz", false}, {"Avant Garde", false}, {"Big Band", false}, {"Bop", false}, {"Classic Jazz", false}, {"Cool Jazz", false}, {"Fusion", false}, {"Hard Bop", false}, {"Latin Jazz", false}, {"Smooth Jazz", false}, {"Swing", false}, {"Vocal Jazz", false}, {"World Fusion", false}, {"Latin", true, true}, {"Bachata", false}, {"Banda", false}, {"Bossa Nova", false}, {"Cumbia", false}, {"Flamenco", false}, {"Latin Dance", false}, {"Latin Pop", false}, {"Latin Rap and Hip Hop", false}, {"Latin Rock", false}, {"Mariachi", false}, {"Merengue", false}, {"Ranchera", false}, {"Reggaeton", false}, {"Regional Mexican", false}, {"Salsa", false}, {"Samba", false}, {"Tango", false}, {"Tejano", false}, {"Tropicalia", false}, {"Metal", true, true}, {"Black Metal", false}, {"Classic Metal", false}, {"Death Metal", false}, {"Extreme Metal", false}, {"Grindcore", false}, {"Hair Metal", false}, {"Heavy Metal", false}, {"Metalcore", false}, {"Power Metal", false}, {"Progressive Metal", false}, {"Rap Metal", false}, {"Thrash Metal", false}, {"Misc", true, false}, {"New Age", true, true}, {"Environmental", false}, {"Ethnic Fusion", false}, {"Healing", false}, {"Meditation", false}, {"Spiritual", false}, {"Pop", true, true}, {"Adult Contemporary", false}, {"Barbershop", false}, {"Bubblegum Pop", false}, {"Dance Pop", false}, {"Idols", false}, {"JPOP", false}, {"KPOP", false}, {"Oldies", false}, {"Soft Rock", false}, {"Teen Pop", false}, {"Top 40", false}, {"World Pop", false}, {"Public Radio", true, true}, {"College", false}, {"News", false}, {"Sports", false}, {"Talk", false}, {"Weather", false}, {"R&B and Urban", true, false}, {"Classic R&B", false}, {"Contemporary R&B", false}, {"Doo Wop", false}, {"Funk", false}, {"Motown", false}, {"Neo Soul", false}, {"Quiet Storm", false}, {"Soul", false}, {"Urban Contemporary", false}, {"Rap", true, true}, {"Alternative Rap", false}, {"Dirty South", false}, {"East Coast Rap", false}, {"Freestyle", false}, {"Gangsta Rap", false}, {"Hip Hop", false}, {"Mixtapes", false}, {"Old School", false}, {"Turntablism", false}, {"Underground Hip Hop", false}, {"West Coast Rap", false}, {"Reggae", true, true}, {"Contemporary Reggae", false}, {"Dancehall", false}, {"Dub", false}, {"Pop Reggae", false}, {"Ragga", false}, {"Reggae Roots", false}, {"Rock Steady", false}, {"Rock", true, true}, {"Adult Album Alternative", false}, {"British Invasion", false}, {"Celtic Rock", false}, {"Classic Rock", false}, {"Garage Rock", false}, {"Glam", false}, {"Hard Rock", false}, {"Jam Bands", false}, {"JROCK", false}, {"Piano Rock", false}, {"Prog Rock", false}, {"Psychedelic", false}, {"Rock & Roll", false}, {"Rockabilly", false}, {"Singer and Songwriter", false}, {"Surf", false}, {"Seasonal and Holiday", true, true}, {"Anniversary", false}, {"Birthday", false}, {"Christmas", false}, {"Halloween", false}, {"Hanukkah", false}, {"Honeymoon", false}, {"Kwanzaa", false}, {"Valentine", false}, {"Wedding", false}, {"Winter", false}, {"Soundtracks", true, true}, {"Anime", false}, {"Kids", false}, {"Original Score", false}, {"Showtunes", false}, {"Video Game Music", false}, {"Talk", true, true}, {"BlogTalk", false}, {"Comedy", false}, {"Community", false}, {"Educational", false}, {"Government", false}, {"News", false}, {"Old Time Radio", false}, {"Other Talk", false}, {"Political", false}, {"Scanner", false}, {"Spoken Word", false}, {"Sports", false}, {"Technology", false}, {"Themes", true, true}, {"Adult", false}, {"Best Of", false}, {"Chill", false}, {"Eclectic", false}, {"Experimental", false}, {"Female", false}, {"Heartache", false}, {"Instrumental", false}, {"LGBT", false}, {"Love and Romance", false}, {"Party Mix", false}, {"Patriotic", false}, {"Rainy Day Mix", false}, {"Reality", false}, {"Sexy", false}, {"Shuffle", false}, {"Travel Mix", false}, {"Tribute", false}, {"Trippy", false}, {"Work Mix", false} }; // pulled from nmrCommon\intTypes.h typedef unsigned char __uint8; typedef unsigned short __uint16; typedef unsigned int __uint32; typedef unsigned long long __uint64; #pragma pack(push,1) // this structure should be 16384 bytes in total size // and is defined in full size so that we know its ok struct uv2xHdr { // uvox2 message __uint8 sync; __uint8 qos; __uint16 msgType; __uint16 msgLen; __uint8 m_data[UV_MAX_DATA_LEN]; __uint8 end; }; struct uv2xMetadataHdr { /* uvox 2 metadata header */ __uint8 sync; __uint8 qos; __uint16 msgType; __uint16 msgLen; __uint16 id; /* ID (cookie) identifying a metadata package */ __uint16 span; /* Span of messages in the metadata package being assembled */ __uint16 index; /* Index of the message in the metadata package being assembled */ __uint8 m_data[UV_MAX_META_LEN]; __uint8 end; }; #pragma pack(pop) #define MSG_AUTH 0x1001 #define MSG_BROADCAST_SETUP 0x1002 #define MSG_NEGOTIATE_BUFFER_SIZE 0x1003 #define MSG_STANDBY 0x1004 #define MSG_TERMINATE 0x1005 #define MSG_FLUSH_CACHED_METADATA 0x1006 #define MSG_LISTENER_AUTHENTICATION 0x1007 #define MSG_MAX_PAYLOAD_SIZE 0x1008 #define MSG_CIPHER 0x1009 #define MSG_MIME_TYPE 0x1040 #define MSG_FILE_TRANSFER_BEGIN 0x1050 #define MSG_FILE_TRANSFER_DATA 0x1051 #define MSG_BROADCAST_INTERRUPTION 0x2001 #define MSG_BROADCAST_TERMINATE 0x2002 #define MSG_ICYNAME 0x1100 #define MSG_ICYGENRE 0x1101 #define MSG_ICYURL 0x1102 #define MSG_ICYPUB 0x1103 #define MSG_METADATA_CONTENTINFO 0x3000 #define MSG_METADATA_URL 0x3001 #define MSG_METADATA_XML 0x3901 #define MSG_METADATA_XML_NEW 0x3902 // only id the start of the album art type as it's variable #define MSG_METADATA_ALBUMART 0x4000 #define MSG_METADATA_STATION_ART 0x0000 #define MSG_METADATA_PLAYING_ART 0x0100 /* 0x4 0x0xx Station logo 0x4 0x1xx Album art 00 = image/jpeg 01 = image/png 02 = image/bmp 03 = image/gif */ #define MSG_METADATA_TIMEREMAINING 0x5001 #define MP3_DATA 0x7000 #define VLB_DATA 0x8000 #define AAC_LC_DATA 0x8001 #define AACP_DATA 0x8003 #define OGG_DATA 0x8004 struct T_OUTPUT_CONFIG { char Name[32]; wchar_t DisplayName[32]; char UserID[256]; char Address[1024]; u_short Port; char StationID[12]; char Password[256]; // 4 - 8 for 1.8.2 char cipherkey[64]; // sc2 cipherkey int AutoRecon; int ReconTime; char Description[1024]; char ServerURL[2048]; char Genre[256]; char ICQ[128]; char AIM[1024]; char IRC[1024]; int Public; int doTitleUpdate; int protocol; int protocol_retry; char Now[1024]; char Next[1024]; }; #define DEFAULT_ENCODER (C_ENCODER *)(-1) #define OM_ENCODE 1 #define OM_OUTPUT 2 #define OM_OTHER 4 #define OM_ALL (OM_ENCODE | OM_OUTPUT | OM_OTHER) enum OUTPUTTYPE { OUTTYPE_SOURCE, OUTTYPE_TITLE, }; typedef unsigned long ARGB32; struct T_OUTPUT_TITLE { wchar_t *Title; wchar_t *Song; wchar_t *Album; wchar_t *Artist; wchar_t *Genre; wchar_t *Comment; wchar_t *Year; std::vector NextList; void *APIC[2]; int APICLength[2]; int APICType[2]; T_OUTPUT_TITLE() : Title(0), Song(0), Album(0), Artist(0), Genre(0), Comment(0), Year(0) { memset(APIC, 0, sizeof(void *) * 2); memset(APICLength, 0, sizeof(int) * 2); memset(APICType, 0, sizeof(int) * 2); } ~T_OUTPUT_TITLE() { if (Title) { free(Title); Title = 0; } if (Song) { free(Song); Song = 0; } if (Album) { free(Album); Album = 0; } if (Artist) { free(Artist); Artist = 0; } if (Genre) { free(Genre); Genre = 0; } if (Comment) { free(Comment); Comment = 0; } if (Year) { free(Year); Year = 0; } } }; struct T_OUTPUT_INFO { unsigned int BytesSent; // how many bytes of content we've sent clock_t ConnectionTime; // time a socket connection occurred int Version; // server version int Caps; // server capabilities int Reconnect; // flag for the reconnection algorithm int ReconnectTime; // value used in conjunction with the reconnection algorithm int Succeeded; // had at least one successful connection (for reconnection alg.) -1 = password failure int last_state; // using this as a means to allow for closing on errors but able to show a better message int Switching; // if we're doing an automatic protocol version change (from v2 to v1) char *ErrorMsg; time_t ConnectedAt; int meta_cached; int art_cached[2]; unsigned short art_index[2]; unsigned short art_cached_span[2]; int art_cached_length[2]; // metadata information about the stream, etc wchar_t *Title; std::vector NextList; wchar_t *Song; wchar_t *Album; wchar_t *Artist; wchar_t *Genre; wchar_t *Comment; wchar_t *Year; void *APIC[2]; int APICLength[2]; int APICType[2]; T_OUTPUT_INFO() : BytesSent(0), ConnectionTime(0), Version(0), Caps(0), Reconnect(0), ReconnectTime(0), Succeeded(0), last_state(0), Switching(0), ErrorMsg(0), ConnectedAt(0), meta_cached(0), Title(0), Song(0), Album(0), Artist(0), Genre(0), Comment(0), Year(0) { memset(art_cached, 0, sizeof(int) * 2); memset(art_index, 0, sizeof(unsigned short) * 2); memset(art_cached_span, 0, sizeof(unsigned short) * 2); memset(art_cached_length, 0, sizeof(int) * 2); memset(APIC, 0, sizeof(void *) * 2); memset(APICLength, 0, sizeof(int) * 2); memset(APICType, 0, sizeof(int) * 2); } ~T_OUTPUT_INFO() { if (Title) { free(Title); Title = 0; } if (Song) { free(Song); Song = 0; } if (Album) { free(Album); Album = 0; } if (Artist) { free(Artist); Artist = 0; } if (Genre) { free(Genre); Genre = 0; } if (Comment) { free(Comment); Comment = 0; } if (Year) { free(Year); Year = 0; } if (Succeeded == -2 && ErrorMsg) { free(ErrorMsg); ErrorMsg = 0; } } }; struct T_OUTPUT { int Connection; // using this for the title update callback so the correct instance is updated void (*TitleCallback)(const int Connection, const int Mode); api_connection *Output; enum OUTPUTTYPE Type; int Bitrate; // this shouldn't be here, but it's the only way we can tell the shoutcast server the bitrate char *ContentType; // neither should this int SlowClose; // set to 1 to wait until all data is sent before closing the connection T_OUTPUT_CONFIG *Config; T_OUTPUT_INFO Info; T_OUTPUT() : Connection(0), TitleCallback(0), Output(0), Type(OUTTYPE_SOURCE), Bitrate(0), ContentType(0), SlowClose(0), Config(0) {} }; enum OUTPUTSTATE { OUT_ERROR, // not a true state, but is returned when GetState() is called with an invalid connection handle OUT_DISCONNECTED, OUT_CONNECT, OUT_REQUEST_CIPHER, OUT_RECV_CIPHER, OUT_SENDAUTH, OUT_RECVAUTHRESPONSE, OUT_SEND_MIME, OUT_RECV_MIME, OUT_SEND_BITRATE, OUT_RECV_BITRATE, OUT_SEND_BUFSIZE, OUT_RECV_BUFSIZE, OUT_SEND_MAX, OUT_RECV_MAX, OUT_SENDYP, OUT_SEND_INITFLUSH, OUT_RECV_INITFLUSH, OUT_SEND_INITSTANDBY, OUT_RECV_INITSTANDBY, /*OUT_SEND_INTRO, OUT_RECV_INTRO, OUT_SEND_BACKUP, OUT_RECV_BACKUP,*/ OUT_SENDCONTENT, OUT_DISCONNECT, OUT_RECONNECT, OUT_TITLESENDUPDATE, OUT_FAIL_CIPHER, OUT_SEND_METADATA, OUT_SEND_ARTWORK, }; static void *mutex; class SHOUTCAST_OUTPUT { private: C_ENCODER *Encoder; int IOwnEncoder; C_SERIAL_JOBMANAGER OutputManager; T_OUTPUT_TITLE metadata; protected: static int Output_Disconnected(int state, int last_state, T_OUTPUT *userData); static int Output_Connect(int state, int last_state, T_OUTPUT *userData); static int Output_Request_Cipher(int state, int last_state, T_OUTPUT *userData); static int Output_Receive_Cipher(int state, int last_state, T_OUTPUT *userData); static int Output_SendAuth(int state, int last_state, T_OUTPUT *userData); static int Output_RecvAuthResponse(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Mime(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_Mime(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Bitrate(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_Bitrate(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Buf_Size(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_Buf_Size(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Max_Size(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_Max_Size(int state, int last_state, T_OUTPUT *userData); static int Output_DUMMY(int state, int last_state, T_OUTPUT *userData); static int Output_SendYP(int state, int last_state, T_OUTPUT *userData); static int Output_Send_InitFlush(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_InitFlush(int state, int last_state, T_OUTPUT *userData); static int Output_Send_InitStandby(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_InitStandby(int state, int last_state, T_OUTPUT *userData); /*static int Output_Send_Intro(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_Intro(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Backup(int state, int last_state, T_OUTPUT *userData); static int Output_Recv_Backup(int state, int last_state, T_OUTPUT *userData);*/ static int Output_SendContent(int state, int last_state, T_OUTPUT *userData); static int Output_Disconnect(int state, int last_state, T_OUTPUT *userData); static int Output_Reconnect(int state, int last_state, T_OUTPUT *userData); static int Output_Title_SendUpdate(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Metadata(int state, int last_state, T_OUTPUT *userData); static int Output_Send_Artwork(int state, int last_state, T_OUTPUT *userData); // uvox21 static void createUvoxFrame(int length, char *payload_in, int type, T_OUTPUT *userData); static int createUvoxMetaFrame(int length, char *payload_in, int type, T_OUTPUT *userData, unsigned short id, unsigned short span = 1); static int parseUvoxFrame(char *payload_in, char *payload_out); static int checkUvoxFrameForError(char *pload_out, int state, T_OUTPUT *userData); void (*lame_init)(void); void (*lame_init_params)(lame_global_flags *); int (*lame_encode_buffer_interleaved)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size); int (*lame_encode_flush)(lame_global_flags *, char *mp3buffer, int size); HINSTANCE libinst; public: SHOUTCAST_OUTPUT(); void SetLame(void *init, void *params, void *encode, void *finish); ~SHOUTCAST_OUTPUT(); int Run(int mode = 0, void *Input = NULL, int InputSize = 0, int SaveEncoder = -1); int AddOutput(int Connection, T_OUTPUT_CONFIG *Config, void (*TitleCallback)(const int Connection, const int Mode)=0); void UpdateOutput(int Connection); void RemoveOutput(int Connection); int ConnectOutput(int Connection); int DisconnectOutput(int Connection, int withReconnect = 0, int reconnectTime = -1); // withReconnect of -1 will use the output config's setting void SetEncoder(C_ENCODER *encoder, int takeOwnership = 0); // we will attempt to cache the title information to save on duplication and // also to make it easier for the title to be re-sent on server disconnect void UpdateTitleCache(wchar_t *Title, std::vector NextList, wchar_t *Song, wchar_t *Album, wchar_t *Artist, wchar_t *Genre, wchar_t *Comment, wchar_t* Year, int Connection, bool sendNext); void UpdateTitle(wchar_t *Title, std::vector NextList, int Connection, bool sendNext, bool UseCache = true); void UpdateArtwork(int Connection); void UpdateAlbumArtCache(void* APIC, int APIClength, int APICType, int Connection); int UpdateAlbumArt(int Connection); enum OUTPUTSTATE GetState(int Connection); T_OUTPUT_CONFIG *operator[](int Connection); T_OUTPUT_CONFIG *GetOutput(int Connection); C_ENCODER *GetEncoder(); T_OUTPUT_INFO *GetOutputInfo(int Connection); }; static unsigned short mid = 1; #ifdef _DEBUG #define DEBUG_STATE OutputDebugString(__FUNCTION__); OutputDebugString("\r\n"); #else #define DEBUG_STATE #endif #define STATE userData->Info.last_state = last_state #define LOCK if(WaitForSingleObject(mutex,INFINITE) == WAIT_OBJECT_0) #define UNLOCK ReleaseMutex(mutex); extern char sourceVersion[64]; extern HWND hMainDLG; #endif // !__SHOUTCAST_OUTPUT_H__