aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/DSP/dsp_sc/main.cpp
diff options
context:
space:
mode:
authorJean-Francois Mauguit <jfmauguit@mac.com>2024-09-24 09:03:25 -0400
committerGitHub <noreply@github.com>2024-09-24 09:03:25 -0400
commitbab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch)
tree12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/Plugins/DSP/dsp_sc/main.cpp
parent4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff)
parent20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff)
downloadwinamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/Plugins/DSP/dsp_sc/main.cpp')
-rw-r--r--Src/Plugins/DSP/dsp_sc/main.cpp5804
1 files changed, 5804 insertions, 0 deletions
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