Skip to content

Commit

Permalink
Command line configuration overrides
Browse files Browse the repository at this point in the history
Fix WAV export having variable length, allow multiple instances
  • Loading branch information
bbbradsmith committed Jun 23, 2023
1 parent be109f4 commit d0d8d70
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 45 deletions.
30 changes: 27 additions & 3 deletions distribute/nsfplay.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,30 @@ Command line arguments
======================

NSFPlay [filename]
> Open and begin playing NSF file.
Open and begin playing NSF file.
> [filename] NSF or NSFE input file

NFSPlay [nsf_filename] [wav_filename] [track] [milliseconds]
> Create WAV file from command line (cuts off after milliseconds are reached, or loop detection).
NFSPlay [nsf_filename] [wav_filename] (track) (milliseconds) >error.log
Create WAV file from command line
> [nsf_filename] NSF or NSFE input file
> [wav_filename] WAV output file
> (track) - optional, 1 if omitted
> (milliseconds) - optional, sets PLAY_TIME

Because it is a GUI application, errors cannot be reported to the command line
console, but you can redirect errors to a log file with >.
(>error.log in the example)

Optionally any value from the configuration file can be temporarily set with
an argument beginning -. If this is done, the configuration will not be
automatically saved on close, but can still be saved if you manually save
the new configuration from the menu.

NSFPlay -AUTO_DETECT=0
> Disables automatic track length detection.

NSFPlay -REGION=4
> Force PAL region.


Configuration files
Expand Down Expand Up @@ -171,6 +191,10 @@ NSFPlay 2.6 - (Unofficial)
- YMF281B plgDavid patch set option. (Gumball2415m plgDavid)
- Resizable info window.
- Right click info button to open the keyboard track-info window.
- Fix random variation in command line WAV export times.
- Allow multiple instances of command line WAV export.
- Add redirectable diagnostic log to stdout for command line usage. (Use > to send it to a file.)
- Allow command-line configuration overrides.

