[NCSF] Fix volume issues with a little help from the Nintendo DS SDK.
[NCSF] Fix volume issues with a little help from the Nintendo DS SDK.

Also added a clone of DeSmuME's Sound View that is only build during a
debug build, which helped to identify the above issues.

file:a/README.txt -> file:b/README.txt
--- a/README.txt
+++ b/README.txt
@@ -45,14 +45,20 @@
   v1.9 - 2014-09-28 - Removed the Cosine, B-Spline, and Osculating
                       interpolations, added 4-point and 6-point Legrange
                       interpolations in their place.
-                    - Updated zlib to v1.28.
+                    - Updated zlib to v1.2.8.
+v1.9.1 - 2014-10-05 - Fixed volume issues that stemmed from how FeOS Sound
+                      System was handling volume. Utilized some code from the
+                      Nintendo DS SDK to help fix this.
+                    - Added a clone of DeSmuME's Sound View that only shows up
+                      in debug builds, was used to help me identify the above
+                      issue to fix it.
 
 This is a Winamp plugin to play NCSF files. NCSF is a PSF-style music format that
 uses SDAT files from Nintendo DS ROMs as it's "program".
 
 Contains:
-*    in_ncsf.dll - The NCSF Winamp plugin
-* zlib DLL v1.28 - Required by the plugin
+*     in_ncsf.dll - The NCSF Winamp plugin
+* zlib DLL v1.2.8 - Required by the plugin
 
 To allow Winamp to use the plugin, place in_ncsf.dll into your Winamp plugins
 directory and zlib1.dll into your Winamp directory (NOTE: NOT the plugins

--- a/src/in_ncsf/SSEQPlayer/Channel.cpp
+++ b/src/in_ncsf/SSEQPlayer/Channel.cpp
@@ -1,7 +1,7 @@
 /*
  * SSEQ Player - Channel structures
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-06-17
+ * Last modification on 2014-10-05
  *
  * Adapted from source code of FeOS Sound System
  * By fincs
@@ -71,10 +71,8 @@
 void Channel::UpdateVol(const Track &trk)
 {
 	int finalVol = trk.ply->masterVol;
-	finalVol += Cnv_Sust(trk.vol);
-	finalVol += Cnv_Sust(trk.expr);
-	if (finalVol < -AMPL_K)
-		finalVol = -AMPL_K;
+	finalVol += Cnv_Scale(trk.vol);
+	finalVol += Cnv_Scale(trk.expr);
 	this->extAmpl = finalVol;
 }
 
@@ -390,7 +388,7 @@
 		tmr <<= shift;
 	}
 	else
-		return 0x10;
+		return 0xFFFF;
 
 	if (tmr < 0x10)
 		return 0x10;
@@ -539,18 +537,19 @@
 			totalVol += this->velocity;
 			if (bModulation && this->modType == 1)
 				totalVol += modParam;
-			totalVol += AMPL_K;
-			if (totalVol < 0)
+			if (totalVol < -AMPL_K)
+				totalVol = -AMPL_K;
+			else if (totalVol > 0)
 				totalVol = 0;
 
 			cr &= ~(SOUND_VOL(0x7F) | SOUND_VOLDIV(3));
-			cr |= SOUND_VOL(static_cast<int>(getvoltbl[totalVol]));
-
-			if (totalVol < AMPL_K - 240)
+			cr |= SOUND_VOL(static_cast<int>(getvoltbl[totalVol + AMPL_K]));
+
+			if (totalVol < -240)
 				cr |= SOUND_VOLDIV(3);
-			else if (totalVol < AMPL_K - 120)
+			else if (totalVol < -120)
 				cr |= SOUND_VOLDIV(2);
-			else if (totalVol < AMPL_K - 60)
+			else if (totalVol < -60)
 				cr |= SOUND_VOLDIV(1);
 
 			this->vol = ((cr & SOUND_VOL(0x7F)) << 4) >> calcVolDivShift((cr & SOUND_VOLDIV(3)) >> 8);

--- a/src/in_ncsf/SSEQPlayer/Track.cpp
+++ b/src/in_ncsf/SSEQPlayer/Track.cpp
@@ -1,7 +1,7 @@
 /*
  * SSEQ Player - Track structure
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2013-04-01
+ * Last modification on 2014-10-05
  *
  * Adapted from source code of FeOS Sound System
  * By fincs
@@ -187,7 +187,7 @@
 	chn->prio = this->prio;
 	chn->key = key;
 	chn->orgKey = bIsPCM ? noteDef->noteNumber : 69;
-	chn->velocity = Cnv_Sust(vel);
+	chn->velocity = Cnv_Scale(vel);
 	chn->pan = static_cast<int>(noteDef->pan) - 64;
 	chn->modDelayCnt = 0;
 	chn->modCounter = 0;
@@ -229,7 +229,7 @@
 	chn->flags.reset();
 	chn->prio = this->prio;
 	chn->key = key;
-	chn->velocity = Cnv_Sust(vel);
+	chn->velocity = Cnv_Scale(vel);
 	chn->modDelayCnt = 0;
 	chn->modCounter = 0;
 

--- a/src/in_ncsf/SSEQPlayer/common.h
+++ b/src/in_ncsf/SSEQPlayer/common.h
@@ -1,7 +1,7 @@
 /*
  * SSEQ Player - Common functions
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-08
+ * Last modification on 2014-10-05
  *
  * Some code from FeOS Sound System
  * By fincs
@@ -159,6 +159,33 @@
 		return ((fall << 1) + 1) & 0xFFFF;
 	else
 		return (0x1E00 / (0x7E - fall)) & 0xFFFF;
+}
+
+// This function actually doesn't come from FSS, I got the lookup table from the Nintendo DS SDK.
+inline int Cnv_Scale(int scale)
+{
+	static const int16_t lut[] =
+	{
+		-32768, -421, -361, -325, -300, -281, -265, -252,
+		-240, -230, -221, -212, -205, -198, -192, -186,
+		-180, -175, -170, -165, -161, -156, -152, -148,
+		-145, -141, -138, -134, -131, -128, -125, -122,
+		-120, -117, -114, -112, -110, -107, -105, -103,
+		-100, -98, -96, -94, -92, -90, -88, -86,
+		-85, -83, -81, -79, -78, -76, -74, -73,
+		-71, -70, -68, -67, -65, -64, -62, -61,
+		-60, -58, -57, -56, -54, -53, -52, -51,
+		-49, -48, -47, -46, -45, -43, -42, -41,
+		-40, -39, -38, -37, -36, -35, -34, -33,
+		-32, -31, -30, -29, -28, -27, -26, -25,
+		-24, -23, -23, -22, -21, -20, -19, -18,
+		-17, -17, -16, -15, -14, -13, -12, -12,
+		-11, -10, -9, -9, -8, -7, -6, -6,
+		-5, -4, -3, -3, -2, -1, -1, 0
+	};
+	if (scale & 0x80)
+		scale = 0x7F;
+	return lut[scale];
 }
 
 inline int Cnv_Sust(int sust)
@@ -184,7 +211,7 @@
 	};
 
 	if (sust & 0x80) // Supposedly invalid value...
-		sust = 0; // Use apparently correct default
+		sust = 0x7F; // Use apparently correct default
 	return lut[sust];
 }
 

--- a/src/in_ncsf/XSFConfig_NCSF.cpp
+++ b/src/in_ncsf/XSFConfig_NCSF.cpp
@@ -1,15 +1,17 @@
 /*
  * xSF - NCSF configuration
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-28
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  */
 
-#include <bitset>
-#include "XSFPlayer_NCSF.h"
-#include "XSFConfig.h"
+#include "XSFConfig_NCSF.h"
 #include "convert.h"
+#ifdef _DEBUG
+# include "resource.h"
+# include <CommCtrl.h>
+#endif
 
 enum
 {
@@ -17,31 +19,9 @@
 	idMutes
 };
 
-class XSFConfig_NCSF : public XSFConfig
-{
-protected:
-	static unsigned initInterpolation;
-	static std::string initMutes;
-
-	friend class XSFConfig;
-	unsigned interpolation;
-	std::bitset<16> mutes;
-
-	XSFConfig_NCSF();
-	void LoadSpecificConfig();
-	void SaveSpecificConfig();
-	void GenerateSpecificDialogs();
-	INT_PTR CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
-	void ResetSpecificConfigDefaults(HWND hwndDlg);
-	void SaveSpecificConfigDialog(HWND hwndDlg);
-	void CopySpecificConfigToMemory(XSFPlayer *xSFPlayer, bool preLoad);
-public:
-	void About(HWND parent);
-};
-
 unsigned XSFConfig::initSampleRate = 44100;
 std::string XSFConfig::commonName = "NCSF Decoder";
-std::string XSFConfig::versionNumber = "1.9";
+std::string XSFConfig::versionNumber = "1.9.1";
 unsigned XSFConfig_NCSF::initInterpolation = 4;
 std::string XSFConfig_NCSF::initMutes = "0000000000000000";
 
@@ -51,6 +31,9 @@
 }
 
 XSFConfig_NCSF::XSFConfig_NCSF() : XSFConfig(), interpolation(0), mutes()
