// operating system includes #include "windows.h" #include "winsock.h" #include "commctrl.h" // run-time library includes #include "stdio.h" #include "string.h" // helper library include #include "../../CDC/CDC.h" // application includes #include "resource.h" #include "cdtime.h" #include "remote.h" HANDLE g_hCom; // handle to the communication resource // mutexes HANDLE g_mxComm; // protects the comm resource, i.e. the CD changer HANDLE g_mxStatusInfo; // protects the status global variables int g_curaddrunit; typedef enum { PLAY, PAUSE, STOP, NODISC, RANDOM, PLAYDISC } STATUSCODES; typedef struct { unsigned short fFlags; CDTime cdtStart; CDTime cdtLength; } TRACKINFO; #define TIF_PLAYED #define NUMDISCS 99 typedef struct { unsigned long xID; unsigned short iFirst; unsigned short cTracks; CDTime cdtTotal; unsigned short fFlags; TRACKINFO * pTrackInfo; } DISCINFO; #define DIF_PRESENT 0x0001 struct UNITINFO { HANDLE mxPlaylist; // mutex which protects the playlist HGLOBAL hPlaylist; int cPlaylist; int iPlaylistCur; STATUSCODES status; DISCINFO di[NUMDISCS]; } g_ui[2]; // useful strings char g_szNull[] = ""; const char g_szClass[] = "NSMCDWND"; const char g_szError[] = "Error"; HWND g_hWnd; HINSTANCE g_hInst; // shared status information protected by g_mxStatusInfo CDTime g_cdtCurDisc; CDTime g_cdtCurTrack; int g_iCurDisc; int g_iCurTrack; int g_iCurIndex; BOOL g_fAutopauseMode; int g_nsmStatus; // user interface selected unit int g_unit = 0; RECT g_rStatus = { 10, 10, 250, 80 }; //#define TEST #define Assert(x) { if (!(x)) _asm int 3 } #define NSM_LOAD_DISC 1 #define NSM_STOP_DISC 2 #define NSM_RETURN_DISC 3 #define NSM_TRACK_POS 4 #define NSM_INDEX_POS 5 #define NSM_TIME_POS 6 #define NSM_AUTOPAUSE_ON 7 #define NSM_AUTOPAUSE_OFF 8 #define NSM_PAUSE_ON 9 #define NSM_PAUSE_OFF 10 #define NSM_FAST 11 #define NSM_JUMP 12 #define NSM_GET_CD_ID 13 #define NSM_GET_NSM_ID 14 #define NSM_GET_DISC_DATA 15 #define NSM_GET_STATUS 16 #define NSM_GET_TOC_DATA 17 #define NSM_GET_ELAPSED 18 #define NSM_GET_VERSION 19 #define NSM_EXT_FAST 30 #define NSM_TRACK_INDEX_POS 31 char *g_pszStatus[] = { " ","(none)", // 0 "LN","no CD in loader", // 1 "LE","loader error", // 2 "SN","stop, no TOC read", // 3 "SE","stop due to error", // 4 "SD","stop due to end of disc", // 5 "ST","stop due to command", // 6 "PL","play", // 7 "PF","play fast forward", // 8 "PR","play fast reverse", // 9 "PA","pause due to command", // 10 "PT","pause due to autopause", // 11 "PD","pause due to autopause, end of disc" }; // 12 typedef struct { unsigned short Disc; unsigned short Track; } PLAYLISTITEM; /////////////////////////////////////// HANDLE CreateInheritMutex(void) { SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; return CreateMutex(&sa,FALSE,NULL); } int LVGetSelected(HWND hList) { int cItem = SendMessage(hList,LVM_GETITEMCOUNT,0,0); int i; UINT stateMask = LVIS_SELECTED; for (i=0; i0 - nsm error code // //////////////////////////////////////////////////////////////////////////// int QnsmCommand(int iUnit,int cmd,char *pszArg=NULL,char *pszRet=NULL); int QnsmCommand( int iUnit, int cmd, char *pszArg, char *pszRet) { char buf[50]; int cbCmd; int retval; if (iUnit != g_curaddrunit) { nsmRawCommWrite(iUnit ? "\x81" : "\x80",1); g_curaddrunit = iUnit; } if (!pszArg) pszArg = g_szNull; cbCmd = strlen(pszArg)+2; sprintf(buf,"\002%.02d%.02d%s\003",cbCmd,cmd,pszArg); nsmRawCommWrite(buf,cbCmd+4); // Wait for the response and possibly store it { char ch; DWORD cbRead; int i=0; do { ReadFile(g_hCom,&ch,1,&cbRead,NULL); if (i==5) retval = (ch-'0') * 10; if (i==6) retval += (ch-'0'); if (pszRet) pszRet[i] = ch; i++; } while (imxPlaylist,INFINITE); pui->cPlaylist = 0; c = 0; if (pui->hPlaylist) ::GlobalFree(pui->hPlaylist); pui->hPlaylist = ::GlobalAlloc(GMEM_MOVEABLE,sizeof(PLAYLISTITEM)); for (i=0; idi[i]; if (di.fFlags & DIF_PRESENT) { pui->cPlaylist += di.cTracks; pui->hPlaylist = ::GlobalReAlloc(pui->hPlaylist,pui->cPlaylist * sizeof(PLAYLISTITEM),GMEM_MOVEABLE); ppl = (PLAYLISTITEM *)::GlobalLock(pui->hPlaylist); for (j=0; jhPlaylist); } } // Randomize it ppl = (PLAYLISTITEM *)::GlobalLock(pui->hPlaylist); for (i=0; icPlaylist; i++) { j = MulDiv(myrand(),pui->cPlaylist-1,0xFFFF); plTemp = ppl[i]; ppl[i] = ppl[j]; ppl[j] = plTemp; } ::GlobalUnlock(pui->hPlaylist); ::ReleaseMutex(pui->mxPlaylist); } BOOL PlayDiscTrack(int iUnit,unsigned short disc,unsigned short track,BOOL fAutopause=TRUE); BOOL PlayDiscTrack( int iUnit, unsigned short disc, unsigned short track, BOOL fAutopause) { char buf[10]; DISCINFO &di = g_ui[iUnit].di[disc-1]; if (disc==0 || disc > NUMDISCS || !(di.fFlags & DIF_PRESENT) || track < di.iFirst || track >= (di.iFirst + di.cTracks)) return FALSE; WaitForSingleObject(g_mxComm,INFINITE); sprintf(buf,"A%.2d",disc); QnsmCommand(iUnit,NSM_LOAD_DISC,buf); sprintf(buf,"A%.2d",track); QnsmCommand(iUnit,NSM_TRACK_POS,buf); QnsmCommand(iUnit,NSM_PAUSE_OFF); if (fAutopause) QnsmCommand(iUnit,NSM_AUTOPAUSE_ON); else QnsmCommand(iUnit,NSM_AUTOPAUSE_OFF); ReleaseMutex(g_mxComm); return TRUE; } void NextPlaylist(int iUnit) { PLAYLISTITEM *ppl; UNITINFO *pui = &g_ui[iUnit]; ::WaitForSingleObject(pui->mxPlaylist,INFINITE); ppl = (PLAYLISTITEM *)::GlobalLock(pui->hPlaylist); PlayDiscTrack(iUnit,ppl[pui->iPlaylistCur].Disc,ppl[pui->iPlaylistCur].Track); pui->iPlaylistCur++; if (pui->iPlaylistCur==pui->cPlaylist) { GenerateRndPlaylist(iUnit); pui->iPlaylistCur=0; } ::GlobalUnlock(pui->hPlaylist); ::ReleaseMutex(pui->mxPlaylist); } void GetArtistDisc(int disc,char *pszartist,char *pszdisc ) { HFILE hf; int i; char c; char buf[200]; char *pdisc; char *p; hf = _lopen("nsm.txt",OF_READ); if (hf == HFILE_ERROR) { *pszartist = 0; *pszdisc = 0; } else { for (i=0; istatus==NODISC) return; if (pui->status==STOP) nsmCommand(iUnit,NSM_TRACK_POS,"A01"); nsmCommand(iUnit,NSM_PAUSE_OFF); pui->status = PLAY; } void kPause(int iUnit) { UNITINFO *pui = &g_ui[iUnit]; if (pui->status==NODISC) return; nsmCommand(iUnit,NSM_PAUSE_ON); pui->status = PAUSE; } void kStop(int iUnit) { UNITINFO *pui = &g_ui[iUnit]; if (pui->status==NODISC) return; nsmCommand(iUnit,NSM_STOP_DISC); pui->status = STOP; } void kRandom(int iUnit) { UNITINFO *pui = &g_ui[iUnit]; GenerateRndPlaylist(iUnit); pui->status=RANDOM; pui->iPlaylistCur = 0; NextPlaylist(iUnit); } void InitSock(SOCKET); unsigned long ListenThread(SOCKET sk) { SOCKET sk_connect; SOCKADDR_IN sin; int i; if (sk==0) { sk = ::socket(AF_INET,SOCK_STREAM,0); if (sk==INVALID_SOCKET) return 0; sin.sin_family = AF_INET; sin.sin_port = htons(PORT_NUM); sin.sin_addr.s_addr = INADDR_ANY; i = sizeof(sin); if (::bind(sk,(struct sockaddr *)&sin,sizeof(sin))==SOCKET_ERROR) { char buf[80]; wsprintf(buf,"Error %d",WSAGetLastError()); ::MessageBox(g_hWnd,buf,"Error with bind()",MB_OK); return 0; } if (::listen(sk,SOMAXCONN)==SOCKET_ERROR) { char buf[80]; wsprintf(buf,"Error %d",WSAGetLastError()); ::MessageBox(g_hWnd,buf,"Error with listen()",MB_OK); return 0; } } i = sizeof(sin); sk_connect = ::accept(sk,(struct sockaddr *)&sin,&i); if (sk_connect==INVALID_SOCKET) { char buf[80]; wsprintf(buf,"Error %d",WSAGetLastError()); ::MessageBox(g_hWnd,buf,"Error with accept()",MB_OK); return 0; } // start another listener InitSock(sk); while (1) { int status; char buf[80]; status = ::recv(sk_connect,buf,sizeof(buf),0); if (status == SOCKET_ERROR) { int err = WSAGetLastError(); if (err==WSAECONNRESET) return 0; wsprintf(buf,"Error %d",err); ::MessageBox(g_hWnd,buf,"Error with recv()",MB_OK); return 0; } if (status) { REMOTECMD rc = *(REMOTECMD *)buf; void *parg = &((REMOTECMD *)buf)[1]; switch (rc) { case NextTrack: { reqDEFAULT *req = (reqDEFAULT *)parg; NextPlaylist(req->unit); } break; case Play: { reqDEFAULT *req = (reqDEFAULT *)parg; kPlay(req->unit); } break; case Pause: { reqDEFAULT *req = (reqDEFAULT *)parg; kPause(req->unit); } break; case Stop: { reqDEFAULT *req = (reqDEFAULT *)parg; kStop(req->unit); } break; case RestartTrack: PlayDiscTrack(g_unit,g_iCurDisc,g_iCurTrack); break; case PlayDisc: { reqPLAYDISC *req = (reqPLAYDISC *)parg; PlayDiscTrack(req->unit,req->disc,g_ui[req->unit].di[req->disc-1].iFirst,FALSE); g_ui[req->unit].status = PLAYDISC; } break; case PlayTrack: { reqPLAYTRACK *req = (reqPLAYTRACK *)parg; PlayDiscTrack(g_unit,req->disc,req->track); } break; case PlayRandom: { reqDEFAULT *req = (reqDEFAULT *)parg; kRandom(req->unit); } break; case GetDiscTrack: { reqPLAYTRACK rpt; rpt.disc = g_iCurDisc; rpt.track = g_iCurTrack; ::send(sk_connect,(char *)&rpt,sizeof(rpt),0); } break; case GetTrackInfo: { reqGETTRACKINFO rti; rti.disc = g_iCurDisc; rti.track = g_iCurTrack; rti.sztrack[0] = 0; GetArtistDisc(g_iCurDisc,rti.szartist,rti.szdisc); if (*rti.szartist == 0) lstrcpy(rti.szartist,"couldn't open nsm.txt"); ::send(sk_connect,(char *)&rti,sizeof(rti),0); } break; } } else { //::MessageBox(g_hWnd,"Connection broken","NSM Error",MB_OK); return 0; } } return 0; } void InitSock(SOCKET sk) { DWORD tid; HANDLE hThread; hThread = CreateThread(NULL,0,(unsigned long (__stdcall *)(void *))ListenThread,(LPVOID)sk,0,&tid); CloseHandle(hThread); } void FillTrackList(int iUnit,int iSel) { HWND hLVTracks = GetDlgItem(g_hWnd,IDC_LIST2); int j; DISCINFO &di = g_ui[iUnit].di[iSel]; LV_ITEM item; char buf[15]; ListView_DeleteAllItems(hLVTracks); if (iSel>=0 && iSelcode == NM_CLICK) { FillTrackList(g_unit,LVGetSelected(pnmhdr->hwndFrom)); } } } break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, uParam, lParam); } return 0; } //////////////////////////////////////////////////////////////////////////// // // // //////////////////////////////////////////////////////////////////////////// int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { g_hInst = hInstance; mysrand(); InitCommonControls(); // Startup Windows sockets subsystem { WSADATA wsad; ::WSAStartup(MAKEWORD(1,1),&wsad); } if (!InitComm()) { MessageBox(NULL,"Port in use.",g_szError,MB_OK); return 0; } // Mutex synchronizes access to the status global variables g_mxStatusInfo = CreateInheritMutex(); if (!g_mxStatusInfo) return 0; for (int i=0; i<2; i++) { g_ui[i].mxPlaylist = CreateInheritMutex(); if (!g_ui[i].mxPlaylist) return 0; } nsmRawCommWrite("\x80",1); g_curaddrunit = 0; #ifndef TEST nsmCommand(0,NSM_STOP_DISC); g_ui[0].status = STOP; #endif // Start a socket listener InitSock(0); WNDCLASS wc; // Fill in window class structure with parameters that describe the // main window. wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance,MAKEINTRESOURCE(IDI_ICON1)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wc.lpszMenuName = NULL;//MAKEINTRESOURCE(IDR_MENU1); wc.lpszClassName = g_szClass; RegisterClass(&wc); g_hWnd = CreateWindow( g_szClass, "NSM CD Controller", WS_DLGFRAME, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); if (!g_hWnd) return FALSE; ShowWindow(g_hWnd,nCmdShow); UpdateWindow(g_hWnd); MSG msg; while (GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); } CloseHandle(g_hCom); ::WSACleanup(); return 0; }