/****************************************************************************

	PROGRAM: Winplay

	PURPOSE: Windows Polyphonic Music Player 1.0

		Copyright (C) Sergey Ryzhkov
				  Moscow  1991.

		Program is placed in the public domain by.
		Permission is hereby granted to copy, modify,
		and otherwise use this program except for profit.

		This is the windows implementation of some algorithms
		designed by A.Bogatyrev, Mike Talvola and Steve Muenter
		(see later).

****************************************************************************/

#include <windows.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include "winplay.h"

HANDLE hInst;

HWND hEditWnd;                                  /* handle to edit window */
HWND hwnd;                                      /* handle to main window */

char FileName[128];
char PathName[128];
char OpenName[128];
char DefPath[128];
char DefSpec[13] = "*.pol";
char DefExt[] = ".pol";
char str[255];

char DefaultName[128];

/****************************************************************************

    FUNCTION: WinMain(HANDLE, HANDLE, LPSTR, int)

    PURPOSE: calls initialization function, processes message loop

****************************************************************************/

int PASCAL WinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
HANDLE hInstance;
HANDLE hPrevInstance;
LPSTR lpCmdLine;
int nCmdShow;
{
    MSG msg;
	int i;

	if (!hPrevInstance)
        if (!InitApplication(hInstance))
            return (FALSE);

	for ( i = 0 ;
		  (DefaultName[i] = lpCmdLine[i]) && i < sizeof(DefaultName)-1 ;
		  i ++ );

	if (!InitInstance(hInstance, nCmdShow))
        return (FALSE);

	while (GetMessage(&msg, NULL, NULL, NULL)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
    }
    return (msg.wParam);
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE: Initializes window data and registers window class

****************************************************************************/

BOOL InitApplication(hInstance)
HANDLE hInstance;
{
    WNDCLASS  wc;

    wc.style = NULL;
    wc.lpfnWndProc = MainWndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(hInstance, "WinPlayIcon");
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName = "WinPlayMenu";
	wc.lpszClassName = "WinPlayWClass";

    return (RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:  Saves instance handle and creates main window

****************************************************************************/

BOOL InitInstance(hInstance, nCmdShow)
    HANDLE          hInstance;
    int             nCmdShow;
{
    hInst = hInstance;

    hwnd = CreateWindow(
		"WinPlayWClass",
		"Windows Music Play",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
		200,
		50,
        NULL,
        NULL,
        hInstance,
        NULL
    );

    if (!hwnd)
        return (FALSE);

	if (DefaultName[0] == 0) {
		ShowWindow(hwnd, nCmdShow);
		UpdateWindow(hwnd);
	}
    return (TRUE);

}

/****************************************************************************

    FUNCTION: MainWndProc(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages

    MESSAGES:

        WM_COMMAND    - application menu (About dialog box)
        WM_DESTROY    - destroy window

****************************************************************************/

long FAR PASCAL MainWndProc(hWnd, message, wParam, lParam)
HWND hWnd;
unsigned message;
WORD wParam;
LONG lParam;
{
	FARPROC lpProcAbout, lpOpenDlg;

	int Return;

	switch (message) {
		case WM_CREATE:
			if ( DefaultName[0] ) {
				play (DefaultName);
				PostQuitMessage(0);
			} else
				return (DefWindowProc(hWnd, message, wParam, lParam));
			break;
		case WM_COMMAND:
            switch (wParam) {
                case IDM_ABOUT:
                    lpProcAbout = MakeProcInstance(About, hInst);
                    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
                    FreeProcInstance(lpProcAbout);
                    break;

				case IDM_PLAY:
                    /* Call OpenDlg() to get the filename */

                    lpOpenDlg = MakeProcInstance((FARPROC) OpenDlg, hInst);
                    Return = DialogBox(hInst, "Open", hWnd, lpOpenDlg);
                    FreeProcInstance(lpOpenDlg);

					if (Return)
					   /*
                       MessageBox (
                             GetFocus(),
							 OpenName,
							 "Play File",
							 MB_ICONASTERISK | MB_OK);
					   */
					   play (OpenName);
                    break;
			}
            break;

        case WM_SETFOCUS:
            SetFocus (hEditWnd);
            break;

        case WM_SIZE:
            MoveWindow(hEditWnd, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
            break;

        case WM_DESTROY:
            PostQuitMessage(0);
            break;

        default:
            return (DefWindowProc(hWnd, message, wParam, lParam));
    }
    return (NULL);
}

/****************************************************************************

    FUNCTION: OpenDlg(HWND, unsigned, WORD, LONG)

    PURPOSE: Let user select a file, and return.  Open code not provided.

****************************************************************************/

HANDLE FAR PASCAL OpenDlg(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    HANDLE hFile=1;     /* Temp value for return */

    switch (message) {
        case WM_COMMAND:
            switch (wParam) {

                case IDC_LISTBOX:
                    switch (HIWORD(lParam)) {

                        case LBN_SELCHANGE:
                            /* If item is a directory name, append "*.*" */
                            if (DlgDirSelect(hDlg, str, IDC_LISTBOX))
                                strcat(str, DefSpec);

                            SetDlgItemText(hDlg, IDC_EDIT, str);
                            SendDlgItemMessage(hDlg,
                                IDC_EDIT,
                                EM_SETSEL,
                                NULL,
                                MAKELONG(0, 0x7fff));
                            break;

                        case LBN_DBLCLK:
                            goto openfile;
                    }
                    return (TRUE);

                case IDOK:
openfile:
                    GetDlgItemText(hDlg, IDC_EDIT, OpenName, 128);
                    if (strchr(OpenName, '*') || strchr(OpenName, '?')) {
                        SeparateFile(hDlg, (LPSTR) str, (LPSTR) DefSpec,
                            (LPSTR) OpenName);
                        if (str[0])
                            strcpy(DefPath, str);
                        ChangeDefExt(DefExt, DefSpec);
                        UpdateListBox(hDlg);
                        return (TRUE);
                    }

                    if (!OpenName[0]) {
                        MessageBox(hDlg, "No filename specified.",
                            NULL, MB_OK | MB_ICONHAND);
                        return (TRUE);
                    }

                    AddExt(OpenName, DefExt);

                    /* The routine to open the file would go here, and the */
                    /* file handle would be returned instead of NULL.           */
                    EndDialog(hDlg, hFile);
                    return (TRUE);

                case IDCANCEL:
                    EndDialog(hDlg, NULL);
                    return (FALSE);
            }
            break;

        case WM_INITDIALOG:                        /* message: initialize    */
            UpdateListBox(hDlg);
            SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
            SendDlgItemMessage(hDlg,               /* dialog handle      */
                IDC_EDIT,                          /* where to send message  */
                EM_SETSEL,                         /* select characters      */
                NULL,                              /* additional information */
                MAKELONG(0, 0x7fff));              /* entire contents      */
            SetFocus(GetDlgItem(hDlg, IDC_EDIT));
            return (FALSE); /* Indicates the focus is set to a control */
    }
    return FALSE;
}

/****************************************************************************

    FUNCTION: UpdateListBox(HWND);

    PURPOSE: Update the list box of OpenDlg

****************************************************************************/

void UpdateListBox(hDlg)
HWND hDlg;
{
    strcpy(str, DefPath);
    strcat(str, DefSpec);
    DlgDirList(hDlg, str, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* To ensure that the listing is made for a subdir. of
     * current drive dir...
     */
    if (!strchr (DefPath, ':'))
	DlgDirList(hDlg, DefSpec, IDC_LISTBOX, IDC_PATH, 0x4010);

    /* Remove the '..' character from path if it exists, since this
     * will make DlgDirList move us up an additional level in the tree
     * when UpdateListBox() is called again.
     */
    if (strstr (DefPath, ".."))
	DefPath[0] = '\0';

    SetDlgItemText(hDlg, IDC_EDIT, DefSpec);
}

/****************************************************************************

    FUNCTION: ChangeDefExt(PSTR, PSTR);

    PURPOSE: Change the default extension

****************************************************************************/

void ChangeDefExt(Ext, Name)
PSTR Ext, Name;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr)
        if (!strchr(pTptr, '*') && !strchr(pTptr, '?'))
            strcpy(Ext, pTptr);
}

/****************************************************************************

    FUNCTION: SeparateFile(HWND, LPSTR, LPSTR, LPSTR)

    PURPOSE: Separate filename and pathname

****************************************************************************/

void SeparateFile(hDlg, lpDestPath, lpDestFileName, lpSrcFileName)
HWND hDlg;
LPSTR lpDestPath, lpDestFileName, lpSrcFileName;
{
    LPSTR lpTmp;
    char  cTmp;

    lpTmp = lpSrcFileName + (long) lstrlen(lpSrcFileName);
    while (*lpTmp != ':' && *lpTmp != '\\' && lpTmp > lpSrcFileName)
        lpTmp = AnsiPrev(lpSrcFileName, lpTmp);
    if (*lpTmp != ':' && *lpTmp != '\\') {
        lstrcpy(lpDestFileName, lpSrcFileName);
        lpDestPath[0] = 0;
        return;
    }
    lstrcpy(lpDestFileName, lpTmp + 1);
    cTmp = *(lpTmp + 1);
    lstrcpy(lpDestPath, lpSrcFileName);
     *(lpTmp + 1) = cTmp;
    lpDestPath[(lpTmp - lpSrcFileName) + 1] = 0;
}

/****************************************************************************

    FUNCTION: AddExt(PSTR, PSTR);

    PURPOSE: Add default extension

/***************************************************************************/

void AddExt(Name, Ext)
PSTR Name, Ext;
{
    PSTR pTptr;

    pTptr = Name;
    while (*pTptr && *pTptr != '.')
        pTptr++;
    if (*pTptr != '.')
        strcat(Name, Ext);
}

/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES:

        WM_INITDIALOG - initialize dialog box
        WM_COMMAND    - Input received

****************************************************************************/

BOOL FAR PASCAL About(hDlg, message, wParam, lParam)
HWND hDlg;
unsigned message;
WORD wParam;
LONG lParam;
{
    switch (message) {
        case WM_INITDIALOG:
            return (TRUE);

        case WM_COMMAND:
	    if (wParam == IDOK
                || wParam == IDCANCEL) {
                EndDialog(hDlg, TRUE);
                return (TRUE);
            }
            break;
    }
    return (FALSE);
}

/****************************************************************************
 *
 *      tri()    - play 3-voice music
 *
 *      Copyright (C) A.Bogatyrev (abs)
 *                 Moscow  1990.
 *      Program is placed in the public domain by: ABS.
 *		Permission is hereby granted to copy, modify,
 *	    and otherwise use this program except for profit.
 *
****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <conio.h>

# define SPEAKER_PORT   0x61
# define SAY		    0x48
# define SHUTUP        (0x48|0x2)
# define SPEAK( c )     outp(SPEAKER_PORT, ( 0x8000 & c ) ? SAY : SHUTUP)
/* short must has 16 bits */

void tri ( tune )
	short *tune;
{
	short Tempo;
	register short TempoCount;
	register short Duration;

	short Value, sValue;
	register short Count1, Count2, Count3; /* voice period count */
	register short Voice1, Voice2, Voice3; /* voice period data */

	Tempo = TempoCount = 0x1fff;
	Voice1 = Voice2 = Voice3 = 0;
	Count1 = Count2 = Count3 = 0;

	for( ;; ){
		/*
		if ( kbhit() && getch() == '\033' ) {
			enable ();
			return;
		}
		*/
		sValue = *tune++;
		Value = sValue & 0x1FFF;

		switch( sValue & 0xE000 ){
		case 0:
			_enable();
			return;         /* EOV */

		case 0x4000:
			Tempo = TempoCount = Value;
			continue;

		case 0x6000:
			continue;          /* nop */

		case 0x8000:
			Voice1 = Value;
			continue;

		case 0xA000:
			Voice2 = Value;
			continue;

		case 0xC000:
		case 0xE000:
			Voice3 = Value;
			continue;

		case 0x2000:
			Duration = Value;
			_disable();		/* cli */
			for( ;; ){
				TempoCount --;
				if( TempoCount <= 0 ){
					TempoCount = Tempo;
					Duration --;
					if( Duration <= 0 ){
						_enable();	/* sti */
						break;		/* for(;;) */
					}
				}
		/* play: */
				Count3 += Voice3;
				SPEAK( Count3 );

				Count2 += Voice2;
				SPEAK( Count2 );

				Count1 += Voice1;
				SPEAK( Count1 );
			}
			continue;
		} /* endsw */
	} /* endfor */
}

/****************************************************************************

		  Polyphonic Music Player v1.2
	Written in DeSmet C and placed in the public domain
		by:  Mike Talvola
		     Agoura Hills, CA

	Permission is hereby granted to copy, modify, and
	otherwise use this program except for profit.


	Inspired by "Polyphonic Music on the IBM PC"
		 by:  Steve Muenter
***************************************************************************/


#define TRUE	1
#define FALSE   0

short tune[5000];

char name[80];
FILE *tfile;

void play (BYTE * szName)
{
	int tindex;
	static char b[128];

	strcpy(name, szName);
	if (strchr (name,'.') == NULL)
		strcat(name, ".POL");
	tfile = fopen(name, "r");
	if (tfile == 0) {
		sprintf(b, "Can't open song file '%s'", name);
		MessageBox (GetFocus(), (LPSTR)b, (LPSTR)NULL, MB_OK);
		return;
	}
	fgets(name, 80, tfile);
	tindex = 0;
	while (fscanf(tfile, "%d", &tune[tindex++]) != EOF);
	fclose(tfile);
	tri(&tune[1]);
}