+#ifdef _DEBUG
+	, soundViewData()
+#endif
 {
 	this->supportedSampleRates.push_back(8000);
 	this->supportedSampleRates.push_back(11025);
@@ -131,7 +114,7 @@
 
 void XSFConfig_NCSF::CopySpecificConfigToMemory(XSFPlayer *xSFPlayer, bool)
 {
-	XSFPlayer_NCSF *NCSFPlayer = static_cast<XSFPlayer_NCSF *>(xSFPlayer);
+	auto NCSFPlayer = static_cast<XSFPlayer_NCSF *>(xSFPlayer);
 	NCSFPlayer->SetInterpolation(this->interpolation);
 	NCSFPlayer->SetMutes(this->mutes);
 }
@@ -142,3 +125,223 @@
 		"Utilizes code adapted from the FeOS Sound System library by fincs, git revision 5204c55 on GitHub, for audio playback.").c_str(), ConvertFuncs::StringToWString(XSFConfig::commonName + " v" + XSFConfig::versionNumber).c_str(), MB_OK);
 }
 
+#ifdef _DEBUG
+INT_PTR CALLBACK XSFConfig_NCSF::SoundViewDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+	auto data = reinterpret_cast<SoundViewData *>(GetWindowLongW(hwndDlg, DWLP_USER));
+	if (!data && uMsg != WM_INITDIALOG)
+		return false;
+
+	switch (uMsg)
+	{
+		case WM_INITDIALOG:
+			if (!data)
+			{
+				data = reinterpret_cast<SoundViewData *>(lParam);
+				SetWindowLongW(hwndDlg, DWLP_USER, reinterpret_cast<LONG>(data));
+			}
+			data->hDlg = hwndDlg;
+			for (int chanId = 0; chanId < 16; ++chanId)
+			{
+				SendDlgItemMessageW(hwndDlg, IDC_SOUND0VOLBAR + chanId, PBM_SETRANGE, 0, MAKELPARAM(0, 128));
+				SendDlgItemMessageW(hwndDlg, IDC_SOUND0PANBAR + chanId, PBM_SETRANGE, 0, MAKELPARAM(0, 128));
+				SendDlgItemMessageW(hwndDlg, IDC_SOUND0MUTE + chanId, BM_SETCHECK, data->config->mutes[chanId], 0);
+			}
+			break;
+		case WM_CLOSE:
+		case WM_DESTROY:
+			data->config->CloseSoundView();
+			break;
+		case WM_COMMAND:
+			{
+				auto id = GET_WM_COMMAND_ID(wParam, lParam);
+				switch (id)
+				{
+					case IDC_BUTTON_VOLMODE:
+						data->volModeAlternative = !!IsDlgButtonChecked(hwndDlg, IDC_BUTTON_VOLMODE);
+						break;
+					case IDC_SOUND0MUTE:
+					case IDC_SOUND1MUTE:
+					case IDC_SOUND2MUTE:
+					case IDC_SOUND3MUTE:
+					case IDC_SOUND4MUTE:
+					case IDC_SOUND5MUTE:
+					case IDC_SOUND6MUTE:
+					case IDC_SOUND7MUTE:
+					case IDC_SOUND8MUTE:
+					case IDC_SOUND9MUTE:
+					case IDC_SOUND10MUTE:
+					case IDC_SOUND11MUTE:
+					case IDC_SOUND12MUTE:
+					case IDC_SOUND13MUTE:
+					case IDC_SOUND14MUTE:
+					case IDC_SOUND15MUTE:
+						data->config->mutes[id - IDC_SOUND0MUTE] = !!IsDlgButtonChecked(hwndDlg, id);
+						data->config->SaveConfig();
+						data->player->SetMutes(data->config->mutes);
+						break;
+					case IDC_SOUND_UNMUTE_ALL:
+						for (int chanId = 0; chanId < 16; ++chanId)
+							SendDlgItemMessageW(hwndDlg, IDC_SOUND0MUTE + chanId, BM_SETCHECK, false, 0);
+						data->config->mutes.reset();
+						data->config->SaveConfig();
+						data->player->SetMutes(data->config->mutes);
+						break;
+					default:
+						return false;
+				}
+			}
+			break;
+		default:
+			return false;
+	}
+
+	return true;
+}
+
+void XSFConfig_NCSF::CallSoundView(XSFPlayer *xSFPlayer, HINSTANCE hInstance, HWND hwndParent)
+{
+	this->soundViewData.reset(new SoundViewData);
+	this->soundViewData->config = this;
+	this->soundViewData->player = static_cast<XSFPlayer_NCSF *>(xSFPlayer);
+
+	auto hDlg = CreateDialogParamW(hInstance, MAKEINTRESOURCEW(IDD_SOUND_VIEW), hwndParent, XSFConfig_NCSF::SoundViewDialogProc, reinterpret_cast<LPARAM>(this->soundViewData.get()));
+	if (!hDlg)
+	{
+		this->soundViewData.reset();
+		return;
+	}
+
+	ShowWindow(hDlg, SW_SHOW);
+	UpdateWindow(hDlg);
+}
+
+static inline void ProgressSetPosImmediate(HWND hDlg, int nIDDlgItem, int nPos)
+{
+	int nMin = SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_GETRANGE, true, reinterpret_cast<LPARAM>(nullptr));
+	int nMax = SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_GETRANGE, false, reinterpret_cast<LPARAM>(nullptr));
+
+	// get rid of fancy progress animation since Windows Vista
+	// http://stackoverflow.com/questions/1061715/how-do-i-make-tprogressbar-stop-lagging
+	if (nPos < nMax)
+	{
+		SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_SETPOS, nPos + 1, 0);
+		SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_SETPOS, nPos, 0); // This will set Progress backwards and give an instant update
+	}
+	else
+	{
+		SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_SETRANGE32, nMin, nPos + 1);
+		SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_SETPOS, nPos + 1, 0);
+		SendDlgItemMessageW(hDlg, nIDDlgItem, PBM_SETRANGE32, nMin, nPos); // This will also set Progress backwards also so instant update
+	}
+}
+
+static inline int32_t muldiv7(int32_t val, uint8_t mul)
+{
+	return mul == 127 ? val : ((val * mul) >> 7);
+}
+
+void XSFConfig_NCSF::RefreshSoundView()
+{
+	auto player = this->soundViewData->player;
+	auto hDlg = this->soundViewData->hDlg;
+	std::wstring buf;
+	for (size_t chanId = 0; chanId < 16; ++chanId)
+	{
+		auto &chn = player->GetChannel(chanId);
+
+		if (chn.state > CS_START)
+		{
+			ProgressSetPosImmediate(hDlg, IDC_SOUND0PANBAR + chanId, muldiv7(128, chn.reg.panning));
+			uint8_t datashift = chn.reg.volumeDiv;
+			if (datashift == 3)
+				datashift = 4;
+			int32_t vol = muldiv7(128, chn.reg.volumeMul) >> datashift;
+			ProgressSetPosImmediate(hDlg, IDC_SOUND0VOLBAR + chanId, vol);
+
+			if (this->soundViewData->volModeAlternative)
+				buf = wstringify(chn.reg.volumeMul) + L"/" + wstringify(1 << datashift);
+			else
+				buf = wstringify(vol);
+			SetDlgItemTextW(hDlg, IDC_SOUND0VOL + chanId, buf.c_str());
+
+			if (!chn.reg.panning)
+				buf = L"L";
+			else if (chn.reg.panning == 64)
+				buf = L"C";
+			else if (chn.reg.panning == 127)
+				buf = L"R";
+			else if (chn.reg.panning < 64)
+				buf = L"L" + wstringify(64 - chn.reg.panning);
+			else //if (chn.reg.panning > 64)
+				buf = L"R" + wstringify(chn.reg.panning - 64);
+			SetDlgItemTextW(hDlg, IDC_SOUND0PAN + chanId, buf.c_str());
+
+			SetDlgItemTextW(hDlg, IDC_SOUND0HOLD + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0BUSY + chanId, L"---");
+
+			static const std::wstring modes[] = { L"Manual", L"Loop Infinite", L"One-Shot", L"Prohibited" };
+			SetDlgItemTextW(hDlg, IDC_SOUND0REPEATMODE + chanId, (wstringify(chn.reg.repeatMode) + L" (" + modes[chn.reg.repeatMode] + L")").c_str());
+
+			if (chn.reg.format != 3)
+			{
+				static const std::wstring formats[] = { L"PCM8", L"PCM16", L"IMA-ADPCM" };
+				SetDlgItemTextW(hDlg, IDC_SOUND0FORMAT + chanId, (wstringify(chn.reg.format) + L" (" + formats[chn.reg.format] + L")").c_str());
+			}
+			else
+			{
+				buf = L"3 (";
+				if (chanId < 8)
+					buf += L"PSG/Noise?";
+				else if (chanId < 14)
+					buf += wstringify(chn.reg.waveDuty == 7 ? 0 : 12.5 * (chn.reg.waveDuty + 1)) + L"% Square";
+				else
+					buf += L"Noise";
+				buf += L")";
+				SetDlgItemTextW(hDlg, IDC_SOUND0FORMAT + chanId, buf.c_str());
+			}
+
+			SetDlgItemTextW(hDlg, IDC_SOUND0SAD + chanId, L"---");
+
+			SetDlgItemTextW(hDlg, IDC_SOUND0PNT + chanId, (L"samp #" + wstringify(chn.reg.loopStart)).c_str());
+
+			std::wstring tmpBuf = ConvertFuncs::StringToWString(NumToHexString(chn.reg.timer)).substr(2);
+			buf = L"$" + tmpBuf + L" (";
+			tmpBuf = wstringify((ARM7_CLOCK / 2) / static_cast<double>(0x10000 - chn.reg.timer));
+			if (tmpBuf.find('.') != std::wstring::npos)
+				tmpBuf = tmpBuf.substr(0, tmpBuf.find('.') + 2);
+			buf += tmpBuf + L" Hz)";
+			SetDlgItemTextW(hDlg, IDC_SOUND0TMR + chanId, buf.c_str());
+
+			SetDlgItemTextW(hDlg, IDC_SOUND0POSLEN + chanId, (L"samp #" + wstringify(static_cast<uint32_t>(chn.reg.samplePosition)) + L" / " + wstringify(chn.reg.totalLength)).c_str());
+		}
+		else if (this->soundViewData->channelLastStates[chanId] != CS_NONE)
+		{
+			ProgressSetPosImmediate(hDlg, IDC_SOUND0PANBAR + chanId, 0);
+			ProgressSetPosImmediate(hDlg, IDC_SOUND0VOLBAR + chanId, 0);
+			SetDlgItemTextW(hDlg, IDC_SOUND0VOL + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0PAN + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0HOLD + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0BUSY + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0REPEATMODE + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0FORMAT + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0SAD + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0PNT + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0TMR + chanId, L"---");
+			SetDlgItemTextW(hDlg, IDC_SOUND0POSLEN + chanId, L"---");
+		}
+
+		this->soundViewData->channelLastStates[chanId] = chn.state;
+	}
+}
+
+void XSFConfig_NCSF::CloseSoundView()
+{
+	if (this->soundViewData)
+	{
+		DestroyWindow(this->soundViewData->hDlg);
+		this->soundViewData.reset();
+	}
+}
+#endif
+

