aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/DSP/dsp_sc
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/DSP/dsp_sc')
-rw-r--r--Src/Plugins/DSP/dsp_sc/Include/c_datapump.h174
-rw-r--r--Src/Plugins/DSP/dsp_sc/Include/c_wavein.h156
-rw-r--r--Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dllbin0 -> 4608 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dllbin0 -> 220672 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt186
-rw-r--r--Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi438
-rw-r--r--Src/Plugins/DSP/dsp_sc/NSIS/modern-install.icobin0 -> 40585 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/NSIS/win.bmpbin0 -> 154544 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/ICY.ICObin0 -> 4286 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/Script1.rc679
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/downarrow.icobin0 -> 1150 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/kill.icobin0 -> 1150 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/play.icobin0 -> 1150 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/refresh.icobin0 -> 1150 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/resource.h327
-rw-r--r--Src/Plugins/DSP/dsp_sc/Resource/stop.icobin0 -> 1150 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/api.h36
-rw-r--r--Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp189
-rw-r--r--Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h33
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html367
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html326
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html87
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/About_tab.pngbin0 -> 23732 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).pngbin0 -> 38941 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.pngbin0 -> 38700 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.pngbin0 -> 25373 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.pngbin0 -> 23796 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.pngbin0 -> 20164 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.pngbin0 -> 22202 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.pngbin0 -> 23276 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.pngbin0 -> 22617 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.pngbin0 -> 23258 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.pngbin0 -> 23164 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.pngbin0 -> 22510 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.pngbin0 -> 21513 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.pngbin0 -> 23161 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.pngbin0 -> 28401 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.pngbin0 -> 28310 bytes
-rw-r--r--Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj424
-rw-r--r--Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters188
-rw-r--r--Src/Plugins/DSP/dsp_sc/main.cpp5804
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h411
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp95
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h53
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp84
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h37
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp80
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h33
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp105
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h51
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp151
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h76
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp117
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h47
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h44
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h254
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h24
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h194
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h817
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h1323
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h184
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp1636
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp90
-rw-r--r--Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h34
-rw-r--r--Src/Plugins/DSP/dsp_sc/utils.cpp653
-rw-r--r--Src/Plugins/DSP/dsp_sc/utils.h67
66 files changed, 16074 insertions, 0 deletions
diff --git a/Src/Plugins/DSP/dsp_sc/Include/c_datapump.h b/Src/Plugins/DSP/dsp_sc/Include/c_datapump.h
new file mode 100644
index 00000000..bcf3863b
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Include/c_datapump.h
@@ -0,0 +1,174 @@
+#ifndef __C_DATAPUMP_H__
+#define __C_DATAPUMP_H__
+
+#include <stdlib.h>
+#include <memory.h>
+#include <stddef.h>
+#pragma intrinsic(memcpy,memset)
+
+template<class T> class C_DATAPUMP {
+private:
+protected:
+ T *BufferBottom; // bottom of the physical buffer
+ T *BufferTop; // top of the physical buffer
+ T *BufferStart; // start of the logical buffer
+ T *BufferEnd; // end of the logical buffer
+
+ virtual void addItems(T *inputBuffer, size_t inputSize) { // inputSize = number of <T> records inputBuffer contains
+ if(inputBuffer && inputSize) {
+ memcpy(BufferEnd,inputBuffer,inputSize*sizeof(T)); // copy our records in
+ BufferEnd += inputSize;
+ if(BufferEnd >= BufferTop) BufferEnd = BufferBottom + (BufferEnd-BufferTop);
+ }
+ }
+
+ virtual void delItems(int where, size_t numItems) { // where: 0 = start, 1 = end
+ if(numItems > 0) {
+ if(numItems > size()) { // just void everything
+ BufferEnd = BufferStart;
+ } else {
+ if(where == 0) { // start
+ BufferStart += numItems;
+ if(BufferStart >= BufferTop) BufferStart = BufferBottom + (BufferTop-BufferStart);
+ } else if(where == 1) { // end
+ BufferEnd -= numItems;
+ if(BufferEnd < BufferBottom) BufferEnd = BufferTop - (BufferBottom-BufferEnd);
+ }
+ }
+ }
+ }
+
+ virtual void getItems(T *outputBuffer, size_t outputSize) { // outputSize = number of <T> records outputBuffer needs
+ if(outputBuffer && outputSize) {
+ memcpy(outputBuffer,BufferStart,outputSize*sizeof(T));
+ }
+ }
+
+public:
+ C_DATAPUMP(int bufferSize) { // bufferSize = number of <T> records
+ BufferBottom = NULL;
+ BufferTop = NULL;
+ BufferStart = NULL;
+ BufferEnd = NULL;
+ resizeBuffer(bufferSize);
+ }
+
+ virtual ~C_DATAPUMP() {
+ if(getBufferSize() && BufferBottom) {
+ free(BufferBottom);
+ BufferBottom = NULL;
+ }
+ }
+
+ virtual void resizeBuffer(size_t bufferSize) { // bufferSize = number of <T> records
+ // this will invalidate any data in the buffer, so be careful when calling this function
+ if(bufferSize) {
+ if(getBufferSize() != bufferSize) {
+ if(BufferBottom && BufferTop && getBufferSize()) { // buffer is valid
+ if(getBufferSize() > bufferSize) { // buffer is getting smaller (will invalidate buffer)
+ BufferTop -= getBufferSize()-bufferSize;
+ invalidate();
+ } else { // buffer is getting larger (will _NOT_ invalidate buffer... nicely moves the data over =)
+ T *newBuffer = (T *)malloc(bufferSize * sizeof(T));
+ // new
+ BufferEnd = newBuffer + get(newBuffer,bufferSize);
+ free(BufferBottom);
+ BufferBottom = newBuffer;
+ BufferTop = BufferBottom + bufferSize;
+ BufferStart = BufferBottom;
+ /* old
+ T *bufptr = newBuffer;
+ int top = BufferEnd >= BufferStart ? BufferEnd-BufferStart : BufferTop-BufferStart; // number of <T> records at top of physical buffer
+ int bottom = BufferEnd >= BufferStart ? 0 : BufferEnd-BufferBottom; // number of <T> records at bottom of physical buffer
+ if(top > 0) {
+ memcpy(bufptr,BufferStart,top*sizeof(T));
+ bufptr += top;
+ }
+ if(bottom > 0) {
+ memcpy(bufptr,BufferBottom,bottom*sizeof(T));
+ bufptr += bottom;
+ }
+ free(BufferBottom);
+ BufferBottom = newBuffer;
+ BufferTop = BufferBottom + bufferSize;
+ BufferStart = BufferBottom;
+ BufferEnd = bufptr;
+ */
+ }
+ } else { // no buffer, create (invalidates the buffer... duh)
+ BufferBottom = (T *)malloc(bufferSize * sizeof(T));
+ BufferTop = BufferBottom + bufferSize;
+ invalidate();
+ }
+ }
+ }
+ }
+
+ virtual size_t size() { // will get the number of <T> records the logical buffer contains
+ return BufferEnd >= BufferStart ? BufferEnd-BufferStart : (BufferTop-BufferStart)+(BufferEnd-BufferBottom);
+ }
+
+ virtual size_t put(T *inputBuffer, size_t inputSize) { // inputSize = number of <T> records inputBuffer contains
+ // returns number of <T> records added to logical buffer
+ size_t retval = 0;
+ if(inputBuffer && inputSize) {
+ size_t fitting = ((BufferTop-BufferBottom)-1) - size(); // can't go over our logical boundary.... blah
+ if(fitting > inputSize) fitting = inputSize; // the entire thing can fit. yeay!
+ retval = fitting;
+ if(fitting > 0) {
+ T *bufptr = inputBuffer;
+ size_t top = BufferEnd >= BufferStart ? BufferTop-BufferEnd : 0; // number of <T> records free at top of physical buffer
+ size_t bottom = BufferEnd >= BufferStart ? BufferStart-BufferBottom : (BufferStart-BufferEnd); // number of <T> records free at bottom of physical buffer
+ if(top > 0) {
+ if(top > fitting) top = fitting;
+ addItems(bufptr,top);
+ fitting -= top;
+ bufptr += top;
+ }
+ if(bottom > 0 && fitting > 0) {
+ if(bottom > fitting) bottom = fitting;
+ addItems(bufptr,bottom);
+ }
+ }
+ }
+ return retval;
+ }
+
+ virtual size_t get(T *outputBuffer, size_t outputSize) { // outputSize = number of <T> records outputBuffer needs
+ // returns number of <T> records pulled from the logical buffer
+ size_t retval = 0;
+ if(outputBuffer && outputSize) {
+ size_t fitting = size();
+ if(fitting > outputSize) fitting = outputSize;
+ retval = fitting;
+ if(fitting > 0) {
+ T *bufptr = outputBuffer;
+ size_t top = BufferEnd >= BufferStart ? BufferEnd-BufferStart : BufferTop-BufferStart; // number of <T> records at top of physical buffer
+ size_t bottom = BufferEnd >= BufferStart ? 0 : BufferEnd-BufferBottom; // number of <T> records at bottom of physical buffer
+ if(top > 0) {
+ if(top > fitting) top = fitting;
+ getItems(bufptr,top);
+ delItems(0,top);
+ fitting -= top;
+ bufptr += top;
+ }
+ if(bottom > 0 && fitting > 0) {
+ if(bottom > fitting) bottom = fitting;
+ getItems(bufptr,bottom);
+ delItems(0,bottom);
+ }
+ }
+ }
+ return retval;
+ }
+
+ virtual size_t getBufferSize() { // returns the size of the physical buffer in <T> items
+ return BufferTop-BufferBottom;
+ }
+
+ virtual void invalidate() { // calling this will wipe all data in the buffer and reset the logical pointers
+ BufferStart = BufferEnd = BufferBottom;
+ }
+};
+
+#endif // !__C_DATAPUMP_H__ \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/Include/c_wavein.h b/Src/Plugins/DSP/dsp_sc/Include/c_wavein.h
new file mode 100644
index 00000000..7bcac3cb
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Include/c_wavein.h
@@ -0,0 +1,156 @@
+#ifndef __C_WAVEIN_H__
+#define __C_WAVEIN_H__
+
+#include <windows.h>
+#include <mmsystem.h>
+#define EXIT_ON_ERROR(hr) \
+ if (FAILED(hr)) { goto Exit; }
+#define SAFE_RELEASE(what) \
+ if ((what) != NULL) \
+{ (what)->Release(); (what) = NULL; }
+
+template<int numbuffers, int buffersize> class C_WAVEIN {
+private:
+ short Samples[numbuffers][buffersize];
+ WAVEFORMATEX wfx;
+ WAVEHDR wvhdr[numbuffers];
+ HWAVEIN hwi;
+ WAVEINCAPS wic;
+ unsigned long iNumDevs, iy;
+ HRESULT hr;
+ IMMDeviceEnumerator *pEnumerate;
+ IMMDevice *pDevice;
+ IMMDeviceCollection *ppDevices;
+ IPropertyStore *pProps;
+ BOOL useXpSound;
+ PROPVARIANT varName;
+ char buf[1024];
+public:
+ C_WAVEIN() {
+ hwi = NULL;
+ memset(Samples, 0, sizeof(Samples));
+ memset(wvhdr, 0, sizeof(wvhdr));
+ iNumDevs = iy = 0;
+ hr = S_OK;
+ pEnumerate = NULL;
+ pDevice = NULL;
+ ppDevices = NULL;
+ pProps = NULL;
+ useXpSound = false;
+ memset(buf, 0, sizeof(buf));
+ }
+
+ virtual ~C_WAVEIN() {
+ Close();
+ }
+
+ char * getDeviceName(unsigned int devid=-1) {
+ hr = S_OK;
+ pEnumerate = NULL;
+ pDevice = NULL;
+ ppDevices = NULL;
+ pProps = NULL;
+ useXpSound = false;
+ PROPVARIANT varName;
+ PropVariantInit(&varName);
+ // Get enumerator for audio endpoint devices.
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&pEnumerate);
+ EXIT_ON_ERROR(hr)
+
+ hr = pEnumerate->GetDefaultAudioEndpoint(eCapture,eConsole,&pDevice);
+ EXIT_ON_ERROR(hr)
+Exit:
+ if (FAILED(hr)) {
+ useXpSound = true;
+ } else
+ useXpSound = false;
+
+ memset(buf, 0, sizeof(buf));
+ if (useXpSound) {
+ if (!waveInGetDevCaps(devid, &wic, sizeof(WAVEINCAPS))) {
+ lstrcpyn(buf, wic.szPname, ARRAYSIZE(buf));
+ goto Fin;
+ }
+ } else {
+ pDevice->OpenPropertyStore(STGM_READ, &pProps);
+ pProps->GetValue(PKEY_Device_FriendlyName, &varName);
+ WideCharToMultiByte(CP_ACP, 0, (LPWSTR)varName.pwszVal, -1, buf, ARRAYSIZE(buf), NULL, NULL);
+ goto Fin;
+ }
+Fin:
+ PropVariantClear(&varName);
+ SAFE_RELEASE(pProps)
+ SAFE_RELEASE(pEnumerate)
+ SAFE_RELEASE(pDevice)
+ SAFE_RELEASE(ppDevices)
+ CoUninitialize();
+ return buf;
+ }
+
+ void Create(int sRate, int nCh,int devid=-1) {
+ if (hwi == NULL) {
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.wBitsPerSample = 16;
+ wfx.nSamplesPerSec = sRate;
+ wfx.nChannels = (WORD)nCh;
+ wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8;
+ wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
+ wfx.cbSize = 0;
+ waveInOpen(&hwi,devid,&wfx,0,0,CALLBACK_NULL);
+ waveInStop(hwi);
+ waveInReset(hwi);
+ for(int i = 0; i < numbuffers; i++) {
+ memset(&wvhdr[i],0,sizeof(wvhdr[i]));
+ wvhdr[i].lpData = (char *)&Samples[i];
+ wvhdr[i].dwBufferLength = buffersize * sizeof(short);
+ waveInPrepareHeader(hwi,&wvhdr[i],sizeof(WAVEHDR));
+ waveInAddBuffer(hwi,&wvhdr[i],sizeof(WAVEHDR));
+ }
+ waveInStart(hwi);
+ }
+ }
+
+ void Close() {
+ if (hwi != NULL) {
+ waveInStop(hwi);
+ waveInReset(hwi);
+ for(int i = 0; i < numbuffers; i++) {
+ if (wvhdr[i].dwFlags & WHDR_PREPARED) {
+ waveInUnprepareHeader(hwi,&wvhdr[i],sizeof(WAVEHDR));
+ }
+ }
+ waveInClose(hwi);
+ hwi = NULL;
+ }
+ }
+
+ short *operator[](int buffernum) {
+ return (short *)&Samples[buffernum];
+ }
+
+ int getNumSamples(int buffernum) {
+ return wvhdr[buffernum].dwBytesRecorded / (wfx.nChannels * sizeof(short));
+ }
+
+ int isOpen() {
+ return hwi != NULL;
+ }
+
+ int isFilled(int buffernum) {
+ return wvhdr[buffernum].dwFlags & WHDR_DONE && wvhdr[buffernum].dwBytesRecorded <= buffersize * sizeof(short);
+ }
+
+ void cycleBuffer(int buffernum) {
+ if (hwi != NULL) {
+ wvhdr[buffernum].dwFlags = WHDR_PREPARED;
+ wvhdr[buffernum].dwBytesRecorded = 0;
+ waveInAddBuffer(hwi,&wvhdr[buffernum],sizeof(WAVEHDR));
+ }
+ }
+};
+
+#endif // !__C_WAVEIN_H__ \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll b/Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll
new file mode 100644
index 00000000..1968ffed
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll
new file mode 100644
index 00000000..9196f328
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt
new file mode 100644
index 00000000..0682d8d1
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt
@@ -0,0 +1,186 @@
+RADIONOMY Shoutcast Terms / Disclaimer / Contact Us
+
+
+Shoutcast Terms of Service
+
+
+Welcome to Shoutcast! You may use Shoutcast’s website, applications, software, widgets updates and services of Shoutcast.com and its affiliated sites (collectively, "Shoutcast") on the condition that agree to the following terms.
+
+BY ACCESSING, USING OR INTERACTING WITH THE SERVICE, YOU SIGNIFY ELECTRONICALLY THAT YOU AGREE TO THESE TERMS OF SERVICE ("TERMS") AND THE TERMS OF OUR PRIVACY POLICY (http://www.radionomy.com/en/static/privacy).
+
+If you do not agree, you may not use Shoutcast.
+
+
+1. About these Terms.
+
+Shoutcast is provided by RADIONOMY SA, and its affiliates, successors, parents, subsidiaries, assigns and licensee, who provide Shoutcast (collectively, "we" or "us"). You understand and agree that as Shoutcast evolves, we may in our sole discretion change or update these Terms at any time. You can review the most current version of these terms by clicking on the "Terms of Service" hypertext link located at the bottom of our homepage at http://www.shoutcast.com. Your ongoing use of Shoutcast after we post or notify you about changes to the Terms signifies your acceptance of the new terms. If you do not agree to the changes, your sole recourse is to stop using Shoutcast.
+
+
+2. About the Shoutcast.
+
+a.Shoutcast is intended for general audiences and for personal and information use only. We may add, change or discontinue any aspect of Shoutcast at any time, in our sole discretion and without notice to you. We may offer certain applications, services or features for a fee, which will be subject to separate payment terms that will be binding on you.
+
+b.Shoutcast is controlled and operated within Belgium. Although Shoutcast is accessible from outside of Belgium, you understand and agree that Shoutcast (i) is not designed or customized for distribution for any specific country or jurisdiction (Territory), (b) is not intended for distribution to, or use by, any person or entity in any Territory where such distribution or use would be contrary to local law or regulation, (c) may not be appropriate or available for access or use in any particular Territory, and (d) is provided without any Content filtering or rating mechanism. We have no obligation to assure that Shoutcast complies with applicable local laws and regulations within the Territories in which you elect to use Shoutcast. Your use of Shoutcast within any specific Territory is entirely at Your own risk. You are solely responsible for complying with any local laws in which you access or use Shoutcast.
+
+
+3. Content
+
+a. Shoutcast offers information, directories and search functionality to enable users to access, view and listen to content such as music ("Content") and provides links to other sites and third party providers of music, Internet music stations and other content ("Content Providers"). As a directory and search service, Shoutcast does not host and is not responsible for the Content accessed through its directory or search functionality. Such Content is hosted and served by the Content Providers. The Content Providers are solely responsible for their Content offered through Shoutcast, including without limitation, obtaining all rights, licenses and royalties pertaining to their Content.
+
+b. You understand that Shoutcast does not pre-screen Content and you agree that Shoutcast has no obligation to pre-screen Content, although Shoutcast reserves the right to do so in its sole discretion. Some Content may contain materials that are objectionable, unlawful, or inaccurate. Shoutcast’s offerings of directories, search results, links and/or access to Content does not mean that we endorse the Content or the Content Providers. You acknowledge and agree that we are not responsible or liable to you for any Content or other materials hosted and served from these third party Content Providers.
+
+
+4. Registration
+
+Registration is not required; however, you will need to register with us and obtain a user ID if you want to use certain interactive features on Shoutcast. If you register, you must provide accurate identification, contact, and other information required as part of the registration process. You agree to keep your information current. You may not create any script or other automated tool that attempts to create multiple developer accounts. We may in our sole discretion reject any registration for any reason. By signing up with Shoutcast, you represent and warrant that your information is accurate. Our affiliates may give you the ability to use your Shoutcast user ID to access other affiliate services. Your use of services provided by our affiliates may be subject to additional terms, conditions and privacy policies, which will apply to you if you elect to use those services.
+
+
+5. Additional Terms.
+
+Certain Shoutcast features may be subject to supplemental usage rules, guidelines and terms, which you will have an opportunity to review and which will be binding on you if you elect to use those features.
+
+
+6. Electronic Delivery Policy and Your Consent.
+
+You acknowledge that you are agreeing to these terms online and electronically and that these Terms have the same effective as an agreement on paper. You authorize us to provide you with required notices, agreements and information concerning the Shoutcast electronically. Your affirmative act of submitting and clicking to register for Shoutcast constitutes your electronic signature to these Terms. We will provide you our notices either by sending them to the e-mail address that you give to us or by posting the notices on the home page of the Shoutcast or on the relevant web page of the applicable service. If you want to withdraw your consent to receive notices electronically, your only recourse is to discontinue your use of the Shoutcast.
+
+
+7. Privacy Policy.
+
+The Shoutcast Privacy Policy located at http://www.radionomy.com/en/static/privacy explains the practices that apply to your information when you use the Shoutcast. Your ongoing use of the Shoutcast signifies your consent to the Privacy Policy. You can review the Privacy Policy by clicking on the Privacy Policy link located on the home page of the Shoutcast.
+
+
+8. Access.
+
+You must provide at your own expense the equipment and Internet connections that you will need to access and use the Shoutcast, whether you access Shoutcast through broadband, wifi, wireless or other type of connection. If you access the Shoutcast through a telephone line, please call your local phone company to determine if the access numbers you select are subject to long distance or other toll charges at your location. Also, wireless, data or text messaging charges apply if you access the Shoutcast through wireless applications (e.g., cell phones). Check with your carrier to verify whether there are any such fees that may apply to you.
+
+
+9. Your Responsibilities.
+
+You may use Shoutcast for lawful purposes only. You are responsible for all activities under your account, including all legal liability incurred from the use of your account by you or others.
+
+
+10. Restrictions.
+
+You agree that you will not access or use Shoutcast or its Content, or otherwise engage in any conduct that:
+
+1. violates or infringes the rights of others including, without limitation, patent, trademark, trade secret, copyright, publicity or other proprietary rights is unlawful;
+
+2. uses technology or other means to access Shoutcast or Content that is not authorized by us;
+
+3. uses any automated system, including without limitation, "robots," "spiders," or "offline readers," to access Shoutcast or Content;
+
+4. attempts to introduce viruses or any other computer code, files or programs that interrupt, destroy or limit the functionality of any computer software or hardware or telecommunications equipment;
+
+5. attempts to gain unauthorized access to our computer network or user accounts;
+
+6. encourages conduct that would constitute a criminal offense, or that gives rise to civil liability; offers, promotes or encourages betting or wagering prohibited by law;
+
+7. violates these Terms, guidelines or any policy posted on Shoutcast;
+
+8. attempts to damage, disable, overburden, or impair our servers or networks; or
+
+9. interferes with any other party's use and enjoyment of Shoutcast.
+
+10. You agree that we may take any legal and technical remedies to enforce these Terms, including without limitation, immediate termination of your account or access to Shoutcast if we believe in our discretion that you are violating these Terms.
+
+
+11. No Spam.
+
+You may not use Shoutcast or any of our communication tools to transmit, directly or indirectly, any unsolicited bulk communications (including e-mails and instant messages). You may not harvest information about our users for the purpose of sending, or to facilitate the sending, of unsolicited bulk communications. You may not induce or allow others to use Shoutcast to violate the terms of this section. We may terminate your access or use of Shoutcast immediately and take any other legal action if you, or anyone using your access to Shoutcast, violates these provisions. We may take any technical remedies to prevent unsolicited bulk communications from entering, utilizing, or remaining within our computer or communications networks.
+
+
+12. Proprietary Rights.
+
+We, our suppliers, and our users who lawfully post Content on the Shoutcast own the property rights to that Content. Further, your agree that Shoutcast, its application, software and database information, and those techniques, algorithms, and processes contained therein which have been developed, acquired, or licensed by us are proprietary to RADIONOMY. and our affiliates.
+
+Shoutcast and the Content is protected by international treaties, and by copyright, trademark, patent, and trade secret laws and other proprietary rights and also may have security components that protect digital information. You agree that you will not violate these rights and access and use Shoutcast and the Content only as authorized by the owners of these rights.
+
+
+13. License To Use the Shoutcast.
+
+We grant you a personal, non-exclusive, non-transferable, limited and revocable license to use Shoutcast subject to these Terms. You may not use Shoutcast in a manner that exceeds the rights granted for your use of Shoutcast and its Content. Without limitation of the foregoing, you may not frame any portion of Shoutcast or Content, or reproduce, record, reprint, copy, store, publicly display, broadcast, transmit, modify, translate, port, publish, sublicense, assign, transfer, sell, loan, make derivative works or otherwise distribute the Content without our prior written consent. You may not circumvent any mechanisms for preventing the unauthorized reproduction or distribution of the Content or Shoutcasts applications, software or services. Your license terminates immediately upon cancellation or termination of your account or if we believe you are in violation of these Terms.
+
+
+14. Content You Post To Public Areas.
+
+Certain areas of Shoutcast may allow you to post Content (such as comments) that can be accessed and viewed by others, including the public in general. You may only post Content to public areas on Shoutcast that you created or that you have permission to post. You many not publicly post defamatory Content or someone else’s image or personal information without the express authorization of that person. You may not post Content that violates these Terms. We do not claim ownership of any Content that you may post. However, by submitting Content to public areas of Shoutcast, you grant us, our parent, affiliates, and distributors the right to use, copy, display, perform, distribute, adapt and promote this Content in any medium. You agree that we have no duty to pre-screen Content, but we have the right to refuse to post or to edit submitted Content. We reserve the right to remove Content for any reason, but we are not responsible for any failure or delay in removing such material.
+
+
+15. Procedure For Making Claims Of Copyright Infringement.
+
+If you believe that your copyrighted work has been copied and is accessible on the RADIONOMY, Winamp or Shoutcast in a way that constitutes copyright infringement, you may notify RADIONOMY by providing our copyright agent the following information:
+
+1. an electronic or physical signature of the owner of the copyright or the person authorized to act on the owner's behalf.
+
+2. a description of the copyrighted work that you claim has been infringed and a description of the infringing activity.
+
+3. identification of the location where the original or an authorized copy of the copyrighted work exists, for example the URL (i.e., web page address) where it is posted or the name of the book in which it has been published.
+
+4. identification of the URL or other specific location on the Shoutcast site where the material that you claim is infringing is located, including enough information to allow us to locate the material.
+
+5. your name, address, telephone number, and email address.
+
+6. a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law.
+
+7. a statement by you that the above information in your Notice is accurate and that you are the copyright owner or authorized to act on the copyright owner's behalf.
+
+
+RADIONOMY's agent for notice of claims of copyright infringement on this site can be reached as follows:
+
+By mail:
+Radionomy SA
+55K Boulevard International
+1070 Brussels
+Belgium
+
+Email: contact@radionomy.com
+
+
+16. Advertisers.
+
+You agree that the Shoutcast may be supported by advertising. Any dealings that you have with advertisers found on the Shoutcast are between you and the advertiser and you acknowledge and agree that we are not liable for any loss or claim you may have against an advertiser.
+
+
+17. Use Of Software.
+
+We may make software available for you to download or use. Such software will be subject to the terms of the license agreement that accompanies it. If there is no license agreement presented to you with the software, then the following license, in addition to the other provisions of these Terms, govern your use of such software. We grant you a personal, non-exclusive, non-transferable, limited license to install the software on any single computer or authorized device. The software is protected by copyright and other intellectual property laws and treaties and is owned by us or our suppliers. You may not sell or redistribute the software. You may not incorporate it or any portion of it into another product. You may not reverse engineer, decompile, or disassemble the software or otherwise attempt to derive the source code (except where expressly permitted by law). You may not modify, adapt, or create derivative works from the software in any way or remove proprietary notices in the software. You agree to abide by all laws and regulations in effect regarding your use of the software. You may not authorize or assist any third party to do any of the things prohibited in this paragraph.
+
+We may automatically check your version of the software and update it to improve its performance and capabilities. If you shut down the software during an automatic update or otherwise interfere with the installation of the update, the software may be damaged and/or cease to operate.
+
+18. Export laws.
+
+You agree to fully comply with all import and export laws, regulations, rules and orders of the European Union, or any foreign government agency or authority, and that you will not directly or indirectly export, re-export, transfer and/or release the software, related technology, or any product thereof, for any proscribed end-use, or to any proscribed country, entity or person (wherever located), without proper authorization from the European Union or other applicable agencies. You are responsible for and assume all expenses relating to your compliance with the described laws, regulations, rules and orders, and for obtaining all necessary authorizations and clearances. You further agree to assume responsibility for and bear all expenses relating to your compliance with the described laws, regulations, rules and orders, and obtaining all necessary authorizations and clearances.
+
+
+19. DISCLAIMER.
+
+WE PROVIDE SHOUTCAST "AS IS" AND WITH ALL FAULTS. YOU ARE USING SHOUTCAST AT YOUR OWN RISK. WE, OUR LICENSORS AND DISTRIBUTORS DISCLAIM ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES THAT SHOUTCAST IS FREE OF DEFECTS AND ABLE TO OPERATE ON AN UNINTERRUPTED BASIS OR THAT IT WILL MEET YOUR REQUIREMENTS. WE DISCLAIM THE IMPLIED WARRANTIES THAT SHOUTCAST IS MERCHANTABLE, OF SATISFACTORY QUALITY, RELIABLE, ACCURATE, FIT FOR A PARTICULAR PURPOSE OR NEED, OR NON-INFRINGING, UNLESS SUCH IMPLIED WARRANTIES ARE LEGALLY INCAPABLE OF EXCLUSION. YOU HEREBY ACKNOWLEDGE A) CONTENT PROVIDERS AND NOT US ARE RESPONSIBLE FOR OBTAINING ALL NECESSARY RIGHTS, PERMISSIONS, LICENSES, APPLICABLE TAXES, CERTIFICATIONS AND CLEARANCES FOR THE STATIONS AND CONTENT THAT THEY PROIVIDE, B) CONTENT PROVIDERS AND NOT US ARE SOLELY RESPONSIBLE FOR ENSURING THAT THE CONTENT COMPLIES WITH ALL APPLICABLE LAWS AND REGULATIONS, C) CONTENT PROVIDERS AND NOT US ARE SOLELY RESPONSIBLE FOR ALL APPLICABLE ROYALTIES TO THE COPYRIGHT OWNERS WITH RESPECT TO THE CONTENT CONTAINED IN THEIR INDIVIDUAL STATION(S), AND D) WE SHALL HAVE NO RESPONSIBILITY OR LIABILITY TO YOU OR ANY THIRD PARTY TO OBTAIN SUCH RIGHTS, PERMISSIONS, CLEARANCES OR ROYALTY PAYMENTS AND SHALL HAVE NO RESPONSIBILITY OR LIABILITY TO YOU OR ANY THIRD PARTY FOR THE FAILURE OF SUCH CONTENT TO OBTAIN SUCH RIGHTS, PERMISSIONS, CLEARANCES OR PAY ANY APPLICABLE ROYALTIES.
+
+
+20. LIMITATION OF LIABILITY.
+
+WE, OUR LICENSORS AND DISTRIBUTORS HAVE NO LIABILITY WITH RESPECT TO YOUR USE OF SHOUTCAST. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL WE OR OUR PARENT, AFFILIATES, DIRECTORS, EMPLOYEES, DISTRIBUTORS, LICENSORS, SUPPLIERS, PARTNERS, AGENTS, DISTRIBUTORS OR RESELLERS (RADIONOMY) BE LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR EXEMPLARY DAMAGES ARISING OUT OF OR IN ANY WAY RELATING TO THIS AGREEMENT OR THE USE OF OR INABILITY TO USE SHOUTCAST, INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, LOST PROFITS, LOSS OF DATA, CORRUPTION OF DATA, COMPUTER FAILURE OR MALFUNCTION. YOUR SOLE REMEDY WITH RESPECT TO ANY DISPUTE WITH US OR SHOUTCAST IS TO CANCEL YOUR USE OF THE SERVICE. IN ADDITION, THE MAXIMUM AGGREGATE LIABILITY OF THE RADIONOMY GROUP FOR ANY CLAIMS ARISING IN CONNECTION WITH THIS AGREEMENT WILL NOT EXCEED ONE HUNDRED DOLLARS (USD $100). SHOUTCAST IS PROVIDED WITHOUT CHARGE AND YOU AGREE THAT THE FOREGOING LIMITATIONS REPRESENT A REASONABLE ALLOCATION OF RISK UNDER THESE TERMS.
+
+
+21. Indemnification.
+
+Upon a request by us, you agree to defend, indemnify, and hold harmless us and our parent and other affiliated companies, and our respective employees, contractors, officers, directors, and agents from all liabilities, claims, and expenses, including attorney's fees that arise from your use or misuse of Shoutcast. We reserve the right, at our own expense, to assume the exclusive defense and control of any matter otherwise subject to indemnification by you, in which event you will cooperate with us in asserting any available defenses.
+
+
+22. Choice of Law and Location for Resolving Disputes.
+
+YOU EXPRESSLY AGREE THAT EXCLUSIVE JURISDICTION FOR ANY CLAIM OR DISPUTE WITH RADIONOMY, RADIONOMY’S AFFILIATES, SHOUTCAST, THIS AGREEMENT, OR RELATING IN ANY WAY TO YOUR USE OF THIS SITE AND/OR SHOUTCAST (OR ANY FEATURES THEREOF) RESIDES IN THE FEDERAL OR STATE COURTS LOCATED IN BELGIUM AND EXPRESSLY CONSENT TO THE EXERCISE OF PERSONAL JURISDICTION IN SUCH COURTS IN CONNECTION WITH ANY SUCH DISPUTE INCLUDING ANY CLAIM INVOLVING RADIONOMY, THIS AGREEMENT, OF THIS SITE AND/OR SHOUTCAST (OR ANY FEATURES THEREOF). PLEASE NOTE THAT BY AGREEING TO THESE TERMS OF USE, YOU ARE WAIVING CLAIMS THAT YOU MIGHT OTHERWISE HAVE AGAINST US BASED ON THE LAWS OF OTHER JURISDICTIONS, INCLUDING YOUR OWN.
+
+
+23. Severability and Integration.
+
+These Terms and any supplemental terms, policies, rules and guidelines posted on Shoutcast constitute the entire agreement between you and us and supersede all previous written or oral agreements. If any part of these Terms is held invalid or unenforceable, that portion shall be construed in a manner consistent with applicable law to reflect, as nearly as possible, the original intentions of the parties, and the remaining portions shall remain in full force and effect.
+
+
+24. Termination.
+
+Your right to use Shoutcast automatically terminates if you violate these Terms or any rules or guidelines posted in connection with Shoutcast. We also reserve the right, in our sole discretion, to terminate your access to all or part of Shoutcast, for any reason, with or without notice.
+
+
+Last updated 7-15-2014
diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi
new file mode 100644
index 00000000..9a9adeac
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi
@@ -0,0 +1,438 @@
+;--------------------------------
+;Include Modern UI
+
+!include "MUI2.nsh"
+!include "LogicLib.nsh"
+!include "FileFunc.nsh"
+!include "WordFunc.nsh"
+!include "WinVer.nsh"
+;--------------------------------
+; this is the version for Winamp 5.9.1
+!define MINIMAL_VERSION "5.9.1.10021"
+
+; The name of the installer
+!define NAME "Shoutcast Source DSP Plug-in"
+!define VERSION "2.4.2"
+!define BUILD "449"
+!define UNINSTALL "Shoutcast Source DSP"
+!define UNINSTALLER "uninstall_shoutcast-source-dsp-v2.exe"
+Name "${NAME}"
+
+BrandingText "${NAME} v${VERSION} Build ${BUILD}"
+
+; detect winamp path from uninstall string if available
+InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Winamp" "UninstallString"
+
+; The file to write
+OutFile "shoutcast-dsp-2-4-2-windows.exe"
+
+; The default installation directory
+InstallDir "$PROGRAMFILES32\Winamp"
+
+; The text to prompt the user to enter a directory
+DirText "Please select your Winamp path below (you will be able to proceed when Winamp is detected):"
+# currently doesn't work - DirShow hide
+
+; Request application privileges for Windows Vista+
+RequestExecutionLevel admin
+
+; Set the compressor (get installer as small as possible)
+SetCompressor /SOLID lzma
+
+; Set the install types available
+InstType "Full Install"
+InstType "Base Install"
+
+; global variables
+Var /GLOBAL WINAMP_INI_DIR
+Var GetInstalledSize.total
+
+;--------------------------------
+;Interface Settings
+
+!define MUI_ABORTWARNING
+
+;--------------------------------
+;Pages
+ !define MUI_ICON "modern-install.ico"
+ !define MUI_UNICON "modern-install.ico"
+
+ !define MUI_WELCOMEPAGE_TITLE_3LINES
+ !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of the ${NAME}.$\n$\nIt is recommended that you close all instances of Winamp before starting Setup. This will make it possible to install relevant files within your Winamp installation without issues.$\n$\nClick Next to continue."
+ !define MUI_WELCOMEFINISHPAGE_BITMAP "win.bmp"
+ !insertmacro MUI_PAGE_WELCOME
+ !insertmacro MUI_PAGE_LICENSE "dsp_sc_license.txt"
+; is best to call the version check when leaving the directory page so it will be working against correct path
+ !define MUI_PAGE_CUSTOMFUNCTION_LEAVE CheckWinampVersion
+ !insertmacro MUI_PAGE_DIRECTORY
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_INSTFILES
+
+ !define MUI_FINISHPAGE_RUN
+ !define MUI_FINISHPAGE_RUN_FUNCTION SetAsCurrentDSP
+ !define MUI_FINISHPAGE_RUN_TEXT "Set as the current DSP plug-in"
+
+ !define MUI_FINISHPAGE_SHOWREADME ; "$INSTDIR\winamp.exe"
+ !define MUI_FINISHPAGE_SHOWREADME_FUNCTION RunWinamp
+ !define MUI_FINISHPAGE_SHOWREADME_TEXT "Run Winamp"
+
+ !define MUI_FINISHPAGE_TEXT_LARGE
+ !define MUI_FINISHPAGE_TITLE_3LINES
+ !define MUI_FINISHPAGE_TEXT "${NAME} has been installed.$\n$\nTo enable the plug-in if it is not your current DSP plug-in, goto Winamp Preferences -> Plug-ins -> DSP/Effect and select the '${NAME}' entry.$\n$\nClick Finish to close this wizard."
+ !define MUI_PAGE_CUSTOMFUNCTION_SHOW RestoreCheckedStates
+ !define MUI_PAGE_CUSTOMFUNCTION_LEAVE SaveCheckedStates
+ !insertmacro MUI_PAGE_FINISH
+
+ !define MUI_UNWELCOMEFINISHPAGE_BITMAP "win.bmp"
+ !define MUI_UNWELCOMEPAGE_TEXT "This wizard will guide you through the uninstallation of the ${NAME}.$\n$\nBefore starting the uninstalltion, make sure Winamp and the ${NAME} are not running.$\n$\nClick Next to continue."
+ !define MUI_UNFINISHPAGE_TEXT "${NAME} has been uninstalled from your Winamp install.$\n$\nClick Finish to close this wizard."
+ !insertmacro MUI_UNPAGE_WELCOME
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+ !insertmacro MUI_UNPAGE_FINISH
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English" ;first language is the default language
+
+;--------------------------------
+;Reserve Files
+
+ ;If you are using solid compression, files that are required before
+ ;the actual installation should be stored first in the data block,
+ ;because this will make your installer start faster.
+
+ !insertmacro MUI_RESERVEFILE_LANGDLL
+;---------------------------------
+
+Section "Shoutcast Source DSP" Core
+ SectionIn 1 2 RO
+
+ SetOutPath "$INSTDIR\Plugins"
+
+ File "dsp_sc.dll"
+
+ ; look for lamedll.dll and remove as we now use lame_enc.dll
+ IfFileExists "$INSTDIR\Plugins\lamedll.dll" 0 +2
+ Delete "lamedll.dll"
+
+ ; Let's upgrade to Lame 3.100.1
+ SetOutPath "$INSTDIR\Shared"
+ File "..\..\..\..\resources\libraries\lame_enc.dll"
+
+; VC142 runtimes are required for Win7 & 8 installations
+; Chances are these will already exist, but let's make sure anyway...
+ ${If} ${AtLeastWin7}
+ ${AndIf} ${AtMostWin8.1}
+ SetOutPath "$INSTDIR\Microsoft.VC142.CRT"
+ File ..\..\..\..\resources\libraries\msvcp140.dll
+ File ..\..\..\..\resources\libraries\vcruntime140.dll
+ File ..\..\..\..\resources\libraries\msvcp140_1.dll
+ File ..\..\..\..\resources\libraries\msvcp140_2.dll
+ File ..\..\..\..\resources\libraries\msvcp140_atomic_wait.dll
+ File ..\..\..\..\resources\libraries\msvcp140_codecvt_ids.dll
+ File ..\..\..\..\resources\libraries\vccorlib140.dll
+ File ..\..\..\..\resources\libraries\concrt140.dll
+ ${EndIf}
+
+ ; Write the uninstall keys for Windows
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "DisplayName" "Shoutcast Source DSP Plug-in v2"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "UninstallString" "$\"$INSTDIR\${UNINSTALLER}$\""
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "NoModify" 1
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "NoRepair" 1
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "Publisher" "Radionomy SA"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "HelpLink" "http://forums.shoutcast.com/forumdisplay.php?f=140"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "InstallLocation" "$INSTDIR\Plugins"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "DisplayVersion" "${VERSION}.${BUILD}"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "URLInfoAbout" "http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "URLUpdateInfo" "https://www.shoutcast.com"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "DisplayIcon" "$\"$INSTDIR\${UNINSTALLER}$\""
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "QuietUninstallString" "$\"$INSTDIR\${UNINSTALLER}$\" /S"
+ Call GetInstalledSize
+ WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "EstimatedSize" "$GetInstalledSize.total"
+
+ IfFileExists "$INSTDIR\Uninstallers\${UNINSTALLER}" 0 +2
+ Delete "$INSTDIR\Uninstallers\${UNINSTALLER}"
+
+ SetOutPath "$INSTDIR\"
+
+ WriteUninstaller "$INSTDIR\${UNINSTALLER}"
+
+SectionEnd
+
+Section "Documentation" Docs
+ SectionIn 1
+
+SetOutPath "$INSTDIR\Plugins\Shoutcast Source DSP"
+ File ..\docs\Source_DSP_Plug-in.html
+ File ..\docs\Source_DSP_Plug-in_Config_Examples.html
+ File ..\docs\Source_DSP_Changelog.html
+SetOutPath "$INSTDIR\Plugins\Shoutcast Source DSP\res"
+ File ..\docs\res\*.png
+SectionEnd
+
+;--------------------------------
+;Section description text
+
+!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${Core} "The ${NAME} file.$\n$\n(This is always required)"
+ !insertmacro MUI_DESCRIPTION_TEXT ${Docs} "This will be put in 'Plugins\Shoutcast Source DSP' in the selected destination.$\n$\nThese will show you the features available in the plug-in as well as how to make it connect with the Shoutcast 2 tools."
+!insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}"
+ Delete "$INSTDIR\Plugins\dsp_sc.dll"
+ Delete "$INSTDIR\Plugins\lamedll.dll" ; we don't actually install this any more
+ ; Delete "$INSTDIR\Shared\lame_enc.dll"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\dsp_sc.txt"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\dsp_sc_config.txt"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\Source_DSP_Plug-in.html"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\Source_DSP_Changelog.html"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\Source_DSP_Plug-in_Config_Examples.html"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\res\*.png"
+ Delete "$INSTDIR\Plugins\Shoutcast Source DSP\res\docs.css"
+ RMDir "$INSTDIR\Plugins\Shoutcast Source DSP\res"
+ RMDir "$INSTDIR\Plugins\Shoutcast Source DSP"
+ ; Delete "$INSTDIR\Microsoft.VC142.CRT\*.dll" ; not wise, because then Winamp won't run on Win7-Win8.1 - - - why did this line exist for the VC90 Runtime?
+ Delete "$INSTDIR\${UNINSTALLER}"
+
+SectionEnd
+
+Function .onInit
+
+ !insertmacro MUI_LANGDLL_DISPLAY
+
+ ;Detect running Winamp instances and close them
+ !define WINAMP_FILE_EXIT 40001
+
+ FindWindow $R0 "Winamp v1.x"
+ IntCmp $R0 0 ok
+ MessageBox MB_YESNO|MB_ICONEXCLAMATION "Please close all instances of Winamp before installing$\n${NAME}.$\n$\nAttempt to close Winamp now?" IDYES checkagain IDNO no
+ checkagain:
+ FindWindow $R0 "Winamp v1.x"
+ IntCmp $R0 0 ok
+ SendMessage $R0 ${WM_COMMAND} ${WINAMP_FILE_EXIT} 0
+ Goto checkagain
+ no:
+ Abort
+ ok:
+
+FunctionEnd
+
+Function .onVerifyInstDir
+
+ ;Check for Winamp installation
+
+ IfFileExists $INSTDIR\Winamp.exe Good
+ Abort
+ Good:
+
+FunctionEnd
+
+;Uninstaller Functions
+/* Function un.GetParent
+
+ Exch $R0
+ Push $R1
+ Push $R2
+ Push $R3
+
+ StrCpy $R1 0
+ StrLen $R2 $R0
+
+ loop:
+ IntOp $R1 $R1 + 1
+ IntCmp $R1 $R2 get 0 get
+ StrCpy $R3 $R0 1 -$R1
+ StrCmp $R3 "\" get
+ Goto loop
+
+ get:
+ StrCpy $R0 $R0 -$R1
+
+ Pop $R3
+ Pop $R2
+ Pop $R1
+ Exch $R0
+
+FunctionEnd
+
+Function un.onInit
+
+ !insertmacro MUI_UNGETLANGUAGE
+
+ Push "$INSTDIR"
+ Call un.GetParent
+ Pop $R0
+ StrCpy $INSTDIR $R0
+
+FunctionEnd */
+
+Function GetWinampIniPath
+ StrCpy $WINAMP_INI_DIR $INSTDIR
+ ${If} $0 == ""
+ StrCpy $WINAMP_INI_DIR "$PROGRAMFILES\Winamp"
+ ${EndIf}
+ ClearErrors
+
+ ${If} ${FileExists} "$WINAMP_INI_DIR\paths.ini"
+ ReadINIStr $0 "$WINAMP_INI_DIR\paths.ini" "Winamp" "inidir"
+ ${If} $0 != ""
+ ${WordFind2X} $0 "{" "}" "E+1" $2
+ ${If} ${Errors}
+ ${IfNot} ${FileExists} "$0\*.*"
+ ${WordFind2X} $0 "%" "%" "E+1" $2
+
+ ${If} $2 == "WINAMP_ROOT_DIR"
+ ClearErrors
+ ${GetRoot} "$WINAMP_INI_DIR" $3
+ ${WordReplace} "$0" "%$2%" "$3" "E+1" $R0
+ ${If} ${Errors}
+ Return
+ ${Else}
+ StrCpy $WINAMP_INI_DIR $R0
+ ${EndIf}
+ ${ElseIf} $2 == "WINAMP_PROGRAM_DIR"
+ ClearErrors
+ ${WordReplace} "$0" "%$2%" "$WINAMP_INI_DIR" "E+1" $R0
+ ${If} ${Errors}
+ Return
+ ${Else}
+ StrCpy $WINAMP_INI_DIR $R0
+ ${EndIf}
+ ${Else}
+ ClearErrors
+ ReadEnvStr $R0 "$2"
+ ${If} $R0 != ""
+ ${WordReplace} "$0" "%$2%" "$R0" "E+1" $R0
+ ${If} ${Errors}
+ Return
+ ${Else}
+ StrCpy $WINAMP_INI_DIR $R0
+ ${EndIf}
+ ${Else}
+ Return
+ ${EndIf}
+ ${EndIf}
+ ${Else}
+ StrCpy $WINAMP_INI_DIR $0
+ ${EndIf}
+ ${Else}
+ System::Call "shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r4, i $2, i0) i .r3"
+ ClearErrors
+ ${WordReplace} "$0" "{$2}" "$4" "E+1" $R0
+ ${If} ${Errors}
+ Return
+ ${Else}
+ StrCpy $WINAMP_INI_DIR $R0
+ ${EndIf}
+ ${EndIf}
+ ${Else}
+ Return
+ ${EndIf}
+ ${Else}
+ Return
+ ${EndIf}
+FunctionEnd
+
+; set as the current DSP
+Function SetAsCurrentDSP
+ WriteINIStr "$WINAMP_INI_DIR\winamp.ini" "winamp" "dspplugin_name" "dsp_sc.dll"
+ WriteINIStr "$WINAMP_INI_DIR\winamp.ini" "winamp" "dspplugin_num" "0"
+FunctionEnd
+
+Function RunWinamp
+ StrCpy $1 1
+ File "/oname=$PLUGINSDIR\ShellDispatch.dll" "ShellDispatch.dll"
+ ${If} ${FileExists} "$PLUGINSDIR\ShellDispatch.dll"
+ ${AndIf} ${FileExists} "$INSTDIR\winamp.exe"
+ Push $0
+ StrCpy $0 ""
+ ClearErrors
+ GetFullPathName /SHORT $0 "$PLUGINSDIR\ShellDispatch.dll"
+ ${IfNot} ${Errors}
+ ${AndIf} $0 != ""
+ ExecWait 'rundll32.exe $0,RunDll_ShellExecute "open" "$INSTDIR\winamp.exe"' $1
+ ${If} ${Errors}
+ StrCpy $1 1
+ ${EndIf}
+ ${EndIf}
+ Pop $0
+ ${EndIf}
+
+ ${If} $1 != 0
+ Exec "$INSTDIR\winamp.exe"
+ ${EndIf}
+FunctionEnd
+
+; restore the last checked states on the finish page
+Function RestoreCheckedStates
+ Call GetWinampIniPath
+
+ ReadINIStr $0 "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "cur"
+ ${If} $0 == "0"
+ ${NSD_Uncheck} $mui.FinishPage.Run
+ ${EndIf}
+
+ ReadINIStr $0 "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "run"
+ ${If} $0 == "0"
+ ${NSD_Uncheck} $mui.FinishPage.ShowReadme
+ ${EndIf}
+FunctionEnd
+
+; save the last checked states on the finish page
+Function SaveCheckedStates
+
+ ${NSD_GetState} $mui.FinishPage.Run $0
+ WriteINIStr "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "cur" $0
+
+ ${NSD_GetState} $mui.FinishPage.ShowReadme $0
+ WriteINIStr "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "run" $0
+
+FunctionEnd
+
+; the check version function
+; only issue is that the strings won't be localised as i see the installer in the zip supports it
+Function CheckWinampVersion
+ ${GetFileVersion} "$INSTDIR\winamp.exe" $R0 ; Get Winamp.exe version information, $R0 = Actual Version
+ ${if} $R0 != "" ; check if Version info is not empty
+ ${VersionCompare} $R0 ${MINIMAL_VERSION} $R1 ; $R1 = Result $R1=0 Versions are equal, $R1=1 Version1 is newer, $R1=2 Version2 is newer
+ ${if} $R1 == "2"
+ MessageBox MB_OK "Warning: This plug-in requires at least Winamp v${MINIMAL_VERSION} or higher.$\nThe detected version of your Winamp install is: $R0$\n$\nThe Shoutcast Source plug-in may not function correctly with the$\n version of winamp detected.Please update your Winamp client!!$\n"
+ Abort
+ ${EndIf}
+ ${Else}
+ MessageBox MB_OK "Warning: A valid Winamp install was not detected in the specified path.$\n$\nPlease check the Winamp directory and either install the latest version$\nfrom Winamp.com or choose another directory with a valid Winamp install$\nbefore you can install the Shoutcast Source on your machine."
+ Abort
+ ${EndIf}
+FunctionEnd
+
+; Return on top of stack the total size of the selected (installed) sections, formated as DWORD
+; Assumes no more than 256 sections are defined
+Function GetInstalledSize
+ Push $0
+ Push $1
+ StrCpy $GetInstalledSize.total 0
+ ${ForEach} $1 0 256 + 1
+ StrCpy $0 0
+ ${if} ${SectionIsSelected} $1
+ SectionGetSize $1 $0
+ IntOp $GetInstalledSize.total $GetInstalledSize.total + $0
+ ${Endif}
+
+ ; Error flag is set when an out-of-bound section is referenced
+ ${if} ${errors}
+ ${break}
+ ${Endif}
+ ${Next}
+
+ ClearErrors
+ Pop $1
+ Pop $0
+ IntFmt $GetInstalledSize.total "0x%08X" $GetInstalledSize.total
+ Push $GetInstalledSize.total
+FunctionEnd \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico b/Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico
new file mode 100644
index 00000000..ba0bb6d7
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/win.bmp b/Src/Plugins/DSP/dsp_sc/NSIS/win.bmp
new file mode 100644
index 00000000..63250f65
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/NSIS/win.bmp
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO b/Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO
new file mode 100644
index 00000000..9f9119a4
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/Script1.rc b/Src/Plugins/DSP/dsp_sc/Resource/Script1.rc
new file mode 100644
index 00000000..493b987d
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/Script1.rc
@@ -0,0 +1,679 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_DIALOG DIALOGEX 0, 0, 213, 301
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Shoutcast Source"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Tab1",IDC_TAB,"SysTabControl32",WS_TABSTOP,4,4,206,293
+ GROUPBOX "",IDC_RECT,10,22,194,267,NOT WS_VISIBLE
+END
+
+IDD_ENCODER DIALOGEX 0, 0, 180, 139
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Encoder Configuration",IDC_INFO_FRAME4,0,0,179,56
+ GROUPBOX "Encoder Configuration",IDC_INFO_FRAME5,0,0,180,73
+ LTEXT "Encoder Type",IDC_ENCODER_HEADER,5,12,45,8
+ COMBOBOX IDC_ENCTYPE,5,24,169,52,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Encoder Settings",IDC_ENCSETTINGS_LABEL,5,41,95,8
+ RTEXT "",IDC_ENCSETTINGS_LAME_VER,104,41,70,8,NOT WS_VISIBLE
+ PUSHBUTTON "Encoder Settings...",IDC_ENCSETTINGS_BUTTON,5,52,169,14,NOT WS_VISIBLE
+ COMBOBOX IDC_ENCSETTINGS,5,53,169,105,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "Save Encoded Output",IDC_INFO_FRAME3,0,77,180,55
+ CONTROL "Save a copy of the encoded stream audio\nNote: Extension is updated on encoder change",IDC_SAVE_ENCODED_AUDIO,
+ "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,5,89,169,16
+ EDITTEXT IDC_SAVE_ENCODED_AUDIO_EDIT,15,110,142,14,ES_AUTOHSCROLL | ES_READONLY
+ PUSHBUTTON "...",IDC_SAVE_ENCODED_AUDIO_BROWSE,158,110,16,14
+END
+
+IDD_MAIN DIALOGEX 0, 0, 194, 271
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Status / Info (Double-Click To Configure)",IDC_STATIC,0,0,193,164,BS_LEFT
+ CONTROL "List1",IDC_OUTPUTSTATUS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSCROLL | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,6,12,182,70,WS_EX_CLIENTEDGE
+ LTEXT "",IDC_SUMMARY,8,86,180,72
+ GROUPBOX "Active input device: ",IDC_INPUT_METERS,0,168,193,102
+ CTEXT "Input Levels\n(Current / Peak)",IDC_STATIC,7,178,60,16
+ CTEXT "-inf dB",IDC_VOLUMETEXT_L,8,195,21,8
+ CTEXT "-inf dB",IDC_VOLUMETEXT_R,46,195,21,8
+ CTEXT "(-inf dB)",IDC_VOLUMETEXT_LP,5,204,27,8
+ CTEXT "(-inf dB)",IDC_VOLUMETEXT_RP,43,204,27,8
+ CONTROL "Progress1",IDC_VOLUMEGRAPH_L,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,15,215,9,47
+ RTEXT "0 dB",IDC_STATIC,28,215,21,8
+ RTEXT "-22 dB",IDC_STATIC,28,224,21,8
+ RTEXT "-45 dB",IDC_STATIC,28,234,21,8
+ RTEXT "-67 dB",IDC_STATIC,28,243,21,8
+ RTEXT "-inf dB",IDC_STATIC,28,253,21,8
+ CONTROL "Progress1",IDC_VOLUMEGRAPH_R,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,53,215,9,47
+ CONTROL "&Winamp",IDC_INPUT_WINAMP,"Button",BS_AUTORADIOBUTTON,73,168,40,8
+ CONTROL "&Soundcard",IDC_INPUT_SOUNDCARD,"Button",BS_AUTORADIOBUTTON,114,168,48,8
+END
+
+IDD_INPUT DIALOGEX 0, 0, 194, 271
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Input Configuration",IDC_STATIC,0,0,193,105
+ LTEXT "Input Device",IDC_INPUTDEVICESTATIC,6,12,42,8,WS_DISABLED
+ COMBOBOX IDC_INPUTDEVICE,6,24,100,70,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Input Settings",IDC_INPUTSETUPSTATIC,6,42,46,8
+ COMBOBOX IDC_INPUTSETUP,6,54,100,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CTEXT "",IDC_CURDEVICE,6,77,116,20
+ CTEXT "Input Levels\n(Current / Peak)",IDC_STATIC,120,12,60,16
+ CTEXT "-inf dB",IDC_VOLUMETEXT_L,120,31,21,8
+ CTEXT "-inf dB",IDC_VOLUMETEXT_R,158,31,21,8
+ CTEXT "(-inf dB)",IDC_VOLUMETEXT_LP,117,40,27,8
+ CTEXT "(-inf dB)",IDC_VOLUMETEXT_RP,155,40,27,8
+ CONTROL "Progress1",IDC_VOLUMEGRAPH_L,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,127,50,9,47
+ RTEXT "0 dB",IDC_STATIC,140,50,21,8
+ RTEXT "-22 dB",IDC_STATIC,140,59,21,8
+ RTEXT "-45 dB",IDC_STATIC,140,68,21,8
+ RTEXT "-67 dB",IDC_STATIC,140,78,21,8
+ RTEXT "-inf dB",IDC_STATIC,140,88,21,8
+ CONTROL "Progress1",IDC_VOLUMEGRAPH_R,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,165,50,9,47
+ GROUPBOX "",IDC_PANEL_RECT,0,109,193,138,NOT WS_VISIBLE
+END
+
+IDD_PANEL_WINAMP DIALOGEX 0, 0, 194, 160
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Metadata (Double-click to explore item folder)",IDC_SNDCARDSTATIC,0,0,193,84
+ CONTROL "",IDC_METALIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,11,183,67
+ GROUPBOX "Playing Artwork",IDC_SNDCARDSTATIC2,0,87,193,72
+ CONTROL "",IDC_ARTWORK,"Static",SS_BITMAP | SS_CENTERIMAGE | SS_REALSIZEIMAGE,5,99,102,54
+ CONTROL "",IDC_ARTWORK3,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,115,99,72,54
+ CTEXT "No artwork available for the playing item",IDC_ARTWORK2,63,116,65,18
+END
+
+IDD_CONNECTION DIALOGEX 0, 0, 194, 271
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Output",IDC_STATIC,0,0,193,67
+ LISTBOX IDC_OUTPUTLIST,6,12,50,46,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP
+ GROUPBOX "Status",IDC_STATIC,62,8,126,33
+ LTEXT "",IDC_STATUS,68,18,113,18
+ CONTROL "Auto Connect",IDC_AUTOCONNECT,"Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,62,45,60,16
+ PUSHBUTTON "Connect",IDC_CONNECT,127,45,61,16
+ CONTROL "Tab1",IDC_CONTAB,"SysTabControl32",WS_TABSTOP,0,71,194,198
+ GROUPBOX "",IDC_PANELRECT_C,6,90,180,171,NOT WS_VISIBLE | WS_DISABLED
+END
+
+IDD_PANEL_DIRECTORY DIALOGEX 0, 0, 180, 175
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Directory Configuration",IDC_STATIC,0,0,180,105
+ CONTROL "Make this stream public (Recommended)",IDC_PUBLIC,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,12,144,10
+ LTEXT "Name",IDC_NAME_HEADER,5,25,19,8
+ EDITTEXT IDC_DESCRIPTION,5,33,169,14,ES_AUTOHSCROLL
+ LTEXT "URL",IDC_STATIC,5,50,16,8
+ EDITTEXT IDC_SERVERURL,5,58,84,14,ES_AUTOHSCROLL
+ LTEXT "Genre",IDC_STATIC,93,50,20,8
+ EDITTEXT IDC_GENRE,93,58,70,14,ES_AUTOHSCROLL | ES_READONLY
+ PUSHBUTTON "",IDC_GENRES,164,57,11,15,BS_ICON
+ LTEXT "AIM",IDC_STATIC,5,77,14,8
+ EDITTEXT IDC_AIM,5,85,54,14,ES_AUTOHSCROLL
+ LTEXT "ICQ",IDC_STATIC,63,77,13,8
+ EDITTEXT IDC_ICQ,63,85,54,14,ES_AUTOHSCROLL
+ LTEXT "IRC",IDC_STATIC,121,77,13,8
+ EDITTEXT IDC_IRC,121,85,53,14,ES_AUTOHSCROLL
+ GROUPBOX "",IDC_INFO_FRAME,0,107,179,52
+ CTEXT "Making the stream 'public' will instruct the server to list the stream in the Shoutcast Radio Directory. Uncheck this if this is not wanted e.g. for a internal company stream.",IDC_INFO_TEXT,4,118,172,34
+END
+
+IDD_PANEL_LOGIN DIALOGEX 0, 0, 180, 175
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Output Configuration",IDC_STATIC,0,0,180,114
+ LTEXT "Server Address",IDC_ADDRESS_HEADER,5,12,50,8
+ EDITTEXT IDC_ADDRESS,5,22,80,14,ES_AUTOHSCROLL
+ LTEXT "Port",IDC_STATIC,89,12,14,8
+ EDITTEXT IDC_PORT,89,22,40,14,ES_AUTOHSCROLL
+ LTEXT "Stream ID",IDC_STATIC,133,12,32,8
+ EDITTEXT IDC_STATIONID,133,22,40,14,ES_AUTOHSCROLL
+ LTEXT "DJ / User ID",IDC_STATIC,5,40,40,8
+ EDITTEXT IDC_USERID,5,50,60,14,ES_AUTOHSCROLL
+ LTEXT "Password",IDC_PASSWORD_HEADER,69,40,32,8
+ EDITTEXT IDC_PASSWORD,69,50,104,14,ES_PASSWORD | ES_AUTOHSCROLL
+ CONTROL "Automatic reconnection on connection failure",IDC_RECONNECT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,68,160,10
+ LTEXT "Reconnection timeout",IDC_STATIC,5,83,73,8
+ EDITTEXT IDC_TIMEOUT,80,81,28,12,ES_AUTOHSCROLL | ES_NUMBER
+ LTEXT "seconds",IDC_STATIC,110,83,29,8
+ LTEXT "Connect using:",IDC_STATIC,5,98,50,8
+ COMBOBOX IDC_PROTOCOL,59,96,114,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ GROUPBOX "",IDC_INFO_FRAME2,0,115,179,59
+ CTEXT "Connecting to a v1.x server in v2.x mode will show the ""Unable To Connect To The Server"" error. To fix this error you will need to select either ""Automatic mode"" or ""v1.x mode"".",IDC_INFO_TEXT2,4,126,172,42,NOT WS_VISIBLE
+ CTEXT "When the DJ password is formatted as <djlogin>:<djpassword> e.g. dj_1:noise\n\nEnter <djlogin> in 'DJ / User ID' e.g. dj_1\nEnter <djpassword> in 'Password' e.g. noise",IDC_INFO_TEXT3,4,126,172,42,NOT WS_VISIBLE
+ CTEXT """Automatic mode"" attempts to pick the most appropriate protocol mode to connect to the server. If this does not work correctly, you can select a specific protocol mode to use.",IDC_INFO_TEXT4,4,126,172,42,NOT WS_VISIBLE
+END
+
+IDD_NSVCONFIG DIALOGEX 0, 0, 269, 198
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "AAC+ Encoder Options"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ DEFPUSHBUTTON "OK",IDCANCEL,214,177,50,14
+ GROUPBOX "",IDC_GO_HERE,7,7,255,167,NOT WS_VISIBLE
+END
+
+IDD_ABOUT DIALOGEX 0, 0, 194, 271
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "About",IDC_STATIC,0,0,193,100
+ ICON IDI_ICY,IDC_ABOUT_ICON,86,11,20,20,SS_NOTIFY | SS_REALSIZEIMAGE
+ CTEXT "",IDC_PROGRAMNAME,27,35,140,61
+ RTEXT "Alternatively visit",IDC_STATIC,10,256,58,8
+ CONTROL "www.shoutcast.com",IDC_ABOUTLINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_VCENTER | WS_TABSTOP,71,254,68,11
+ LTEXT "for updates.",IDC_STATIC,141,256,42,8
+ GROUPBOX "Documentation and Support",IDC_STATIC,0,104,193,62
+ LTEXT "For more information about the features available in the plug-in please look at the provided plug-in",IDC_STATIC,5,116,182,18
+ CONTROL "documentation",IDC_HELPLINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_TOP | WS_TABSTOP,139,123,51,11
+ LTEXT "For issues with the plug-in not covered by the online documentation please visit the",IDC_STATIC,5,142,182,18
+ CONTROL "Shoutcast support forum",IDC_FORUMLINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_TOP | WS_TABSTOP,103,149,86,11
+ GROUPBOX "Updates",IDC_UPDATE_HEADER,0,170,193,100
+ LTEXT "We recommend keeping the plug-in up-to-date for bug fixes, perfomance improvements and new features.",IDC_STATIC,5,181,182,18
+ PUSHBUTTON "Check for updates",IDC_GET_UPDATE,47,205,100,14
+ CTEXT "A new version of the Source DSP is now available.\n\t to download and install the new version.",IDC_STATIC_UPDATE,5,226,182,18,NOT WS_VISIBLE
+ CONTROL "Click here",IDC_UPDATELINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_TOP | NOT WS_VISIBLE | WS_TABSTOP,13,233,33,11
+END
+
+IDD_LOGGING DIALOGEX 0, 0, 180, 160
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Connection Logging",IDC_STATIC,0,0,180,60
+ CONTROL "Enable logging of connection status messages",IDC_LOGGING,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,12,163,10
+ CONTROL "Clear log file on logging startup",IDC_CLEAR_ON_STARTUP,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,26,115,10
+ PUSHBUTTON "Open log file...",IDC_VIEW_LOG,5,40,82,14
+ PUSHBUTTON "Clear log file",IDC_CLEAR_LOG,91,40,82,14
+ GROUPBOX "Next Track Logging",IDC_STATIC,0,64,180,60
+ CONTROL "Enable next track logging",IDC_NEXT_TRACK_LOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,77,97,10
+ EDITTEXT IDC_NEXT_TRACK_EDIT,15,91,142,14,ES_AUTOHSCROLL | ES_READONLY
+ PUSHBUTTON "...",IDC_NEXT_TRACK_BROWSE,158,91,16,14
+ CONTROL "Save report as xml instead of plain text",IDC_NEXT_TRACK_XML,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,109,142,10
+END
+
+IDD_ARTWORK DIALOGEX 0, 0, 180, 175
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Artwork",IDC_ARTWORK_V1_FRAME,0,0,179,70
+ CTEXT "Stream is setup for a Shoutcast v1.x server which does not support in-stream artwork.\n\nTo send in-stream artwork, select either ""Automatic mode"" or ""v2.x mode"" and ensure you are connecting to a v2.x server.",IDC_ART_V1_NOTE,5,12,169,54,NOT WS_VISIBLE
+ GROUPBOX "Artwork",IDC_ARTWORK_V2_FRAME,0,0,179,136
+ CONTROL "Send in-stream artwork",IDC_USE_ART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,12,91,10
+ CONTROL "Send artwork from the playing file (if available)\nNote: This is sent in the loaded image format",IDC_USE_ART_PLAYING,
+ "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,15,26,160,18
+ CONTROL "Send artwork for stream branding",IDC_USE_ART_STREAM,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,47,124,10
+ EDITTEXT IDC_ART_EDIT,25,61,132,14,ES_AUTOHSCROLL | ES_READONLY
+ PUSHBUTTON "...",IDC_ART_BROWSE,158,61,16,14
+ CTEXT "",IDC_ART_V2_NOTE,5,84,168,44
+END
+
+#if defined(APSTUDIO_INVOKED) || !defined(FULL)
+IDD_PANEL_TITLE DIALOGEX 0, 0, 180, 175
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Title Update Configuration",IDC_STATIC,0,0,180,109
+ CONTROL "Disable title updates",IDC_NOTITLES,"Button",BS_AUTORADIOBUTTON,5,13,81,10
+ CONTROL "Follow Winamp's title updates",IDC_AUTOTITLE,"Button",BS_AUTORADIOBUTTON,5,27,110,10
+ CONTROL "Send next track title to the server (if available)",IDC_SENDNEXTTITLES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,41,166,10
+ CONTROL "Manual title updates",IDC_MANUALTITLE,"Button",BS_AUTORADIOBUTTON,5,55,81,10
+ RTEXT "Now",IDC_STATIC,13,72,15,8
+ EDITTEXT IDC_TITLE,33,69,98,14,ES_AUTOHSCROLL | WS_DISABLED
+ RTEXT "Next",IDC_STATIC,13,89,16,8
+ EDITTEXT IDC_NEXT,34,88,97,14,ES_AUTOHSCROLL | WS_DISABLED
+ PUSHBUTTON "Send\nUpdate",IDC_SEND,135,68,40,35,BS_MULTILINE
+END
+#endif
+
+#if defined(APSTUDIO_INVOKED) || defined(FULL)
+#if defined(APSTUDIO_INVOKED)
+IDD_PANEL_TITLE$(FULL) DIALOGEX 0, 0, 180, 175
+#else
+IDD_PANEL_TITLE DIALOGEX 0, 0, 180, 175
+#endif
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Title Update Configuration",IDC_STATIC,0,0,180,94
+ CONTROL "Follow Winamp updates",IDC_AUTOTITLE,"Button",BS_AUTORADIOBUTTON,5,13,91,10
+ CONTROL "Send next track title to the server (if available)",IDC_SENDNEXTTITLES,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,27,166,10
+ CONTROL "Manually update",IDC_MANUALTITLE,"Button",BS_AUTORADIOBUTTON,5,41,69,10
+ RTEXT "Now",IDC_STATIC,13,58,15,8
+ EDITTEXT IDC_TITLE,33,55,98,14,ES_AUTOHSCROLL | WS_DISABLED
+ RTEXT "Next",IDC_STATIC,13,75,16,8
+ EDITTEXT IDC_NEXT,34,74,97,14,ES_AUTOHSCROLL | WS_DISABLED
+ PUSHBUTTON "Send\nUpdate",IDC_SEND,135,55,40,32,BS_MULTILINE
+ CONTROL "Use external file",IDC_EXTERNALTITLE,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,5,92,68,10
+ EDITTEXT IDC_NEXT_TRACK_EDIT,15,106,142,14,ES_AUTOHSCROLL | ES_READONLY | NOT WS_VISIBLE
+ CONTROL "Send when file content changes",IDC_AUTOTITLE4,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,15,124,118,10
+ CONTROL "Send every",IDC_AUTOTITLE5,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,15,138,53,10
+ LTEXT "Delay sending title update by",IDC_STATIC,15,157,94,8,NOT WS_VISIBLE
+ EDITTEXT IDC_TIMEOUT,68,137,32,12,ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE
+ CONTROL "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | NOT WS_VISIBLE,98,135,11,16
+ LTEXT "seconds (if changed)",IDC_STATIC,104,139,68,8,NOT WS_VISIBLE
+ PUSHBUTTON "...",IDC_NEXT_TRACK_BROWSE,158,106,16,14,NOT WS_VISIBLE
+ EDITTEXT IDC_TIMEOUT2,112,155,32,12,ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE
+ CONTROL "",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | NOT WS_VISIBLE,143,152,11,16
+ LTEXT "seconds",IDC_STATIC,148,157,27,8,NOT WS_VISIBLE
+END
+#endif
+
+IDD_PANEL_LINEIN DIALOGEX 0, 0, 194, 160
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Soundcard Mixer Control",IDC_SNDCARDSTATIC,0,0,193,156
+ LTEXT "Choose Capture Device",IDC_STATIC,6,12,76,8
+ COMBOBOX IDC_DEVBOX,6,24,163,30,CBS_DROPDOWNLIST | WS_TABSTOP
+ PUSHBUTTON "",IDC_REFRESH_DEVICES,170,23,16,15,BS_ICON
+ PUSHBUTTON "Open Mixer",IDC_MIXER,5,41,181,15
+ RTEXT "Music Level",IDC_MUSSTATIC,10,61,46,8,SS_CENTERIMAGE
+ CONTROL "Slider1",IDC_MUSSLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,61,96,12
+ LTEXT "0 dB",IDC_MUSLEV1_TEXT,159,61,24,8,SS_CENTERIMAGE
+ RTEXT "BG Level",IDC_BGMUSSTATIC,10,74,46,8,SS_CENTERIMAGE
+ CONTROL "Slider1",IDC_MUS2SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,74,96,12
+ LTEXT "0 dB",IDC_MUSLEV2_TEXT,159,74,25,8,SS_CENTERIMAGE
+ RTEXT "Mic Level",IDC_MICSTATIC,10,87,46,8,SS_CENTERIMAGE
+ CONTROL "Slider1",IDC_MICSLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,87,96,12
+ LTEXT "0 dB",IDC_MICLEV_TEXT,159,87,26,8,SS_CENTERIMAGE
+ RTEXT "Fade Time",IDC_FADESTATIC,10,100,46,8,SS_CENTERIMAGE
+ CONTROL "Slider1",IDC_FADESLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,100,96,12
+ LTEXT "0ms",IDC_FADETIME_TEXT,159,100,28,8,SS_CENTERIMAGE
+ RTEXT "Capture Device Fade Time",IDC_MICFADESTATIC,7,113,49,18
+ CONTROL "",IDC_MICFADESLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,114,96,12
+ LTEXT "0ms",IDC_MICFADETIME_TEXT,159,114,28,8,SS_CENTERIMAGE
+ PUSHBUTTON "Push to Talk",IDC_PTT,5,133,130,17
+ CONTROL "Lock",IDC_LOCK,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,139,133,38,17
+ PUSHBUTTON "",IDC_LOCK_MODE,177,133,11,17,BS_ICON
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DIALOG, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ END
+
+ IDD_ENCODER, DIALOG
+ BEGIN
+ LEFTMARGIN, 5
+ RIGHTMARGIN, 174
+ END
+
+ IDD_MAIN, DIALOG
+ BEGIN
+ LEFTMARGIN, 6
+ RIGHTMARGIN, 188
+ END
+
+ IDD_INPUT, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 256
+ END
+
+ IDD_CONNECTION, DIALOG
+ BEGIN
+ LEFTMARGIN, 6
+ RIGHTMARGIN, 188
+ END
+
+ IDD_NSVCONFIG, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 262
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 191
+ END
+
+ IDD_ABOUT, DIALOG
+ BEGIN
+ BOTTOMMARGIN, 258
+ END
+
+ IDD_LOGGING, DIALOG
+ BEGIN
+ HORZGUIDE, 74
+ END
+END
+#endif // APSTUDIO_INVOKED
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICY ICON "ICY.ICO"
+IDI_DOWNARROW ICON "downarrow.ico"
+IDI_REFRESH ICON "refresh.ico"
+IDI_PLAY ICON "play.ico"
+IDI_STOP ICON "stop.ico"
+IDI_KILL ICON "kill.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,4,2,449
+ PRODUCTVERSION 2,4,2,449
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "CompanyName", "Radionomy SA"
+ VALUE "FileDescription", "Winamp DSP Plug-in"
+ VALUE "FileVersion", "2, 4, 2, 449"
+ VALUE "InternalName", "dsp_sc.dll"
+ VALUE "LegalCopyright", "Copyright © 2023 Radionomy SA"
+ VALUE "LegalTrademarks", "Shoutcast is a trademark of Radionomy SA"
+ VALUE "OriginalFilename", "dsp_sc.dll"
+ VALUE "ProductName", "Shoutcast Source DSP"
+ VALUE "ProductVersion", "2, 4, 2, 449"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ 65535 "{88380E65-4068-49BA-8EA4-3F2AF12D0A4F}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_PLUGIN_NAME "Shoutcast Source DSP v%s"
+ IDS_MODULE_NAME "Shoutcast Source DSP"
+ IDS_LAMEDLL_ISSUE "There was an error while trying to load the encoder dll file (lamedll.dll).\nPlease make sure you have the correct version required for this plug-in.\n\nThe Source DSP will now abort loading until resolved."
+ IDS_CURRENT_BITRATE "Current bitrate: %d kbps"
+ IDS_ENCODER_SETTINGS "Encoder Settings"
+ IDS_OUTPUT_X "Output %u"
+ IDS_NONE "None"
+ IDS_MP3_ENCODER "MP3 Encoder"
+ IDS_AACP_ENCODER "AAC+ Encoder"
+ IDS_NO_ENCODER_SELECTED "No encoder selected"
+ IDS_PANEL_LOGIN "Login"
+ IDS_PANEL_DIRECTORY "Directory"
+ IDS_PANEL_ENCODERS "Encoder"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_TAB_MAIN "Summary"
+ IDS_TAB_OUTPUT "Output"
+ IDS_TAB_INPUT "Input"
+ IDS_COL_OUTPUT_NAME "Stream"
+ IDS_COL_STATUS "Status"
+ IDS_INPUT_WINAMP "Winamp (Recommended)"
+ IDS_INPUT_SOUNDCARD "Soundcard Input"
+ IDS_X_DB "%d dB"
+ IDS_INF_DB "-Inf dB"
+ IDS_NOT_CONNECTED "Not Connected"
+ IDS_ERROR "Error!"
+ IDS_CONNECTING "Connecting..."
+ IDS_SEND_CIPHER_REQUEST "Sending Cipher Request"
+ IDS_CIPHER_RESPONSE_RECEIVED "Cipher Response Received"
+ IDS_SENDING_AUTH "Sending Authorization"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_RECEIVING_AUTH_RESPONSE "Receiving Authorization Response"
+ IDS_SENDING_CONTENT_TYPE "Sending Content Type"
+ IDS_RESPONSE_RECEIVED "Response Received"
+ IDS_SENDING_BITRATE "Sending Bitrate"
+ IDS_SEND_BUF_SIZE "Sending Buffer Size"
+ IDS_SEND_MAX_PAYLOAD_SIZE "Sending Maximum Payload Size"
+ IDS_SEND_YP_INFO "Sending YP Information"
+ IDS_SEND_FLUSH "Sending Flush"
+ IDS_SEND_STANDBY "Sending Standby"
+ IDS_SEND_INTRO_FILE "Sending Intro File"
+ IDS_SEND_BACKUP_FILE "Sending Backup File"
+ IDS_DISCONNECTING "Disconnecting"
+ IDS_RECONNECTING_X "Reconnecting [%u]"
+ IDS_SEND_TITLE_UPDATE "Sending Title Update"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_CONNECT "Connect"
+ IDS_KILL "Kill"
+ IDS_ABORT "Abort"
+ IDS_DISCONNECT "Disconnect"
+ IDS_X_HZ_X "%u Hz, %s"
+ IDS_MONO "Mono"
+ IDS_STEREO "Stereo"
+ IDS_X_MS "%u ms"
+ IDS_PLUGIN_UNINSTALL "Do you also want to remove the saved settings for this plug-in?"
+ IDS_PASS_ERROR "Authentication Error:\nInvalid DJ / User ID or Password"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_DEVICE_STRING "Device: %s"
+ IDS_MIC_LEGACY_MODE "Microphone (Legacy mode)"
+ IDS_LINEIN_LEGACY_MODE "Line In (Legacy mode)"
+ IDS_PTT_ON_STARTUP "Enable 'Push to Talk' on startup"
+ IDS_PANEL_LOGGING "Logs"
+ IDS_CIPHER_ERROR "Authentication Error:\nCipher Does Not Match"
+ IDS_NO_DEVICES_FOUND "No input devices found or currently available"
+ IDS_NO_CAPTURE_DEVICES "No available capture devices found"
+ IDS_ABOUT_MESSAGE "%s\nv%hs [Build %hs]\n\nBuild date: %hs\n\nCopyright © 2014 Radionomy SA\nAll Rights Reserved."
+ IDS_PANEL_ART "Artwork"
+ IDS_JPEG_FILE "JPEG File"
+ IDS_PNG_FILE "PNG File"
+ IDS_BMP_FILE "BMP File"
+ IDS_GIF_FILE "GIF File"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_FHGAAC_ENCODER "ADTS-AAC Encoder"
+ IDS_PANEL_TITLES "Titles"
+ IDS_PUBLIC "Public"
+ IDS_PRIVATE "Private"
+ IDS_FOLLOW_WA "Follow Winamp"
+ IDS_MANUAL "Manual"
+ IDS_DISABLED "Disabled"
+ IDS_NOT_SET "Not set"
+ IDS_SUMMARY "Shoutcast mode: v%d\nDirectory mode: %s\nOutput server: %hs:%d\nTitle updates: %s\nEncoder mode: %s%s\nLogging enabled: %s\nAuto Connect: %s\nSave Encoded Output: %s"
+ IDS_LAME_ENCODER_VER "Encoder v%s"
+ IDS_SUMMARY_KBPS " at %d kbps"
+ IDS_X_DB_PEAK "(%d dB)"
+ IDS_CHANGE_NAME "Change Name"
+ IDS_SET_PASSWORD "Set Password"
+ IDS_SET_ENCODER "Set Encoder"
+ IDS_STREAMID_ERROR "Authentication Error:\nEntered Stream ID Not Allowed"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_PARSE_ERROR "Connection Error:\nSent or Received Invalid Request"
+ IDS_VERSION_ERROR "Authentication Error:\nUltravox Version Not Supported"
+ IDS_IN_USE_ERROR "Connection Not Allowed By Server Due To Existing Source Connection"
+ IDS_YES "Yes"
+ IDS_NO "No"
+ IDS_NOT_CONFIGURED "Stream Configuration Not Complete"
+ IDS_ALL_FILES "All Files (*.*)|*.*||"
+ IDS_MPEG_AUDIO_FILES "MPEG Audio Files (*.%s)|*.%s||"
+ IDS_BITRATE_ERROR "Connection Not Allowed By Server Due To Configured Output Bitrate"
+ IDS_SET_SERVER "Set Server"
+ IDS_NOT_SET_SUMMARY "<not set>"
+ IDS_SENT_X "[%u:%0.2u:%0.2u] Sent %s"
+ IDS_B "bytes"
+ IDS_KIB "KiB"
+ IDS_MIB "MiB"
+ IDS_GIB "GiB"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_TIB "TiB"
+ IDS_BLOCKED_ERROR "Connection Not Allowed By Server"
+ IDS_V2_ARTWORK "Stream artwork: %s\nPlaying artwork: %s\n\nArtwork which is over 511 KiB in size will\nnot be sent to the Shoutcast v2 server."
+ IDS_ENABLED_SIZE "Loaded (%s)"
+ IDS_EMPTY_ART "Oversized or empty"
+ IDS_STREAM_MOVED_ERROR "Connection Not Allowed By Server Due To Stream Having Been Moved"
+ IDS_ARTWORK_SIZES "Width: %dpx\nHeight: %dpx\n\nRaw size: %s\nType: %s"
+ IDS_FILEPATH "Filepath"
+ IDS_TITLE "Title"
+ IDS_ARTIST "Artist"
+ IDS_ALBUM "Album"
+ IDS_GENRE "Genre"
+ IDS_YEAR "Year"
+ IDS_COMMENT "Comment"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_FAILED_LOAD_LAMEDLL "Unable to find the MP3 encoder dll (lame_enc.dll).\nPlease make sure it is installed in the following location:\n\n""%s""\n\n\nThe Shoutcast Source DSP will load in a reduced mode."
+ IDS_MP3_ENCODING_NOT_AVAILABLE "MP3 encoding not available"
+ IDS_SWITCHING_PROTOCOL "Automatically Switching Shoutcast Protocol Mode From v%d.x To v%d.x"
+ IDS_AUTOMATIC "Automatic mode"
+ IDS_V2_MODE "v2.x mode"
+ IDS_V1_MODE "v1.x mode (legacy servers)"
+ IDS_ENABLE_OTHER_MODE "Unable To Connect To The Server.\nEnable 'Automatic' or 'v%d.x' mode."
+ IDS_24BIT_MODE_DETECTED "Winamp appears to be configured to provide 24-bit audio via the 'Playback' preferences.\n\nThis is not supported by this plug-in and it will now abort loading.\n\nDisabling the 24-bit audio option on the 'Playback' preferences page will allow this plug-in to be used."
+ IDS_UPDATE_HEADER "Update: v%hs is now available"
+ IDS_UPDATE_TITLE "<> Plug-in Update Available <>"
+ IDS_TAB_ABOUT "About | Support | Updates"
+ IDS_UPDATE "Updates"
+ IDS_UPDATE_CHECK_ERROR "The Winamp install is not able to run the update check\nat this time. If this warning persists you will need to\nmanually check for an updated version of the plug-in.\n\nChoose 'Yes' to go to the Shoutcast download page."
+ IDS_CHECK_FOR_UPDATES "Check for updates"
+ IDS_CHECKING_FOR_UPDATES "Checking for updates..."
+ IDS_NO_NEW_UPDATE "This is the current version of the Source DSP plug-in."
+END
+
+STRINGTABLE
+BEGIN
+ IDS_HAS_NEW_UPDATE "A new version of the Source DSP is now available.\n\t to download and install the new version."
+ IDS_CHECK_UPDATE_FAIL "Unable to check for version updates at this time."
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico b/Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico
new file mode 100644
index 00000000..b3acb3f6
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/kill.ico b/Src/Plugins/DSP/dsp_sc/Resource/kill.ico
new file mode 100644
index 00000000..232f7d1b
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/kill.ico
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/play.ico b/Src/Plugins/DSP/dsp_sc/Resource/play.ico
new file mode 100644
index 00000000..91ad0c01
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/play.ico
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/refresh.ico b/Src/Plugins/DSP/dsp_sc/Resource/refresh.ico
new file mode 100644
index 00000000..c31d7ea3
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/refresh.ico
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/resource.h b/Src/Plugins/DSP/dsp_sc/Resource/resource.h
new file mode 100644
index 00000000..0c1328cc
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/resource.h
@@ -0,0 +1,327 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Script1.rc
+//
+#define IDS_PLUGIN_NAME 1
+#define IDS_MODULE_NAME 2
+#define IDS_LAMEDLL_ISSUE 4
+#define IDS_CURRENT_BITRATE 5
+#define IDS_ENCODER_SETTINGS 6
+#define IDS_OUTPUT_X 7
+#define IDS_NONE 8
+#define IDS_MP3_ENCODER 9
+#define IDS_AACP_ENCODER 10
+#define IDS_AAC_LC_ENCODER 11
+#define IDS_NO_ENCODER_SELECTED 12
+#define IDS_PANEL_CONNECTION 13
+#define IDS_PANEL_LOGIN 13
+#define IDS_PANEL_YELLOWPAGES 14
+#define IDS_PANEL_DIRECTORY 14
+#define IDS_PANEL_ENCODERS 15
+#define IDS_TAB_MAIN 16
+#define IDS_TAB_OUTPUT 17
+#define IDS_TAB_INPUT 18
+#define IDS_COL_OUTPUT_NAME 20
+#define IDS_COL_STATUS 21
+#define IDS_INPUT_WINAMP 22
+#define IDS_INPUT_SOUNDCARD 23
+#define IDS_X_DB 24
+#define IDS_INF_DB 25
+#define IDS_NOT_CONNECTED 26
+#define IDS_ERROR 27
+#define IDS_CONNECTING 28
+#define IDS_SEND_CIPHER_REQUEST 29
+#define IDS_CIPHER_RESPONSE_RECEIVED 30
+#define IDS_SENDING_AUTH 31
+#define IDS_RECEIVING_AUTH_RESPONSE 32
+#define IDS_SENDING_CONTENT_TYPE 33
+#define IDS_RESPONSE_RECEIVED 34
+#define IDS_SENDING_BITRATE 35
+#define IDS_SEND_BUF_SIZE 37
+#define IDS_SEND_MAX_PAYLOAD_SIZE 38
+#define IDS_SEND_YP_INFO 39
+#define IDS_SEND_FLUSH 40
+#define IDS_SEND_STANDBY 41
+#define IDS_SEND_INTRO_FILE 42
+#define IDS_SEND_BACKUP_FILE 43
+#define IDS_DISCONNECTING 44
+#define IDS_SENT_X_BYTES 45
+#define IDS_RECONNECTING_X 46
+#define IDS_SEND_TITLE_UPDATE 47
+#define IDS_CONNECT 48
+#define IDS_KILL 49
+#define IDS_ABORT 50
+#define IDS_DISCONNECT 51
+#define IDS_X_HZ_X 53
+#define IDS_MONO 54
+#define IDS_STEREO 55
+#define IDS_X_MS 56
+#define IDS_AAC_LC_CONFIG_TITLE 58
+#define IDS_STRING59 59
+#define IDS_PLUGIN_UNINSTALL 59
+#define IDS_PASS_ERROR 62
+#define IDS_OGG_ENCODER 63
+#define IDS_OGG_CONFIG_TITLE 64
+#define IDS_XP_SOUND 65
+#define IDS_STRING65 65
+#define IDS_DEVICE_STRING 65
+#define IDS_MIC_LEGACY_MODE 66
+#define IDS_LINEIN_LEGACY_MODE 67
+#define IDS_PTT_ON_STARTUP 68
+#define IDS_PANEL_LOGGING 69
+#define IDS_CIPHER_ERROR 70
+#define IDS_STRING36 71
+#define IDS_NO_DEVICES_FOUND 71
+#define IDS_NO_CAPTURE_DEVICES 72
+#define IDS_ABOUT_MESSAGE 73
+#define IDS_CIPHER_ERROR_ENABLE_V1_MODE 74
+#define IDS_PANEL_ART 75
+#define IDS_JPEG_FILE 76
+#define IDS_PNG_FILE 77
+#define IDS_BMP_FILE 78
+#define IDS_GIF_FILE 79
+#define IDS_FHGAAC_ENCODER 80
+#define IDS_PANEL_TITLES 81
+#define IDS_PUBLIC 82
+#define IDS_PRIVATE 83
+#define IDS_FOLLOW_WA 84
+#define IDS_MANUAL 85
+#define IDS_DISABLED 86
+#define IDS_NOT_SET 87
+#define IDS_SUMMARY 88
+#define IDS_LAME_ENCODER_VER 89
+#define IDS_SUMMARY_KBPS 90
+#define IDS_X_DB_PEAK 91
+#define IDS_CHANGE_NAME 92
+#define IDS_SET_PASSWORD 93
+#define IDS_SET_ENCODER 94
+#define IDS_STREAMID_ERROR 95
+#define IDS_PARSE_ERROR 96
+#define IDS_VERSION_ERROR 97
+#define IDS_IN_USE_ERROR 98
+#define IDS_YES 99
+#define IDS_YES2 100
+#define IDS_NO 100
+#define IDD_DIALOG 101
+#define IDS_NOT_CONFIGURED 101
+#define IDS_ALL_FILES 102
+#define IDD_MAIN 103
+#define IDS_MPEG_AUDIO_FILES 103
+#define IDD_CONNECTION 104
+#define IDS_BITRATE_ERROR 104
+#define IDD_ENCODER 105
+#define IDS_SET_SERVER 105
+#define IDD_INPUT 106
+#define IDS_NOT_SET_SUMMARY 106
+#define IDD_PANEL_LINEIN 107
+#define IDS_SENT_X 107
+#define IDI_ICY 108
+#define IDS_B 108
+#define IDS_KIB 109
+#define IDD_PANEL_DIRECTORY 110
+#define IDS_MiB 110
+#define IDS_MIB 110
+#define IDS_GiB 111
+#define IDS_GIB 111
+#define IDS_TIB 112
+#define IDD_NSVCONFIG 113
+#define IDS_BLOCKED_ERROR 113
+#define IDS_V2_ARTWORK 114
+#define IDB_BITMAP1 115
+#define IDS_ENABLED_SIZE 115
+#define IDS_EMPTY_ART 116
+#define IDS_STREAM_MOVED_ERROR 117
+#define IDS_ARTWORK_SIZES 120
+#define IDS_FILEPATH 121
+#define IDI_DOWNARROW 122
+#define IDS_TITLE 122
+#define IDD_LOGGING 123
+#define IDS_ARTIST 123
+#define IDI_REFRESH 124
+#define IDD_ARTWORK 124
+#define IDS_ALBUM 124
+#define IDD_PANEL_TITLE 125
+#define IDS_GENRE 125
+#define IDS_YEAR 126
+#define IDI_PLAY 127
+#define IDS_COMMENT 127
+#define IDI_STOP 128
+#define IDS_FAILED_LOAD_LAMEDLL 128
+#define IDI_KILL 129
+#define IDS_MP3_ENCODING_NOT_AVAILABLE 129
+#define IDS_SWITCHING_PROTOCOL 130
+#define IDS_AUTOMATIC 131
+#define IDD_PANEL_WINAMP 132
+#define IDS_V2_MODE 132
+#define IDD_PANEL_LOGIN 133
+#define IDS_V1_MODE 133
+#define IDD_ABOUT 134
+#define IDS_ENABLE_OTHER_MODE 134
+#define IDS_24BIT_MODE_DETECTED 135
+#define IDS_UPDATE_HEADER 136
+#define IDS_UPDATE_TITLE 137
+#define IDS_TAB_ABOUT 138
+#define IDS_UPDATE 139
+#define IDS_UPDATE_CHECK_ERROR 140
+#define IDS_CHECK_FOR_UPDATES 141
+#define IDS_CHECKING_FOR_UPDATES 142
+#define IDS_NO_NEW_UPDATE 143
+#define IDS_STRING144 144
+#define IDS_HAS_NEW_UPDATE 144
+#define IDS_CHECK_UPDATE_FAIL 145
+#define IDC_ABOUT_ICON 666
+#define IDC_TAB 1000
+#define IDC_RECT 1001
+#define IDC_OUTPUTSTATUS 1002
+#define IDC_ADDRESS 1003
+#define IDC_PORT 1004
+#define IDC_PASSWORD 1005
+#define IDC_CONNECT 1006
+#define IDC_STATUS 1007
+#define IDC_OUTPUTLIST 1008
+#define IDC_ENCODERLIST 1009
+#define IDC_ENCSETTINGS 1010
+#define IDC_ENCTYPE 1012
+#define IDC_STATIONID 1014
+#define IDC_AUTOTITLE4 1015
+#define IDC_AUTOTITLE5 1016
+#define IDC_DESCRIPTION 1017
+#define IDC_PUBLIC 1019
+#define IDC_URL 1020
+#define IDC_GENRE 1021
+#define IDC_AIM 1022
+#define IDC_ICQ 1023
+#define IDC_IRC 1024
+#define IDC_SENDNEXTTITLES 1025
+#define IDC_TITLE 1027
+#define IDC_NEXT 1028
+#define IDC_INPUTDEVICE 1031
+#define IDC_MUS2SLIDER 1032
+#define IDC_VOLUMETEXT_L 1033
+#define IDC_VOLUMEGRAPH_L 1034
+#define IDC_VOLUMETEXT_R 1035
+#define IDC_VOLUMEGRAPH_R 1036
+#define IDC_VOLUMETEXT_LP 1037
+#define IDC_VOLUMETEXT_RP 1038
+#define IDC_MUSSLIDER 1039
+#define IDC_MICSLIDER 1040
+#define IDC_MICLEV_TEXT 1041
+#define IDC_FADESLIDER 1042
+#define IDC_FADETIME_TEXT 1043
+#define IDC_MIXER 1044
+#define IDC_PTT 1045
+#define IDC_LOCK 1046
+#define IDC_MICFADESLIDER 1047
+#define IDC_MICFADETIME_TEXT 1048
+#define IDC_INPUTDEVICESTATIC 1049
+#define IDC_INPUTSETUP 1050
+#define IDC_SNDCARDSTATIC 1051
+#define IDC_MICINPUTSTATIC 1052
+#define IDC_SNDCARDSTATIC2 1052
+#define IDC_MUSSTATIC 1053
+#define IDC_BGMUSSTATIC 1054
+#define IDC_MICSTATIC 1055
+#define IDC_FADESTATIC 1056
+#define IDC_INPUTSETUPSTATIC 1057
+#define IDC_MICFADESTATIC 1057
+#define IDC_PANEL_RECT 1058
+#define IDC_RECONNECT 1060
+#define IDC_TIMEOUT 1061
+#define IDC_PANELRECT_C 1062
+#define IDC_CONTAB 1063
+#define IDC_CHECK_MINTOSYSTRAY 1064
+#define IDC_MINTOSYSTRAY 1065
+#define IDC_ENCSETTINGS_BUTTON 1066
+#define IDC_ENCSETTINGS_LABEL 1067
+#define IDC_ENCSETTINGS_LAME_VER 1068
+#define IDC_USERID 1069
+#define IDC_LOCK_MODE 1070
+#define IDC_REFRESH_DEVICES 1071
+#define IDC_ABOUTLINK 1072
+#define IDC_SEND 1073
+#define IDC_INPUTMODE 1074
+#define IDC_SUMMARY 1075
+#define IDC_DEVBOX 1076
+#define IDC_CURDEVICE 1077
+#define IDC_INFO_FRAME 1079
+#define IDC_INFO_TEXT 1080
+#define IDC_INFO_FRAME2 1081
+#define IDC_LOGGING 1081
+#define IDC_INFO_TEXT2 1082
+#define IDC_CLEAR_LOG 1082
+#define IDC_VIEW_LOG 1083
+#define IDC_INFO_TEXT3 1083
+#define IDC_CLEAR_ON_STARTUP 1084
+#define IDC_INFO_TEXT4 1084
+#define IDC_IGNORE_SENT_MESSAGE 1085
+#define IDC_NEXT_TRACK_LOG 1086
+#define IDC_NEXT_TRACK_EDIT 1087
+#define IDC_NEXT_TRACK_XML 1088
+#define IDC_NEXT_TRACK_BROWSE 1089
+#define IDC_USE_ART 1090
+#define IDC_USE_ART_PLAYING 1091
+#define IDC_USE_ART_STREAM 1092
+#define IDC_ART_BROWSE 1093
+#define IDC_ART_EDIT 1094
+#define IDC_ART_V1_NOTE 1095
+#define IDC_ART_V1_NOTE2 1096
+#define IDC_SPIN1 1097
+#define IDC_ART_V2_NOTE 1097
+#define IDC_SPIN2 1098
+#define IDC_GENRES 1099
+#define IDC_NOTITLES 1100
+#define IDC_AUTOTITLE 1101
+#define IDC_EXTERNALTITLE 1102
+#define IDC_MANUALTITLE 1103
+#define IDC_INPUT_METERS 1104
+#define IDC_STREAM_1 1105
+#define IDC_STREAM_2 1106
+#define IDC_STREAM_3 1107
+#define IDC_STREAM_4 1108
+#define IDC_STREAM_5 1109
+#define IDC_FORUMLINK 1110
+#define IDC_HELPLINK 1111
+#define IDC_GO_HERE 1112
+#define IDC_FORUMLINK2 1112
+#define IDC_GET_UPDATE 1112
+#define IDC_TIMEOUT2 1113
+#define IDC_UPDATELINK 1113
+#define IDC_AUTOCONNECT 1114
+#define IDC_AUTOURL 1115
+#define IDC_PROGRAMNAME 1116
+#define IDC_SERVERURL 1117
+#define IDC_MUSLEV1_TEXT 1118
+#define IDC_MUSLEV2_TEXT 1119
+#define IDC_SAVE_ENCODED_AUDIO 1120
+#define IDC_SAVE_ENCODED_AUDIO_EDIT 1121
+#define IDC_SAVE_ENCODED_AUDIO_BROWSE 1122
+#define IDC_INFO_FRAME3 1123
+#define IDC_INFO_FRAME4 1124
+#define IDC_INFO_FRAME5 1125
+#define IDC_ADDRESS_HEADER 1126
+#define IDC_PASSWORD_HEADER 1127
+#define IDC_NAME_HEADER 1128
+#define IDC_ENCODER_HEADER 1129
+#define IDC_INPUT_WINAMP 1130
+#define IDC_INPUT_SOUNDCARD 1131
+#define IDC_ARTWORK_V1_FRAME 1132
+#define IDC_ARTWORK_V1_FRAME2 1133
+#define IDC_ARTWORK_V2_FRAME 1133
+#define IDC_ARTWORK 1133
+#define IDC_ARTWORK2 1134
+#define IDC_ARTWORK3 1135
+#define IDC_METALIST 1136
+#define IDC_PROTOCOL 1137
+#define IDC_STATIC_UPDATE 1138
+#define IDC_UPDATE_HEADER 1139
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 135
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1140
+#define _APS_NEXT_SYMED_VALUE 110
+#endif
+#endif
diff --git a/Src/Plugins/DSP/dsp_sc/Resource/stop.ico b/Src/Plugins/DSP/dsp_sc/Resource/stop.ico
new file mode 100644
index 00000000..6a8f1b7d
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/Resource/stop.ico
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/api.h b/Src/Plugins/DSP/dsp_sc/api.h
new file mode 100644
index 00000000..d2b78cfc
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/api.h
@@ -0,0 +1,36 @@
+#ifndef NULLSOFT_APIH
+#define NULLSOFT_APIH
+
+#include <api/service/api_service.h>
+extern api_service *serviceManager;
+#define WASABI_API_SVC serviceManager
+
+#include <api/service/waServiceFactory.h>
+
+#include "../Agave/language/api_language.h"
+
+#include "../Agave/queue/api_queue.h"
+
+#include <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgrApi;
+#define WASABI_API_MEMMGR memmgrApi
+
+#include <api/service/svcs/svc_imgload.h>
+#include <api/service/svcs/svc_imgwrite.h>
+
+#include "../Agave/AlbumArt/api_albumart.h"
+
+#include "../Agave/ExplorerFindFile/api_explorerfindfile.h"
+
+// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
+static const GUID playbackConfigGroupGUID =
+{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
+
+#include "..\Agave/Config/api_config.h"
+extern api_config *configApi;
+#define AGAVE_API_CONFIG configApi
+
+#include "..\..\..\Components\wac_network\wac_network_http_receiver_api.h"
+#include "..\..\..\Components\wac_downloadManager\wac_downloadManager_api.h"
+
+#endif \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp
new file mode 100644
index 00000000..776f2c61
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp
@@ -0,0 +1,189 @@
+#include "c_crossfader.h"
+
+C_CROSSFADER::C_CROSSFADER(int length, int nCh, int sRate) : C_DATAPUMP<short>(length * 1 * 1) { // in milliseconds
+ BufferLength = 0;
+ srate = sRate;
+ nch = nCh;
+ crossfade = 0;
+ mode = 0;
+ SetBufferLength(length);
+}
+
+C_CROSSFADER::~C_CROSSFADER() {
+ C_DATAPUMP<short>::~C_DATAPUMP();
+}
+
+// protected interfaces
+
+void C_CROSSFADER::SampleRateConvert(int newsrate) { // in samples per second // this needs work
+ srate = newsrate;
+ SetBufferLength(BufferLength);
+/*
+ if (BufferBottom && BufferTop) {
+ int newsratestep = srate != 0 ? newsrate / srate : 0;
+ int oldsratestep = newsrate != 0 ? srate / newsrate : 0;
+ if (newsratestep && oldsratestep) {
+ int newbuflength = (BufferLength * newsrate) / srate;
+ int newbufsize = (newbuflength * nch * srate * sizeof(short)) / 1000;
+ short *newbuf = (short *)malloc(newbufsize);
+ short *newbufptr = newbuf;
+ short *endbufptr = newbufptr+(newbufsize/sizeof(short));
+ short *oldbufptr = BufferBottom;
+ do {
+ for(int i = 0; i < newsratestep; i++, newbufptr+=nch) {
+ if (newbufptr >= endbufptr) break;
+ *newbufptr = *oldbufptr;
+ if (nch == 2) *(newbufptr+1) = *(oldbufptr+1);
+ }
+ oldbufptr += oldsratestep * nch;
+ } while(newbufptr < endbufptr);
+ free(BufferBottom);
+ BufferLength = newbuflength;
+ BufferBottom = newbuf;
+ BufferTop = endbufptr;
+ BufferStart = BufferEnd = BufferBottom;
+ }
+ }
+*/
+}
+
+void C_CROSSFADER::ChannelConvert(int newnch) { // this needs work
+ nch = newnch;
+ SetBufferLength(BufferLength);
+/*
+ if (BufferBottom && BufferTop) {
+ int newbuflength = (BufferLength * newnch) / nch;
+ int newbufsize = (newbuflength * nch * srate * sizeof(short)) / 1000;
+ short *newbuf = (short *)malloc(newbufsize);
+ short *newbufptr = newbuf;
+ short *endbufptr = newbufptr+(newbufsize/sizeof(short));
+ short *oldbufptr = BufferBottom;
+ for(; newbufptr < endbufptr; newbufptr+=newnch, oldbufptr+=nch) {
+ if (newnch == 1) *newbufptr = (*oldbufptr + *(oldbufptr+1)) >> 1;
+ else *(newbufptr+1) = *newbufptr = *oldbufptr;
+ }
+ free(BufferBottom);
+ BufferLength = newbuflength;
+ BufferBottom = newbuf;
+ BufferTop = endbufptr;
+ BufferStart = BufferEnd = BufferBottom;
+ }
+*/
+}
+
+// human interfaces
+
+void C_CROSSFADER::SetSampleRate(int sRate) { // in samples per second
+ if (sRate != srate) {
+ if (srate && sRate) SampleRateConvert(sRate);
+ }
+ if (sRate) srate = sRate;
+}
+
+void C_CROSSFADER::SetChannels(int nCh) {
+ if (nCh != nch) {
+ if (nch && nCh) ChannelConvert(nCh);
+ }
+ if (nCh) nch = nCh;
+}
+
+void C_CROSSFADER::SetBufferLength(int bufferLength) { // in milliseconds
+ BufferLength = bufferLength;
+ resizeBuffer((BufferLength * srate * nch) / 1000);
+}
+
+void C_CROSSFADER::SetCrossfading(int onoff) {
+ if (crossfade == 0) {
+ crossfade = onoff ? 1 : 0;
+ if (onoff) BufferEnd = BufferStart;
+ }
+}
+
+void C_CROSSFADER::SetCrossfadeMode(int Mode) {
+ mode = Mode;
+}
+
+void C_CROSSFADER::addItems(short *inputBuffer, size_t inputSize) {
+ if (inputBuffer && inputSize) {
+ size_t numsamps = inputSize*nch;
+ if (crossfade==0) {
+ memcpy(BufferEnd,inputBuffer,numsamps*sizeof(short)); // copy our records in
+ BufferEnd += numsamps;
+ } else { // do our crappy crossfade
+ short *smpptr = inputBuffer;
+ size_t bufsamps = (BufferTop-BufferBottom)/nch;
+ size_t bufpos = (BufferEnd >= BufferStart ? BufferEnd-BufferStart : (BufferEnd-BufferBottom)+(BufferTop-BufferStart)) / nch;
+ size_t dist = ((BufferTop - BufferBottom) / nch) - bufpos;
+ for(size_t i = 0; i != numsamps; i++) {
+ if (BufferEnd >= BufferTop) BufferEnd = BufferBottom + (BufferEnd-BufferTop);
+ if (mode == 0) { // X-style (techno mix-style)
+ *BufferEnd = (short)(((((double)*BufferEnd++)*dist) + (((double)*smpptr++)*bufpos)) / bufsamps);
+ if (nch==1 || i&1) { // every-other I or always when mono.
+ bufpos++;
+ dist--;
+ }
+ } else if (mode == 1) { // h-style (rock radio station-style)
+ *BufferEnd = (short)(((((double)*BufferEnd++)*dist) + ((double)*smpptr++)) / bufsamps);
+ if (nch==1 || i&1) dist--; // every-other I or always when mono.
+ }
+ }
+ if (dist < inputSize) crossfade = 0;
+ }
+ if (BufferEnd >= BufferTop) BufferEnd = BufferBottom + (BufferEnd-BufferTop);
+ }
+}
+
+size_t C_CROSSFADER::put(short *inputBuffer, size_t inputSize) { // in channel-less shorts
+ // returns number of <T> records added to logical buffer
+ size_t retval = 0;
+ if (inputBuffer && inputSize) {
+ size_t fitting = (((BufferTop-BufferBottom)-1) - size()) / nch; // can't go over our logical boundary.... blah
+ if (fitting > inputSize) fitting = inputSize; // the entire thing can fit. yeay!
+ retval = fitting;
+ if (fitting > 0) {
+ short *bufptr = inputBuffer;
+ size_t top = (BufferEnd >= BufferStart ? BufferTop-BufferEnd : 0) / nch; // number of <T> records free at top of physical buffer
+ size_t bottom = (BufferEnd >= BufferStart ? BufferStart-BufferBottom : (BufferStart-BufferEnd)) / nch; // number of <T> records free at bottom of physical buffer
+ if (top > 0) {
+ if (top > fitting) top = fitting;
+ addItems(bufptr,top);
+ fitting -= top;
+ bufptr += top*nch;
+ }
+ if (bottom > 0 && fitting > 0) {
+ if (bottom > fitting) bottom = fitting;
+ addItems(bufptr,bottom);
+ }
+ }
+ }
+ return retval;
+}
+
+size_t C_CROSSFADER::get(short *outputBuffer, size_t outputSize, int nCh) { // in channel-less shorts
+ // returns number of <T> records pulled from the logical buffer
+ size_t retval = 0;
+ nch = nCh;
+ if (outputBuffer && outputSize) {
+ size_t fitting = size() / nch;
+ if (fitting > outputSize) fitting = outputSize;
+ retval = fitting;
+ if (fitting > 0) {
+ short *bufptr = outputBuffer;
+ size_t top = (BufferEnd >= BufferStart ? BufferEnd-BufferStart : BufferTop-BufferStart) / nch; // number of <T> records at top of physical buffer
+ size_t bottom = (BufferEnd >= BufferStart ? 0 : BufferEnd-BufferBottom) / nch; // number of <T> records at bottom of physical buffer
+ if (top > 0) {
+ if (top > fitting) top = fitting;
+ getItems(bufptr,top*nch);
+ delItems(0,top*nch);
+ fitting -= top;
+ bufptr += top*nch;
+ }
+ if (bottom > 0 && fitting > 0) {
+ if (bottom > fitting) bottom = fitting;
+ getItems(bufptr,bottom*nch);
+ delItems(0,bottom*nch);
+ }
+ }
+ }
+ return retval;
+} \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h
new file mode 100644
index 00000000..7c044a9a
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h
@@ -0,0 +1,33 @@
+#ifndef __C_CROSSFADER_H__
+#define __C_CROSSFADER_H__
+
+#include "../Include/c_datapump.h"
+
+class C_CROSSFADER : public C_DATAPUMP<short> {
+private:
+protected:
+ int BufferLength; // in milliseconds
+ int srate;
+ int nch;
+ int crossfade;
+ int mode;
+
+ void SampleRateConvert(int newsrate);
+ void ChannelConvert(int newnch);
+
+ virtual void addItems(short *inputBuffer, size_t inputSize); // overriding the addItems() function to do crossfading and channels
+public:
+ C_CROSSFADER(int length, int nCh, int sRate); // length is in milliseconds
+ virtual ~C_CROSSFADER();
+
+ void SetChannels(int nCh);
+ void SetSampleRate(int sRate); // in samples per second
+ void SetBufferLength(int bufferLength); // in milliseconds
+ void SetCrossfading(int onoff);
+ void SetCrossfadeMode(int Mode); // 0 = X-style, 1 = h-style
+
+ virtual size_t put(short *inputBuffer, size_t inputSize); // in channel-less shorts
+ virtual size_t get(short *outputBuffer, size_t outputSize, int nCh); // in channel-less shorts
+};
+
+#endif // !__C_CROSSFADER_H__ \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html
new file mode 100644
index 00000000..36b83184
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html
@@ -0,0 +1,367 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" dir="ltr" lang="en">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Shoutcast Source DSP Changelog</title>
+ <style type="text/css">body,table,textarea,pre{font-family:Arial,Helvetica,sans-serif;font-size:10pt;}
+ .header{background-color:rgb(66,164,189);color:white;padding:1px;}
+ a:link,a:visited{color:#2762AE;text-decoration:none;}
+ a:hover{text-decoration:underline;}
+ ul{list-style-type:none;text-align:left;}
+ ul.n{list-style-type:square;text-align:left;}
+ pre{background-color:#F0F0F0;border:1px solid #CCCCCC;font-weight:bold;padding:5px 10px 5px 10px;display:inline-block;margin-top:0;margin-bottom:0;margin-left:25px;}
+ pre.src{border:0;font-family:monospace;background-color:white;}
+ span.b{font-weight:normal;color:#3366CC;}
+ span.c{font-style:italic;font-weight:normal;color:#808080;}
+ span.v{font-weight:normal;}
+ span.r{font-weight:normal;color:#000066;}
+ span.s{font-weight:normal;color:#FF0000;}
+ #toc{border:1px solid #CCCCCC;padding:0 5px 0 5px;text-align: center;}
+ p.ver{font-size:85%;margin-top:-1.5em;text-align:center;}
+ .thumb{float:right;clear:both;margin-left:10px;margin-top:5px;margin-bottom:10px;width:202px;}
+ .serv{float:right;clear:both;margin-left:10px;margin-top:5px;margin-bottom:10px;width:252px;}
+ .pages{white-space:nowrap;padding-right:25px;}
+ img{width:200px;}
+ img.serv{width:250px;}
+ img.thumb{border:1px solid #CCCCCC;}
+ hr{border:0;background-color:#CCCCCC;height:1px;)</style>
+ <script type="text/javascript">if(window.showTocToggle){var tocShowText="show";var tocHideText="hide";showTocToggle()}function showTocToggle(){if(document.createTextNode){var linkHolder=document.getElementById('toctitle');if(!linkHolder){return}var outerSpan=document.createElement('span');outerSpan.className='toctoggle';var toggleLink=document.createElement('a');toggleLink.id='togglelink';toggleLink.className='internal';toggleLink.href='javascript:toggleToc()';toggleLink.appendChild(document.createTextNode(tocHideText));outerSpan.appendChild(document.createTextNode('['));outerSpan.appendChild(toggleLink);outerSpan.appendChild(document.createTextNode(']'));linkHolder.appendChild(document.createTextNode(' '));linkHolder.appendChild(outerSpan);var cookiePos=document.cookie.indexOf("dlog_hidetoc=");if(cookiePos>-1&&document.cookie.charAt(cookiePos+13)==1){toggleToc()}}}function changeText(el,newText){if(el.innerText){el.innerText=newText}else if(el.firstChild&&el.firstChild.nodeValue){el.firstChild.nodeValue=newText}}function toggleToc(){var toc=document.getElementById('toc').getElementsByTagName('ul')[0];var first=document.getElementById('first');var toggleLink=document.getElementById('togglelink');if(toc&&toggleLink&&toc.style.display=='none'){changeText(toggleLink,tocHideText);document.getElementById('toc').style.cssFloat='left';toc.style.display='block';first.style.marginLeft='150px';document.cookie="dlog_hidetoc=0"}else{changeText(toggleLink,tocShowText);document.getElementById('toc').style.cssFloat='';toc.style.display='none';first.style.marginLeft='';document.cookie="dlog_hidetoc=1"}}window.onload=function(){var cookiePos=document.cookie.indexOf("dlog_hidetoc=");if(cookiePos>-1&&document.cookie.charAt(cookiePos+13)==1){toggleToc()}}</script>
+</head>
+<div class="header">
+<h1 align="center">Shoutcast Source DSP Changelog</h1>
+<p class="ver">(Last Updated 25 August 2022)</p>
+</div>
+<table id="toc" style="float:left;margin-right:40px;margin-bottom:5px;"><tbody><tr><td>
+<div id="toctitle"><b>Contents</b> <span class="toctoggle">[<a id="togglelink" class="internal" href="javascript:toggleToc()">hide</a>]</span></div>
+<ul style="padding-left:0px;">
+<li><a href="#241">2.4.1 Build 444</a></li>
+<li><a href="#235">2.3.5 Build 222</a></li>
+<li><a href="#234">2.3.4 Build 210</a></li>
+<li><a href="#233">2.3.3 Build 201</a></li>
+<li><a href="#232">2.3.2 Build 189</a></li>
+<li><a href="#231">2.3.1 Build 182</a></li>
+<li><a href="#230">2.3.0 Build 177</a></li>
+<li><a href="#223">2.2.3 Build 112</a></li>
+<li><a href="#222">2.2.2 Build 107</a></li>
+<li><a href="#221">2.2.1 Build 99</a></li>
+<li><a href="#220">2.2.0 Build 97</a></li>
+<li><a href="#213">2.1.3 Build 42</a></li>
+<li><a href="#211">2.1.1 Build 36</a></li>
+<li><a href="#210">2.1.0 Build 33</a></li>
+<li><a href="#202">2.0.2 Build 27</a></li>
+<li><a href="#200">2.0.0</a></li>
+</ul>
+</td></tr></tbody></table>
+
+<a name="241"></a><h3>2.4.1 Build 444</h3>
+<hr>
+<ul class="n" id="first" style="margin-left:150px;">
+<li>Built with VS2019 using Winamp 5.9-specific internal code
+<li>Will only install on Winamp 5.9.0 or newer
+<li>Required VS2019 runtimes installed on Win7-8.1 (minimum OS = Win7)
+<li>Updated: Lame encoder v3.100.1
+</ul>
+<p>&nbsp;</p>
+
+<a name="235"></a><h3>2.3.5 Build 222</h3>
+<hr>
+<ul class="n" id="first" style="margin-left:150px;">
+<li>Changed to use Winamp's networking library (jnetlib) instead of the older forked version being used (this will be helpful in the future)</li>
+<li>Changed to prompt if Winamp is set to use 24-bit playback mode (which we do not support in this plug-in)</li>
+<li>Fixed some setups not being able to connect to a 2.x DNAS in 2.x mode due to the recent 'automatic mode' support and slow DNAS handshaking</li>
+<li>Fixed the next song title not being recognised by the DNAS (expects the XML with a specific case of the element names e.g. soon, not SOON)</li>
+<li>Fixed the automatic reconnect time being reset to 1 second between sessions</li>
+<li>Reduced the memory usage of connections to only allocate the memory needed (this typically only saves a few KB but it's still a saving!)</li>
+<li>Miscellaneous code tidyups, documentation updates, branding changes and other small related changes</li>
+</ul>
+<p><br></p>
+
+<a name="234"></a><h3>2.3.4 Build 210</h3>
+<hr>
+<ul class="n">
+<li>The first Radionomy provided Shoutcast Source DSP release after the sale of Shoutcast (and Winamp) in January 2014</li>
+<li>This is primarily a maintenance release to resolve issues and broadcaster requests with the 2.3.x Source DSP since the last build provided under AOL ownership<br></li>
+<li>Added 'automatic mode' for selecting the Shoutcast protocol to use which should ease setup issues (there is still the ability to explicitly set the protocol mode like before)</li>
+<li>Changed minimum required version of Winamp to v5.6+ due to building changes (below) and to simplify version compatibility and testing</li>
+<li>Changed building of the plug-in to better match with the Winamp style for dependent dlls (this saves ~132KB)
+<li>Changed title update handling in respect to issues related to CVE-2014-4166 (which we were not informed about before it was disclosed!)</li>
+<li>Changed to allow the plug-in to load if lame_enc.dll cannot be found (related to the change below) - previously it would not load at all</li>
+<li>Updated genres to the current supported list of genres (as detailed in http://forums.shoutcast.com/showthread.php?t=303241)</li>
+<li>Removed lame_enc.dll from the installer - if not present in your Winamp install you will need to manually obtain a copy and place in the Shared folder of your Winamp install</li>
+<li>Miscellaneous code tidyups, optimisations, adjustments for future Winamp releases, branding resource changes and other related changes</li>
+</ul>
+<p><br></p>
+
+<a name="233"></a><h3>2.3.3 Build 201</h3>
+<hr>
+<ul class="n">
+<li>Added support for multiple instances of the plug-in so as not to mangle the settings (ini name is now based on the plug-in dll name)</li>
+<li>Added blocking of station names with only punctuation in them from being allowed to connect (matches YP-side) and updated illegal list</li>
+<li>Added displaying of the metadata and artwork currently present in Winamp when that is the selected mode on the input tab</li>
+<li>Added reporting of the 'type' of the artwork on the Winamp metadata panel (generally requires Winamp 5.64+ to work correctly)</li>
+<li>Added double-click to view file in explorer on the Winamp metadata panel</li>
+<li>Added Winamp v5.64+ safe mode support</li>
+<li>Changed how loading of the dialog is handled to try to improve it to appear on top of Winamp if set to load on startup</li>
+<li>Changed selecting appropriate input fields to select all text in the field (makes it quicker to enter new port values, etc)</li>
+<li>Changed handling of the UI tabs to reduced memory usage where possible</li>
+<li>Changed default playing artwork to use jpeg when not able to get the raw artwork on older (<5.6) Winamp clients</li>
+<li>Changed main input dialog to hide the soundcard options when in Winamp mode instead of disabling and changing the text</li>
+<li>Changed the 'quit' code to better ensure everything applicable is left in a clean state (helps to improve re-opening the window without a complete unload of the plug-in</li>
+<li>Fixed loading of the plug-in to not crash / lockup Winamp's UI when lame_enc.dll cannot be found</li>
+<li>Fixed Winamp / Soundcard options on the summary tab not being checked on loading</li>
+<li>Fixed some of the error indicator drawing appearing incorrectly after closing the window and re-opening (without a complete unload of the plug-in)</li>
+<li>Fixed closing the window not working properly next time / after a few repeat closes due to memory corruption issues and not correctly stopping the broadcast threads in all scenarios</li>
+<li>Miscellaneous code tidyups, size optimisations (~40kb), crash fixes, improved memory handling and other related changes to improve useability of the plug-in</li>
+</ul>
+<p><br></p>
+
+<a name="232"></a><h3>2.3.2 Build 189</h3>
+<hr>
+<ul class="n">
+<li>Fixed all reproduceable issues when switching between Winamp and Soundcard mode as well as switching between the different soundcard input modes (should fix all reported crashes when switching between these modes)</li>
+<li>Fixed soundcard input not being initialised correctly in all of the previous v2.3.x releases</li>
+<li>Fixed the 'connect' button getting disabled when switching from the 'summary' to the 'output' tab</li>
+<li>Fixed crash when playlist was cleared and Winamp was in a specific playback state leading to information could not be properly handled</li>
+<li>Updated list of station names not allows for being listed in the Directory</li>
+<li>Updated albumart support to get the raw playing artwork instead of the decoded artwork and having to re-encode to png when using Winamp v5.6+ (still happens on pre-5.6 or if there is an external artwork file due to the Winamp artwork api)</li>
+</ul>
+<p><br></p>
+
+<a name="231"></a><h3>2.3.1 Build 182</h3>
+<hr>
+<ul class="n">
+<li>Added logging of the metadata and artwork details obtained from Winamp before sent to the server (if logging it enabled)</li>
+<li>Fixed some connection stability issues when connecting to a remote server (typically happens when in-stream artwork is enabled)</li>
+<li>Fixed large metadata updates (typically in-stream artwork but also could affect title updates) not sending all frames to the server</li>
+<li>Fixed some rare lockups when sending metadata frames to the server</li>
+<li>Fixed the 'kill' action not working or responding as expected in certain scenarios</li>
+<li>Changed the 'Directory' tab to enable Name, Url and Genre options when using v2 mode and the stream is set to be public</li>
+<li>Changed toggling of the in-stream artwork options to refresh the cached artwork copy when re-enabled</li>
+<li>Changed log messages to filter out excessive "Cipher Response Received" messages and changed to show "Unable To Connect To The Server. Try enabling 'Shoutcast v1 mode'." if stuck at that state</li>
+<li>Removed the "Ignore 'Sent X bytes' status messages" option from the logging tab (should have been removed in v2.3.0)</li>
+</ul>
+<p><br></p>
+
+<a name="230"></a><h3>2.3.0 Build 177</h3>
+<hr>
+<ul class="n">
+<li>Added a number of stream configuration details onto the summary view to see what each stream is configured for without having to go to the 'output' tab</li>
+<li>Added clickable buttons on the summary listview to allow for quick control of the streams</li>
+<li>Added toggling of the stream playing state in the summary listview view via the space key</li>
+<li>Added support for saving the encoded stream output to a specified file to allow DJ's to keep a copy of their output</li>
+<li>Added options for toggling between Winamp and Soundcard mode on the summary page</li>
+<li>Added tooltips to the summary listview so any clipped text can be seen</li>
+<li>Added peak level indication since the DSP was started for left and right</li>
+<li>Added visual info on the artwork page if artwork will be sent or not</li>
+<li>Added better checking of entered values to ensure only what is supported can be entered e.g. port range limit from 1-65535</li>
+<li>Added better handling of NAK errors from the DNAS in v1 and v2 mode as well as required updates from v2 protocol changes e.g. for 'Bit Rate Error' and 'Stream Moved' responses</li>
+<li>Added 'red' tab text to indicate the tab which has missing or invalid information which prevents a connection from starting</li>
+<li>Added some details of the connection in the logs to make it easier to see the details</li>
+<li>Added handling to remember custom titles between Winamp instances</li>
+<li>Changed all of the known genres to support all changes made to the official genre list at the time of release including the adding of new genres (Decades -> 00s, Folk -> Old Time, International -> German) and changing some (Jewish to Hebrew or removing any dashes)</li>
+<li>Changed some of the output sub-tab names to make things more consistent</li>
+<li>Changed 'user id' to be able to accept the DJ name when used in v1 mode so it will automatically convert it to the 'name:password' format as used with v1 Transcoder DJ connections</li>
+<li>Changed the title options to be on their own page with some layout changes</li>
+<li>Changed a failed connection to now wait up to a second before trying again to prevent hammering the server</li>
+<li>Changed '[xx:xx:xx] Sent xxx bytes' to now scale from bytes to KiB to MiB to GiB</li>
+<li>Changed in-stream metadata (titles and artwork) to be included in the v2 stream bytes sent total shown</li>
+<li>Changed paused / stopped silence filling to keep the output bitrate the same as playing now without the prior hacks</li>
+<li>Changed the 'online documentation' link to open a local copy if available</li>
+<li>Changed the GUID for the plug-in's language file to now be {88380E65-4068-49BA-8EA4-3F2AF12D0A4F} due to the large number of resource changes from the previous releases</li>
+<li>Changed user-agent for v1 title updates to match with the v2 metadata's TENC field version (is now "Shoutcast Source DSP x.x.xx Title Update (Mozilla)")</li>
+<li>Changed to use Lame 3.99.5 (lame_enc.dll) or the most current version shipped with Winamp (makes it easier to update without a custom built lamedll.dll as previously used)</li>
+<li>Changed the 'connect' button to show 'set password' or 'change name' or 'set encoder' or 'set server' when disabled to make it clearer why it's not enabled e.g. if 'unnamed server' or nothing is set for the station name or password fields</li>
+<li>Changed default page to be the output page instead of summary on new installs</li>
+<li>Changed encoder default to be AAC+ if possible (so it's one less thing to do on loading) and fixed MP3 to default to 96kbps stereo on clean installs</li>
+<li>Changed how the dialog is loaded to resolve a part close / crash seen in a few rare cases</li>
+<li>Changed the waveInReset(..) change from 2.1.3 back to the pre-2.1.3 behaviour to see if it resolves some of the crash issues reported since the change</li>
+<li>Fixed a small audio loss / silence injection when a stream starts or when a title update happens</li>
+<li>Fixed the title cache update to only send an update if there is an actual change (filters out quirks with streaming from another stream)</li>
+<li>Fixed 'invalid password' scenarios not being correctly reported</li>
+<li>Fixed stream artwork not being correctly updated after being set to an invalid / empty file</li>
+<li>Fixed unusually large cipherkeys causing a crash when attempting to connect to the server</li>
+<li>Fixed v2 mode doubling up the sent bytes total in some specific scenarios</li>
+<li>Fixed memory leak when processing the playing album art due to not always removing the original image</li>
+<li>Fixed a disconnect-connect or re-connect scenario incorrectly trying to re-send the stream artwork when not present / not enabled</li>
+<li>Fixed metadata packet creation some times going over the 16384 byte limit (16371 byte payload limit)</li>
+<li>Fixed artwork cleared updates being sent when not applicable</li>
+<li>Fixed playing state not being correctly detected if Winamp was already playing when the DSP is loaded</li>
+<li>Fixed issue causing sparodic injection of invalid data into the output buffer for encoding</li>
+<li>Fixed artwork not being correctly sent after a disconnect in some scenarios</li>
+<li>Fixed random crash when updating the next playing song information</li>
+<li>Fixed excessive updating of the controls on the output page</li>
+<li>Fixed MP3 encoder not showing all encoding options when in Winamp mode under some incorrectly inherited settings</li>
+<li>Fixed v1 metadata updates potentially causing a one handle leak for each title update</li>
+<li>Fixed manual titles not being sent in all cases</li>
+<li>Fixed next titles being sent even if option is unchecked</li>
+<li>Removed dsp_sc_enc.ini usage with all temporary encoder settings now stored in dsp_sc.ini</li>
+<li>Removed default values for userid and password to force a valid value to be entered</li>
+<li>Miscellaneous code tidyups, optimisations, removal of unwanted code, resource changes and other related changes to improve useability of the plug-in</li>
+</ul>
+<p><br></p>
+
+<a name="223"></a><h3>2.2.3 Build 112 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Fixed title updates to remove characters the v2 DNAS will abort a connection on</li>
+<li>Fixed DSP not starting connections if Winamp is starting minimised</li>
+<li>Fixed the AAC encoder not being re-loaded if closing the dialog and re-opening without re-loading the DSP</li>
+<li>Fixed some rare issues preventing the dialog from loading correctly</li>
+</ul>
+<p><br></p>
+
+<a name="222"></a><h3>2.2.2 Build 107 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Added support for Winamp 5.62's new AAC encoder dll (Winamp now uses Fraunhofer's AAC library instead of Coding Technologies)</li>
+<li>Changed the genre to be chosen from a menu (in supported situations) so only allow supported values</li>
+<li>Changed MP3 default settings to be 96 kbps Stereo (meant to have been this for a while but wasn't working)</li>
+<li>Changed default genre to be 'Misc' on clean installs or on loading and not matching the supported genre list</li>
+<li>Changed the version string so it's more like the v1 tools (and pending DNAS / Transcoder updates)</li>
+<li>Changed 'Description' to 'Name' on the Yellow Pages tab</li>
+<li>Fixed the vu input meters to not show a level if there is currently no audio input instead of keeping the last value</li>
+<li>Fixed issue with loading of the config dialog not showing the tabs correctly in some situations</li>
+<li>Fixed sending a manual title update in v2 mode also incorrectly sending inappropriate cached title data</li>
+<li>Miscellaneous code tidyups, optimisations, removal of unwanted code</li>
+</ul>
+<p><br></p>
+
+<a name="221"></a><h3>2.2.1 Build 99 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Fixed crash on some machines when the playlist editor is empty</li>
+<li>Fixed some minor localisation issues with some of the error messages</li>
+<li>Fixed the installer not setting the DSP as the default DSP for some non-standard installs</li>
+<li>Changed message when loading in an invalid configuration to mention DSP stackers</li>
+</ul>
+<p><br></p>
+
+<a name="220"></a><h3>2.2.0 Build 97 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Added new 'Artwork' tab which allows for configuration of how and what artwork will be sent for Shoutcast 2 streams to a compatible Shoutcast 2 DNAS)</li>
+<li>Added support of the IPC_GETNEXTLISTPOS api in Winamp 5.61+ to better determine the next song to be played even if shuffle is enabled</li>
+<li>Added explict blocking of trying to load the plug-in not in Winamp to resolve loading issues and crashes due to lacking api support required</li>
+<li>Added to the logs tab the option to log the next tracks to be played from the DSP in plain txt or in xml format</li>
+<li>Added sending of icypub data as per Shoutcast 2 protocol specifications (only needed for the Shoutcast 2 DNAS)</li>
+<li>Added 'lookahead' ini only option for determining how many next tracks from the playback queue (if available) to report (default is 3)</li>
+<li>Changed all of the Shoutcast 2 packet generation to fix a number of issues like large / invalid packets, being unable to connect, unstable connections</li>
+<li>Changed all of the title gathering to no long poll Winamp but instead query it directly (reduces cpu usage and inproves reliability of metadata gathering)</li>
+<li>Changed all of the plug-in UI to use unicode where possible to improve localisation support</li>
+<li>Changed some of the UI elements to make certain information or errors more obvious (like the 'Cipher Response' message when using the wrong Shoutcast mode)</li>
+<li>Changed the 'Logging' tab to 'Logs' due to the wider range of options it now provides</li>
+<li>Changed next track logging to be a per-configuration feature instead of being applied globally (as in the previous DSP release)</li>
+<li>Changed to send the full title in the metadata <extension> block for the first (current) title so it follows the Shoutcast 2 specs</li>
+<li>Changed the 'Send Update' button to not be enabled unless there is a title to send as well as disabling the next title option as applicable</li>
+<li>Changed YellowPages tab to disable options not applicable to Shoutcast 2 mode and when running as a public server (where the details relating to 'streamauthhash' for the DNAS are used instead)</li>
+<li>Changed to send a default stream id if one is not specified in Shoutcast 2 mode to improve DJ connection issues (which can fail if not specified)</li>
+<li>Fixed some metadata conversions leading to crashes</li>
+<li>Fixed internal utf8 conversions to prevent malformed Shoutcast 2 metadata being generated which would cause the Shoutcast 2 DNAS to block the connection</li>
+<li>Fixed some of the entered stream configuration options to not accept invalid input and revert to safe defaults as applicable if this happens</li>
+<li>Fixed some issues with logging initialisation leading to random lockups in some rare cases</li>
+<li>Fixed memory corruption using Shoutcast 2 mode preventing 'Connection 1' being used in rare cases (mainly affected Windows 2000 / XP systems)</li>
+<li>Fixed metadata not being sent if the connection to the DNAS is lost and a connection then comes back or is manually started</li>
+<li>Fixed clean up of resources if unloading whilst Winamp is still running to prevent a potential crash on close or UI corruption when the plug-in is loaded again</li>
+<li>Fixed when Winamp is not playing or is paused outputting blank stream data at a higher rate compared to playing leading to higher bandwidth usage then should be happening</li>
+<li>Fixed the Summary page listview flickering on update</li>
+<li>Fixed rare crash when Winamp is not playing and certain playlist configurations are in use when trying to find the next track title</li>
+<li>Fixed to not reset the music levels if not using the soundcard input on closing</li>
+<li>Fixed to not reset the Winamp level if not using the soundcard input on startup but will instead apply it on changing to soundcard input</li>
+<li>Fixed playback queue lookup issues on older 5.5x clients when api_queue is not present or not correctly loaded when queried</li>
+<li>Fixed the 'Send Update' option to not send cached information from Winamp's title and to not crash in rare situations</li>
+<li>Fixed rare lockup issue when using the soundcard input due to the input device taking longer to reset than expected</li>
+<li>Fixed refresh capture device not setting to a valid selection if the number of devices changed</li>
+<li>Updated help link for the plug-in to go to the new page at <a target="_blank" href="http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in">http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in</a></li>
+<li>Updated installer to allow the plug-in to be set as the default DSP as well as run Winamp after completion (with the checked states remembered for next time)</li>
+<li>Miscellaneous code tidyups, optimisations, removal of unwanted code and other build related changes to make this more portable at a later date</li>
+</ul>
+<p><br></p>
+
+<a name="213"></a><h3>2.1.3 Build 42 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Added passing of metadata from the playing track (if known) to the server so it acts like sc_trans from a client connecting to the stream</li>
+<li>Added an option to not log 'Status X bytes' messages (enabled by default) and improved log file handling</li>
+<li>Added a refresh capture device button to help update the plug-in if connected capture devices have changed</li>
+<li>Changed status info duration to be the time connected rather than a relative date time and allows for more than 24hrs to be displayed e.g. 26:48:57 instead of looping back to 03:48:57</li>
+<li>Changed logging to filter 'Status X bytes' messages to only 1 second (if the option to include them is enabled)</li>
+<li>Changed log files to use CR+LF linebreaks instead of just LF</li>
+<li>Changed logging to remove newlines so each message is a single line to match the status info</li>
+<li>Fixed crash on Vista (and potentially Windows 7) where no capture devices are being present resulting in no default capture device known</li>
+<li>Fixed crash in SC2 mode when a different cipher is set in the plug-in to the server as well as indicating this error in the status info</li>
+<li>Fixed button images in the 'Soundcard Mixer Control' section not appearing on all OSes</li>
+</ul>
+<p><br></p>
+
+<a name="211"></a><h3>2.1.1 Build 36 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Added new 'Logging' tab on the Output tab to log the connection status messages</li>
+<li>Added a mini dropdown next to the 'Lock' button for 'Push to Talk' to allow the mode to be automatically enabled on startup</li>
+<li>Fixed plug-in to not crash when the network connection is lost</li>
+<li>Fixed random plug-in crashes whilst the plug-in is streaming (mainly in SC2 mode)</li>
+<li>Fixed internal plug-in uninstall not always working</li>
+<li>Fixed SC2 title updates to properly work as UTF-8 and to not strip out characters incorrectly</li>
+<li>Fixed next track detection to only be reported if shuffle mode is off and not to act in an undefined manner when on the last playlist item (wraps around to the start of the playlist as needed)</li>
+<li>Fixed title updates to cope with the same title being played but the next song title being different</li>
+<li>Changed SC2 metadata to not output &lt;soon&gt; and &lt;title seq="2"&gt; tags in the xml metadata if they are not known (when shuffle mode is enabled)</li>
+<li>Changed the &lt;TENC/&gt; tag in the xml metadata to include the plug-in version</li>
+</ul>
+<p><br></p>
+
+<a name="210"></a><h3>2.1.0 Build 33 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Added a separate capture device fader timeout option</li>
+<li>Added copies of the plug-in documentation as an installer option</li>
+<li>Added help and documentation links to the 'About' tab</li>
+<li>Changed on Vista / Windows 7 to only show actually connected capture devices (requires a restart of the plug-in if connecting a new device whilst the plug-in is active)</li>
+<li>Changed the 'Open Mixer' button to open to the recording devices dialog on Vista / Windows 7</li>
+<li>Changed wording of the legacy mode checkbox to be clearer (hopefully) and added an info panel below to deal with the 'Cipher response message'</li>
+<li>Changed capture device level to not alter the device's level unless Push to Talk is active</li>
+<li>Changed the resolution on the faders from 500ms to 100ms (will re-map old settings)</li>
+<li>Changed opening of help links in the plug-in to follow Winamp's style of handling</li>
+<li>Fixed major issue in the plug-in leading to breaking of Winamp (and 3rd party plug-in's) COM usage</li>
+<li>Fixed running of the plug-in not starting auto-connect connections when 'Input' or 'About' were the opened tab</li>
+<li>Fixed capture device level not being correctly handled leading to spiking in on transitions (affected at least Windows 2000 / XP where it is all known to work)</li>
+<li>Fixed capture devices source selection not being remembered</li>
+<li>Fixed capture device and source levels not being set back to the non-Push to Talk level if Push to Talk is active when the plug-in is closed</li>
+<li>Fixed a few localisation issues with missing items on Windows 2000 / XP</li>
+<li>Fixed capture deviceRemoved tooltip from the microphone slider on the line-in page</li>
+<li>Fixed some issues with the installer and uninstaller</li>
+<li>Miscellaneous code changes to make some things easier to manage</li>
+</ul>
+<p><br></p>
+
+<a name="202"></a><h3>2.0.2 Build 27 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Fixed Shoutcast 1 connection errors to a remote connection</li>
+<li>Fixed authorisation error checking for Ultravox 2 & 2.1</li>
+<li>More changes to the output manager to avoid out of sync states</li>
+<li>Fixed timing issue which caused out of sequence Ultravox audio data frames in some scenarios</li>
+<li>Fixed some localisation and tabbing order issues on the config pages</li>
+<li>Removed unwanted encoder option on the Output -> Connection tab</li>
+<li>Added a Shoutcast 1 mode only information prompt on how to enter the password for DJ connections</li>
+</ul>
+<p><br></p>
+
+<a name="200"></a><h3>2.0.0 <a href="#" style="font-size:55%;">[top]</a></h3>
+<hr>
+<ul class="n">
+<li>Added Shoutcast 2 (Ultravox 2.1) support for the generated stream data</li>
+<li>Cleanup and general fixes to the streaming support in the plug-in</li>
+<li>Fixed settings not being saved on Vista / Windows 7</li>
+<li>Fixed a number of lock-ups in the plug-in (should be more stable now)</li>
+<li>Fixed plug-in to not stall if Winamp is not playing</li>
+<li>Fixed a number of UI issues (tabs not showing in all cases, controls not in the correct tabbing order, theming issues, notification icon handling)</li>
+<li>Config window now remembers its last position between use</li>
+<li>Improved Lame encoder quality</li>
+<li>Attempted to resolve standard AAC (LC-AAC) not working (additionally this is reported as audio/aacp so it will work with the YP)</li>
+<li>Uses the current enc_aacplus.dll (AAC / AAC+ encoder) from the Winamp install used instead of bundling an old version from Winamp 5.1)</li>
+<li>Fixed Shoutcast 1 issue with titles containing "[" & "]"</li>
+<li>Changes made to improve selection of the 'microphone' device allowing for more control over the capture device used</li>
+<li>Added localisation support to the plug-in (including supporting localised encoder plug-ins when showing their configurations)</li>
+<li>Some other minor changes including those from the <a target="_blank" href="http://forums.shoutcast.com/showthread.php?t=322874">1.9.2 beta</a></li>
+</ul>
+<p><br></p> \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html
new file mode 100644
index 00000000..bb743171
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html
@@ -0,0 +1,326 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" dir="ltr" lang="en"><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Shoutcast Source DSP Plug-in v2.4.1</title>
+ <style type="text/css">body,table,textarea,pre{font-family:Arial,Helvetica,sans-serif;font-size:10pt;}
+ .header{background-color:rgb(66,164,189);color:white;padding:1px;}
+ a:link,a:visited{color:#2762AE;text-decoration:none;}
+ a:hover{text-decoration:underline;}
+ ul{list-style-type:none;text-align:left;}
+ pre{background-color:#F0F0F0;border:1px solid #CCCCCC;font-weight:bold;padding:5px 10px 5px 10px;display:inline-block;margin-top:0;margin-bottom:0;margin-left:25px;}
+ pre.src{border:0;font-family:monospace;background-color:white;}
+ span.c{font-style:italic;font-weight:normal;color:#808080;}
+ span.v{font-weight:normal;}
+ span.r{font-weight:normal;color:#000066;}
+ span.s{font-weight:normal;color:#FF0000;}
+ #toc{border:1px solid #CCCCCC;padding:0 5px 0 5px;text-align: center;}
+ p.ver{font-size:85%;margin-top:-1.5em;text-align:center;}
+ .thumb,.thumb_about,.thumb_config{float:right;margin-left:10px;margin-top:5px;margin-bottom:10px;}
+ .thumb{width:132px;}
+ .thumb_about{width:242px;}
+ .thumb_config{width:402px;}
+ img{border:0;height:203px;width:130px;}
+ img.about{height:185px;width:240px;}
+ img.config{height:312px;width:400px;}
+ hr{border:0;background-color:#CCCCCC;height:1px;)</style>
+ <script type="text/javascript">if(window.showTocToggle){var tocShowText="show";var tocHideText="hide";showTocToggle()}function showTocToggle(){if(document.createTextNode){var linkHolder=document.getElementById('toctitle');if(!linkHolder){return}var outerSpan=document.createElement('span');outerSpan.className='toctoggle';var toggleLink=document.createElement('a');toggleLink.id='togglelink';toggleLink.className='internal';toggleLink.href='javascript:toggleToc()';toggleLink.appendChild(document.createTextNode(tocHideText));outerSpan.appendChild(document.createTextNode('['));outerSpan.appendChild(toggleLink);outerSpan.appendChild(document.createTextNode(']'));linkHolder.appendChild(document.createTextNode(' '));linkHolder.appendChild(outerSpan);var cookiePos=document.cookie.indexOf("dsp_hidetoc=");if(cookiePos>-1&&document.cookie.charAt(cookiePos+12)==1){toggleToc()}}}function changeText(el,newText){if(el.innerText){el.innerText=newText}else if(el.firstChild&&el.firstChild.nodeValue){el.firstChild.nodeValue=newText}}function toggleToc(){var toc=document.getElementById('toc').getElementsByTagName('ul')[0];var toggleLink=document.getElementById('togglelink');if(toc&&toggleLink&&toc.style.display=='none'){changeText(toggleLink,tocHideText);document.getElementById('toc').style.cssFloat='left';toc.style.display='block';document.cookie="dsp_hidetoc=0"}else{changeText(toggleLink,tocShowText);document.getElementById('toc').style.cssFloat='';toc.style.display='none';document.cookie="dsp_hidetoc=1"}}window.onload=function(){var cookiePos=document.cookie.indexOf("dsp_hidetoc=");if(cookiePos>-1&&document.cookie.charAt(cookiePos+12)==1){toggleToc()}}</script>
+</head>
+<div class="header">
+<h1 align="center">Shoutcast Source DSP Plug-in 2.4.1</h1>
+<p class="ver">(Last Updated 24 August 2022)</p>
+</div>
+<table id="toc" style="float:left;margin-right:40px;margin-bottom:5px;"><tbody><tr><td>
+<div id="toctitle"><b>Contents</b> <span class="toctoggle">[<a id="togglelink" class="internal" href="javascript:toggleToc()">hide</a>]</span></div>
+<ul style="padding-left:0px;">
+<li><a href="#Introduction_to_the_Source_DSP">1 Introduction to the Source DSP</a></li>
+<li><a href="#Getting_Started">2 Getting Started</a>
+<ul>
+<li><a href="#Installing_the_Plug-in">2.1 Installing the Plug-in</a></li>
+</ul>
+</li>
+<li><a href="#Configuration_Window">3 Configuration Window</a>
+<ul>
+<li><a href="#Summary_Tab">3.1 Summary Tab</a></li>
+<li><a href="#Output_Tab">3.2 Output Tab</a>
+<ul>
+<li><a href="#Login_Tab">3.2.1 Login Tab</a></li>
+<li><a href="#Directory_Tab">3.2.2 Directory Tab</a></li>
+<li><a href="#Encoder_Tab">3.2.3 Encoder Tab</a></li>
+<ul>
+<li><a href="#Save_Encoded_Output">3.2.3.1 Save Encoded Output</a></li>
+</ul>
+<li><a href="#Titles_Tab">3.2.4 Titles Tab</a></li>
+<li><a href="#Artwork_Tab">3.2.5 Artwork Tab</a></li>
+<li><a href="#Logs_Tab">3.2.6 Logs Tab</a></li>
+</ul>
+</li>
+<li><a href="#Input_Tab">3.3 Input Tab</a>
+<ul>
+<li><a href="#Input_Configuration">3.3.1 Input Configuration</a></li>
+<li><a href="#Soundcard_Mixer_Control">3.3.2 Soundcard Mixer Control</a></li>
+</ul>
+</li>
+<li><a href="#About_Tab">3.4 About | Support | Updates Tab</a>
+<ul>
+<li><a href="#Documentation_and_Support">3.4.1 Documentation and Support</a></li>
+</ul>
+</li>
+</ul>
+</li>
+<li><a href="#Known_Issues">4 <span class="toctext">Known Issues</a>
+<ul>
+<li><a href="#Soundcard_Mixer_Control_2">4.1 <span class="toctext">Soundcard Mixer Control</a></li>
+</ul>
+</li>
+<li><a href="#Shoutcast_2_Cipher_Key">5 Shoutcast 2 Cipher Key</a></li>
+<li><a href="#Example_Configurations">6 Example Configurations</a></li>
+</ul>
+</td></tr></tbody></table>
+
+<a name="Introduction_to_the_Source_DSP"></a><h2>1.&nbsp;Introduction to the Source DSP</h2>
+<hr>
+<p>The aim of this document is to show you the different features offered by the Source DSP plug-in. Version 2 of the plug-in is designed to work only on <b>Winamp 5.9</b> and higher due to better api usage and integration with the Winamp player. If you want to use the Source DSP in an alternative player, then it would need to support all of the required Winamp api and services which the plug-in makes use off.</p>
+<p>The key feature of the plug-in is the ability to use Winamp as a source to a DNAS server or a Transcoder / AutoDJ instance or any compatible tool which accepts Shoutcast streams.</p>
+<p>Additionally the plug-in will allow you to capture an audio input from the soundcard and its line-in or microphone inputs (<a href="#Soundcard_Mixer_Control" title="">see section 3.3.2</a>) subject to OS and the audio system.</p>
+<p><br></p>
+
+<a name="Getting_Started"></a><h2>2.&nbsp;Getting Started <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>To start using the Source DSP you need a configured and running DNAS server (sc_serv) or an alternative server to connect to and to have all of the login details required to connect as a source. The plug-in can be used as either a full full source or it can be used as a DJ connection in the case of being used with a compatible Transcoder / AutoDJ instance.</p>
+<p><br></p>
+
+<a name="Installing_the_Plug-in"></a><h2>2.1.&nbsp;Installing the Plug-in <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb_about"><a href="res/Select_Source_DSP_in_Winamp.png" title="Select Source DSP in Winamp's Preferences"><img class="about" alt="Select Source DSP in Winamp's Preferences" src="res/Select_Source_DSP_in_Winamp.png"></a></div>
+<p>The installer will detect the Winamp install on your machine and will then install it to the correct location. If the detected Winamp version is prior to v5.6 or if there is no winamp.exe present in the folder chosen then the installer will abort the installation.</p>
+<p>Once installed, if you have not chosen to make the Source DSP the default DSP plug-in, you will need to open Winamp and go to the following location:</p>
+<pre>Preferences -&gt; Plug-ins -&gt; DSP/Effect</pre>
+<p>follwed by selecting the 'Nullsoft Shoutcast Source DSP' entry shown in the plug-in list.</p>
+<p><br></p>
+
+<a name="Configuration_Window"></a><h2>3.&nbsp;Configuration Window <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>The configuration window is the main interface of the plug-in and is where login details for the connection to the server can be changed or the current status viewed.</p>
+<pre>When the configuration window is closed then any active connections will be closed.
+If you want to hide the window then you can click use the minimise button on the
+window and click on the notification area icon added when the minimise happened.</pre>
+<p><br></p>
+
+<a name="Summary_Tab"></a><h2>3.1.&nbsp;Summary Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Summary_tab.png" title="Shoutcast Source Summary Tab"><img alt="Shoutcast Source Summary Tab" src="res/Summary_tab.png"></a></div>
+<p><b>Status / Info</b>&nbsp;: This will show information about the status of the 5 possible outputs the plug-in is able to provide going from not connected to current duration of the connection.</p>
+<pre>If you double-click one of the output items you will be taken to the 'Output Tab'
+(<a href="#Output_Tab" title="">see section 3.2</a>) where it will show the current settings for the output selected.</pre>
+<p><b>Active Input Device</b>&nbsp;: This allows you to toggle between using Winamp and the configured soundcard input as well seeing the current audio capture mode. For more configuration options go to the 'Input Tab' (<a href="#Input_Tab" title="">see section 3.3</a>).</p>
+<p><b>Input Levels</b>&nbsp;: These show the current and peak audio level of the left and right channels as is being passed through the plug-ins core. This can aid in seeing if the input source is possibly not working or to check the audio is clipped.</p>
+<p><br></p>
+
+<a name="Output_Tab"></a><h2>3.2.&nbsp;Output Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>This tab allows you to configure the 5 separate outputs the plug-in is able to generate where the settings for the output are selected by clicking the required item in the list.</p>
+<div class="thumb"><a href="res/Output_tag_configuration_error.png" title="Shoutcast Source Output Tab showing password configuration error"><img alt="Shoutcast Source Output Tab showing password configuration error" src="res/Output_tag_configuration_error.png"></a></div>
+<p><b>Status</b>&nbsp;: This will show the current information about the output source ranging from not being connected to error messages due to invalid passwords to running correctly.</p>
+<p><b>Auto Connect</b>&nbsp;: This will make the plug-in attempt to run this output as soon as it is started or when the option is checked if not already running when checked.</p>
+<p><b>Connect / Abort / Disconnect / Kill Button</b>&nbsp;: This allows you to start a connection, abort a connection try or kill / disconnect an active connection. If 'Auto Connect' is checked and you click this for a disconnect action then the plug-in automatically re-starts the connection.</p>
+<pre>If there is an issue the 'Connect / Abort / Disconnect / Kill Button' will show
+the configuration setting which is invalid e.g. 'Set Password' if the encoder
+has not been specified. The tab and the title above where the value is not
+set will have it's text changed to red to make it easier to identify.</pre>
+<p><br></p>
+
+<a name="Login_Tab"></a><h2>3.2.1.&nbsp;Login Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Output_tab_login_tab_v1_enabled.png" title="Shoutcast Source Output Connection Tab in v1 (Legacy) mode"><img alt="Shoutcast Source Output Connection Tab in v1 (Legacy) mode" src="res/Output_tab_login_tab_v1_enabled.png"></a></div>
+<p>This tab allows you to specify the details needed for connecting to a DNAS server.<br><br></p>
+<p><b>Server Address</b>&nbsp;: This is the address of the server to connect to and will depend upon the setup which is being used. If the server being connected to is on the same machine then 'localhost' can be entered, otherwise the exact IP or DNS name of the server e.g. myserver.com needs to be entered here.</p>
+<p><b>Port</b>&nbsp;: This is the port related to the 'address' of the server to connect to. This needs to match 'portbase' (<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#Networking" title="Shoutcast DNAS Server 2">DNAS Server - section 4.8</a>) or the port value given to use.</p>
+<p><b>Stream ID</b>&nbsp;: This is the identifier used to identify the source to the server when using a Shoutcast 2 supporting setup. This needs to match 'streamid' (<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#Networking" title="Shoutcast DNAS Server 2">DNAS Server - section 4.8</a>) or the port value given to use.</p>
+<pre>This is disabled if running in v1.x mode.</pre>
+<p><b>DJ / User ID</b>&nbsp;: This is the user id as specified on the server for the type of connection the plug-in is being asked to make. Most likely you will be provided with a user id only if it is applicable to your setup.</p>
+<pre>If using a compatible v2 DNAS server then this can be entered and will
+be used as an identifier of the current DJ but it is not used for the login.</pre>
+<p><b>Password</b>&nbsp;: This is the password required for accessing the server (if set on the server). This needs to match 'password' (<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#Networking" title="Shoutcast DNAS Server 2">DNAS Server - section 4.8</a>) or the password value given to use.</p>
+<p><b>Automatic reconnection on connection failure</b>&nbsp;: This will make the plug-in attempt to connect back to the server if there is a break in the connection.
+<p><b>Reconnection timeout</b>&nbsp;: This is the number of seconds for the plug-in to wait in-between any connection attempts which fail before it will try again.</p>
+<div class="thumb"><a href="res/Output_tab_login_tab_v2_enabled.png" title="Shoutcast Source Output Connection Tab in v2 mode"><img alt="Shoutcast Source Output Connection Tab in v2 mode" src="res/Output_tab_login_tab_v2_enabled.png"></a></div>
+<p><b>Connect using</b>&nbsp;: This controls the mode the plug-in will run as. It provides <b>'automatic'</b>, <b>'v2.x'</b> and <b>'v1.x'</b> modes with <b>'automatic'</b> being the preferred mode (and the default on new installs).</p>
+<p>Not setting the correct mode for the server you want to connect to will cause the connection attempt to fail or enter into what appears to be a hung state where you are likely to see a 'Unable To Connect To The Server' error if connecting in v2 mode to v1 server. If the plug-in determines this is likely to have happened then it will show the following in status area:
+<pre>Unable To Connect To The Server.
+Enable 'Automatic' or 'v1.x' mode.</pre></p>
+<p><br>When 'automatic' mode is enabled the information panel displayed below this option shows the following message:</p>
+<pre>"Automatic mode" attempts to pick the most
+appropriate protocol mode to connect to the
+server. If this does not work correctly, you
+can select a specific protocol mode to use.</pre>
+<p><br>When 'v2.x' mode is enabled the information panel displayed below this option shows the following message:</p>
+<pre>Connecting to a v1.x server in v2.x mode will
+show the "Unable To Connect To The Server"
+error. To fix this error you will need to select
+either "Automatic mode" or "v1.x mode".</pre>
+<p><br>When 'v1.x' mode is enabled the information panel displayed below this option shows the following message:</p>
+<pre>When the DJ password is formatted as
+&lt;djlogin>:&lt;djpassword&gt; e.g. dj_1:noise
+
+Enter &lt;djlogin&gt; in 'DJ / User ID' e.g. dj_1
+Enter &lt;djpassword&gt; in 'Password' e.g. noise</pre>
+<p><br></p>
+
+<a name="Directory_Tab"></a><h2>3.2.2.&nbsp;Directory Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Output_tab_directory_tab.png" title="Shoutcast Source Output Directory Tab"><img alt="Shoutcast Source Output Directory Tab" src="res/Output_tab_directory_tab.png"></a></div>
+<p>This tab allows you to specify values specific to the stream for being listed or for what
+is provided to listeners when they connect to the DNAS server based on the version set.<br><br></p>
+<p><b>Make this server public (Recommended)</b>&nbsp;: With this enabled, the stream is indicated as being allowed to appear in the Shoutcast Directory. This will enable options as applicable based also on the mode the plug-in is set to run as.</p>
+<p><b>Name</b>&nbsp;: This is the name you want to use for the source (often what will be used in Shoutcast Directory listing).</p>
+<p><b>URL</b>&nbsp;: This is the url for the stream allowing listeners to view or get more information.</p>
+<p><b>Genre</b>&nbsp;: This is the genre for the source and is used to categorise the stream if listed on the Shoutcast Directory listing. Select the genre from the arrow button menu. It is not possible to manually enter the genre and the input field is read-only.</p>
+<p><b>Arrow Button</b>&nbsp;: This will show a menu with known genres and sub-genres allowed for any Shoutcast Directory listings. This will only be enabled if using v1.x mode or if using v2 mode and 'Make this server public' is unchecked.</p>
+<p><b>AIM / ICQ / IRC</b>&nbsp;: These allow you to specify some contact information for clients though support of these fields is only available when using v1.x mode.</p>
+<p><br></p>
+
+<a name="Encoder_Tab"></a><h2>3.2.3.&nbsp;Encoder Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Output_tab_encoder_tab.png" title="Shoutcast Source Output Encoder Tab"><img alt="Shoutcast Source Output Encoder Tab" src="res/Output_tab_encoder_tab.png"></a></div>
+<p>This tab allows you to specify the encoder to be used to create the output stream from the input stream the plug-in gets. The following encoders are available with the plug-in:</p>
+<pre>MP3 (audio/mpeg)
+AAC (audio/aacp)</pre>
+<p><br></p>
+<p>The AAC (actually ADTS-AAC) encoding is provided by enc_aacplus.dll (Winamp 5.1 to 5.61) or enc_fhgaac.dll (Winamp 5.62 and up). If this is not detected in the Winamp plug-ins folder then only MP3 encoding is available.</p>
+<p>Based on the encoder selected, the 'encoder settings' section will provide different options for controlling the encoder settings as either a button to open a configuration window or a dropdown list with options to choose from.</p>
+<p><br></p>
+
+<a name="Save_Encoded_Output"></a><h2>3.2.3.1.&nbsp;Save Encoded Output <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>This allows you to make a backup of the stream audio data sent to the DNAS server.<br><br></p>
+<p><b>Save a copy of the encoded stream audio</b>&nbsp;: Enables or disables saving a copy of the audio.</p>
+<pre>The extension of the output file is automatically changed based on the selected
+encoder option to ensure that the file can be easily played in most media players.</pre>
+<p><br></p>
+
+<a name="Titles_Tab"></a><h2>3.2.4.&nbsp;Titles Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Output_tab_titles_tab.png" title="Shoutcast Source Output Titles Tab"><img alt="Shoutcast Source Output Titles Tab" src="res/Output_tab_titles_tab.png"></a></div>
+<p>This tab allows you to specify how the stream metadata is gathered from Winamp or if it is manually entered with the options provided.<br><br></>
+<p><b>Disable title updates</b>&nbsp;: This will prevent the Source DSP from sending any title updates.</p>
+<p><b>Follow Winamp's title updates</b>&nbsp;: This makes the Source DSP use Winamp's title updates for stream title updates, sent in the format based on the 'Connect using' setting.</p>
+<p><b>Send next track title to</b>&nbsp;: This sends the next track title to the server when using the v2 mode and if the plug-in can determine the next track.</p>
+<pre>The current version of Winamp is always recommended to use
+due to the improved support for this feature since Winamp v5.61.</pre>
+<p><b>Manual title updates</b>&nbsp;: This will only send titles updates when 'Send Update' is pressed which uses the custom title information entered into the 'now' and 'next' fields (which are enabled as applicable to the mode in use).</p>
+<pre>The 'Send Update' button is enabled when a title is entered or it is
+different from the existing title. When using Shoutcast v2 mode the
+'next' title field will become available as long as title field is not empty.</pre>
+<p><br></p>
+
+<a name="Artwork_Tab"></a><h2>3.2.5.&nbsp;Artwork Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Output_tab_artwork_tab_v2_enabled.png" title="Shoutcast Source Output Artwork in v2 mode"><img alt="Shoutcast Source Output Artwork in v2 mode" src="res/Output_tab_artwork_tab_v2_enabled.png"></a></div>
+<p>This tab allows you to specify whether in-stream artwork will be sent to the Shoutcast server and if so the type of artwork which will be sent which can be for the station in general as well as per file artwork (much like album art display in most media players).<br><br></p>
+<p><b>Send in-stream artwork</b>&nbsp;: Enables or disables sending of in-stream artwork.</p>
+<pre>If this is enabled and then disabled, it is possible that the
+plug-in will send some clear artwork messages after disabling
+this option to ensure there is no artwork cached by the server.</pre>
+<p><b>Send artwork from the playing file (if available)</b>&nbsp;: This sends artwork from the currently playing song to the server and acts in the same way as the album art view in most media players.</p>
+<pre>If unchecked or there is no artwork for the playing song then the
+DNAS server may be sent a clear artwork message as applicable.
+
+This is sent as a PNG image to the Shoutcast server.</pre>
+<p><b>Send artwork for stream branding</b>&nbsp;: This will send the image as selected in the box below to the server to act as the station or stream image.</p>
+<pre>If left empty then the DNAS server may be sent a clear artwork message as applicable.</pre>
+<p><br></p>
+<div class="thumb"><a href="res/Output_tab_artwork_tab_v1_enabled.png" title="Shoutcast Source Output Artwork in v1 (Legacy) mode"><img alt="Shoutcast Source Output Artwork in v1 (Legacy) mode" src="res/Output_tab_artwork_tab_v1_enabled.png"></a></div>
+<p>Using the plug-in with a connection to a legacy server will cause the following notice to be shown:</p>
+<pre>Stream is setup for a Shoutcast v1.x server
+which does not support in-stream artwork.
+
+To send in-stream artwork, select either
+"Automatic mode" or "v2.x mode" and
+ensure you are connecting to a v2.x server.</pre>
+<p><br>The plug-in is only able to send in-stream artwork upto 511 KiB (523680 bytes) in size due to the Shoutcast 2 protocol specification for metadata packets. If this limit is reached then the artwork will not be sent and instead the server will get a clear artwork message. This tab page will show if the artwork cannot be used.</p>
+<p>Viewing the in-stream artwork depends on native playback support of Shoutcast v2 streams in the player used by the client so without a compatible player the client will not be able to view it is as it is not available with Shoutcast v1 streams.</p>
+<p><br></p>
+
+<a name="Logs_Tab"></a><h2>3.2.6.&nbsp;Logs Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Output_tab_logs_tab.png" title="Shoutcast Source Output Logs Tab"><img alt="Shoutcast Source Output Logs Tab" src="res/Output_tab_logs_tab.png"></a></div>
+<p>This tab allows you to specify the logging options of the status messages as shown at the top of this page. Additionally it also provides the means to log the filepath of the next tracks (if known) which are going to be played by Winamp with support for logging of the track titles if using the xml output mode.<br></p>
+<pre>The main logging options are not enabled by default though this can be used
+for tracking problems with the plug-in e.g. if you are having connection issues.</pre>
+<p><br><b>Enable logging of connection status messages</b>&nbsp;: Enables or disables connection logging.</p>
+<p><b>Clear log file on logging startup</b>&nbsp;: This will reset the log everytime the plug-in starts.</p>
+<p><b>Open log file...</b>&nbsp;: This will open the log file in the associated program for .log files.</p>
+<p><b>Clear log file</b>&nbsp;: This will clear the log file if it exists. It will not remove the file.</p>
+<p><b>Enable next track logging</b>&nbsp;: This will enable creating a log file (based on the following options) of the known next tracks to be played by Winamp.</p>
+<p><b>Save report as xml instead of plain text</b>&nbsp;: Changing this will create the log as an xml file containing filepath and title with each item identified by the 'seq' attribute.</p>
+<pre>The next track logging is only updated when the plug-in detects a track change.
+If the plain text / xml mode is changed or the plug-in starts then the file contents
+will be cleared until the next track title change happens.</pre>
+<p><br></p>
+
+<a name="Input_Tab"></a><h2>3.3.&nbsp;Input Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+
+<a name="Input_Configuration"></a><h2>3.3.1.&nbsp;Input Configuration <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/Input_tab_winamp_input.png" title="Shoutcast Source Input Tab in Winamp mode"><img alt="Shoutcast Source Input Tab in Winamp mode" src="res/Input_tab_winamp_input.png"></a></div>
+<p><b>Input Device</b>&nbsp;: This allows you to choose between using Winamp or your soundcard (usually the line-in) as the input source for the output stream the plug-in makes. Depending upon the selection made additional options will be shown below.</p>
+<p><b>Input Levels</b>&nbsp;: These show the current and peak audio level of the left and right channels as is being passed through the plug-ins core. This can aid in seeing if the input source is possibly not working or to check the audio is clipped.</p>
+<p><b>Input Settings</b>&nbsp;: When the soundcard input is selected then this allows for control over the sample rate used on the input source. When the Winamp input is selected then this will show information about what's currently playing.</p>
+<p><br></p>
+
+<a name="Soundcard_Mixer_Control"></a><h2>3.3.2.&nbsp;Soundcard Mixer Control <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p><b>Choose Microphone</b>&nbsp;: This will allow you to choose any of the input devices reported by the OS for use with the microphone overlay mode the plug-in provides.</p>
+<p><b>Refresh Button</b>&nbsp;: This allows you to refresh the capture device list on Vista / Windows 7 (is disabled otherwise) since the plug-in was started. This is useful if you have connected a device to the machine and now want to use it.</p>
+<div class="thumb"><a href="res/Input_tab_soundcard_input.png" title="Shoutcast Source Input Tab in soundcard mode"><img alt="Shoutcast Source Input Tab in soundcard mode" src="res/Input_tab_soundcard_input.png"></a></div>
+<p><b>Open Mixer</b>&nbsp;: This will open the operating systems recording and playback options (when using Windows 2000 / XP) which will allow you to change any required input and output settings for the system (though the amount you can change does depend upon the operating system being used - (<a href="#Known_Issues" title="">see section 4.0</a>)).</p>
+<p><b>Music Level</b>&nbsp;: This controls the Winamp output level (from no audio to full audio level).</p>
+<p><b>BG Level</b>&nbsp;: This controls the Winamp output level when the 'Push to Talk' option is active (from no audio to full audio level).</p>
+<p><b>Mic Level</b>&nbsp;: This controls the chosen microphone device's output level when the 'Push to Talk' option is active (from no audio to full audio level).</p>
+<p><b>Fade Time</b>&nbsp;: This controls the amount of time it takes for the audio to fade from the non 'Push to Talk' mode to 'Push to Talk' being the active mode in usage (from no fade i.e. instantly changes to 2.5 second fade duration).</p>
+<p><b>Capture Device Fade Time</b>&nbsp;: This controls the amount of time it takes for the selected capture device to fade from the non 'Push to Talk' mode to 'Push to Talk' being the active mode in usage (from no fade i.e. instantly changes to 2.5 second fade duration).</p>
+<p><b>Push to Talk</b>&nbsp;: When this is pressed then the chosen microphone device becomes the active input source as used by any active output streams (<a href="#Output_Tab" title="">see section 3.2</a>). When enabled this button will appear in an activated state.</p>
+<p><b>Lock</b>&nbsp;: When this is pressed it will toggle the 'Push to Talk' mode on or off depending on the current state of this option when it pressed. When enabled this will appear in an activated state along with the 'Push to Talk' button.</p>
+<p><b>Arrow Button</b>&nbsp;: This will show a menu with the option "Enable 'Push to Talk' on startup" allowing for the mode to be re-enabled when the plug-in is started. This may be of use as the plug-in turns off the mode and sets the system levels back to the non-pushed mode when the plug-in's window is closed.</p>
+<p><br></p>
+
+<a name="About_Tab"></a><h2>3.4.&nbsp;About Tab <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb"><a href="res/About_tab.png" title="Shoutcast Source About Tab"><img alt="Shoutcast Source About Tab" src="res/About_tab.png"></a></div>
+<p>This tab provides information about the version of the plug-in you are using - useful for determining if you are using an older version of the plug-in or when reporting issues.</p>
+<p><br></p>
+
+<a name="Documentation_and_Support"></a><h2>3.4.1.&nbsp;Documentation and Support <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>This part of the tab provides links to access the available documentation and also for going to the Shoutcast support forum if issues are being experienced with the plug-in.</p>
+<p>The documentation is either the current version as shipped with the plug-in if selected during install (stored in <b>&lt;winampdir&gt;\Plugins\Shoutcast Source DSP</b>) or if not found it directs you to an online copy available at <a target="_blank" href="http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in">http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in</a></p>
+<p>The support forum is accessed via <a href="http://forums.shoutcast.com/forumdisplay.php?f=140" title="http://forums.shoutcast.com/forumdisplay.php?f=140">http://forums.shoutcast.com/forumdisplay.php?f=140</a></p>
+<p><br></p>
+
+<a name="Known_Issues"></a><h2>4.&nbsp;Known Issues <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>The following are currently known issue(s) to affect the currently released build of the Source DSP plug-in:</p>
+<p><br></p>
+
+<a name="Soundcard_Mixer_Control_2"></a><h2>4.1.&nbsp;Soundcard Mixer Control <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p><b>Issue</b>: The soundcard mixer control does not work correctly or as expected on Vista / Windows 7 especially with the handling of the selected 'microphone' device due to changes in the audio system which prevent the capture handling from Windows 2000 / XP working in the same way. Windows 2000 / XP should still work as expected.</p>
+<p><b>Workaround</b>: The only obvious work around is to use the features the OS provides to enable the 'Listen to this device' option via the system's recording devices feature and then mix the levels with the controls the OS provides.</p>
+<p><b>Expected Resolution</b>: This issue is still being investigated and hopefully a solution will be provided to allow for control of the input device in unison with the selected 'microphone' device with-in the plug-ins interface when using this mode.</p>
+<p><br></p>
+
+<a name="Shoutcast_2_Cipher_Key"></a><h2>5.&nbsp;Shoutcast 2 Cipher Key <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>If you find that you do need to change the uvoxcipherkey (<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#YP_Server_Behaviour" title="Shoutcast DNAS Server 2">DNAS Server - section 4.14</a>) in you sc_serv setup, then you can change the cipher key the DSP uses. You will only need to do this if you get the following status message when making a connection:</p>
+<pre>Authentication Error:
+Cipher Does Not Match</pre>
+<p>This is done currently via editing 'Cipherkey' entry in dsp_sc.ini in your Winamp config folder where you just need to change the string after the equal sign to the value from 'uvoxcipherkey' or 'djcipher' depending upon what you are trying to connect to.</p>
+<p>The dsp_sc.ini file can usually be found by entering <b>%appdata%\Winamp\plugins</b> into the address bar in Windows Explorer. If it is not there then you should search for <b>dsp_sc.ini</b> and make sure to have the search program you are using to look for hidden files (this is just incase the OS is hiding the settings folder).</p>
+<p><br></p>
+
+<a name="Example_Configurations"></a><h2>6.&nbsp;Example Configurations <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>If you are unsure of what to enter to get the Source DSP connected to the official tools, you should look at the <a href="Source_DSP_Plug-in_Config_Examples.html" title="Source DSP Plug-in Example Configurations">Source DSP Plug-in Example Configurations</a>. This shows you where to take configuration values from the official tool configuration file(s) and where in the plug-in configuration you need to enter them for the different operating modes available.</p>
+<p>For 3rd party servers or broadcast tools, you may need to consult their documentation to determine where you need to get the required configuration values from.</p>
+</body></html> \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html
new file mode 100644
index 00000000..5b8ec295
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" dir="ltr" lang="en"><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <title>Shoutcast Source DSP Plug-in Configuration Examples</title>
+ <style type="text/css">body,table,textarea,pre{font-family:Arial,Helvetica,sans-serif;font-size:10pt;}
+ .header{background-color:rgb(66,164,189);color:white;padding:1px;}
+ a:link,a:visited{color:#2762AE;text-decoration:none;}
+ a:hover{text-decoration:underline;}
+ ul{list-style-type:none;text-align:left;}
+ pre{background-color:#F0F0F0;border:1px solid #CCCCCC;font-weight:bold;padding:5px 10px 5px 10px;display:inline-block;margin-top:0;margin-bottom:0;margin-left:25px;}
+ pre.src{border:0;font-family:monospace;background-color:white;}
+ span.c{font-style:italic;font-weight:normal;color:#808080;}
+ span.v{font-weight:normal;}
+ span.r{font-weight:normal;color:#000066;}
+ span.s{font-weight:normal;color:#FF0000;}
+ #toc{border:1px solid #CCCCCC;padding:0 5px 0 5px;text-align: center;}
+ p.ver{font-size:85%;margin-top:-1.5em;text-align:center;}
+ .thumb,.thumb_about,.thumb_config{float:right;margin-left:10px;margin-top:5px;margin-bottom:10px;}
+ .thumb{width:132px;}
+ .thumb_about{width:242px;}
+ .thumb_config{width:402px;}
+ img{border:0;height:203px;width:130px;}
+ img.about{height:185px;width:240px;}
+ img.config{height:312px;width:400px;}
+ hr{border:0;background-color:#CCCCCC;height:1px;)</style>
+ <script type="text/javascript">if(window.showTocToggle){var tocShowText="show";var tocHideText="hide";showTocToggle()}function showTocToggle(){if(document.createTextNode){var linkHolder=document.getElementById('toctitle');if(!linkHolder){return}var outerSpan=document.createElement('span');outerSpan.className='toctoggle';var toggleLink=document.createElement('a');toggleLink.id='togglelink';toggleLink.className='internal';toggleLink.href='javascript:toggleToc()';toggleLink.appendChild(document.createTextNode(tocHideText));outerSpan.appendChild(document.createTextNode('['));outerSpan.appendChild(toggleLink);outerSpan.appendChild(document.createTextNode(']'));linkHolder.appendChild(document.createTextNode(' '));linkHolder.appendChild(outerSpan);var cookiePos=document.cookie.indexOf("dsp_eg_hidetoc=");if(cookiePos>-1&&document.cookie.charAt(cookiePos+15)==1){toggleToc()}}}function changeText(el,newText){if(el.innerText){el.innerText=newText}else if(el.firstChild&&el.firstChild.nodeValue){el.firstChild.nodeValue=newText}}function toggleToc(){var toc=document.getElementById('toc').getElementsByTagName('ul')[0];var toggleLink=document.getElementById('togglelink');if(toc&&toggleLink&&toc.style.display=='none'){changeText(toggleLink,tocHideText);document.getElementById('toc').style.cssFloat='left';toc.style.display='block';document.cookie="dsp_eg_hidetoc=0"}else{changeText(toggleLink,tocShowText);document.getElementById('toc').style.cssFloat='';toc.style.display='none';document.cookie="dsp_eg_hidetoc=1"}}window.onload=function(){var cookiePos=document.cookie.indexOf("dsp_eg_hidetoc=");if(cookiePos>-1&&document.cookie.charAt(cookiePos+15)==1){toggleToc()}}</script>
+</head>
+<div class="header">
+<h1 align="center">Shoutcast Source DSP Plug-in Configuration Examples</h1>
+<p class="ver">(Last Updated 26 September 2014)</p>
+</div>
+<table id="toc" style="float:left;margin-right:40px;margin-bottom:5px;"><tbody><tr><td>
+<div id="toctitle"><b>Contents</b> <span class="toctoggle">[<a id="togglelink" class="internal" href="javascript:toggleToc()">hide</a>]</span></div>
+<ul style="padding-left:0px;">
+<li><a href="#Introduction">1 Introduction</a></li>
+<li><a href="#Configurations">2 Configurations</a>
+<ul>
+<li><a href="#Direct_Source_to_a_Shoutcast_v2_DNAS_Server">2.1 Direct Source to a Shoutcast 2.x DNAS Server</a></li>
+<li><a href="#Direct_Source_to_a_Shoutcast_v1_DNAS_Server_.28Legacy.29">2.2 Direct Source to a Shoutcast 1.x DNAS Server (Legacy)</a></li>
+</ul>
+</li>
+</ul>
+</td></tr></tbody></table>
+
+<a name="Introduction"></a><h2>1.&nbsp;Introduction</h2>
+<hr>
+<p>The aim of this document is to show you what needs to be entered in the different options in the plug-ins configuration window to allow it to work with the DNAS server (sc_serv) configuration examples. Although this does not cover cases of connecting to other Shoutcast compatible server software or Transcoder / AutoDJ instances, it should still allow you to get broadcasting as long as you have the basic information needed.</p>
+<p><br></p>
+
+<a name="Configurations"></a><h2>2.&nbsp;Configurations <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<p>In all of the example configurations, it is assumed that Winamp has been chosen as the input source for the stream(s) being created and that all of the passwords used are the same as those in the DNAS server example configuration files. Remember that you should change the example passwords when setting up your Shoutcast system.</p>
+<p>The choice of the encoder used is left as something for you to decide upon considering the DSP plug-in supports MP3 and AAC along with all of the different bitrates, etc. However this should not cause an issue with the example setups used but is something you need to decide upon as part of the general process in setting up a Shoutcast system.</p>
+<p>Finally the name of the options as shown in the english translation of the plug-in on its 'Output' tab (<a href="Source_DSP_Plug-in.html#Output_Tab">see Source DSP - section 3.2</a>) will be used in this file to identify the options which need to be entered. This is mentioned incase a localised version of the DSP plug-in is used (a nice feature implemented in version 2 of the plug-in).</p>
+<pre>Since v2.3.4, the Source DSP can automatically try to choose
+the correct mode to connect to the DNAS server. If this does
+not work as expected then you can select the expected mode.</pre>
+<p><br></p>
+
+<a name="Direct_Source_to_a_Shoutcast_v2_DNAS_Server"></a><h2>2.1.&nbsp;Direct Source to a Shoutcast 2.x DNAS Server <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb_config"><a href="res/Direct_Source_to_a_Shoutcast_v2_DNAS_Server.png" title="Direct Source to a Shoutcast 2.x DNAS Server"><img class="config" alt="Direct Source to a Shoutcast 2.x DNAS Server" src="res/Direct_Source_to_a_Shoutcast_v2_DNAS_Server.png"></a></div>
+<h3>Connection Tab</h3>
+<p><b>Server Address</b>&nbsp;: localhost (or the IP of the server if it is different from the local machine [<a href="Source_DSP_Plug-in.html#Login_Tab">see Source DSP - section 3.2.1</a>] ).</p>
+<p><b>Port</b>&nbsp;: 8000 (or the value set for 'portbase' [<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#Networking">see DNAS server - section 4.8</a>] ).</p>
+<p><b>Stream ID</b>&nbsp;: 1 (or the value set for 'streamid' for the relevant connection being made to the server [<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#Stream_Configuration">see DNAS server - section 4.12</a>] ).</p>
+<p><b>DJ / User ID</b>&nbsp;: this can be left blank and is not used with a source connection.</p>
+<p><b>Password</b>&nbsp;: testing (or the value set for 'password' [<a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#Networking">see DNAS server - section 4.8</a>] ).</p>
+<p><b>Connect using:</b>&nbsp;: set to 'Automatic mode' (recommended) or 'v2.x mode'.</p>
+<p><br></p>
+<h3>Directory Tab</h3>
+<p>Here you can enter any details as required to identify or provide contact details for your stream to any clients connecting or when viewed on the Shoutcast Directory listing.</p>
+<p><b>Make this stream public (Recommended)</b>&nbsp;: The usage of this setting depends upon the value 'publicserver' in your server configuration. See <a target="_blank" href="http://wiki.shoutcast.com/wiki/Shoutcast_DNAS_Server_2#YP_Server_Behaviour">DNAS server - section 4.14</a> for details.</p>
+<p><br></p>
+
+<a name="Direct_Source_to_a_Shoutcast_v1_DNAS_Server_.28Legacy.29"></a><h2>2.2.&nbsp;Direct Source to a Shoutcast 1.x DNAS Server (Legacy) <a style="font-size:55%;" href="#">[top]</a></h2>
+<hr>
+<div class="thumb_config"><a href="res/Direct_Source_to_a_Shoutcast_v1_DNAS_Server_(Legacy).png" title="Direct Source to a Shoutcast 1.x DNAS Server (Legacy)"><img class="config" alt="Direct Source to a Shoutcast 1.x DNAS Server (Legacy)" src="res/Direct_Source_to_a_Shoutcast_v1_DNAS_Server_(Legacy).png"></a></div>
+<h3>Connection Tab</h3>
+<p><b>Server Address</b>&nbsp;: localhost (or the IP of the server if it is different from the local machine [<a href="Source_DSP_Plug-in.html#Login_Tab">see Source DSP - section 3.2.1</a>] ).</p>
+<p><b>Port</b>&nbsp;: 8000 (or the value set for 'portbase' for the Shoutcast 1.x DNAS server used).</p>
+<p><b>Password</b>&nbsp;: testing (or the value set for 'password' for the Shoutcast 1.x server used).</p>
+<p><b>Connect using:</b>&nbsp;: set to 'Automatic mode' (recommended) or 'v1.x mode'.</p>
+<p><br></p>
+<h3>Directory Tab</h3>
+<p>Here you can enter any details as required to identify or provide contact details for your stream to any clients connecting or when viewed on the Shoutcast Directory listing.</p>
+</body></html> \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png
new file mode 100644
index 00000000..8c8b44d2
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png
new file mode 100644
index 00000000..8ba1e792
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png
new file mode 100644
index 00000000..4932e8a8
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png
new file mode 100644
index 00000000..dda9c996
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png
new file mode 100644
index 00000000..13964cad
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png
new file mode 100644
index 00000000..31cd72e9
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png
new file mode 100644
index 00000000..a5932275
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png
new file mode 100644
index 00000000..4e335adc
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png
new file mode 100644
index 00000000..f332bfc9
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png
new file mode 100644
index 00000000..acf0908d
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png
new file mode 100644
index 00000000..40674054
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png
new file mode 100644
index 00000000..7a3f9872
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png
new file mode 100644
index 00000000..0707cfba
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png
new file mode 100644
index 00000000..2c1c82a5
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png b/Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png
new file mode 100644
index 00000000..f327f76e
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png
new file mode 100644
index 00000000..659f97d3
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png
Binary files differ
diff --git a/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj
new file mode 100644
index 00000000..6e842b13
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj
@@ -0,0 +1,424 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>17.0</VCProjectVersion>
+ <ProjectGuid>{8CC45367-89FC-439A-94B3-A8FD1EA0346A}</ProjectGuid>
+ <RootNamespace>dsp_sc</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>17.0.32505.173</_ProjectFileVersion>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <GenerateManifest>false</GenerateManifest>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <GenerateManifest>false</GenerateManifest>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <LinkIncremental>false</LinkIncremental>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\dsp_sc___Win32_Release_LAME_DLL0/dsp_sc.tlb</TypeLibraryName>
+ <HeaderFileName />
+ </Midl>
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;NDEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <PrecompiledHeader />
+ <PrecompiledHeaderOutputFile />
+ <BrowseInformation />
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <WarningLevel>Level3</WarningLevel>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <Version />
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DelayLoadDLLs>uxtheme.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <EntryPointSymbol />
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+
+xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TypeLibraryName>.\dsp_sc___Win32_Release_LAME_DLL0/dsp_sc.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;NDEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <PrecompiledHeaderOutputFile>
+ </PrecompiledHeaderOutputFile>
+ <BrowseInformation>
+ </BrowseInformation>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <WarningLevel>Level3</WarningLevel>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <Version>
+ </Version>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DelayLoadDLLs>uxtheme.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <EntryPointSymbol>
+ </EntryPointSymbol>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+
+xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\dsp_sc___Win32_Debug_LAME_DLL0/dsp_sc.tlb</TypeLibraryName>
+ <HeaderFileName />
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;_DEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <PrecompiledHeader />
+ <BrowseInformation>true</BrowseInformation>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <WarningLevel>Level3</WarningLevel>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <Version />
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <DelayLoadDLLs>uxtheme.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AssemblyDebug>true</AssemblyDebug>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>true</GenerateMapFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention />
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+
+xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TypeLibraryName>.\dsp_sc___Win32_Debug_LAME_DLL0/dsp_sc.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;_DEBUG;WIN32;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <StructMemberAlignment>Default</StructMemberAlignment>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <BrowseInformation>true</BrowseInformation>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <WarningLevel>Level3</WarningLevel>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <Version>
+ </Version>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <DelayLoadDLLs>uxtheme.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AssemblyDebug>true</AssemblyDebug>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>true</GenerateMapFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+
+xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="crossfader\c_crossfader.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="sc2srclib\Encoders\c_encoder.cpp" />
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_aacp.cpp" />
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_fhgaac.cpp" />
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_mp3dll.cpp" />
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_nsv.cpp" />
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_ogg.cpp" />
+ <ClCompile Include="sc2srclib\shoutcast_output.cpp" />
+ <ClCompile Include="sc2srclib\uvAuth21\uvAuth21.cpp" />
+ <ClCompile Include="utils.cpp" />
+ <ClCompile Include="Wasapi\capture.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="Wasapi\player.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="Wasapi\WasapiCapture.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\nu\ServiceBuilder.h" />
+ <ClInclude Include="..\..\..\Winamp\Dsp.h" />
+ <ClInclude Include="api.h" />
+ <ClInclude Include="crossfader\c_crossfader.h" />
+ <ClInclude Include="Include\c_datapump.h" />
+ <ClInclude Include="Include\c_wavein.h" />
+ <ClInclude Include="Resource\resource.h" />
+ <ClInclude Include="sc2srclib\Encoders\c_encoder.h" />
+ <CustomBuild Include="sc2srclib\Encoders\c_encoder_aacp.h" />
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_fhgaac.h" />
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_mp3dll.h" />
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_nsv.h" />
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_ogg.h" />
+ <ClInclude Include="sc2srclib\Encoders\enc_if.h" />
+ <ClInclude Include="sc2srclib\Include\c_jobmanager.h" />
+ <ClInclude Include="sc2srclib\Include\c_serial_jobmanager.h" />
+ <ClInclude Include="sc2srclib\Include\shoutcast_output.h" />
+ <ClInclude Include="sc2srclib\uvAuth21\uvAuth21.h" />
+ <ClInclude Include="utils.h" />
+ <CustomBuild Include="Wasapi\player.h">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CustomBuild>
+ <CustomBuild Include="Wasapi\WasapiCapture.h">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="Resource\Script1.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="playarrow.ico" />
+ <Image Include="Resource\downarrow.ico" />
+ <Image Include="Resource\ICY.ICO" />
+ <Image Include="Resource\kill.ico" />
+ <Image Include="Resource\play.ico" />
+ <Image Include="Resource\playarrow.ico" />
+ <Image Include="Resource\refresh.ico" />
+ <Image Include="Resource\stop.ico" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters
new file mode 100644
index 00000000..bbd5d760
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters
@@ -0,0 +1,188 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{6b20b659-1ced-4ee8-bf03-14e1689ce5e9}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{5544d45e-4f9f-4984-9d57-266bf79aebf3}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{9e7d499b-1f5b-4215-845b-b8b421a57b91}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ <Filter Include="Resource Files\Icons">
+ <UniqueIdentifier>{5be3772d-ed9f-42e1-9a1f-a903f36e4a59}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="crossfader">
+ <UniqueIdentifier>{309ad66f-437f-44ee-9e2d-7bed1fe02107}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="sc2srclib">
+ <UniqueIdentifier>{6f4b5174-fe26-4264-8a5d-a509ff994842}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="sc2srclib\Include">
+ <UniqueIdentifier>{706fd0cd-d690-4d4d-a3e8-5ab5d1a85686}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="sc2srclib\Encoders">
+ <UniqueIdentifier>{f2016de0-c2f5-4f04-a642-bf341dd800c6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="sc2srclib\Encoders\Header Files">
+ <UniqueIdentifier>{66c1cf32-bd88-471e-9d7d-d9a72d855483}</UniqueIdentifier>
+ <Extensions>*.h</Extensions>
+ </Filter>
+ <Filter Include="sc2srclib\Encoders\Source Files">
+ <UniqueIdentifier>{91cc5093-54b5-4477-a50a-b6922b8e2ac0}</UniqueIdentifier>
+ <Extensions>*.cpp</Extensions>
+ </Filter>
+ <Filter Include="sc2srclib\uvAuth21">
+ <UniqueIdentifier>{f922861e-01eb-4f1c-9092-c288fbf3c1d0}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Wasapi">
+ <UniqueIdentifier>{9a8d5ddf-f47d-4007-9ef3-20230df8ccb5}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="utils.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="crossfader\c_crossfader.cpp">
+ <Filter>crossfader</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\shoutcast_output.cpp">
+ <Filter>sc2srclib</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\Encoders\c_encoder.cpp">
+ <Filter>sc2srclib\Encoders\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_aacp.cpp">
+ <Filter>sc2srclib\Encoders\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_fhgaac.cpp">
+ <Filter>sc2srclib\Encoders\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_mp3dll.cpp">
+ <Filter>sc2srclib\Encoders\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_nsv.cpp">
+ <Filter>sc2srclib\Encoders\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\Encoders\c_encoder_ogg.cpp">
+ <Filter>sc2srclib\Encoders\Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sc2srclib\uvAuth21\uvAuth21.cpp">
+ <Filter>sc2srclib\uvAuth21</Filter>
+ </ClCompile>
+ <ClCompile Include="Wasapi\capture.cpp">
+ <Filter>Wasapi</Filter>
+ </ClCompile>
+ <ClCompile Include="Wasapi\player.cpp">
+ <Filter>Wasapi</Filter>
+ </ClCompile>
+ <ClCompile Include="Wasapi\WasapiCapture.cpp">
+ <Filter>Wasapi</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Include\c_datapump.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Include\c_wavein.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource\resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="utils.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="crossfader\c_crossfader.h">
+ <Filter>crossfader</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Include\c_jobmanager.h">
+ <Filter>sc2srclib\Include</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Include\c_serial_jobmanager.h">
+ <Filter>sc2srclib\Include</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Include\shoutcast_output.h">
+ <Filter>sc2srclib\Include</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Encoders\enc_if.h">
+ <Filter>sc2srclib\Encoders</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Encoders\c_encoder.h">
+ <Filter>sc2srclib\Encoders\Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_fhgaac.h">
+ <Filter>sc2srclib\Encoders\Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_mp3dll.h">
+ <Filter>sc2srclib\Encoders\Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_nsv.h">
+ <Filter>sc2srclib\Encoders\Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\Encoders\c_encoder_ogg.h">
+ <Filter>sc2srclib\Encoders\Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sc2srclib\uvAuth21\uvAuth21.h">
+ <Filter>sc2srclib\uvAuth21</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\ServiceBuilder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\Dsp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="Resource\Script1.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="Resource\downarrow.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="Resource\ICY.ICO">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="Resource\kill.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="Resource\play.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="playarrow.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="Resource\playarrow.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="Resource\refresh.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ <Image Include="Resource\stop.ico">
+ <Filter>Resource Files\Icons</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="sc2srclib\Encoders\c_encoder_aacp.h">
+ <Filter>sc2srclib\Encoders\Header Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="Wasapi\player.h">
+ <Filter>Wasapi</Filter>
+ </CustomBuild>
+ <CustomBuild Include="Wasapi\WasapiCapture.h">
+ <Filter>Wasapi</Filter>
+ </CustomBuild>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/main.cpp b/Src/Plugins/DSP/dsp_sc/main.cpp
new file mode 100644
index 00000000..8a3236c1
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/main.cpp
@@ -0,0 +1,5804 @@
+// These are disabled for now they have unknown issues.
+//#define USE_OGG
+//#define CAPTURE_TESTING
+
+// TODO / BE NICE FOR FUTURE VERSIONS
+// 1) Fix capture issues on Vista / Windows 7
+// 2) Allow metadata to be specified from file
+// 3) Move over to using enc_lame (and ui changes for it) [partial]
+
+#define APP_Name "Shoutcast Source"
+#define APP_Version "2.4.2"
+#define APP_VersionW L"2.4.2"
+#define APP_Build "449"
+
+#include <windows.h>
+#include <commctrl.h>
+#include <stdio.h>
+#include <shlobj.h>
+#include <shellapi.h>
+#include <shlwapi.h>
+#include <commdlg.h>
+#include <math.h>
+#include <mmdeviceapi.h>
+#include <audioclient.h>
+#include <endpointvolume.h>
+#include <functiondiscoverykeys.h>
+#ifdef CAPTURE_TESTING
+#include "Wasapi/WASAPICapture.h"
+#endif
+#include "resource/resource.h"
+#include "sc2srclib/include/shoutcast_output.h"
+#include "sc2srclib/encoders/c_encoder_mp3dll.h"
+#include "sc2srclib/encoders/c_encoder_nsv.h"
+#include "sc2srclib/encoders/c_encoder_fhgaac.h"
+#include "sc2srclib/encoders/c_encoder_aacp.h"
+#ifdef USE_OGG
+#include "sc2srclib/Encoders/c_encoder_ogg.h"
+#endif
+// allows us to compile with the Wasabi sdk without having to change things
+//#define __WASABI_TYPES_H
+//#define _GUID_H
+//typedef unsigned long ARGB32;
+//static const GUID INVALID_GUID = { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} };
+#include "api.h"
+#include "include/c_wavein.h"
+#include "crossfader/c_crossfader.h"
+#include <winamp/wa_ipc.h>
+#include <winamp/dsp.h>
+#include "nu/servicebuilder.h"
+#include "utils.h"
+#ifdef CAPTURE_TESTING
+#include "wasapi/player.h"
+#endif
+#include <strsafe.h>
+
+#define NUM_OUTPUTS 5
+#define NUM_ENCODERS NUM_OUTPUTS
+#define NUM_BUFFERS 3
+#define MAX_TABWNDS 4
+#define MAX_COLS 6
+#define MAX_CELLS 12
+#define MAX_INWNDS 2
+#define MAX_OUTWNDS 6
+#define SYSTRAY_BASE_ICON 1024
+#define SYSTRAY_ICY_ICON 1
+#define SYSTRAY_BASE_MSG WM_USER
+#define SYSTRAY_MAXIMIZE_MSG 27
+#define DEFAULT_INPUTDEVICE 0 // winamp
+
+#define DOWNLOAD_URL L"http://www.shoutcast.com/BroadcastNow"
+// 404, change to one of these?
+// #define DOWNLOAD_URL L"http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in"
+// #define DOWNLOAD_URL L"http://www.shoutcast.com"
+
+char sourceVersion[64] = {APP_Version "." APP_Build};
+static char szDescription[256];
+static char szDescription2[256];
+static wchar_t szDescription2W[256];
+
+#ifdef CAPTURE_TESTING
+static Player *pPlayer = NULL;
+
+//
+// The Player object calls the methods in this class to
+// notify the application when certain audio events occur.
+//
+class CPlayerCallbacks : public PlayerCallbacks
+{
+ // Notification callback for volume change. Typically, the user
+ // adjusts the volume through the SndVol.exe application.
+ void VolumeChangeCallback(float volume, BOOL mute)
+ {
+ /*EnableWindowDlgItem(g_hDlg, IDC_SLIDER_VOLUME, TRUE);
+ SetWindowText(GetDlgItem(g_hDlg, IDC_STATIC_MUTE),
+ (mute == TRUE) ? L"Mute" : L"");
+ PostMessage(GetDlgItem(g_hDlg, IDC_SLIDER_VOLUME),
+ TBM_SETPOS, TRUE, LPARAM(volume*MAX_VOLUME_LEVEL));*/
+ };
+
+ // Notification callback for when stream stops playing unexpectedly
+ // (typically, because the player reached the end of a wave file).
+ void PlayerStopCallback(void)
+ {
+ /*SetActiveWindow(g_hDlg);
+ PostMessage(GetDlgItem(g_hDlg, IDC_BUTTON_STOP), BM_CLICK, 0, 0);*/
+ };
+
+ // Notification callback for when the endpoint capture device is
+ // disconnected (for example, the user pulls out the microphone plug).
+ void CaptureDisconnectCallback(void)
+ {
+ /*SetWindowText(GetDlgItem(g_hDlg, IDC_STATIC_LASTACTION),
+ L"Capture device disconnected!");
+ SendMessage(GetDlgItem(g_hDlg, IDC_COMBO_CAPTUREDEVICE), CB_RESETCONTENT, 0, 0);*/
+ };
+
+ // Notification callback for when the endpoint rendering device is
+ // disconnected (for example, the user pulls out the headphones plug).
+ void RenderDisconnectCallback(void)
+ {
+ /*SetWindowText(GetDlgItem(g_hDlg, IDC_STATIC_LASTACTION),
+ L"Playback device disconnected!");
+ EnableWindowDlgItem(g_hDlg, IDC_SLIDER_VOLUME, FALSE);
+ SetWindowTextW(GetDlgItem(g_hDlg, IDC_STATIC_MUTE), L"Disconnected");
+ SendMessage(GetDlgItem(g_hDlg, IDC_COMBO_RENDERDEVICE), CB_RESETCONTENT, 0, 0);
+ SendMessage(GetDlgItem(g_hDlg, IDC_SLIDER_VOLUME), TBM_SETPOS, TRUE, 0);*/
+ };
+};
+
+
+static CPlayerCallbacks *pCallbacks = NULL;
+#endif
+
+// this is used to help determine if we're running on an older
+// version of Winamp where jnetlib has issues with the re-use
+// of connection handles when the connection previously failed
+int iscompatibility = 0;
+// used for the about page link so we don't cause win2k issues
+int isthemethere = 0;
+// Wasabi based services for localisation support
+api_service *WASABI_API_SVC = 0;
+api_config *AGAVE_API_CONFIG = 0;
+api_language *WASABI_API_LNG = 0;
+api_queue *WASABI_API_QUEUEMGR = 0;
+api_albumart *AGAVE_API_ALBUMART = 0;
+api_memmgr *WASABI_API_MEMMGR = 0;
+api_explorerfindfile* WASABI_API_EXPLORERFINDFILE = 0;
+api_downloadManager *WAC_API_DOWNLOADMANAGER = 0;
+
+// these two must be declared as they're used by the language api's
+// when the system is comparing/loading the different resources
+HINSTANCE WASABI_API_LNG_HINST = 0,
+ WASABI_API_ORIG_HINST = 0;
+HFONT boldFont = 0, normalFont = 0;
+HICON icy = 0, wa_icy = 0;
+static HHOOK nowPlayingHook,
+ nowPlayingHook2;
+static LPARAM nowPlayingID = -1;
+// just using these to track the paused and playing states
+int was_paused = 0,
+ was_playing = 0;
+DWORD play_duration = 0,
+ play_diff = 0;
+int isplaying = -1, ptt_load = 0;
+static wchar_t lastFile[MAX_PATH];
+int lastFilterIndex = 4;
+static int lastSec[NUM_OUTPUTS],
+ lastMode[NUM_OUTPUTS] = {-1, -1, -1, -1, -1},
+ lastEnable[NUM_OUTPUTS];
+
+static HWND buttonWnd[ NUM_OUTPUTS ];
+static HWND tabWnd;
+static HWND outTabWnd;
+static HWND updateWnd;
+
+static ARGB32 *playingImage;
+static ARGB32 *streamImage[NUM_OUTPUTS] = {(ARGB32 *)-1, (ARGB32 *)-1, (ARGB32 *)-1, (ARGB32 *)-1, (ARGB32 *)-1};
+static int playingImage_w, playingImage_h, playingLength, playingType;
+static int streamLength[NUM_OUTPUTS];
+static bool secChanged[NUM_OUTPUTS];
+void Config(struct winampDSPModule *this_mod);
+int Init(struct winampDSPModule *this_mod);
+int ModifySamples(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate);
+void Quit(struct winampDSPModule *this_mod);
+int secureFunc(int key){
+ int res = key * (unsigned long)1103515245;
+ res += (unsigned long)13293;
+ res &= (unsigned long)0x7FFFFFFF;
+ res ^= key;
+ return res;
+}
+winampDSPModule *getModule(int which);
+winampDSPModule module = {
+ "nullsoft(dsp_sc.dll)",
+ NULL,
+ NULL,
+ Config,
+ Init,
+ ModifySamples,
+ Quit,
+ NULL
+};
+
+winampDSPHeader header = {
+ DSP_HDRVER+1,
+ "Nullsoft " APP_Name " DSP " APP_Version,
+ getModule,
+ secureFunc
+};
+
+
+static ARGB32 * writeImg(const ARGB32 *data, int w, int h, int *length, const wchar_t *ext) {
+ if (!ext || ext && !*ext) return NULL;
+ if (*ext == L'.') ext++;
+ FOURCC imgwrite = svc_imageWriter::getServiceType();
+ int n = WASABI_API_SVC->service_getNumServices(imgwrite);
+ for (int i=0; i<n; i++) {
+ waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgwrite,i);
+ if (sf) {
+ svc_imageWriter * l = (svc_imageWriter*)sf->getInterface();
+ if (l) {
+ if (wcsstr(l->getExtensions(),ext)) {
+ void* ret = l->convert(data, 32, w, h, length);
+ sf->releaseInterface(l);
+ return (ARGB32 *)ret;
+ }
+ sf->releaseInterface(l);
+ }
+ }
+ }
+ return NULL;
+}
+
+HICON GetICYIcon(bool winamp = false) {
+ if (!winamp) {
+ if (!icy) {
+ icy = (HICON)LoadImage(WASABI_API_ORIG_HINST?WASABI_API_ORIG_HINST:module.hDllInstance,
+ MAKEINTRESOURCE(IDI_ICY), IMAGE_ICON, 0, 0,
+ LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION);
+ }
+ return icy;
+ } else {
+ if (!wa_icy) {
+ wa_icy = (HICON)LoadImage(GetModuleHandle("winamp.exe"),
+ MAKEINTRESOURCE(102), IMAGE_ICON, 0, 0,
+ LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION);
+ }
+ return (wa_icy ? wa_icy : icy);
+ }
+}
+
+BOOL InitLocalisation(HWND winamp) {
+ // if this is valid then we should be running on Winamp 5.5+ so try to get the localisation api
+ if (IsWindow(winamp)) {
+ iscompatibility = 1; ////// SendMessage( winamp, WM_WA_IPC, 0, IPC_IS_COMPATIBILITY_ENABLED );
+ isthemethere = !SendMessage(winamp, WM_WA_IPC, IPC_ISWINTHEMEPRESENT, IPC_USE_UXTHEME_FUNC);
+ if (!WASABI_API_LNG_HINST) {
+ // loader so that we can get the localisation service api for use
+ WASABI_API_SVC = (api_service*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GET_API_SERVICE);
+ if (WASABI_API_SVC == (api_service*)1) {
+ WASABI_API_SVC = NULL;
+ return FALSE;
+ }
+
+ // initialise all of the wasabi based services
+ ServiceBuild(WASABI_API_SVC, AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceBuild(WASABI_API_SVC, WASABI_API_LNG, languageApiGUID);
+ ServiceBuild(WASABI_API_SVC, WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ ServiceBuild(WASABI_API_SVC, AGAVE_API_ALBUMART, albumArtGUID);
+ ServiceBuild(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID);
+ ServiceBuild(WASABI_API_SVC, WASABI_API_EXPLORERFINDFILE, ExplorerFindFileApiGUID);
+ ServiceBuild(WASABI_API_SVC, WAC_API_DOWNLOADMANAGER, DownloadManagerGUID);
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(GetMyInstance(), DspShoutcastLangGUID);
+
+ // do this here so if there is no localisation support then the module names go to defaults
+ if (!szDescription[0]) {
+ StringCchPrintfA(szDescription, ARRAYSIZE(szDescription), LocalisedStringA(IDS_PLUGIN_NAME, NULL, 0), APP_Version);
+ header.description = szDescription;
+ }
+
+ if (!szDescription2[0]) {
+ module.description = LocalisedStringA(IDS_MODULE_NAME, szDescription2, 256);
+ LocalisedString(IDS_MODULE_NAME, szDescription2W, 256);
+ }
+ }
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ __declspec(dllexport) winampDSPHeader *winampDSPGetHeader2(HWND hwndParent) {
+ if (InitLocalisation(hwndParent)) {
+ return &header;
+ }
+ MessageBoxA(module.hwndParent,
+ "You are attempting to use the " APP_Name " plug-in in an\n"
+ "unsupported version of Winamp or in a non-Winamp install or via a\n"
+ "DSP stacker which does not implement the Winamp 5.5+ DSP api.\n\n"
+ "To work this plug-in requires Winamp 5.5 and higher (the most current\n"
+ "release is recommended) or for the non-Winamp install or DSP stacker\n"
+ "to be updated to support the required Winamp api's the plug-in uses.",
+ "Nullsoft " APP_Name, MB_ICONEXCLAMATION|MB_APPLMODAL);
+ return 0;
+ }
+
+ __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) {
+ // this isn't ideal but it ensures that we show a localised version of the message
+ // if not it'll make sure that we're using the plug-in dll's internal resources
+ // though for ease of code handling we have to fill in some of the dsp structures
+ // as the plug-in has effectively been unloaded at this stage for the uninstall.
+ HWND winamp = GetWinampHWND(0);
+ module.hDllInstance = hDllInst;
+ module.hwndParent = winamp;
+ InitLocalisation(winamp);
+
+ wchar_t title[256] = {0};
+ StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_Version);
+
+ // prompt to remove the settings files (defaults to no just incase)
+ if (MessageBoxW(hwndDlg, LocalisedString(IDS_PLUGIN_UNINSTALL, NULL, 0), title, MB_YESNO|MB_DEFBUTTON2) == IDYES) {
+ DeleteFile(GetSCIniFile(winamp));
+ }
+ return DSP_PLUGIN_UNINSTALL_NOW;
+ }
+
+#ifdef __cplusplus
+}
+#endif
+
+winampDSPModule *getModule(int which) {
+ if (which == 0) return &module;
+ return NULL;
+}
+// Client's proprietary event-context GUID
+//extern GUID g_guidMyContext;
+
+// Maximum volume level on trackbar
+#define MAX_VOL 100
+
+#define SAFE_RELEASE(what) \
+ if ((what) != NULL) \
+{ (what)->Release(); (what) = NULL; }
+
+//GUID g_guidMyContext = GUID_NULL;
+
+static IAudioStreamVolume *g_pStreamVol = NULL;
+static IAudioEndpointVolume *g_pEndptVol = NULL;
+static IAudioClient *g_pAudioClient = NULL;
+static IAudioCaptureClient *g_pCaptureClient = NULL;
+
+/*#define EXIT_ON_ERROR(hr) \
+ if (FAILED(hr)) { goto Exit; }
+#define ERROR_CANCEL(hr) \
+ if (FAILED(hr)) { \
+ MessageBox(hDlg, TEXT("The program will exit."), \
+ TEXT("Fatal error"), MB_OK); \
+ EndDialog(hDlg, TRUE); return TRUE; }*/
+
+HANDLE cf_mutex = NULL;
+C_CROSSFADER *Crossfader = NULL;
+int CrossfadeLen = 5000;
+unsigned int Input_Device_ID=0;
+int Restore_PTT = 0;
+unsigned int numInputs=0;
+char updateStr[16] = {0};
+
+typedef struct {
+ // BladeEnc DLL Version number
+ BYTE byDLLMajorVersion;
+ BYTE byDLLMinorVersion;
+ // BladeEnc Engine Version Number
+ BYTE byMajorVersion;
+ BYTE byMinorVersion;
+ // DLL Release date
+ BYTE byDay;
+ BYTE byMonth;
+ WORD wYear;
+ // BladeEnc Homepage URL
+ #define BE_MAX_HOMEPAGE 128
+ CHAR zHomepage[BE_MAX_HOMEPAGE + 1];
+ BYTE byAlphaLevel;
+ BYTE byBetaLevel;
+ BYTE byMMXEnabled;
+ BYTE btReserved[125];
+} BE_VERSION, *PBE_VERSION;
+typedef VOID (*BEVERSION)(PBE_VERSION);
+BEVERSION beVersion = NULL;
+void *init = NULL;
+void *params = NULL;
+void *encode = NULL;
+void *finish = NULL;
+void *lameclose = NULL;
+
+int CALLBACK DialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+HINSTANCE instance = NULL;
+HINSTANCE libinst = NULL;
+HWND hMainDLG = NULL;
+RECT mainrect;
+HWND inWin = NULL, inWinWa = NULL;
+
+struct T_TABWND {
+ HWND hWnd;
+ int id;
+ int timer_freq;
+ TCITEMW tcitem;
+} wnd[MAX_TABWNDS] = {0},
+ out_wnd[MAX_OUTWNDS] = {0};
+int num_tabwnds = 0,
+ num_outwnds = 0;
+
+struct T_COL {
+ LVCOLUMNW lvcol;
+ LVITEMW lvitem[MAX_CELLS];
+ int num_cells;
+} col[MAX_COLS] = {0};
+int num_cols = 0;
+
+struct T_INPUTWND{
+ HWND hWnd;
+ int id;
+ int timer_freq;
+} in_wnd[MAX_INWNDS] = {0};
+int num_inwnds = 0;
+
+// shoutcast source
+struct T_INPUT_CONFIG {
+ int srate;
+ int nch;
+};
+struct MY_T_OUTPUT {
+ T_OUTPUT_CONFIG Config;
+ int Encoder; // encoder this config is used by
+ int Handle; // handle that the encoder understands
+ int AutoTitle;
+ int AutoConnect;
+ int Logging;
+ int LogCOS;
+ int NextTitles;
+ int nextTrackLog;
+ int nextTrackLogXML;
+ wchar_t nextTrackPath[MAX_PATH];
+ int useArt;
+ int usePlayingArt;
+ int useStreamArt;
+ wchar_t stationArtPath[MAX_PATH];
+ int saveEncoded;
+ wchar_t saveEncodedPath[MAX_PATH];
+} Output[NUM_OUTPUTS] = {0};
+SHOUTCAST_OUTPUT Encoder[NUM_ENCODERS];
+HANDLE Enc_mutex[NUM_ENCODERS] = {0};
+int Enc_LastType[NUM_ENCODERS] = {0};
+C_WAVEIN<NUM_BUFFERS, 5120 > Soundcard;
+
+int last_buffer = 0;
+int Connection_CurSelPos = 0;
+int Encoder_CurSelPos = 0;
+int Input_CurSelPos = 3;
+int InputDevice = DEFAULT_INPUTDEVICE;
+clock_t audiolag = 0;
+clock_t lastaudio = 0;
+int curtab = 1;
+int curouttab = 0;
+int lookAhead = 3;
+bool skipMetada = false;
+bool doNextLookAhead = false;
+HANDLE hthread = NULL;
+DWORD threadid = 0;
+HANDLE hthreadout = NULL;
+DWORD threadoutid = 0;
+HWND hWinamp = NULL;
+int ini_modified = 0;
+HANDLE logFiles[NUM_OUTPUTS] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
+
+struct T_VU {
+ int vu_l;
+ int vu_r;
+ int update;
+ int lastUpdate;
+} VU;
+int peak_vu_l = -90;
+int peak_vu_r = -90;
+
+T_INPUT_CONFIG InputConfig;
+int MusVol = 9;
+int Mus2Vol = 3;
+int MicVol = 9;
+int FadeTime = 20;
+int MicFadeTime = 10; // mimic old behaviour with a faster fade up/down of the capture device
+int devopts = 0;
+clock_t FadeStartTime;
+clock_t MicFadeStartTime;
+int FadeOut = 0;
+WNDPROC prevButtonProc = NULL,
+ prevListViewProc = NULL,
+ prevHeaderProc = NULL,
+ prevTabWndProc = NULL,
+ prevOutTabWndProc = NULL;
+int blockmousemove = 0;
+T_INPUT_CONFIG LineInputAttribs[]= {
+ {22050, 1},
+ {44100, 1},
+ {22050, 2},
+ {44100, 2},
+};
+
+void AddSystrayIcon(HWND hWnd, UINT uIconId, HICON hIcon, UINT uMsg, LPWSTR lpszToolTip) {
+ NOTIFYICONDATAW tnid = {0};
+ tnid.cbSize = sizeof (NOTIFYICONDATAW);
+ tnid.hWnd = hWnd;
+ tnid.uID = SYSTRAY_BASE_ICON + uIconId;
+ tnid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
+ tnid.uCallbackMessage = SYSTRAY_BASE_MSG + uMsg;
+ tnid.hIcon = hIcon;
+ wcsncpy(tnid.szTip, lpszToolTip, ARRAYSIZE(tnid.szTip));
+ Shell_NotifyIconW(NIM_ADD, &tnid);
+ return;
+}
+
+void RemoveSystrayIcon(HWND hWnd, UINT uIconId) {
+ NOTIFYICONDATAW tnid = {0};
+ tnid.cbSize = sizeof (NOTIFYICONDATAW);
+ tnid.hWnd = hWnd;
+ tnid.uID = SYSTRAY_BASE_ICON + uIconId;
+ Shell_NotifyIconW(NIM_DELETE, &tnid);
+ return;
+}
+
+void AddTab(int dialog_id, wchar_t *tab_name, HWND hWndParent, DLGPROC DlgProc, int tab_id, int rect_id, int timer_freq) {
+ RECT r = {0};
+ T_TABWND *twnd = &wnd[num_tabwnds];
+ GetWindowRect(GetDlgItem(hWndParent, rect_id), &r);
+ ScreenToClient(hWndParent, (POINT *) & r);
+ twnd->id = dialog_id;
+ twnd->timer_freq = timer_freq;
+ twnd->hWnd = LocalisedCreateDialog(instance, dialog_id, hWndParent, DlgProc, dialog_id);
+ ShowWindow(twnd->hWnd, SW_HIDE);
+ SetWindowPos(twnd->hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ twnd->tcitem.mask = TCIF_TEXT;
+ twnd->tcitem.pszText = tab_name;
+ twnd->tcitem.cchTextMax = wcslen(tab_name);
+ SendDlgItemMessage(hWndParent, tab_id, TCM_INSERTITEMW, num_tabwnds++, (LPARAM)&twnd->tcitem);
+ if (IsWindow(twnd->hWnd) && isthemethere) {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)twnd->hWnd, IPC_USE_UXTHEME_FUNC);
+ }
+}
+
+void SetTab(int tabnum, HWND hWndParent, int tab_id) {
+ NMHDR nmh;
+ nmh.code = TCN_SELCHANGE;
+ nmh.hwndFrom = GetDlgItem(hWndParent, tab_id);
+ nmh.idFrom = tab_id;
+ curtab = tabnum;
+ SendMessage(nmh.hwndFrom, TCM_SETCURSEL, curtab, 0);
+ SendMessage(hWndParent, WM_NOTIFY, tab_id, (LPARAM) & nmh);
+}
+
+void AddInTab(int dialog_id, wchar_t *tab_name, HWND hWndParent) {
+ RECT r = {0};
+ T_INPUTWND *twnd = &in_wnd[num_inwnds++];
+ GetWindowRect(GetDlgItem(hWndParent, IDC_PANEL_RECT), &r);
+ ScreenToClient(hWndParent, (POINT *)&r);
+ twnd->id = dialog_id;
+ twnd->timer_freq = 0;
+ twnd->hWnd = LocalisedCreateDialog(instance, dialog_id, hWndParent, DialogFunc, dialog_id);
+ ShowWindow(twnd->hWnd, SW_HIDE);
+ SetWindowPos(twnd->hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ SendDlgItemMessageW(hWndParent, IDC_INPUTDEVICE, CB_ADDSTRING, 0, (LPARAM)tab_name);
+ if (IsWindow(twnd->hWnd) && isthemethere) {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)twnd->hWnd, IPC_USE_UXTHEME_FUNC);
+ }
+}
+
+void SetInTab(int tabnum, HWND hWndParent, int combo_id) {
+ SendDlgItemMessage(hWndParent, combo_id, CB_SETCURSEL, tabnum, 0);
+ SendMessage(hWndParent, WM_COMMAND, MAKEWPARAM(combo_id, CBN_SELCHANGE), (LPARAM) GetDlgItem(hWndParent, combo_id));
+}
+
+void AddOutTab(int dialog_id, wchar_t *tab_name, HWND hWndParent, DLGPROC DlgProc, int tab_id, int rect_id, int timer_freq) {
+ RECT r = {0};
+ T_TABWND *twnd = &out_wnd[num_outwnds];
+ GetWindowRect(GetDlgItem(hWndParent, IDC_PANELRECT_C), &r);
+ ScreenToClient(hWndParent, (POINT *)&r);
+ twnd->id = dialog_id;
+ twnd->timer_freq = 0;
+ twnd->hWnd = LocalisedCreateDialog(instance, dialog_id, hWndParent, DlgProc, dialog_id);
+ ShowWindow(twnd->hWnd, SW_HIDE);
+ SetWindowPos(twnd->hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
+ twnd->tcitem.mask = TCIF_TEXT;
+ twnd->tcitem.pszText = tab_name;
+ twnd->tcitem.cchTextMax = wcslen(tab_name);
+ SendDlgItemMessageW(hWndParent, IDC_CONTAB, TCM_INSERTITEMW, num_outwnds++, (LPARAM) & twnd->tcitem);
+ if (IsWindow(twnd->hWnd) && isthemethere) {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)twnd->hWnd, IPC_USE_UXTHEME_FUNC);
+ }
+}
+
+void SetOutTab(int tabnum, HWND hWndParent, int tab_id) {
+ NMHDR nmh;
+ nmh.code = TCN_SELCHANGE;
+ nmh.hwndFrom = GetDlgItem(hWndParent, tab_id);
+ nmh.idFrom = tab_id;
+ curouttab = tabnum;
+ SendMessage(nmh.hwndFrom, TCM_SETCURSEL, curouttab, 0);
+ SendMessage(hWndParent, WM_NOTIFY, tab_id, (LPARAM) & nmh);
+}
+
+void AddColumn(wchar_t *column_text, HWND listView) {
+ T_COL *tcol = &col[num_cols];
+ tcol->lvcol.mask = LVCF_TEXT;
+ tcol->lvcol.pszText = column_text;
+ tcol->lvcol.cchTextMax = wcslen(column_text);
+ SendMessageW(listView, LVM_INSERTCOLUMNW, num_cols++, (LPARAM)&tcol->lvcol);
+}
+
+void AddColItem(wchar_t *cell_text, int colnum, HWND hWndParent, int list_id, int pos = -1) {
+ LVITEMW *tcell = &col[colnum].lvitem[col[colnum].num_cells];
+ tcell->mask = TVIF_TEXT;
+ tcell->iItem = pos == -1 ? col[colnum].num_cells++ : pos;
+ tcell->iSubItem = colnum;
+ tcell->pszText = cell_text;
+ tcell->cchTextMax = wcslen(cell_text);
+ SendDlgItemMessageW(hWndParent, list_id, pos == -1 && colnum == 0 ? LVM_INSERTITEMW : LVM_SETITEMW, 0, (LPARAM)tcell);
+}
+
+void __inline interleave_buffer(const short * inLeft, short *outputbuf, const size_t num_samples) {
+ for (size_t i = 0; i < num_samples; ++i) {
+ outputbuf[i * 2] = inLeft[i];
+ outputbuf[i * 2 + 1] = inLeft[i];
+ }
+}
+
+DWORD WINAPI ThreadInput(LPVOID lpParameter) {
+ do {
+ // this is needed when doing soundcard capture
+ if (InputDevice == 1) DialogFunc((HWND) lpParameter, WM_TIMER, MAKEWPARAM(1234,0), 0);
+ short mybuf[32768] = {0};
+ size_t mysamps = 0;
+
+ if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) {
+ if (Input_CurSelPos != -1) {
+ if (InputDevice == 0) {
+ if (isplaying != 1) {
+ // when stopped or paused, we need to pump silent output
+ // and from testing, sending 2 emtpy samples appears to
+ // keep the output bitrate about the same as playback's
+ mysamps = sizeof(mybuf)/8;
+ } else {
+ mysamps = Crossfader->get(mybuf, sizeof (mybuf) / (InputConfig.nch * sizeof (short)), InputConfig.nch) * InputConfig.nch;
+ }
+ } else {
+ int samps = (LineInputAttribs[Input_CurSelPos].nch * sizeof (short));
+ if(LineInputAttribs[Input_CurSelPos].nch == 1) {
+ mysamps = Crossfader->get(mybuf, (samps ? sizeof (mybuf) / samps : sizeof (mybuf)) / 2, LineInputAttribs[Input_CurSelPos].nch) * LineInputAttribs[Input_CurSelPos].nch;
+ short *newbuf = (short*) malloc(mysamps * 2 * sizeof(short));
+ interleave_buffer(mybuf, newbuf, mysamps);
+ mysamps *= 2;
+ memcpy(mybuf, newbuf, mysamps * sizeof (short));
+ free(newbuf);
+ } else {
+ mysamps = Crossfader->get(mybuf, (samps ? sizeof (mybuf) / samps : sizeof (mybuf)), LineInputAttribs[Input_CurSelPos].nch) * LineInputAttribs[Input_CurSelPos].nch;
+ }
+ }
+ }
+ ReleaseMutex(cf_mutex);
+ }
+
+ if (mysamps > 0) {
+ short *tmp = mybuf;
+ for (size_t k = 0; k != NUM_ENCODERS; k++) {
+ if (WaitForSingleObject(Enc_mutex[k], INFINITE) == WAIT_OBJECT_0) {
+ size_t size = mysamps * sizeof (short);
+ short * newbuf = (short*) malloc(size);
+ if (newbuf) {
+ memcpy(newbuf, tmp, size);
+ Encoder[k].Run(OM_ENCODE, newbuf, size, k); // this seems to be modifying newbuf
+ free(newbuf);
+ }
+ ReleaseMutex(Enc_mutex[k]);
+ }
+ }
+ }
+ Sleep(25);
+ } while (hthread != NULL);
+ return 0;
+}
+
+DWORD WINAPI ThreadOutput(LPVOID lpParameter) {
+ do {
+ for (int k = 0; k < NUM_ENCODERS; k++) {
+ if (WaitForSingleObject(Enc_mutex[k], 25) == WAIT_OBJECT_0) {
+ if (Encoder[k].GetEncoder()) {
+ Encoder[k].Run(OM_OUTPUT | OM_OTHER);
+ }
+ ReleaseMutex(Enc_mutex[k]);
+ }
+ }
+ Sleep(25);
+ } while (hthreadout != NULL);
+ return 0;
+}
+
+char olddev[256] = {0};
+int DisplayDeviceName(void) {
+ if (InputDevice) {
+ char deviceBuf[256] = {0};
+ char *deviceName = Soundcard.getDeviceName(0);
+
+ if (deviceName && *deviceName) {
+ char tmp[128] = {0};
+ StringCchPrintfA(deviceBuf, ARRAYSIZE(deviceBuf), WASABI_API_LNGSTRING_BUF(IDS_DEVICE_STRING, tmp, 128), deviceName);
+ } else {
+ WASABI_API_LNGSTRING_BUF(IDS_NO_DEVICES_FOUND, deviceBuf, ARRAYSIZE(deviceBuf));
+ olddev[0] = 0;
+ }
+ SendDlgItemMessage(wnd[2].hWnd, IDC_CURDEVICE, WM_SETTEXT, 0,(LPARAM)deviceBuf);
+
+ // vista - 7 check for default device and restart soundcard if needed
+ if (IsVistaUp()) {
+ if (strcmp(deviceBuf, olddev) != 0) {
+ lstrcpyn(olddev, deviceBuf, ARRAYSIZE(olddev));
+ SuspendThread(hthread);
+ Soundcard.Close();
+ Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch));
+ ResumeThread(hthread);
+ ini_modified = 1;
+ }
+ }
+ } else {
+ SendDlgItemMessage(wnd[2].hWnd, IDC_CURDEVICE, WM_SETTEXT, 0,(LPARAM)"");
+ }
+
+ return 1;
+}
+
+#ifdef CAPTURE_TESTING
+//-----------------------------------------------------------
+// The input argument to this function is a pointer to the
+// IMMDevice interface for a capture endpoint device. The
+// function traverses the data path that extends from the
+// endpoint device to the system bus (for example, PCI)
+// or external bus (USB). If the function discovers a MUX
+// (input selector) in the path, it selects the MUX input
+// that connects to the stream from the endpoint device.
+//-----------------------------------------------------------
+#define EXIT_ON_ERROR(hres) \
+ if (FAILED(hres)) { goto Exit; }
+#define SAFE_RELEASE(punk) \
+ if ((punk) != NULL) \
+ { (punk)->Release(); (punk) = NULL; }
+
+const IID IID_IDeviceTopology = __uuidof(IDeviceTopology);
+const IID IID_IPart = __uuidof(IPart);
+const IID IID_IConnector = __uuidof(IConnector);
+const IID IID_IAudioInputSelector = __uuidof(IAudioInputSelector);
+
+HRESULT SelectCaptureDevice(IMMDevice *pEndptDev)
+{
+ HRESULT hr = S_OK;
+ DataFlow flow;
+ IDeviceTopology *pDeviceTopology = NULL;
+ IConnector *pConnFrom = NULL;
+ IConnector *pConnTo = NULL;
+ IPart *pPartPrev = NULL;
+ IPart *pPartNext = NULL;
+ IAudioInputSelector *pSelector = NULL;
+
+ if (pEndptDev == NULL)
+ {
+ EXIT_ON_ERROR(hr = E_POINTER)
+ }
+
+ // Get the endpoint device's IDeviceTopology interface.
+ hr = pEndptDev->Activate(
+ IID_IDeviceTopology, CLSCTX_ALL, NULL,
+ (void**)&pDeviceTopology);
+ EXIT_ON_ERROR(hr)
+
+ // The device topology for an endpoint device always
+ // contains just one connector (connector number 0).
+ hr = pDeviceTopology->GetConnector(0, &pConnFrom);
+ SAFE_RELEASE(pDeviceTopology)
+ EXIT_ON_ERROR(hr)
+
+ // Make sure that this is a capture device.
+ hr = pConnFrom->GetDataFlow(&flow);
+ EXIT_ON_ERROR(hr)
+
+ if (flow != Out)
+ {
+ // Error -- this is a rendering device.
+ EXIT_ON_ERROR(hr = AUDCLNT_E_WRONG_ENDPOINT_TYPE)
+ }
+
+ // Outer loop: Each iteration traverses the data path
+ // through a device topology starting at the input
+ // connector and ending at the output connector.
+ while (TRUE)
+ {
+ BOOL bConnected;
+ hr = pConnFrom->IsConnected(&bConnected);
+ EXIT_ON_ERROR(hr)
+
+ // Does this connector connect to another device?
+ if (bConnected == FALSE)
+ {
+ // This is the end of the data path that
+ // stretches from the endpoint device to the
+ // system bus or external bus. Verify that
+ // the connection type is Software_IO.
+ ConnectorType connType;
+ hr = pConnFrom->GetType(&connType);
+ EXIT_ON_ERROR(hr)
+
+ if (connType == Software_IO)
+ {
+ break; // finished
+ }
+ EXIT_ON_ERROR(hr = E_FAIL)
+ }
+
+ // Get the connector in the next device topology,
+ // which lies on the other side of the connection.
+ hr = pConnFrom->GetConnectedTo(&pConnTo);
+ EXIT_ON_ERROR(hr)
+ SAFE_RELEASE(pConnFrom)
+
+ // Get the connector's IPart interface.
+ hr = pConnTo->QueryInterface(
+ IID_IPart, (void**)&pPartPrev);
+ EXIT_ON_ERROR(hr)
+ SAFE_RELEASE(pConnTo)
+
+ // Inner loop: Each iteration traverses one link in a
+ // device topology and looks for input multiplexers.
+ while (TRUE)
+ {
+ PartType parttype;
+ UINT localId;
+ IPartsList *pParts;
+
+ // Follow downstream link to next part.
+ hr = pPartPrev->EnumPartsOutgoing(&pParts);
+ EXIT_ON_ERROR(hr)
+
+ hr = pParts->GetPart(0, &pPartNext);
+ pParts->Release();
+ EXIT_ON_ERROR(hr)
+
+ hr = pPartNext->GetPartType(&parttype);
+ EXIT_ON_ERROR(hr)
+
+ if (parttype == Connector)
+ {
+ // We've reached the output connector that
+ // lies at the end of this device topology.
+ hr = pPartNext->QueryInterface(
+ IID_IConnector,
+ (void**)&pConnFrom);
+ EXIT_ON_ERROR(hr)
+
+ SAFE_RELEASE(pPartPrev)
+ SAFE_RELEASE(pPartNext)
+ break;
+ }
+
+ // Failure of the following call means only that
+ // the part is not a MUX (input selector).
+ hr = pPartNext->Activate(
+ CLSCTX_ALL,
+ IID_IAudioInputSelector,
+ (void**)&pSelector);
+ if (hr == S_OK)
+ {
+ // We found a MUX (input selector), so select
+ // the input from our endpoint device.
+ hr = pPartPrev->GetLocalId(&localId);
+ EXIT_ON_ERROR(hr)
+
+ hr = pSelector->SetSelection(localId, NULL);
+ EXIT_ON_ERROR(hr)
+
+ SAFE_RELEASE(pSelector)
+ }
+
+ SAFE_RELEASE(pPartPrev)
+ pPartPrev = pPartNext;
+ pPartNext = NULL;
+ }
+ }
+
+Exit:
+ SAFE_RELEASE(pConnFrom)
+ SAFE_RELEASE(pConnTo)
+ SAFE_RELEASE(pPartPrev)
+ SAFE_RELEASE(pPartNext)
+ SAFE_RELEASE(pSelector)
+ return hr;
+}
+#endif
+
+void setlev(int cs, int va) {
+ if (IsVistaUp() &&
+ (cs == MIXERLINE_COMPONENTTYPE_SRC_LINE || cs == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)) {
+ //HRESULT hr = S_OK;
+ IMMDeviceEnumerator *pEnumerator = NULL;
+ IMMDevice *pDevice = NULL;
+ IMMDeviceCollection *ppDevices = NULL;
+ //hr = CoCreateGuid(&g_guidMyContext);
+ //EXIT_ON_ERROR(hr)
+
+ // Get enumerator for audio endpoint devices.
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&pEnumerator);
+ EXIT_ON_ERROR(hr)
+
+ hr = pEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &ppDevices);
+ EXIT_ON_ERROR(hr)
+
+ hr = ppDevices->Item(Input_Device_ID, &pDevice);
+ EXIT_ON_ERROR(hr)
+
+ //activate
+ hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&g_pEndptVol);
+ EXIT_ON_ERROR(hr)
+
+ //set mic volume
+ float fVolume = (float)(va / 100.0f);
+ if (va > 2) {
+ hr = g_pEndptVol->SetMasterVolumeLevelScalar(fVolume, NULL/*&g_guidMyContext*/);
+ EXIT_ON_ERROR(hr)
+ } else {//mute
+ hr = g_pEndptVol->SetMasterVolumeLevelScalar((float)0.0f, NULL/*&g_guidMyContext*/);
+ }
+
+ /*hr = pDevice->Activate(__uuidof(IAudioStreamVolume ), CLSCTX_ALL, NULL, (void**)&g_pStreamVol);
+ EXIT_ON_ERROR(hr)
+
+ g_pStreamVol->SetChannelVolume(0, fVolume);
+ g_pStreamVol->SetChannelVolume(1, fVolume);
+
+ IMMDevice *pDefaultDevice = NULL;
+ hr = pEnumerator->GetDefaultAudioEndpoint(eCapture,eConsole,&pDefaultDevice);
+ EXIT_ON_ERROR(hr)
+
+ hr = pDefaultDevice->Activate(__uuidof(IAudioStreamVolume ), CLSCTX_ALL, NULL, (void**)&g_pStreamVol);
+ EXIT_ON_ERROR(hr)
+
+ g_pStreamVol->SetChannelVolume(0, fVolume);
+ g_pStreamVol->SetChannelVolume(1, fVolume);*/
+
+Exit:
+ SAFE_RELEASE(pEnumerator)
+ SAFE_RELEASE(pDevice)
+ SAFE_RELEASE(ppDevices)
+ SAFE_RELEASE(g_pEndptVol)
+ CoUninitialize();
+ } // end if mic
+
+ for (UINT i = 0; i < (IsVistaUp() ? mixerGetNumDevs() : 1); i++) {
+ HMIXER hmix;
+ #ifdef FOLLOW_MIXER
+ // TODO use a different handle??
+ mixerOpen(&hmix, i, (DWORD_PTR)hMainDLG, 0, MIXER_OBJECTF_MIXER|CALLBACK_WINDOW);
+ #endif
+ if (mixerOpen(&hmix, i, (DWORD_PTR)hMainDLG, 0, MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) {
+ MIXERLINE ml = {sizeof (ml), 0};
+ ml.dwComponentType = cs;
+ if (mixerGetLineInfo((HMIXEROBJ) hmix, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) {
+ MIXERLINECONTROLS mlc = {sizeof (mlc), ml.dwLineID,};
+ MIXERCONTROL mc = {sizeof (mc),};
+ mlc.cControls = 1;
+ mlc.cbmxctrl = sizeof (mc);
+ mlc.pamxctrl = &mc;
+ mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
+ if (mixerGetLineControls((HMIXEROBJ) hmix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) {
+ MIXERCONTROLDETAILS mcd = {sizeof (mcd), mc.dwControlID, ml.cChannels,};
+ MIXERCONTROLDETAILS_UNSIGNED v[2];
+ mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
+ mcd.paDetails = v;
+ v[0].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100;
+ v[1].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100;
+ /*MMRESULT result = */mixerSetControlDetails((HMIXEROBJ) hmix, &mcd, MIXER_OBJECTF_MIXER);
+ }
+ }
+ mixerClose(hmix);
+ }
+ }
+
+ DisplayDeviceName();
+}
+
+int SetDeviceName(void) {
+ HRESULT hr = S_OK;
+ IMMDeviceEnumerator *pEnumerate = NULL;
+ IMMDevice *pDevice = NULL;
+ IMMDevice *pDefaultDevice = NULL;
+ IMMDeviceCollection *ppDevices = NULL;
+ IPropertyStore *pProps = NULL;
+ PROPVARIANT varName;
+
+ if (IsVistaUp()) {
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ PropVariantInit(&varName);
+ // Get enumerator for audio endpoint devices.
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&pEnumerate);
+ EXIT_ON_ERROR(hr)
+
+ hr = pEnumerate->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &ppDevices);
+ EXIT_ON_ERROR(hr)
+
+ numInputs = 0;
+ hr = ppDevices->GetCount(&numInputs);
+ EXIT_ON_ERROR(hr)
+
+ // treat this as a dummy stop
+ Exit:;
+ }
+
+ int oldCount = SendDlgItemMessage(inWin, IDC_DEVBOX, CB_GETCOUNT, 0, 0);
+ SendDlgItemMessage(inWin, IDC_DEVBOX, CB_RESETCONTENT, 0,0);
+ EnableWindowDlgItem(inWin, IDC_REFRESH_DEVICES, IsVistaUp());
+
+ if (!IsVistaUp()) {//change back to true when vista enabled !
+ SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_MIC_LEGACY_MODE, NULL, 0));
+ SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_LINEIN_LEGACY_MODE, NULL, 0));
+ } else {
+ hr = pEnumerate->GetDefaultAudioEndpoint(eCapture, eConsole, &pDefaultDevice);
+ if (SUCCEEDED(hr) && pDefaultDevice != NULL) {
+ LPWSTR defaultName = NULL;
+ pDefaultDevice->GetId(&defaultName);
+
+ //jkey: This is for vista or 7, so we scan through and add friendly device names
+ // though need to make sure that we don't re-add the current input device
+ // otherwise with the waveout fudge we'll get really bad feedback on output
+ for (unsigned int i=0; i < numInputs; i++) {
+ LPWSTR itemName = NULL;
+ ppDevices->Item(i, &pDevice);
+ pDevice->GetId(&itemName);
+ // check the id of the endpoints to prevent adding in the default output device
+ if (defaultName && wcsicmp(itemName, defaultName)) {
+ pDevice->OpenPropertyStore(STGM_READ, &pProps);
+ pProps->GetValue(PKEY_Device_FriendlyName, &varName);
+ SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0,(LPARAM)varName.pwszVal);
+ SendDlgItemMessage(inWin, IDC_DEVBOX, CB_SETITEMDATA, i,(LPARAM)i);
+ }
+ CoTaskMemFree(itemName);
+ }
+ CoTaskMemFree(defaultName);
+ }
+
+ int count = SendDlgItemMessage(inWin, IDC_DEVBOX, CB_GETCOUNT, 0, 0);
+ if (!count) {
+ SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_NO_CAPTURE_DEVICES));
+ }
+ EnableWindowDlgItem(inWin, IDC_DEVBOX, count);
+ // reset to the first item in the list if there's any changes
+ if (!oldCount || count != oldCount) {
+ SendDlgItemMessage(inWin, IDC_DEVBOX, CB_SETCURSEL, 0, 0);
+ }
+
+ PropVariantClear(&varName);
+ SAFE_RELEASE(pProps)
+ SAFE_RELEASE(pEnumerate)
+ SAFE_RELEASE(pDevice)
+ SAFE_RELEASE(ppDevices)
+ CoUninitialize();
+ }
+
+ SendDlgItemMessage(inWin, IDC_DEVBOX, CB_SETCURSEL, Input_Device_ID, 0);
+ DisplayDeviceName();
+ return 1;
+}
+
+/*int getlev(int cs) {
+ HMIXER hmix;
+ int retval = -1;
+#ifdef USE_VISTA_SOUND_FIX
+ HRESULT hr = S_OK;
+ IMMDeviceEnumerator *pEnumerator = NULL;
+ IMMDevice *pDevice = NULL;
+ IMMDeviceCollection *ppDevices = NULL;
+ hr = CoCreateGuid(&g_guidMyContext);
+ EXIT_ON_ERROR(hr)
+
+ // Get enumerator for audio endpoint devices.
+ CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+ hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
+ NULL, CLSCTX_INPROC_SERVER,
+ __uuidof(IMMDeviceEnumerator),
+ (void**)&pEnumerator);
+ EXIT_ON_ERROR(hr)
+
+ hr = pEnumerator->EnumAudioEndpoints(eCapture,DEVICE_STATE_ACTIVE,&ppDevices);
+ EXIT_ON_ERROR(hr)
+
+ hr = ppDevices->Item(Input_Device_ID,&pDevice);
+ EXIT_ON_ERROR(hr)
+
+ //activate
+ hr = pDevice->Activate(__uuidof(IAudioEndpointVolume),
+ CLSCTX_ALL, NULL, (void**)&g_pEndptVol);
+ EXIT_ON_ERROR(hr)
+ //set mic volume
+ float fVolume =0.0;
+ hr = g_pEndptVol->GetMasterVolumeLevel(&fVolume);
+ EXIT_ON_ERROR(hr)
+
+Exit:
+ if (FAILED(hr)) {
+ useXpSound = true;
+ } else {
+ retval = (int)fVolume * 100;
+ return retval;
+ }
+
+ SAFE_RELEASE(pEnumerator)
+ SAFE_RELEASE(pDevice)
+ SAFE_RELEASE(ppDevices)
+ SAFE_RELEASE(g_pEndptVol)
+ CoUninitialize();
+
+#endif //USE_VISTA_SOUND_FIX
+ if (mixerOpen(&hmix, 0, 0, 0, 0) == MMSYSERR_NOERROR) {
+ MIXERLINE ml = {sizeof (ml), 0};
+ ml.dwComponentType = cs;
+ if (mixerGetLineInfo((HMIXEROBJ) hmix, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR) {
+ MIXERLINECONTROLS mlc = {sizeof (mlc), ml.dwLineID,};
+ MIXERCONTROL mc = {sizeof (mc),};
+ mlc.cControls = 1;
+ mlc.cbmxctrl = sizeof (mc);
+ mlc.pamxctrl = &mc;
+ mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
+ if (mixerGetLineControls((HMIXEROBJ) hmix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR) {
+ MIXERCONTROLDETAILS mcd = {sizeof (mcd), mc.dwControlID, ml.cChannels,};
+ MIXERCONTROLDETAILS_UNSIGNED v[2];
+ mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
+ mcd.paDetails = v;
+ if (mixerGetControlDetails((HMIXEROBJ) hmix, &mcd, 0) == MMSYSERR_NOERROR) {
+ retval = (v[0].dwValue * 100) / (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum);
+ // retval = ((v[0].dwValue + v[1].dwValue) * 5) / (mc.Bounds.dwMaximum-mc.Bounds.dwMinimum);
+ }
+ }
+ }
+ mixerClose(hmix);
+ }
+ return retval;
+}*/
+
+void TitleCallback(int Connection, int Mode) {
+ MY_T_OUTPUT *Out = &Output[Connection];
+ // title update
+ if (Mode == 0) {
+ if (Out->AutoTitle == 1) {
+ // look at the playback queue so we can get the correct 'next song'
+ if (!WASABI_API_QUEUEMGR) {
+ // due to loading orders its possible the queue won't have been loaded on init so check
+ ServiceBuild(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID);
+ }
+
+ std::vector<std::wstring> nextList;
+ nextList.clear();
+ Encoder[Out->Encoder].UpdateTitle(0, nextList, Out->Handle, !!Out->NextTitles);
+ }
+ } else if (Mode == 1) {
+ // album art update
+ Encoder[Out->Encoder].UpdateAlbumArt(Out->Handle);
+ }
+}
+
+void CenterWindow(void) {
+ RECT rect, rectP;
+ int width, height;
+ int screenwidth, screenheight;
+ int x, y;
+
+ GetWindowRect(hMainDLG, &rect);
+ GetWindowRect(GetDesktopWindow(), &rectP);
+
+ width = rect.right - rect.left;
+ height = rect.bottom - rect.top;
+
+ x = ((rectP.right-rectP.left) - width) / 2 + rectP.left;
+ y = ((rectP.bottom-rectP.top) - height) / 2 + rectP.top;
+
+ screenwidth = GetSystemMetrics(SM_CXSCREEN);
+ screenheight = GetSystemMetrics(SM_CYSCREEN);
+
+ if (x < 0) x = 0;
+ if (y < 0) y = 0;
+ if (x + width > screenwidth) x = screenwidth - width;
+ if (y + height > screenheight) y = screenheight - height;
+
+ mainrect.left = x;
+ mainrect.top = y;
+}
+
+int LoadConfig(void) {
+ lookAhead = GetPrivateProfileInt(APP_Name, "lookAhead", lookAhead, IniName);
+ skipMetada = !!GetPrivateProfileInt(APP_Name, "skipMetada", skipMetada, IniName);
+ lastFilterIndex = GetPrivateProfileInt(APP_Name, "ofnidx", lastFilterIndex, IniName);
+
+ curtab = GetPrivateProfileInt(APP_Name, "CurTab", curtab, IniName);
+ Connection_CurSelPos = GetPrivateProfileInt(APP_Name, "Connection_CurSelPos", Connection_CurSelPos, IniName);
+ curouttab = GetPrivateProfileInt(APP_Name, "Connection_CurTab", curouttab, IniName);
+ Encoder_CurSelPos = GetPrivateProfileInt(APP_Name, "Encoder_CurSelPos", Encoder_CurSelPos, IniName);
+ InputDevice = GetPrivateProfileInt(APP_Name, "InputDevice", InputDevice, IniName);
+
+ Input_CurSelPos = GetPrivateProfileInt(APP_Name, "Input_CurSelPos", Input_CurSelPos, IniName);
+ InputConfig.srate = LineInputAttribs[3].srate;
+ InputConfig.nch = LineInputAttribs[3].nch;
+ cf_mutex = CreateMutex(NULL, TRUE, NULL);
+ Crossfader = new C_CROSSFADER(CrossfadeLen,
+ LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].nch,
+ LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].srate);
+
+ MusVol = GetPrivateProfileInt(APP_Name, "MusicVolume", MusVol, IniName);
+ Mus2Vol = GetPrivateProfileInt(APP_Name, "BGMusicVolume", Mus2Vol, IniName);
+ MicVol = GetPrivateProfileInt(APP_Name, "MicVolume", MicVol, IniName);
+
+ // as we've changed the scaling then we will need to adjust from old to new
+ int tempFadeTime = GetPrivateProfileInt(APP_Name, "PTT_FadeTime", -1, IniName);
+ if (tempFadeTime == -1) {
+ FadeTime = GetPrivateProfileInt(APP_Name, "PTT_FT", FadeTime, IniName);
+ } else {
+ FadeTime = tempFadeTime * 5;
+ // remove the old instance of the settings
+ WritePrivateProfileString(APP_Name, "PTT_FadeTime", 0, IniName);
+ }
+
+ int tempMicFadeTime = GetPrivateProfileInt(APP_Name, "PTT_MicFadeTime", -1, IniName);
+ if (tempMicFadeTime == -1) {
+ MicFadeTime = GetPrivateProfileInt(APP_Name, "PTT_MicFT", MicFadeTime, IniName);
+ } else {
+ MicFadeTime = tempMicFadeTime * 5;
+ // remove the old instance of the settings
+ WritePrivateProfileString(APP_Name, "PTT_MicFadeTime", 0, IniName);
+ }
+
+ Restore_PTT = GetPrivateProfileInt(APP_Name, "PTT_Restore", 0, IniName);
+ Input_Device_ID = GetPrivateProfileInt(APP_Name, "PTT_MicInput",0, IniName);
+
+ // align to middle of screen on new installs
+ CenterWindow();
+ mainrect.left = GetPrivateProfileInt(APP_Name, "WindowLeft", mainrect.left, IniName);
+ mainrect.top = GetPrivateProfileInt(APP_Name, "WindowTop", mainrect.top, IniName);
+
+ GetPrivateProfileString(APP_Name, "Update", 0, updateStr, 16, IniName);
+ // no point in indicating a new version if we're now showing as ahead
+ if (!CompareVersions(updateStr)) {
+ WritePrivateProfileString(APP_Name, "Update", 0, IniName);
+ updateStr[0] = 0;
+ }
+
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ T_OUTPUT_CONFIG *Out = &Output[i].Config;
+ StringCchPrintfA(Out->Name, 32, "Output %u", i + 1);
+ StringCchPrintfW(Out->DisplayName, 32, WASABI_API_LNGSTRINGW(IDS_OUTPUT_X), i + 1);
+ Output[i].Encoder = -1;
+ Output[i].Handle = -1;
+ GetPrivateProfileString(Out->Name, "Address", "localhost", Out->Address, ARRAYSIZE(Out->Address), IniName);
+ GetPrivateProfileString(Out->Name, "UserID", "", Out->UserID, ARRAYSIZE(Out->UserID), IniName);
+ GetPrivateProfileString(Out->Name, "StreamID", "1", Out->StationID, ARRAYSIZE(Out->StationID), IniName);
+ Out->Port = GetPrivateProfileInt(Out->Name, "Port", 8000, IniName);
+ GetPrivateProfileString(Out->Name, "Password", "", Out->Password, ARRAYSIZE(Out->Password), IniName);
+ GetPrivateProfileString(Out->Name, "Cipherkey", "foobar", Out->cipherkey, ARRAYSIZE(Out->cipherkey), IniName);
+ GetPrivateProfileString(Out->Name, "Description", "Unnamed Server", Out->Description, ARRAYSIZE(Out->Description), IniName);
+ GetPrivateProfileString(Out->Name, "URL", "http://www.shoutcast.com", Out->ServerURL, ARRAYSIZE(Out->ServerURL), IniName);
+ GetPrivateProfileString(Out->Name, "Genre3", "Misc", Out->Genre, ARRAYSIZE(Out->Genre), IniName);
+ // check that the genre is a support value otherwise reset it to 'misc'
+ bool foundGenre = false;
+ for (int g = 0; g < ARRAYSIZE(genres); g++) {
+ if (!strcmpi(genres[g].name, Out->Genre)) {
+ foundGenre = true;
+ break;
+ }
+ }
+ if (foundGenre == false) {
+ lstrcpyn(Out->Genre, "Misc", ARRAYSIZE(Out->Genre));
+ }
+
+ GetPrivateProfileString(Out->Name, "AIM", "N/A", Out->AIM, ARRAYSIZE(Out->AIM), IniName);
+ GetPrivateProfileString(Out->Name, "ICQ", "0", Out->ICQ, ARRAYSIZE(Out->ICQ), IniName);
+ GetPrivateProfileString(Out->Name, "IRC", "N/A", Out->IRC, ARRAYSIZE(Out->IRC), IniName);
+ Out->Public = GetPrivateProfileInt(Out->Name, "Public", 1, IniName);
+ Out->AutoRecon = GetPrivateProfileInt(Out->Name, "AutoRecon", 1, IniName);
+ Out->ReconTime = GetPrivateProfileInt(Out->Name, "ReconTime", 5, IniName);
+ if (Out->ReconTime < 1) {
+ Out->ReconTime = 5;
+ }
+ Out->doTitleUpdate = GetPrivateProfileInt(Out->Name, "doTitleUpdate", 1, IniName);
+ GetPrivateProfileString(Out->Name, "now", "", Out->Now, ARRAYSIZE(Out->Now), IniName);
+ GetPrivateProfileString(Out->Name, "next", "", Out->Next, ARRAYSIZE(Out->Next), IniName);
+ Output[i].AutoTitle = GetPrivateProfileInt(Out->Name, "AutoTitle", 1, IniName);
+ Output[i].AutoConnect = GetPrivateProfileInt(Out->Name, "AutoConnect", 0, IniName);
+ Output[i].Logging = GetPrivateProfileInt(Out->Name, "Logging", 0, IniName);
+ Output[i].LogCOS = GetPrivateProfileInt(Out->Name, "LogCOS", 0, IniName);
+ Output[i].NextTitles = GetPrivateProfileInt(Out->Name, "NextTitles", 1, IniName);
+ Output[i].Config.protocol = GetPrivateProfileInt(Out->Name, "protocol", -1, IniName);
+ if (Output[i].Config.protocol == -1) {
+ Output[i].Config.protocol = MAKEWORD(2, 1);
+ }
+ // check the v1 password for : and split it if the dj/user id is empty (i.e. post 2.2.3 import)
+ if (LOBYTE(Output[i].Config.protocol) == 1 && Out->Password[0] && !Out->UserID[0]) {
+ char* password = strstr(Out->Password, ":");
+ if (password) {
+ *password = 0;
+ lstrcpyn(Out->UserID, Out->Password, ARRAYSIZE(Out->UserID));
+ lstrcpyn(Out->Password, ++password, ARRAYSIZE(Out->Password));
+ }
+ }
+ Output[i].nextTrackLog = GetPrivateProfileInt(Out->Name, "nextTrackLog", 0, IniName);
+ Output[i].nextTrackLogXML = GetPrivateProfileInt(Out->Name, "nextTrackLogXML", 0, IniName);
+ if (!GetPrivateProfileStringUTF8(Out->Name, "nextTrackPath", 0, Output[i].nextTrackPath, ARRAYSIZE(Output[i].nextTrackPath), IniName)) {
+ GetDefaultNextTracksLogFile(module.hwndParent, ARRAYSIZE(Output[i].nextTrackPath), Output[i].nextTrackPath, i);
+ }
+
+ Output[i].useArt = GetPrivateProfileInt(Out->Name, "useArt", 0, IniName);
+ Output[i].usePlayingArt = GetPrivateProfileInt(Out->Name, "usePlayingArt", 0, IniName);
+ Output[i].useStreamArt = GetPrivateProfileInt(Out->Name, "useStreamArt", 1, IniName);
+ GetPrivateProfileStringUTF8(Out->Name, "stationArtPath", 0, Output[i].stationArtPath, ARRAYSIZE(Output[i].stationArtPath), IniName);
+
+ Output[i].saveEncoded = GetPrivateProfileInt(Out->Name, "saveEncoded", 0, IniName);
+ GetPrivateProfileStringUTF8(Out->Name, "saveEncodedPath", 0, Output[i].saveEncodedPath, ARRAYSIZE(Output[i].saveEncodedPath), IniName);
+
+ Output[i].Encoder = GetPrivateProfileInt(Out->Name, "Encoder", i == 0 ? 0 : -1, IniName);
+ if (Output[i].Encoder != -1) {
+ if (WaitForSingleObject(Enc_mutex[Output[i].Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Output[i].Handle = Encoder[Output[i].Encoder].AddOutput(i, Out, TitleCallback);
+ ReleaseMutex(Enc_mutex[Output[i].Encoder]);
+ }
+ }
+ }
+ return 1;
+}
+
+wchar_t* BuildLameVersion(void) {
+ static wchar_t version[128] = {0};
+ if (libinst != NULL && !version[0]) {
+ BE_VERSION ver;
+ beVersion(&ver);
+
+ if (ver.byBetaLevel) {
+ StringCchPrintfW(version, ARRAYSIZE(version), L"%u.%ub%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion, (unsigned int)ver.byBetaLevel);
+ } else if (ver.byAlphaLevel) {
+ StringCchPrintfW(version, ARRAYSIZE(version), L"%u.%ua%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion, (unsigned int)ver.byAlphaLevel);
+ } else {
+ StringCchPrintfW(version, ARRAYSIZE(version), L"%u.%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion);
+ }
+ }
+ return version;
+}
+
+void LoadEncoders() {
+ /* load lame_enc.dll */
+ wchar_t dllname[MAX_PATH] = {0};
+ StringCchPrintfW(dllname, ARRAYSIZE(dllname), L"%s\\lame_enc.dll", GetSharedDirectoryW(module.hwndParent));
+
+ libinst = LoadLibraryW(dllname);
+ if (libinst == NULL) {
+ wchar_t title[128] = {0}, message[512] = {0};
+ StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_VersionW);
+ StringCchPrintfW(message, ARRAYSIZE(message), LocalisedString(IDS_FAILED_LOAD_LAMEDLL, NULL, 0), GetSharedDirectoryW(module.hwndParent));
+ MessageBoxW(module.hwndParent, message, title, MB_ICONWARNING);
+ } else {
+ beVersion = (BEVERSION) GetProcAddress(libinst, "beVersion");
+ init = (void *) GetProcAddress(libinst, "lame_init");
+ params = (void *) GetProcAddress(libinst, "lame_init_params");
+ encode = (void *) GetProcAddress(libinst, "lame_encode_buffer_interleaved");
+ finish = (void *) GetProcAddress(libinst, "lame_encode_flush");
+
+ if (!init || !params || !encode || !finish) {
+ wchar_t title[128] = {0};
+ StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_VersionW);
+ MessageBoxW(module.hwndParent, LocalisedString(IDS_LAMEDLL_ISSUE, NULL, 0), title, MB_ICONWARNING);
+ FreeLibrary(libinst);
+ libinst = 0;
+ }
+ }
+
+ /* load encoder type */
+ for (int i = 0; i < NUM_ENCODERS; i++) {
+ char name[32] = {0};
+ Enc_mutex[i] = CreateMutex(NULL, TRUE, NULL);
+ StringCchPrintfA(name, ARRAYSIZE(name), "Encoder %u", i + 1);
+ int EncType = GetPrivateProfileInt(name, "Type", 2, IniName);
+ // store our type for later on
+ Enc_LastType[i] = EncType;
+ switch (EncType) {
+ /* mp3 */
+ case 1:
+ {
+ fallback:
+ Encoder[i].SetLame(init, params, encode, finish);
+ Encoder[i].SetEncoder(DEFAULT_ENCODER);
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) {
+ T_ENCODER_MP3_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_MP3_INFO *encset = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ EncSettings.output_bitRate = GetPrivateProfileInt(name, "BitRate", EncSettings.output_bitRate, IniName);
+ EncSettings.output_sampleRate = GetPrivateProfileInt(name, "SampleRate", EncSettings.output_sampleRate, IniName);
+ EncSettings.output_numChannels = GetPrivateProfileInt(name, "NumChannels", EncSettings.output_numChannels, IniName);
+ EncSettings.QualityMode = GetPrivateProfileInt(name, "QualityMode", 8, IniName);
+ Enc->ChangeSettings(&EncSettings);
+ }
+ }
+ }
+ break;
+
+ case 2:
+ // map any AAC LC from the prior versions to FHG AAC with 5.62+
+ case 3:
+ { // FHG AAC
+ if (C_ENCODER_FHGAAC::isPresent(module.hwndParent)) {
+ Encoder[i].SetEncoder(new C_ENCODER_FHGAAC(module.hwndParent), 1);
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ T_ENCODER_FHGAAC_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_FHGAAC_INFO *encset = (T_ENCODER_FHGAAC_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ Enc->ChangeSettings(&EncSettings);
+ ((C_ENCODER_NSV*) Enc)->ReadConfFile(IniName, name);
+ }
+ } else if (C_ENCODER_AACP::isPresent(module.hwndParent)) { // AAC+
+ Encoder[i].SetEncoder(new C_ENCODER_AACP(module.hwndParent), 1);
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ T_ENCODER_AACP_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_AACP_INFO *encset = (T_ENCODER_AACP_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ Enc->ChangeSettings(&EncSettings);
+ ((C_ENCODER_NSV*) Enc)->ReadConfFile(IniName, name);
+ }
+ } else {
+ //Encoder[i].SetEncoder(NULL);
+ // attempt to get to a valid encoder if the aac one disappeared
+ goto fallback;
+ }
+ }
+ break;
+#ifdef USE_OGG
+ case 4:
+ { // OGG
+ if (C_ENCODER_OGG::isPresent(module.hwndParent)) {
+ Encoder[i].SetEncoder(new C_ENCODER_OGG(module.hwndParent), 1);
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ T_ENCODER_OGG_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_OGG_INFO *encset = (T_ENCODER_OGG_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ Enc->ChangeSettings(&EncSettings);
+ ((C_ENCODER_NSV*) Enc)->ReadConfFile(IniName, name);
+ }
+ } else Encoder[i].SetEncoder(NULL);
+ }
+ break;
+#endif // USE_OGG
+ default:
+ {
+ Encoder[i].SetEncoder(NULL);
+ }
+ break;
+ }
+ ReleaseMutex(Enc_mutex[i]);
+ }
+}
+
+void SetEncoderPanelMode(HWND hDlg, C_ENCODER * Enc) {
+ BOOL show = (Enc != NULL);
+ if (show) {
+ if (Enc->UseNsvConfig() == true) {
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS, SW_HIDE);
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON, SW_SHOW);
+ wchar_t tmp[128] = {0};
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_CURRENT_BITRATE, NULL, 0), ((T_EncoderIOVals*) Enc->GetExtInfo())->output_bitRate);
+ SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LABEL, tmp);
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_LAME_VER, SW_HIDE);
+ } else {
+ wchar_t *lame_version = BuildLameVersion();
+ if (lame_version && *lame_version)
+ {
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS, SW_SHOW);
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON, SW_HIDE);
+ SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LABEL, LocalisedString(IDS_ENCODER_SETTINGS, NULL, 0));
+
+ wchar_t tmp[128] = {0};
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_LAME_ENCODER_VER, NULL, 0), lame_version);
+ SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LAME_VER, tmp);
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_LAME_VER, SW_SHOW);
+ } else {
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS, SW_HIDE);
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON, SW_HIDE);
+ SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LABEL, LocalisedString(IDS_MP3_ENCODING_NOT_AVAILABLE, NULL, 0));
+ }
+ }
+ } else {
+ ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_LAME_VER, SW_HIDE);
+ }
+
+ // show / hide the groupbox around the main encoder options
+ // which is setup to make it look like it's only around the
+ // options available depending upon the mode that is in use
+ ShowWindowDlgItem(hDlg, IDC_INFO_FRAME4, !show);
+ ShowWindowDlgItem(hDlg, IDC_INFO_FRAME5, show);
+
+ // show / hide the save encoded audio options as applicable
+ ShowWindowDlgItem(hDlg, IDC_INFO_FRAME3, show);
+ ShowWindowDlgItem(hDlg, IDC_SAVE_ENCODED_AUDIO, show);
+ ShowWindowDlgItem(hDlg, IDC_SAVE_ENCODED_AUDIO_EDIT, show);
+ ShowWindowDlgItem(hDlg, IDC_SAVE_ENCODED_AUDIO_BROWSE, show);
+}
+
+void FreeStreamAlbumArt(int Index) {
+ if (streamImage[Index] > (ARGB32 *)0 &&
+ streamImage[Index] != (ARGB32 *)-1 &&
+ WASABI_API_MEMMGR) {
+ WASABI_API_MEMMGR->sysFree(streamImage[Index]);
+ streamImage[Index] = (ARGB32 *)-1;
+ }
+ streamLength[Index] = 0;
+}
+
+// destroy dlg and exit
+int doQuit(void) {
+ if (nowPlayingHook) {
+ UnhookWindowsHookEx(nowPlayingHook);
+ nowPlayingHook = 0;
+ }
+ if (nowPlayingHook2) {
+ UnhookWindowsHookEx(nowPlayingHook2);
+ nowPlayingHook2 = 0;
+ }
+
+ RemoveSystrayIcon(hMainDLG, SYSTRAY_ICY_ICON);
+
+ GetWindowRect(hMainDLG, &mainrect);
+
+ KillTimer(hMainDLG, wnd[curtab].id);
+ KillTimer(hMainDLG, IDD_ENCODER);
+ KillTimer(hMainDLG, 666);
+ KillTimer(hMainDLG, 1234);
+ KillTimer(hMainDLG, 1337);
+ KillTimer(hMainDLG, 2234);
+ KillTimer(hMainDLG, 2235);
+ if (curtab == 1) KillTimer(wnd[curtab].hWnd, out_wnd[curouttab].id);
+ if (curtab == 2) KillTimer(wnd[curtab].hWnd, in_wnd[InputDevice].id);
+ ini_modified = 1;
+ /* Disconnect all outputs */
+ int done;
+ do {
+ done = 1;
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ MY_T_OUTPUT *Out = &Output[i];
+ if (Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ int state = Enc->GetState(Out->Handle);
+ if (state != OUT_DISCONNECTED && state != OUT_ERROR) {
+ done = 0;
+ Enc->DisconnectOutput(Out->Handle);
+ } else {
+ // shutdown the logging options
+ if (Out->Logging) {
+ StopLogging(i);
+ }
+ if (Out->nextTrackLog) {
+ StopNextTracks(i);
+ }
+ if (Out->saveEncoded) {
+ StopSaveEncoded(i);
+ }
+ }
+ Enc->Run(OM_OUTPUT | OM_OTHER);
+ }
+ }
+ Sleep(200);
+ } while (!done);
+
+ // reset levels if PTT is enabled when we are closing
+ if (FadeOut && InputDevice) {
+ int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
+ setlev(micsrc, 0);
+ setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, MusVol * 10);
+ }
+
+ if (hthread) {
+ CloseHandle(hthread);
+ hthread = NULL;
+ }
+
+ if (hthreadout) {
+ CloseHandle(hthreadout);
+ hthreadout = NULL;
+ }
+
+ ReleaseMutex(cf_mutex);
+ Soundcard.Close();
+
+ SendMessage(hMainDLG, WM_TIMER, MAKEWPARAM(666,0), 0); // force an INI save
+
+ if (playingImage && WASABI_API_MEMMGR) {
+ WASABI_API_MEMMGR->sysFree(playingImage);
+ playingImage = 0;
+ }
+
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ FreeStreamAlbumArt(i);
+ }
+
+ if (libinst) {
+ FreeLibrary(libinst);
+ libinst = 0;
+ }
+ C_ENCODER_FHGAAC::Unload();
+ C_ENCODER_AACP::Unload();
+#ifdef USE_OGG
+ C_ENCODER_OGG::Unload();
+#endif // USE_OGG
+
+ for (int i = 0; i < num_tabwnds; i++) DestroyWindow(wnd[i].hWnd);
+ for (int i = 0; i < num_inwnds; i++) DestroyWindow(in_wnd[i].hWnd);
+ for (int i = 0; i < num_outwnds; i++) DestroyWindow(out_wnd[i].hWnd);
+
+ for (int ii = 0; ii < NUM_OUTPUTS; ii++) {
+ MY_T_OUTPUT *Out = &Output[ii];
+ // removed encoder selection
+ if (Out->Encoder != -1) {
+ if (Out->Handle != -1) {
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[Out->Encoder].RemoveOutput(Out->Handle);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ }
+ }
+
+ DestroyWindow(hMainDLG);
+ hMainDLG = NULL;
+
+ num_cols = num_outwnds = num_inwnds = num_tabwnds = 0;
+ memset(&col, 0, sizeof(col));
+ memset(&wnd, 0, sizeof(wnd));
+ memset(&out_wnd, 0, sizeof(out_wnd));
+ memset(&in_wnd, 0, sizeof(in_wnd));
+ memset(&Output, 0, sizeof(Output));
+ WASABI_API_LNG_HINST = WASABI_API_ORIG_HINST = 0;
+
+ memset(&lastFile, 0, sizeof(lastFile));
+ memset(&lastSec, 0, sizeof(lastSec));
+ memset(&lastFile, 0, sizeof(lastFile));
+ memset(&lastMode, -1, sizeof(lastMode));
+ memset(&lastEnable, 0, sizeof(lastEnable));
+ memset(&buttonWnd, 0, sizeof(buttonWnd));
+ memset(&tabWnd, 0, sizeof(tabWnd));
+ memset(&outTabWnd, 0, sizeof(outTabWnd));
+ memset(&streamLength, 0, sizeof(streamLength));
+ memset(&secChanged, 0, sizeof(secChanged));
+
+ playingImage_w = playingImage_h = playingLength = playingType;
+
+ if (boldFont) {
+ DeleteObject(boldFont);
+ boldFont = NULL;
+ }
+ normalFont = NULL;
+
+ ServiceRelease(WASABI_API_SVC, AGAVE_API_CONFIG, AgaveConfigGUID);
+ ServiceRelease(WASABI_API_SVC, WASABI_API_LNG, languageApiGUID);
+ ServiceRelease(WASABI_API_SVC, WASABI_API_MEMMGR, memMgrApiServiceGuid);
+ ServiceRelease(WASABI_API_SVC, AGAVE_API_ALBUMART, albumArtGUID);
+ ServiceRelease(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID);
+ ServiceRelease(WASABI_API_SVC, WASABI_API_EXPLORERFINDFILE,ExplorerFindFileApiGUID);
+ ServiceRelease(WASABI_API_SVC, WAC_API_DOWNLOADMANAGER, DownloadManagerGUID);
+ WASABI_API_SVC = NULL;
+
+ return 1;
+}
+
+void SetBoldDialogItemFont(HWND hwndControl) {
+ if (!boldFont) {
+ HFONT hFont = (HFONT)SendMessageW(hMainDLG, WM_GETFONT, 0, 0);
+ LOGFONTW lf = {0};
+ GetObjectW(hFont, sizeof(LOGFONTW), &lf);
+ lf.lfWeight = FW_BOLD;
+ boldFont = CreateFontIndirectW(&lf);
+ }
+ if (boldFont) {
+ SendMessageW(hwndControl, WM_SETFONT, (WPARAM)boldFont, MAKELPARAM(1,0));
+ }
+}
+
+LRESULT WINAPI headerProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ if (uMsg == WM_SETCURSOR) {
+ return TRUE;
+ }
+ return CallWindowProcW(prevHeaderProc, hWnd, uMsg, wParam, lParam);
+}
+
+LRESULT WINAPI listViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch (uMsg) {
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_STREAM_1:
+ case IDC_STREAM_2:
+ case IDC_STREAM_3:
+ case IDC_STREAM_4:
+ case IDC_STREAM_5:
+ {
+ int oldCurSelPos = Connection_CurSelPos;
+ Connection_CurSelPos = (LOWORD(wParam) - IDC_STREAM_1);
+ ListView_SetItemState(hWnd, Connection_CurSelPos, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED);
+ SetFocus(hWnd);
+ SendMessage(wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_CONNECT, BN_CLICKED), (LPARAM)GetDlgItem(wnd[1].hWnd, IDC_CONNECT));
+ Connection_CurSelPos = oldCurSelPos;
+ }
+ return 0;
+ }
+ break;
+
+ case WM_NOTIFY:
+ if(((LPNMHDR)lParam)->code == HDN_BEGINTRACKW ||
+ ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGW) {
+ return TRUE;
+ }
+ break;
+ }
+ return CallWindowProcW(prevListViewProc, hWnd, uMsg, wParam, lParam);
+}
+
+void SetTabErrorText(HWND hwnd, int index, int current, LPRECT r, bool update = false) {
+ HDC hDC = GetDC(hwnd);
+ if (!normalFont) {
+ normalFont = (HFONT)SendMessageW(hMainDLG, WM_GETFONT, 0, 0);
+ }
+ HFONT hOldFont = (HFONT)SelectObject(hDC, normalFont);
+ int oldTextColor = SetTextColor(hDC, (!update ? RGB(255,0,0) : RGB(0,0,255)));
+ int oldBkMode = SetBkMode(hDC, TRANSPARENT);
+
+ wchar_t buf[128] = {0};
+ TCITEMW pitem = {0};
+ pitem.mask = TCIF_TEXT;
+ pitem.pszText = buf;
+ pitem.cchTextMax = ARRAYSIZE(buf);
+ SendMessageW(hwnd, TCM_GETITEMW, index, (LPARAM)&pitem);
+
+ r->top += (current ? 1 : 3);
+ r->left += 6;
+
+ // if themeing is enabled then we need to paint the background to avoid the font going weird / double-bold like
+ // and to ensure we're correct, am taking a 1px sliver and stretching it across the area before drawing the text
+ if (isthemethere) {
+ StretchBlt(hDC, r->left, r->top + 1, r->right - r->left - 3, r->bottom - r->top - 3, hDC, r->left - 4, r->top + 1, 1, r->bottom - r->top - 3, SRCCOPY);
+ }
+
+ DrawTextW(hDC, pitem.pszText, wcslen(pitem.pszText), r, DT_SINGLELINE);
+ SetTextColor(hDC, oldTextColor);
+ SetBkMode(hDC, oldBkMode);
+ SelectObject(hDC, hOldFont);
+ ReleaseDC(hwnd, hDC);
+}
+
+LRESULT WINAPI tabWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ LRESULT ret = CallWindowProcW(prevTabWndProc, hWnd, uMsg, wParam, lParam);
+ if (uMsg == WM_PAINT) {
+ RECT r = {0};
+ int item = ListView_GetNextItem(GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS), -1, LVNI_SELECTED);
+ if ((curtab == 1 ?
+ (lastMode[Connection_CurSelPos] >= 3 && lastMode[Connection_CurSelPos] <= 6) :
+ (lastMode[item] >= 3 && lastMode[item] <= 6)
+ ) && TabCtrl_GetItemRect(hWnd, 1, &r)) {
+ SetTabErrorText(hWnd, 1, (curtab == 1), &r);
+ }
+
+ // show the update flag on things!
+ if (updateStr && updateStr[0]) {
+ RECT r = {0};
+ TabCtrl_GetItemRect(tabWnd, 3, &r);
+ SetTabErrorText(tabWnd, 3, (curtab == 3), &r, TRUE);
+ }
+ }
+ return ret;
+}
+
+LRESULT WINAPI outTabWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ LRESULT ret = CallWindowProcW(prevOutTabWndProc, hWnd, uMsg, wParam, lParam);
+ if (uMsg == WM_PAINT) {
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ RECT r = {0};
+ int index[] = {0, 0, 1, 2};
+ if ((lastMode[i] >= 3 && lastMode[i] <= 6) && (i == Connection_CurSelPos) && TabCtrl_GetItemRect(hWnd, index[(lastMode[i] - 3)], &r)) {
+ SetTabErrorText(hWnd, index[(lastMode[i] - 3)], (curouttab == index[(lastMode[i] - 3)]), &r);
+ }
+ }
+ }
+ return ret;
+}
+
+LRESULT WINAPI buttonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ int ret;
+ switch (uMsg) {
+ case WM_LBUTTONDOWN:
+ ret = CallWindowProcW(prevButtonProc, hWnd, uMsg, wParam, lParam);
+ if (SendDlgItemMessage(GetParent(hWnd), IDC_LOCK, BM_GETSTATE, 0, 0) != BST_CHECKED) {
+ blockmousemove = 1;
+ KillTimer(hMainDLG, 2234);
+ KillTimer(hMainDLG, 2235);
+ if ((MicFadeStartTime != 0 || FadeStartTime != 0) && FadeOut != 1) {
+ clock_t myTime = clock();
+ if (FadeStartTime != 0) FadeStartTime = myTime - ((FadeTime * 100)-(myTime - FadeStartTime));
+ if (MicFadeStartTime != 0) MicFadeStartTime = myTime - ((MicFadeTime * 100)-(myTime - MicFadeStartTime));
+ }
+ FadeOut = 1;
+ SetTimer(hMainDLG, 2234, 10, NULL); // fade out
+ SetTimer(hMainDLG, 2235, 10, NULL); // fade out
+ //if (FadeOut) do_capture();
+ #ifdef CAPTURE_TESTING
+ if (FadeOut) {
+ if (!pPlayer) {
+ pPlayer = new Player(hMainDLG);
+ }
+ if (!pCallbacks) {
+ pCallbacks = new CPlayerCallbacks();
+ }
+ pPlayer->SetPlayerCallbacks(pCallbacks);
+ pPlayer->RefreshDeviceList(eRender);
+ pPlayer->RefreshDeviceList(eCapture);
+ pPlayer->SelectDefaultDevice(eRender, eConsole);
+ pPlayer->SelectDefaultDevice(eCapture, eConsole);
+ pPlayer->SelectDeviceFromList(eCapture, 0);
+ //pPlayer->SelectDeviceFromList(eRender, 2);
+ if (pPlayer->Play(eCaptureEndpoint) == FALSE) {
+ return TRUE;
+ }
+ }
+ #endif
+ } else {
+ SendMessage(hWnd, BM_SETSTATE, TRUE, 0);
+ ret = 0;
+ }
+ return ret;
+
+ case WM_MOUSEMOVE:
+ if (blockmousemove) return 0;
+ break;
+
+ case WM_LBUTTONUP:
+ ret = CallWindowProcW(prevButtonProc, hWnd, uMsg, wParam, lParam);
+ if (SendDlgItemMessage(GetParent(hWnd), IDC_LOCK, BM_GETSTATE, 0, 0) != BST_CHECKED) {
+ blockmousemove = 0;
+ KillTimer(hMainDLG, 2234);
+ KillTimer(hMainDLG, 2235);
+ if ((MicFadeStartTime != 0 || FadeStartTime != 0) && FadeOut != 0) {
+ clock_t myTime = clock();
+ if (FadeStartTime != 0) FadeStartTime = myTime - ((FadeTime * 100)-(myTime - FadeStartTime));
+ if (MicFadeStartTime != 0) MicFadeStartTime = myTime - ((MicFadeTime * 100)-(myTime - MicFadeStartTime));
+ }
+ FadeOut = 0;
+ SetTimer(hMainDLG, 2234, 10, NULL); // fade in
+ SetTimer(hMainDLG, 2235, 10, NULL); // fade in
+ } else {
+ SendMessage(hWnd, BM_SETSTATE, TRUE, 0);
+ ret = 0;
+ }
+ return ret;
+ }
+ return CallWindowProcW(prevButtonProc, hWnd, uMsg, wParam, lParam);
+}
+
+int CALLBACK EncFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ // doing this to get the button + combobox to appear as the same size when scrolling through them all
+ RECT r;
+ GetClientRect(GetDlgItem(hDlg, IDC_ENCSETTINGS), &r);
+ MapWindowPoints(GetDlgItem(hDlg, IDC_ENCSETTINGS), hDlg, (LPPOINT)&r,2);
+ InflateRect(&r, 1, 1);
+ SetWindowPos(GetDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON), 0, r.left, r.top, r.right-r.left, r.bottom-r.top, SWP_NOZORDER|SWP_NOACTIVATE);
+ }
+ break;
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case IDC_ENCODERLIST:
+ {
+ if (HIWORD(wParam) == LBN_SELCHANGE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Encoder_CurSelPos = Out->Encoder;
+
+ if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) {
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+ HWND e = hDlg;
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_RESETCONTENT, 0, 0);
+ int item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_NONE, NULL, 0));
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"None");
+
+ item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_MP3_ENCODER, NULL, 0));
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"MP3 Encoder");
+
+ if (C_ENCODER_FHGAAC::isPresent(module.hwndParent)) {
+ item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_FHGAAC_ENCODER, NULL, 0));
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"Fraunhofer Encoder");
+ } else if (C_ENCODER_AACP::isPresent(module.hwndParent)) {
+ item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_AACP_ENCODER, NULL, 0));
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"AAC+ Encoder");
+ }
+#ifdef USE_OGG
+ if (C_ENCODER_OGG::isPresent(module.hwndParent)) {
+ // TODO
+ item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) L"OGG Vorbis Encoder"/*LocalisedString(IDS_OGG_ENCODER, NULL, 0)*/);
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"OGG Vorbis Encoder");
+ }
+#endif // USE_OGG
+
+ SendDlgItemMessage(e, IDC_ENCSETTINGS, CB_RESETCONTENT, 0, 0);
+ SetEncoderPanelMode(e, Enc);
+
+ int attribnum = 0, typeval = 0;
+ if (Enc) {
+ int i;
+ for (int i = 0; i < NUM_ENCODERS; i++) {
+ char* encoder = (char*)SendDlgItemMessage(e, IDC_ENCTYPE, CB_GETITEMDATA, i, 0);
+ if (!strcmp(Enc->GetName(), encoder)) {
+ typeval = i;
+ break;
+ }
+ }
+
+ int infosize = sizeof (T_EncoderIOVals);
+ T_ENCODER_MP3_INFO *EncInfo = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize);
+ for (i = Enc->GetNumAttribs() - 1; i >= 0; i--) {
+ T_ATTRIB attrib;
+ Enc->EnumAttrib(i, &attrib);
+ SendDlgItemMessage(e, IDC_ENCSETTINGS, CB_INSERTSTRING, 0, (LPARAM) attrib.Text);
+ T_ENCODER_MP3_INFO *OutVal = (T_ENCODER_MP3_INFO *) attrib.OutputVals;
+ if (OutVal && EncInfo && infosize) {
+ T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *) EncInfo;
+ if(OutVal->output_bitRate == EncSettings->output_bitRate &&
+ OutVal->output_sampleRate == EncSettings->output_sampleRate &&
+ OutVal->output_numChannels == EncSettings->output_numChannels) {
+ attribnum = i;
+ }
+ }
+ }
+ } else {
+ SendDlgItemMessageW(e, IDC_ENCSETTINGS, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_NONE, NULL, 0));
+ ShowWindowDlgItem(e, IDC_ENCSETTINGS, SW_HIDE);
+ ShowWindowDlgItem(e, IDC_ENCSETTINGS_BUTTON, SW_HIDE);
+ SetDlgItemTextW(e, IDC_ENCSETTINGS_LABEL, LocalisedString(IDS_NO_ENCODER_SELECTED, NULL, 0));
+ }
+
+ SendDlgItemMessage(e, IDC_ENCSETTINGS, CB_SETCURSEL, attribnum, 0);
+ SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETCURSEL, typeval, 0);
+ ReleaseMutex(Enc_mutex[Encoder_CurSelPos]);
+ ini_modified = 1;
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_ENCSETTINGS, CBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_ENCSETTINGS));
+ }
+ }
+ }
+ break;
+
+ case IDC_ENCTYPE:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int typenum = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0);
+
+ // check to see if this is the same encoder as last time as
+ // there's no need to update it if there hasn't been a change
+ // so selecting the same encoder won't cause a settings reset
+ if (typenum && typenum == Enc_LastType[Encoder_CurSelPos]) break;
+ else Enc_LastType[Encoder_CurSelPos] = typenum;
+
+ char* typestr = (char*)SendMessage((HWND) lParam, CB_GETITEMDATA, typenum, 0);
+ if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) {
+ if (!strcmpi("MP3 Encoder", typestr)) {
+ //lame setup
+ Encoder[Encoder_CurSelPos].SetLame(init, params, encode, finish);
+ Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_MP3(init, params, encode, finish), 1);
+
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+
+ if (Enc) {
+ if (strcmpi(Enc->GetName(), "MP3 Encoder") == 0) {
+ T_ENCODER_MP3_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_MP3_INFO *encset = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize)
+ memcpy(&EncSettings, encset, infosize);
+
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ EncSettings.output_bitRate = MP3_DEFAULT_OUTPUTBITRATE;
+ EncSettings.output_sampleRate = MP3_DEFAULT_OUTPUTSAMPLERATE;
+ EncSettings.output_numChannels = MP3_DEFAULT_OUTPUTNUMCHANNELS;
+ Enc->ChangeSettings(&EncSettings);
+ }
+ }
+ } else if (!strcmpi("Fraunhofer Encoder", typestr)) { // FHG AAC
+ Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_FHGAAC(module.hwndParent), 1);
+
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+ if (Enc) {
+ T_ENCODER_FHGAAC_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_FHGAAC_INFO *encset = (T_ENCODER_FHGAAC_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ Enc->ChangeSettings(&EncSettings);
+ }
+ } else if (!strcmpi("AAC+ Encoder", typestr)) { //AAC+
+ Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_AACP(module.hwndParent), 1);
+
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+ if (Enc) {
+ T_ENCODER_AACP_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_AACP_INFO *encset = (T_ENCODER_AACP_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ Enc->ChangeSettings(&EncSettings);
+ }
+ }
+#ifdef USE_OGG
+ else if (!strcmpi("OGG Vorbis Encoder", typestr)) { //OGG
+ Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_OGG(module.hwndParent), 1);
+
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+ if (Enc) {
+ T_ENCODER_OGG_INFO EncSettings;
+ int infosize = sizeof (EncSettings);
+ T_ENCODER_OGG_INFO *encset = (T_ENCODER_OGG_INFO *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) memcpy(&EncSettings, encset, infosize);
+ EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ Enc->ChangeSettings(&EncSettings);
+ }
+ }
+#endif // USE_OGG
+ else {
+ int i;
+ for (i = 0; i < NUM_OUTPUTS; i++) {
+ MY_T_OUTPUT *Out = &Output[i];
+ if (Out->Encoder == Encoder_CurSelPos && Out->Handle != -1) Encoder[Encoder_CurSelPos].DisconnectOutput(Out->Handle);
+ }
+ Encoder[Encoder_CurSelPos].SetEncoder(NULL);
+ }
+
+ // if we can re-map the extension then we do so against the encoder selected
+ // which will override what was manually set but this ensures it is correct!
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ if (Out->saveEncodedPath[0] && typenum != 0) {
+ PathRemoveExtensionW(Out->saveEncodedPath);
+ PathAddExtensionW(Out->saveEncodedPath,
+ (Enc_LastType[Connection_CurSelPos] != 0 ?
+ (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/mpeg") ? L".mp3" :
+ (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/ogg") ? L".ogg" : L".aac")) : L""));
+
+ // update things as the filename was changed
+ SetDlgItemTextW(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, Out->saveEncodedPath);
+ if (Out->saveEncoded) {
+ StopSaveEncoded(Connection_CurSelPos);
+ StartSaveEncoded(Connection_CurSelPos, Out->saveEncodedPath);
+ }
+ }
+
+ SetEncoderPanelMode(hDlg, Encoder[Encoder_CurSelPos].GetEncoder());
+ ReleaseMutex(Enc_mutex[Encoder_CurSelPos]);
+ ini_modified = 1;
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_ENCODERLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_ENCODERLIST));
+ }
+ }
+ }
+ break;
+
+ case IDC_ENCSETTINGS_BUTTON:
+ {
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+ if (Enc) if (Enc->UseNsvConfig()) {
+ ((C_ENCODER_NSV*) Enc)->Configure(hDlg, module.hDllInstance);
+ if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) {
+ ini_modified = 1;
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ MY_T_OUTPUT *Out = &Output[i];
+ if (Out->Encoder == Encoder_CurSelPos && Out->Handle != -1) {
+ Encoder[Encoder_CurSelPos].DisconnectOutput(Out->Handle, 1, 5);
+ }
+ }
+ ReleaseMutex(Enc_mutex[Encoder_CurSelPos]);
+ }
+ }
+ SetEncoderPanelMode(hDlg, Enc);
+ }
+ break;
+
+ case IDC_ENCSETTINGS:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ T_ATTRIB attrib;
+ int attribnum = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0);
+ if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) {
+ C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder();
+ if (Enc) {
+ int i;
+ if (attribnum < 0 || attribnum >= Enc->GetNumAttribs()) attribnum = 0;
+ int oldattrib = -1;
+ int infosize = sizeof (T_EncoderIOVals);
+ T_EncoderIOVals *EncInfo = (T_EncoderIOVals *) Enc->GetExtInfo(&infosize);
+ for (i = Enc->GetNumAttribs() - 1; i >= 0 && oldattrib == -1; i--) {
+ Enc->EnumAttrib(i, &attrib);
+ if (attrib.OutputVals && EncInfo && infosize) {
+ T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *) attrib.OutputVals;
+ T_ENCODER_MP3_INFO *EncSettings2 = (T_ENCODER_MP3_INFO *) EncInfo;
+ //if (memcmp(attrib.OutputVals, EncInfo, infosize) == 0) oldattrib = i;
+ if(EncSettings2->output_bitRate == EncSettings->output_bitRate &&
+ EncSettings2->output_sampleRate == EncSettings->output_sampleRate &&
+ EncSettings2->output_numChannels == EncSettings->output_numChannels) {
+ oldattrib = i;
+ }
+ }
+ }
+ if (attribnum != oldattrib) {
+ if (Enc->EnumAttrib(attribnum, &attrib)) {
+ // we make sure that we set the input channels and samplerate to
+ // that of the mode being used instead of the default values as
+ // this allows us to get things to work correctly (done in 2.3.2)
+ T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *) attrib.OutputVals;
+ if(InputDevice == 1) {
+ EncSettings->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ EncSettings->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ }
+ Enc->ChangeSettings(EncSettings);
+ ini_modified = 1;
+ for (i = 0; i < NUM_OUTPUTS; i++) {
+ MY_T_OUTPUT *Out = &Output[i];
+ if (Out->Encoder == Encoder_CurSelPos && Out->Handle != -1) {
+ Encoder[Encoder_CurSelPos].DisconnectOutput(Out->Handle, 1, 5);
+ }
+ }
+ }
+ }
+ }
+ ReleaseMutex(Enc_mutex[Encoder_CurSelPos]);
+ }
+ }
+ }
+ break;
+ }//switch
+ }
+ break;
+ }// umsg
+ return CallWindowProcW((WNDPROC)DialogFunc, hDlg, uMsg, wParam, lParam);
+}
+
+/* Connection Page callback */
+INT_PTR CALLBACK ConnectionFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ switch (uMsg) {
+ case WM_INITDIALOG:
+ {
+ switch (lParam) {
+ case IDD_CONNECTION:
+ {
+ int i;
+ wchar_t temp[128];
+ SendDlgItemMessage(hDlg, IDC_OUTPUTLIST, LB_RESETCONTENT, 0, 0);
+ for (i = 0; i < NUM_OUTPUTS; i++) {
+ T_OUTPUT_CONFIG *Out = &Output[i].Config;
+ SendDlgItemMessageW(hDlg, IDC_OUTPUTLIST, LB_ADDSTRING, 0, (LPARAM) Out->DisplayName);
+ }
+ SendDlgItemMessage(hDlg, IDC_OUTPUTLIST, LB_SETCURSEL, Connection_CurSelPos, 0);
+ num_outwnds = 0;
+ SendDlgItemMessage(hDlg, IDC_CONTAB, TCM_DELETEALLITEMS, 0, 0);
+ AddOutTab(IDD_PANEL_LOGIN, LocalisedString(IDS_PANEL_LOGIN, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0);
+ AddOutTab(IDD_PANEL_DIRECTORY, LocalisedString(IDS_PANEL_DIRECTORY, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0);
+ AddOutTab(IDD_ENCODER, LocalisedString(IDS_PANEL_ENCODERS, temp, 128), hDlg, EncFunc, IDC_CONTAB, IDC_PANELRECT_C, 0);
+ AddOutTab(IDD_PANEL_TITLE, LocalisedString(IDS_PANEL_TITLES, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0);
+ AddOutTab(IDD_ARTWORK, LocalisedString(IDS_PANEL_ART, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0);
+ AddOutTab(IDD_LOGGING, LocalisedString(IDS_PANEL_LOGGING, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0);
+ SetOutTab(curouttab, hDlg, IDC_CONTAB);
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_OUTPUTLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_OUTPUTLIST));
+ SendDlgItemMessage(wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK,Output[Connection_CurSelPos].AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0);
+ }
+ break;
+ }//lparam
+ }
+ return 0;
+ }//uMsg
+ return CallWindowProcW((WNDPROC)DialogFunc, hDlg, uMsg, wParam, lParam);
+}
+
+void ShowUpdateMessage(HWND hDlg)
+{
+ bool update = (updateStr && updateStr[0]);
+ if (IsWindow(hDlg))
+ {
+ HWND control = GetDlgItem(hDlg, IDC_UPDATE_HEADER);
+ if (update)
+ {
+ wchar_t message[128] = {0};
+ StringCchPrintfW(message, ARRAYSIZE(message), WASABI_API_LNGSTRINGW(IDS_UPDATE_HEADER), updateStr);
+
+ SetWindowTextW(control, message);
+ SetBoldDialogItemFont(control);
+ }
+ else
+ {
+ SetWindowTextW(control, WASABI_API_LNGSTRINGW(IDS_UPDATE));
+ SendMessageW(control, WM_SETFONT, SendMessageW(hMainDLG, WM_GETFONT, 0, 0), 0);
+ InvalidateRect(hDlg, 0, TRUE);
+ }
+
+ ShowWindowDlgItem(hDlg, IDC_STATIC_UPDATE, update);
+ ShowWindowDlgItem(hDlg, IDC_UPDATELINK, update);
+ }
+
+ SetWindowTextW(hMainDLG, WASABI_API_LNGSTRINGW((update ? IDS_UPDATE_TITLE : IDS_MODULE_NAME)));
+
+ if (IsWindow(tabWnd)) {
+ RECT r = {0};
+ TabCtrl_GetItemRect(tabWnd, 3, &r);
+ InvalidateRect(tabWnd, &r, 0);
+ }
+}
+
+class VersionCheckCallback : public ifc_downloadManagerCallback
+{
+public:
+ void OnInit( DownloadToken token )
+ {
+ api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token );
+ if ( http )
+ {
+ http->AllowCompression();
+ http->addheader( "Accept: */*" );
+ }
+ }
+
+ void OnFinish( DownloadToken token )
+ {
+ api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token );
+ if ( http && http->getreplycode() == 200 )
+ {
+ char *buf = 0;
+ size_t size = 0;
+ if ( WAC_API_DOWNLOADMANAGER->GetBuffer( token, (void **)&buf, &size ) == 0 && size > 0 )
+ {
+ buf[ size - 1 ] = 0;
+
+ char *p = buf;
+ while ( size && ( *p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' ) )
+ {
+ p++;
+ size--;
+ }
+
+ // e.g. dsp_sc,2.3.4.210,http://download.nullsoft.com/shoutcast/tools/shoutcast-dsp-2-3-4-windows.exe
+ if ( p && *p )
+ {
+ char *tok = strtok( p, "," );
+ if ( tok )
+ {
+ if ( !strncmp( tok, "dsp_sc", 6 ) )
+ {
+ tok = strtok( NULL, "," );
+ if ( tok )
+ {
+ bool needsUpdating = CompareVersions( tok );
+ lstrcpyn( updateStr, ( needsUpdating ? tok : "" ), ARRAYSIZE( updateStr ) );
+ WritePrivateProfileString( APP_Name, "Update", ( needsUpdating ? updateStr : 0 ), IniName );
+ ShowUpdateMessage( updateWnd );
+ if ( !needsUpdating )
+ {
+ ShowWindowDlgItem( updateWnd, IDC_STATIC_UPDATE, TRUE );
+ }
+ SetDlgItemTextW( updateWnd, IDC_STATIC_UPDATE, WASABI_API_LNGSTRINGW( ( needsUpdating ? IDS_HAS_NEW_UPDATE : IDS_NO_NEW_UPDATE ) ) );
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if ( IsWindow( updateWnd ) )
+ {
+ EnableWindowDlgItem( updateWnd, IDC_GET_UPDATE, TRUE );
+ SetDlgItemTextW( updateWnd, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_FOR_UPDATES ) );
+ }
+ }
+
+ void OnError( DownloadToken token )
+ {
+ if ( IsWindow( updateWnd ) )
+ {
+ ShowWindowDlgItem( updateWnd, IDC_STATIC_UPDATE, TRUE );
+ SetDlgItemTextW( updateWnd, IDC_STATIC_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_UPDATE_FAIL ) );
+ EnableWindowDlgItem( updateWnd, IDC_GET_UPDATE, TRUE );
+ SetDlgItemTextW( updateWnd, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_FOR_UPDATES ) );
+ }
+ }
+
+ RECVS_DISPATCH;
+};
+
+#define CBCLASS VersionCheckCallback
+START_DISPATCH;
+VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit )
+VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish )
+VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError )
+END_DISPATCH;
+#undef CBCLASS
+
+static VersionCheckCallback versionCheckCallback;
+
+static void CheckVersion( HWND hDlg )
+{
+ ShowWindowDlgItem( hDlg, IDC_STATIC_UPDATE, FALSE );
+ ShowWindowDlgItem( hDlg, IDC_UPDATELINK, FALSE );
+
+ if ( WAC_API_DOWNLOADMANAGER )
+ {
+ char url[ 128 ] = { 0 };
+ StringCchPrintf( url, ARRAYSIZE( url ), "http://yp.shoutcast.com/update?c=dsp_sc&v=%s&wa=%x", APP_Version, GetWinampVersion( module.hwndParent ) );
+ updateWnd = hDlg;
+ WAC_API_DOWNLOADMANAGER->DownloadEx( url, &versionCheckCallback, api_downloadManager::DOWNLOADEX_BUFFER );
+ }
+ else
+ {
+ wchar_t title[ 128 ] = { 0 }, message[ 512 ] = { 0 };
+ StringCchPrintfW( title, ARRAYSIZE( title ), LocalisedString( IDS_PLUGIN_NAME, NULL, 0 ), APP_VersionW );
+ StringCchPrintfW( message, ARRAYSIZE( message ), LocalisedString( IDS_UPDATE_CHECK_ERROR, NULL, 0 ) );
+ if ( MessageBoxW( module.hwndParent, message, title, MB_ICONWARNING | MB_YESNO ) == IDYES )
+ {
+ SendMessage( module.hwndParent, WM_WA_IPC, (WPARAM)DOWNLOAD_URL, IPC_OPEN_URL );
+ }
+ SetDlgItemTextW( hDlg, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_FOR_UPDATES ) );
+ EnableWindowDlgItem( hDlg, IDC_GET_UPDATE, TRUE );
+ }
+}
+
+INT_PTR CALLBACK AboutFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ if (uMsg == WM_COMMAND) {
+ switch(LOWORD(wParam))
+ {
+ case IDC_ABOUTLINK:
+ {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)"http://www.shoutcast.com/", IPC_OPEN_URL);
+ }
+ break;
+
+ case IDC_HELPLINK:
+ {
+ // look for a lcoal copy of the documentation otherwise then do as before and use the wiki verison
+ wchar_t path[MAX_PATH] = {0};
+ StringCchPrintfW(path, ARRAYSIZE(path), L"%s\\SHOUTcast Source DSP\\Source_DSP_Plug-in.html", GetPluginDirectoryW(module.hwndParent));
+ if(ShellExecuteW(module.hwndParent, L"open", path, NULL, NULL, SW_SHOWNORMAL) < (HINSTANCE)32)
+ {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)L"http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in", IPC_OPEN_URL);
+ }
+ }
+ break;
+
+ case IDC_FORUMLINK:
+ {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)L"http://forums.shoutcast.com/forumdisplay.php?f=140", IPC_OPEN_URL);
+ }
+ break;
+
+ case IDC_UPDATELINK:
+ {
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)DOWNLOAD_URL, IPC_OPEN_URL);
+ }
+ break;
+
+ case IDC_ABOUT_ICON:
+ {
+ if (HIWORD(wParam) == STN_DBLCLK) {
+ static bool toggle;
+ SendDlgItemMessage(hDlg, IDC_ABOUT_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)GetICYIcon(!toggle));
+ SendMessage(hMainDLG, WM_SETICON, ICON_SMALL, (LPARAM)GetICYIcon(!toggle));
+ toggle = !toggle;
+ }
+ }
+ break;
+
+ case IDC_GET_UPDATE:
+ {
+ EnableWindowDlgItem(hDlg, IDC_GET_UPDATE, FALSE);
+ SetDlgItemTextW(hDlg, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW(IDS_CHECKING_FOR_UPDATES));
+ CheckVersion(hDlg);
+ }
+ break;
+ }
+ }
+ LRESULT ret = CallWindowProcW((WNDPROC)DialogFunc, hDlg, uMsg, wParam, lParam);
+ link_handledraw(hDlg, uMsg, wParam, lParam);
+ return ret;
+}
+
+void UpdateTitleControls(void) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ if (Out->Config.doTitleUpdate) {
+ int length = (GetWindowTextLength(GetDlgItem(out_wnd[3].hWnd, IDC_TITLE)) > 0);
+ int sc2mode = (LOBYTE(Output[Connection_CurSelPos].Config.protocol) == 1);
+
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SENDNEXTTITLES, Out->AutoTitle && !sc2mode);
+
+ if (Out->AutoTitle) {
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, FALSE);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_TITLE, FALSE);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, FALSE);
+ } else {
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, length);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_TITLE, TRUE);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, !sc2mode && length);
+ }
+ } else {
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, FALSE);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_TITLE, FALSE);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, FALSE);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SENDNEXTTITLES, FALSE);
+ }
+}
+
+void LockOptionControls(BOOL unlock) {
+ int protocol = LOBYTE(Output[Connection_CurSelPos].Config.protocol),
+ automatic = (HIBYTE(Output[Connection_CurSelPos].Config.protocol) == 1);
+ BOOL sc1 = (protocol == 1);
+
+ // First Connection panel
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_ADDRESS, unlock);
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_PORT, unlock);
+ // ensure the SC2 items are enabled only if SC1 mode is disabled
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_STATIONID, (sc1 ? FALSE : unlock));
+
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_USERID, unlock);
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_PASSWORD, unlock);
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_RECONNECT, unlock);
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_TIMEOUT, unlock);
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_PROTOCOL, unlock);
+
+ // controls the information shown on the connection panel
+ //ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_FRAME2, !sc1);
+ ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT2, (protocol == 2) && !automatic);
+ ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT3, (protocol == 1) && !automatic);
+ ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT4, automatic);
+
+ // artwork panel controls
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART, !sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, !sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_STREAM, !sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, !sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, !sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_V1_NOTE, sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ARTWORK_V1_FRAME, sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_V2_NOTE, !sc1);
+ ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ARTWORK_V2_FRAME, !sc1);
+
+ // Second Connection panel
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_PUBLIC, unlock);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_DESCRIPTION, unlock);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_SERVERURL, unlock);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_GENRE, FALSE);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_GENRES, unlock);
+ // ensure the SC1 items are enabled only if SC1 mode is enabled
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_AIM, (!sc1 ? FALSE : unlock));
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_ICQ, (!sc1 ? FALSE : unlock));
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_IRC, (!sc1 ? FALSE : unlock));
+
+ // Encoder tab
+ EnableWindowDlgItem(out_wnd[2].hWnd, IDC_ENCTYPE, unlock);
+ EnableWindowDlgItem(out_wnd[2].hWnd, IDC_ENCSETTINGS_BUTTON, unlock);
+ EnableWindowDlgItem(out_wnd[2].hWnd, IDC_ENCSETTINGS, unlock);
+}
+
+void WritePrivateProfileInt(LPCSTR lpKeyName, int value, LPCSTR lpAppName, LPCSTR lpFileName) {
+ char tmp[64] = {0};
+ StringCchPrintfA(tmp, ARRAYSIZE(tmp), "%u", value);
+ WritePrivateProfileString((!lpAppName ? APP_Name : lpAppName), lpKeyName, tmp, (!lpFileName ? IniName : lpFileName));
+}
+
+wchar_t* GetFileMetaData(wchar_t* file, wchar_t* metadata, wchar_t* buffer, int buffer_len) {
+extendedFileInfoStructW efs;
+ efs.filename=file;
+ efs.metadata=metadata;
+ efs.ret=buffer;
+ efs.retlen=buffer_len;
+ SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)&efs, IPC_GET_EXTENDED_FILE_INFOW);
+ return buffer;
+}
+
+void UpdateNextTracks(wchar_t* next, int pos, std::vector<int> &nextListIdx, std::vector<std::wstring> &nextList) {
+ nextList.clear();
+ nextListIdx.clear();
+
+ int queued = (WASABI_API_QUEUEMGR ? WASABI_API_QUEUEMGR->GetNumberOfQueuedItems() : 0);
+ if (WASABI_API_QUEUEMGR && queued) {
+ for (int i = 0; i < queued && (lookAhead == -1 ? 1 : i < lookAhead); i++) {
+ int idx = WASABI_API_QUEUEMGR->GetQueuedItemFromIndex(i);
+ nextList.push_back((wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, idx, IPC_GETPLAYLISTTITLEW));
+ nextListIdx.push_back(idx);
+ }
+ } else {
+ if (next[0]) {
+ nextList.push_back(next);
+ nextListIdx.push_back(pos);
+ }
+ }
+}
+
+int GetNextTracks(int len, int pos, wchar_t* next) {
+ int nextpos = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETNEXTLISTPOS);
+
+ // attempt to use the new look ahead api in 5.61+ otherwise use existing
+ // method or if the new api returns a failure just to cover all bases
+ if (doNextLookAhead && nextpos != -1 && len >= 1) {
+ wcscpy_s((wchar_t*)next, 1024, (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, nextpos, IPC_GETPLAYLISTTITLEW));
+ } else {
+ // get the next title (as long as shuffle is off, etc)
+ // with it mapped back to the first track as appropriate
+ if (len >= 2 && !SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GET_SHUFFLE)) {
+ pos = (pos >= len ? 0 : pos);
+ wcscpy_s((wchar_t*)next, 1024, (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, pos, IPC_GETPLAYLISTTITLEW));
+ }
+ }
+ return pos;
+}
+
+void FillNextTracks(int index, bool xml) {
+ // on change then we refresh the file as applicable
+ std::vector<std::wstring> nextList;
+ std::vector<int> nextListIdx;
+ wchar_t next[1024] = {0};
+ int curpos = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS);
+ int len = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTLENGTH);
+ int pos = curpos + 1;
+ pos = GetNextTracks(len, pos, next);
+ UpdateNextTracks(next, pos, nextListIdx, nextList);
+ WriteNextTracks(index, module.hwndParent, nextListIdx, nextList, xml);
+}
+
+static ARGB32 *loadImgFromFile(const wchar_t *file, int *len)
+{
+ *len = 0;
+ HANDLE hf = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+ if (hf != INVALID_HANDLE_VALUE)
+ {
+ *len = GetFileSize(hf, 0);
+ if (WASABI_API_MEMMGR) {
+ ARGB32* im = (ARGB32 *)WASABI_API_MEMMGR->sysMalloc(*len);
+ if (im) {
+ DWORD bytes_read;
+ ReadFile(hf, im, *len, &bytes_read, 0);
+ CloseHandle(hf);
+ return im;
+ }
+ }
+ CloseHandle(hf);
+ }
+ return (ARGB32 *)-1;
+}
+
+static wchar_t bytes[32], kilo[32], mega[32], giga[32], tera[32];
+wchar_t* sizeStr(unsigned int size) {
+ static wchar_t temp[256];
+ if (GetWinampVersion(module.hwndParent) >= 0x5064) {
+ // TODO swap over to the native Winamp version on newer clients
+ return WASABI_API_LNG->FormattedSizeString(temp, ARRAYSIZE(temp), size);
+ } else {
+ if (!bytes[0]) {
+ LocalisedString(IDS_B, bytes, ARRAYSIZE(bytes));
+ LocalisedString(IDS_KIB, kilo, ARRAYSIZE(kilo));
+ LocalisedString(IDS_MIB, mega, ARRAYSIZE(mega));
+ LocalisedString(IDS_GIB, giga, ARRAYSIZE(giga));
+ }
+
+ if(size < 1024) {
+ StringCchPrintfW(temp, ARRAYSIZE(temp), L"%u %s", size, bytes);
+ } else if(size < 1048576) {
+ StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1024.0f), kilo);
+ } else if(size < 1073741824) {
+ StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1048576.0f), mega);
+ } else if(size < 1099511627776){
+ StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1073741824.0f), giga);
+ } else{
+ StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1073741824.0f), tera);
+ }
+ }
+ return temp;
+}
+
+void UpdateArtworkMessage() {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ SHOUTCAST_OUTPUT *Enc = Out->Encoder != -1 ? &Encoder[Out->Encoder] : NULL;
+ wchar_t buf[1024], playing[256], stream[256];
+ T_OUTPUT_INFO *Info = Enc->GetOutputInfo(Out->Handle);
+ int streamSize = (Info ? Info->art_cached_length[0] : 0),
+ playingSize = (Info ? Info->art_cached_length[1] : 0);
+
+ StringCchPrintfW(stream, ARRAYSIZE(stream),
+ WASABI_API_LNGSTRINGW(!Out->useArt || !Out->useStreamArt ? IDS_DISABLED :
+ (streamSize == 0 ? IDS_EMPTY_ART : IDS_ENABLED_SIZE)),
+ sizeStr(streamSize));
+
+ StringCchPrintfW(playing, ARRAYSIZE(playing),
+ WASABI_API_LNGSTRINGW(!Out->useArt || !Out->usePlayingArt ? IDS_DISABLED :
+ (playingSize == 0 ? IDS_EMPTY_ART : IDS_ENABLED_SIZE)),
+ sizeStr(playingSize));
+
+ StringCchPrintfW(buf, ARRAYSIZE(buf), WASABI_API_LNGSTRINGW(IDS_V2_ARTWORK), stream, playing);
+ SetDlgItemTextW(out_wnd[4].hWnd, IDC_ART_V2_NOTE, buf);
+}
+
+void UpdatePlayingAlbumArt(int Connection, int Index, bool usePlayingArt) {
+ // do some quick checks as no need to send if not applicable to do so
+ if (!usePlayingArt && !playingImage) {
+ return;
+ }
+
+ // hard code the playing art to be sent in png format
+ Encoder[Index].UpdateAlbumArtCache((usePlayingArt ? playingImage : 0),
+ (usePlayingArt ? playingLength : 0),
+ playingType, Connection);
+ UpdateArtworkMessage();
+}
+
+bool UpdateStreamAlbumArt(int Connection, int Index, const wchar_t* stationArtPath, bool useStreamArt) {
+ int artType = 0x0;
+ bool update = false;
+
+ // if not enabled and loaded then unload and refresh
+ if (!useStreamArt && streamImage[Index] != (ARGB32 *)-1) {
+ FreeStreamAlbumArt(Index);
+ // bit of a fiddle to force a dummy update
+ artType = 0x4000;
+ update = true;
+ } else if (useStreamArt && streamImage[Index] != (ARGB32 *)-1) {
+ FreeStreamAlbumArt(Index);
+ }
+
+ // if enabled and not loaded then attempt to load
+ if (!update && streamImage[Index] == (ARGB32 *)-1) {
+ if (useStreamArt) {
+ streamImage[Index] = loadImgFromFile(stationArtPath, &streamLength[Index]);
+
+ wchar_t* ext = PathFindExtensionW(stationArtPath);
+ if (ext) {
+ if (*ext) ext++;
+
+ update = true;
+ if (!wcsnicmp(ext, L"jpeg", 4) || !wcsnicmp(ext, L"jpg", 3)) {
+ artType = 0x4000;
+ } else if (!wcsnicmp(ext, L"png", 3)) {
+ artType = 0x4001;
+ } else if (!wcsnicmp(ext, L"bmp", 3)) {
+ artType = 0x4002;
+ } else if (!wcsnicmp(ext, L"gif", 3)) {
+ artType = 0x4003;
+ } else {
+ update = false;
+ }
+ }
+ } else {
+ // if not enabled and not loaded then do nothing
+ UpdateArtworkMessage();
+ return false;
+ }
+ }
+
+ if (update) {
+ Encoder[Index].UpdateAlbumArtCache((!useStreamArt || streamImage[Index] == (ARGB32 *)-1 ? 0 : streamImage[Index]),
+ (!useStreamArt ? 0: streamLength[Index]), artType, Connection);
+ }
+ UpdateArtworkMessage();
+ return update;
+}
+
+void UpdateVUMeters()
+{
+ int volume = 0;
+ int vu_l = VU.vu_l;
+ int vu_r = VU.vu_r;
+ VU.vu_l = 0;
+ VU.vu_r = 0;
+ VU.lastUpdate = VU.update;
+ VU.update = 0;
+ wchar_t tmp[256], temp[32], temp2[32];
+
+ if (vu_l != 0) {
+ volume = (int) (20 * log10((double) vu_l));
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90);
+ } else {
+ volume = 0;
+ LocalisedString(IDS_INF_DB, tmp, 256);
+ }
+ if (volume - 90 > peak_vu_l) {
+ StringCchPrintfW(temp2, ARRAYSIZE(temp2), LocalisedString(IDS_X_DB_PEAK, temp, 32), (peak_vu_l = volume - 90));
+ SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_LP, temp2);
+ SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_LP, temp2);
+ }
+
+ SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_L, tmp);
+ SendDlgItemMessage(wnd[0].hWnd, IDC_VOLUMEGRAPH_L, PBM_SETPOS, volume, 0);
+ SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_L, tmp);
+ SendDlgItemMessage(wnd[2].hWnd, IDC_VOLUMEGRAPH_L, PBM_SETPOS, volume, 0);
+ if (vu_r != 0) {
+ volume = (int) (20 * log10((double) vu_r));
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90);
+ } else {
+ volume = 0;
+ LocalisedString(IDS_INF_DB, tmp, 256);
+ }
+ if (volume - 90 > peak_vu_r) {
+ StringCchPrintfW(temp2, ARRAYSIZE(temp2), LocalisedString(IDS_X_DB_PEAK, temp, 32), (peak_vu_r = volume - 90));
+ SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_RP, temp2);
+ SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_RP, temp2);
+ }
+
+ SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_R, tmp);
+ SendDlgItemMessage(wnd[0].hWnd, IDC_VOLUMEGRAPH_R, PBM_SETPOS, volume, 0);
+ SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_R, tmp);
+ SendDlgItemMessage(wnd[2].hWnd, IDC_VOLUMEGRAPH_R, PBM_SETPOS, volume, 0);
+}
+
+void UpdateSummaryDetails(int item) {
+ if (item == -1) {
+ return;
+ }
+
+ wchar_t message[2048], temp[128], temp2[128], temp3[128],
+ temp4[128] = {0}, temp5[128], temp6[128], temp7[128];
+ char temp8[128];
+ MY_T_OUTPUT *Out = &Output[item];
+ C_ENCODER *Enc = Encoder[Out->Encoder].GetEncoder();
+
+ if (Enc) {
+ StringCchPrintfW(temp4, ARRAYSIZE(temp4), LocalisedString(IDS_SUMMARY_KBPS, NULL, 0), ((T_EncoderIOVals*) Enc->GetExtInfo())->output_bitRate);
+ }
+
+ StringCchPrintfW(message, ARRAYSIZE(message), LocalisedString(IDS_SUMMARY, NULL, 0),
+ // TODO show automatic mode ?
+ (LOBYTE(Out->Config.protocol) != 1 ? 2 : 1),
+ LocalisedString((Out->Config.Public ? IDS_PUBLIC : IDS_PRIVATE), temp, 128),
+ (Out->Config.Address[0] ? Out->Config.Address : LocalisedStringA(IDS_NOT_SET_SUMMARY, temp8, 128)),
+ Out->Config.Port,
+ LocalisedString((Out->Config.doTitleUpdate ? (Out->AutoTitle ? IDS_FOLLOW_WA : IDS_MANUAL) : IDS_DISABLED), temp2, 128),
+ (Enc_LastType[item] != 0 ? (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/mpeg")?L"MP3":L"AAC+") : LocalisedString(IDS_NOT_SET, temp3, 128)),
+ temp4,
+ LocalisedString((Out->Logging ? IDS_YES : IDS_NO), temp5, 128),
+ LocalisedString((Out->AutoConnect ? IDS_YES : IDS_NO), temp6, 128),
+ LocalisedString((Out->saveEncoded ? IDS_YES : IDS_NO), temp7, 128));
+
+ SetDlgItemTextW(wnd[0].hWnd, IDC_SUMMARY, message);
+
+ // see if we're looking at a incomplete setup and flag up the output tab as applicable
+ if (IsWindow(tabWnd)) {
+ RECT r = {0};
+ TabCtrl_GetItemRect(tabWnd, 1, &r);
+ if ((lastMode[item] >= 3 && lastMode[item] <= 6)) {
+ SetTabErrorText(tabWnd, 1, 0, &r);
+ } else {
+ InvalidateRect(tabWnd, &r, 0);
+ }
+ }
+}
+
+void ProcessPlayingStatusUpdate(void) {
+ if (isplaying == 1 && SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME)) {
+ if (was_paused) {
+ was_paused = 0;
+ play_diff = GetTickCount();
+ }
+ } else if (isplaying == 3) {
+ // pause
+ was_paused = 1;
+ } else if (isplaying == 0 && was_playing == 1) {
+ // stop
+ was_playing = was_paused = 0;
+ } else if (isplaying == 0 && was_playing == 0) {
+ // non-playing track advance
+ PostMessage(hMainDLG, WM_USER, 0, nowPlayingID);
+ }
+}
+
+// check against a list of known "illegal" names as well as not allowing
+// the creation of a station with the name the same as a genre string
+bool stationNameAllowed(char* name){
+ if(name && *name)
+ {
+ int checked = 0;
+ for (int i = 0; i < ARRAYSIZE(genres); i++) {
+ checked += !lstrcmpi(name, genres[i].name);
+ }
+
+ // check for punctuation only titles with double-processing
+ // to strip out alphanum+space and space in second and then
+ // we compare a difference in lengths where different is ok
+ if (lstrlen(name) > 0) {
+ char* stripped = (char*)malloc(lstrlen(name)+1);
+ char* stripped2 = (char*)malloc(lstrlen(name)+1);
+ stripped[0] = 0;
+ for(int i = 0, j = 0; i < lstrlen(name); i++) {
+ if(!isalnum(name[i]) && name[i] != ' ') {
+ stripped[j] = name[i];
+ stripped[++j] = 0;
+ }
+ }
+ for(int i = 0, j = 0; i < lstrlen(name); i++) {
+ if(name[i] != ' ') {
+ stripped2[j] = name[i];
+ stripped2[++j] = 0;
+ }
+ }
+ checked += !(lstrlen(stripped2) > lstrlen(stripped));
+ free(stripped);
+ free(stripped2);
+ }
+
+ char* invalidNames[] = {"127.0.0.1", "admin", "auto dj", "auto jedi", "auto pj",
+ "auto-dj", "autodj", "autopj", "demo", "dj", "internet radio",
+ "live", "local server", "localhost", "localserver", "music",
+ "my radio", "my server", "my station name", "my test server",
+ "n/a", "pj", "playlist", "radio", "radio station", "test",
+ "test server", "unnamed server", "virtual dj", "virtualdj",
+ "web rdio", "web radio", "song", "teste", "default stream",
+ "radio stream", "whmsonic autodj", "autopilot",
+ "this is my server name"};
+ for(int i = 0; i < ARRAYSIZE(invalidNames); i++)
+ {
+ checked += !lstrcmpi(name, invalidNames[i]);
+ }
+ return (!checked);
+ }
+ return false;
+}
+
+HBITMAP WAResizeImage(HBITMAP hbmp, INT cx, INT cy) {
+ BITMAP bi = {0};
+ if (!hbmp || !GetObjectW(hbmp, sizeof(BITMAP), &bi)) return hbmp;
+
+ if (bi.bmWidth != cx || bi.bmHeight != cy) {
+ HDC hdc, hdcDst, hdcSrc;
+ HBITMAP hbmpOld1, hbmpOld2;
+
+ int ix = cy*(bi.bmWidth*1000/bi.bmHeight)/1000, iy;
+ if (ix > cx) {
+ iy = cx*(bi.bmHeight*1000/bi.bmWidth)/1000;
+ ix = cx;
+ }
+ else iy = cy;
+
+ hdc = GetDC(NULL);
+ hdcSrc = CreateCompatibleDC(hdc);
+ hdcDst = CreateCompatibleDC(hdc);
+ hbmpOld1 = (HBITMAP)SelectObject(hdcSrc, hbmp);
+ hbmp = CreateCompatibleBitmap(hdc, cx, cy);
+ hbmpOld2 = (HBITMAP)SelectObject(hdcDst, hbmp);
+ if (ix != cx || iy != cy) {
+ RECT r;
+ SetRect(&r, 0, 0, cx, cy);
+ FillRect(hdcDst, &r, GetSysColorBrush(COLOR_BTNFACE));
+ }
+ SetStretchBltMode(hdcDst, HALFTONE);
+ StretchBlt(hdcDst, (cx - ix)/2, (cy - iy)/2, ix, iy, hdcSrc, 0, 0, bi.bmWidth, bi.bmHeight, SRCCOPY);
+
+ SelectObject(hdcDst, hbmpOld2);
+ hbmpOld2 = (HBITMAP)SelectObject(hdcSrc, hbmpOld1);
+ if (hbmpOld2) DeleteObject(hbmpOld2);
+
+ DeleteDC(hdcSrc);
+ DeleteDC(hdcDst);
+ ReleaseDC(NULL, hdc);
+ }
+ return hbmp;
+}
+
+HBITMAP getBitmap(ARGB32 * data, int w, int h)
+{
+ BITMAPINFO info={0};
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biWidth = w;
+ info.bmiHeader.biHeight = -h;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = 32;
+ info.bmiHeader.biCompression = BI_RGB;
+ HDC dc = GetDC(NULL);
+ HBITMAP bm = CreateCompatibleBitmap(dc, w, h);
+ SetDIBits(dc, bm, 0, h, data, &info, DIB_RGB_COLORS);
+ ReleaseDC(NULL, dc);
+ return WAResizeImage(bm, 155, 88);
+}
+
+ARGB32 * decompressImage(const void *data, int datalen, int * dataW, int * dataH) {
+ ARGB32* ret=NULL;
+ FOURCC imgload = svc_imageLoader::getServiceType();
+ int n = WASABI_API_SVC->service_getNumServices(imgload);
+ for (int i = 0; i < n; i++) {
+ waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i);
+ if (sf) {
+ svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
+ if (l) {
+ if (l->testData(data,datalen)) {
+ ret = l->loadImage(data,datalen,dataW,dataH);
+ sf->releaseInterface(l);
+ break;
+ }
+ sf->releaseInterface(l);
+ }
+ }
+ }
+ return ret;
+}
+
+INT_PTR CALLBACK DialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ if (SYSTRAY_BASE_MSG + SYSTRAY_MAXIMIZE_MSG == uMsg) {
+ int which = LOWORD(wParam) - SYSTRAY_BASE_ICON;
+ switch (LOWORD(lParam)) {
+ case WM_LBUTTONDOWN:
+ {
+ switch (which) {
+ case 1:
+ {
+ RemoveSystrayIcon(hDlg, SYSTRAY_ICY_ICON);
+ ShowWindow(hDlg, SW_SHOW);
+ SendMessage(hDlg, WM_SYSCOMMAND, SC_RESTORE, 0);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ switch (uMsg) {
+#ifdef FOLLOW_MIXER
+ case MM_MIXM_CONTROL_CHANGE:
+ {
+ HMIXER mix = (HMIXER)wParam;
+ DWORD dwControlID = (DWORD)lParam;
+ //if (dwControlID == 3)
+ {
+ MIXERLINE ml = {sizeof (ml), 0};
+ ml.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
+ ml.dwLineID = dwControlID;
+ if (mixerGetLineInfo((HMIXEROBJ) mix, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) {
+ MIXERLINECONTROLS mlc = {sizeof (mlc), dwControlID,};
+ MIXERCONTROL mc = {sizeof (mc),};
+ //mlc.cControls = 1;
+ mlc.cbmxctrl = sizeof (mc);
+ mlc.pamxctrl = &mc;
+ mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
+ //mlc.dwControlID = dwControlID;
+ mlc.dwLineID = dwControlID;//ml.dwLineID;
+ char a[128];
+ sprintf(a,"MM_MIXM_CONTROL_CHANGE: %d\n",dwControlID);
+ OutputDebugString(a);
+ if (mixerGetLineControls((HMIXEROBJ) mix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) {
+ //MIXERCONTROLDETAILS mcd = {sizeof (mcd), 0,};
+ char a[128];
+ sprintf(a,"MM_MIXM_CONTROL_CHANGE 2: %d\n",dwControlID);
+ OutputDebugString(a);
+ /*MIXERCONTROLDETAILS_UNSIGNED v[2];
+ mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED);
+ mcd.paDetails = v;
+ /*v[0].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100;
+ v[1].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100;
+ MMRESULT result = mixerSetControlDetails((HMIXEROBJ) hmix, &mcd, MIXER_OBJECTF_MIXER);*/
+ }
+ }
+ }
+ }
+ break;
+#endif
+
+ case WM_LBUTTONUP:
+ GetWindowRect(hDlg, &mainrect);
+ break;
+
+ case WM_INITDIALOG:
+ {
+ switch (lParam) {
+ case IDD_DIALOG:
+ {
+ int i;
+ wchar_t temp[128] = {0};
+ hMainDLG = hDlg;
+ for (i = 0; i < NUM_ENCODERS; i++) Enc_mutex[i] = NULL;
+ INITCOMMONCONTROLSEX ICCE;
+ ICCE.dwSize = sizeof (ICCE);
+ ICCE.dwICC = ICC_WIN95_CLASSES;
+ InitCommonControlsEx(&ICCE);
+ GetSCIniFile(module.hwndParent);
+ VU.update = VU.lastUpdate = 0;
+ memset(&VU, 0, sizeof (VU));
+ memset(&Output, 0, sizeof (Output));
+
+ // as only 5.61+ supports the IPC_GETNEXTLISTPOS api we check and cache version now
+ doNextLookAhead = (GetWinampVersion(module.hwndParent) >= 0x5061);
+
+ /* Load config */
+ LoadConfig();
+
+ /* Load Encoders */
+ LoadEncoders();
+
+ /* Start logging and encoded output saving */
+ for (i = 0; i < NUM_OUTPUTS; i++) {
+ if (Output[i].Logging) {
+ StartLogging(i, Output[i].LogCOS);
+ }
+ if (Output[i].nextTrackLog) {
+ StartNextTracks(i, Output[i].nextTrackPath);
+ }
+ if (Output[i].saveEncoded) {
+ StartSaveEncoded(i, Output[i].saveEncodedPath);
+ }
+ }
+
+ // ensure we've cached the title information, etc as needed before starting
+ isplaying = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING);
+ if (isplaying) ProcessPlayingStatusUpdate();
+ PostMessage(hDlg, WM_USER, 0, nowPlayingID);
+
+ /* Setup main tabs */
+ num_tabwnds = 0;
+ SendDlgItemMessage(hDlg, IDC_TAB, TCM_DELETEALLITEMS, 0, 0);
+ AddTab(IDD_MAIN, LocalisedString(IDS_TAB_MAIN, temp, 128), hDlg, DialogFunc, IDC_TAB, IDC_RECT, 100);
+ AddTab(IDD_CONNECTION, LocalisedString(IDS_TAB_OUTPUT, temp, 128), hDlg, ConnectionFunc, IDC_TAB, IDC_RECT, 100);
+ AddTab(IDD_INPUT, LocalisedString(IDS_TAB_INPUT, temp, 128), hDlg, DialogFunc, IDC_TAB, IDC_RECT, 100);
+ AddTab(IDD_ABOUT, LocalisedString(IDS_TAB_ABOUT, temp, 128), hDlg, AboutFunc, IDC_TAB, IDC_RECT, 100);
+ SetTab(curtab, hDlg, IDC_TAB);
+
+ /* threads and timer */
+ hthread = CreateThread(NULL, 0, ThreadInput, hDlg, CREATE_SUSPENDED, &threadid);
+ SetThreadPriority(hthread, THREAD_PRIORITY_HIGHEST);
+ hthreadout = CreateThread(NULL, 0, ThreadOutput, hDlg, CREATE_SUSPENDED, &threadoutid);
+ SetThreadPriority(hthreadout, THREAD_PRIORITY_HIGHEST);
+ Soundcard.Close();
+ Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch));
+ SetTimer(hDlg, 666, 300000, NULL); // start up INI save timer
+ FadeStartTime = 0;
+ MicFadeStartTime = 0;
+ FadeOut = 0;
+ ini_modified = 1;
+ ReleaseMutex(cf_mutex);
+ SendDlgItemMessage(wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK,Output[Connection_CurSelPos].AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ // deal with the PTT restore mode along with setting up the 'down arrow' button
+ SendDlgItemMessage(in_wnd[1].hWnd, IDC_LOCK, BM_SETCHECK, Restore_PTT ? BST_CHECKED : BST_UNCHECKED, 0);
+ PostMessage(in_wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_LOCK, BN_CLICKED), (LPARAM)GetDlgItem(in_wnd[1].hWnd, IDC_LOCK));
+
+ SendDlgItemMessage(in_wnd[1].hWnd, IDC_LOCK_MODE, BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_DOWNARROW),
+ IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION));
+
+ SendDlgItemMessage(in_wnd[1].hWnd, IDC_REFRESH_DEVICES, BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_REFRESH),
+ IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION));
+
+ /* setup information parts to be in bold */
+ SetBoldDialogItemFont(GetDlgItem(wnd[0].hWnd, IDC_SUMMARY));
+ SetBoldDialogItemFont(GetDlgItem(out_wnd[1].hWnd, IDC_INFO_TEXT));
+ SetBoldDialogItemFont(GetDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT2));
+ SetBoldDialogItemFont(GetDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT3));
+ SetBoldDialogItemFont(GetDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT4));
+ SetBoldDialogItemFont(GetDlgItem(wnd[1].hWnd, IDC_CONNECT));
+ SetBoldDialogItemFont(GetDlgItem(out_wnd[4].hWnd, IDC_ART_V1_NOTE));
+ SetBoldDialogItemFont(GetDlgItem(out_wnd[4].hWnd, IDC_ART_V2_NOTE));
+
+ /* subclass the listview on the summary page for specific handling */
+ HWND listView = GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS);
+ ListView_SetItemState(listView, 0, LVIS_SELECTED, LVIS_SELECTED);
+ prevListViewProc = (WNDPROC) SetWindowLongPtrW(listView, GWLP_WNDPROC, (LONG_PTR) listViewProc);
+ prevHeaderProc = (WNDPROC) SetWindowLongPtrW(ListView_GetHeader(listView), GWLP_WNDPROC, (LONG_PTR) headerProc);
+
+ /* subclass the tab control on the main dialog for error notifications */
+ tabWnd = GetDlgItem(hMainDLG, IDC_TAB);
+ prevTabWndProc = (WNDPROC) SetWindowLongPtrW(tabWnd, GWLP_WNDPROC, (LONG_PTR) tabWndProc);
+
+ /* subclass the tab control on the connection page for error notifications */
+ outTabWnd = GetDlgItem(wnd[1].hWnd, IDC_CONTAB);
+ prevOutTabWndProc = (WNDPROC) SetWindowLongPtrW(outTabWnd, GWLP_WNDPROC, (LONG_PTR) outTabWndProc);
+
+ /* only once everything else has been done then we start the threads */
+ ResumeThread(hthread);
+ ResumeThread(hthreadout);
+ break;
+ }
+
+ case IDD_MAIN:
+ {
+ HWND listView = GetDlgItem(hDlg, IDC_OUTPUTSTATUS);
+ ListView_DeleteAllItems(listView);
+ ListView_SetExtendedListViewStyle(listView, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER|LVS_EX_LABELTIP);
+ AddColumn(L"", listView);
+ AddColumn(LocalisedString(IDS_COL_OUTPUT_NAME, NULL, 0), listView);
+ AddColumn(LocalisedString(IDS_COL_STATUS, NULL, 0), listView);
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ AddColItem(L"", 0, hDlg, IDC_OUTPUTSTATUS);
+ AddColItem(Output[i].Config.DisplayName, 1, hDlg, IDC_OUTPUTSTATUS);
+ AddColItem(L"", 2, hDlg, IDC_OUTPUTSTATUS);
+ }
+
+ // fill the header of the output list with the column headers
+ ListView_SetColumnWidth(listView, 0, 24);//LVSCW_AUTOSIZE_USEHEADER);
+ ListView_SetColumnWidth(listView, 1, LVSCW_AUTOSIZE_USEHEADER);
+ int width = ListView_GetColumnWidth(listView, 0) + ListView_GetColumnWidth(listView, 1);
+ RECT listViewRect;
+ GetClientRect(listView, &listViewRect);
+ ListView_SetColumnWidth(listView, 2, (listViewRect.right - listViewRect.left) - width);
+
+ // add in status / action buttons for the first column...
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ POINT pt;
+ ListView_GetItemPosition(listView, i, &pt);
+ ListView_GetItemRect(listView, i, &listViewRect, LVIR_BOUNDS);
+ buttonWnd[i] = CreateWindowW(L"button", L"", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_DISABLED | BS_ICON,
+ 1, pt.y, ListView_GetColumnWidth(listView, 0) - 3,
+ (listViewRect.bottom - listViewRect.top),
+ listView, (HMENU)(IDC_STREAM_1+i), 0, 0);
+ SendMessage(buttonWnd[i], BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_PLAY),
+ IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION));
+ }
+
+ CheckRadioButton(hDlg, IDC_INPUT_WINAMP, IDC_INPUT_SOUNDCARD, (IDC_INPUT_WINAMP + InputDevice));
+ SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_L, PBM_SETRANGE, 0, MAKELONG(0, 90));
+ SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_R, PBM_SETRANGE, 0, MAKELONG(0, 90));
+ for (int ii = 0; ii < NUM_OUTPUTS; ii++) {
+ int encnum = ii;
+ MY_T_OUTPUT *Out = &Output[ii];
+ // removed encoder selection
+ if (Out->Encoder != -1) {
+ if (Out->Handle != -1) {
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[Out->Encoder].RemoveOutput(Out->Handle);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ }
+
+ Out->Encoder = encnum;
+ if (Out->Encoder != -1) {
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Out->Handle = Encoder[Out->Encoder].AddOutput(Out->Encoder, &Out->Config, TitleCallback);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ ini_modified = 1;
+ }
+ // TODO
+ //SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_NOTITLES, BN_CLICKED), (LPARAM) GetDlgItem(hDlg, IDC_NOTITLES));
+ }
+ break;
+ }
+
+ case IDD_INPUT:
+ {
+ SendDlgItemMessage(hDlg, IDC_INPUTDEVICE, CB_RESETCONTENT, 0, 0);
+ AddInTab(IDD_PANEL_WINAMP, LocalisedString(IDS_INPUT_WINAMP, NULL, 0), hDlg);
+ AddInTab(IDD_PANEL_LINEIN, LocalisedString(IDS_INPUT_SOUNDCARD, NULL, 0), hDlg);
+ SetInTab(InputDevice, hDlg, IDC_INPUTDEVICE);
+ EnableWindowDlgItem(hDlg, IDC_INPUTDEVICE, 1);
+ EnableWindowDlgItem(hDlg, IDC_INPUTDEVICESTATIC, 1);
+ SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_L, PBM_SETRANGE, 0, MAKELONG(0, 90));
+ SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_R, PBM_SETRANGE, 0, MAKELONG(0, 90));
+ break;
+ }
+
+ case IDD_ABOUT:
+ {
+ // this will make sure that we've got the icon shown even when using a localised version
+ // as well as setting the dialog icon to the icy icon irrespective of the dialog class's
+ SendDlgItemMessage(hDlg, IDC_ABOUT_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)GetICYIcon());
+ SendMessage(hMainDLG, WM_SETICON, ICON_SMALL, (LPARAM)GetICYIcon());
+
+ wchar_t about[1024], tmp[256];
+ StringCchPrintfW(about, ARRAYSIZE(about), LocalisedString(IDS_ABOUT_MESSAGE, NULL, 0),
+ LocalisedString(IDS_MODULE_NAME, tmp, 256),
+ APP_Version, APP_Build, __DATE__);
+ SetDlgItemTextW(hDlg, IDC_PROGRAMNAME, about);
+ link_startsubclass(hDlg, IDC_ABOUTLINK);
+ link_startsubclass(hDlg, IDC_HELPLINK);
+ link_startsubclass(hDlg, IDC_FORUMLINK);
+ link_startsubclass(hDlg, IDC_UPDATELINK);
+
+ ShowUpdateMessage(hDlg);
+ break;
+ }
+
+ case IDD_PANEL_WINAMP:
+ {
+ wchar_t buf[128] = {0};
+ inWinWa = hDlg;
+ HWND metalist = GetDlgItem(hDlg, IDC_METALIST);
+ ListView_SetExtendedListViewStyle(metalist, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER|LVS_EX_LABELTIP);
+ AddColumn(L"", metalist);
+ AddColumn(L"", metalist);
+ AddColItem(LocalisedString(IDS_FILEPATH, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ AddColItem(LocalisedString(IDS_TITLE, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ AddColItem(LocalisedString(IDS_ARTIST, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ AddColItem(LocalisedString(IDS_ALBUM, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ AddColItem(LocalisedString(IDS_GENRE, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ AddColItem(LocalisedString(IDS_YEAR, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ AddColItem(LocalisedString(IDS_COMMENT, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST);
+ ListView_SetColumnWidth(metalist, 0, LVSCW_AUTOSIZE);
+ RECT metalistRect;
+ GetClientRect(metalist, &metalistRect);
+ ListView_SetColumnWidth(metalist, 1, (metalistRect.right - metalistRect.left) - ListView_GetColumnWidth(metalist, 0));
+ break;
+ }
+
+ case IDD_PANEL_LINEIN:
+ {
+ inWin = NULL;
+ prevButtonProc = (WNDPROC) SetWindowLongPtrW(GetDlgItem(hDlg, IDC_PTT), GWLP_WNDPROC, (LONG_PTR) buttonProc);
+ SendDlgItemMessage(hDlg, IDC_MUSSLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 10));
+ SendDlgItemMessage(hDlg, IDC_MUS2SLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 10));
+ SendDlgItemMessage(hDlg, IDC_MICSLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 10));
+ SendDlgItemMessage(hDlg, IDC_FADESLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 25));
+ SendDlgItemMessage(hDlg, IDC_MICFADESLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 25));
+ SendDlgItemMessage(hDlg, IDC_MUSSLIDER, TBM_SETPOS, TRUE, MusVol);
+ SendDlgItemMessage(hDlg, IDC_MUS2SLIDER, TBM_SETPOS, TRUE, Mus2Vol);
+ SendDlgItemMessage(hDlg, IDC_MICSLIDER, TBM_SETPOS, TRUE, MicVol);
+ SendDlgItemMessage(hDlg, IDC_FADESLIDER, TBM_SETPOS, TRUE, FadeTime);
+ SendDlgItemMessage(hDlg, IDC_MICFADESLIDER, TBM_SETPOS, TRUE, MicFadeTime);
+ SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MUSSLIDER));
+ SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MUS2SLIDER));
+ SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MICSLIDER));
+ SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_FADESLIDER));
+ SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MICFADESLIDER));
+ inWin = hDlg;
+ SetDeviceName();
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_DEVBOX, CBN_SELCHANGE), (LPARAM)GetDlgItem(hDlg,IDC_DEVBOX));
+ break;
+ }
+
+ case IDD_PANEL_LOGIN:
+ {
+ SendDlgItemMessageW(hDlg, IDC_PROTOCOL, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_AUTOMATIC, NULL, 0));
+ SendDlgItemMessage(hDlg, IDC_PROTOCOL, CB_SETITEMDATA, 0,(LPARAM)0);
+ SendDlgItemMessageW(hDlg, IDC_PROTOCOL, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_V2_MODE, NULL, 0));
+ SendDlgItemMessage(hDlg, IDC_PROTOCOL, CB_SETITEMDATA, 1,(LPARAM)2);
+ SendDlgItemMessageW(hDlg, IDC_PROTOCOL, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_V1_MODE, NULL, 0));
+ SendDlgItemMessage(hDlg, IDC_PROTOCOL, CB_SETITEMDATA, 2,(LPARAM)1);
+ SendDlgItemMessage(hDlg, IDC_TIMEOUT, EM_SETLIMITTEXT, 5, 0);
+ break;
+ }
+
+ case IDD_PANEL_DIRECTORY:
+ {
+ SendDlgItemMessage(hDlg, IDC_GENRES, BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_DOWNARROW),
+ IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION));
+ break;
+ }
+ }
+ }
+ return 1;
+
+ case WM_CLOSE:
+ {
+ if (hDlg == hMainDLG)
+ {
+ doQuit();
+ }
+ }
+ break;
+
+ case WM_USER:
+ {
+ if (lParam == nowPlayingID && wParam == 0) {
+ // Winamp Title handling
+ wchar_t title[1024] = {0},
+ next[1024] = {0},
+ song[1024] = {0},
+ artist[1024] = {0},
+ album[1024] = {0},
+ genre[1024] = {0},
+ comment[1024] = {0},
+ year[32] = {0},
+ tmp2[1024] = {0};
+ char buffer[1024] = {0},
+ buffer2[1024] = {0};
+
+ // winamp playlist length in tracks
+ int len = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTLENGTH);
+ int curpos = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS);
+ int pos = curpos + 1;
+ int len2 = 0;
+
+ // get the current title
+ if (len >= 1) {
+ wcscpy_s(lastFile, MAX_PATH, (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, curpos, IPC_GETPLAYLISTFILEW));
+ wcscpy_s(title, ARRAYSIZE(title), (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, curpos, IPC_GETPLAYLISTTITLEW));
+ } else {
+ title[0] = lastFile[0] = 0;
+ }
+
+ // get the position of the next track if possible
+ pos = GetNextTracks(len, pos, next);
+
+ if (skipMetada == false) {
+ AddColItem(lastFile, 1, inWinWa, IDC_METALIST, 0);
+ GetFileMetaData(lastFile, L"title", song, ARRAYSIZE(song));
+ AddColItem((song && song[0] ? song : title), 1, inWinWa, IDC_METALIST, 1);
+ GetFileMetaData(lastFile, L"artist", artist, ARRAYSIZE(artist));
+ AddColItem((artist[0] ? artist : L"<emtpy>"), 1, inWinWa, IDC_METALIST, 2);
+ GetFileMetaData(lastFile, L"album", album, ARRAYSIZE(album));
+ AddColItem((album[0] ? album : L"<emtpy>"), 1, inWinWa, IDC_METALIST, 3);
+ GetFileMetaData(lastFile, L"genre", genre, ARRAYSIZE(genre));
+ AddColItem((genre[0] ? genre : L"<emtpy>"), 1, inWinWa, IDC_METALIST, 4);
+ GetFileMetaData(lastFile, L"year", year, ARRAYSIZE(year));
+ AddColItem((year[0] ? year : L"<emtpy>"), 1, inWinWa, IDC_METALIST, 5);
+ GetFileMetaData(lastFile, L"comment", comment, ARRAYSIZE(comment));
+ AddColItem((comment[0] ? comment : L"<emtpy>"), 1, inWinWa, IDC_METALIST, 6);
+
+ if (WASABI_API_MEMMGR && playingImage) {
+ WASABI_API_MEMMGR->sysFree(playingImage); playingImage = 0;
+ }
+
+ playingLength = 0;
+ playingType = 0x4100; // default in-case of issue
+
+ // attempt to get the type of the image, defaulting to jpeg for older versions
+ // as only on 5.64+ are we able to query Winamp properly for the artwork type
+ wchar_t* mimeType = 0, *uiType = L"jpeg";
+ if (GetWinampVersion(module.hwndParent) >= 0x5064)
+ {
+ LPVOID bits;
+ size_t len;
+ if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArtData(lastFile, L"cover", &bits, &len, &mimeType) == ALBUMART_SUCCESS) {
+ // make sure to free the original image after we've converted
+ playingImage = (ARGB32 *)bits;
+ playingLength = len;
+
+ wchar_t *ext = &mimeType[0];
+ if (ext && *ext) {
+ uiType = ext;
+ if (!wcsnicmp(ext, L"jpeg", 4) || !wcsnicmp(ext, L"jpg", 3)) {
+ playingType = 0x4100;
+ } else if (!wcsnicmp(ext, L"png", 3)) {
+ playingType = 0x4101;
+ } else if (!wcsnicmp(ext, L"bmp", 3)) {
+ playingType = 0x4102;
+ } else if (!wcsnicmp(ext, L"gif", 3)) {
+ playingType = 0x4103;
+ }
+ }
+
+ // update the current artwork on the winamp panel
+ // have to decode as we get the raw data normally
+ HWND artwork = GetDlgItem(inWinWa, IDC_ARTWORK);
+ HBITMAP bm = getBitmap(decompressImage(playingImage, playingLength, &playingImage_w, &playingImage_h), playingImage_w, playingImage_h);
+ HBITMAP bmold = (HBITMAP)SendMessage(artwork, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bm);
+ if (bmold) DeleteObject(bmold);
+ InvalidateRect(artwork, NULL, TRUE);
+ }
+ } else {
+ playingImage_w = 0, playingImage_h = 0;
+ if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArt(lastFile, L"cover", &playingImage_w, &playingImage_h, &playingImage) == ALBUMART_SUCCESS) {
+ // make sure to free the original image after we've converted
+ ARGB32 *firstPlayingImage = playingImage;
+
+ // update the current artwork on the winamp panel
+ // no need to decode as it's already done by this
+ HWND artwork = GetDlgItem(inWinWa, IDC_ARTWORK);
+ HBITMAP bm = getBitmap(playingImage, playingImage_w, playingImage_h);
+ HBITMAP bmold = (HBITMAP)SendMessage(artwork, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bm);
+ if (bmold) DeleteObject(bmold);
+ InvalidateRect(artwork, NULL, TRUE);
+
+ playingImage = writeImg(playingImage, playingImage_w, playingImage_h, &playingLength, L"jpeg");
+ WASABI_API_MEMMGR->sysFree(firstPlayingImage);
+ } else {
+ playingImage = 0;
+ }
+ }
+
+ wchar_t tmp3[1024] = {0};
+ if(playingImage)
+ {
+ StringCchPrintfW(tmp3, ARRAYSIZE(tmp3), L"Playing Artwork: Width=%d; Height=%d; Data=%s;",
+ playingImage_w, playingImage_h, sizeStr(playingLength));
+
+ // only use this on compatible Winamp installs where possible
+ wchar_t *mime = 0;
+ if (GetWinampVersion(module.hwndParent) >= 0x5064 &&
+ AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArtOrigin(lastFile, L"cover", &mime)) {
+ if (mime)
+ {
+ uiType = wcschr(mime, L'/');
+ if (uiType && *uiType)
+ {
+ uiType++;
+ }
+ }
+ } else {
+ if (mimeType)
+ {
+ uiType = wcschr(mimeType, L'/');
+ if (uiType && *uiType)
+ {
+ uiType++;
+ }
+ }
+ }
+
+ wchar_t buf[256] = {0};
+ StringCchPrintfW(buf, ARRAYSIZE(buf), LocalisedString(IDS_ARTWORK_SIZES, NULL, 0),
+ playingImage_w, playingImage_h, sizeStr(playingLength),
+ (uiType && *uiType ? uiType : mime));
+ SetDlgItemTextW(inWinWa, IDC_ARTWORK3, buf);
+ WASABI_API_MEMMGR->sysFree(mime);
+ }
+ else
+ {
+ // update the current artwork on the winamp panel
+ // by setting a generic image when nothing loaded
+ HWND artwork = GetDlgItem(inWinWa, IDC_ARTWORK);
+ HBITMAP bmold = (HBITMAP)SendMessage(artwork, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)0);
+ if (bmold) DeleteObject(bmold);
+ SetDlgItemTextW(inWinWa, IDC_ARTWORK3, L"");
+ StringCchPrintfW(tmp3, ARRAYSIZE(tmp3), L"Playing Artwork: Cleared;");
+ }
+ if (mimeType) WASABI_API_MEMMGR->sysFree(mimeType);
+ ShowWindowDlgItem(inWinWa, IDC_ARTWORK, (playingLength > 0));
+ ShowWindowDlgItem(inWinWa, IDC_ARTWORK2, (playingLength == 0));
+ ShowWindowDlgItem(inWinWa, IDC_ARTWORK3, (playingLength > 0));
+ CreateLogFileMessage(buffer2, tmp3, &len2);
+ }
+
+ // look at the playback queue so we can get the correct 'next song'
+ if (WASABI_API_SVC && !WASABI_API_QUEUEMGR) {
+ // due to loading orders its possible the queue won't have been loaded on init so check
+ ServiceBuild(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID);
+ }
+
+ std::vector<std::wstring> nextList;
+ std::vector<int> nextListIdx;
+ UpdateNextTracks(next, pos, nextListIdx, nextList);
+
+ StringCchPrintfW(tmp2, ARRAYSIZE(tmp2), L"Metadata: Artist=%s; Album=%s; Genre=%s; Year=%s; Comment=%s; Title=%s;",
+ artist, album, genre, year, comment, (song && song[0] ? song : title));
+ len = 0;
+ CreateLogFileMessage(buffer, tmp2, &len);
+
+ // update the title cache for all of the encoders otherwise we might fail, etc
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ MY_T_OUTPUT *Out = &Output[i];
+
+ if (Out->nextTrackLog) {
+ WriteNextTracks(i, module.hwndParent, nextListIdx, nextList, !!Out->nextTrackLogXML);
+ }
+
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[i].UpdateTitleCache(title, nextList, song, album, artist, genre, comment, year, Out->Handle, !!Out->NextTitles);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+
+ // skip this all if the mode is not enabled and only in SC2 mode
+ if ((LOBYTE(Out->Config.protocol) != 1) && Out->useArt) {
+ // this will only update generally on first connection
+ if (Out->useStreamArt && streamImage[i] == (ARGB32 *)-1) {
+ UpdateStreamAlbumArt(Out->Handle, i, Out->stationArtPath, !!Out->useStreamArt);
+ }
+
+ // this will update against what is read from the playing
+ if (Out->usePlayingArt) {
+ UpdatePlayingAlbumArt(Out->Handle, i, Out->useArt && Out->usePlayingArt);
+ }
+ }
+
+ // save the updated metadata to the log file (if enabled)
+ if (Out->Logging && logFiles[i] != INVALID_HANDLE_VALUE) {
+ DWORD written = 0;
+ WriteFile(logFiles[i], buffer, len, &written, 0);
+ WriteFile(logFiles[i], buffer2, len2, &written, 0);
+ }
+ }
+ }
+ }
+ break;
+
+ case WM_TIMER:
+ {
+ if (wParam == IDD_MAIN || wParam == IDD_INPUT) {
+ if (VU.update || VU.update != VU.lastUpdate)
+ {
+ UpdateVUMeters();
+ }
+ }
+
+ if (wParam == 1234) { // input timer
+ if (InputDevice == 1) {
+ for (int i = 0; i < NUM_BUFFERS; i++) {
+ if (Soundcard.isFilled(last_buffer)) {
+ short *buffer = Soundcard[last_buffer];
+ int scardsmps = Soundcard.getNumSamples(last_buffer);
+ int numsamps = scardsmps * InputConfig.nch;
+ if (!VU.update) {
+ for (int j = 0; j < numsamps; j++) {
+ if (VU.vu_l < buffer[j]) VU.vu_l = buffer[j];
+ if (LineInputAttribs[Input_CurSelPos].nch == 2) {
+ if (VU.vu_r < buffer[j + 1]) VU.vu_r = buffer[j + 1];
+ j++;
+ }
+ }
+ if (LineInputAttribs[Input_CurSelPos].nch == 1) VU.vu_r = VU.vu_l;
+ VU.update = 1;
+ }
+ if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) {
+ Crossfader->put(buffer, scardsmps);
+ ReleaseMutex(cf_mutex);
+ }
+ Soundcard.cycleBuffer(last_buffer++);
+ if (last_buffer >= NUM_BUFFERS) last_buffer = 0;
+ }
+ }
+ }
+ } else if (wParam == IDD_MAIN || wParam == IDD_CONNECTION ||
+ wParam == IDD_INPUT || wParam == IDD_ABOUT) {
+ wchar_t title[1024] = {0};
+ wchar_t next[1024] = {0};
+ int states[NUM_OUTPUTS] = {OUT_ERROR,OUT_ERROR,OUT_ERROR,OUT_ERROR,OUT_ERROR};
+
+ for (int i = 0; i < NUM_OUTPUTS; i++) {
+ wchar_t tmp[1024] = {0};
+ static wchar_t old_tmp[NUM_ENCODERS][1024] = {0};
+ MY_T_OUTPUT *Out = &Output[i];
+ SHOUTCAST_OUTPUT *Enc = Out->Encoder != -1 ? &Encoder[Out->Encoder] : NULL;
+ bool encoder_ok = false;
+ if (Enc) {
+ C_ENCODER *Encoder = Enc->GetEncoder();
+ if (Encoder) {
+ if (strcmp(Encoder->GetName(), "MP3 Encoder") == 0) {
+ encoder_ok = (libinst != NULL);
+ } else {
+ encoder_ok = true;
+ }
+ }
+ }
+ lastEnable[i] = (encoder_ok && Out->Config.Address[0] && Out->Config.Password[0] &&
+ stationNameAllowed(Out->Config.Description));
+
+ if (Out->Encoder == -1 || Out->Handle == -1) {
+ LocalisedString(IDS_NOT_CONNECTED, tmp, ARRAYSIZE(tmp));
+ if (wParam == IDD_CONNECTION && i == Connection_CurSelPos) {
+ GetDlgItemTextW(out_wnd[3].hWnd, IDC_TITLE, title, sizeof (title) / sizeof (wchar_t));
+ GetDlgItemTextW(out_wnd[3].hWnd, IDC_NEXT, next, sizeof (next) / sizeof (wchar_t));
+ }
+ } else {
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ T_OUTPUT_INFO *Info = Enc->GetOutputInfo(Out->Handle);
+ if (title != NULL && next != NULL && Info != NULL) {
+ if (Info->Title)
+ {
+ free(Info->Title);
+ }
+ Info->Title = _wcsdup(title);
+ }
+
+ states[i] = Enc->GetState(Out->Handle);
+ switch (states[i]) {
+ case OUT_ERROR:
+ {
+ LocalisedString(IDS_ERROR, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_DISCONNECTED:
+ {
+ if (Info->Succeeded < 0) {
+ if (Info->ErrorMsg && Info->ErrorMsg[0]) {
+ // if an error is reported, try to give a user-friendly error message
+ if (!strcmp("NAK:Deny", Info->ErrorMsg))
+ // localised Password error
+ LocalisedString(IDS_PASS_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("CipherFail",Info->ErrorMsg))
+ // localised cipher error
+ LocalisedString(IDS_CIPHER_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("BitrateError",Info->ErrorMsg))
+ // localised bitrate error (not allowed / not supported)
+ LocalisedString(IDS_BITRATE_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("StreamID",Info->ErrorMsg))
+ // localised stream moved error
+ LocalisedString(IDS_STREAMID_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("StreamMoved",Info->ErrorMsg))
+ LocalisedString(IDS_STREAM_MOVED_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("VersionError",Info->ErrorMsg))
+ // localised version error
+ LocalisedString(IDS_VERSION_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("Blocked",Info->ErrorMsg))
+ // localised blocked error
+ LocalisedString(IDS_BLOCKED_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("InUse",Info->ErrorMsg))
+ // localised in use error
+ LocalisedString(IDS_IN_USE_ERROR, tmp, ARRAYSIZE(tmp));
+ else if (!strcmp("ParseError",Info->ErrorMsg))
+ // localised parse error
+ LocalisedString(IDS_PARSE_ERROR, tmp, ARRAYSIZE(tmp));
+ else
+ // non localised dynamic nak error
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), L"%hs", Info->ErrorMsg);
+ } else {
+ // localised Password error
+ LocalisedString(IDS_PASS_ERROR, tmp, ARRAYSIZE(tmp));
+ }
+ Out->Config.AutoRecon = 0;
+ } else {
+ if (Info->last_state == OUT_RECV_CIPHER ||
+ Info->last_state == OUT_REQUEST_CIPHER) {
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_ENABLE_OTHER_MODE, NULL, 0), (Info->last_state == OUT_RECV_CIPHER ? 1 : 2));
+ } else {
+ LocalisedString((lastEnable[i] ? IDS_NOT_CONNECTED : IDS_NOT_CONFIGURED), tmp, ARRAYSIZE(tmp));
+ }
+ lastSec[i] = 0;
+ }
+ }
+ break;
+ case OUT_CONNECT:
+ {
+ if ((clock() - Info->ConnectionTime) / CLOCKS_PER_SEC < 1) {
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_RECONNECTING_X, NULL, 0), 1);
+ } else {
+ LocalisedString(IDS_CONNECTING, tmp, ARRAYSIZE(tmp));
+ }
+ }
+ break;
+ case OUT_REQUEST_CIPHER:
+ {
+ LocalisedString(IDS_SEND_CIPHER_REQUEST, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_CIPHER:
+ {
+ LocalisedString(IDS_CIPHER_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SENDAUTH:
+ {
+ LocalisedString(IDS_SENDING_AUTH, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECVAUTHRESPONSE:
+ {
+ LocalisedString(IDS_RECEIVING_AUTH_RESPONSE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_MIME:
+ {
+ LocalisedString(IDS_SENDING_CONTENT_TYPE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_MIME:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_BITRATE:
+ {
+ LocalisedString(IDS_SENDING_BITRATE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_BITRATE:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_BUFSIZE:
+ {
+ LocalisedString(IDS_SEND_BUF_SIZE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_BUFSIZE:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_MAX:
+ {
+ LocalisedString(IDS_SEND_MAX_PAYLOAD_SIZE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_MAX:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SENDYP:
+ {
+ LocalisedString(IDS_SEND_YP_INFO, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_INITFLUSH:
+ {
+ LocalisedString(IDS_SEND_FLUSH, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_INITFLUSH:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_INITSTANDBY:
+ {
+ LocalisedString(IDS_SEND_STANDBY, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_INITSTANDBY:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ /*case OUT_SEND_INTRO:
+ {
+ LocalisedString(IDS_SEND_INTRO_FILE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_INTRO:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_SEND_BACKUP:
+ {
+ LocalisedString(IDS_SEND_BACKUP_FILE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECV_BACKUP:
+ {
+ LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp));
+ }
+ break;*/
+ case OUT_SEND_ARTWORK:
+ case OUT_SEND_METADATA:
+ break;
+ case OUT_SENDCONTENT:
+ {
+ time_t time_value = time(NULL) - Info->ConnectedAt;
+ long hour = (long)(time_value/3600);
+ long min = (time_value/60)%60;
+ long sec = time_value%60;
+ static wchar_t format[256];
+ if (!format[0]) LocalisedString(IDS_SENT_X, format, ARRAYSIZE(format));
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), format, hour, min, sec, sizeStr(Info->BytesSent));
+ lastSec[i] = sec;
+
+ // do this to filter out some of the log
+ // events so it'll only happen every second
+ if (lastSec[i] == sec - 1) {
+ secChanged[i] = true;
+ }
+ }
+ break;
+ case OUT_DISCONNECT:
+ {
+ LocalisedString(IDS_DISCONNECTING, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ case OUT_RECONNECT:
+ {
+ if (Info->Reconnect) {
+ int seconds = Info->ReconnectTime - ((clock() - Info->ConnectionTime) / CLOCKS_PER_SEC);
+ if (seconds > 0) {
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_RECONNECTING_X, NULL, 0), seconds);
+ } else {
+ if (Info->Switching) {
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_SWITCHING_PROTOCOL, NULL, 0),
+ (Info->Switching == 2 ? 2 : 1), (Info->Switching == 2 ? 1 : 2));
+ } else {
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_RECONNECTING_X, NULL, 0), seconds);
+ }
+ }
+ } else {
+ if (Info->Switching) {
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_SWITCHING_PROTOCOL, NULL, 0),
+ (Info->Switching == 2 ? 2 : 1), (Info->Switching == 2 ? 1 : 2));
+ } else {
+ LocalisedString(IDS_DISCONNECTING, tmp, ARRAYSIZE(tmp));
+ }
+ }
+ }
+ break;
+ case OUT_TITLESENDUPDATE:
+ {
+ LocalisedString(IDS_SEND_TITLE_UPDATE, tmp, ARRAYSIZE(tmp));
+ }
+ break;
+ }
+
+ if (Out->AutoConnect && states[i] == OUT_DISCONNECTED) {
+ Enc->ConnectOutput(Out->Handle);
+ }
+
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+
+ // output log messages to the log files if enabled
+ // but filter things a bit more with the sent state
+ if (tmp[0] && wcsnicmp(old_tmp[i], tmp, ARRAYSIZE(tmp))) {
+ if (Out->Logging && logFiles[i] != INVALID_HANDLE_VALUE) {
+ if (states[i] != OUT_SENDCONTENT || (states[i] == OUT_SENDCONTENT && secChanged[i] == true)) {
+ DWORD written = 0;
+ int len = 0;
+ char buffer[1024];
+ if (states[i] == OUT_CONNECT) {
+ wchar_t tmp2[2048] = {0};
+ int protocol = (LOBYTE(Out->Config.protocol) != 1 ? 2 : 1);
+ StringCchPrintfW(tmp2, ARRAYSIZE(tmp2),
+ L"Connecting to... Server: %hs; Port: %d; Mode: v%d; Stream ID: %hs; DJ / User ID: %hs",
+ Out->Config.Address, Out->Config.Port, protocol,
+ (protocol == 1 ? "n/a" : Out->Config.StationID),
+ (!Out->Config.UserID[0] ? "n/a" : Out->Config.UserID));
+ CreateLogFileMessage(buffer, tmp2, &len);
+ WriteFile(logFiles[i], buffer, len, &written, 0);
+ } else {
+ CreateLogFileMessage(buffer, tmp, &len);
+ WriteFile(logFiles[i], buffer, len, &written, 0);
+ }
+ }
+ }
+ secChanged[i] = false;
+ }
+
+ // update summary and output page view and text states as applicable
+ if (wParam == IDD_MAIN ||
+ (wParam == IDD_CONNECTION && i == Connection_CurSelPos)) {
+ // update status text for the output being processed
+ if (tmp[0]) {
+ if (wParam == IDD_CONNECTION && i == Connection_CurSelPos) {
+ SetDlgItemTextW(wnd[1].hWnd, IDC_STATUS, tmp);
+ }
+ AddColItem(tmp, 2, wnd[0].hWnd, IDC_OUTPUTSTATUS, i);
+ }
+
+ bool encoder_ok = false;
+ if (Enc) {
+ C_ENCODER *Encoder = Enc->GetEncoder();
+ if (Encoder) {
+ if (strcmp(Encoder->GetName(), "MP3 Encoder") == 0) {
+ encoder_ok = (libinst != NULL);
+ } else {
+ encoder_ok = true;
+ }
+ }
+ }
+
+ // update the playback buttons on the summary page
+ int mode = (Out->Config.Address[0] ? (Out->Config.Password[0] ?
+ (stationNameAllowed(Out->Config.Description) ?
+ (encoder_ok ? (states[i] == OUT_DISCONNECTED ? 0 :
+ states[i] == OUT_DISCONNECT ? 1 : states[i] == OUT_RECONNECT ? 7 : 2) : 6) : 5) : 4) : 3);
+
+ // used to limit the amount of processing which is done for this to keep updates to just what is needed
+ if (lastMode[i] == -1 || lastMode[i] != mode) {
+ int image_id[] = {IDI_PLAY, IDI_KILL, IDI_STOP, IDI_PLAY, IDI_PLAY, IDI_PLAY, IDI_PLAY, IDI_STOP};
+
+ // do checks to see if we need to update the error state of the tab items
+ int oldMode = lastMode[i];
+ lastMode[i] = mode;
+
+ RECT r = {0};
+ if (IsWindow(outTabWnd)) {
+ int index[] = {0, 0, 1, 2};
+ if (lastMode[i] >= 3 && lastMode[i] <= 6) {
+ TabCtrl_GetItemRect(outTabWnd, index[(lastMode[i] - 3)], &r);
+ InvalidateRect(outTabWnd, &r, 0);
+ }
+
+ if (oldMode >= 3 && oldMode <= 6) {
+ TabCtrl_GetItemRect(outTabWnd, index[(oldMode - 3)], &r);
+ InvalidateRect(outTabWnd, &r, 0);
+ }
+ }
+
+ TabCtrl_GetItemRect(GetDlgItem(hMainDLG, IDC_TAB), 1, &r);
+ InvalidateRect(GetDlgItem(hMainDLG, IDC_TAB), &r, 0);
+
+ // control the button states on the pages
+ EnableWindow(buttonWnd[i], lastEnable[i]);
+
+ EnableWindowDlgItem(wnd[1].hWnd, IDC_CONNECT, lastEnable[i]);
+ EnableWindowDlgItem(wnd[1].hWnd, IDC_AUTOCONNECT, lastEnable[i]);
+
+ SendMessage(buttonWnd[i], BM_SETIMAGE, IMAGE_ICON,
+ (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(image_id[mode]),
+ IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION));
+
+ // control the 'connect' button to be disabled when no encoder / no password / invalid station name
+ if (i == Connection_CurSelPos) {
+ int button_id[] = {IDS_CONNECT, IDS_KILL, IDS_DISCONNECT, IDS_SET_SERVER, IDS_SET_PASSWORD, IDS_CHANGE_NAME, IDS_SET_ENCODER, IDS_ABORT};
+ SetDlgItemTextW(wnd[1].hWnd, IDC_CONNECT, LocalisedString(button_id[mode], NULL, 0));
+ LockOptionControls((states[i] == OUT_DISCONNECTED && Out->Handle != -1));
+
+ InvalidateRect(GetDlgItem(out_wnd[0].hWnd, IDC_ADDRESS_HEADER), 0, 0);
+ InvalidateRect(GetDlgItem(out_wnd[0].hWnd, IDC_PASSWORD_HEADER), 0, 0);
+ InvalidateRect(GetDlgItem(out_wnd[1].hWnd, IDC_NAME_HEADER), 0, 0);
+ InvalidateRect(GetDlgItem(out_wnd[2].hWnd, IDC_ENCODER_HEADER), 0, 0);
+ }
+ }
+
+ // update the title set
+ if (Out->AutoTitle) SetDlgItemTextW(out_wnd[0].hWnd, IDC_TITLE, title);
+ }
+
+ // preserve the last status message for filtering of the log output, etc
+ if (tmp[0]) wcsncpy(old_tmp[i], tmp, 1024);
+ }
+ } else if (wParam == 1337) { // stream title update
+ KillTimer(hDlg, wParam);
+ PostMessage(hMainDLG, WM_USER, 0, nowPlayingID);
+ } else if (wParam == 2234) { // fade (music)
+ if (InputDevice) {
+ clock_t MusCurTime = clock();
+ if (FadeStartTime == 0) {
+ FadeStartTime = MusCurTime;
+ }
+ MusCurTime -= FadeStartTime;
+ int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
+ if (MusCurTime < (FadeTime * 100)) {
+ int musvol = FadeOut ? (MusVol * 10)+((((Mus2Vol - MusVol)*10) * MusCurTime) / (FadeTime * 100)) : (Mus2Vol * 10)+((((MusVol - Mus2Vol)*10) * MusCurTime) / (FadeTime * 100));
+ setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, musvol);
+ } else {
+ if (FadeOut) {
+ setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, Mus2Vol * 10);
+ } else {
+ setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, MusVol * 10);
+ }
+ FadeStartTime = 0;
+ KillTimer(hDlg, wParam);
+ // kill captured device
+ #ifdef CAPTURE_TESTING
+ if (!FadeOut) {// end_capture();
+ if (pPlayer != NULL) {
+ pPlayer->Stop();
+ delete pPlayer;
+ pPlayer = NULL;
+ }
+ }
+ #endif
+ }
+ }
+ } else if (wParam == 2235) { // fade (capture device)
+ if (InputDevice) {
+ clock_t MicCurTime = clock();
+ if (MicFadeStartTime == 0) {
+ MicFadeStartTime = MicCurTime;
+ }
+ MicCurTime -= MicFadeStartTime;
+ int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
+ if (MicCurTime < (MicFadeTime * 100)) {
+ int micvol = FadeOut ? ((MicVol * 10) * MicCurTime) / (MicFadeTime * 100) : (MicVol * 10)+(((MicVol*-10) * MicCurTime) / (MicFadeTime * 100));
+ setlev(micsrc, micvol);
+ } else {
+ if (FadeOut) {
+ setlev(micsrc, MicVol * 10);
+ } else {
+ setlev(micsrc, 0);
+ }
+ MicFadeStartTime = 0;
+ KillTimer(hDlg, wParam);
+ // kill captured device
+ #ifdef CAPTURE_TESTING
+ if (!FadeOut) {// end_capture();
+ if (pPlayer != NULL) {
+ pPlayer->Stop();
+ delete pPlayer;
+ pPlayer = NULL;
+ }
+ }
+ #endif
+ }
+ }
+ } else if (wParam == 666) { // INI save timer
+ if (!ini_modified) break;
+ ini_modified = 0;
+ int i;
+ WritePrivateProfileInt("ofnidx", lastFilterIndex, 0, 0);
+ WritePrivateProfileInt("CurTab", curtab, 0, 0);
+ WritePrivateProfileInt("Connection_CurSelPos", Connection_CurSelPos, 0, 0);
+ WritePrivateProfileInt("Connection_CurTab", curouttab, 0, 0);
+ WritePrivateProfileInt("Encoder_CurSelPos", Encoder_CurSelPos, 0, 0);
+ WritePrivateProfileInt("Input_CurSelPos", Input_CurSelPos, 0, 0);
+ WritePrivateProfileInt("InputDevice", InputDevice, 0, 0);
+ WritePrivateProfileInt("MusicVolume", MusVol, 0, 0);
+ WritePrivateProfileInt("BGMusicVolume", Mus2Vol, 0, 0);
+ WritePrivateProfileInt("MicVolume", MicVol, 0, 0);
+ WritePrivateProfileInt("PTT_FT", FadeTime, 0, 0);
+ WritePrivateProfileInt("PTT_MicFT", MicFadeTime, 0, 0);
+ WritePrivateProfileInt("PTT_MicInput", Input_Device_ID, 0, 0);
+ WritePrivateProfileInt("PTT_Restore", Restore_PTT, 0, 0);
+
+ if (!IsIconic(hDlg)) {
+ WritePrivateProfileInt("WindowLeft", mainrect.left, 0, 0);
+ WritePrivateProfileInt("WindowTop", mainrect.top, 0, 0);
+ }
+
+ for (i = 0; i < NUM_ENCODERS; i++) {
+ int Type = 0;
+ char name[32];
+ StringCchPrintfA(name, ARRAYSIZE(name), "Encoder %u", i + 1);
+
+ if (WaitForSingleObject(Enc_mutex[i], INFINITE) == WAIT_OBJECT_0) {
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) {
+ int infosize = sizeof (T_ENCODER_MP3_INFO);
+ T_ENCODER_MP3_INFO *EncInfo = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize);
+ WritePrivateProfileInt("BitRate", EncInfo->output_bitRate, name, 0);
+ WritePrivateProfileInt("SampleRate", EncInfo->output_sampleRate, name, 0);
+ WritePrivateProfileInt("NumChannels", EncInfo->output_numChannels, name, 0);
+ WritePrivateProfileInt("QualityMode", EncInfo->QualityMode, name, 0);
+ Type = 1;
+ } else if (strcmp(Enc->GetName(), "Fraunhofer Encoder") == 0) { // FHG AAC
+ ((C_ENCODER_NSV*) Enc)->FillConfFile(IniName, name);
+ Type = 2;
+ } else if (strcmp(Enc->GetName(), "AAC+ Encoder") == 0) { // AAC+
+ ((C_ENCODER_NSV*) Enc)->FillConfFile(IniName, name);
+ Type = 2;
+ }
+#ifdef USE_OGG
+ else if (strcmp(Enc->GetName(), "OGG Vorbis Encoder") == 0) { // OGG
+ ((C_ENCODER_NSV*) Enc)->FillConfFile(IniName, name);
+ Type = 4;
+ }
+#endif
+ }
+ ReleaseMutex(Enc_mutex[i]);
+ }
+ WritePrivateProfileInt("Type", Type, name, 0);
+ }
+
+ for (i = 0; i < NUM_OUTPUTS; i++) {
+ T_OUTPUT_CONFIG *Out = &Output[i].Config;
+ WritePrivateProfileString(Out->Name, "Address", Out->Address, IniName);
+ WritePrivateProfileInt("Port", Out->Port, Out->Name, 0);
+ WritePrivateProfileString(Out->Name, "UserID", Out->UserID, IniName);
+ WritePrivateProfileString(Out->Name, "StreamID", Out->StationID, IniName);
+ WritePrivateProfileString(Out->Name, "Password", Out->Password, IniName);
+ // disabled saving of this as it defeats the point of setting it on load
+ // (as it's otherwise over-written with the correct value from the server)
+ //WritePrivateProfileString(Out->Name, "Cipherkey", Out->cipherkey, IniName);
+ WritePrivateProfileString(Out->Name, "Description", Out->Description, IniName);
+ WritePrivateProfileString(Out->Name, "URL", Out->ServerURL, IniName);
+ WritePrivateProfileString(Out->Name, "Genre3", Out->Genre, IniName);
+ WritePrivateProfileString(Out->Name, "AIM", Out->AIM, IniName);
+ WritePrivateProfileString(Out->Name, "ICQ", Out->ICQ, IniName);
+ WritePrivateProfileString(Out->Name, "IRC", Out->IRC, IniName);
+ WritePrivateProfileInt("Public", Out->Public ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("AutoRecon", Out->AutoRecon ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("ReconTime", Out->ReconTime ? Out->ReconTime : 1, Out->Name, 0);
+ WritePrivateProfileInt("doTitleUpdate", Out->doTitleUpdate ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileString(Out->Name, "now", Out->Now, IniName);
+ WritePrivateProfileString(Out->Name, "next", Out->Next, IniName);
+ WritePrivateProfileInt("AutoTitle", Output[i].AutoTitle ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("AutoConnect", Output[i].AutoConnect ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("Logging", Output[i].Logging ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("LogCOS", Output[i].LogCOS ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("NextTitles", Output[i].NextTitles ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("Protocol", Output[i].Config.protocol, Out->Name, 0);
+ WritePrivateProfileInt("nextTrackLog", Output[i].nextTrackLog ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("nextTrackLogXML", Output[i].nextTrackLogXML ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileString(Out->Name, "nextTrackPath", ConvertToUTF8(Output[i].nextTrackPath), IniName);
+ WritePrivateProfileInt("useArt", Output[i].useArt ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("usePlayingArt", Output[i].usePlayingArt ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileInt("useStreamArt", Output[i].useStreamArt ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileString(Out->Name, "stationArtPath", ConvertToUTF8(Output[i].stationArtPath), IniName);
+ WritePrivateProfileInt("saveEncoded", Output[i].saveEncoded ? 1 : 0, Out->Name, 0);
+ WritePrivateProfileString(Out->Name, "saveEncodedPath", ConvertToUTF8(Output[i].saveEncodedPath), IniName);
+ WritePrivateProfileInt("Encoder", Output[i].Encoder, Out->Name, 0);
+ }
+ }
+ }
+ break;
+
+ case WM_SHOWWINDOW:
+ {
+ if (IsWindow(hDlg) && hDlg == hMainDLG && wnd[curtab].timer_freq != 0) {
+ KillTimer(hDlg, wnd[curtab].id);
+ SetTimer(hDlg, wnd[curtab].id, wnd[curtab].timer_freq, NULL);
+ } else if (IsWindow(hDlg) && hDlg == wnd[2].hWnd && in_wnd[InputDevice].timer_freq != 0) {
+ KillTimer(hDlg, in_wnd[InputDevice].id);
+ SetTimer(hDlg, in_wnd[InputDevice].id, in_wnd[InputDevice].timer_freq, NULL);
+ } else if (IsWindow(hDlg) && hDlg == wnd[1].hWnd && out_wnd[curouttab].timer_freq != 0) {
+ KillTimer(hDlg, out_wnd[curouttab].id);
+ SetTimer(hDlg, out_wnd[curouttab].id, out_wnd[curouttab].timer_freq, NULL);
+ }
+ }
+ break;
+
+ //Handle Minimize to systray here
+ case WM_SIZE:
+ {
+ switch (wParam) {
+ case SIZE_RESTORED:
+ {
+ RemoveSystrayIcon(hDlg, SYSTRAY_ICY_ICON);
+ ShowWindow(hDlg, SW_SHOW);
+ }
+ break;
+ case SIZE_MINIMIZED:
+ {
+ AddSystrayIcon(hDlg, SYSTRAY_ICY_ICON, GetICYIcon(),
+ SYSTRAY_MAXIMIZE_MSG, szDescription2W);
+ ShowWindow(hDlg, SW_HIDE);
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case IDC_NOTITLES:
+ case IDC_AUTOTITLE:
+ case IDC_MANUALTITLE:
+ //case IDC_EXTERNALTITLE:
+ {
+ // TODO will need to change around some of the logic to cope with the external option
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ int lastAutoTitle = Out->AutoTitle;
+ Out->Config.doTitleUpdate = (LOWORD(wParam) != IDC_NOTITLES);
+ Out->AutoTitle = (LOWORD(wParam) == IDC_AUTOTITLE);
+ ini_modified = 1;
+ CheckRadioButton(hDlg, IDC_NOTITLES, IDC_MANUALTITLE, LOWORD(wParam));
+ UpdateTitleControls();
+ // if enabling then send the title update
+ if ((LOWORD(wParam) == IDC_AUTOTITLE) && lastAutoTitle != Out->AutoTitle) {
+ if (Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ std::vector<std::wstring> nextList;
+ nextList.clear();
+ Enc->UpdateTitle(0, nextList, Out->Handle, !!Out->NextTitles, true);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_SENDNEXTTITLES:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->NextTitles = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_VIEW_LOG:
+ {
+ wchar_t file[MAX_PATH];
+ ShellExecuteW(hMainDLG, L"open", GetSCLogFile(module.hwndParent, ARRAYSIZE(file), file, Connection_CurSelPos), 0, NULL, SW_SHOW);
+ }
+ break;
+
+ case IDC_CLEAR_LOG:
+ {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ if (Out->Logging && logFiles[Connection_CurSelPos] != INVALID_HANDLE_VALUE) {
+ SetFilePointer(logFiles[Connection_CurSelPos], 0, NULL, FILE_BEGIN);
+ SetEndOfFile(logFiles[Connection_CurSelPos]);
+ } else {
+ wchar_t file[MAX_PATH];
+ HANDLE handle = CreateFileW(GetSCLogFile(module.hwndParent, ARRAYSIZE(file), file, Connection_CurSelPos),
+ GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+ if (handle != INVALID_HANDLE_VALUE) {
+ SetFilePointer(handle, 0, NULL, FILE_BEGIN);
+ SetEndOfFile(handle);
+ CloseHandle(handle);
+ }
+ }
+ }
+ break;
+
+ case IDC_LOGGING:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->Logging = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ if (Out->Logging) {
+ StartLogging(Connection_CurSelPos,Out->LogCOS);
+ } else {
+ StopLogging(Connection_CurSelPos);
+ }
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_CLEAR_ON_STARTUP:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->LogCOS = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_NEXT_TRACK_LOG:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->nextTrackLog = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ if (Out->nextTrackLog) {
+ StartNextTracks(Connection_CurSelPos, Out->nextTrackPath);
+ FillNextTracks(Connection_CurSelPos, !!Out->nextTrackLogXML);
+ } else {
+ StopNextTracks(Connection_CurSelPos);
+ }
+ EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_XML, Out->nextTrackLog);
+ EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, Out->nextTrackLog);
+ EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_BROWSE, Out->nextTrackLog);
+
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_NEXT_TRACK_XML:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->nextTrackLogXML = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ // reset the file if changing the state
+ if (Out->nextTrackLog) {
+ StopNextTracks(Connection_CurSelPos);
+ StartNextTracks(Connection_CurSelPos, Out->nextTrackPath);
+ FillNextTracks(Connection_CurSelPos, !!Out->nextTrackLogXML);
+ }
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_NEXT_TRACK_EDIT:
+ {
+ if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_NEXT_TRACK_BROWSE:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ wchar_t filepath[MAX_PATH] = {0},
+ file[MAX_PATH] = {0},
+ filter[64] = {0};
+ // strip path so we can set initial directory, etc
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ wcsncpy(filepath, Out->nextTrackPath, MAX_PATH);
+ wcsncpy(file, Out->nextTrackPath, MAX_PATH);
+ PathRemoveFileSpecW(filepath);
+ PathStripPathW(file);
+
+ OPENFILENAMEW ofn = {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=hMainDLG;
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_ALL_FILES, filter, 64);
+ wchar_t * ptr=filter;
+ while(ptr && *ptr) {
+ if (*ptr==L'|') *ptr=0;
+ ptr++;
+ }
+
+ ofn.lpstrFilter=filter;
+ ofn.lpstrInitialDir=filepath;
+ ofn.lpstrFile=file;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt=L"log";
+ if (GetSaveFileNameW(&ofn))
+ {
+ // update things if the filename changed, etc
+ wcsncpy(Out->nextTrackPath, file, MAX_PATH);
+ SetDlgItemTextW(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, Out->nextTrackPath);
+ if (Out->nextTrackLog) {
+ StopNextTracks(Connection_CurSelPos);
+ StartNextTracks(Connection_CurSelPos, Out->nextTrackPath);
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_SAVE_ENCODED_AUDIO:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->saveEncoded = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+
+ // reset the file if changing the state
+ if (Out->saveEncoded) {
+ StartSaveEncoded(Connection_CurSelPos, Out->saveEncodedPath);
+ } else {
+ StopSaveEncoded(Connection_CurSelPos);
+ }
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_SAVE_ENCODED_AUDIO_EDIT:
+ {
+ if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_SAVE_ENCODED_AUDIO_BROWSE:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ wchar_t filepath[MAX_PATH] = {0},
+ file[MAX_PATH] = {0},
+ filter[64] = {0};
+ // strip path so we can set initial directory, etc
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ wcsncpy(filepath, Out->saveEncodedPath, MAX_PATH);
+ wcsncpy(file, Out->saveEncodedPath, MAX_PATH);
+ PathRemoveFileSpecW(filepath);
+ PathStripPathW(file);
+
+ OPENFILENAMEW ofn = {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=hMainDLG;
+ // sets the default extension if not specified to the type of the encoder being used
+ ofn.lpstrDefExt=(Enc_LastType[Connection_CurSelPos] != 0 ?
+ (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/mpeg") ? L"mp3" :
+ (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/ogg") ? L"ogg" : L"aac")) : L"");
+
+ StringCchPrintfW(filter, ARRAYSIZE(filter), WASABI_API_LNGSTRINGW(IDS_MPEG_AUDIO_FILES), ofn.lpstrDefExt, ofn.lpstrDefExt);
+ wchar_t* ptr=filter;
+ while(ptr && *ptr) {
+ if (*ptr==L'|') *ptr=0;
+ ptr++;
+ }
+
+ ofn.lpstrFilter=filter;
+ ofn.lpstrInitialDir=filepath;
+ ofn.lpstrFile=file;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
+
+ if (GetSaveFileNameW(&ofn))
+ {
+ // update things if the filename changed, etc
+ wcsncpy(Out->saveEncodedPath, file, MAX_PATH);
+ SetDlgItemTextW(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, Out->saveEncodedPath);
+ if (Out->saveEncoded) {
+ StopSaveEncoded(Connection_CurSelPos);
+ StartSaveEncoded(Connection_CurSelPos, Out->saveEncodedPath);
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_USE_ART:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->useArt = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, Out->useArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_STREAM, Out->useArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, Out->useArt && Out->useStreamArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, Out->useArt && Out->useStreamArt);
+
+ // only update if we need to do so
+ UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, Out->useArt && Out->useStreamArt);
+
+ // this will update against what is read from the playing
+ UpdatePlayingAlbumArt(Out->Handle, Connection_CurSelPos, Out->useArt && Out->usePlayingArt);
+
+ Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle);
+
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_USE_ART_PLAYING:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->usePlayingArt = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ ini_modified = 1;
+
+ if(Out->usePlayingArt){
+ playingLength = 0;
+ playingType = 0x4101; // default in-case of issue
+
+ int w = 0, h = 0;
+ if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArt(lastFile, L"cover", &w, &h, &playingImage) == ALBUMART_SUCCESS) {
+ // make sure to free the original image after we've converted
+ ARGB32 *firstPlayingImage = playingImage;
+ playingImage = writeImg(playingImage, w, h, &playingLength, L"png");
+ WASABI_API_MEMMGR->sysFree(firstPlayingImage);
+ } else {
+ playingImage = 0;
+ }
+ }
+
+ UpdatePlayingAlbumArt(Out->Handle, Connection_CurSelPos, Out->useArt && Out->usePlayingArt);
+ Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle);
+ }
+ }
+ break;
+
+ case IDC_USE_ART_STREAM:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->useStreamArt = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ ini_modified = 1;
+
+ // skip this all if the mode is not enabled and only in SC2 mode
+ if ((LOBYTE(Out->Config.protocol) != 1) && Out->useArt) {
+ // this will only update generally on first connection
+ if (Out->useStreamArt && streamImage[Connection_CurSelPos] == (ARGB32 *)-1) {
+ UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, !!Out->useStreamArt);
+ }
+ }
+
+ if (UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, !!Out->useStreamArt)) {
+ Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle);
+ }
+
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, Out->useStreamArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, Out->useStreamArt);
+ }
+ }
+ break;
+
+ case IDC_ART_EDIT:
+ {
+ if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_ART_BROWSE:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ wchar_t filepath[MAX_PATH] = {0},
+ file[MAX_PATH] = {0};
+ // strip path so we can set initial directory, etc
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ wcsncpy(filepath, Out->stationArtPath, MAX_PATH);
+ wcsncpy(file, Out->stationArtPath, MAX_PATH);
+ PathRemoveFileSpecW(filepath);
+ PathStripPathW(file);
+
+ OPENFILENAMEW ofn = {0};
+ ofn.lStructSize=sizeof(ofn);
+ ofn.hwndOwner=hMainDLG;
+ ofn.lpstrInitialDir=filepath;
+ ofn.lpstrFile=file;
+ ofn.nMaxFile=MAX_PATH;
+ ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST;
+ ofn.lpstrDefExt=L"png";
+ ofn.nFilterIndex=lastFilterIndex;
+
+ static int tests_run = 0;
+ static wchar_t filter[1024] = {0}, *sff = filter;
+
+ if (!tests_run) {
+ tests_run = 1;
+ FOURCC imgload = svc_imageLoader::getServiceType();
+ int n = WASABI_API_SVC->service_getNumServices(imgload);
+ for (int i=0; i<n; i++) {
+ waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload, i);
+ if (sf) {
+ svc_imageLoader * l = (svc_imageLoader*)sf->getInterface();
+ if (l) {
+ static int tests_idx[4] = {0, 1, 2, 3};
+ size_t size = 1024;
+ int j = 0, tests_str[] = {IDS_JPEG_FILE, IDS_PNG_FILE, IDS_BMP_FILE, IDS_GIF_FILE};
+ wchar_t *tests[] = {L"*.jpg", L"*.png", L"*.bmp", L"*.gif"};
+ for (int i = 0; i < ARRAYSIZE(tests); i++) {
+ if (l->isMine(tests[i])) {
+ tests_idx[j] = i;
+ j++;
+ int len = 0;
+ LocalisedString(tests_str[i], sff, size);
+ size-=(len = wcslen(sff)+1);
+ sff+=len;
+ wcsncpy(sff, (!i ? L"*.jpg;*.jpeg" : tests[i]), size);
+ size-=(len = wcslen(sff)+1);
+ sff+=len;
+ }
+ }
+ sf->releaseInterface(l);
+ }
+ }
+ }
+ }
+
+ ofn.lpstrFilter = filter;
+
+ if (GetOpenFileNameW(&ofn))
+ {
+ // update things if the filename changed, etc
+ wcsncpy(Out->stationArtPath, file, MAX_PATH);
+ SetDlgItemTextW(out_wnd[4].hWnd, IDC_ART_EDIT, Out->stationArtPath);
+
+ if (UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, !!Out->useStreamArt)) {
+ Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle);
+ }
+ }
+ lastFilterIndex = ofn.nFilterIndex;
+ }
+ }
+ break;
+
+ case IDC_AUTOCONNECT:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ Output[Connection_CurSelPos].AutoConnect = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ if (Output[Connection_CurSelPos].Encoder) {
+ if (WaitForSingleObject(Enc_mutex[Output[Connection_CurSelPos].Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[Output[Connection_CurSelPos].Encoder].UpdateOutput(Output[Connection_CurSelPos].Handle);
+ ReleaseMutex(Enc_mutex[Output[Connection_CurSelPos].Encoder]);
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_RECONNECT:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ Output[Connection_CurSelPos].Config.AutoRecon = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ if (Output[Connection_CurSelPos].Encoder) {
+ if (WaitForSingleObject(Enc_mutex[Output[Connection_CurSelPos].Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[Output[Connection_CurSelPos].Encoder].UpdateOutput(Output[Connection_CurSelPos].Handle);
+ ReleaseMutex(Enc_mutex[Output[Connection_CurSelPos].Encoder]);
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_PROTOCOL:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+
+ int cur_sel = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0),
+ protocol = SendMessage((HWND) lParam, CB_GETITEMDATA, cur_sel, 0);
+
+ Out->Config.protocol = MAKEWORD(protocol, !protocol);
+
+ // force a refresh on selection change
+ lastMode[Connection_CurSelPos] = -1;
+
+ // jkey: disable or enable userid stationid based on protocol.
+ if (Output[Connection_CurSelPos].Encoder) {
+ if (WaitForSingleObject(Enc_mutex[Output[Connection_CurSelPos].Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[Output[Connection_CurSelPos].Encoder].UpdateOutput(Output[Connection_CurSelPos].Handle);
+ ReleaseMutex(Enc_mutex[Output[Connection_CurSelPos].Encoder]);
+ }
+ }
+ //shoutcast 2 else 1 enable aim,icq,irc
+ EnableWindowDlgItem(hDlg, IDC_STATIONID, (LOBYTE(Output[Connection_CurSelPos].Config.protocol) == 1));
+ UpdateTitleControls();
+ }
+ }
+ break;
+
+ case IDC_CONNECT:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ SHOUTCAST_OUTPUT *Enc = Out->Encoder != -1 ? &Encoder[Out->Encoder] : NULL;
+ if (Enc && Out->Handle != -1) {
+ int state = Enc->GetState(Out->Handle);
+ if (state == OUT_DISCONNECTED) { // disconnected... connect now
+ Enc->ConnectOutput(Out->Handle);
+ } else { // connected... disconnect now
+ Enc->DisconnectOutput(Out->Handle);
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_DEVBOX:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ Input_Device_ID = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0);
+ }
+ }
+ break;
+
+ case IDC_LOCK:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ int checked = SendMessage((HWND) lParam, BM_GETCHECK, -1, -1) == BST_CHECKED;
+ EnableWindowDlgItem(hDlg, IDC_PTT, !checked);
+ SendDlgItemMessage(hDlg, IDC_PTT, BM_SETSTATE, checked ? BST_CHECKED : BST_UNCHECKED, 0);
+ KillTimer(hMainDLG, 2234);
+ KillTimer(hMainDLG, 2235);
+ if ((MicFadeStartTime != 0 || FadeStartTime != 0) && FadeOut != checked) {
+ clock_t myTime = clock();
+ if (FadeStartTime != 0) FadeStartTime = myTime - ((FadeTime * 100)-(myTime - FadeStartTime));
+ if (MicFadeStartTime != 0) MicFadeStartTime = myTime - ((MicFadeTime * 100)-(myTime - MicFadeStartTime));
+ }
+ if(!ptt_load && Restore_PTT || ptt_load) {
+ FadeOut = checked;
+ SetTimer(hMainDLG, 2234, 10, NULL); // fade out
+ SetTimer(hMainDLG, 2235, 10, NULL); // fade out
+ } else {
+ SetDeviceName();
+ }
+ ptt_load = 1;
+ //if (FadeOut) do_capture();
+ #ifdef CAPTURE_TESTING
+ if (FadeOut) {
+ if (!pPlayer) {
+ pPlayer = new Player(hMainDLG);
+ }
+ if (!pCallbacks) {
+ pCallbacks = new CPlayerCallbacks();
+ }
+ pPlayer->SetPlayerCallbacks(pCallbacks);
+ pPlayer->RefreshDeviceList(eRender);
+ pPlayer->RefreshDeviceList(eCapture);
+ pPlayer->SelectDefaultDevice(eRender, eConsole);
+ pPlayer->SelectDefaultDevice(eCapture, eConsole);
+ pPlayer->SelectDeviceFromList(eCapture, 0);
+ //pPlayer->SelectDeviceFromList(eRender, 2);
+ if (pPlayer->Play(eCaptureEndpoint) == FALSE) {
+ return TRUE;
+ }
+ }
+ #endif
+ }
+ }
+ break;
+
+ case IDC_LOCK_MODE:
+ {
+ HMENU hmenu = CreatePopupMenu();
+ RECT r;
+ GetWindowRect((HWND)lParam, &r);
+
+ MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING,
+ Restore_PTT ? MFS_CHECKED : 0, 1337};
+ i.dwTypeData = LocalisedString(IDS_PTT_ON_STARTUP, NULL, 0);
+ InsertMenuItemW(hmenu, 0, TRUE, &i);
+ if (TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, r.left, r.bottom, 0, (HWND)lParam, NULL) == 1337) {
+ Restore_PTT = !Restore_PTT;
+ }
+ DestroyMenu(hmenu);
+ }
+ break;
+
+ case IDC_REFRESH_DEVICES:
+ {
+ if (IsVistaUp()) {
+ SendDlgItemMessage(inWin,IDC_DEVBOX, CB_RESETCONTENT, 0, 0);
+ SetDeviceName();
+ }
+ }
+ break;
+
+ case IDC_MIXER:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ // open vista / win7 or win2k / xp recording panel
+ // (more sensible esp. for vista / win7)
+ if (IsVistaUp()) {
+ ShellExecuteW(hMainDLG, L"open", L"control.exe", L"mmsys.cpl,,1", NULL, SW_SHOW);
+ } else {
+ ShellExecuteW(hMainDLG, L"open", L"sndvol32.exe", L"", NULL, SW_SHOW);
+ ShellExecuteW(hMainDLG, L"open", L"sndvol32.exe", L"/r", NULL, SW_SHOW);
+ }
+ }
+ }
+ break;
+
+ case IDC_OUTPUTLIST:
+ {
+ if (HIWORD(wParam) == LBN_SELCHANGE) {
+ Connection_CurSelPos = SendMessage((HWND) lParam, LB_GETCURSEL, 0, 0);
+
+ // force a refresh on selection change
+ lastMode[Connection_CurSelPos] = -1;
+
+ // do checks to see if we need to update the error state of the tab items
+ if (IsWindow(outTabWnd)) {
+ RECT r = {0};
+ for (int i = 0; i < MAX_OUTWNDS; i++) {
+ TabCtrl_GetItemRect(outTabWnd, i, &r);
+ InvalidateRect(outTabWnd, &r, 0);
+ }
+
+ TabCtrl_GetItemRect(GetDlgItem(hMainDLG, IDC_TAB), 1, &r);
+ InvalidateRect(GetDlgItem(hMainDLG, IDC_TAB), &r, 0);
+ }
+
+ T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config;
+ MY_T_OUTPUT * OutEnc = &Output[Connection_CurSelPos];
+ int sc2mode = (LOBYTE(Out->protocol) != 1);
+ // Output page 1
+ SendDlgItemMessage(out_wnd[0].hWnd, IDC_ADDRESS, EM_SETLIMITTEXT, ARRAYSIZE(Out->Address) - 1, 0);
+ SetDlgItemText(out_wnd[0].hWnd, IDC_ADDRESS, Out->Address);
+ SendDlgItemMessage(out_wnd[0].hWnd, IDC_STATIONID, EM_SETLIMITTEXT, 10, 0);
+ SetDlgItemText(out_wnd[0].hWnd, IDC_STATIONID, Out->StationID);
+ SendDlgItemMessage(out_wnd[0].hWnd, IDC_USERID, EM_SETLIMITTEXT, ARRAYSIZE(Out->UserID) - 1, 0);
+ SetDlgItemText(out_wnd[0].hWnd, IDC_USERID, Out->UserID);
+ SetDlgItemInt(out_wnd[0].hWnd, IDC_PORT, Out->Port, 0);
+ SendDlgItemMessage(out_wnd[0].hWnd, IDC_PASSWORD, EM_SETLIMITTEXT, ARRAYSIZE(Out->Password) - 1, 0);
+ SetDlgItemText(out_wnd[0].hWnd, IDC_PASSWORD, Out->Password);
+ int encval = OutEnc->Encoder;
+ SendDlgItemMessage(out_wnd[0].hWnd, IDC_RECONNECT, BM_SETCHECK, Out->AutoRecon ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK, OutEnc->AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ SetDlgItemInt(out_wnd[0].hWnd, IDC_TIMEOUT, Out->ReconTime, 0);
+
+ EnableWindowDlgItem(out_wnd[0].hWnd, IDC_STATIONID, sc2mode);
+ SendDlgItemMessage(out_wnd[0].hWnd, IDC_PROTOCOL, CB_SETCURSEL, (HIBYTE(Out->protocol) ? 0 : (sc2mode ? 1 : 2)), 0);
+
+
+ // Output page 2
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_PUBLIC, BM_SETCHECK, Out->Public ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_DESCRIPTION, EM_SETLIMITTEXT, ARRAYSIZE(Out->Description) - 1, 0);
+ SetDlgItemText(out_wnd[1].hWnd, IDC_DESCRIPTION, Out->Description);
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_SERVERURL, EM_SETLIMITTEXT, ARRAYSIZE(Out->ServerURL) - 1, 0);
+ SetDlgItemText(out_wnd[1].hWnd, IDC_SERVERURL, Out->ServerURL);
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_GENRE, EM_SETLIMITTEXT, ARRAYSIZE(Out->Genre) - 1, 0);
+ SetDlgItemText(out_wnd[1].hWnd, IDC_GENRE, Out->Genre);
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_AIM, EM_SETLIMITTEXT, ARRAYSIZE(Out->AIM) - 1, 0);
+ SetDlgItemText(out_wnd[1].hWnd, IDC_AIM, Out->AIM);
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_ICQ, EM_SETLIMITTEXT, ARRAYSIZE(Out->ICQ) - 1, 0);
+ SetDlgItemText(out_wnd[1].hWnd, IDC_ICQ, Out->ICQ);
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_IRC, EM_SETLIMITTEXT, ARRAYSIZE(Out->IRC) - 1, 0);
+ SetDlgItemText(out_wnd[1].hWnd, IDC_IRC, Out->IRC);
+
+ SendDlgItemMessage(out_wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK, OutEnc->AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ // setup the handling of the encoder saving options
+ SendDlgItemMessage(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, EM_SETLIMITTEXT, ARRAYSIZE(OutEnc->stationArtPath) - 1, 0);
+ SetDlgItemTextW(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, OutEnc->saveEncodedPath);
+ SendDlgItemMessage(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO, BM_SETCHECK, OutEnc->saveEncoded ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ // setup the handling of the titles options
+ SendDlgItemMessage(out_wnd[3].hWnd, IDC_TITLE, EM_SETLIMITTEXT, ARRAYSIZE(Out->Now) - 1, 0);
+ SetDlgItemText(out_wnd[3].hWnd, IDC_TITLE, Out->Now);
+ SendDlgItemMessage(out_wnd[3].hWnd, IDC_NEXT, EM_SETLIMITTEXT, ARRAYSIZE(Out->Next) - 1, 0);
+ SetDlgItemText(out_wnd[3].hWnd, IDC_NEXT, Out->Next);
+ CheckRadioButton(out_wnd[3].hWnd, IDC_NOTITLES, IDC_MANUALTITLE, (Out->doTitleUpdate ? (OutEnc->AutoTitle ? IDC_AUTOTITLE : IDC_MANUALTITLE) : IDC_NOTITLES));
+ SendDlgItemMessage(out_wnd[3].hWnd, IDC_SENDNEXTTITLES, BM_SETCHECK, OutEnc->NextTitles ? BST_CHECKED : BST_UNCHECKED, 0);
+ UpdateTitleControls();
+
+ // setup the handling of the artwork options
+ SendDlgItemMessage(out_wnd[4].hWnd, IDC_ART_EDIT, EM_SETLIMITTEXT, ARRAYSIZE(OutEnc->stationArtPath) - 1, 0);
+ SetDlgItemTextW(out_wnd[4].hWnd, IDC_ART_EDIT, OutEnc->stationArtPath);
+ SendDlgItemMessage(out_wnd[4].hWnd, IDC_USE_ART, BM_SETCHECK, OutEnc->useArt ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, BM_SETCHECK, OutEnc->usePlayingArt ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(out_wnd[4].hWnd, IDC_USE_ART_STREAM, BM_SETCHECK, OutEnc->useStreamArt ? BST_CHECKED : BST_UNCHECKED, 0);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, OutEnc->useArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_STREAM, OutEnc->useArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, OutEnc->useArt && OutEnc->useStreamArt);
+ EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, OutEnc->useArt && OutEnc->useStreamArt);
+ UpdateArtworkMessage();
+
+ // setup the handling of the next track logging option
+ SendDlgItemMessage(out_wnd[5].hWnd, IDC_LOGGING, BM_SETCHECK, OutEnc->Logging ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(out_wnd[5].hWnd, IDC_CLEAR_ON_STARTUP, BM_SETCHECK, OutEnc->LogCOS ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ SendDlgItemMessage(out_wnd[5].hWnd, IDC_NEXT_TRACK_LOG, BM_SETCHECK, OutEnc->nextTrackLog ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(out_wnd[5].hWnd, IDC_NEXT_TRACK_XML, BM_SETCHECK, OutEnc->nextTrackLogXML ? BST_CHECKED : BST_UNCHECKED, 0);
+ SendDlgItemMessage(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, EM_SETLIMITTEXT, ARRAYSIZE(OutEnc->nextTrackPath) - 1, 0);
+ SetDlgItemTextW(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, OutEnc->nextTrackPath);
+ EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_XML, OutEnc->nextTrackLog);
+ EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, OutEnc->nextTrackLog);
+ EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_BROWSE, OutEnc->nextTrackLog);
+
+ // this is sent to the encoders tab so it will update the selection for the current instance
+ // note: this is a change in build 009 to remove the prior listbox and reduce ui inconsistency
+ SendMessage(out_wnd[2].hWnd, WM_COMMAND, MAKEWPARAM(IDC_ENCODERLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(wnd[2].hWnd, IDC_ENCODERLIST));
+
+ ini_modified = 1;
+ }
+ }
+ break;
+
+ case IDC_INPUTSETUP:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) {
+ Crossfader->SetChannels(LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].nch);
+ Crossfader->SetSampleRate(LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].srate);
+ ReleaseMutex(cf_mutex);
+ }
+
+ int attrib = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0);
+ if (attrib != Input_CurSelPos) {
+ SuspendThread(hthread);
+ Soundcard.Close();
+ if (InputDevice == 1) {
+ Input_CurSelPos = attrib;
+ }
+ for (int i = 0; i < NUM_ENCODERS; i++) {
+ if (WaitForSingleObject(Enc_mutex[i], INFINITE) == WAIT_OBJECT_0) {
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ int infosize = sizeof (T_EncoderIOVals);
+ T_EncoderIOVals *encset = (T_EncoderIOVals *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) {
+ T_EncoderIOVals *EncSettings = (T_EncoderIOVals *) malloc(infosize);
+ memcpy(EncSettings, encset, infosize);
+
+ if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) {
+ ((T_ENCODER_MP3_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_MP3_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ } else if (strcmp(Enc->GetName(), "Fraunhofer Encoder") == 0) {
+ ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ } else if (strcmp(Enc->GetName(), "AAC+ Encoder") == 0) {
+ ((T_ENCODER_AACP_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_AACP_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ }
+#ifdef USE_OGG
+ else if (strcmp(Enc->GetName(), "OGG Vorbis Encoder") == 0) {
+ ((T_ENCODER_OGG_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_OGG_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ }
+#endif // USE_OGG
+ Enc->ChangeSettings(EncSettings);
+ free(EncSettings);
+ }
+ }
+ ReleaseMutex(Enc_mutex[i]);
+ }
+ }
+ Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch));
+ ResumeThread(hthread);
+ ini_modified = 1;
+ }
+ }
+ }
+ break;
+
+ case IDC_INPUT_WINAMP:
+ case IDC_INPUT_SOUNDCARD:
+ {
+ // update the input mode from the summary page options
+ HWND inputCtrl = GetDlgItem(wnd[2].hWnd, IDC_INPUTDEVICE);
+ SendMessage(inputCtrl, CB_SETCURSEL, (LOWORD(wParam) - IDC_INPUT_WINAMP), 0);
+ SendMessage(wnd[2].hWnd, WM_COMMAND, MAKEWPARAM(IDC_INPUTDEVICE,CBN_SELCHANGE), (LPARAM)inputCtrl);
+ }
+ break;
+
+ case IDC_INPUTDEVICE:
+ {
+ if (HIWORD(wParam) == CBN_SELCHANGE) {
+ int this_device = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0);
+ if (InputDevice != this_device) {
+ SuspendThread(hthread);
+ Soundcard.Close();
+ InputDevice = this_device;
+ if (InputDevice == 0) { // winamp
+ SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MUSSLIDER));
+ SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MUS2SLIDER));
+ SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MICSLIDER));
+ SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_FADESLIDER));
+ SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MICFADESLIDER));
+ }
+ for (int i = 0; i < NUM_ENCODERS; i++) {
+ if (WaitForSingleObject(Enc_mutex[i], INFINITE) == WAIT_OBJECT_0) {
+ C_ENCODER *Enc = Encoder[i].GetEncoder();
+ if (Enc) {
+ int infosize = sizeof (T_EncoderIOVals);
+ T_EncoderIOVals *encset = (T_EncoderIOVals *) Enc->GetExtInfo(&infosize);
+ if (encset && infosize) {
+ T_EncoderIOVals *EncSettings = (T_EncoderIOVals *) malloc(infosize);
+ memcpy(EncSettings, encset, infosize);
+
+ if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) {
+ ((T_ENCODER_MP3_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_MP3_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ } else if (strcmp(Enc->GetName(), "Fraunhofer Encoder") == 0) {
+ ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ } else if (strcmp(Enc->GetName(), "AAC+ Encoder") == 0) {
+ ((T_ENCODER_AACP_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_AACP_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ }
+#ifdef USE_OGG
+ else if (strcmp(Enc->GetName(), "OGG Vorbis Encoder") == 0) {
+ ((T_ENCODER_OGG_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch);
+ ((T_ENCODER_OGG_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate);
+ }
+#endif // USE_OGG
+ Enc->ChangeSettings(EncSettings);
+ free(EncSettings);
+ }
+ }
+ ReleaseMutex(Enc_mutex[i]);
+ }
+ }
+ Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch));
+
+ DisplayDeviceName();
+ CheckRadioButton(wnd[0].hWnd, IDC_INPUT_WINAMP, IDC_INPUT_SOUNDCARD, (IDC_INPUT_WINAMP + InputDevice));
+
+ peak_vu_l = peak_vu_r = -90;
+ ResumeThread(hthread);
+ ini_modified = 1;
+ }
+ SendDlgItemMessage(hDlg, IDC_INPUTSETUP, CB_RESETCONTENT, 0, 0);
+ if (InputDevice == 1) {
+ wchar_t temp[128];
+ int num_input_items = ARRAYSIZE(LineInputAttribs);
+ for (int i = 0; i < num_input_items; i++) {
+ wchar_t tmp[32];
+ StringCchPrintfW(temp, ARRAYSIZE(temp), LocalisedString(IDS_X_HZ_X, tmp, 32), LineInputAttribs[i].srate, LocalisedString(LineInputAttribs[i].nch == 1 ? IDS_MONO : IDS_STEREO, NULL, 0));
+ SendDlgItemMessageW(hDlg, IDC_INPUTSETUP, CB_ADDSTRING, 0, (LPARAM) temp);
+ }
+ SendDlgItemMessage(hDlg, IDC_INPUTSETUP, CB_SETCURSEL, Input_CurSelPos, 0);
+ }
+ for (int i = 0; i < num_inwnds; i++) ShowWindow(in_wnd[i].hWnd, i == InputDevice && curtab == 2 ? SW_SHOW : SW_HIDE);
+ SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_INPUTSETUP, CBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_INPUTSETUP));
+ ShowWindowDlgItem(hDlg, IDC_INPUTSETUPSTATIC, InputDevice == 1);
+ ShowWindowDlgItem(hDlg, IDC_INPUTSETUP, InputDevice == 1);
+ }
+ }
+ break;
+
+ // server box
+ case IDC_ADDRESS:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config;
+ GetWindowText((HWND) lParam, Out->Address, ARRAYSIZE(Out->Address));
+ ini_modified = 1;
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ // stream ID box
+ case IDC_STATIONID:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ BOOL success = FALSE;
+ int value = GetDlgItemInt(hDlg, LOWORD(wParam), &success, TRUE);
+ T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config;
+ GetWindowText((HWND) lParam, Out->StationID, ARRAYSIZE(Out->StationID));
+ // check and set the default as required
+ if (!Out->StationID[0] || (success && value < 1 || !success && value == 0)) {
+ SetWindowTextW((HWND) lParam, L"1");
+ lstrcpyn(Out->StationID, "1", ARRAYSIZE(Out->StationID));
+ } else if (Out->StationID[0] && (success && value > 2147483647)) {
+ SetWindowTextW((HWND) lParam, L"2147483647");
+ lstrcpyn(Out->StationID, "2147483647", ARRAYSIZE(Out->StationID));
+ }
+ ini_modified = 1;
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_USERID:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config;
+ GetWindowText((HWND) lParam, Out->UserID, ARRAYSIZE(Out->UserID));
+ ini_modified = 1;
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ // server port
+ case IDC_PORT:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ BOOL success = FALSE;
+ int value = GetDlgItemInt(hDlg, LOWORD(wParam), &success, TRUE);
+ T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config;
+ // check and set the default as required
+ if ((success && value < 1 || !success && value == 0)) {
+ SetWindowTextW((HWND) lParam, L"8000");
+ Out->Port = 8000;
+ } else if ((success && value > 65535)) {
+ SetWindowTextW((HWND) lParam, L"65535");
+ Out->Port = 65535;
+ } else {
+ Out->Port = value;
+ }
+
+ ini_modified = 1;
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ // password
+ case IDC_PASSWORD:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config;
+ GetWindowText((HWND) lParam, Out->Password, ARRAYSIZE(Out->Password));
+ ini_modified = 1;
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_SEND:
+ {
+ EnableWindowDlgItem(hDlg, IDC_SEND, FALSE);
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ if (Out->Encoder != -1 && Out->Handle != -1) {
+ wchar_t title[1024] = {0}, next[1024] = {0};
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ GetWindowTextW(GetDlgItem(out_wnd[3].hWnd, IDC_TITLE), title, ARRAYSIZE(title));
+ GetWindowTextW(GetDlgItem(out_wnd[3].hWnd, IDC_NEXT), next, ARRAYSIZE(next));
+ ini_modified = 1;
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ std::vector<std::wstring> nextList;
+ std::vector<int> nextListIdx;
+ nextList.clear();
+ nextListIdx.clear();
+ if (((Out->AutoTitle == 1 && Out->NextTitles) || Out->AutoTitle == 0) && next[0]) {
+ nextList.push_back(next);
+ nextListIdx.push_back(-1);
+ }
+
+ if (Out->nextTrackLog) {
+ WriteNextTracks(Connection_CurSelPos, module.hwndParent, nextListIdx, nextList, !!Out->nextTrackLogXML);
+ }
+
+ // check if in v2 mode and have a next title specified so as to change the send flag
+ Enc->UpdateTitle(title, nextList, Out->Handle, !!nextList.size(), false);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ }
+ break;
+
+ case IDC_TITLE:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ int length = GetWindowTextLength((HWND)lParam);
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, (length > 0));
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT,
+ (length > 0 && (LOBYTE(Out->Config.protocol) != 1)));
+
+ char temp[sizeof(Out->Config.Now)];
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.Now) != 0) {
+ lstrcpyn(Out->Config.Now, temp, ARRAYSIZE(Out->Config.Now));
+ ini_modified = 1;
+ }
+ }
+ }
+ break;
+
+ case IDC_NEXT:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, TRUE);
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+
+ char temp[sizeof(Out->Config.Next)];
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.Next) != 0) {
+ lstrcpyn(Out->Config.Next, temp, ARRAYSIZE(Out->Config.Next));
+ ini_modified = 1;
+ }
+ }
+ }
+ break;
+
+ case IDC_TIMEOUT:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ char temp[128] = {0};
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+
+ int rt = atoi(temp);
+ if (rt < 1) {
+ rt = 5;
+ SetDlgItemInt(out_wnd[0].hWnd, IDC_TIMEOUT, 5, 0);
+ }
+
+ if (Out->Config.ReconTime != rt) {
+ Out->Config.ReconTime = rt;
+ if (Out->Encoder) {
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Encoder[Out->Encoder].UpdateOutput(Out->Handle);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ ini_modified = 1;
+ }
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ // these will reconnect the Output on edit
+ case IDC_PUBLIC:
+ {
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ Out->Config.Public = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+
+ // force a refresh on selection change
+ lastMode[Connection_CurSelPos] = -1;
+
+ ini_modified = 1;
+ if (Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_GENRES:
+ {
+ // build up a menu to allow the user to select only supported genres for use with YP
+ if (HIWORD(wParam) == BN_CLICKED) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ HMENU hmenu = CreatePopupMenu(), submenu = 0;
+ RECT r;
+ GetWindowRect((HWND)lParam, &r);
+
+ for (unsigned int i = 0; i < ARRAYSIZE(genres); i++) {
+ MENUITEMINFO mii = {sizeof(mii), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, 0, 1+i, 0, 0, 0, 0, 0, 0};
+ bool reAdd = false;
+
+ // fix up genres with & in it to work around menu accelerator display quirks
+ std::string str = genres[i].name;
+ if (str.find("&") != std::string::npos)
+ str.replace(str.find("&"),1,"&&");
+ mii.dwTypeData = (LPSTR)str.c_str();
+
+ if (genres[i].parent) {
+ if (genres[i].children) {
+ reAdd = true;
+ mii.fMask |= MIIM_SUBMENU;
+ submenu = mii.hSubMenu = CreatePopupMenu();
+ }
+ }
+
+ if (reAdd == false && !strcmpi(Out->Config.Genre, genres[i].name)) {
+ mii.fState = MFS_CHECKED;
+ }
+
+ InsertMenuItem((genres[i].parent ? hmenu : submenu), i, TRUE, &mii);
+
+ if (reAdd == true) {
+ mii.fMask -= MIIM_SUBMENU;
+ if (!strcmpi(Out->Config.Genre, genres[i].name)) {
+ mii.fState = MFS_CHECKED;
+ }
+ InsertMenuItem(submenu, i, TRUE, &mii);
+ }
+ }
+
+ int ret = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, r.left, r.bottom, 0, (HWND)hDlg, NULL);
+ if (ret > 0) {
+ int update = 0;
+ ret -= 1;
+ if (strcmp(genres[ret].name, Out->Config.Genre) != 0) {
+ update = 1;
+ SetDlgItemText(hDlg, IDC_GENRE, genres[ret].name);
+ lstrcpyn(Out->Config.Genre, genres[ret].name, ARRAYSIZE(Out->Config.Genre));
+ ini_modified = 1;
+ }
+ if (update && Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ }
+
+ DestroyMenu(hmenu);
+ }
+ }
+ break;
+
+ case IDC_DESCRIPTION:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ char temp[sizeof (Out->Config.Description)];
+ int update = 0;
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.Description) != 0) {
+ update = 1;
+ lstrcpyn(Out->Config.Description, temp, ARRAYSIZE(Out->Config.Description));
+ ini_modified = 1;
+ }
+ if (update && Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_SERVERURL:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ char temp[sizeof (Out->Config.ServerURL)];
+ int update = 0;
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.ServerURL) != 0) {
+ update = 1;
+ lstrcpyn(Out->Config.ServerURL, temp, ARRAYSIZE(Out->Config.ServerURL));
+ ini_modified = 1;
+ }
+ if (update && Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_AIM:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ char temp[sizeof (Out->Config.AIM)];
+ int update = 0;
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.AIM) != 0) {
+ update = 1;
+ lstrcpyn(Out->Config.AIM, temp, ARRAYSIZE(Out->Config.AIM));
+ ini_modified = 1;
+ }
+ if (update && Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_ICQ:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ char temp[sizeof (Out->Config.ICQ)];
+ int update = 0;
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.ICQ) != 0) {
+ update = 1;
+ lstrcpyn(Out->Config.ICQ, temp, ARRAYSIZE(Out->Config.ICQ));
+ ini_modified = 1;
+ }
+ if (update && Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+
+ case IDC_IRC:
+ {
+ if (HIWORD(wParam) == EN_UPDATE) {
+ MY_T_OUTPUT *Out = &Output[Connection_CurSelPos];
+ char temp[sizeof (Out->Config.IRC)];
+ int update = 0;
+ GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp));
+ if (strcmp(temp, Out->Config.IRC) != 0) {
+ update = 1;
+ lstrcpyn(Out->Config.IRC, temp, ARRAYSIZE(Out->Config.IRC));
+ ini_modified = 1;
+ }
+ if (update && Out->Encoder != -1 && Out->Handle != -1) {
+ SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder];
+ if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) {
+ Enc->DisconnectOutput(Out->Handle, 1, 5);
+ ReleaseMutex(Enc_mutex[Out->Encoder]);
+ }
+ }
+ } else if (HIWORD(wParam) == EN_SETFOCUS) {
+ PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1);
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_CTLCOLORSTATIC:
+ {
+ // this is used to update the header text of the options which need to be checked if there is a config error...
+ int id = GetDlgCtrlID((HWND)lParam);
+ if (id == IDC_ADDRESS_HEADER || id == IDC_PASSWORD_HEADER || id == IDC_NAME_HEADER || id == IDC_ENCODER_HEADER) {
+ int header_id[] = {0, 0, 0, IDC_ADDRESS_HEADER, IDC_PASSWORD_HEADER, IDC_NAME_HEADER, IDC_ENCODER_HEADER, 0};
+ if (lastMode[Connection_CurSelPos] >= 3 && lastMode[Connection_CurSelPos] <= 6 &&
+ header_id[lastMode[Connection_CurSelPos]] == id) {
+ SetTextColor((HDC)wParam, RGB(255,0,0));
+ }
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR pnmh = (LPNMHDR) lParam;
+ switch (LOWORD(wParam)) {
+ case IDC_TAB:
+ if (pnmh->code == TCN_SELCHANGE) {
+ int i;
+ KillTimer(hDlg, wnd[curtab].id);
+ curtab = SendMessage(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
+
+ // send this to update the tab just incase we're showing invalid items
+ InvalidateRect(pnmh->hwndFrom, 0, 0);
+
+ ini_modified = 1;
+ if (wnd[curtab].timer_freq != 0) SetTimer(hDlg, wnd[curtab].id, wnd[curtab].timer_freq, NULL);
+ for (i = 0; i < num_tabwnds; i++) ShowWindow(wnd[i].hWnd, curtab == i ? SW_SHOW : SW_HIDE);
+ for (i = 0; i < num_inwnds; i++) ShowWindow(in_wnd[i].hWnd, i == InputDevice && curtab == 2 ? SW_SHOW : SW_HIDE);
+ for (i = 0; i < num_outwnds; i++) ShowWindow(out_wnd[i].hWnd, i == curouttab && curtab == 1 ? SW_SHOW : SW_HIDE);
+ // update the summary details when going back to it
+ if (curtab == 0) {
+ UpdateSummaryDetails(ListView_GetNextItem(GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS), -1, LVNI_SELECTED));
+ } else if(curtab == 1) {
+ // force a refresh on selection change
+ lastMode[Connection_CurSelPos] = -1;
+ }
+ }
+ break;
+
+ case IDC_CONTAB:
+ if (pnmh->code == TCN_SELCHANGE) {
+ int i;
+ KillTimer(hDlg, out_wnd[curouttab].id);
+ curouttab = SendMessage(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0);
+
+ // send this to update the tab just incase we're showing invalid items
+ InvalidateRect(pnmh->hwndFrom, 0, 0);
+
+ ini_modified = 1;
+ if (out_wnd[curouttab].timer_freq != 0) SetTimer(hDlg, out_wnd[curouttab].id, out_wnd[curouttab].timer_freq, NULL);
+ for (i = 0; i < num_outwnds; i++) ShowWindow(out_wnd[i].hWnd, i == curouttab ? SW_SHOW : SW_HIDE);
+ bool enable = (LOBYTE(Output[Connection_CurSelPos].Config.protocol) == 1);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_AIM, enable);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_IRC, enable);
+ EnableWindowDlgItem(out_wnd[1].hWnd, IDC_ICQ, enable);
+ }
+ break;
+
+ case IDC_METALIST:
+ if (pnmh->code == NM_DBLCLK) {
+ LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam;
+ if (lpnmitem->iItem != -1 && WASABI_API_EXPLORERFINDFILE) {
+ wchar_t fn[MAX_PATH]= {0};
+ lstrcpynW(fn, lastFile, MAX_PATH);
+ if (!PathIsURLW(fn)) {
+ // this will attempt to find the path of the parent folder of the file selected
+ // as spc files in a rsn archive can display album art (with the compatibility
+ // wrapper) and so it should cope with such scenarios...
+ wchar_t *filews = fn + lstrlenW(fn) - 1;
+ while(filews && *filews && (*filews != L'.') && (filews != fn)){filews = CharPrevW(fn,filews);}
+ while(filews && *filews && (*filews != L',' && *filews != L':')){filews = CharNextW(filews);}
+ if (filews) *filews = 0;
+
+ filews = wcsstr(fn,L".rsn\\");
+ if(filews) {
+ *(filews+4) = 0;
+ }
+
+ WASABI_API_EXPLORERFINDFILE->AddFile(fn);
+ WASABI_API_EXPLORERFINDFILE->ShowFiles();
+ }
+ }
+ }
+ break;
+
+ case IDC_OUTPUTSTATUS:
+ // on double-click go to the output tab and select the output we used as the currently shown
+ if (pnmh->code == NM_DBLCLK) {
+ LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam;
+ if (lpnmitem->iItem != -1 && lpnmitem->iSubItem != 0) {
+ // only change the viewed output mode if it is different, otherwise just switch tab
+ if (Connection_CurSelPos != lpnmitem->iItem) {
+ Connection_CurSelPos = lpnmitem->iItem;
+
+ // force a refresh on selection change
+ lastMode[Connection_CurSelPos] = -1;
+
+ SendDlgItemMessage(wnd[1].hWnd, IDC_OUTPUTLIST, LB_SETCURSEL, Connection_CurSelPos, 0);
+ SendMessage(wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_OUTPUTLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(wnd[1].hWnd, IDC_OUTPUTLIST));
+ SetFocus(GetDlgItem(wnd[1].hWnd, IDC_OUTPUTLIST));
+ }
+ SetTab(1, hMainDLG, IDC_TAB);
+ }
+ } else if (pnmh->code == LVN_ITEMCHANGED) {
+ // on single-click / keyboard change, show some information about the stream such as most, encoder, etc (makes the page useful)
+ LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam;
+ if (lpnmitem->iItem != -1) {
+ UpdateSummaryDetails(lpnmitem->iItem);
+ }
+ } else if(pnmh->code == LVN_KEYDOWN) {
+ LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN) lParam;
+ // toggle state in the output list via 'space'
+ if (pnkd->wVKey == VK_SPACE) {
+ int item = ListView_GetNextItem(GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS), -1, LVNI_SELECTED);
+ if (lastEnable[item]) {
+ int oldCurSelPos = Connection_CurSelPos;
+ Connection_CurSelPos = item;
+ SendMessage(wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_CONNECT, BN_CLICKED), (LPARAM)GetDlgItem(wnd[1].hWnd, IDC_CONNECT));
+ Connection_CurSelPos = oldCurSelPos;
+ }
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_HSCROLL:
+ {
+ int curpos = SendMessage((HWND) lParam, TBM_GETPOS, 0, 0);
+ if (GetDlgItem(hDlg, IDC_MUSSLIDER) == (HWND) lParam) {
+ MusVol = curpos;
+ if (InputDevice == 1 && !FadeOut) setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, MusVol * 10);
+ wchar_t tmp[256] = {0};
+ if (curpos != 0) {
+ wchar_t temp[32] = {0};
+ int volume = (int) (20 * log10(curpos * 3276.));
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90);
+ } else {
+ LocalisedString(IDS_INF_DB, tmp, 256);
+ }
+ SetDlgItemTextW(hDlg, IDC_MUSLEV1_TEXT, tmp);
+ } else if (GetDlgItem(hDlg, IDC_MUS2SLIDER) == (HWND) lParam) {
+ Mus2Vol = curpos;
+ if (InputDevice == 1 && FadeOut) setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, Mus2Vol * 10);
+ wchar_t tmp[256] = {0};
+ if (curpos != 0) {
+ wchar_t temp[32] = {0};
+ int volume = (int) (20 * log10(curpos * 3276.));
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90);
+ } else {
+ LocalisedString(IDS_INF_DB, tmp, 256);
+ }
+ SetDlgItemTextW(hDlg, IDC_MUSLEV2_TEXT, tmp);
+ } else if (GetDlgItem(hDlg, IDC_MICSLIDER) == (HWND) lParam) {
+ MicVol = curpos;
+ int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
+ // changed this so it will only change the capture device level if PTT is pressed
+ if (InputDevice == 1 && FadeOut) setlev(micsrc, MicVol *10);
+ wchar_t tmp[256] = {0};
+ if (curpos != 0) {
+ wchar_t temp[32] = {0};
+ int volume = (int) (20 * log10(curpos * 3276.));
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90);
+ } else {
+ LocalisedString(IDS_INF_DB, tmp, 256);
+ }
+ SetDlgItemTextW(hDlg, IDC_MICLEV_TEXT, tmp);
+ } else if (GetDlgItem(hDlg, IDC_FADESLIDER) == (HWND) lParam) {
+ FadeTime = curpos;
+ wchar_t tmp[256] = {0}, temp[32] = {0};
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_MS, temp, 32), curpos * 100);
+ SetDlgItemTextW(hDlg, IDC_FADETIME_TEXT, tmp);
+ } else if (GetDlgItem(hDlg, IDC_MICFADESLIDER) == (HWND) lParam) {
+ MicFadeTime = curpos;
+ wchar_t tmp[256] = {0}, temp[32] = {0};
+ StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_MS, temp, 32), curpos * 100);
+ SetDlgItemTextW(hDlg, IDC_MICFADETIME_TEXT, tmp);
+ }
+ }
+ break;
+ }
+
+ if (FALSE != DirectMouseWheel_ProcessDialogMessage(hDlg, uMsg, wParam, lParam)) {
+ return TRUE;
+ }
+ return 0;
+}
+
+LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode == HC_ACTION) {
+ LPCWPSTRUCT msg = (LPCWPSTRUCT)lParam;
+ // catch the new file playing message and update the cached metadata
+ if (msg->message == WM_WA_IPC) {
+ if (msg->lParam == IPC_PLAYING_FILEW) {
+ DWORD diff = GetTickCount();
+ was_paused = 0;
+ play_duration = 0;
+ play_diff = diff;
+ was_playing = 1;
+ if (wcsnicmp(lastFile, (wchar_t*)msg->wParam, MAX_PATH)) {
+ PostMessage(hMainDLG, WM_USER, 0, nowPlayingID);
+ }
+ }
+ }
+ }
+ return (nowPlayingHook ? CallNextHookEx(nowPlayingHook, nCode, wParam, lParam) : 0);
+}
+
+LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
+ if (nCode == HC_ACTION) {
+ LPMSG msg = (LPMSG)lParam;
+ // catch the new file playing message and update the cached metadata
+ if (msg->message == WM_WA_IPC) {
+ if (msg->lParam == IPC_CB_MISC && msg->wParam == IPC_CB_MISC_STATUS) {
+ isplaying = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING);
+ ProcessPlayingStatusUpdate();
+ } else if (msg->lParam == IPC_UPDTITLE) {
+ // attempt to keep a track of other title updates
+ // e.g. the re-streaming an already playing stream
+ wchar_t currentFile[MAX_PATH] = {0};
+ wchar_t *file=(wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC,
+ SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS),
+ IPC_GETPLAYLISTFILEW);
+ wcsncpy(currentFile, (file ? file : L""), MAX_PATH);
+ if (!wcsnicmp(currentFile, lastFile, MAX_PATH)) {
+ // do a 1 second delay since it's likely Winamp will send
+ // this a few times so we reset the timer everytime so we
+ // only do a proper title update once everything settles
+ KillTimer(hMainDLG, 1337);
+ SetTimer(hMainDLG, 1337, 1000, NULL);
+ }
+ }
+ }
+ }
+ return (nowPlayingHook2 ? CallNextHookEx(nowPlayingHook2, nCode, wParam, lParam) : 0);
+}
+
+VOID CALLBACK TimerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(hwnd, idEvent);
+ SetForegroundWindow(hMainDLG);
+}
+
+void doConfig(HWND hwndParent) {
+ if (WASABI_API_SVC) {
+ if (!IsWindow(hMainDLG)) {
+ if (AGAVE_API_CONFIG) {
+ if (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16) > 16) {
+ wchar_t title[128] = {0}, message[512] = {0};
+ StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_VersionW);
+ StringCchPrintfW(message, ARRAYSIZE(message), LocalisedString(IDS_24BIT_MODE_DETECTED, NULL, 0));
+ MessageBoxW(module.hwndParent, message, title, MB_ICONWARNING);
+ return;
+ }
+ }
+
+ // using this to lessen issues with new track events with higher bitrates leading to failures
+ if (!nowPlayingHook) {
+ nowPlayingHook = SetWindowsHookExW(WH_CALLWNDPROC, CallWndProc, instance, GetCurrentThreadId());
+ }
+ if (!nowPlayingHook2) {
+ nowPlayingHook2 = SetWindowsHookExW(WH_GETMESSAGE, GetMsgProc, instance, GetCurrentThreadId());
+ }
+ if (nowPlayingID == -1) {
+ nowPlayingID = SendMessage(hwndParent, WM_WA_IPC, (WPARAM)&"dsp_sc_np", IPC_REGISTER_WINAMP_IPCMESSAGE);
+ }
+
+ HWND hwnd = LocalisedCreateDialog(instance, IDD_DIALOG, hwndParent, DialogFunc, IDD_DIALOG);
+ SetWindowPos(hwnd, HWND_TOP, mainrect.left, mainrect.top, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
+ SetTimer(hMainDLG, 999, 1, TimerProc2);
+ } else {
+ if (IsIconic(hMainDLG)) {
+ DialogFunc(hMainDLG, WM_SIZE, SIZE_RESTORED, 0);
+ ShowWindow(hMainDLG, SW_RESTORE);
+ ShowWindow(hMainDLG, SW_SHOW);
+ SetActiveWindow(hMainDLG);
+ }
+ SetForegroundWindow(hMainDLG);
+ }
+ }
+}
+
+VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
+{
+ KillTimer(hwnd, idEvent);
+ doConfig(hwnd);
+}
+
+void Config(winampDSPModule *this_mod) {
+ // this will hold back opening the config dialog on loading until Winamp is in a ready state
+ // this resolves a partial fail to load i've often been seeing (plus from some users afaict)
+ SetTimer(this_mod->hwndParent, 999, 1, TimerProc);
+}
+
+int Init(winampDSPModule *this_mod) {
+ instance = this_mod->hDllInstance;
+
+ // this will hold back opening the config dialog on loading until Winamp is in a ready state
+ // this resolves a partial fail to load i've often been seeing (plus from some users afaict)
+ SetTimer(this_mod->hwndParent, 999, 1, TimerProc);
+ return 1;
+}
+
+int ModifySamples(winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate) {
+ int numorig = numsamples;
+
+ //connect but only if we're meant to be i.e. there's at least 1 active output
+ if (InputDevice == 0) {
+ if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) {
+ // CT> Resample into the desired srate and nch if needed
+ // TODO check out the handling of this for 24-bit output
+ short cf_buf[256 * 1024] = {0};
+ if (srate != InputConfig.srate || nch != InputConfig.nch) {
+ char *s = (char *) samples;
+ int ns = numsamples * 2;
+ if (InputConfig.nch == 1) {
+ if (nch != 1 || bps != 16 || srate != (int) InputConfig.srate) {
+ if (nch == 2) {
+ int x;
+ int nns = MulDiv(numsamples, InputConfig.srate, srate);
+ int r = 0;
+ int dr = MulDiv(numsamples, 1 << 12, nns);
+ if (bps == 16)
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x] = samples[(r >> 12)*2] / 2 + samples[(r >> 12)*2 + 1] / 2;
+ r += dr;
+ }
+ }
+ else
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x] = ((((char *) samples)[(r >> 12)*2]^128) << 8) / 2 +
+ ((((char *) samples)[(r >> 12)*2 + 1]^128) << 8) / 2;
+ r += dr;
+ }
+ }
+ ns = nns * 2;
+ } else {
+ int x;
+ int nns = MulDiv(numsamples, InputConfig.srate, srate);
+ int r = 0;
+ int dr = MulDiv(numsamples, 1 << 12, nns);
+ if (bps == 16)
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x] = samples[r >> 12];
+ r += dr;
+ }
+ }
+ else
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x] = (((char *) samples)[r >> 12]^128) << 8;
+ r += dr;
+ }
+ }
+ ns = nns * 2;
+ }
+ s = (char *) cf_buf;
+ }
+ } else {
+ if (nch != 2 || bps != 16 || srate != (int) InputConfig.srate) {
+ if (nch == 2) {
+ int x;
+ int nns = MulDiv(numsamples, InputConfig.srate, srate);
+ int r = 0;
+ int dr = MulDiv(numsamples, 1 << 12, nns);
+ if (bps == 16)
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x * 2] = samples[(r >> 12)*2];
+ cf_buf[x * 2 + 1] = samples[(r >> 12)*2 + 1];
+ r += dr;
+ }
+ }
+ else
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x * 2] = (((char *) samples)[(r >> 12)*2]^128) << 8;
+ cf_buf[x * 2 + 1] = (((char *) samples)[(r >> 12)*2 + 1]^128) << 8;
+ r += dr;
+ }
+ ns = nns * 4;
+ }
+ } else {
+ int x;
+ int nns = MulDiv(numsamples, InputConfig.srate, srate);
+ int r = 0;
+ int dr = MulDiv(numsamples, 1 << 12, nns);
+ if (bps == 16)
+ {
+ for (x = 0; x < nns; x++) {
+ cf_buf[x * 2] = cf_buf[x * 2 + 1] = samples[r >> 12];
+ r += dr;
+ }
+ }
+ else
+ {
+ for (x = 0; x < nns; x++)
+ {
+ cf_buf[x * 2] = cf_buf[x * 2 + 1] = (((char *) samples)[r >> 12]^128) << 8;
+ r += dr;
+ }
+ ns = nns * 4;
+ }
+ }
+
+ s = (char *) cf_buf;
+ }
+ else ns *= 2;
+ }
+ samples = (short *) s;
+ numsamples = ns / (InputConfig.nch * 2);
+ }
+
+ if (!VU.update) {
+ for (int j = 0; j < numsamples; j++) {
+ if (VU.vu_l < samples[j]) VU.vu_l = samples[j];
+ if (InputConfig.nch == 2) {
+ if (VU.vu_r < samples[j + 1]) VU.vu_r = samples[j + 1];
+ j++;
+ }
+ }
+ if (InputConfig.nch == 1) VU.vu_r = VU.vu_l;
+ VU.update = 1;
+ }
+ Crossfader->put(samples, numsamples);
+ ReleaseMutex(cf_mutex);
+ }
+ }
+ return numorig;
+}
+
+
+void Quit(winampDSPModule *this_mod) {
+ doQuit();
+} \ No newline at end of file
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
diff --git a/Src/Plugins/DSP/dsp_sc/utils.cpp b/Src/Plugins/DSP/dsp_sc/utils.cpp
new file mode 100644
index 00000000..809234df
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/utils.cpp
@@ -0,0 +1,653 @@
+#include <windows.h>
+#include <uxtheme.h>
+#include "utils.h"
+#include "api.h"
+
+#include <time.h>
+#include <string>
+#include <functional>
+#include <stdio.h>
+#include <stdlib.h>
+#include <list>
+#include <fstream>
+#include <sstream>
+#include <shlwapi.h>
+
+#include <winamp/wa_ipc.h>
+#include <winamp/dsp.h>
+#include "resource/resource.h"
+
+extern winampDSPModule module;
+
+// Config file
+char IniName[MAX_PATH] = {0},
+ IniEncName[MAX_PATH] = {0},
+ IniDir[MAX_PATH] = {0},
+ PluginDir[MAX_PATH] = {0};
+wchar_t IniDirW[MAX_PATH] = {0},
+ PluginDirW[MAX_PATH] = {0},
+ SharedDirW[MAX_PATH] = {0};
+HANDLE NextTracks[NUM_OUTPUTS] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
+HANDLE SaveEncoded[NUM_OUTPUTS] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE};
+
+static bool IsVista = false,
+ checked = false;
+
+bool IsVistaUp() {
+ if (checked == false) {
+ OSVERSIONINFO version = {0};
+ version.dwOSVersionInfoSize = sizeof(version);
+ if (!GetVersionEx(&version)) ZeroMemory(&version, sizeof(OSVERSIONINFO));
+ IsVista = (version.dwMajorVersion >= 6);
+ checked = true;
+ }
+ return IsVista;
+}
+
+UINT ver = -1;
+UINT GetWinampVersion(HWND winamp)
+{
+ if(ver == -1)
+ {
+ return (ver = SendMessage(winamp, WM_WA_IPC, 0, IPC_GETVERSION));
+ }
+ return ver;
+}
+
+char* LocalisedStringA(UINT uID, char *str, size_t maxlen) {
+ if (WASABI_API_LNG) {
+ if (!str) {
+ return WASABI_API_LNGSTRING(uID);
+ } else {
+ return WASABI_API_LNGSTRING_BUF(uID, str, maxlen);
+ }
+ } else {
+ __declspec(thread) static char *tmp;
+ char* strtmp = 0;
+ if (!str) {
+ if (!tmp) tmp = (char *)malloc(1024*sizeof(char));
+ strtmp = tmp;
+ maxlen = 1024;
+ } else {
+ strtmp = str;
+ }
+ LoadStringA(module.hDllInstance, uID, strtmp, maxlen);
+ return strtmp;
+ }
+}
+
+wchar_t* LocalisedString(UINT uID, wchar_t *str, size_t maxlen) {
+ if (WASABI_API_LNG) {
+ if (!str) {
+ return WASABI_API_LNGSTRINGW(uID);
+ } else {
+ return WASABI_API_LNGSTRINGW_BUF(uID, str, maxlen);
+ }
+ } else {
+ __declspec(thread) static wchar_t *tmp;
+ wchar_t* strtmp = 0;
+ if (!str) {
+ if (!tmp) tmp = (wchar_t *)malloc(1024*sizeof(wchar_t));
+ strtmp = tmp;
+ maxlen = 1024;
+ } else {
+ strtmp = str;
+ }
+ LoadStringW(module.hDllInstance, uID, strtmp, maxlen);
+ return strtmp;
+ }
+}
+
+HWND LocalisedCreateDialog(HINSTANCE instance, UINT dialog_id, HWND hWndParent, DLGPROC DlgProc, LPARAM user_id) {
+ if (WASABI_API_LNG) {
+ return WASABI_API_CREATEDIALOGPARAMW(dialog_id, hWndParent, DlgProc, user_id);
+ } else {
+ return CreateDialogParamW(instance, MAKEINTRESOURCEW(dialog_id), hWndParent, DlgProc, user_id);
+ }
+}
+
+INT_PTR LocalisedDialogBox(HINSTANCE hDllInstance, UINT dialog_id, HWND hWndParent, DLGPROC lpDialogFunc) {
+ if (WASABI_API_LNG) {
+ return WASABI_API_DIALOGBOXW(dialog_id, hWndParent, lpDialogFunc);
+ } else {
+ return DialogBoxW(hDllInstance, MAKEINTRESOURCEW(dialog_id), hWndParent, lpDialogFunc);
+ }
+}
+
+// about the most reliable way i can find to get the Winamp window as it could
+// have been started with the /CLASS= parameter which then means it won't be
+// 'Winamp v1.x' so instead go for a fixed child window which will always be
+// there (and deals with other apps who create a 'fake' Winamp window (like AIMP)
+// and there are two versions to cope with classic or modern skins being used.
+HWND hwndWinamp = 0;
+BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) {
+ char name[24];
+ GetClassName(hwnd,name,24);
+ // this check will only work for classic skins
+ if (!strnicmp(name, "Winamp PE", 24)) {
+ HWND child = GetWindow(GetWindow(hwnd, GW_CHILD), GW_CHILD);
+ GetClassName(child, name, 24);
+ // this check improves reliability of this check against players
+ // like KMPlayer which also create a fake playlist editor window
+ if (!strnicmp(name, "WinampVis", 24) || strnicmp(name, "TSkinPanel", 24)) {
+ hwndWinamp = GetWindow(hwnd, GW_OWNER);
+ return FALSE;
+ }
+ } else if (!strnicmp(name, "BaseWindow_RootWnd", 24)) {
+ // this check will only work for modern skins
+ HWND child = GetWindow(GetWindow(hwnd, GW_CHILD), GW_CHILD);
+ GetClassName(child, name, 24);
+ if (!strnicmp(name, "Winamp PE", 24)) {
+ hwndWinamp = GetWindow(hwnd, GW_OWNER);
+ return FALSE;
+ }
+ } else if (!strnicmp(name, "Winamp v1.x", 24)) {
+ // this check will fail if /CLASS= was used on Winamp
+ HWND child = GetWindow(hwnd, GW_CHILD);
+ GetClassName(child, name, 24);
+ if (!strnicmp(name, "WinampVis", 24)) {
+ hwndWinamp = hwnd;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+HWND GetWinampHWND(HWND winamp) {
+ // if no HWND is passed then attemp to find it
+ if (!IsWindow(winamp)) {
+ // but only do the enumeration again if we have an invalid HWND cached
+ if (!IsWindow(hwndWinamp)) {
+ hwndWinamp = 0;
+ EnumThreadWindows(GetCurrentThreadId(), EnumWindowsProc, 0);
+ }
+ return hwndWinamp;
+ } else {
+ return (hwndWinamp = winamp);
+ }
+}
+
+HINSTANCE GetMyInstance() {
+ MEMORY_BASIC_INFORMATION mbi = {0};
+ if (VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) {
+ return (HINSTANCE)mbi.AllocationBase;
+ }
+ return NULL;
+}
+
+char* GetIniDirectory(HWND winamp) {
+ if (!IniDir[0]) {
+ // this gets the string of the full ini file path
+ strncpy(IniDir, (char*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORY), MAX_PATH);
+ }
+ return IniDir;
+}
+
+wchar_t* GetIniDirectoryW(HWND winamp) {
+ if (!IniDirW[0]) {
+ // this gets the string of the full ini file path
+ wcsncpy(IniDirW, (wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW), MAX_PATH);
+ }
+ return IniDirW;
+}
+
+char* GetPluginDirectory(HWND winamp) {
+ // this gets the string of the full plug-in folder path
+ strncpy(PluginDir, (char*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORY), MAX_PATH);
+ return PluginDir;
+}
+
+wchar_t* GetPluginDirectoryW(HWND winamp) {
+ // this gets the string of the full plug-in folder path
+ wcsncpy(PluginDirW, (wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW), MAX_PATH);
+ return PluginDirW;
+}
+
+wchar_t* GetSharedDirectoryW(HWND winamp) {
+ // this gets the string of the full shared dll folder path
+ wchar_t* str = (wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETSHAREDDLLDIRECTORYW);
+ if (str > (wchar_t*)65536) {
+ wcsncpy(SharedDirW, str, MAX_PATH);
+ } else {
+ // and on older versions of Winamp we revert to the plug-ins folder path
+ wcsncpy(SharedDirW, GetPluginDirectoryW(winamp), MAX_PATH);
+ }
+ return SharedDirW;
+}
+
+void GetDefaultNextTracksLogFile(HWND winamp, int bufferLen, wchar_t* buffer, int index) {
+ snwprintf(buffer, bufferLen, L"%s\\Plugins\\dsp_sc_nexttracks_%d.log", GetIniDirectoryW(winamp), index+1);
+}
+
+char* GetSCIniFile(HWND winamp) {
+ if (!IniName[0]) {
+
+ // allows support for multiple instances of the dsp_sc.dll
+ // without the settings being saved into the same section
+ char dll_name[MAX_PATH] = {"dsp_sc"};
+ if (GetModuleFileName(module.hDllInstance, dll_name, MAX_PATH)) {
+ PathStripPath(dll_name);
+ PathRemoveExtension(dll_name);
+ }
+ snprintf(IniName, MAX_PATH, "%s\\Plugins\\%s.ini", GetIniDirectory(winamp), dll_name);
+ }
+ return IniName;
+}
+
+wchar_t* GetSCLogFile(HWND winamp, int bufferLen, wchar_t* logFile, int index) {
+ snwprintf(logFile, bufferLen, L"%s\\Plugins\\dsp_sc_%d.log", GetIniDirectoryW(winamp), index + 1);
+ return logFile;
+}
+
+char* CreateLogFileMessage(char* buffer, wchar_t* message, int* len) {
+ SYSTEMTIME sysTime;
+ GetLocalTime(&sysTime);
+ char d[100], t[100], msg[1024];
+ GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, "yyyy'-'MM'-'dd", d, 99);
+ GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, "HH':'mm':'ss", t, 99);
+
+ std::string utf8 = ConvertToUTF8(message);
+ char* m = (char*)utf8.c_str();
+ char* n = msg;
+ while (m && *m) {
+ if (m && *m && *m == '\n') {
+ *n = ' ';
+ } else if (m) {
+ if (n) *n = *m;
+ }
+ m = CharNext(m);
+ n = CharNext(n);
+ }
+ *n = 0;
+
+ *len = snprintf(buffer, 1024, "%s %s\t%s\r\n", d, t, msg);
+ return buffer;
+}
+
+void StartNextTracks(int index, wchar_t* file) {
+ NextTracks[index] = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0);
+ if (NextTracks[index] != INVALID_HANDLE_VALUE) {
+ // reset the file on loading things
+ SetFilePointer(NextTracks[index], 0, NULL, FILE_BEGIN);
+ SetEndOfFile(NextTracks[index]);
+ }
+}
+
+void WriteNextTracks(int index, HWND winamp, std::vector<int> nextListIdx, std::vector<std::wstring> nextList, bool xml) {
+ if (NextTracks[index] != INVALID_HANDLE_VALUE) {
+ DWORD written;
+
+ // reset the file so if there are no tracks then that'll be set
+ SetFilePointer(NextTracks[index], 0, NULL, FILE_BEGIN);
+ SetEndOfFile(NextTracks[index]);
+
+ std::stringstream s;
+ if (xml) {
+ s << "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<nexttracks>\n";
+ }
+
+ if (!nextList.empty()) {
+ std::vector<std::wstring>::const_iterator i = nextList.begin();
+ std::vector<int>::const_iterator idx = nextListIdx.begin();
+ for (int count = 1; i != nextList.end(); ++i, ++idx, count++) {
+ wchar_t *file=(wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, (*idx), IPC_GETPLAYLISTFILEW);
+ if (xml) {
+ std::string filepath = ConvertToUTF8Escaped(file);
+ s << "\t<file seq=\"" << count << "\">" << filepath << "</file>\n\t";
+ std::string next = ConvertToUTF8Escaped((*i).c_str());
+ s << "<title seq=\"" << count << "\">" << next << "</title>\n";
+ } else {
+ std::string rawfilepath = ConvertToUTF8(file);
+ WriteFile(NextTracks[index], rawfilepath.c_str(), rawfilepath.length(), &written, 0);
+ WriteFile(NextTracks[index], "\r\n", 2, &written, 0);
+ }
+ }
+ }
+
+ if (xml) {
+ s << "</nexttracks>\n";
+ WriteFile(NextTracks[index], s.str().data(), s.str().length(), &written, 0);
+ }
+ }
+}
+
+void StopNextTracks(int index) {
+ if (NextTracks[index] != INVALID_HANDLE_VALUE) {
+ CloseHandle(NextTracks[index]);
+ NextTracks[index] = INVALID_HANDLE_VALUE;
+ }
+}
+
+void StartSaveEncoded(int index, wchar_t* file) {
+ SaveEncoded[index] = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0);
+ if (SaveEncoded[index] != INVALID_HANDLE_VALUE) {
+ // reset the file on loading things
+ SetFilePointer(SaveEncoded[index], 0, NULL, FILE_BEGIN);
+ SetEndOfFile(SaveEncoded[index]);
+ }
+}
+
+void WriteSaveEncoded(int index, LPCVOID buffer, int bufferLen) {
+ if (SaveEncoded[index] != INVALID_HANDLE_VALUE) {
+ DWORD written;
+ WriteFile(SaveEncoded[index], buffer, bufferLen, &written, 0);
+ }
+}
+
+void StopSaveEncoded(int index) {
+ if (SaveEncoded[index] != INVALID_HANDLE_VALUE) {
+ CloseHandle(SaveEncoded[index]);
+ SaveEncoded[index] = INVALID_HANDLE_VALUE;
+ }
+}
+
+void StartLogging(int index, int clearOnStart) {
+ wchar_t name[MAX_PATH];
+ logFiles[index] = CreateFileW(GetSCLogFile(module.hwndParent, ARRAYSIZE(name), name, index), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0);
+ if (logFiles[index] != INVALID_HANDLE_VALUE) {
+ // clear the file when started
+ if (clearOnStart) {
+ SetFilePointer(logFiles[index], 0, NULL, FILE_BEGIN);
+ SetEndOfFile(logFiles[index]);
+ } else {
+ SetFilePointer(logFiles[index], 0, NULL, FILE_END);
+ }
+
+ int len = 0;
+ DWORD written;
+ char buf[1024];
+ CreateLogFileMessage(buf, L"Logging starting", &len);
+ WriteFile(logFiles[index], buf, len, &written, 0);
+ }
+}
+
+void StopLogging(int index) {
+ if (logFiles[index] != INVALID_HANDLE_VALUE) {
+ int len = 0;
+ DWORD written;
+ char buf[1024];
+ CreateLogFileMessage(buf, L"Logging stopping\r\n", &len);
+ WriteFile(logFiles[index], buf, len, &written, 0);
+ CloseHandle(logFiles[index]);
+ }
+ logFiles[index] = INVALID_HANDLE_VALUE;
+}
+
+BOOL IsDirectMouseWheelMessage(const UINT uMsg) {
+ static UINT WINAMP_WM_DIRECT_MOUSE_WHEEL = WM_NULL;
+
+ if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL) {
+ WINAMP_WM_DIRECT_MOUSE_WHEEL = RegisterWindowMessageW(L"WINAMP_WM_DIRECT_MOUSE_WHEEL");
+ if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL)
+ return FALSE;
+ }
+
+ return (WINAMP_WM_DIRECT_MOUSE_WHEEL == uMsg);
+}
+
+HWND ActiveChildWindowFromPoint(HWND hwnd, POINTS cursor_s, const int *controls, size_t controlsCount) {
+ POINT pt = {0};
+ RECT controlRect = {0};
+ POINTSTOPOINT(pt, cursor_s);
+
+ while (controlsCount--) {
+ HWND controlWindow = GetDlgItem(hwnd, controls[controlsCount]);
+ if (NULL != controlWindow &&
+ FALSE != GetClientRect(controlWindow, &controlRect)) {
+ MapWindowPoints(controlWindow, HWND_DESKTOP, (POINT*)&controlRect, 2);
+ if (FALSE != PtInRect(&controlRect, pt)) {
+ unsigned long windowStyle;
+ windowStyle = (unsigned long)GetWindowLongPtrW(controlWindow, GWL_STYLE);
+ if (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & windowStyle))
+ return controlWindow;
+ break;
+ }
+ }
+ }
+ return NULL;
+}
+
+BOOL DirectMouseWheel_ProcessDialogMessage(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) {
+ if (FALSE != IsDirectMouseWheelMessage(uMsg)) {
+ const int controls[] = {
+ IDC_MUSSLIDER,
+ IDC_MUS2SLIDER,
+ IDC_MICSLIDER,
+ IDC_FADESLIDER,
+ IDC_MICFADESLIDER,
+ };
+ HWND targetWindow = ActiveChildWindowFromPoint(hwnd, MAKEPOINTS(lParam), controls, ARRAYSIZE(controls));
+ if (NULL != targetWindow) {
+ SendMessage(targetWindow, WM_MOUSEWHEEL, wParam, lParam);
+ SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (long)TRUE);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
+static HCURSOR link_hand_cursor;
+LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam);
+ // override the normal cursor behaviour so we have a hand to show it is a link
+ if (uMsg == WM_SETCURSOR) {
+ if ((HWND)wParam == hwndDlg) {
+ if (!link_hand_cursor) {
+ link_hand_cursor = LoadCursor(NULL, IDC_HAND);
+ }
+ SetCursor(link_hand_cursor);
+ return TRUE;
+ }
+ }
+ return ret;
+}
+
+void link_startsubclass(HWND hwndDlg, UINT id) {
+ HWND ctrl = GetDlgItem(hwndDlg, id);
+ if (!GetPropW(ctrl, L"link_proc")) {
+ SetPropW(ctrl, L"link_proc", (HANDLE)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor));
+ }
+}
+
+BOOL link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ if (uMsg == WM_DRAWITEM) {
+ DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam;
+ if (di->CtlType == ODT_BUTTON) {
+ wchar_t wt[123];
+ int y;
+ RECT r;
+
+ // due to the fun of theming and owner drawing we have to get the background colour
+ if (isthemethere){
+ HTHEME hTheme = OpenThemeData(hwndDlg, L"Tab");
+ if (hTheme) {
+ DrawThemeParentBackground(di->hwndItem, di->hDC, &di->rcItem);
+ CloseThemeData(hTheme);
+ }
+ }
+
+ HPEN hPen, hOldPen;
+ GetDlgItemTextW(hwndDlg, wParam, wt, ARRAYSIZE(wt));
+
+ // draw text
+ SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
+ r = di->rcItem;
+ r.left += 2;
+ DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE);
+
+ memset(&r, 0, sizeof(r));
+ DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT);
+
+ // draw underline
+ y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1;
+ hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220));
+ hOldPen = (HPEN) SelectObject(di->hDC, hPen);
+ MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL);
+ LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y);
+ SelectObject(di->hDC, hOldPen);
+ DeleteObject(hPen);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+#include <map>
+class xmlEscapes: public std::map<char,std::string>
+{
+public:
+ xmlEscapes() {
+ (*this)['<'] = "&lt;";
+ (*this)['>'] = "&gt;";
+ (*this)['&'] = "&amp;";
+ (*this)['\''] = "&apos;";
+ (*this)['"'] = "&quot;";
+ }
+};
+
+static const xmlEscapes gsXmlEscapes;
+
+// this will only be receiving an already converted
+// string so no need to do the commented part again
+char* escapeXML(const char* s) {
+ static char result[2048] = {0};
+ memset(&result, 0, 2048);
+ int len = strlen(s);
+ for (int x = 0, y = 0; x < len; x++)
+ {
+ xmlEscapes::const_iterator i = gsXmlEscapes.find(s[x]);
+ if (i != gsXmlEscapes.end()) {
+ strcat(&result[y-1], (*i).second.c_str());
+ y += (*i).second.size();
+ } else if (s[x] >= 0 && s[x] <= 31 && s[x] != 9 && s[x] != 10 && s[x] != 13) {
+ // strip out characters which aren't supported by the DNAS
+ // (only allow backspace, linefeed and carriage return)
+ #ifdef DEBUG
+ result[y] = '\xEF';
+ y++;
+ result[y] = '\xBF';
+ y++;
+ result[y] = '\xBD';
+ y++;
+ #endif
+ } else if ((x < len - 2) && s[x] == '\xEF' && s[x+1] == '\xBF' && s[x+2] == '\xBF') {
+ // and any UTF-8 boms which are in there (seen it happen!)
+ x+=2;
+ #ifdef DEBUG
+ result[y] = '\xEF';
+ y++;
+ result[y] = '\xBF';
+ y++;
+ result[y] = '\xBD';
+ y++;
+ #endif
+ } else {
+ result[y] = s[x];
+ y++;
+ }
+ }
+ return result;
+}
+
+char* ConvertToUTF8Escaped(const wchar_t *str) {
+ static char utf8tmp[1024] = {0};
+ memset(&utf8tmp, 0, sizeof(utf8tmp));
+ WideCharToMultiByte(CP_UTF8, 0, str, -1, utf8tmp, sizeof(utf8tmp), 0, 0);
+ return escapeXML(utf8tmp);
+}
+
+char* ConvertToUTF8(const wchar_t *str) {
+ static char utf8tmp2[1024] = {0};
+ memset(&utf8tmp2, 0, sizeof(utf8tmp2));
+ WideCharToMultiByte(CP_UTF8, 0, str, -1, utf8tmp2, sizeof(utf8tmp2), 0, 0);
+ return utf8tmp2;
+}
+
+int ConvertFromUTF8(const char *src, wchar_t *dest, int destlen) {
+ if (destlen == 0)
+ return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, destlen);
+ int converted = MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, destlen-1);
+ if (!converted)
+ return 0;
+ dest[converted]=0;
+ return converted+1;
+}
+
+DWORD GetPrivateProfileStringUTF8(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPWSTR lpReturnedString, DWORD nSize, LPCSTR lpFileName) {
+ char tmp[MAX_PATH] = {0};
+ GetPrivateProfileString(lpAppName, lpKeyName, lpDefault, tmp, nSize, lpFileName);
+ return ConvertFromUTF8(tmp, lpReturnedString, nSize);
+}
+
+
+void ShowWindowDlgItem(HWND hDlg, int nIDDlgItem, int nCmdShow) {
+ ShowWindow(GetDlgItem(hDlg, nIDDlgItem), nCmdShow);
+}
+
+void EnableWindowDlgItem(HWND hDlg, int nIDDlgItem, BOOL bEnable) {
+ EnableWindow(GetDlgItem(hDlg, nIDDlgItem), bEnable);
+}
+
+template<typename S,typename F>
+std::vector<S> tokenizer_if(const S &ins,F isdelimiter) throw()
+{
+ std::vector<S> result;
+ S accum;
+
+ for(typename S::const_iterator i = ins.begin(); i != ins.end(); ++i)
+ {
+ if (!isdelimiter(*i))
+ {
+ accum.push_back(*i);// was +=
+ }
+ else
+ {
+ if (!accum.empty())
+ {
+ result.push_back(accum);
+ accum = S();
+ }
+ }
+ }
+
+ if (!accum.empty())
+ result.push_back(accum);
+ return result;
+}
+
+template<typename S>
+inline std::vector<S> tokenizer(const S &ins,typename S::value_type delim) throw()
+ { return tokenizer_if(ins,bind1st(std::equal_to<typename S::value_type>(),delim)); }
+
+extern char sourceVersion[64];
+bool CompareVersions(char *verStr)
+{
+ bool needsUpdating = false;
+ if (verStr && *verStr)
+ {
+ std::vector<std::string> newVerStr = tokenizer(std::string(verStr), '.');
+ std::vector<std::string> curVerStr = tokenizer(std::string(sourceVersion), '.');
+ int newVer[] = {::atoi(newVerStr[0].c_str()), ::atoi(newVerStr[1].c_str()), ::atoi(newVerStr[2].c_str()), ::atoi(newVerStr[3].c_str())},
+ curVer[] = {::atoi(curVerStr[0].c_str()), ::atoi(curVerStr[1].c_str()), ::atoi(curVerStr[2].c_str()), ::atoi(curVerStr[3].c_str())};
+
+ // look to compare from major to minor parts of the version strings
+ // 2.x.x.x vs 3.x.x.x
+ if (newVer[0] > curVer[0]) {
+ needsUpdating = true;
+ }
+ // 2.0.x.x vs 2.2.x.x
+ else if((newVer[0] == curVer[0]) && (newVer[1] > curVer[1])) {
+ needsUpdating = true;
+ }
+ // 2.0.0.x vs 2.0.1.x
+ else if((newVer[0] == curVer[0]) && (newVer[1] == curVer[1]) && (newVer[2] > curVer[2])) {
+ needsUpdating = true;
+ }
+ // 2.0.0.29 vs 2.0.0.30
+ else if((newVer[0] == curVer[0]) && (newVer[1] == curVer[1]) && (newVer[2] == curVer[2]) && (newVer[3] > curVer[3])) {
+ needsUpdating = true;
+ }
+ }
+ return needsUpdating;
+} \ No newline at end of file
diff --git a/Src/Plugins/DSP/dsp_sc/utils.h b/Src/Plugins/DSP/dsp_sc/utils.h
new file mode 100644
index 00000000..3e816142
--- /dev/null
+++ b/Src/Plugins/DSP/dsp_sc/utils.h
@@ -0,0 +1,67 @@
+#ifndef __UTILS_H
+#define __UTILS_H
+
+#include <vector>
+#include <string>
+
+#ifdef WIN32
+#define snprintf _snprintf
+#define snwprintf _snwprintf
+#endif
+
+extern char IniName[MAX_PATH],
+ IniEncName[MAX_PATH],
+ IniDir[MAX_PATH];
+
+wchar_t* GetSharedDirectoryW(HWND winamp);
+wchar_t* GetPluginDirectoryW(HWND winamp);
+char* GetIniDirectory(HWND winamp);
+char* GetSCIniFile(HWND winamp);
+void GetDefaultNextTracksLogFile(HWND winamp, int bufferLen, wchar_t* buffer, int index);
+
+#define NUM_OUTPUTS 5
+extern HANDLE logFiles[NUM_OUTPUTS];
+wchar_t* GetSCLogFile(HWND winamp, int bufferLen, wchar_t* logFile, int index);
+char* CreateLogFileMessage(char* buffer, wchar_t* message, int* len);
+void StartLogging(int index, int clearOnStart);
+void StopLogging(int index);
+
+void StartNextTracks(int index, wchar_t* file);
+void WriteNextTracks(int index, HWND winamp, std::vector<int> nextListIdx, std::vector<std::wstring> nextList, bool xml);
+void StopNextTracks(int index);
+
+void StartSaveEncoded(int index, wchar_t* file);
+void WriteSaveEncoded(int index, LPCVOID buffer, int bufferLen);
+void StopSaveEncoded(int index);
+
+INT_PTR LocalisedDialogBox(HINSTANCE hDllInstance, UINT dialog_id, HWND hWndParent, DLGPROC lpDialogFunc);
+HWND LocalisedCreateDialog(HINSTANCE instance, UINT dialog_id, HWND hWndParent, DLGPROC DlgProc, LPARAM user_id);
+char* LocalisedStringA(UINT uID, char *str, size_t maxlen);
+wchar_t* LocalisedString(UINT uID, wchar_t *str, size_t maxlen);
+
+UINT GetWinampVersion(HWND winamp);
+
+bool IsVistaUp();
+HINSTANCE GetMyInstance();
+HWND GetWinampHWND(HWND winamp);
+
+extern int isthemethere;
+BOOL link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+void link_startsubclass(HWND hwndDlg, UINT id);
+BOOL DirectMouseWheel_ProcessDialogMessage(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam);
+
+char* escapeXML(const char* s);
+char* ConvertToUTF8Escaped(const wchar_t *str);
+char* ConvertToUTF8(const wchar_t *str);
+int ConvertFromUTF8(const char *src, wchar_t *dest, int destlen);
+// reads a unicode string stored in utf8 from an ini file
+DWORD GetPrivateProfileStringUTF8(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPWSTR lpReturnedString, DWORD nSize, LPCSTR lpFileName);
+
+void WritePrivateProfileInt(LPCSTR lpKeyName, int value, LPCSTR lpAppName, LPCSTR lpFileName);
+
+void ShowWindowDlgItem(HWND hDlg, int nIDDlgItem, int nCmdShow);
+void EnableWindowDlgItem(HWND hDlg, int nIDDlgItem, BOOL bEnable);
+
+bool CompareVersions(char *verStr);
+
+#endif \ No newline at end of file