NSFPlay 2.5 - 10/25/2022
- APU frequency dividers now count down (more accurate pitch change timing).
Expand Down
1 change: 1 addition & 0 deletions libemuwa2/emu_outdisk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ int EmuOutDisk::Open(int rate, int nch, int bps, int buflen, int prebuf) {
m_file = mmioOpenW(wfile, NULL, MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
if (m_file == NULL)
{
printf("Failed to open WAV: %s\n",m_filename);
return -1;
}

Expand Down
4 changes: 2 additions & 2 deletions libemuwa2/emu_winamp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ EmuWinamp::EmuWinamp(char *dll_name) {
m_in_mod->hMainWindow = CreateWindow(
"WinampEmu","Winamp Emulation",WS_POPUP,0,0,32,32,NULL,NULL,GetModuleHandle(NULL),this);
SetProp(m_in_mod->hMainWindow,"WinampEmu",(HANDLE)this);
printf("hMainWindow :%p\n",m_in_mod->hMainWindow);
//printf("hMainWindow :%p\n",m_in_mod->hMainWindow);
m_in_mod->hDllInstance = m_dll;

m_plugin_direct = (PLUGIN_DIRECT)GetProcAddress(m_dll, "pluginDirect");
Expand Down Expand Up @@ -158,7 +158,7 @@ int EmuWinamp::Play(char *fn) {

int retcode = m_in_mod->Play(m_fn);

m_wo[0] = 0; //
m_wo[0] = 0;

if(!retcode)
m_playing = true;
Expand Down
84 changes: 55 additions & 29 deletions nsfplay/nsfplay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,67 +45,93 @@ CnsfplayApp::CnsfplayApp() :

CnsfplayApp theApp;


BOOL CnsfplayApp::InitInstance()
{
InitCommonControls();

InitCommonControls();
CWinApp::InitInstance();

int wargc;
wchar_t** wargv = CommandLineToArgvW(GetCommandLineW(),&wargc);

CMutex mutex(FALSE, m_pszExeName);
if( mutex.Lock(0) == TRUE ) {
// find positional arguments
const int MAX_PARGS = 4;
int pargc = 0;
wchar_t* pargv[4];
for (int i=1; i<wargc; ++i) {
if(wargv[i][0] != L'-') {
if (pargc < MAX_PARGS)
pargv[pargc] = wargv[i];
++pargc;
}
}
if (pargc > MAX_PARGS)
printf("Too many positional arguments. Maximum %d, got: %d\n",MAX_PARGS,pargc);

if (pargc >= 2) { // WAV export
CnsfplayDlg dlg;
m_pDlg = &dlg;
m_pMainWnd = &dlg;
m_hAccel = ::LoadAccelerators(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_ACCELERATOR));

if(5==wargc) // command line wave out
{
char nsf_file[2048]; file_utf8(wargv[1],nsf_file,2048);
char wav_file[2048]; file_utf8(wargv[2],wav_file,2048);
char track[32]; file_utf8(wargv[3],track,32);
char time[32]; file_utf8(wargv[4],time,32);
dlg.WriteSingleWave(nsf_file, wav_file, track, time);
dlg.m_cancel_open = true;
char nsf_file[2048]; file_utf8(pargv[0],nsf_file,2048);
char wav_file[2048]; file_utf8(pargv[1],wav_file,2048);
int track = -1;
int time = -1;
if (pargc >= 3) {
char temp[32]; file_utf8(pargv[2],temp,32);
track = ::atoi(temp);
}
if (pargc >= 4) {
char temp[32]; file_utf8(pargv[3],temp,32);
time = ::atoi(temp);
}
else if(2<=wargc)
{
dlg.ParseArgs(wargc,wargv);
dlg.WriteSingleWave(nsf_file, wav_file, track, time);
dlg.m_cancel_open = true;
dlg.DoModal();
return FALSE;
}

// GUI open
CMutex mutex(FALSE, m_pszExeName); // see if program is already open
if( mutex.Lock(0) == TRUE ) { // new instance

CnsfplayDlg dlg;
m_pDlg = &dlg;
m_pMainWnd = &dlg;
m_hAccel = ::LoadAccelerators(AfxGetInstanceHandle(),MAKEINTRESOURCE(IDR_ACCELERATOR));

if(pargc > 0) {
const int INIT_FILE_MAX = 2048;
char init_file[INIT_FILE_MAX];
file_utf8(wargv[1],init_file,INIT_FILE_MAX);
file_utf8(pargv[0],init_file,INIT_FILE_MAX);
dlg.m_init_file = CString(init_file);
}

INT_PTR nResponse = dlg.DoModal();
dlg.ParseArgs(wargc,wargv);
dlg.DoModal();
mutex.Unlock();

} else {

// NOTE this string must match CAPTION in IDD_NSFPLAY_DIALOG in nsfplay.rc
CWnd* pWnd = CWnd::FindWindow(NULL, NSFPLAY_TITLE);
if( pWnd ) {
pWnd->SetForegroundWindow();

if(2<=wargc) {
size_t size = wcslen(wargv[1])+1;
} else { // otherwise send the new file to already open NSFPlay
// NOTE this string must match CAPTION in IDD_NSFPLAY_DIALOG in nsfplay.rc
CWnd* pWnd = CWnd::FindWindow(NULL, NSFPLAY_TITLE);
if( pWnd ) {
pWnd->SetForegroundWindow();
if(pargc > 0) {
size_t size = wcslen(pargv[0])+1;
HANDLE hMem = GlobalAlloc(GMEM_ZEROINIT,sizeof(DROPFILES)+(size*sizeof(wchar_t))+1);
DROPFILES *DropFiles = (DROPFILES *)GlobalLock(hMem);
DropFiles->pFiles=sizeof(DROPFILES);
DropFiles->pt.x=10;
DropFiles->pt.y=10;
DropFiles->fNC=1;
DropFiles->fWide=1;
wcscpy((wchar_t*)(((char*)DropFiles)+sizeof(DROPFILES)),wargv[1]);
wcscpy((wchar_t*)(((char*)DropFiles)+sizeof(DROPFILES)),pargv[0]);
GlobalUnlock(hMem);
pWnd->PostMessage(WM_DROPFILES,(WPARAM)hMem,0);
}
}
}
return FALSE;
return FALSE;
}

BOOL CnsfplayApp::ProcessMessageFilter(int code, LPMSG lpMsg)
Expand Down
62 changes: 52 additions & 10 deletions nsfplay/nsfplayDlg.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,11 +480,39 @@ void CnsfplayDlg::OnBnClickedOpen()
}
}

int CnsfplayDlg::ParseArgs(int wargc, const wchar_t* const * wargv)
{
if (!in_yansf) return 0; // can't parse arguments without direct plugin access
int result = 0;
for (int i=1; i<wargc; ++i)
{
if (wargv[i][0] != L'-') continue;
char arg[1024];
file_utf8(wargv[i],arg,1024);
char* split = strstr(arg,"=");
if (split == NULL)
{
printf("Argument = expected but not found: %s\n",arg);
++result;
continue;
}
std::string val(arg+1,split-(arg+1));
if (!in_yansf->npm->cf->HasValue(val))
{
printf("Unknown configuration argument: %s\n",val.c_str());
++result;
continue;
}
(*(in_yansf->npm->cf))[val] = split+1;
in_yansf->npm->no_save_config = true; // changes are temporary unless explicitly saved by user
}
return result;
}

// command line wave output
int CnsfplayDlg::WriteSingleWave(char* nsf_file, char* wave_file, char* track, char* ms)
int CnsfplayDlg::WriteSingleWave(char* nsf_file, char* wave_file, int track, int ms)
{
int it = ::atoi(track);
int ims = ::atoi(ms);
if (track < 1) track = 1;

int last_pos = 0;
int hang_count = 0;
Expand All @@ -494,28 +522,41 @@ int CnsfplayDlg::WriteSingleWave(char* nsf_file, char* wave_file, char* track, c
if (in_yansf) // direct connection allows to set play time exactly
{
in_yansf->npm->no_save_config = true;
(*(in_yansf->npm->cf))["PLAY_TIME"] = ms;
if (ms >= 0) (*(in_yansf->npm->cf))["PLAY_TIME"] = ms;
else ms = (*(in_yansf->npm->cf))["PLAY_TIME"];
(*(in_yansf->npm->cf))["AUTO_DETECT"] = 0;
ims += (*(in_yansf->npm->cf))["FADE_TIME"];
ims += 10000; // buffer against early cutoff
ms += (*(in_yansf->npm->cf))["FADE_TIME"];
ms += 10000; // buffer against early cutoff
}
else
{
if (ms < 0) ms = 3 * 60000; // 3 minute default?
sleep_time = 5; // no plugin: kludge a cutoff length with 5ms accuracy
}
else sleep_time = 5; // no plugin: kludge a cutoff length with 5ms accuracy

m_emu->Play(nsf_file);
if (m_emu->Play(nsf_file))
{
printf("Error opening file: %s\n",nsf_file);
if (in_yansf) printf("Error: %s\n",in_yansf->npm->sdat->LoadError());
m_emu->Stop();
return 1;
}
printf("Opened: %s\n",nsf_file);
m_emu->Stop();
for (int t=1; t < it; ++t)
for (int t=1; t < track; ++t)
{
m_emu->Next();
m_emu->Stop();
}
m_emu->Waveout(wave_file);
printf("Wave Output: %s\n",wave_file);
m_emu->Play(NULL);
do
{
::Sleep(sleep_time);
if (!m_emu->IsPlaying()) break;
int iot = m_emu->GetOutputTime();
if (iot >= ims) break;
if (iot >= ms) break;

if (last_pos != iot)
{
Expand All @@ -530,6 +571,7 @@ int CnsfplayDlg::WriteSingleWave(char* nsf_file, char* wave_file, char* track, c
}
while (true);
m_emu->Stop();
printf("Play time: %d ms\n",m_emu->GetOutputTime());

return 0;
}
3 changes: 2 additions & 1 deletion nsfplay/nsfplayDlg.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,6 @@ class CnsfplayDlg : public CDialog
afx_msg void OnBnClickedWaveout();
afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);

int WriteSingleWave(char* nsf_file, char* wave_file, char* track, char* ms);
int ParseArgs(int wargc, const wchar_t* const * wargv); // apply config override arguments starting with -
int WriteSingleWave(char* nsf_file, char* wave_file, int track, int ms);
};
5 changes: 5 additions & 0 deletions vcm/vcm.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ namespace vcm
MutexGuard mg_(this);
return data[id];
}
inline bool HasValue( const std::string id )
{
MutexGuard mg_(this);
return data.find( id ) != data.end();
}
inline void Clear()
{
MutexGuard mg_(this);
Expand Down

0 comments on commit d0d8d70

Please sign in to comment.