--- /dev/null
+++ b/src/in_ncsf/XSFConfig_NCSF.h
@@ -1,1 +1,70 @@
+/*
+ * xSF - NCSF configuration
+ * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
+ * Last modification on 2014-10-05
+ *
+ * Partially based on the vio*sf framework
+ */
 
+#pragma once
+
+#include <bitset>
+#include <memory>
+#include "XSFConfig.h"
+#include "XSFPlayer_NCSF.h"
+#include "windowsh_wrapper.h"
+
+class XSFConfig_NCSF;
+
+#ifdef _DEBUG
+struct SoundViewData
+{
+	XSFConfig_NCSF *config;
+	XSFPlayer_NCSF *player;
+	uint8_t channelLastStates[16];
+	HWND hDlg;
+
+	bool volModeAlternative;
+
+	SoundViewData() : config(nullptr), player(nullptr), hDlg(nullptr), volModeAlternative(false)
+	{
+		std::fill_n(&this->channelLastStates[0], sizeof(this->channelLastStates), CS_START);
+	}
+};
+#endif
+
+class XSFConfig_NCSF : public XSFConfig
+{
+protected:
+	static unsigned initInterpolation;
+	static std::string initMutes;
+
+	friend class XSFConfig;
+	friend struct SoundViewData;
+	unsigned interpolation;
+	std::bitset<16> mutes;
+
+	XSFConfig_NCSF();
+	void LoadSpecificConfig();
+	void SaveSpecificConfig();
+	void GenerateSpecificDialogs();
+	INT_PTR CALLBACK ConfigDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+	void ResetSpecificConfigDefaults(HWND hwndDlg);
+	void SaveSpecificConfigDialog(HWND hwndDlg);
+	void CopySpecificConfigToMemory(XSFPlayer *xSFPlayer, bool preLoad);
+
+#ifdef _DEBUG
+	std::unique_ptr<SoundViewData> soundViewData;
+
+	static INT_PTR CALLBACK SoundViewDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
+#endif
+public:
+	void About(HWND parent);
+
+#ifdef _DEBUG
+	void CallSoundView(XSFPlayer *xSFPlayer, HINSTANCE hInstance, HWND hwndParent);
+	void RefreshSoundView();
+	void CloseSoundView();
+#endif
+};
+

