diff options
Diffstat (limited to 'Src/Plugins/DSP/dsp_sc/sc2srclib')
23 files changed, 5940 insertions, 0 deletions
diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h new file mode 100644 index 00000000..d81a8b7c --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h @@ -0,0 +1,411 @@ +//PORTABLE +#ifndef _PTRLIST_H +#define _PTRLIST_H + +#define POS_LAST -1 + +// 1k each, leaving 16 bytes for MALLOC overhead +#define PTRLIST_INCREMENT 252 + +template<class T> +class PtrList +{ +public: + PtrList() + { + nitems = 0; + nslots = 0; + items = NULL; + } + + PtrList( PtrList<T> *other ) + { + nitems = other->nitems; + nslots = other->nslots; + items = (T **)memdup( other->items, nslots * sizeof( T * ) ); + } + + virtual ~PtrList() + { + if ( items ) + free( items ); + } + + int getNumItems() { return nitems; }; + + T *enumItem( int n ) + { + if ( items == NULL ) + return NULL; + + if ( n < 0 ) + return NULL; + + if ( n >= nitems ) + return NULL; + + return items[ n ]; + } + + T *operator[]( int n ) { return enumItem( n ); } + +// this will safely return NULL if 0 items due to enumItems's boundscheck + T *getFirst() { return enumItem( 0 ); } + + T *getLast() { return enumItem( nitems - 1 ); } + + virtual T *addItem( T *item, int pos = POS_LAST ) + { +// ASSERTPR(item != NULL, "can't put NULLs into ptrlists"); +// ASSERT(nitems <= nslots); + if ( items == NULL ) + { + nslots = PTRLIST_INCREMENT; + items = (T **)malloc( sizeof( T * ) * nslots ); + nitems = 0; + } + else if ( nslots == nitems ) + { // time to expand + T **newitems; + nslots += PTRLIST_INCREMENT; + newitems = (T **)malloc( sizeof( T * ) * nslots ); + memcpy( newitems, items, nitems * sizeof( T * ) ); + + if ( items ) + free( items ); + + items = newitems; + } + + _addToList( item ); + if ( pos != POS_LAST ) + moveItem( nitems - 1, pos ); + + return item; + } + + // FG> avoid using this before i tested it more + void moveItem( int from, int to ) + { + if ( from == to ) + return; + + T *ptr = items[ from ]; + + if ( nitems > from + 1 ) // if moving from the end, there is nothing to shift behind our src position + //IMPORTANT note for future ports: This assumes MEMCPY accepts overlapping segments. + memcpy( &items[ from ], &items[ from + 1 ], ( nitems - from ) * sizeof( T * ) ); + + if ( to > from ) + to--; + + if ( nitems > to ) // if moving to the end, there is nothing to shift behind our target position + memcpy( &items[ to + 1 ], &items[ to ], ( nitems - to - 1 ) * sizeof( T * ) ); + + items[ to ] = ptr; + } + + // deletes first instance of a pointer in list, returns how many are left + int delItem( T *item ) + { + int c = 0; + + if ( item == NULL || items == NULL || nitems <= 0 ) + return 0; + + // count occurences of item in items, remember the first one + T **p = items; + int first = -1; + + for ( int i = 0; i < nitems; i++, p++ ) + { + if ( *p == item ) + { + if ( c++ == 0 ) + first = i; + } + } + + // if we found one, remove it + if ( c > 0 ) + { + delByPos( first ); // delByPos is faaast + c--; // decrease count + } + + return c; // returns how many occurences of this item left + } + + // removes all instances of this pointer + void delEveryItem( T *item ) { while ( delItem( item ) ); } + + // removes pointer at specified pos + void delByPos( int pos ) + { + if ( pos < 0 || pos >= nitems ) + return; //JC + + --nitems; + if ( pos == nitems ) + return; // last one? nothing to copy over + + memcpy( items + pos, items + ( pos + 1 ), sizeof( T * ) * ( nitems - pos ) ); // CT:not (nitems-(pos+1)) as nitems has been decremented earlier + } + + // removes last item, leaving pointer alone + void removeLastItem() + { + if ( nitems == 0 || items == NULL ) + return; + + nitems--; // hee hee + } + + // removes all entries, leaving pointers alone + void removeAll() + { + if ( items ) + free( items ); + + items = NULL; + nitems = 0; + nslots = 0; + } + + // removes all entries, calling FREE on the pointers + void freeAll() + { + for ( int i = 0; i < nitems; i++ ) //JC + if ( items ) + free( items[ i ] ); + + removeAll(); + } + + // removes all entries, calling delete on the pointers + void deleteAll() + { + int i; + if ( items == NULL || nitems <= 0 ) + return; //JC + + for ( i = 0; i < nitems; i++ ) + { //JC + delete items[ i ]; + } + + removeAll(); + } + + virtual int haveItem( T *item ) + {// gross-ass linear search to see if we have it + for ( int i = 0; i < nitems; i++ ) //JC + if ( items[ i ] == item ) + return 1; + + return 0; + } + + virtual int searchItem( T *item ) + { // gross-ass linear search to find index of item + for ( int i = 0; i < getNumItems(); i++ ) + if ( items[ i ] == item ) + return i; + + return -1; + } + +protected: + virtual void _addToList( T *item ) { items[ nitems++ ] = item; } + + int nitems, nslots; + T **items; +}; + +template<class T> +class PtrListBaseSorted : public PtrList<T> +{ +public: + virtual T *findItem( char *attrib ) + { +#if 1 + if ( nitems == 0 || items == NULL ) + return NULL; + + int bot = 0, top = nitems - 1; + + for ( int c = 0; c < nitems + 16; c++ ) + { + if ( bot > top ) + return NULL; + + int mid = ( bot + top ) / 2; + int r = compareAttrib( attrib, items[ mid ] ); + + if ( r == 0 ) + return items[ mid ]; + + if ( r < 0 ) + { + top = mid - 1; + } + else + { + bot = mid + 1; + } + } + // ASSERTPR(0, "binary search fucked up"); +#else + for ( int i = 0; i < nitems; i++ ) + { + if ( compareAttrib( attrib, items[ i ] ) == 0 ) + return items[ i ]; + } +#endif + return NULL; + } + +protected: + // comparator for searching -- override + virtual int compareAttrib( char *attrib, T *item ) = 0; + + // comparator for sorting -- override , -1 p1 < p2, 0 eq, 1 p1 > p2 + virtual int compareItem( T *p1, T *p2 ) = 0; +}; + + +#if 0 +// try not to use this +template<class T> +class PtrListSortedInsertion : public PtrListBaseSorted<T> +{ +protected: + virtual void _addToList( T *item ) + { + if ( nitems == 0 ) + { + items[ nitems++ ] = item; + + return; + } + + for ( int i = 0; i < nitems; i++ ) + { + if ( compareItem( items[ i ], item ) == 1 ) + { + T *tmp = items[ i ]; + items[ i ] = item; + for ( int j = i + 1; j < nitems; j++ ) + { + T *tmp2 = items[ j ]; + items[ j ] = tmp; + tmp = tmp2; + } + + items[ nitems++ ] = tmp; + + return; + } + } + + items[ nitems++ ] = item; + + return; + } +}; +#endif + +// a base class to defer sorting until lookup +template<class T> +class PtrListSorted : public PtrListBaseSorted<T> +{ +public: + PtrListSorted() + { + nitems = 0; + nslots = 0; + items = NULL; + need_sorting = 0; + } + + virtual T *addItem( T *item ) + { + need_sorting = 1; + + return PtrList<T>::addItem( item ); + } + + virtual T *findItem( char *attrib ) + { + sort(); + + return PtrListBaseSorted<T>::findItem( attrib ); + } + + int needSort() { return need_sorting; } + + void sort() + { + if ( need_sorting ) + _sort(); + + need_sorting = 0; + } + +private: + int need_sorting; + + virtual void _sort() = 0; +}; + +template<class T> +class PtrListQuickSorted : public PtrListSorted<T> +{ +public: + virtual void _sort() + { + if ( items == NULL || nitems <= 1 ) + return; + + Qsort( 0, nitems - 1 ); + } + + void swap( int a, int b ) + { + T *tmp = items[ a ]; + items[ a ] = items[ b ]; + items[ b ] = tmp; + } + + void Qsort( int lo0, int hi0 ) + { + int lo = lo0, hi = hi0; + if ( hi0 > lo0 ) + { + T *mid = enumItem( ( lo0 + hi0 ) / 2 ); + while ( lo <= hi ) + { + while ( ( lo < hi0 ) && ( compareItem( items[ lo ], mid ) < 0 ) ) + lo++; + + while ( ( hi > lo0 ) && ( compareItem( items[ hi ], mid ) > 0 ) ) + hi--; + + if ( lo <= hi ) + { + swap( lo, hi ); + lo++; + hi--; + } + } + + if ( lo0 < hi ) + Qsort( lo0, hi ); + + if ( lo < hi0 ) + Qsort( lo, hi0 ); + } + } +}; + +#endif diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp new file mode 100644 index 00000000..a2a3f2fe --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp @@ -0,0 +1,95 @@ +#include <windows.h> +#include "c_encoder.h" + +C_ENCODER::C_ENCODER(int ExtInfoSize) { + SetName("Untyped Encoder"); + ExtendedInfoPtr = (T_EncoderIOVals *)malloc(ExtInfoSize); + ExtendedInfoSize = ExtInfoSize; +} + +C_ENCODER::~C_ENCODER() { + Close(); + if(ExtendedInfoPtr && ExtendedInfoSize) free(ExtendedInfoPtr); + ExtendedInfoSize = 0; +} + +void C_ENCODER::Close() { + ClearAttribs(); +} + +void C_ENCODER::SetName(const char *name) { + if (name) lstrcpyn(Name, name, C_ENCODER_NameLen); +} + +char *C_ENCODER::GetName() { + return Name; +} + +void C_ENCODER::Reset() { +} + +void C_ENCODER::ChangeSettings(const void *Settings) { + if(ExtendedInfoPtr && ExtendedInfoSize && Settings && Settings != ExtendedInfoPtr) { + memcpy(ExtendedInfoPtr,Settings,ExtendedInfoSize); + Reset(); + } +} + +void C_ENCODER::Create(const T_EncoderIOVals *Settings, const char *name) { + if(name) SetName(name); + ChangeSettings(Settings); +} + +void C_ENCODER::ClearAttribs() { + for(int i = AttribList.size()-1; i >= 0; i--) { + T_ATTRIB *myAttrib = AttribList[i]; + if(myAttrib->OutputVals) delete[] myAttrib->OutputVals; + } + + //AttribList.deleteAll(); + for (auto attrib : AttribList) + { + delete attrib; + } + AttribList.clear(); +} + +void C_ENCODER::AddAttrib(const char *Text, const void *Attributes) { + T_ATTRIB *myAttrib = new T_ATTRIB; + if(Text!=NULL) { + ::strncpy((char *)&myAttrib->Text,Text,sizeof(myAttrib->Text)); + } else { + ::strncpy((char *)&myAttrib->Text,"<This should never appear here...>",sizeof(myAttrib->Text)); + } + myAttrib->OutputVals = (T_EncoderIOVals *)Attributes; + AttribList.push_back(myAttrib); +} + +int C_ENCODER::Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused) { + if((inputbuf != NULL) && (outputbuf != NULL) && (inputbufsize != 0) && (outputbufsize != 0) && (inputamtused != NULL)) { + int numitems = (inputbufsize > outputbufsize) ? outputbufsize : inputbufsize; + memcpy(outputbuf,inputbuf,numitems); + *inputamtused = numitems; + return numitems; + } + return 0; +} + +int C_ENCODER::GetNumAttribs() { + return AttribList.size(); +} + +int C_ENCODER::EnumAttrib(const int val, T_ATTRIB *buf) { + if((val < 0)||(val >= AttribList.size())||(buf==NULL)) return 0; + T_ATTRIB *myattrib = AttribList[val]; + if(myattrib==NULL) return 0; + ::memcpy(buf,myattrib,sizeof(T_ATTRIB)); + return 1; +} + +char * C_ENCODER::GetContentType() { + + //if(strcmp(this->GetName(), "MP3 Encoder") == 0) + return "audio/mpeg"; + +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h new file mode 100644 index 00000000..04ef9220 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h @@ -0,0 +1,53 @@ +#ifndef __C_ENCODER_H__ +#define __C_ENCODER_H__ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <vector> + +#define C_ENCODER_NameLen 1024 + +struct T_EncoderIOVals { + unsigned int output_bitRate; +}; + +struct T_ATTRIB { + char Text[256]; +void *OutputVals; +}; + +class C_ENCODER { +private: + char Name[C_ENCODER_NameLen]; + std::vector<T_ATTRIB*> AttribList; +protected: + T_EncoderIOVals *ExtendedInfoPtr; + int ExtendedInfoSize; + void SetName(const char *name); + void ClearAttribs(); + void AddAttrib(const char *Text, const void *Attributes); +public: + C_ENCODER(int ExtInfoSize = 0); + virtual ~C_ENCODER(); + + virtual void ChangeSettings(const void *Settings); + virtual void Create(const T_EncoderIOVals *Settings, const char *name = NULL); + virtual void Close(); + virtual void Reset(); + virtual char *GetName(); + virtual void *GetExtInfo(int *ExtInfoSize = NULL) { + if(ExtInfoSize != NULL) *ExtInfoSize = ExtendedInfoSize; + return ExtendedInfoPtr; + } + + virtual char * GetContentType(); + virtual int Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused); /* all values are in BYTES! */ + + virtual int GetNumAttribs(); + virtual int EnumAttrib(const int val, T_ATTRIB *buf); + + virtual bool UseNsvConfig() { return false; } +}; + +#endif /* !__C_ENCODER_H__ */
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp new file mode 100644 index 00000000..62a09e89 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp @@ -0,0 +1,84 @@ +#include "c_encoder_aacp.h" +#include "../../utils.h" + +HINSTANCE C_ENCODER_AACP::hEncoderInstance = NULL; + +C_ENCODER_AACP::C_ENCODER_AACP(HWND winamp) : C_ENCODER_NSV(sizeof(T_ENCODER_AACP_INFO)) { + SetName("AAC+ Encoder"); + winampWnd = winamp; + ConfigAudio3 = NULL; + if(hEncoderInstance == NULL) { + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_aacplus.dll", GetPluginDirectoryW(winamp)); + hEncoderInstance = LoadLibraryW(dir); + } + + if(hEncoderInstance) { + void * CreateAudio3=(void *)GetProcAddress(hEncoderInstance, "CreateAudio3"); + void * GetAudioTypes3=(void *)GetProcAddress(hEncoderInstance, "GetAudioTypes3"); + void * ConfigAudio3=(void *)GetProcAddress(hEncoderInstance, "ConfigAudio3"); + void * SetWinampHWND=(void *)GetProcAddress(hEncoderInstance, "SetWinampHWND"); + SetEncoder(CreateAudio3,GetAudioTypes3,ConfigAudio3,SetWinampHWND); + } + + T_ENCODER_AACP_INFO * EncInfo = (T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + EncInfo->output_bitRate = AACP_DEFAULT_OUTPUTBITRATE; + EncInfo->output_channelmode = AACP_DEFAULT_OUTPUTCHANNELMODE; + EncInfo->output_quality = AACP_DEFAULT_OUTPUTQUALITY; + EncInfo->output_samplerate = AACP_DEFAULT_OUTPUTSAMPLERATE; + EncInfo->output_v2enable = AACP_DEFAULT_OUTPUTV2ENABLE; +} + +C_ENCODER_AACP::~C_ENCODER_AACP() { + C_ENCODER_NSV::~C_ENCODER_NSV(); +} + +static int cacheVal=0; +bool C_ENCODER_AACP::isPresent(HWND winamp) { + if(cacheVal!=0 && hEncoderInstance!=0) return cacheVal==2; + bool ret=false; + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_aacplus.dll", GetPluginDirectoryW(winamp)); + FILE * f = _wfopen(dir, L"rb"); + if (f) { + fseek(f,0,2); + if(ftell(f) > 0) ret=true; + fclose(f); + } + cacheVal=ret?2:1; + return ret; +} + +void C_ENCODER_AACP::FillAttribs() { + T_ENCODER_AACP_INFO &EncInfo = *(T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + T_ENCODER_AACP_INFO *attribs = new T_ENCODER_AACP_INFO; + *attribs = EncInfo; + AddAttrib("",attribs); +} + +void C_ENCODER_AACP::FillConfFile(char * conf_file, char * section) { + if(!section) section="audio_aacplus"; + + T_ENCODER_AACP_INFO &EncInfo = *(T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + + WritePrivateProfileInt("samplerate", EncInfo.output_samplerate, section, conf_file); + WritePrivateProfileInt("channelmode", EncInfo.output_channelmode, section, conf_file); + WritePrivateProfileInt("bitrate", EncInfo.output_bitRate * 1000, section, conf_file); + WritePrivateProfileInt("v2enable", EncInfo.output_v2enable, section, conf_file); +} + +void C_ENCODER_AACP::ReadConfFile(char * conf_file, char * section) { + if(!section) section="audio_aacplus"; + + T_ENCODER_AACP_INFO &EncInfo = *(T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + T_ENCODER_AACP_INFO *attribs = new T_ENCODER_AACP_INFO; + *attribs = EncInfo; + + attribs->output_samplerate = GetPrivateProfileInt(section,"samplerate",AACP_DEFAULT_OUTPUTSAMPLERATE,conf_file); + attribs->output_channelmode = GetPrivateProfileInt(section,"channelmode",AACP_DEFAULT_OUTPUTCHANNELMODE,conf_file); + attribs->output_bitRate = GetPrivateProfileInt(section,"bitrate",AACP_DEFAULT_OUTPUTBITRATE,conf_file)/1000; + attribs->output_quality = GetPrivateProfileInt(section,"quality",AACP_DEFAULT_OUTPUTQUALITY,conf_file); + attribs->output_v2enable = GetPrivateProfileInt(section,"v2enable",AACP_DEFAULT_OUTPUTV2ENABLE,conf_file); + + ChangeSettings(attribs); +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h new file mode 100644 index 00000000..f6028169 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h @@ -0,0 +1,37 @@ +#ifndef __C_ENCODER_AACP_H__ +#define __C_ENCODER_AACP_H__ + +#include "c_encoder_nsv.h" + +struct T_ENCODER_AACP_INFO : public T_ENCODER_NSV_INFO +{ + unsigned int output_quality; + unsigned int output_samplerate; + unsigned int output_channelmode; + unsigned int output_v2enable; +}; + +#define AACP_DEFAULT_OUTPUTCHANNELMODE 4 +#define AACP_DEFAULT_OUTPUTBITRATE 48 +#define AACP_DEFAULT_OUTPUTQUALITY 2 +#define AACP_DEFAULT_OUTPUTSAMPLERATE 44100 +#define AACP_DEFAULT_OUTPUTV2ENABLE 1 + +class C_ENCODER_AACP : public C_ENCODER_NSV { +private: + HWND winamp; +protected: + virtual void FillAttribs(); +public: + static HINSTANCE hEncoderInstance; + C_ENCODER_AACP(HWND hwnd = 0); + virtual ~C_ENCODER_AACP(); + static bool isPresent(HWND winamp); + virtual void ReadConfFile(char * conf_file, char * section=NULL); + virtual void FillConfFile(char * conf_file, char * section=NULL); + static void Unload() { if(hEncoderInstance) FreeLibrary(hEncoderInstance); hEncoderInstance=0; } + virtual char * GetContentType() { return "audio/aacp"; } + virtual HINSTANCE GetEncoderInstance() { return hEncoderInstance; } +}; + +#endif /* !__C_ENCODER_AACP_H__ */
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp new file mode 100644 index 00000000..749988d2 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp @@ -0,0 +1,80 @@ +#include "c_encoder_fhgaac.h" +#include "../../utils.h" + +HINSTANCE C_ENCODER_FHGAAC::hEncoderInstance = NULL; + +C_ENCODER_FHGAAC::C_ENCODER_FHGAAC(HWND winamp) : C_ENCODER_NSV(sizeof(T_ENCODER_FHGAAC_INFO)) { + SetName("Fraunhofer Encoder"); + winampWnd = winamp; + ConfigAudio3 = NULL; + if(hEncoderInstance == NULL) { + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_fhgaac.dll", GetPluginDirectoryW(winamp)); + hEncoderInstance = LoadLibraryW(dir); + } + + if(hEncoderInstance) { + void * CreateAudio3=(void *)GetProcAddress(hEncoderInstance, "CreateAudio3"); + void * GetAudioTypes3=(void *)GetProcAddress(hEncoderInstance, "GetAudioTypes3"); + void * ConfigAudio3=(void *)GetProcAddress(hEncoderInstance, "ConfigAudio3"); + void * SetWinampHWND=(void *)GetProcAddress(hEncoderInstance, "SetWinampHWND"); + SetEncoder(CreateAudio3,GetAudioTypes3,ConfigAudio3,SetWinampHWND,1); + } + + T_ENCODER_FHGAAC_INFO * EncInfo = (T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + EncInfo->output_bitRate = FHGAAC_DEFAULT_OUTPUTBITRATE; + EncInfo->output_profile = FHGAAC_DEFAULT_OUTPUTPROFILE; + EncInfo->output_surround = FHGAAC_DEFAULT_OUTPUTSURROUND; +} + +C_ENCODER_FHGAAC::~C_ENCODER_FHGAAC() { + C_ENCODER_NSV::~C_ENCODER_NSV(); +} + +static int cacheVal=0; +bool C_ENCODER_FHGAAC::isPresent(HWND winamp) { + if(cacheVal!=0 && hEncoderInstance!=0) return cacheVal==2; + bool ret=false; + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_fhgaac.dll", GetPluginDirectoryW(winamp)); + FILE * f = _wfopen(dir, L"rb"); + if (f) { + fseek(f,0,2); + if(ftell(f) > 0) ret=true; + fclose(f); + } + cacheVal=ret?2:1; + return ret; +} + +void C_ENCODER_FHGAAC::FillAttribs() { + T_ENCODER_FHGAAC_INFO &EncInfo = *(T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + T_ENCODER_FHGAAC_INFO *attribs = new T_ENCODER_FHGAAC_INFO; + *attribs = EncInfo; + AddAttrib("",attribs); +} + +void C_ENCODER_FHGAAC::FillConfFile(char * conf_file, char * section) { + if(!section) section="audio_adtsaac"; + + T_ENCODER_FHGAAC_INFO &EncInfo = *(T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + + WritePrivateProfileInt("profile", EncInfo.output_profile, section, conf_file); + WritePrivateProfileInt("bitrate", EncInfo.output_bitRate, section, conf_file); + WritePrivateProfileInt("surround", EncInfo.output_surround, section, conf_file); + WritePrivateProfileInt("shoutcast", 1, section, conf_file); +} + +void C_ENCODER_FHGAAC::ReadConfFile(char * conf_file, char * section) { + if(!section) section="audio_adtsaac"; + + T_ENCODER_FHGAAC_INFO &EncInfo = *(T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + T_ENCODER_FHGAAC_INFO *attribs = new T_ENCODER_FHGAAC_INFO; + *attribs = EncInfo; + + attribs->output_profile = GetPrivateProfileInt(section,"profile",FHGAAC_DEFAULT_OUTPUTPROFILE,conf_file); + attribs->output_bitRate = GetPrivateProfileInt(section,"bitrate",FHGAAC_DEFAULT_OUTPUTBITRATE,conf_file); + attribs->output_surround = GetPrivateProfileInt(section,"surround",FHGAAC_DEFAULT_OUTPUTSURROUND,conf_file); + + ChangeSettings(attribs); +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h new file mode 100644 index 00000000..67071945 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h @@ -0,0 +1,33 @@ +#ifndef __C_ENCODER_FHGAAC_H__ +#define __C_ENCODER_FHGAAC_H__ + +#include "c_encoder_nsv.h" + +struct T_ENCODER_FHGAAC_INFO : public T_ENCODER_NSV_INFO +{ + unsigned int output_profile; + unsigned int output_surround; +}; + +#define FHGAAC_DEFAULT_OUTPUTBITRATE 48 +#define FHGAAC_DEFAULT_OUTPUTPROFILE 0 // automatic +#define FHGAAC_DEFAULT_OUTPUTSURROUND 0 + +class C_ENCODER_FHGAAC : public C_ENCODER_NSV { +private: + HWND winamp; +protected: + virtual void FillAttribs(); +public: + static HINSTANCE hEncoderInstance; + C_ENCODER_FHGAAC(HWND hwnd = 0); + virtual ~C_ENCODER_FHGAAC(); + static bool isPresent(HWND winamp); + virtual void ReadConfFile(char * conf_file, char * section=NULL); + virtual void FillConfFile(char * conf_file, char * section=NULL); + static void Unload() { if(hEncoderInstance) FreeLibrary(hEncoderInstance); hEncoderInstance=0; } + virtual char * GetContentType() { return "audio/aacp"; } + virtual HINSTANCE GetEncoderInstance() { return hEncoderInstance; } +}; + +#endif /* !__C_ENCODER_AACP_H__ */
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp new file mode 100644 index 00000000..4a703b24 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp @@ -0,0 +1,105 @@ +#include "c_encoder_mp3dll.h" +#include "../../utils.h" + +T_ENCODER_MP3_INFO formatlist[] = { + {8, 22050, 1, 44100, 2, 8}, {16, 22050, 1, 44100, 2, 8}, {24, 22050, 1, 44100, 2, 8}, + {32, 22050, 1, 44100, 2, 8}, {40, 22050, 1, 44100, 2, 8}, {48, 22050, 1, 44100, 2, 8}, + {48, 44100, 1, 44100, 2, 8}, {56, 22050, 1, 44100, 2, 8}, {56, 44100, 1, 44100, 2, 8}, + {64, 44100, 1, 44100, 2, 8}, {80, 44100, 1, 44100, 2, 8}, {96, 44100, 1, 44100, 2, 8}, + {112, 44100, 1, 44100, 2, 8}, {128, 44100, 1, 44100, 2, 8}, {40, 22050, 2, 44100, 2, 8}, + {48, 22050, 2, 44100, 2, 8}, {56, 22050, 2, 44100, 2, 8}, {64, 22050, 2, 44100, 2, 8}, + {80, 22050, 2, 44100, 2, 8}, {56, 44100, 2, 44100, 2, 8}, {64, 44100, 2, 44100, 2, 8}, + {80, 44100, 2, 44100, 2, 8}, {96, 44100, 2, 44100, 2, 8}, {112, 44100, 2, 44100, 2, 8}, + {128, 44100, 2, 44100, 2, 8}, {160, 44100, 2, 44100, 2, 8}, {192, 44100, 2, 44100, 2, 8}, + {224, 44100, 2, 44100, 2, 8}, {256, 44100, 2, 44100, 2, 8}, {320, 44100, 2, 44100, 2, 8} +}; + +static unsigned int formatlist_numEntries = sizeof(formatlist) / sizeof(T_ENCODER_MP3_INFO); + +C_ENCODER_MP3::C_ENCODER_MP3(void *init, void *params, void *encode, void *finish) : C_ENCODER(sizeof(T_ENCODER_MP3_INFO)) { //sizeof(T_ENCODER_LAMEMP3_INFO) + SetName("MP3 Encoder"); + T_ENCODER_MP3_INFO &EncInfo = *((T_ENCODER_MP3_INFO *)ExtendedInfoPtr); + Handle = NULL; + has_encoded = 0; + EncInfo = formatlist[MP3_DEFAULT_ATTRIBNUM]; + hMutex = CreateMutex(NULL,TRUE,NULL); + ReleaseMutex(hMutex); + + lame_init = (lame_t (__cdecl *)(void))init; + lame_init_params = (int (__cdecl *)(lame_global_flags *))params; + lame_encode_buffer_interleaved = (int (__cdecl *)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size))encode; + lame_encode_flush = (int (__cdecl *)(lame_global_flags *, char *mp3buffer, int size))finish; +} + +C_ENCODER_MP3::~C_ENCODER_MP3() { + WaitForSingleObject(hMutex,INFINITE); + CloseHandle(hMutex); + hMutex = NULL; + C_ENCODER::~C_ENCODER(); +} + +void C_ENCODER_MP3::Close() { + C_ENCODER::Close(); + if(lame_init != NULL) { + if(has_encoded && lame_encode_flush) { + char buf[1024] = {0}; + lame_encode_flush(Handle,(char *)buf,sizeof(buf)); + } + //delete Handle; caused crash !! needs looking at + Handle = NULL; + has_encoded = 0; + } +} + +void C_ENCODER_MP3::Reset() { + T_ENCODER_MP3_INFO &EncInfo = *(T_ENCODER_MP3_INFO *)ExtendedInfoPtr; + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + Close(); + if(lame_init != NULL && EncInfo.input_sampleRate != 0 && EncInfo.input_numChannels != 0) { + if(EncInfo.output_sampleRate != 0 && EncInfo.output_bitRate != 0 && Handle == NULL) { + has_encoded = 0; + Handle = lame_init(); + Handle->samplerate_in = EncInfo.input_sampleRate; + Handle->num_channels = 2; // always process as 2 channels as it resolves issues with soundcard input in mono mode (which is padded to stereo) + Handle->samplerate_out = EncInfo.output_sampleRate; + Handle->mode = EncInfo.output_numChannels == 2 ? (MPEG_mode)0 : (MPEG_mode)3; + Handle->brate = EncInfo.output_bitRate; + Handle->VBR = vbr_off; + Handle->write_lame_tag = 0; + Handle->write_id3tag_automatic = 0; + Handle->quality = EncInfo.QualityMode; + if(Handle->quality < 0 || Handle->quality > 9) Handle->quality = 8; + lame_init_params(Handle); + } else { + Handle = NULL; + } + for(unsigned int i = 0; i < formatlist_numEntries; i++) { + char textbuf[256]; + formatlist[i].QualityMode = EncInfo.QualityMode; + snprintf(textbuf,sizeof(textbuf),"%dkbps, %dHz, %s",formatlist[i].output_bitRate,formatlist[i].output_sampleRate,(formatlist[i].output_numChannels == 1 ? "Mono" : "Stereo")); + T_ENCODER_MP3_INFO *attribs = new T_ENCODER_MP3_INFO; + *attribs = formatlist[i]; + AddAttrib((char *)&textbuf,attribs); + } + } + ReleaseMutex(hMutex); +} + +int C_ENCODER_MP3::Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused) { + if((inputbuf != NULL) && (outputbuf != NULL) && (inputbufsize != 0) && (outputbufsize != 0) && (inputamtused != NULL) && (Handle != NULL)) { + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return 0; + int outputamtused = 0; + if(lame_encode_buffer_interleaved) { + outputamtused = lame_encode_buffer_interleaved(Handle, (short *)inputbuf, inputbufsize / (2 * sizeof(short)), (char *)outputbuf, outputbufsize); + if(outputamtused < 0) { + ReleaseMutex(hMutex); + return 0; + } + has_encoded = 1; + } + *inputamtused = inputbufsize; + ReleaseMutex(hMutex); + return outputamtused; + } + return 0; +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h new file mode 100644 index 00000000..05beb05e --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h @@ -0,0 +1,51 @@ + +#ifndef __C_ENCODER_MP3DLL_H__ +#define __C_ENCODER_MP3DLL_H__ + +#include "c_encoder.h" +#include "../lame/include/lame.h" +#include "../lame/libmp3lame/lame_global_flags.h" +#include <windows.h> + +// Defaults for this encoder +#define MP3_DEFAULT_INPUTSAMPLERATE 44100 +#define MP3_DEFAULT_INPUTNUMCHANNELS 2 +#define MP3_DEFAULT_OUTPUTSAMPLERATE 44100 +#define MP3_DEFAULT_OUTPUTNUMCHANNELS 2 +#define MP3_DEFAULT_OUTPUTBITRATE 96 + +#define MP3_DEFAULT_ATTRIBNUM 22 + +struct T_ENCODER_MP3_INFO { + int output_bitRate; + int output_sampleRate; + int output_numChannels; + int input_sampleRate; + int input_numChannels; + int QualityMode; +}; + +#define HBE_STREAM lame_global_flags * + +class C_ENCODER_MP3 : public C_ENCODER { +private: + HANDLE hMutex; + lame_t Handle; + int has_encoded; +protected: + lame_t (*lame_init)(void); + int (*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); + +public: + C_ENCODER_MP3(void *init, void *params, void *encode, void *finish); + virtual ~C_ENCODER_MP3(); + + virtual void Close(); + virtual void Reset(); + + virtual int Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused); /* all values are in BYTES! */ +}; + +#endif /* !__C_ENCODER_MP3_H__ */
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp new file mode 100644 index 00000000..18098535 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp @@ -0,0 +1,151 @@ +#include "c_encoder_nsv.h" +#include "../../utils.h" +#include <mmsystem.h> +#include <stdio.h> + +static char * configfile; +static unsigned int configfourcc; +static HWND (*ConfigAudio3)(HWND intParent, HINSTANCE hinst, unsigned int outt, char *configfile); +static HINSTANCE encoderDllInstance; +static HWND cfgwnd=NULL; +C_ENCODER_NSV::C_ENCODER_NSV(int ExtInfoSize) : C_ENCODER(ExtInfoSize) { + hMutex = CreateMutex(NULL,TRUE,NULL); + ReleaseMutex(hMutex); + CreateAudio3 = NULL; + ConfigAudio3 = NULL; + GetAudioTypes3 = NULL; + SetWinampHWND = NULL; + SetConfigItem = NULL; + GetConfigItem = NULL; + winampWnd = NULL; + fourcc = 0; + encoder = NULL; +} + +void C_ENCODER_NSV::SetEncoder(void * CreateAudio3, void * GetAudioTypes3, void * ConfigAudio3, void * SetWinampHWND, int encoderNum) { + *(void **)&(this->CreateAudio3) = CreateAudio3; + *(void **)&(this->GetAudioTypes3) = GetAudioTypes3; + *(void **)&(this->ConfigAudio3) = ConfigAudio3; + *(void **)&(this->SetWinampHWND) = SetWinampHWND; + + if(this->SetWinampHWND) { + this->SetWinampHWND(winampWnd); + } + + if(this->GetAudioTypes3) { + char name[C_ENCODER_NameLen]; + fourcc = this->GetAudioTypes3(encoderNum,name); + } +} + +C_ENCODER_NSV::~C_ENCODER_NSV() { + WaitForSingleObject(hMutex,INFINITE); + + CloseHandle(hMutex); + hMutex = NULL; + C_ENCODER::~C_ENCODER(); +} + +void C_ENCODER_NSV::Close() { + C_ENCODER::Close(); + + if(encoder) + delete encoder; + encoder = NULL; +} + +void C_ENCODER_NSV::Reset() { + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + + Close(); + + if(!configfile) { + configfile = GetSCIniFile(winampWnd); + } + + FillConfFile(configfile); + + int nch = ((T_ENCODER_NSV_INFO*)ExtendedInfoPtr)->input_numChannels; + int srate = ((T_ENCODER_NSV_INFO*)ExtendedInfoPtr)->input_sampleRate; + if (CreateAudio3) { + const int bps = 16; /* I think this is always the case. */ + encoder = CreateAudio3(nch,srate,bps,mmioFOURCC('P','C','M',' '),&fourcc,configfile); + } + else encoder = NULL; + + /* I think (in that I havn't found anything to the contrary) that in the CreateAudio3 call, the encoder + * reads all its settings from the conf_file and never touches them again. Hence, this is safe. Ahem. + */ + FillAttribs(); + ReleaseMutex(hMutex); +} + +int C_ENCODER_NSV::Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused) { + + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return 0; + int ret=0; + if(encoder && (inputbuf != NULL) && (outputbuf != NULL) && (inputbufsize != 0) && (outputbufsize != 0) && (inputamtused != NULL)) { + ret = encoder->Encode(0,(void *)inputbuf,inputbufsize,inputamtused,outputbuf,outputbufsize); + } else + *inputamtused = inputbufsize; /* we havn't got the dll, so just say everything is ok? */ + + ReleaseMutex(hMutex); + return ret; +} + +static BOOL CALLBACK configure_dlgproc(HWND intDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + if(configfourcc == mmioFOURCC('A','D','T','S')) { + SetWindowTextW(intDlg, LocalisedString(IDS_FHGAAC_ENCODER, NULL, 0)); + } + #ifdef USE_OGG + else if(configfourcc == mmioFOURCC('O','G','G',' ')) { + SetWindowTextW(intDlg, LocalisedString(IDS_OGG_CONFIG_TITLE, NULL, 0)); + } + #endif + + cfgwnd=ConfigAudio3(intDlg, encoderDllInstance, configfourcc, configfile); + + if(cfgwnd) { + RECT r; + GetWindowRect(GetDlgItem(intDlg,IDC_GO_HERE),&r); + ScreenToClient(intDlg,(LPPOINT)&r); + SetWindowPos(cfgwnd,NULL,r.left,r.top,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER); + ShowWindow(cfgwnd,SW_SHOWNA); + InvalidateRect(intDlg,NULL,FALSE); + } + break; + case WM_COMMAND: + if(LOWORD(wParam) == IDCANCEL) EndDialog(intDlg,0); + break; + case WM_DESTROY: + DestroyWindow(cfgwnd); + break; + } + return 0; +} + +void C_ENCODER_NSV::Configure(HWND parent, HINSTANCE hDllInstance) { + if(ConfigAudio3) { + configfourcc = fourcc; + if(!configfile) { + configfile = GetSCIniFile(winampWnd); + } + + ::ConfigAudio3 = this->ConfigAudio3; + ::encoderDllInstance = GetEncoderInstance(); + + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + FillConfFile(configfile); + ReleaseMutex(hMutex); + + LocalisedDialogBox(hDllInstance,IDD_NSVCONFIG,parent,::configure_dlgproc); + + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + ReadConfFile(configfile); + Reset(); + ReleaseMutex(hMutex); + } +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h new file mode 100644 index 00000000..14adca08 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h @@ -0,0 +1,76 @@ +/* This is an abstract class to use the NSV style of encoder. + */ + +#ifndef __C_ENCODER_NSV_H__ +#define __C_ENCODER_NSV_H__ + +#include "c_encoder.h" +#include "enc_if.h" +#include <windows.h> +#include <Shlobj.h> +#include "../../Resource/resource.h" + +struct T_ENCODER_NSV_INFO { + unsigned int output_bitRate; + unsigned int input_numChannels; + unsigned int input_sampleRate; +}; + +class C_ENCODER_NSV : public C_ENCODER { +private: + HANDLE hMutex; +protected: + // These are exported by enc_*.dll + AudioCoder* (*CreateAudio3)(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile); + int (*GetAudioTypes3)(int idx, char *desc); + HWND (*ConfigAudio3)(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); + void (*SetWinampHWND)(HWND hwnd); + int (*SetConfigItem)(unsigned int outt, char *item, char *data, char *configfile); + int (*GetConfigItem)(unsigned int outt, char *item, char *data, int len, char *configfile); + /* We don't need the rest of the exports + AudioCoder *(*FinishAudio3)(char *fn, AudioCoder *c); + void (*PrepareToFinish)(const char *filename, AudioCoder *coder); + */ + + // our encoder (the AudioCoder class is defined in enc_if.h) + AudioCoder* encoder; + + // the type of the output format + unsigned int fourcc; + + // fill up the attribute list (using AddAttrib) + virtual void FillAttribs()=0; + + // child classes MUST call this in their constructor + // note: encoderNum defaults to 0 which resolves to the first encoder + // in most enc_* but make sure to set this correctly for others + virtual void SetEncoder(void * CreateAudio3, void * GetAudioTypes3, void * ConfigAudio3, void * SetWinampHWND, int encoderNum=0); + + // this is used in Configure() + virtual HINSTANCE GetEncoderInstance()=0; + + // this is used for esternal encoders so they can be correctly localised + HWND winampWnd; + +public: + C_ENCODER_NSV(int ExtInfoSize = sizeof(T_ENCODER_NSV_INFO)); + virtual ~C_ENCODER_NSV(); + + virtual void Close(); + virtual void Reset(); + + virtual int Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused); /* all values are in BYTES! */ + + // show configuration dialog + virtual void Configure(HWND parent,HINSTANCE hDllInstance); + + virtual bool UseNsvConfig() { return true; }; + + // populate the configuration file with current settings + virtual void FillConfFile(char * conf_file, char * section=NULL)=0; + + // read the configuration file and change current settings + virtual void ReadConfFile(char * conf_file, char * section=NULL)=0; +}; + +#endif /* !__C_ENCODER_NSV_H__ */
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp new file mode 100644 index 00000000..e9420b7b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp @@ -0,0 +1,117 @@ +#include "c_encoder_ogg.h" +#include "../../utils.h" + +HINSTANCE C_ENCODER_OGG::hEncoderInstance = NULL; + +C_ENCODER_OGG::C_ENCODER_OGG(HWND winamp) : C_ENCODER_NSV(sizeof(T_ENCODER_OGG_INFO)) { + SetName("OGG Vorbis Encoder"); + winampWnd = winamp; + ConfigAudio3 = NULL; + if(hEncoderInstance == NULL) { + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_vorbis.dll", GetPluginDirectoryW(winamp)); + hEncoderInstance = LoadLibraryW(dir); + } + + if(hEncoderInstance) { + void * CreateAudio3=(void *)GetProcAddress(hEncoderInstance, "CreateAudio3"); + void * GetAudioTypes3=(void *)GetProcAddress(hEncoderInstance, "GetAudioTypes3"); + void * ConfigAudio3=(void *)GetProcAddress(hEncoderInstance, "ConfigAudio3"); + void * SetWinampHWND=(void *)GetProcAddress(hEncoderInstance, "SetWinampHWND"); + SetEncoder(CreateAudio3,GetAudioTypes3,ConfigAudio3,SetWinampHWND); + } + + T_ENCODER_OGG_INFO * EncInfo = (T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + EncInfo->output_bitRate = OGG_DEFAULT_OUTPUTBITRATE; + EncInfo->output_channelmode = OGG_DEFAULT_OUTPUTMODE; + EncInfo->output_samplerate = OGG_DEFAULT_OUTPUTSAMPLERATE; +} + +C_ENCODER_OGG::~C_ENCODER_OGG() { + C_ENCODER_NSV::~C_ENCODER_NSV(); +} + +static int cacheVal=0; +bool C_ENCODER_OGG::isPresent(HWND winamp) { + if(cacheVal!=0 && hEncoderInstance!=0) return cacheVal==2; + bool ret=false; + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_vorbis.dll", GetPluginDirectoryW(winamp)); + FILE * f = _wfopen(dir, L"rb"); + if (f) { + fseek(f,0,2); + if(ftell(f) > 0) ret=true; + fclose(f); + } + cacheVal=ret?2:1; + return ret; +} + +void C_ENCODER_OGG::FillAttribs() { + T_ENCODER_OGG_INFO &EncInfo = *(T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + T_ENCODER_OGG_INFO *attribs = new T_ENCODER_OGG_INFO; + *attribs = EncInfo; + AddAttrib("",attribs); +} + +void C_ENCODER_OGG::FillConfFile(char * conf_file, char * section) { + if(!section) section="audio_ogg"; + + T_ENCODER_OGG_INFO &EncInfo = *(T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + configtype * cfg = new configtype; + cfg->cfg_abr_use_max=0; + cfg->cfg_abr_use_min=0; + cfg->cfg_mode=0; //VBR + cfg->cfg_vbrquality=EncInfo.output_quality; + cfg->cfg_abr_nominal=EncInfo.output_bitRate; + cfg->cfg_abr_max=EncInfo.output_bitRate; + cfg->cfg_abr_min=EncInfo.output_bitRate; + + if (conf_file) WritePrivateProfileStruct(section,"conf",cfg,sizeof(configtype),conf_file); +} +int setBitrate(float ql) +{ + int br = 64; + //ql = ql*10; + // jkey: this is a pain in the ass,but the only + // way i can figure out how to get the bitrate + // outside of enc_vorbis. + // Also quality enforcement is needed to prevent the + // yp filling up with non standard bitrate streams. + // although this is vbr and will be variable bitrate anyway. + if(ql == 10 || (ql < 10 && ql > 9.5)){br=500;ql = 10.0f; return br;} + if(ql == 9.0f || (ql < 10.0f && ql > 9.0f)){br=320;ql = 9.0f;return br;} + if(ql == 8.0f || (ql < 9.0f && ql > 8.0f)){br=256;ql = 8.0f;return br;} + if(ql == 7.0f || (ql < 8.0f && ql > 7.0f)){br=224;ql = 7.0f;return br;} + if(ql == 6.0f || (ql < 7.0f && ql > 6.0f)){br=192;ql = 6.0f;return br;} + if(ql == 5.0f || (ql < 6.0f && ql > 5.0f)){br=160;ql = 5.0f;return br;} + if(ql == 4.0f || (ql < 5.0f && ql > 4.0f)){br=128;ql = 4.0f;return br;} + if(ql == 3.0f || (ql < 4.0f && ql > 3.0f)){br=112;ql = 3.0f;return br;} + if(ql == 2.0f || (ql < 3.0f && ql > 2.0f)){br=96;ql = 2.0f;return br;} + if(ql == 1.0f || (ql < 2.0f && ql > 1.0f)){br=80;ql = 1.0f;return br;} + if(ql == 0.0f || (ql < 1.0f && ql > 0.0f)){ br=64;ql = 0.0f;return br;} + if(ql == -0.5f || (ql < 0.0f && ql > -0.5f)){br=56;ql = -0.5f;return br;} + if(ql == -1.0f || ql < -0.5f){br=48;ql = -1.0f;return br;} + return br; +} +void C_ENCODER_OGG::ReadConfFile(char * conf_file, char * section) { + if(!section) section="audio_ogg"; + T_ENCODER_OGG_INFO &EncInfo = *(T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + T_ENCODER_OGG_INFO *attribs = new T_ENCODER_OGG_INFO; + *attribs = EncInfo; + configtype * cfg = new configtype; + cfg->cfg_abr_use_max=0; + cfg->cfg_abr_use_min=0; + cfg->cfg_mode=0; //VBR + cfg->cfg_vbrquality=0.0f; + cfg->cfg_abr_nominal=64; + cfg->cfg_abr_max=352; + cfg->cfg_abr_min=32; + + if (conf_file) GetPrivateProfileStruct(section,"conf",cfg,sizeof(configtype),conf_file); + attribs->output_samplerate = OGG_DEFAULT_OUTPUTSAMPLERATE; + attribs->output_channelmode = cfg->cfg_mode; + attribs->output_quality = cfg->cfg_vbrquality; + attribs->output_bitRate = setBitrate(attribs->output_quality*10); + ChangeSettings(attribs); +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h new file mode 100644 index 00000000..be20d689 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h @@ -0,0 +1,47 @@ +#ifndef __C_ENCODER_OGG_H__ +#define __C_ENCODER_OGG_H__ + +#include "c_encoder_nsv.h" +//#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) + +typedef struct +{ + bool cfg_abr_use_max,cfg_abr_use_min; + UINT cfg_mode; + + float cfg_vbrquality; + UINT cfg_abr_nominal; + UINT cfg_abr_max; + UINT cfg_abr_min; +} configtype; + +struct T_ENCODER_OGG_INFO : public T_ENCODER_NSV_INFO +{ + float output_quality; + unsigned int output_samplerate; + unsigned int output_channelmode; +}; + +#define OGG_DEFAULT_OUTPUTMODE 0 +#define OGG_DEFAULT_OUTPUTBITRATE 192 +#define OGG_DEFAULT_OUTPUTSAMPLERATE 44100 +#define OGG_DEFAULT_OUTPUTQUALITY 2.0f + +class C_ENCODER_OGG : public C_ENCODER_NSV { +private: + HWND winamp; +protected: + virtual void FillAttribs(); +public: + static HINSTANCE hEncoderInstance; + C_ENCODER_OGG(HWND hwnd = 0); + virtual ~C_ENCODER_OGG(); + static bool isPresent(HWND winamp); + virtual void ReadConfFile(char * conf_file, char * section=NULL); + virtual void FillConfFile(char * conf_file, char * section=NULL); + static void Unload() { if(hEncoderInstance) FreeLibrary(hEncoderInstance); hEncoderInstance=0; } + virtual char * GetContentType() { return "audio/ogg"; } + virtual HINSTANCE GetEncoderInstance() { return hEncoderInstance; } +}; + +#endif /* !__C_ENCODER_OGG_H__ */
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h new file mode 100644 index 00000000..70eaff30 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h @@ -0,0 +1,44 @@ +/* +** enc_if.h - common encoder interface +** +** Copyright (C) 2001-2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NSV_ENC_IF_H_ +#define _NSV_ENC_IF_H_ +class VideoCoder +{ + public: + VideoCoder() { } + virtual int Encode(void *in, void *out, int *iskf)=0; // returns bytes in out + virtual ~VideoCoder() { }; +}; + +class AudioCoder +{ + public: + AudioCoder() { } + virtual int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)=0; //returns bytes in out + virtual ~AudioCoder() { }; +}; + +// unsigned int GetAudioTypes3(int idx, char *desc); +// unsigned int GetVideoTypes3(int idx, char *desc); +// AudioCoder *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile); +// VideoCoder *CreateVideo3(int w, int h, double frt, unsigned int pixt, unsigned int *outt, char *configfile); +// HWND ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); +// HWND ConfigVideo3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); + +#endif //_NSV_ENC_IF_H_
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h new file mode 100644 index 00000000..c7feacc9 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h @@ -0,0 +1,254 @@ +#ifndef __C_JOBMANAGER_H__ +#define __C_JOBMANAGER_H__ + +#include <vector> + +#ifdef _WIN32 +#include <wtypes.h> +#include <winbase.h> // for mutex support +#define T_MUTEX HANDLE +#else // _WIN32 +#error "This won't compile under anything other than windows since I haven't implemented mutexing on anything else" +#endif // _WIN32 + +template<class T> class C_JOBMANAGER { +public: + typedef int (*T_JOBHANDLER)(int state, int last_state, T *userData); +private: + struct T_JOB { + int state; + int last_state; + int suspended; + T *userData; + }; + struct T_HANDLER { + int state; + int last_state; + T_JOBHANDLER jobHandler; + }; + std::vector<T_JOB*> JobList; + std::vector<T_HANDLER*> HandlerList; + T_MUTEX mutex; + +protected: + int waitForMutex() { +#ifdef _WIN32 + if(WaitForSingleObject(mutex,INFINITE) == WAIT_OBJECT_0) return 1; +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + return 0; + } + + void releaseMutex() { +#ifdef _WIN32 + ReleaseMutex(mutex); +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + } + +public: + C_JOBMANAGER() { +#ifdef _WIN32 + mutex = CreateMutex(NULL,TRUE,NULL); + ReleaseMutex(mutex); +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + } + + virtual ~C_JOBMANAGER() { + waitForMutex(); +#ifdef _WIN32 + CloseHandle(mutex); + mutex = NULL; +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + + //JobList.deleteAll(); + for (auto job : JobList) + { + delete job; + } + JobList.clear(); + + //HandlerList.deleteAll(); + for (auto handler : HandlerList) + { + delete handler; + } + HandlerList.clear(); + } + + T *operator[](int job) { + if(!waitForMutex()) return NULL; + T_JOB *j = JobList[job]; + T *val = NULL; + if(j) val = j->userData; + releaseMutex(); + return val; + } + + virtual int AddJob(int state, T *userData, int suspended = 0, int isUserUnique = 1) { + if(!waitForMutex()) return -1; + int n = JobList.size(); + if(isUserUnique && n) { + for(int i = n-1; i >= 0; i--) { + T_JOB *item = JobList[i]; + if(item) { + if(item->userData == userData) { + releaseMutex(); + return -1; + } + } + } + } + T_JOB *job = new T_JOB; + job->last_state = OUT_DISCONNECTED; + job->state = state; + job->suspended = suspended; + job->userData = userData; + JobList.push_back(job); + releaseMutex(); + return n; + } + + virtual int GetJobState(int job) { + int retval = -1; + if(waitForMutex()) { + int n = JobList.size(); + if(job < n && job >= 0) retval = JobList[job]->state; + releaseMutex(); + } + return retval; + } + + virtual void SetJobState(int job, int state) { + if(!waitForMutex()) return; + int n = JobList.size(); + if(job < n && job >= 0) JobList[job]->state = state; + releaseMutex(); + } + + virtual void SuspendJob(int job, int suspended) { + if(!waitForMutex()) return; + int n = JobList.size(); + if(job < n && job >= 0) JobList[job]->suspended = suspended; + releaseMutex(); + } + + virtual void DelJob(int job) { + if(!waitForMutex()) return; + int n = JobList.size(); + if(job < n && job >= 0) { + delete JobList[job]; + JobList.erase(JobList.begin() + job); + } + releaseMutex(); + } + + virtual void ClearJobs() { + if(!waitForMutex()) + return; + + //JobList.deleteAll(); + for (auto job : JobList) + { + delete job; + } + JobList.clear(); + + releaseMutex(); + } + + virtual void AddHandler(int state, T_JOBHANDLER jobHandler) { + if(!waitForMutex()) return; + int n = HandlerList.size(); + for(int i = n-1; i >= 0; i--) { + T_HANDLER *item = HandlerList[i]; + if(item) { + if(item->state == state) { + releaseMutex(); + return; + } + } + } + T_HANDLER *handler = new T_HANDLER; + handler->state = state; + handler->jobHandler = jobHandler; + HandlerList.push_back(handler); + releaseMutex(); + } + + virtual void DelHandler(int state) { + if(!waitForMutex()) return; + int n = HandlerList.size(); + for(int i = n-1; i >= 0; i--) { + T_HANDLER *item = HandlerList[i]; + if(item) { + if(item->state == state) { + delete HandlerList[i]; + HandlerList.erase(HandlerList.begin() + i); + releaseMutex(); + return; + } + } + } + releaseMutex(); + } + + virtual void ClearHandlers() { + if(!waitForMutex()) + return; + + //HandlerList.deleteAll(); + for (auto handler : HandlerList) + { + delete handler; + } + HandlerList.clear(); + + releaseMutex(); + } + + virtual int GetNumJobs() { + if(!waitForMutex()) return -1; + int n = JobList.size(); + releaseMutex(); + return n; + } + + virtual int GetNumHandlers() { + if(!waitForMutex()) return -1; + int n = HandlerList.size(); + releaseMutex(); + return n; + } + + virtual void Run(int job) { + if(!waitForMutex()) return; + int nJ = JobList.size(); + int nH = HandlerList.size(); + if(job < nJ && job >= 0) { + T_JOB *job_item = JobList[job]; + for(int i = nH-1; i >= 0; i--) { + T_HANDLER *handler = HandlerList[i]; + if(handler) { + if(handler->state == job_item->state) { + if(!job_item->suspended) { + int cur_state = job_item->state; + job_item->state = handler->jobHandler(job_item->state,job_item->last_state,job_item->userData); + job_item->last_state = cur_state; + } + break; + } + } + } + } + releaseMutex(); + } +}; + +#endif // !__C_JOBMANAGER_H__
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h new file mode 100644 index 00000000..6023dd01 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h @@ -0,0 +1,24 @@ +#ifndef __C_SERIAL_JOBMANAGER_H__ +#define __C_SERIAL_JOBMANAGER_H__ + +#include "c_jobmanager.h" + +template<class T> class C_SERIAL_JOBMANAGER : public C_JOBMANAGER<T> { +private: + int currentJob; +public: + C_SERIAL_JOBMANAGER() { + currentJob = 0; + } + ~C_SERIAL_JOBMANAGER() { } + int GetCurrentJob() { return currentJob; } + virtual void Run(int passes = 1) { + int numPasses = passes; + while(numPasses-- > 0) { + C_JOBMANAGER<T>::Run(currentJob++); + if(currentJob > GetNumJobs()) currentJob = 0; + } + } +}; + +#endif // !__C_SERIAL_JOBMANAGER_H__
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h new file mode 100644 index 00000000..5b074de0 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h @@ -0,0 +1,194 @@ +#ifndef __C_SHOUTCAST_2_OUTPUT_H__ +#define __C_SHOUTCAST_2_OUTPUT_H__ + +#include <time.h> +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include "c_serial_jobmanager.h" +#include "../jnetlib/jnetlib.h" +#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" + +#ifdef USEAACP +#include "../Encoders/c_encoder_aacp.h" +#endif +struct T_OUTPUT_CONFIG { + char Name[32]; + char UserID[256]; + char Address[1024]; + u_short Port; + char StationID[8]; + char Password[256]; // 4 - 8 for 1.8.2 + char cipherkey[32];// sc2 cipherkey + int AutoRecon; + int ReconTime; + char Description[1024]; + char ServerURL[2048]; + int Genre1; + int Genre2; + char Genre3[1024]; + char ICQ[128]; + char AIM[512]; + char IRC[512]; + char content_type[11]; + int Public; + int doTitleUpdate; + int protocol; + int DoUpload; + char introfilepath[4096]; + char backupfile[4096]; +}; + +#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, +}; + +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 sc2Suceeded;//sc2 version + char ErrorMsg[1024]; + time_t ConnectedAt; + wchar_t Title[1024]; + wchar_t Next[1024]; + char URL[1024]; + int introuploaded; + int backupuploaded; +}; + +struct T_OUTPUT { + JNL_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; + int m_sendmetadata; + int m_initdone; +}; + +enum OUTPUTSTATE { + OUT_ERROR, // not a true state, but is returned when GetState() is called with an invalid connection handle + OUT_IDLE, + 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_RECVYP, + 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, +}; +#define OUT_DISCONNECTED OUT_IDLE + +class C_SHOUTCAST_2_OUTPUT { +private: + C_ENCODER *Encoder; + int IOwnEncoder; + C_SERIAL_JOBMANAGER<T_OUTPUT> OutputManager; + HANDLE mutex; + +protected: + static int Output_Idle(int state, T_OUTPUT *userData); + static int Output_Connect(int state, T_OUTPUT *userData); + static int Output_Request_Cipher(int state, T_OUTPUT *userData); + static int Output_Receive_Cipher(int state, T_OUTPUT *userData); + static int Output_SendAuth(int state, T_OUTPUT *userData); + static int Output_RecvAuthResponse(int state, T_OUTPUT *userData); + static int Output_Send_Mime(int state, T_OUTPUT *userData); + static int Output_Recv_Mime(int state, T_OUTPUT *userData); + static int Output_Send_Bitrate(int state, T_OUTPUT *userData); + static int Output_Recv_Bitrate(int state, T_OUTPUT *userData); + static int Output_Send_Buf_Size(int state, T_OUTPUT *userData); + static int Output_Recv_Buf_Size(int state, T_OUTPUT *userData); + static int Output_Send_Max_Size(int state, T_OUTPUT *userData); + static int Output_Recv_Max_Size(int state, T_OUTPUT *userData); + static int Output_DUMMY(int state, T_OUTPUT *userData); + static int Output_SendYP(int state, T_OUTPUT *userData); + static int Output_RecvYP(int state, T_OUTPUT *userData); + static int Output_Send_InitFlush(int state, T_OUTPUT *userData); + static int Output_Recv_InitFlush(int state, T_OUTPUT *userData); + static int Output_Send_InitStandby(int state, T_OUTPUT *userData); + static int Output_Recv_InitStandby(int state, T_OUTPUT *userData); + static int Output_Send_InitMeta(int state, T_OUTPUT *userData); + static int Output_Recv_InitMeta(int state, T_OUTPUT *userData); + static int Output_Send_Intro(int state, T_OUTPUT *userData); + static int Output_Recv_Intro(int state, T_OUTPUT *userData); + static int Output_Send_Backup(int state, T_OUTPUT *userData); + static int Output_Recv_Backup(int state, T_OUTPUT *userData); + + static int Output_SendContent(int state, T_OUTPUT *userData); + static int Output_Disconnect(int state, T_OUTPUT *userData); + static int Output_Reconnect(int state, T_OUTPUT *userData); + static int Output_Title_SendUpdate(int state, T_OUTPUT *userData); + static int Output_Title_SendUpdatev2(int state, T_OUTPUT *userData); + // uvox21 + static char * createUvoxFrameClasstype(std::string typeString); + static int createUvoxFrame(int length, char * payload_in,char * payload_out, char * classtype); + 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); + +public: + C_SHOUTCAST_2_OUTPUT(); + void SetLame(void *init, void *params, void *encode, void *finish); + ~C_SHOUTCAST_2_OUTPUT(); + int Run(int mode = 0, void *Input = NULL, int InputSize = 0); + int AddOutput(T_OUTPUT_CONFIG *Config); + void UpdateOutput(int Connection); + void RemoveOutput(int Connection); + void ConnectOutput(int Connection); + void 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); + void UpdateTitle(wchar_t*Title,wchar_t*Next, int Connection,int titleseq); + 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); + int m_titleseq; +}; + +#endif // !__C_SHOUTCAST_2_OUTPUT_H__
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h new file mode 100644 index 00000000..c6378473 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h @@ -0,0 +1,817 @@ +#ifndef __SHOUTCAST_OUTPUT_H__ +#define __SHOUTCAST_OUTPUT_H__ + +#include <time.h> +#include <string> +#include <stdio.h> +#include <stdlib.h> +#include <list> +#include <fstream> +#include "c_serial_jobmanager.h" +#include "../Components/wac_network/wac_network_connection_api.h" +#include <WinSock2.h> + +#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<std::wstring> 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<std::wstring> 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<T_OUTPUT> 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<std::wstring> 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<std::wstring> 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__
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h new file mode 100644 index 00000000..3126cb95 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h @@ -0,0 +1,1323 @@ +/* + * Interface to MP3 LAME encoding engine + * + * Copyright (c) 1999 Mark Taylor + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* $Id: lame.h,v 1.4 2012/03/17 00:36:51 dromagod Exp $ */ + +#ifndef LAME_LAME_H +#define LAME_LAME_H + +/* for size_t typedef */ +#include <stddef.h> +/* for va_list typedef */ +#include <stdarg.h> +/* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */ +#include <stdio.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef void (*lame_report_function)(const char *format, va_list ap); + +#if defined(WIN32) || defined(_WIN32) +#undef CDECL +#define CDECL __cdecl +#else +#define CDECL +#endif + +#define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1 + +typedef enum vbr_mode_e { + vbr_off=0, + vbr_mt, /* obsolete, same as vbr_mtrh */ + vbr_rh, + vbr_abr, + vbr_mtrh, + vbr_max_indicator, /* Don't use this! It's used for sanity checks. */ + vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */ +} vbr_mode; + + +/* MPEG modes */ +typedef enum MPEG_mode_e { + STEREO = 0, + JOINT_STEREO, + DUAL_CHANNEL, /* LAME doesn't supports this! */ + MONO, + NOT_SET, + MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ +} MPEG_mode; + +/* Padding types */ +typedef enum Padding_type_e { + PAD_NO = 0, + PAD_ALL, + PAD_ADJUST, + PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ +} Padding_type; + + + +/*presets*/ +typedef enum preset_mode_e { + /*values from 8 to 320 should be reserved for abr bitrates*/ + /*for abr I'd suggest to directly use the targeted bitrate as a value*/ + ABR_8 = 8, + ABR_320 = 320, + + V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/ + VBR_10 = 410, + V8 = 420, + VBR_20 = 420, + V7 = 430, + VBR_30 = 430, + V6 = 440, + VBR_40 = 440, + V5 = 450, + VBR_50 = 450, + V4 = 460, + VBR_60 = 460, + V3 = 470, + VBR_70 = 470, + V2 = 480, + VBR_80 = 480, + V1 = 490, + VBR_90 = 490, + V0 = 500, + VBR_100 = 500, + + + + /*still there for compatibility*/ + R3MIX = 1000, + STANDARD = 1001, + EXTREME = 1002, + INSANE = 1003, + STANDARD_FAST = 1004, + EXTREME_FAST = 1005, + MEDIUM = 1006, + MEDIUM_FAST = 1007 +} preset_mode; + + +/*asm optimizations*/ +typedef enum asm_optimizations_e { + MMX = 1, + AMD_3DNOW = 2, + SSE = 3 +} asm_optimizations; + + +/* psychoacoustic model */ +typedef enum Psy_model_e { + PSY_GPSYCHO = 1, + PSY_NSPSYTUNE = 2 +} Psy_model; + + +/* buffer considerations */ +typedef enum buffer_constraint_e { + MDB_DEFAULT=0, + MDB_STRICT_ISO=1, + MDB_MAXIMUM=2 +} buffer_constraint; + + +struct lame_global_struct; +typedef struct lame_global_struct lame_global_flags; +typedef lame_global_flags *lame_t; + + + + +/*********************************************************************** + * + * The LAME API + * These functions should be called, in this order, for each + * MP3 file to be encoded. See the file "API" for more documentation + * + ***********************************************************************/ + + +/* + * REQUIRED: + * initialize the encoder. sets default for all encoder parameters, + * returns NULL if some malloc()'s failed + * otherwise returns pointer to structure needed for all future + * API calls. + */ +lame_global_flags * CDECL lame_init(void); +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* obsolete version */ +int CDECL lame_init_old(lame_global_flags *); +#endif + +/* + * OPTIONAL: + * set as needed to override defaults + */ + +/******************************************************************** + * input stream description + ***********************************************************************/ +/* number of samples. default = 2^32-1 */ +int CDECL lame_set_num_samples(lame_global_flags *, unsigned long); +unsigned long CDECL lame_get_num_samples(const lame_global_flags *); + +/* input sample rate in Hz. default = 44100hz */ +int CDECL lame_set_in_samplerate(lame_global_flags *, int); +int CDECL lame_get_in_samplerate(const lame_global_flags *); + +/* number of channels in input stream. default=2 */ +int CDECL lame_set_num_channels(lame_global_flags *, int); +int CDECL lame_get_num_channels(const lame_global_flags *); + +/* + scale the input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale(lame_global_flags *, float); +float CDECL lame_get_scale(const lame_global_flags *); + +/* + scale the channel 0 (left) input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale_left(lame_global_flags *, float); +float CDECL lame_get_scale_left(const lame_global_flags *); + +/* + scale the channel 1 (right) input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale_right(lame_global_flags *, float); +float CDECL lame_get_scale_right(const lame_global_flags *); + +/* + output sample rate in Hz. default = 0, which means LAME picks best value + based on the amount of compression. MPEG only allows: + MPEG1 32, 44.1, 48khz + MPEG2 16, 22.05, 24 + MPEG2.5 8, 11.025, 12 + (not used by decoding routines) +*/ +int CDECL lame_set_out_samplerate(lame_global_flags *, int); +int CDECL lame_get_out_samplerate(const lame_global_flags *); + + +/******************************************************************** + * general control parameters + ***********************************************************************/ +/* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */ +int CDECL lame_set_analysis(lame_global_flags *, int); +int CDECL lame_get_analysis(const lame_global_flags *); + +/* + 1 = write a Xing VBR header frame. + default = 1 + this variable must have been added by a Hungarian notation Windows programmer :-) +*/ +int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); +int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); + +/* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */ +int CDECL lame_set_decode_only(lame_global_flags *, int); +int CDECL lame_get_decode_only(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* 1=encode a Vorbis .ogg file. default=0 */ +/* DEPRECATED */ +int CDECL lame_set_ogg(lame_global_flags *, int); +int CDECL lame_get_ogg(const lame_global_flags *); +#endif + +/* + internal algorithm selection. True quality is determined by the bitrate + but this variable will effect quality by selecting expensive or cheap algorithms. + quality=0..9. 0=best (very slow). 9=worst. + recommended: 2 near-best quality, not too slow + 5 good quality, fast + 7 ok quality, really fast +*/ +int CDECL lame_set_quality(lame_global_flags *, int); +int CDECL lame_get_quality(const lame_global_flags *); + +/* + mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono + default: lame picks based on compression ration and input channels +*/ +int CDECL lame_set_mode(lame_global_flags *, MPEG_mode); +MPEG_mode CDECL lame_get_mode(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* + mode_automs. Use a M/S mode with a switching threshold based on + compression ratio + DEPRECATED +*/ +int CDECL lame_set_mode_automs(lame_global_flags *, int); +int CDECL lame_get_mode_automs(const lame_global_flags *); +#endif + +/* + force_ms. Force M/S for all frames. For testing only. + default = 0 (disabled) +*/ +int CDECL lame_set_force_ms(lame_global_flags *, int); +int CDECL lame_get_force_ms(const lame_global_flags *); + +/* use free_format? default = 0 (disabled) */ +int CDECL lame_set_free_format(lame_global_flags *, int); +int CDECL lame_get_free_format(const lame_global_flags *); + +/* perform ReplayGain analysis? default = 0 (disabled) */ +int CDECL lame_set_findReplayGain(lame_global_flags *, int); +int CDECL lame_get_findReplayGain(const lame_global_flags *); + +/* decode on the fly. Search for the peak sample. If the ReplayGain + * analysis is enabled then perform the analysis on the decoded data + * stream. default = 0 (disabled) + * NOTE: if this option is set the build-in decoder should not be used */ +int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int); +int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* DEPRECATED: now does the same as lame_set_findReplayGain() + default = 0 (disabled) */ +int CDECL lame_set_ReplayGain_input(lame_global_flags *, int); +int CDECL lame_get_ReplayGain_input(const lame_global_flags *); + +/* DEPRECATED: now does the same as + lame_set_decode_on_the_fly() && lame_set_findReplayGain() + default = 0 (disabled) */ +int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int); +int CDECL lame_get_ReplayGain_decode(const lame_global_flags *); + +/* DEPRECATED: now does the same as lame_set_decode_on_the_fly() + default = 0 (disabled) */ +int CDECL lame_set_findPeakSample(lame_global_flags *, int); +int CDECL lame_get_findPeakSample(const lame_global_flags *); +#endif + +/* counters for gapless encoding */ +int CDECL lame_set_nogap_total(lame_global_flags*, int); +int CDECL lame_get_nogap_total(const lame_global_flags*); + +int CDECL lame_set_nogap_currentindex(lame_global_flags* , int); +int CDECL lame_get_nogap_currentindex(const lame_global_flags*); + + +/* + * OPTIONAL: + * Set printf like error/debug/message reporting functions. + * The second argument has to be a pointer to a function which looks like + * void my_debugf(const char *format, va_list ap) + * { + * (void) vfprintf(stdout, format, ap); + * } + * If you use NULL as the value of the pointer in the set function, the + * lame buildin function will be used (prints to stderr). + * To quiet any output you have to replace the body of the example function + * with just "return;" and use it in the set function. + */ +int CDECL lame_set_errorf(lame_global_flags *, lame_report_function); +int CDECL lame_set_debugf(lame_global_flags *, lame_report_function); +int CDECL lame_set_msgf (lame_global_flags *, lame_report_function); + + + +/* set one of brate compression ratio. default is compression ratio of 11. */ +int CDECL lame_set_brate(lame_global_flags *, int); +int CDECL lame_get_brate(const lame_global_flags *); +int CDECL lame_set_compression_ratio(lame_global_flags *, float); +float CDECL lame_get_compression_ratio(const lame_global_flags *); + + +int CDECL lame_set_preset( lame_global_flags* gfp, int ); +int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int ); + + + +/******************************************************************** + * frame params + ***********************************************************************/ +/* mark as copyright. default=0 */ +int CDECL lame_set_copyright(lame_global_flags *, int); +int CDECL lame_get_copyright(const lame_global_flags *); + +/* mark as original. default=1 */ +int CDECL lame_set_original(lame_global_flags *, int); +int CDECL lame_get_original(const lame_global_flags *); + +/* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */ +int CDECL lame_set_error_protection(lame_global_flags *, int); +int CDECL lame_get_error_protection(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */ +int CDECL lame_set_padding_type(lame_global_flags *, Padding_type); +Padding_type CDECL lame_get_padding_type(const lame_global_flags *); +#endif + +/* MP3 'private extension' bit Meaningless. default=0 */ +int CDECL lame_set_extension(lame_global_flags *, int); +int CDECL lame_get_extension(const lame_global_flags *); + +/* enforce strict ISO compliance. default=0 */ +int CDECL lame_set_strict_ISO(lame_global_flags *, int); +int CDECL lame_get_strict_ISO(const lame_global_flags *); + + +/******************************************************************** + * quantization/noise shaping + ***********************************************************************/ + +/* disable the bit reservoir. For testing only. default=0 */ +int CDECL lame_set_disable_reservoir(lame_global_flags *, int); +int CDECL lame_get_disable_reservoir(const lame_global_flags *); + +/* select a different "best quantization" function. default=0 */ +int CDECL lame_set_quant_comp(lame_global_flags *, int); +int CDECL lame_get_quant_comp(const lame_global_flags *); +int CDECL lame_set_quant_comp_short(lame_global_flags *, int); +int CDECL lame_get_quant_comp_short(const lame_global_flags *); + +int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/ +int CDECL lame_get_experimentalX(const lame_global_flags *); + +/* another experimental option. for testing only */ +int CDECL lame_set_experimentalY(lame_global_flags *, int); +int CDECL lame_get_experimentalY(const lame_global_flags *); + +/* another experimental option. for testing only */ +int CDECL lame_set_experimentalZ(lame_global_flags *, int); +int CDECL lame_get_experimentalZ(const lame_global_flags *); + +/* Naoki's psycho acoustic model. default=0 */ +int CDECL lame_set_exp_nspsytune(lame_global_flags *, int); +int CDECL lame_get_exp_nspsytune(const lame_global_flags *); + +void CDECL lame_set_msfix(lame_global_flags *, double); +float CDECL lame_get_msfix(const lame_global_flags *); + + +/******************************************************************** + * VBR control + ***********************************************************************/ +/* Types of VBR. default = vbr_off = CBR */ +int CDECL lame_set_VBR(lame_global_flags *, vbr_mode); +vbr_mode CDECL lame_get_VBR(const lame_global_flags *); + +/* VBR quality level. 0=highest 9=lowest */ +int CDECL lame_set_VBR_q(lame_global_flags *, int); +int CDECL lame_get_VBR_q(const lame_global_flags *); + +/* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */ +int CDECL lame_set_VBR_quality(lame_global_flags *, float); +float CDECL lame_get_VBR_quality(const lame_global_flags *); + +/* Ignored except for VBR=vbr_abr (ABR mode) */ +int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *); + +int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *); + +int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *); + +/* + 1=strictly enforce VBR_min_bitrate. Normally it will be violated for + analog silence +*/ +int CDECL lame_set_VBR_hard_min(lame_global_flags *, int); +int CDECL lame_get_VBR_hard_min(const lame_global_flags *); + +/* for preset */ +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +int CDECL lame_set_preset_expopts(lame_global_flags *, int); +#endif + +/******************************************************************** + * Filtering control + ***********************************************************************/ +/* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */ +int CDECL lame_set_lowpassfreq(lame_global_flags *, int); +int CDECL lame_get_lowpassfreq(const lame_global_flags *); +/* width of transition band, in Hz. Default = one polyphase filter band */ +int CDECL lame_set_lowpasswidth(lame_global_flags *, int); +int CDECL lame_get_lowpasswidth(const lame_global_flags *); + +/* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */ +int CDECL lame_set_highpassfreq(lame_global_flags *, int); +int CDECL lame_get_highpassfreq(const lame_global_flags *); +/* width of transition band, in Hz. Default = one polyphase filter band */ +int CDECL lame_set_highpasswidth(lame_global_flags *, int); +int CDECL lame_get_highpasswidth(const lame_global_flags *); + + +/******************************************************************** + * psycho acoustics and other arguments which you should not change + * unless you know what you are doing + ***********************************************************************/ + +/* only use ATH for masking */ +int CDECL lame_set_ATHonly(lame_global_flags *, int); +int CDECL lame_get_ATHonly(const lame_global_flags *); + +/* only use ATH for short blocks */ +int CDECL lame_set_ATHshort(lame_global_flags *, int); +int CDECL lame_get_ATHshort(const lame_global_flags *); + +/* disable ATH */ +int CDECL lame_set_noATH(lame_global_flags *, int); +int CDECL lame_get_noATH(const lame_global_flags *); + +/* select ATH formula */ +int CDECL lame_set_ATHtype(lame_global_flags *, int); +int CDECL lame_get_ATHtype(const lame_global_flags *); + +/* lower ATH by this many db */ +int CDECL lame_set_ATHlower(lame_global_flags *, float); +float CDECL lame_get_ATHlower(const lame_global_flags *); + +/* select ATH adaptive adjustment type */ +int CDECL lame_set_athaa_type( lame_global_flags *, int); +int CDECL lame_get_athaa_type( const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* select the loudness approximation used by the ATH adaptive auto-leveling */ +int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int); +int CDECL lame_get_athaa_loudapprox( const lame_global_flags *); +#endif + +/* adjust (in dB) the point below which adaptive ATH level adjustment occurs */ +int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float); +float CDECL lame_get_athaa_sensitivity( const lame_global_flags* ); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* OBSOLETE: predictability limit (ISO tonality formula) */ +int CDECL lame_set_cwlimit(lame_global_flags *, int); +int CDECL lame_get_cwlimit(const lame_global_flags *); +#endif + +/* + allow blocktypes to differ between channels? + default: 0 for jstereo, 1 for stereo +*/ +int CDECL lame_set_allow_diff_short(lame_global_flags *, int); +int CDECL lame_get_allow_diff_short(const lame_global_flags *); + +/* use temporal masking effect (default = 1) */ +int CDECL lame_set_useTemporal(lame_global_flags *, int); +int CDECL lame_get_useTemporal(const lame_global_flags *); + +/* use temporal masking effect (default = 1) */ +int CDECL lame_set_interChRatio(lame_global_flags *, float); +float CDECL lame_get_interChRatio(const lame_global_flags *); + +/* disable short blocks */ +int CDECL lame_set_no_short_blocks(lame_global_flags *, int); +int CDECL lame_get_no_short_blocks(const lame_global_flags *); + +/* force short blocks */ +int CDECL lame_set_force_short_blocks(lame_global_flags *, int); +int CDECL lame_get_force_short_blocks(const lame_global_flags *); + +/* Input PCM is emphased PCM (for instance from one of the rarely + emphased CDs), it is STRONGLY not recommended to use this, because + psycho does not take it into account, and last but not least many decoders + ignore these bits */ +int CDECL lame_set_emphasis(lame_global_flags *, int); +int CDECL lame_get_emphasis(const lame_global_flags *); + + + +/************************************************************************/ +/* internal variables, cannot be set... */ +/* provided because they may be of use to calling application */ +/************************************************************************/ +/* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */ +int CDECL lame_get_version(const lame_global_flags *); + +/* encoder delay */ +int CDECL lame_get_encoder_delay(const lame_global_flags *); + +/* + padding appended to the input to make sure decoder can fully decode + all input. Note that this value can only be calculated during the + call to lame_encoder_flush(). Before lame_encoder_flush() has + been called, the value of encoder_padding = 0. +*/ +int CDECL lame_get_encoder_padding(const lame_global_flags *); + +/* size of MPEG frame */ +int CDECL lame_get_framesize(const lame_global_flags *); + +/* number of PCM samples buffered, but not yet encoded to mp3 data. */ +int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp ); + +/* + size (bytes) of mp3 data buffered, but not yet encoded. + this is the number of bytes which would be output by a call to + lame_encode_flush_nogap. NOTE: lame_encode_flush() will return + more bytes than this because it will encode the reamining buffered + PCM samples before flushing the mp3 buffers. +*/ +int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp ); + +/* number of frames encoded so far */ +int CDECL lame_get_frameNum(const lame_global_flags *); + +/* + lame's estimate of the total number of frames to be encoded + only valid if calling program set num_samples +*/ +int CDECL lame_get_totalframes(const lame_global_flags *); + +/* RadioGain value. Multiplied by 10 and rounded to the nearest. */ +int CDECL lame_get_RadioGain(const lame_global_flags *); + +/* AudiophileGain value. Multipled by 10 and rounded to the nearest. */ +int CDECL lame_get_AudiophileGain(const lame_global_flags *); + +/* the peak sample */ +float CDECL lame_get_PeakSample(const lame_global_flags *); + +/* Gain change required for preventing clipping. The value is correct only if + peak sample searching was enabled. If negative then the waveform + already does not clip. The value is multiplied by 10 and rounded up. */ +int CDECL lame_get_noclipGainChange(const lame_global_flags *); + +/* user-specified scale factor required for preventing clipping. Value is + correct only if peak sample searching was enabled and no user-specified + scaling was performed. If negative then either the waveform already does + not clip or the value cannot be determined */ +float CDECL lame_get_noclipScale(const lame_global_flags *); + + + + + + + +/* + * REQUIRED: + * sets more internal configuration based on data provided above. + * returns -1 if something failed. + */ +int CDECL lame_init_params(lame_global_flags *); + + +/* + * OPTIONAL: + * get the version number, in a string. of the form: + * "3.63 (beta)" or just "3.63". + */ +const char* CDECL get_lame_version ( void ); +const char* CDECL get_lame_short_version ( void ); +const char* CDECL get_lame_very_short_version ( void ); +const char* CDECL get_psy_version ( void ); +const char* CDECL get_lame_url ( void ); +const char* CDECL get_lame_os_bitness ( void ); + +/* + * OPTIONAL: + * get the version numbers in numerical form. + */ +typedef struct { + /* generic LAME version */ + int major; + int minor; + int alpha; /* 0 if not an alpha version */ + int beta; /* 0 if not a beta version */ + + /* version of the psy model */ + int psy_major; + int psy_minor; + int psy_alpha; /* 0 if not an alpha version */ + int psy_beta; /* 0 if not a beta version */ + + /* compile time features */ + const char *features; /* Don't make assumptions about the contents! */ +} lame_version_t; +void CDECL get_lame_version_numerical(lame_version_t *); + + +/* + * OPTIONAL: + * print internal lame configuration to message handler + */ +void CDECL lame_print_config(const lame_global_flags* gfp); + +void CDECL lame_print_internals( const lame_global_flags *gfp); + + +/* + * input pcm data, output (maybe) mp3 frames. + * This routine handles all buffering, resampling and filtering for you. + * + * return code number of bytes output in mp3buf. Can be 0 + * -1: mp3buf was too small + * -2: malloc() problem + * -3: lame_init_params() not called + * -4: psycho acoustic problems + * + * The required mp3buf_size can be computed from num_samples, + * samplerate and encoding rate, but here is a worst case estimate: + * + * mp3buf_size in bytes = 1.25*num_samples + 7200 + * + * I think a tighter bound could be: (mt, March 2000) + * MPEG1: + * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 + * MPEG2: + * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 + * + * but test first if you use that! + * + * set mp3buf_size = 0 and LAME will not check if mp3buf_size is + * large enough. + * + * NOTE: + * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels + * will be averaged into the L channel before encoding only the L channel + * This will overwrite the data in buffer_l[] and buffer_r[]. + * +*/ +int CDECL lame_encode_buffer ( + lame_global_flags* gfp, /* global context handle */ + const short int buffer_l [], /* PCM data for left channel */ + const short int buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* + * as above, but input has L & R channel data interleaved. + * NOTE: + * num_samples = number of samples in the L (or R) + * channel, not the total number of samples in pcm[] + */ +int CDECL lame_encode_buffer_interleaved( + lame_global_flags* gfp, /* global context handlei */ + short int pcm[], /* PCM data for left and right + channel, interleaved */ + int num_samples, /* number of samples per channel, + _not_ number of samples in + pcm[] */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int mp3buf_size ); /* number of valid octets in this + stream */ + + +/* as lame_encode_buffer, but for 'float's. + * !! NOTE: !! data must still be scaled to be in the same range as + * short int, +/- 32768 + */ +int CDECL lame_encode_buffer_float( + lame_global_flags* gfp, /* global context handle */ + const float pcm_l [], /* PCM data for left channel */ + const float pcm_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* as lame_encode_buffer, but for 'float's. + * !! NOTE: !! data must be scaled to +/- 1 full scale + */ +int CDECL lame_encode_buffer_ieee_float( + lame_t gfp, + const float pcm_l [], /* PCM data for left channel */ + const float pcm_r [], /* PCM data for right channel */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); +int CDECL lame_encode_buffer_interleaved_ieee_float( + lame_t gfp, + const float pcm[], /* PCM data for left and right + channel, interleaved */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); + +/* as lame_encode_buffer, but for 'double's. + * !! NOTE: !! data must be scaled to +/- 1 full scale + */ +int CDECL lame_encode_buffer_ieee_double( + lame_t gfp, + const double pcm_l [], /* PCM data for left channel */ + const double pcm_r [], /* PCM data for right channel */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); +int CDECL lame_encode_buffer_interleaved_ieee_double( + lame_t gfp, + const double pcm[], /* PCM data for left and right + channel, interleaved */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); + +/* as lame_encode_buffer, but for long's + * !! NOTE: !! data must still be scaled to be in the same range as + * short int, +/- 32768 + * + * This scaling was a mistake (doesn't allow one to exploit full + * precision of type 'long'. Use lame_encode_buffer_long2() instead. + * + */ +int CDECL lame_encode_buffer_long( + lame_global_flags* gfp, /* global context handle */ + const long buffer_l [], /* PCM data for left channel */ + const long buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* Same as lame_encode_buffer_long(), but with correct scaling. + * !! NOTE: !! data must still be scaled to be in the same range as + * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1) + * + */ +int CDECL lame_encode_buffer_long2( + lame_global_flags* gfp, /* global context handle */ + const long buffer_l [], /* PCM data for left channel */ + const long buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* as lame_encode_buffer, but for int's + * !! NOTE: !! input should be scaled to the maximum range of 'int' + * If int is 4 bytes, then the values should range from + * +/- 2147483648. + * + * This routine does not (and cannot, without loosing precision) use + * the same scaling as the rest of the lame_encode_buffer() routines. + * + */ +int CDECL lame_encode_buffer_int( + lame_global_flags* gfp, /* global context handle */ + const int buffer_l [], /* PCM data for left channel */ + const int buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + + + + + +/* + * REQUIRED: + * lame_encode_flush will flush the intenal PCM buffers, padding with + * 0's to make sure the final frame is complete, and then flush + * the internal MP3 buffers, and thus may return a + * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long + * to hold all possible emitted data. + * + * will also write id3v1 tags (if any) into the bitstream + * + * return code = number of bytes output to mp3buf. Can be 0 + */ +int CDECL lame_encode_flush( + lame_global_flags * gfp, /* global context handle */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int size); /* number of valid octets in this stream */ + +/* + * OPTIONAL: + * lame_encode_flush_nogap will flush the internal mp3 buffers and pad + * the last frame with ancillary data so it is a complete mp3 frame. + * + * 'mp3buf' should be at least 7200 bytes long + * to hold all possible emitted data. + * + * After a call to this routine, the outputed mp3 data is complete, but + * you may continue to encode new PCM samples and write future mp3 data + * to a different file. The two mp3 files will play back with no gaps + * if they are concatenated together. + * + * This routine will NOT write id3v1 tags into the bitstream. + * + * return code = number of bytes output to mp3buf. Can be 0 + */ +int CDECL lame_encode_flush_nogap( + lame_global_flags * gfp, /* global context handle */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int size); /* number of valid octets in this stream */ + +/* + * OPTIONAL: + * Normally, this is called by lame_init_params(). It writes id3v2 and + * Xing headers into the front of the bitstream, and sets frame counters + * and bitrate histogram data to 0. You can also call this after + * lame_encode_flush_nogap(). + */ +int CDECL lame_init_bitstream( + lame_global_flags * gfp); /* global context handle */ + + + +/* + * OPTIONAL: some simple statistics + * a bitrate histogram to visualize the distribution of used frame sizes + * a stereo mode histogram to visualize the distribution of used stereo + * modes, useful in joint-stereo mode only + * 0: LR left-right encoded + * 1: LR-I left-right and intensity encoded (currently not supported) + * 2: MS mid-side encoded + * 3: MS-I mid-side and intensity encoded (currently not supported) + * + * attention: don't call them after lame_encode_finish + * suggested: lame_encode_flush -> lame_*_hist -> lame_close + */ + +void CDECL lame_bitrate_hist( + const lame_global_flags * gfp, + int bitrate_count[14] ); +void CDECL lame_bitrate_kbps( + const lame_global_flags * gfp, + int bitrate_kbps [14] ); +void CDECL lame_stereo_mode_hist( + const lame_global_flags * gfp, + int stereo_mode_count[4] ); + +void CDECL lame_bitrate_stereo_mode_hist ( + const lame_global_flags * gfp, + int bitrate_stmode_count[14][4] ); + +void CDECL lame_block_type_hist ( + const lame_global_flags * gfp, + int btype_count[6] ); + +void CDECL lame_bitrate_block_type_hist ( + const lame_global_flags * gfp, + int bitrate_btype_count[14][6] ); + +#if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0) +#else +/* + * OPTIONAL: + * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file + * pointer fid. These calls perform forward and backwards seeks, so make + * sure fid is a real file. Make sure lame_encode_flush has been called, + * and all mp3 data has been written to the file before calling this + * function. + * NOTE: + * if VBR tags are turned off by the user, or turned off by LAME because + * the output is not a regular file, this call does nothing + * NOTE: + * LAME wants to read from the file to skip an optional ID3v2 tag, so + * make sure you opened the file for writing and reading. + * NOTE: + * You can call lame_get_lametag_frame instead, if you want to insert + * the lametag yourself. +*/ +void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid); +#endif + +/* + * OPTIONAL: + * lame_get_lametag_frame copies the final LAME-tag into 'buffer'. + * The function returns the number of bytes copied into buffer, or + * the required buffer size, if the provided buffer is too small. + * Function failed, if the return value is larger than 'size'! + * Make sure lame_encode flush has been called before calling this function. + * NOTE: + * if VBR tags are turned off by the user, or turned off by LAME, + * this call does nothing and returns 0. + * NOTE: + * LAME inserted an empty frame in the beginning of mp3 audio data, + * which you have to replace by the final LAME-tag frame after encoding. + * In case there is no ID3v2 tag, usually this frame will be the very first + * data in your mp3 file. If you put some other leading data into your + * file, you'll have to do some bookkeeping about where to write this buffer. + */ +size_t CDECL lame_get_lametag_frame( + const lame_global_flags *, unsigned char* buffer, size_t size); + +/* + * REQUIRED: + * final call to free all remaining buffers + */ +int CDECL lame_close (lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* + * OBSOLETE: + * lame_encode_finish combines lame_encode_flush() and lame_close() in + * one call. However, once this call is made, the statistics routines + * will no longer work because the data will have been cleared, and + * lame_mp3_tags_fid() cannot be called to add data to the VBR header + */ +int CDECL lame_encode_finish( + lame_global_flags* gfp, + unsigned char* mp3buf, + int size ); +#endif + + + + + + +/********************************************************************* + * + * decoding + * + * a simple interface to mpglib, part of mpg123, is also included if + * libmp3lame is compiled with HAVE_MPGLIB + * + *********************************************************************/ + +struct hip_global_struct; +typedef struct hip_global_struct hip_global_flags; +typedef hip_global_flags *hip_t; + + +typedef struct { + int header_parsed; /* 1 if header was parsed and following data was + computed */ + int stereo; /* number of channels */ + int samplerate; /* sample rate */ + int bitrate; /* bitrate */ + int mode; /* mp3 frame type */ + int mode_ext; /* mp3 frame type */ + int framesize; /* number of samples per mp3 frame */ + + /* this data is only computed if mpglib detects a Xing VBR header */ + unsigned long nsamp; /* number of samples in mp3 file. */ + int totalframes; /* total number of frames in mp3 file */ + + /* this data is not currently computed by the mpglib routines */ + int framenum; /* frames decoded counter */ +} mp3data_struct; + +/* required call to initialize decoder */ +hip_t CDECL hip_decode_init(void); + +/* cleanup call to exit decoder */ +int CDECL hip_decode_exit(hip_t gfp); + +/* HIP reporting functions */ +void CDECL hip_set_errorf(hip_t gfp, lame_report_function f); +void CDECL hip_set_debugf(hip_t gfp, lame_report_function f); +void CDECL hip_set_msgf (hip_t gfp, lame_report_function f); + +/********************************************************************* + * input 1 mp3 frame, output (maybe) pcm data. + * + * nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r); + * + * input: + * len : number of bytes of mp3 data in mp3buf + * mp3buf[len] : mp3 data to be decoded + * + * output: + * nout: -1 : decoding error + * 0 : need more data before we can complete the decode + * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r + * pcm_l[nout] : left channel data + * pcm_r[nout] : right channel data + * + *********************************************************************/ +int CDECL hip_decode( hip_t gfp + , unsigned char * mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + ); + +/* same as hip_decode, and also returns mp3 header data */ +int CDECL hip_decode_headers( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + ); + +/* same as hip_decode, but returns at most one frame */ +int CDECL hip_decode1( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + ); + +/* same as hip_decode1, but returns at most one frame and mp3 header data */ +int CDECL hip_decode1_headers( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + ); + +/* same as hip_decode1_headers, but also returns enc_delay and enc_padding + from VBR Info tag, (-1 if no info tag was found) */ +int CDECL hip_decode1_headersB( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + , int *enc_delay + , int *enc_padding + ); + + + +/* OBSOLETE: + * lame_decode... functions are there to keep old code working + * but it is strongly recommended to replace calls by hip_decode... + * function calls, see above. + */ +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +int CDECL lame_decode_init(void); +int CDECL lame_decode( + unsigned char * mp3buf, + int len, + short pcm_l[], + short pcm_r[] ); +int CDECL lame_decode_headers( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data ); +int CDECL lame_decode1( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[] ); +int CDECL lame_decode1_headers( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data ); +int CDECL lame_decode1_headersB( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data, + int *enc_delay, + int *enc_padding ); +int CDECL lame_decode_exit(void); + +#endif /* obsolete lame_decode API calls */ + + +/********************************************************************* + * + * id3tag stuff + * + *********************************************************************/ + +/* + * id3tag.h -- Interface to write ID3 version 1 and 2 tags. + * + * Copyright (C) 2000 Don Melton. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +/* utility to obtain alphabetically sorted list of genre names with numbers */ +void CDECL id3tag_genre_list( + void (*handler)(int, const char *, void *), + void* cookie); + +void CDECL id3tag_init (lame_t gfp); + +/* force addition of version 2 tag */ +void CDECL id3tag_add_v2 (lame_t gfp); + +/* add only a version 1 tag */ +void CDECL id3tag_v1_only (lame_t gfp); + +/* add only a version 2 tag */ +void CDECL id3tag_v2_only (lame_t gfp); + +/* pad version 1 tag with spaces instead of nulls */ +void CDECL id3tag_space_v1 (lame_t gfp); + +/* pad version 2 tag with extra 128 bytes */ +void CDECL id3tag_pad_v2 (lame_t gfp); + +/* pad version 2 tag with extra n bytes */ +void CDECL id3tag_set_pad (lame_t gfp, size_t n); + +void CDECL id3tag_set_title(lame_t gfp, const char* title); +void CDECL id3tag_set_artist(lame_t gfp, const char* artist); +void CDECL id3tag_set_album(lame_t gfp, const char* album); +void CDECL id3tag_set_year(lame_t gfp, const char* year); +void CDECL id3tag_set_comment(lame_t gfp, const char* comment); + +/* return -1 result if track number is out of ID3v1 range + and ignored for ID3v1 */ +int CDECL id3tag_set_track(lame_t gfp, const char* track); + +/* return non-zero result if genre name or number is invalid + result 0: OK + result -1: genre number out of range + result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other' + but taken as-is for ID3v2 genre tag */ +int CDECL id3tag_set_genre(lame_t gfp, const char* genre); + +/* return non-zero result if field name is invalid */ +int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue); + +/* return non-zero result if image type is invalid */ +int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size); + +/* lame_get_id3v1_tag copies ID3v1 tag into buffer. + * Function returns number of bytes copied into buffer, or number + * of bytes rquired if buffer 'size' is too small. + * Function fails, if returned value is larger than 'size'. + * NOTE: + * This functions does nothing, if user/LAME disabled ID3v1 tag. + */ +size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size); + +/* lame_get_id3v2_tag copies ID3v2 tag into buffer. + * Function returns number of bytes copied into buffer, or number + * of bytes rquired if buffer 'size' is too small. + * Function fails, if returned value is larger than 'size'. + * NOTE: + * This functions does nothing, if user/LAME disabled ID3v2 tag. + */ +size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size); + +/* normaly lame_init_param writes ID3v2 tags into the audio stream + * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param + * to turn off this behaviour and get ID3v2 tag with above function + * write it yourself into your file. + */ +void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int); +int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp); + +/* experimental */ +int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text); + +/* experimental */ +int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* experimental */ +int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang, + unsigned short const *desc, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue); +#endif + +/* experimental */ +int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue); + +/* experimental */ +int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text); + + +/*********************************************************************** +* +* list of valid bitrates [kbps] & sample frequencies [Hz]. +* first index: 0: MPEG-2 values (sample frequencies 16...24 kHz) +* 1: MPEG-1 values (sample frequencies 32...48 kHz) +* 2: MPEG-2.5 values (sample frequencies 8...12 kHz) +***********************************************************************/ + +extern const int bitrate_table [3][16]; +extern const int samplerate_table [3][ 4]; + +/* access functions for use in DLL, global vars are not exported */ +int CDECL lame_get_bitrate(int mpeg_version, int table_index); +int CDECL lame_get_samplerate(int mpeg_version, int table_index); + + +/* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER + as well since lame_encode_buffer() also returns ID3v2 tag data */ +#define LAME_MAXALBUMART (128 * 1024) + +/* maximum size of mp3buffer needed if you encode at most 1152 samples for + each call to lame_encode_buffer. see lame_encode_buffer() below + (LAME_MAXMP3BUFFER is now obsolete) */ +#define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART) + + +typedef enum { + LAME_OKAY = 0, + LAME_NOERROR = 0, + LAME_GENERICERROR = -1, + LAME_NOMEM = -10, + LAME_BADBITRATE = -11, + LAME_BADSAMPFREQ = -12, + LAME_INTERNALERROR = -13, + + FRONTEND_READERROR = -80, + FRONTEND_WRITEERROR = -81, + FRONTEND_FILETOOLARGE = -82 + +} lame_errorcodes_t; + +#if defined(__cplusplus) +} +#endif +#endif /* LAME_LAME_H */ + diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h new file mode 100644 index 00000000..ad9e677d --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h @@ -0,0 +1,184 @@ +#ifndef LAME_GLOBAL_FLAGS_H +#define LAME_GLOBAL_FLAGS_H + +#ifndef lame_internal_flags_defined +#define lame_internal_flags_defined +struct lame_internal_flags; +typedef struct lame_internal_flags lame_internal_flags; +#endif + + +typedef enum short_block_e { + short_block_not_set = -1, /* allow LAME to decide */ + short_block_allowed = 0, /* LAME may use them, even different block types for L/R */ + short_block_coupled, /* LAME may use them, but always same block types in L/R */ + short_block_dispensed, /* LAME will not use short blocks, long blocks only */ + short_block_forced /* LAME will not use long blocks, short blocks only */ +} short_block_t; + +/*********************************************************************** +* +* Control Parameters set by User. These parameters are here for +* backwards compatibility with the old, non-shared lib API. +* Please use the lame_set_variablename() functions below +* +* +***********************************************************************/ +struct lame_global_struct { + unsigned int class_id; + + /* input description */ + unsigned long num_samples; /* number of samples. default=2^32-1 */ + int num_channels; /* input number of channels. default=2 */ + int samplerate_in; /* input_samp_rate in Hz. default=44.1 kHz */ + int samplerate_out; /* output_samp_rate. + default: LAME picks best value + at least not used for MP3 decoding: + Remember 44.1 kHz MP3s and AC97 */ + float scale; /* scale input by this amount before encoding + at least not used for MP3 decoding */ + float scale_left; /* scale input of channel 0 (left) by this + amount before encoding */ + float scale_right; /* scale input of channel 1 (right) by this + amount before encoding */ + + /* general control params */ + int analysis; /* collect data for a MP3 frame analyzer? */ + int write_lame_tag; /* add Xing VBR tag? */ + int decode_only; /* use lame/mpglib to convert mp3 to wav */ + int quality; /* quality setting 0=best, 9=worst default=5 */ + MPEG_mode mode; /* see enum in lame.h + default = LAME picks best value */ + int force_ms; /* force M/S mode. requires mode=1 */ + int free_format; /* use free format? default=0 */ + int findReplayGain; /* find the RG value? default=0 */ + int decode_on_the_fly; /* decode on the fly? default=0 */ + int write_id3tag_automatic; /* 1 (default) writes ID3 tags, 0 not */ + + int nogap_total; + int nogap_current; + + int substep_shaping; + int noise_shaping; + int subblock_gain; /* 0 = no, 1 = yes */ + int use_best_huffman; /* 0 = no. 1=outside loop 2=inside loop(slow) */ + + /* + * set either brate>0 or compression_ratio>0, LAME will compute + * the value of the variable not set. + * Default is compression_ratio = 11.025 + */ + int brate; /* bitrate */ + float compression_ratio; /* sizeof(wav file)/sizeof(mp3 file) */ + + + /* frame params */ + int copyright; /* mark as copyright. default=0 */ + int original; /* mark as original. default=1 */ + int extension; /* the MP3 'private extension' bit. + Meaningless */ + int emphasis; /* Input PCM is emphased PCM (for + instance from one of the rarely + emphased CDs), it is STRONGLY not + recommended to use this, because + psycho does not take it into account, + and last but not least many decoders + don't care about these bits */ + int error_protection; /* use 2 bytes per frame for a CRC + checksum. default=0 */ + int strict_ISO; /* enforce ISO spec as much as possible */ + + int disable_reservoir; /* use bit reservoir? */ + + /* quantization/noise shaping */ + int quant_comp; + int quant_comp_short; + int experimentalY; + int experimentalZ; + int exp_nspsytune; + + int preset; + + /* VBR control */ + vbr_mode VBR; + float VBR_q_frac; /* Range [0,...,1[ */ + int VBR_q; /* Range [0,...,9] */ + int VBR_mean_bitrate_kbps; + int VBR_min_bitrate_kbps; + int VBR_max_bitrate_kbps; + int VBR_hard_min; /* strictly enforce VBR_min_bitrate + normaly, it will be violated for analog + silence */ + + + /* resampling and filtering */ + int lowpassfreq; /* freq in Hz. 0=lame choses. + -1=no filter */ + int highpassfreq; /* freq in Hz. 0=lame choses. + -1=no filter */ + int lowpasswidth; /* freq width of filter, in Hz + (default=15%) */ + int highpasswidth; /* freq width of filter, in Hz + (default=15%) */ + + + + /* + * psycho acoustics and other arguments which you should not change + * unless you know what you are doing + */ + float maskingadjust; + float maskingadjust_short; + int ATHonly; /* only use ATH */ + int ATHshort; /* only use ATH for short blocks */ + int noATH; /* disable ATH */ + int ATHtype; /* select ATH formula */ + float ATHcurve; /* change ATH formula 4 shape */ + float ATH_lower_db; /* lower ATH by this many db */ + int athaa_type; /* select ATH auto-adjust scheme */ + float athaa_sensitivity; /* dB, tune active region of auto-level */ + short_block_t short_blocks; + int useTemporal; /* use temporal masking effect */ + float interChRatio; + float msfix; /* Naoki's adjustment of Mid/Side maskings */ + + int tune; /* 0 off, 1 on */ + float tune_value_a; /* used to pass values for debugging and stuff */ + + float attackthre; /* attack threshold for L/R/M channel */ + float attackthre_s; /* attack threshold for S channel */ + + + struct { + void (*msgf) (const char *format, va_list ap); + void (*debugf) (const char *format, va_list ap); + void (*errorf) (const char *format, va_list ap); + } report; + + /************************************************************************/ + /* internal variables, do not set... */ + /* provided because they may be of use to calling application */ + /************************************************************************/ + + int lame_allocated_gfp; /* is this struct owned by calling + program or lame? */ + + + + /**************************************************************************/ + /* more internal variables are stored in this structure: */ + /**************************************************************************/ + lame_internal_flags *internal_flags; + + + struct { + int mmx; + int amd3dnow; + int sse; + + } asm_optimizations; +}; + +int is_lame_global_flags_valid(const lame_global_flags * gfp); + +#endif /* LAME_GLOBAL_FLAGS_H */ diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp new file mode 100644 index 00000000..17e05b8b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp @@ -0,0 +1,1636 @@ +#include <windows.h> +#include <Ws2tcpip.h> +#include "../api.h" +#include "Include/shoutcast_output.h" +#include "../../sc_serv3/nmrCommon/stl/stringUtils.h" +#include "../utils.h" +#include "../nu/ServiceBuilder.h" +#include <winamp/dsp.h> + +#pragma intrinsic(memcpy, memset) + +static char buf[1024]; +static char out[1024]; + +extern int iscompatibility; +extern winampDSPModule module; +extern api_service *WASABI_API_SVC; + +static api_connection *CreateConnection(const char *url) +{ + api_connection *conn = 0; + if (WASABI_API_SVC/*module.service*/) + { + if (!_strnicmp(url, "https://", 8)) + { + ServiceBuild(WASABI_API_SVC/*module.service*/, conn, sslConnectionFactoryGUID); + } + else + { + ServiceBuild(WASABI_API_SVC/*module.service*/, conn, connectionFactoryGUID); + } + } + + return conn; +} + +static void ReleaseConnection(api_connection *&conn, const char *url) +{ + if (!conn) { + return; + } + + waServiceFactory *connectionFactory = 0; + if (WASABI_API_SVC/*mod.service*/) + { + if (!_strnicmp(url, "https://", 8)) { + connectionFactory = WASABI_API_SVC/*mod.service*/->service_getServiceByGuid(sslConnectionFactoryGUID); + } else { + connectionFactory = WASABI_API_SVC/*mod.service*/->service_getServiceByGuid(connectionFactoryGUID); + } + } + + if (connectionFactory) { + connectionFactory->releaseInterface(conn); + } + + conn = 0; +} + +int resolvenow(const char *hostname, unsigned short port, addrinfo **addr, int sockettype, char **saddress) +{ + addrinfo hints; + memset(&hints,0,sizeof(hints)); + hints.ai_family = PF_UNSPEC; + if (hostname) + hints.ai_flags = AI_NUMERICHOST; + else + hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + hints.ai_socktype = sockettype; + + char portString[32] = {0}; + sprintf(portString, "%u", (unsigned int)port); + + if (getaddrinfo(hostname, portString, &hints, addr) == 0) + { + return 0; + } + else + { + hints.ai_flags = 0; + if (getaddrinfo(hostname, portString, &hints, addr) == 0) + { + // need to make sure we're not using 0.0.0.0 + // as that will break us when using localhost + bool found = false; + struct addrinfo *ptr = NULL; + for (ptr = *addr; ptr != NULL; ptr = ptr->ai_next) + { + char* address = ::inet_ntoa(((sockaddr_in*)(ptr->ai_addr))->sin_addr); + if (strcmp(address, "0.0.0.0")) + { + *saddress = address; + *addr = ptr; + return 0; + } + } + } + } + return -1; +} + +void compatible_connect(T_OUTPUT *Output, int port) { + + // due to a bug in re-using the connection + // with Winamp's jnetlib implementation we + // need to do a force drop and make a new + // instance so all is initialised properly + if (Output->Output && iscompatibility) { + Output->Output->Close(); + Output->Output->Release(); + Output->Output = NULL; + } + + // create the connection only when it's needed + if (!Output->Output) { + Output->Output = CreateConnection(""); + if (Output->Output) { + Output->Output->Open(API_DNS_AUTODNS, 16384, 16384); + } + } + + if (Output->Output) { + if (iscompatibility && !strnicmp(Output->Config->Address, "localhost", 9)) { + addrinfo *saddr = 0; + char *address = 0; + if (!resolvenow(Output->Config->Address, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1), &saddr, SOCK_STREAM, &address) && address) { + Output->Output->connect(address, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1)); + } else { + Output->Output->connect(Output->Config->Address, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1)); + } + } else { + Output->Output->connect(Output->Config->Address, port); + } + } +} + +SHOUTCAST_OUTPUT::SHOUTCAST_OUTPUT() : lame_init(0), lame_init_params(0), lame_encode_buffer_interleaved(0), lame_encode_flush(0), libinst(NULL) { + IOwnEncoder = 0; + SetEncoder(DEFAULT_ENCODER); + OutputManager.AddHandler(OUT_DISCONNECTED, Output_Disconnected); + OutputManager.AddHandler(OUT_CONNECT, Output_Connect); + OutputManager.AddHandler(OUT_REQUEST_CIPHER, Output_Request_Cipher);//cipher request + OutputManager.AddHandler(OUT_RECV_CIPHER, Output_Receive_Cipher);// and process cipher + OutputManager.AddHandler(OUT_SENDAUTH, Output_SendAuth); + OutputManager.AddHandler(OUT_RECVAUTHRESPONSE, Output_RecvAuthResponse); + OutputManager.AddHandler(OUT_SEND_MIME, Output_Send_Mime); + OutputManager.AddHandler(OUT_RECV_MIME, Output_Recv_Mime); + OutputManager.AddHandler(OUT_SEND_BITRATE, Output_Send_Bitrate); + OutputManager.AddHandler(OUT_RECV_BITRATE, Output_Recv_Bitrate); + OutputManager.AddHandler(OUT_SEND_BUFSIZE, Output_Send_Buf_Size); + OutputManager.AddHandler(OUT_RECV_BUFSIZE, Output_Recv_Buf_Size); + OutputManager.AddHandler(OUT_SEND_MAX, Output_Send_Max_Size); + OutputManager.AddHandler(OUT_RECV_MAX, Output_Recv_Max_Size); + OutputManager.AddHandler(OUT_SENDYP, Output_SendYP); + OutputManager.AddHandler(OUT_SEND_INITFLUSH, Output_Send_InitFlush); + OutputManager.AddHandler(OUT_RECV_INITFLUSH, Output_Recv_InitFlush); + OutputManager.AddHandler(OUT_SEND_INITSTANDBY, Output_Send_InitStandby); + OutputManager.AddHandler(OUT_RECV_INITSTANDBY, Output_Recv_InitStandby); + //OutputManager.AddHandler(OUT_SEND_INTRO, Output_Send_Intro); + //OutputManager.AddHandler(OUT_RECV_INTRO, Output_Recv_Intro); + //OutputManager.AddHandler(OUT_SEND_BACKUP, Output_Send_Backup); + //OutputManager.AddHandler(OUT_RECV_BACKUP, Output_Recv_Backup); + OutputManager.AddHandler(OUT_SENDCONTENT, Output_SendContent); + OutputManager.AddHandler(OUT_DISCONNECT, Output_Disconnect); + OutputManager.AddHandler(OUT_RECONNECT, Output_Reconnect); + OutputManager.AddHandler(OUT_TITLESENDUPDATE, Output_Title_SendUpdate); + OutputManager.AddHandler(OUT_SEND_METADATA, Output_Send_Metadata); + OutputManager.AddHandler(OUT_SEND_ARTWORK, Output_Send_Artwork); + ReleaseMutex((mutex = CreateMutex(NULL, TRUE, NULL))); +} + +void SHOUTCAST_OUTPUT::SetLame(void *init, void *params, void *encode, void *finish) { + LOCK{ + lame_init = (void (__cdecl *)(void))init; + lame_init_params = (void (__cdecl *)(lame_global_flags *))params; + lame_encode_buffer_interleaved = (int (__cdecl *)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size))encode; + lame_encode_flush = (int (__cdecl *)(lame_global_flags *, char *mp3buffer, int size))finish; + }UNLOCK +} + +SHOUTCAST_OUTPUT::~SHOUTCAST_OUTPUT() { + LOCK + CloseHandle(mutex); +} + +/* +Create a Uvox frame +*/ + +void SHOUTCAST_OUTPUT::createUvoxFrame(int length, char *payload_in, int type, T_OUTPUT *userData) { + int len = min(length, UV_MAX_DATA_LEN); + uv2xHdr hdr2 = { UV_SYNC_BYTE, UV_RESERVED, htons(type), htons(len), {UV_END}, UV_END }; + if (payload_in && len > 0 && len <= UV_MAX_DATA_LEN) { + memcpy(hdr2.m_data, payload_in, len); + } + int sent = UV_HEADER_LEN + len + UV_END_LEN; + userData->Output->send(&hdr2, sent); + userData->Info.BytesSent += sent; +} + +int SHOUTCAST_OUTPUT::createUvoxMetaFrame(int length, char *payload_in, int type, + T_OUTPUT *userData, unsigned short id, + unsigned short span) { + // check for the class type and abort if it's not right - will typically be 0x0000 or 0xXXFF + if ((type >= 0x3000) && (type < 0x5000)) { + int artType = ((type & 0xFF00) == (MSG_METADATA_ALBUMART|MSG_METADATA_PLAYING_ART)); + int len = min(length, UV_MAX_META_LEN); + uv2xMetadataHdr metaHdr = { UV_SYNC_BYTE, UV_RESERVED, htons(type), htons(len + UV_META_LEN), + htons(id), htons(span), htons((span > 1 ? userData->Info.art_index[artType] : 0x1)), + {UV_END}, UV_END }; + + if (payload_in && len > 0 && len <= UV_MAX_META_LEN) { + memcpy(metaHdr.m_data, payload_in, len); + } + + int sent = UV_HEADER_LEN + len + UV_META_LEN + UV_END_LEN; + if(userData->Output->send(&metaHdr, sent) != -1) + { + userData->Info.BytesSent += sent; + } + else + { + // if the send buffer is still full then we need to nudge + // it all along so that we can send the metadata as not + // checking for this will cause the loss of meta frames + while(userData->Output->GetSendBytesAvailable()) + { + Sleep(10); // sleep a bit and try again + userData->Output->run(); + if(userData->Output->send(&metaHdr, sent) != -1) + { + userData->Info.BytesSent += sent; + break; + } + } + } + return id+1; + } + return id; +} + +/* +Parse a Uvox Frame +*/ +int SHOUTCAST_OUTPUT::parseUvoxFrame(char *payload_in, char *payload_out) { + LOCK{ + uv2xHdr *hdr2 = (uv2xHdr *)payload_in; + int len = min(ntohs(hdr2->msgLen), UV_MAX_DATA_LEN); + memmove(payload_out, hdr2->m_data, len); + // just doing this to ensure its terminated correctly + hdr2->m_data[len] = UV_END; + }UNLOCK + return 1; +} + +/* +Check for known Uvox Error responses +*/ +int SHOUTCAST_OUTPUT::checkUvoxFrameForError(char *pload_out, int state, T_OUTPUT *userData) { + LOCK{ + if (userData->Info.Succeeded == -2 && + userData->Info.ErrorMsg) { + free(userData->Info.ErrorMsg); + } + userData->Info.Succeeded = 1; + userData->Info.ErrorMsg = 0; + + if (!strcmp("NAK:2.1:Deny", pload_out) || !strcmp("NAK:Deny", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "NAK:Deny"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Stream ID Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "StreamID"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Stream Moved", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "StreamMoved"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:Bit Rate Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "BitrateError"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Version Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "VersionError"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Parse Error", pload_out) || !strcmp("NAK:Parse Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "ParseError"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:Stream In Use", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "InUse"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK", pload_out)) { + userData->Info.Succeeded = -2; + // TODO + userData->Info.ErrorMsg = _strdup(pload_out); + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("ACK", pload_out)) { + userData->Info.Succeeded = 1; + UNLOCK + return userData->Info.Succeeded; + } + + }UNLOCK + return userData->Info.Succeeded; +} + +int SHOUTCAST_OUTPUT::Run(int mode, void *Input, int InputSize, int SaveEncoder) { + int in_used = 0; + LOCK{ + int bad = 1; + + if (mode & OM_OTHER) OutputManager.Run(OutputManager.GetNumJobs()); + + for (int i = OutputManager.GetNumJobs()-1; i >= 0; i--) { + T_OUTPUT *Output = OutputManager[i]; + if (mode & OM_OUTPUT && Output->Output) { + Output->Output->run(); + } + + int state = OutputManager.GetJobState(i); + if (state != OUT_DISCONNECTED) { + int outstate = (Output->Output ? Output->Output->get_state() : CONNECTION_STATE_ERROR); + if ((outstate == CONNECTION_STATE_ERROR || outstate == CONNECTION_STATE_CLOSED) && + state != OUT_DISCONNECT && state != OUT_RECONNECT) { + if (Output->Type == OUTTYPE_SOURCE) { + Output->Info.ConnectionTime = clock(); + Output->SlowClose = 0; + OutputManager.SetJobState(i, state = OUT_DISCONNECT); // kill the connection + } else if (Output->Type == OUTTYPE_TITLE) { + delete OutputManager[i]; + OutputManager.DelJob(i); // title update failed, remove it + } + } + if (state == OUT_SENDCONTENT) bad = 0; + } else { + if (Output->Type == OUTTYPE_TITLE) { + #ifndef _DEBUG + delete OutputManager[i]; + #endif + OutputManager.DelJob(i); // title update succeeded, remove it + } + } + }// for num jobs + if (!bad && Input && (InputSize > 0) && Encoder && (mode & OM_ENCODE)) { + while (in_used < InputSize && InputSize > 0) { + int inused = 0; + char EncodedData[32768] = {0}; + int out_used = Encoder->Encode(((char *)Input+in_used), (InputSize-in_used), EncodedData, sizeof(EncodedData), &inused); + in_used += inused; + + for (int i = OutputManager.GetNumJobs()-1; i >= 0; i--) { + T_OUTPUT *Output = OutputManager[i]; + int state = OutputManager.GetJobState(i); + if (out_used > 0 && state == OUT_SENDCONTENT) { + int sofar = 0, type = 0; + + // when enabled this will save the encoded output to a local file for DJ backup, etc + if (SaveEncoder != -1) { + WriteSaveEncoded(SaveEncoder, EncodedData, out_used); + } + + if (strcmp("audio/aacp", Output->ContentType)==0) { type = AACP_DATA; } // aacp + if (strcmp("audio/aac", Output->ContentType)==0) { type = AAC_LC_DATA; } // aac lc + if (strcmp("audio/mpeg", Output->ContentType)==0) { type = MP3_DATA; } // mp3 + if (strcmp("audio/ogg", Output->ContentType)==0) { type = OGG_DATA; } // ogg + + // make sure that this doesn't go negative otherwise things will go wrong + while (out_used > 0) { + int delta = Output->Output->GetSendBytesAvailable(); + + if (LOBYTE(Output->Config->protocol) != 1) { + // try to clamp things so that it's within the packet size limits + delta = min(delta, UV_MAX_DATA_LEN); + } + + delta = min(delta, out_used); + if (delta > 7) { + if (LOBYTE(Output->Config->protocol) != 1) { + // send sc2 data frames + createUvoxFrame(delta, EncodedData+sofar, type, Output); + } else { + // send sc1 data + Output->Info.BytesSent += delta; + Output->Output->send(EncodedData+sofar, delta); + } + } else { + // check for the connection having dropped and we're just spinning + // and if we are then we need to abort from here else we lock up + int outstate = Output->Output->get_state(); + if (outstate == CONNECTION_STATE_CLOSED || outstate == CONNECTION_STATE_ERROR) { + break; + } + Sleep(10); // sleep a bit and try again + Output->Output->run(); + } + sofar+=delta; + out_used-=delta; + } + } + } + if (InputSize <= 0) break; + } + } + }UNLOCK + return in_used; +} + +int SHOUTCAST_OUTPUT::AddOutput(int Connection, T_OUTPUT_CONFIG *Config, void (*TitleCallback)(const int Connection, const int Mode)) { + if (Connection >= 0 && Connection < 5) { + T_OUTPUT *job = new T_OUTPUT; + job->Bitrate = 0; + job->ContentType = "audio/mpeg"; + if (Encoder) { + int infosize = sizeof(T_EncoderIOVals); + T_EncoderIOVals *EncSettings = (T_EncoderIOVals *)Encoder->GetExtInfo(&infosize); + if (EncSettings && infosize) job->Bitrate = EncSettings->output_bitRate; + job->ContentType = Encoder->GetContentType(); + } + job->Connection = Connection; + job->TitleCallback = TitleCallback; + job->Config = Config; + job->Info.Reconnect = Config->AutoRecon; + job->Info.ReconnectTime = Config->ReconTime; + job->Type = OUTTYPE_SOURCE; + job->SlowClose = 0; + // trigger a title update on start as artwork + // is done later so does not need to set here + job->Info.meta_cached = 1; + + int retval = -1; + LOCK{ + retval = OutputManager.AddJob(OUT_DISCONNECTED, job); + }UNLOCK + return retval; + } + return -1; +} + +void SHOUTCAST_OUTPUT::UpdateOutput(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + Output->Info.Reconnect = Output->Config->AutoRecon; + Output->Info.ReconnectTime = Output->Config->ReconTime; + } + }UNLOCK + } +} + +void SHOUTCAST_OUTPUT::RemoveOutput(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + OutputManager.DelJob(Connection); + delete Output; + } + }UNLOCK + } +} + +int SHOUTCAST_OUTPUT::ConnectOutput(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Encoder) { + UpdateOutput(Connection); + + compatible_connect(Output, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1)); + + Output->Bitrate = 0; + Output->ContentType = "audio/mpeg"; + + if (Encoder) { + int infosize = sizeof(T_EncoderIOVals); + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *)Encoder->GetExtInfo(&infosize); + if (EncSettings && infosize) Output->Bitrate = EncSettings->output_bitRate; + Output->ContentType = Encoder->GetContentType(); + } + OutputManager.SetJobState(Connection, OUT_CONNECT); + } + }UNLOCK + } + return 1; +} + +int SHOUTCAST_OUTPUT::DisconnectOutput(int Connection, int withReconnect, int reconnectTime) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + int state = OutputManager.GetJobState(Connection); + if (state != OUT_DISCONNECTED) { + Output->Info.Reconnect = withReconnect >= 0 ? withReconnect : Output->Config->AutoRecon; + Output->Info.ReconnectTime = reconnectTime >= 0 ? reconnectTime : Output->Config->ReconTime; + Output->SlowClose = 0; + if (Encoder) { + int infosize = sizeof(T_EncoderIOVals); + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *)Encoder->GetExtInfo(&infosize); + if (EncSettings && infosize) Output->Bitrate = EncSettings->output_bitRate; + Output->ContentType = Encoder->GetContentType(); + } + OutputManager.SetJobState(Connection, OUT_DISCONNECT); + } + } + }UNLOCK + } + return 1; +} + +void SHOUTCAST_OUTPUT::SetEncoder(C_ENCODER *encoder, int takeOwnership) { + LOCK{ + if (IOwnEncoder && Encoder && Encoder != DEFAULT_ENCODER) + delete Encoder; + if (encoder != DEFAULT_ENCODER) { + IOwnEncoder = takeOwnership; + Encoder = encoder; + } else { + try { + Encoder = new C_ENCODER_MP3(lame_init, lame_init_params, lame_encode_buffer_interleaved, lame_encode_flush); + IOwnEncoder = 1; + } catch(...) { + Encoder = NULL; + } + } + }UNLOCK +} + +C_ENCODER *SHOUTCAST_OUTPUT::GetEncoder() { + C_ENCODER *Enc = NULL; + LOCK{ + Enc = Encoder; + }UNLOCK + return Enc; +} + +bool validateTitle(wchar_t **dest, const wchar_t* src) +{ + bool allowed = true; + int src_len = lstrlenW(src); + char *str = (char*)calloc(src_len, sizeof(char)); + if (str) + { + WideCharToMultiByte(CP_ACP, 0, src, -1, str, src_len, 0, 0); + + std::string updinfoSong(str); + if (!stringUtil::stripAlphaDigit(updinfoSong).empty()) + { + // work on lowercase comparison as well as doing a check to see if + // after removing white space + punctuation we have a valid title. + std::string m_checkUpdinfoSong = stringUtil::toLower(updinfoSong); + + // exclude weird title updates from being accepted + // as no point in giving junk to the user later on + if (m_checkUpdinfoSong.find("!doctype") != string::npos || + m_checkUpdinfoSong.find("<script") != string::npos || + m_checkUpdinfoSong.find("<html") != string::npos || + m_checkUpdinfoSong.find("<body") != string::npos || + m_checkUpdinfoSong.find("<div") != string::npos || + m_checkUpdinfoSong.find("%] ") != string::npos || + m_checkUpdinfoSong.find("invalid resource") != string::npos || + (m_checkUpdinfoSong.find("nextsong") != string::npos && + m_checkUpdinfoSong.find("sctrans2next") != string::npos) || + + m_checkUpdinfoSong.find("radio online") != string::npos || + + m_checkUpdinfoSong.find("track ") == 0 || + m_checkUpdinfoSong.find("track0") == 0 || + m_checkUpdinfoSong.find("track1") == 0 || + m_checkUpdinfoSong.find("stream ") == 0 || + m_checkUpdinfoSong.find("no artist ") == 0 || + m_checkUpdinfoSong.find("new artist ") == 0 || + m_checkUpdinfoSong.find("line-in ") == 0 || + m_checkUpdinfoSong.find("inter_") == 0 || + m_checkUpdinfoSong.find("jj mckay - ") == 0 || + m_checkUpdinfoSong.find("artist - ") == 0 || + + m_checkUpdinfoSong.find("$") == 0 || + m_checkUpdinfoSong.find("%") == 0 || + m_checkUpdinfoSong.find("&") == 0 || + m_checkUpdinfoSong.find("[") == 0 || + m_checkUpdinfoSong.find("?") == 0 || + m_checkUpdinfoSong.find("_") == 0 || + m_checkUpdinfoSong.find("- ") == 0 || + m_checkUpdinfoSong.find(". ") == 0 || + + m_checkUpdinfoSong == "-" || + m_checkUpdinfoSong == "auto dj" || + m_checkUpdinfoSong == "ao vivo" || + m_checkUpdinfoSong == "unknown" || + m_checkUpdinfoSong == "test" || + m_checkUpdinfoSong == "dsp" || + m_checkUpdinfoSong == "demo" || + m_checkUpdinfoSong == "line input" || + m_checkUpdinfoSong == "dj mike llama - llama whippin` intro" || + m_checkUpdinfoSong == "preview") { + allowed = false; + } + } + else + { + allowed = false; + } + } + else + { + allowed = false; + } + + if (dest) { + if (*dest) + { + free(*dest); + } + *dest = _wcsdup(allowed ? src : L""); + } + + return allowed; +} + +void SHOUTCAST_OUTPUT::UpdateTitleCache(wchar_t *Title, std::vector<std::wstring> NextList, wchar_t *Song, + wchar_t *Album, wchar_t *Artist, wchar_t *Genre, wchar_t *Comment, + wchar_t* Year, int Connection, bool sendNext) { + int update = 0; + + if(!metadata.Title || wcscmp(metadata.Title, Title)) { + if (metadata.Title) free(metadata.Title); + metadata.Title = _wcsdup(Title); + update++; + } + + metadata.NextList.resize(0); + if (!NextList.empty()) { + metadata.NextList = NextList; + update++; + } + + if(!metadata.Song || wcscmp(metadata.Song, (Song ? Song : L""))) { + if (validateTitle(&metadata.Song, (Song ? Song : L""))) { + update++; + } + } + + if(!metadata.Album || wcscmp(metadata.Album, (Album ? Album : L""))) { + if (validateTitle(&metadata.Album, (Album ? Album : L""))) { + update++; + } + } + + if(!metadata.Artist || wcscmp(metadata.Artist, (Artist ? Artist : L""))) { + if (validateTitle(&metadata.Artist, (Artist ? Artist : L""))) { + update++; + } + } + + if(!metadata.Genre || wcscmp(metadata.Genre, (Genre ? Genre : L""))) { + if (validateTitle(&metadata.Genre, (Genre ? Genre : L""))) { + update++; + } + } + + if(!metadata.Comment || wcscmp(metadata.Comment, (Comment ? Comment : L""))) { + if (validateTitle(&metadata.Comment, (Comment ? Comment : L""))) { + update++; + } + } + + if(!metadata.Year || wcscmp(metadata.Year, (Year ? Year : L""))) { + if (validateTitle(&metadata.Year, (Year ? Year : L""))) { + update++; + } + } + + UpdateTitle(0, NextList, Connection, sendNext); +} + +void SHOUTCAST_OUTPUT::UpdateAlbumArtCache(void* APIC, int APICLength, int APICType, int Connection) { + int artType = ((APICType & 0xFF00) == (MSG_METADATA_ALBUMART|MSG_METADATA_PLAYING_ART)); + // make sure we're within the metadata limits + // which is 32 * max metadata payload (523872) + // and if not then we discard the artwork + int prevAPICLength = metadata.APICLength[artType]; + if (APICLength > UV_MAX_TOTAL_META_LEN) { + metadata.APICLength[artType] = 0; + metadata.APIC[artType] = 0; + } else { + metadata.APIC[artType] = APIC; + metadata.APICLength[artType] = APICLength; + } + metadata.APICType[artType] = APICType; + + // update the metadata cache usage so we can work + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + // only update the flag if it's new stream art or is playing and is different from before + if (!(!prevAPICLength && artType && (prevAPICLength == metadata.APICLength[artType]))) { + Output->Info.meta_cached |= (!artType ? 2 : 4); + } + } + }UNLOCK + + UpdateArtwork(Connection); + } +} + +void SHOUTCAST_OUTPUT::UpdateTitle(wchar_t *Title, std::vector<std::wstring> NextList, + int Connection, bool sendNext, bool UseCache) { + //LOCK{ + if (Connection >= 0 && Connection < 5) { + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Output->Type == OUTTYPE_SOURCE && + (OutputManager.GetJobState(Connection) == OUT_SENDCONTENT || + OutputManager.GetJobState(Connection) == OUT_SEND_METADATA)) { + + // clear the metadata send flag + if (Output->Info.meta_cached & 1) { + Output->Info.meta_cached -= 1; + } + + if (LOBYTE(Output->Config->protocol) == 1 && Output->Config->doTitleUpdate) { + T_OUTPUT *job = new T_OUTPUT; + job->Bitrate = Output->Bitrate; + job->ContentType = Encoder->GetContentType(); + job->Config = Output->Config; + job->Type = OUTTYPE_TITLE; + job->SlowClose = 0; + compatible_connect(job, job->Config->Port); + + wchar_t *title = (UseCache?metadata.Title:Title); + if (title) { + validateTitle(&job->Info.Title, title); + validateTitle(&Output->Info.Title, title); + } else { + validateTitle(&job->Info.Title, Output->Info.Title); + } + + OutputManager.AddJob(OUT_CONNECT, job); + } else if (LOBYTE(Output->Config->protocol) != 1) { + validateTitle(&Output->Info.Title, (UseCache?metadata.Title:Title)); + + //#ifndef _DEBUG + if (UseCache) { + if (!metadata.NextList.empty()) { + Output->Info.NextList = metadata.NextList; + } + } else { + if (!NextList.empty()) { + Output->Info.NextList = NextList; + } + } + //#endif + + validateTitle(&Output->Info.Song, (metadata.Song && UseCache ? metadata.Song : L"")); + validateTitle(&Output->Info.Album, (metadata.Album && UseCache ? metadata.Album : L"")); + validateTitle(&Output->Info.Artist, (metadata.Artist && UseCache ? metadata.Artist : L"")); + validateTitle(&Output->Info.Genre, (metadata.Genre ? metadata.Genre : L"")); + validateTitle(&Output->Info.Comment, (metadata.Comment && UseCache ? metadata.Comment : L"")); + validateTitle(&Output->Info.Year, (metadata.Year && UseCache ? metadata.Year : L"")); + + if (Output->Config->doTitleUpdate) { + // only output friendlier line breaks when debugging is needed + #ifdef _DEBUG + #define XML_DEBUG + #endif + + #ifdef XML_DEBUG + #define EOL "\n" + #define TAB "\t" + #else + #define EOL "" + #define TAB "" + #endif + stringstream s; + s << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<metadata>" EOL; + + wchar_t * title = (Output->Info.Song && *Output->Info.Song ? Output->Info.Song : Output->Info.Title); + if (validateTitle(0, title)) { + s << "<TIT2>" << ConvertToUTF8Escaped(title) << "</TIT2>" EOL; + } + + if (Output->Info.Album && Output->Info.Album[0]) s << "<TALB>" << ConvertToUTF8Escaped(Output->Info.Album) << "</TALB>" EOL; + if (Output->Info.Artist && Output->Info.Artist[0]) s << "<TPE1>" << ConvertToUTF8Escaped(Output->Info.Artist) << "</TPE1>" EOL; + if (Output->Info.Year && Output->Info.Year[0]) s << "<TYER>" << ConvertToUTF8Escaped(Output->Info.Year) << "</TYER>" EOL; + if (Output->Info.Comment && Output->Info.Comment[0]) s << "<COMM>" << ConvertToUTF8Escaped(Output->Info.Comment) << "</COMM>" EOL; + if (Output->Info.Genre && Output->Info.Genre[0]) s << "<TCON>" << ConvertToUTF8Escaped(Output->Info.Genre) << "</TCON>" EOL; + + s << "<TENC>SHOUTcast Source DSP v" << sourceVersion << "</TENC>" EOL; + + if (Output->Config->Description[0]) s << "<TRSN>" << escapeXML(Output->Config->Description) << "</TRSN>" EOL; + + s << "<WORS>" << escapeXML(Output->Config->ServerURL && Output->Config->ServerURL[0] ? Output->Config->ServerURL : "http://www.shoutcast.com") << "</WORS>" EOL; + + if (validateTitle(0, title)) { + s << "<extension>" EOL TAB "<TITLE seq=\"1\">" << ConvertToUTF8Escaped(title) << "</TITLE>" EOL; + + if (!Output->Info.NextList.empty() && sendNext) { + s << TAB; + std::string soon; + for (size_t idx = 0, seq = 0; idx < Output->Info.NextList.size(); idx++) { + if (validateTitle(0, Output->Info.NextList[idx].c_str())) + { + std::string next = ConvertToUTF8Escaped(Output->Info.NextList[idx].c_str()); + s << "<TITLE seq=\"" << (seq+2) << "\">" << next << "</TITLE>" EOL TAB; + // store the first item as that is used for the 'soon' item + if (seq == 0) soon = next; + seq++; + } + } + if (!soon.empty()) { + s << "<soon>" << soon << "</soon>" EOL; + } + } + s << "</extension>" EOL; + } + s << "</metadata>"; + + // TODO make sure this will split up > UV_MAX_META_FRAME_LEN blocks + mid = createUvoxMetaFrame(s.str().length(),(char*)s.str().data(), MSG_METADATA_XML_NEW, Output, mid); + } + } + } + } +} + +void SHOUTCAST_OUTPUT::UpdateArtwork(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Output->Type == OUTTYPE_SOURCE) { + + if (LOBYTE(Output->Config->protocol) != 1) { + Output->Info.APIC[0] = metadata.APIC[0]; + Output->Info.APICLength[0] = metadata.APICLength[0]; + Output->Info.APICType[0] = metadata.APICType[0]; + + Output->Info.APIC[1] = metadata.APIC[1]; + Output->Info.APICLength[1] = metadata.APICLength[1]; + Output->Info.APICType[1] = metadata.APICType[1]; + + // calculate the album art span inorder to use it once we've sent the title in following callbacks + for (int i = 0; i < 2; i++) { + int AlbumArtSpan = (metadata.APICLength[i] / UV_MAX_DATA_LEN) + ((metadata.APICLength[i] % UV_MAX_DATA_LEN) < UV_MAX_DATA_LEN ? 1 : 0); + // if over the limit then abort trying to send this + if (AlbumArtSpan > UV_MAX_META_FRAGMENTS) { + AlbumArtSpan = 1; + Output->Info.APIC[i] = 0; + } + + Output->Info.art_cached_span[i] = Output->Info.art_cached[i] = AlbumArtSpan; + Output->Info.art_index[i] = 1; + Output->Info.art_cached_length[i] = Output->Info.APICLength[i]; + } + } + } + }UNLOCK + } +} + +int SHOUTCAST_OUTPUT::UpdateAlbumArt(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Output->Type == OUTTYPE_SOURCE && + OutputManager.GetJobState(Connection) == OUT_SEND_ARTWORK) { + int artType = (Output->Info.meta_cached & 4 ? 1 : (Output->Info.meta_cached & 2 ? 0 : -1)); + if (artType != -1 && LOBYTE(Output->Config->protocol) != 1 && Output->Info.art_cached[artType] > 0) { + int type = Output->Info.APICType[artType]; + int length = Output->Info.art_cached_length[artType]; + int count = Output->Info.art_cached_span[artType] - Output->Info.art_cached[artType]; + + // attempt to process as a multi-part message as APIC most likely won't fit in just one uvox packet + if (length >= UV_MAX_META_LEN) { + createUvoxMetaFrame(UV_MAX_META_LEN, + (char*)Output->Info.APIC[artType]+(count * UV_MAX_META_LEN), + type, Output, mid, Output->Info.art_cached_span[artType]); + Output->Info.art_cached_length[artType] -= UV_MAX_META_LEN; + } else { + createUvoxMetaFrame(length, + (char*)Output->Info.APIC[artType]+(count * UV_MAX_META_LEN), + type, Output, mid, Output->Info.art_cached_span[artType]); + Output->Info.art_cached_length[artType] = 0; + } + Output->Info.art_index[artType] += 1; + + if (Output->Info.art_cached[artType] == 1) { + mid+=1; + Output->Info.meta_cached -= (artType ? 4 : 2); + // reset the values so we can re-send on disconnect, etc + Output->Info.art_cached[artType] = Output->Info.art_cached_span[artType]; + Output->Info.art_index[artType] = 1; + Output->Info.art_cached_length[artType] = Output->Info.APICLength[artType]; + } + } + } + }UNLOCK + } + return 1; +} + +enum OUTPUTSTATE SHOUTCAST_OUTPUT::GetState(int Connection) { + int retval = -1; + if (Connection >= 0 && Connection < 5) { + LOCK{ + retval = OutputManager.GetJobState(Connection); + }UNLOCK + } + return retval == -1 ? OUT_ERROR : (enum OUTPUTSTATE)retval; +} + +T_OUTPUT_CONFIG *SHOUTCAST_OUTPUT::operator[](int Connection) { + return GetOutput(Connection); +} + +T_OUTPUT_CONFIG *SHOUTCAST_OUTPUT::GetOutput(int Connection) { + T_OUTPUT_CONFIG *Config = NULL; + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) Config = Output->Config; + }UNLOCK + } + return Config; +} + +T_OUTPUT_INFO *SHOUTCAST_OUTPUT::GetOutputInfo(int Connection) { + T_OUTPUT_INFO *Info = NULL; + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) Info = &Output->Info; + }UNLOCK + } + return Info; +} + +// Protected methods + +int SHOUTCAST_OUTPUT::Output_Disconnected(int state, int last_state, T_OUTPUT *userData) { + STATE; + LOCK{ + userData->Config->protocol_retry = 0; + }UNLOCK + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_Connect(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Config->protocol_retry = MAKEWORD(0, HIBYTE(userData->Config->protocol_retry)); + userData->Info.Succeeded = 1; + if (userData->Output) { + userData->Output->FlushSend(); + } + + if ((clock() - userData->Info.ConnectionTime) / CLOCKS_PER_SEC >= 1/*userData->Info.ReconnectTime*/) { + if (userData->Output && userData->Output->get_state() == CONNECTION_STATE_CONNECTED) { // are we connected yet? + userData->Info.ConnectionTime = clock(); + if (userData->Type == OUTTYPE_SOURCE) { + userData->Info.Reconnect = userData->Config->AutoRecon; + userData->Info.ReconnectTime = userData->Config->ReconTime; + userData->Info.ConnectedAt = time(NULL); + UNLOCK + return OUT_REQUEST_CIPHER; // connected, go authenticate + } else if (userData->Type == OUTTYPE_TITLE) { + UNLOCK + return OUT_TITLESENDUPDATE; // connected, go update the title + } + } + } + }UNLOCK + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_SendContent(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + if (userData->Output->get_state() == CONNECTION_STATE_ERROR) { // connection error? + return OUT_DISCONNECT; // error happened, go shut down + } + + // check if we have any metadata or artwork to dispatch (depends on protocol) + if (userData->Info.meta_cached & 1) { + userData->Info.meta_cached -= 1; + return OUT_SEND_METADATA; + } else if (LOBYTE(userData->Config->protocol) != 1 && + (userData->Info.meta_cached & 2 || userData->Info.meta_cached & 4) && + userData->Info.art_cached > 0) { + return OUT_SEND_ARTWORK; + } + + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_Disconnect(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + //STATE; + LOCK{ + // reset the metadata flags so it will trigger on a connection coming back + // with usage of what was already cached to determine what we will re-send + userData->Info.meta_cached = 1|(userData->Info.art_cached_length[0] ? 2 : 0)|(userData->Info.art_cached_length[1] ? 4 : 0); + + int outstate = (userData->Output ? userData->Output->get_state() : CONNECTION_STATE_ERROR); + if (outstate == CONNECTION_STATE_CONNECTING) { // are we connecting? if so then just abort + UNLOCK + return OUT_DISCONNECTED; + } + + if (outstate == CONNECTION_STATE_CONNECTED) { // are we connected yet? + userData->Info.ConnectionTime = clock(); + userData->Info.ConnectedAt = 0; + userData->Info.BytesSent = 0; + if (LOBYTE(userData->Config->protocol) != 1) { + createUvoxFrame(1, "0\0", MSG_TERMINATE, userData); + if (userData->Info.Succeeded != -1) { + Output_DUMMY(state, last_state, userData); + } + } + userData->Output->Close(!userData->SlowClose); + } + + if (outstate == CONNECTION_STATE_CLOSING) { + userData->Info.ConnectionTime = clock(); // update the clock with the current time so that we don't get some weird crap for reconnections. + } + + if (outstate == CONNECTION_STATE_CLOSED || outstate == CONNECTION_STATE_ERROR) { + userData->Info.Switching = 0; + if (last_state == OUT_SENDYP) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "Blocked"; + } else if (userData->Type == OUTTYPE_SOURCE && + (last_state == OUT_CONNECT || last_state == OUT_REQUEST_CIPHER || + last_state == OUT_SENDAUTH || last_state == OUT_RECV_CIPHER || + last_state == OUT_DISCONNECT)) { + // if using automatic mode then switch between v1 and v2 modes as needed + if (HIBYTE(userData->Config->protocol) && ((LOBYTE(userData->Config->protocol_retry) > 5) || + (last_state == OUT_CONNECT && HIBYTE(userData->Config->protocol_retry) < 1))) { + int protocol = (2 - (LOBYTE(userData->Config->protocol) != 1)); + userData->Config->protocol_retry = MAKEWORD(0, HIBYTE(userData->Config->protocol_retry)+1); + userData->Info.Switching = (LOBYTE(userData->Config->protocol) != 1 ? 2 : 1); + userData->Config->protocol = MAKEWORD(protocol, 1); + userData->Info.ConnectionTime = clock(); + UNLOCK + return OUT_RECONNECT; + } + } + + if (userData->Info.Reconnect && userData->Type == OUTTYPE_SOURCE) { + // if using automatic mode then switch between v1 and v2 modes as needed + if (HIBYTE(userData->Config->protocol)) { + // TODO need to make this into a function and have it show the switching message + int protocol = (2 - (LOBYTE(userData->Config->protocol) != 1)); + userData->Config->protocol_retry = 0; + userData->Info.Switching = (LOBYTE(userData->Config->protocol) != 1 ? 2 : 1); + userData->Config->protocol = MAKEWORD(protocol, 1); + userData->Info.ConnectionTime = clock(); + } + UNLOCK + return OUT_RECONNECT; + } else { + UNLOCK + return OUT_DISCONNECTED; + } + } + }UNLOCK + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_Reconnect(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if ((clock() - userData->Info.ConnectionTime) / CLOCKS_PER_SEC >= userData->Info.ReconnectTime) { + if (userData->Output) { + compatible_connect(userData, userData->Config->Port + (LOBYTE(userData->Config->protocol) == 1)); + } + UNLOCK + return OUT_CONNECT; + } + }UNLOCK + return state; // sit and spin +} + +void FixString(const char *in, char *out) { + while (in && *in) { + if ((*in >= 'A' && *in <= 'Z') || + (*in >= 'a' && *in <= 'z') || + (*in >= '0' && *in <= '9') || + (*in == '-') || + (*in == '_') || + (*in == '.') || + (*in == '!') || + (*in == '~') || + (*in == '*') || + (*in == '\'') || + (*in == '(') || + (*in == '[') || + (*in == ']') || + (*in == ')')) { + *out++=*in++; + } else if (*in >= 0 && *in < 32 && *in != 9 && *in != 10 && *in != 13) { + // strip out characters which aren't supported by the DNAS + // (only allow backspace, linefeed and carriage return) + in++; + } else { + unsigned int i=*(unsigned char *)in; + const char *t=in; + in++; + if (in == t + 1) { + if (i == '\'') i = '`'; + else if (i == '<') i = '('; + else if (i == '>') i = ')'; + else if (i == '\"') i='`'; + else if (i == '[') i='`'; + else if (i == ']') i='`'; + snprintf(out, 3, "%%%02X", i); + out += 3; + } else if (in == t + 2) { // multibyte + } + } + } + *out=0; +} + +/* SHOUTcast 1 title updates */ +int SHOUTCAST_OUTPUT::Output_Title_SendUpdate(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + char utf8title[1024] = {0}; + char fixedtitle[3072] = {0}; // if every character is URL-encoded, we would get a maximum of 3 times the space needed + char buffer[2048] = {0}; + + WideCharToMultiByte(CP_ACP, 0, userData->Info.Title, -1, utf8title, sizeof(utf8title), 0, 0); + FixString(utf8title, fixedtitle); + fixedtitle[1023] = 0; // DNAS doesn't like titles bigger than 1k + + // send the correct password based on dj name, etc + std::string t, u, p; + u = userData->Config->UserID; + p = userData->Config->Password; + if (!u.empty()) t = u + ":" + p; + else t = p; + + snprintf(buffer, sizeof(buffer), "GET /admin.cgi?pass=%s&mode=updinfo&song=%s&dj=%s HTTP/1.0\n", (char *)t.data(), fixedtitle, userData->Config->UserID); + userData->Output->SendString(buffer); + + stringstream s; + s << "User-Agent: SHOUTcast Source DSP v" << sourceVersion << " Title Update (Mozilla)\n\n"; + userData->Output->SendString((char*)s.str().data()); + + userData->SlowClose = 1; + userData->Info.meta_cached = 1; + }UNLOCK + return OUT_DISCONNECT; +} + +/* SHOUTcast 2 in-stream metada */ +int SHOUTCAST_OUTPUT::Output_Send_Metadata(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->TitleCallback) { + if (userData->Connection >= 0 && userData->Connection < 5) { + userData->TitleCallback(userData->Connection, 0); + } + } + }UNLOCK + return OUT_SENDCONTENT; +} + +/* SHOUTcast 2 in-stream metada */ +int SHOUTCAST_OUTPUT::Output_Send_Artwork(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + int artType = (userData->Info.meta_cached & 4 ? 1 : (userData->Info.meta_cached & 2 ? 0 : -1)); + if (userData->TitleCallback) { + if (userData->Connection >= 0 && userData->Connection < 5) { + if (artType != -1) { + userData->TitleCallback(userData->Connection, 1); + // decrement counts to move to next part of image packets if needed + if (userData->Info.meta_cached & (artType ? 4 : 2)) { + userData->Info.art_cached[artType]--; + if (userData->Info.art_cached[artType] < 0) userData->Info.art_cached[artType] = 0; + } + } + } + } else { + if (artType != -1) { + userData->Info.art_cached[artType] = 0; + } + } + }UNLOCK + return OUT_SENDCONTENT; +} + +int SHOUTCAST_OUTPUT::Output_Request_Cipher(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (LOBYTE(userData->Config->protocol) != 1) { + userData->Output->FlushSend(); + createUvoxFrame(3, "2.1\0", MSG_CIPHER, userData); + UNLOCK + return OUT_RECV_CIPHER; + } else { + // shoutcast 1 + UNLOCK + return OUT_SENDAUTH; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Receive_Cipher(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->ReceiveLine(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + if (checkUvoxFrameForError(out, state, userData) == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + // copy the cipher key now we have it. + strcpy_s(userData->Config->cipherkey, sizeof(userData->Config->cipherkey)-1, out+4); + UNLOCK + userData->Config->protocol_retry = 0; + return OUT_SENDAUTH; + } + } + + userData->Config->protocol_retry = MAKEWORD(LOBYTE(userData->Config->protocol_retry)+1, HIBYTE(userData->Config->protocol_retry)); + + // this handles automatic mode being enabled + if (!HIBYTE(userData->Config->protocol) && + (LOBYTE(userData->Config->protocol_retry) > 5)) { + userData->Output->Close(!userData->SlowClose); + UNLOCK + return OUT_SENDAUTH; + } + + }UNLOCK + + return (LOBYTE(userData->Config->protocol_retry) > 5 ? OUT_DISCONNECT : state); +} + +int SHOUTCAST_OUTPUT::Output_SendAuth(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + userData->Info.Succeeded = 1; + std::string s, + u = userData->Config->UserID, + p = userData->Config->Password; + + if (LOBYTE(userData->Config->protocol) != 1) { + uvAuth21 * auth = new uvAuth21(); + std::string k; + k = userData->Config->cipherkey; + s = "2.1:"; + s += (!userData->Config->StationID[0] ? "1" : userData->Config->StationID); + s += ":"; + s += auth->encrypt(u, p, k); + createUvoxFrame(s.length(), (char *)s.data(), MSG_AUTH, userData); + delete auth; + } else { + if (!u.empty()) s = u + ":" + p; + else s = p; + userData->Output->SendString((char *)s.data()); + userData->Output->SendString("\n"); + } + UNLOCK + return OUT_RECVAUTHRESPONSE; + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_RecvAuthResponse(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (LOBYTE(userData->Config->protocol) != 1) { + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Info.Succeeded = 1; + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + int err = checkUvoxFrameForError(out, state, userData); + if (err == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + // test for a cipher failure + if (strncmp(buf, "ACK:", 4) == 0) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "CipherFail"; + UNLOCK + return OUT_FAIL_CIPHER; + } + UNLOCK + return OUT_SEND_MIME; + } + } + } else { + // auth shoutcast 1 + if (userData->Output->GetReceiveBytesAvailable() > 0) { + userData->Output->ReceiveLine(buf, sizeof(buf)); + char *ok = strstr(buf, "OK"); + if (ok) { // we got OK response... shoutcast v1 + userData->Info.Succeeded = 1; + UNLOCK + return OUT_SENDYP; // send the sc v1 YP stuff, now + } else { + userData->Info.Succeeded = 0; + if (strcmpi(buf, "invalid password") == 0) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "NAK:Deny"; + } else if (strcmpi(buf, "stream moved") == 0) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "StreamMoved"; + } + UNLOCK + return OUT_DISCONNECT; + } + } + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Mime(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + std::string s = userData->ContentType; + userData->Output->FlushSend(); + createUvoxFrame(s.length(), (char *)s.data(), MSG_MIME_TYPE, userData); + }UNLOCK + return OUT_RECV_MIME; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Mime(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + UNLOCK + return OUT_SEND_BITRATE; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Bitrate(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + char bitrate[128] = {0}; + std::string s; + snprintf(bitrate, sizeof(bitrate), "%d:%d", userData->Bitrate*1000, userData->Bitrate*1000); + s = bitrate; + userData->Output->FlushSend(); + createUvoxFrame(s.length(), (char *)s.data(), MSG_BROADCAST_SETUP, userData); + }UNLOCK + return OUT_RECV_BITRATE; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Bitrate(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + int err = checkUvoxFrameForError(out, state, userData); + if (err == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + UNLOCK + return OUT_SEND_BUFSIZE; + } + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Buf_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + char bsize[32] = {0}; + int toSend = (userData->Bitrate * 20)/8; + snprintf(bsize, sizeof(bsize), "%d:0", toSend); + std::string s = bsize; + userData->Output->FlushSend(); + createUvoxFrame(s.length(), (char *)s.data(), MSG_NEGOTIATE_BUFFER_SIZE, userData); + }UNLOCK + return OUT_RECV_BUFSIZE; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Buf_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + UNLOCK + return OUT_SEND_MAX; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Max_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + createUvoxFrame(7, "16377:0\0", MSG_MAX_PAYLOAD_SIZE, userData); + }UNLOCK + return OUT_RECV_MAX; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Max_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + UNLOCK + return OUT_SENDYP; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_DUMMY(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_SendYP(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (LOBYTE(userData->Config->protocol) != 1) { + //icyname + std::string s; + s = userData->Config->Description; + createUvoxFrame(s.length(), (char *)s.data(), MSG_ICYNAME, userData); + Output_DUMMY(state, last_state, userData); + + //icygenre + s = userData->Config->Genre; + createUvoxFrame(s.length(), (char *)s.data(), MSG_ICYGENRE, userData); + Output_DUMMY(state, last_state, userData); + + //icyurl + s = userData->Config->ServerURL; + createUvoxFrame(s.length(), (char *)s.data(), MSG_ICYURL, userData); + Output_DUMMY(state, last_state, userData); + + //icypub + char pub[2] = {0}; + snprintf(pub, sizeof(pub), "%d", !!userData->Config->Public); + createUvoxFrame(1, pub, MSG_ICYPUB, userData); + Output_DUMMY(state, last_state, userData); + UNLOCK + return OUT_SEND_INITFLUSH; + } else { + char ypbuf[4096] = {0}; + char ypbuf2[4096] = {0}; + // send Description + snprintf(ypbuf, sizeof(ypbuf), "icy-name:%s\n", userData->Config->Description); + userData->Output->SendString(ypbuf); + // send Genre + ypbuf2[0] = 0; + if (strlen(userData->Config->Genre) > 0) { + snprintf((char *)&ypbuf2+strlen(ypbuf2), sizeof(ypbuf2), "%s", userData->Config->Genre); + } + snprintf(ypbuf, sizeof(ypbuf), "icy-genre:%s\n", ypbuf2); + userData->Output->SendString(ypbuf); + // send URL + snprintf(ypbuf, sizeof(ypbuf), "icy-url:%s\n", userData->Config->ServerURL); + userData->Output->SendString(ypbuf); + // send IRC + snprintf(ypbuf, sizeof(ypbuf), "icy-irc:%s\n", userData->Config->IRC); + userData->Output->SendString(ypbuf); + // send ICQ + snprintf(ypbuf, sizeof(ypbuf), "icy-icq:%s\n", userData->Config->ICQ); + userData->Output->SendString(ypbuf); + // send AIM + snprintf(ypbuf, sizeof(ypbuf), "icy-aim:%s\n", userData->Config->AIM); + userData->Output->SendString(ypbuf); + // send publicity + snprintf(ypbuf, sizeof(ypbuf), "icy-pub:%u\n", userData->Config->Public ? 1 : 0); + userData->Output->SendString(ypbuf); + // send bitrate (this is a huge bad big ugly hack... needs to be fixed, but this works so far) + snprintf(ypbuf, sizeof(ypbuf), "icy-br:%d\n", userData->Bitrate); + userData->Output->SendString(ypbuf); + // send content type (shouldn't be here as with the bitrate, but it works) + snprintf(ypbuf, sizeof(ypbuf), "content-type:%s\n", userData->ContentType); + userData->Output->SendString(ypbuf); + // end our list of configurations + userData->Output->SendString("\n"); + UNLOCK + return OUT_SENDCONTENT; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_InitFlush(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + createUvoxFrame(1, "0\0", MSG_FLUSH_CACHED_METADATA, userData); + }UNLOCK + return OUT_RECV_INITFLUSH; +} + +int SHOUTCAST_OUTPUT::Output_Recv_InitFlush(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + Output_DUMMY(state, last_state, userData); + }UNLOCK + return OUT_SEND_INITSTANDBY; +} + +int SHOUTCAST_OUTPUT::Output_Send_InitStandby(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + createUvoxFrame(1, "0\0", MSG_STANDBY, userData); + }UNLOCK + return OUT_RECV_INITSTANDBY;//OUT_SENDCONTENT; +} + +int SHOUTCAST_OUTPUT::Output_Recv_InitStandby(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->ReceiveLine(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + if (checkUvoxFrameForError(out, state, userData) == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + UNLOCK + return OUT_SENDCONTENT;//OUT_SEND_INTRO; + } + } + }UNLOCK + return OUT_SENDCONTENT;//OUT_SEND_INTRO; +} + +/*int SHOUTCAST_OUTPUT::Output_Send_Intro(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_RECV_INTRO; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Intro(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_SEND_BACKUP; +} + +int SHOUTCAST_OUTPUT::Output_Send_Backup(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_RECV_BACKUP; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Backup(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_SENDCONTENT; +}*/
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp new file mode 100644 index 00000000..763507e9 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp @@ -0,0 +1,90 @@ +#include "uvAuth21.h" + +uvAuth21::uvAuth21(void) { +} + +uvAuth21::~uvAuth21(void) { +} + +string uvAuth21::encrypt(string user,string pass,string key) { + string blob; + uint8_t* username = (uint8_t*)user.data(); + uint8_t* password = (uint8_t*)pass.data(); + uint8_t* cipherkey = (uint8_t*)key.data(); + blob = XTEA_encipher(username,user.length(),cipherkey,key.length()); + blob += ':'; + blob += XTEA_encipher(password,pass.length(),cipherkey,key.length()); + return blob; +} + +const char * uvAuth21::encrypt(char * user,char * pass,char * key) { + static string blob; + uint8_t* username = (uint8_t*)user; + uint8_t* password = (uint8_t*)pass; + uint8_t* cipherkey = (uint8_t*)key; + blob = XTEA_encipher(username,sizeof(user),cipherkey,sizeof(key)); + blob += ':'; + blob += XTEA_encipher(password,sizeof(pass),cipherkey,sizeof(key)); + return blob.c_str(); +} + +// from wikipedia. Slightly modified to be 32/64 bit clean +void uvAuth21::XTEA_encipher(__uint32* v, __uint32* k) { + unsigned int num_rounds = 32; + __uint32 v0=v[0], v1=v[1], i; + __uint32 sum=0, delta=0x9E3779B9; + for(i=0; i<num_rounds; i++) { + v0 += (((v1 << 4) ^ (v1 >> 5)) + v1) ^ (sum + k[sum & 3]); + sum += delta; + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); + } + v[0]=v0; v[1]=v1; +} + +__uint32 uvAuth21::fourCharsToLong(__uint8 *s) { + __uint32 l = 0; + l |= s[0]; l <<= 8; + l |= s[1]; l <<= 8; + l |= s[2]; l <<= 8; + l |= s[3]; + return l; +} + + std::vector<std::string> &uvAuth21::split(const std::string &s, char delim, std::vector<std::string> &elems) { + std::stringstream ss(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +std::vector<std::string> uvAuth21::dosplit(const std::string &s, char delim) { + std::vector<std::string> elems; + return split(s, delim, elems); +} + +std::string uvAuth21::XTEA_encipher(const __uint8* c_data,size_t c_data_cnt, const __uint8* c_key,size_t c_key_cnt) { + std::ostringstream oss; + std::vector<__uint8> key(c_key,c_key + c_key_cnt); + std::vector<__uint8> data(c_data,c_data + c_data_cnt); + + // key is always 128 bits + while(key.size() < 16) key.push_back(XTEA_KEY_PAD); // pad key with zero + __uint32 k[4] = { fourCharsToLong(&key[0]),fourCharsToLong(&key[4]),fourCharsToLong(&key[8]),fourCharsToLong(&key[12])}; + + // data is multiple of 64 bits + size_t siz = data.size(); + while(siz % 8) { siz+=1; data.push_back(XTEA_DATA_PAD);} // pad data with zero + + for(size_t x = 0; x < siz; x+=8) { + __uint32 v[2]; + v[0] = fourCharsToLong(&data[x]); + v[1] = fourCharsToLong(&data[x+4]); + XTEA_encipher(v,k); + oss << setw(8) << setfill('0') << hex << v[0]; + oss << setw(8) << setfill('0') << hex << v[1]; // hex values. uvox uses colon as seperator so we can't use chars for + // fear of collision + } + return oss.str(); +}
\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h new file mode 100644 index 00000000..971ce02a --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h @@ -0,0 +1,34 @@ +#pragma once + +#include <stdio.h> +#include "../replicant\foundation\win-x86\types.h" +#include <string> +#include <string.h> +#include <iostream> +#include <sstream> +#include <vector> +#include <iomanip> +#include <assert.h> + +#define XTEA_KEY_PAD 0 +#define XTEA_DATA_PAD 0 +#define __uint32 uint32_t +#define __uint8 uint8_t + +using namespace std; + +class uvAuth21 { +public: + uvAuth21(void); + string encrypt(string user, string password, string key); + const char * encrypt(char * user,char * password,char * key); + ~uvAuth21(void); +private: + +protected: + static void XTEA_encipher(__uint32* v, __uint32* k); + static __uint32 fourCharsToLong(__uint8 *s); + static string XTEA_encipher(const __uint8* c_data, size_t c_data_cnt, const __uint8* c_key, size_t c_key_cnt); + static std::vector<std::string> dosplit(const std::string &s, char delim); + static std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems); +};
\ No newline at end of file |