winamp/Src/Plugins/DSP/dsp_sc/main.cpp
2024-09-24 14:54:57 +02:00

5804 lines
220 KiB
C++

// 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();
}