--- a/src/in_ncsf/XSFPlayer_NCSF.cpp
+++ b/src/in_ncsf/XSFPlayer_NCSF.cpp
@@ -1,7 +1,7 @@
 /*
  * xSF - NCSF Player
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-24
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  *
@@ -13,12 +13,15 @@
 #include <zlib.h>
 #include "convert.h"
 #include "XSFPlayer_NCSF.h"
+#include "XSFConfig_NCSF.h"
 #include "XSFCommon.h"
 #include "SSEQPlayer/SDAT.h"
 #include "SSEQPlayer/Player.h"
 
 const char *XSFPlayer::WinampDescription = "NCSF Decoder";
 const char *XSFPlayer::WinampExts = "ncsf;minincsf\0DS Nitro Composer Sound Format files (*.ncsf;*.minincsf)\0";
+
+extern XSFConfig *xSFConfig;
 
 XSFPlayer *XSFPlayer::Create(const std::string &fn)
 {
@@ -115,10 +118,52 @@
 }
 #endif
 
+#ifdef _DEBUG
+static HANDLE soundViewThreadHandle = INVALID_HANDLE_VALUE;
+static bool killSoundViewThread;
+
+static DWORD WINAPI soundViewThread(void *b)
+{
+	auto xSFConfig_NCSF = static_cast<XSFConfig_NCSF *>(xSFConfig);
+	xSFConfig_NCSF->CallSoundView(static_cast<XSFPlayer_NCSF *>(b), xSFConfig->GetHInstance(), nullptr);
+	MSG msg;
+	while (!killSoundViewThread)
+	{
+		xSFConfig_NCSF->RefreshSoundView();
+		if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE))
+		{
+			TranslateMessage(&msg);
+			DispatchMessageW(&msg);
+		}
+	}
+	xSFConfig_NCSF->CloseSoundView();
+	return 0;
+}
+#endif
+
+XSFPlayer_NCSF::~XSFPlayer_NCSF()
+{
+#ifdef _DEBUG
+	killSoundViewThread = true;
+	if (WaitForSingleObject(soundViewThreadHandle, 2000) == WAIT_TIMEOUT)
+	{
+		TerminateThread(soundViewThreadHandle, 0);
+		static_cast<XSFConfig_NCSF *>(xSFConfig)->CloseSoundView();
+	}
+	CloseHandle(soundViewThreadHandle);
+	soundViewThreadHandle = INVALID_HANDLE_VALUE;
+#endif
+}
+
 bool XSFPlayer_NCSF::Load()
 {
 	if (!this->LoadNCSF())
 		return false;
+
+#ifdef _DEBUG
+	killSoundViewThread = false;
+	soundViewThreadHandle = CreateThread(nullptr, 0, soundViewThread, this, 0, nullptr);
+#endif
 
 	PseudoFile file;
 	file.data = &this->sdatData;
@@ -212,3 +257,10 @@
 	this->mutes = newMutes;
 }
 
+#ifdef _DEBUG
+const Channel &XSFPlayer_NCSF::GetChannel(size_t chanNum) const
+{
+	return this->player.channels[chanNum];
+}
+#endif
+

--- a/src/in_ncsf/XSFPlayer_NCSF.h
+++ b/src/in_ncsf/XSFPlayer_NCSF.h
@@ -1,7 +1,7 @@
 /*
  * xSF - NCSF Player
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-24
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  *
@@ -36,11 +36,15 @@
 #ifdef _WIN32
 	XSFPlayer_NCSF(const std::wstring &filename);
 #endif
+	~XSFPlayer_NCSF();
 	bool Load();
 	void GenerateSamples(std::vector<uint8_t> &buf, unsigned offset, unsigned samples);
 	void Terminate();
 
 	void SetInterpolation(unsigned interpolation);
 	void SetMutes(const std::bitset<16> &newMutes);
+#ifdef _DEBUG
+	const Channel &GetChannel(size_t chanNum) const;
+#endif
 };
 

 Binary files /dev/null and b/src/in_ncsf/in_ncsf.rc differ
--- a/src/in_ncsf/in_ncsf.vcxproj
+++ b/src/in_ncsf/in_ncsf.vcxproj
@@ -97,6 +97,7 @@
     <ClCompile Include="XSFPlayer_NCSF.cpp" />
   </ItemGroup>
   <ItemGroup>
+    <ClInclude Include="resource.h" />
     <ClInclude Include="SSEQPlayer\Channel.h" />
     <ClInclude Include="SSEQPlayer\common.h" />
     <ClInclude Include="SSEQPlayer\consts.h" />
@@ -112,6 +113,7 @@
     <ClInclude Include="SSEQPlayer\SWAV.h" />
     <ClInclude Include="SSEQPlayer\SYMBSection.h" />
     <ClInclude Include="SSEQPlayer\Track.h" />
+    <ClInclude Include="XSFConfig_NCSF.h" />
     <ClInclude Include="XSFPlayer_NCSF.h" />
   </ItemGroup>
   <ItemGroup>
@@ -119,6 +121,11 @@
       <Project>{9d6d2540-b3dc-462b-bbc0-5b7c32a4d581}</Project>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="in_ncsf.rc">
+      <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+    </ResourceCompile>
+  </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>

--- a/src/in_ncsf/in_ncsf.vcxproj.filters
+++ b/src/in_ncsf/in_ncsf.vcxproj.filters
@@ -113,8 +113,19 @@
     <ClInclude Include="SSEQPlayer\Track.h">
       <Filter>Header Files\SSEQPlayer</Filter>
     </ClInclude>
+    <ClInclude Include="resource.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
+    <ClInclude Include="XSFConfig_NCSF.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="XSFPlayer_NCSF.h">
       <Filter>Header Files</Filter>
     </ClInclude>
   </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="in_ncsf.rc">
+      <Filter>Resource Files</Filter>
+    </ResourceCompile>
+  </ItemGroup>
 </Project>

 Binary files /dev/null and b/src/in_ncsf/resource.h differ
--- a/src/in_xsf_framework/XSFConfig.cpp
+++ b/src/in_xsf_framework/XSFConfig.cpp
@@ -1,7 +1,7 @@
 /*
  * xSF - Core configuration handler
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-24
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  */
@@ -321,12 +321,12 @@
 
 void XSFConfig::CallConfigDialog(HINSTANCE hInstance, HWND hwndParent)
 {
-	DialogBoxIndirectParam(hInstance, this->configDialog.GenerateTemplate(), hwndParent, XSFConfig::ConfigDialogProcStatic, reinterpret_cast<LPARAM>(this));
+	DialogBoxIndirectParamW(hInstance, this->configDialog.GenerateTemplate(), hwndParent, XSFConfig::ConfigDialogProcStatic, reinterpret_cast<LPARAM>(this));
 }
 
 void XSFConfig::CallInfoDialog(HINSTANCE hInstance, HWND hwndParent)
 {
-	DialogBoxIndirectParam(hInstance, this->infoDialog.GenerateTemplate(), hwndParent, XSFConfig::InfoDialogProcStatic, reinterpret_cast<LPARAM>(this));
+	DialogBoxIndirectParamW(hInstance, this->infoDialog.GenerateTemplate(), hwndParent, XSFConfig::InfoDialogProcStatic, reinterpret_cast<LPARAM>(this));
 }
 
 void XSFConfig::ResetConfigDefaults(HWND hwndDlg)
