mirror of
https://github.com/WinampDesktop/winamp.git
synced 2024-09-24 15:54:12 +00:00
5804 lines
No EOL
220 KiB
C++
5804 lines
No EOL
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();
|
|
} |