@@ -370,6 +370,16 @@
 	this->CopySpecificConfigToMemory(xSFPlayer, preLoad);
 }
 
+void XSFConfig::SetHInstance(HINSTANCE hInstance)
+{
+	this->configIO->SetHInstance(hInstance);
+}
+
+HINSTANCE XSFConfig::GetHInstance() const
+{
+	return this->configIO->GetHInstance();
+}
+
 bool XSFConfig::GetPlayInfinitely() const
 {
 	return this->playInfinitely;

--- a/src/in_xsf_framework/XSFConfig.h
+++ b/src/in_xsf_framework/XSFConfig.h
@@ -1,7 +1,7 @@
 /*
  * xSF - Core configuration handler
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-24
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  */
@@ -27,9 +27,11 @@
 	virtual void SetValueString(const std::string &name, const std::string &value) = 0;
 	template<typename T> void SetValue(const std::string &name, const T &value) { this->SetValueString(name, stringify(value)); }
 	void SetValue(const std::string &name, const std::string &value) { this->SetValueString(name, value); }
-	virtual std::string GetValueString(const std::string &name, const std::string &defaultValue) = 0;
-	template<typename T> T GetValue(const std::string &name, const T &defaultValue) { return convertTo<T>(this->GetValueString(name, stringify(defaultValue))); }
-	std::string GetValue(const std::string &name, const std::string &defaultValue) { return this->GetValueString(name, defaultValue); }
+	virtual std::string GetValueString(const std::string &name, const std::string &defaultValue) const = 0;
+	template<typename T> T GetValue(const std::string &name, const T &defaultValue) const { return convertTo<T>(this->GetValueString(name, stringify(defaultValue))); }
+	std::string GetValue(const std::string &name, const std::string &defaultValue) const { return this->GetValueString(name, defaultValue); }
+	virtual void SetHInstance(HINSTANCE) { }
+	virtual HINSTANCE GetHInstance() const { return nullptr; }
 };
 
 class XSFConfig
@@ -81,6 +83,8 @@
 	void ResetConfigDefaults(HWND hwndDlg);
 	void SaveConfigDialog(HWND hwndDlg);
 	void CopyConfigToMemory(XSFPlayer *xSFPlayer, bool preLoad);
+	void SetHInstance(HINSTANCE hInstance);
+	HINSTANCE GetHInstance() const;
 
 	virtual void About(HWND parent) = 0;
 

--- a/src/in_xsf_framework/XSFConfig_Winamp.cpp
+++ b/src/in_xsf_framework/XSFConfig_Winamp.cpp
@@ -1,7 +1,7 @@
 /*
  * xSF - Winamp-specification configuration handler
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-24
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  */
@@ -18,11 +18,14 @@
 protected:
 	friend class XSFConfigIO;
 	std::wstring iniFilename;
+	HINSTANCE hInst;
 
 	XSFConfigIO_Winamp();
 public:
 	void SetValueString(const std::string &name, const std::string &value);
-	std::string GetValueString(const std::string &name, const std::string &defaultValue);
+	std::string GetValueString(const std::string &name, const std::string &defaultValue) const;
+	void SetHInstance(HINSTANCE hInstance);
+	HINSTANCE GetHInstance() const;
 };
 
 XSFConfigIO *XSFConfigIO::Create()
@@ -57,7 +60,7 @@
 	WritePrivateProfileStringW(ConvertFuncs::StringToWString(XSFConfig::commonName).c_str(), ConvertFuncs::StringToWString(name).c_str(), ConvertFuncs::StringToWString(value).c_str(), this->iniFilename.c_str());
 }
 
-std::string XSFConfigIO_Winamp::GetValueString(const std::string &name, const std::string &defaultValue)
+std::string XSFConfigIO_Winamp::GetValueString(const std::string &name, const std::string &defaultValue) const
 {
 	auto value = std::vector<wchar_t>(MAX_PATH / 2);
 
@@ -74,3 +77,13 @@
 	return ConvertFuncs::WStringToString(std::wstring(value.begin(), value.begin() + result));
 }
 
+void XSFConfigIO_Winamp::SetHInstance(HINSTANCE hInstance)
+{
+	this->hInst = hInstance;
+}
+
+HINSTANCE XSFConfigIO_Winamp::GetHInstance() const
+{
+	return this->hInst;
+}
+

--- a/src/in_xsf_framework/in_xsf.cpp
+++ b/src/in_xsf_framework/in_xsf.cpp
@@ -1,7 +1,7 @@
 /*
  * xSF - Winamp plugin
  * By Naram Qashat (CyberBotX) [cyberbotx@cyberbotx.com]
- * Last modification on 2014-09-25
+ * Last modification on 2014-10-05
  *
  * Partially based on the vio*sf framework
  */
@@ -93,6 +93,7 @@
 	xSFConfig = XSFConfig::Create();
 	xSFConfig->LoadConfig();
 	xSFConfig->GenerateDialogs();
+	xSFConfig->SetHInstance(inMod.hDllInstance);
 }
 
 void quit()