// 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" #define CD0 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 HANDLE g_mxPlaylist; // protects the playlist // useful strings char g_szNull[] = ""; const char g_szClass[] = "NSMCDWND"; const char g_szError[] = "Error"; #ifdef CD0 //const char g_szCommPort[] = "COM6"; #else //const char g_szCommPort[] = "COM7"; #endif const char g_szCommPort[] = "COM1"; HWND g_hWnd; HINSTANCE g_hInst; // shared status information protected by g_mxStatusInfo CDTime g_cdtCurDisc; CDTime g_cdtCurTrack; int g_iCurDisc; // 1..100 int g_iCurTrack; int g_iCurIndex; BOOL g_fAutopauseMode; int g_nsmStatus; RECT g_rStatus = { 10, 10, 250, 80 }; //#define TEST #define NUMDISCS 100 #define Assert(x) { if (!(x)) _asm int 3 } #define Trace(n) { char buf[50]; wsprintf(buf,"%d",n); MessageBox(NULL,buf,"trace",MB_OK); } #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 fFlags; CDTime cdtStart; CDTime cdtLength; } TRACKINFO; #define TIF_PLAYED typedef struct { unsigned long xID; unsigned short iFirst; unsigned short cTracks; CDTime cdtTotal; unsigned short fFlags; TRACKINFO * pTrackInfo; } DISCINFO; #define DIF_PRESENT 0x0001 DISCINFO g_di[NUMDISCS]; typedef struct { unsigned short Disc; unsigned short Track; } PLAYLISTITEM; HGLOBAL g_hPlaylist = NULL; int g_cPlaylist; int g_iPlaylistCur; /////////////////////////////////////// enum { PLAY, PAUSE, STOP, NODISC, RANDOM, PLAYDISC, } g_status; //////////////////////////////////////////////////////////////////////////// // // // //////////////////////////////////////////////////////////////////////////// HANDLE CreateInheritMutex(void) { // SECURITY_ATTRIBUTES sa; // sa.nLength = sizeof(SECURITY_ATTRIBUTES); // sa.lpSecurityDescriptor = NULL; // sa.bInheritHandle = TRUE; return CreateMutex(NULL,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 cmd,char *pszArg=NULL,char *pszRet=NULL); int QnsmCommand( int cmd, char *pszArg, char *pszRet) { char buf[50]; int cbCmd; int retval; 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 (i NUMDISCS || !(g_di[disc-1].fFlags & DIF_PRESENT) || track < g_di[disc-1].iFirst || track >= (g_di[disc-1].iFirst + g_di[disc-1].cTracks)) return FALSE; if (disc==100) disc=0; WaitForSingleObject(g_mxComm,INFINITE); sprintf(buf,"A%.2d",disc); QnsmCommand(NSM_LOAD_DISC,buf); sprintf(buf,"A%.2d",track); QnsmCommand(NSM_TRACK_POS,buf); QnsmCommand(NSM_PAUSE_OFF); if (fAutopause) QnsmCommand(NSM_AUTOPAUSE_ON); else QnsmCommand(NSM_AUTOPAUSE_OFF); ReleaseMutex(g_mxComm); return TRUE; } //////////////////////////////////////////////////////////////////////////// // // // //////////////////////////////////////////////////////////////////////////// void NextPlaylist() { PLAYLISTITEM *ppl; ::WaitForSingleObject(g_mxPlaylist,INFINITE); ppl = (PLAYLISTITEM *)::GlobalLock(g_hPlaylist); PlayDiscTrack(ppl[g_iPlaylistCur].Disc,ppl[g_iPlaylistCur].Track); g_iPlaylistCur++; if (g_iPlaylistCur==g_cPlaylist) { GenerateRndPlaylist(); g_iPlaylistCur=0; } ::GlobalUnlock(g_hPlaylist); ::ReleaseMutex(g_mxPlaylist); } //////////////////////////////////////////////////////////////////////////// // // disc: 1..100 // //////////////////////////////////////////////////////////////////////////// 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; idisc,g_di[req->disc-1].iFirst,FALSE); g_status = PLAYDISC; } break; case PlayTrack: { reqPLAYTRACK *req = (reqPLAYTRACK *)parg; PlayDiscTrack(req->disc,req->track); } break; case PlayRandom: g_status = RANDOM; NextPlaylist(); break; case GetDiscTrack: { reqPLAYTRACK rpt; rpt.disc = g_iCurDisc; rpt.track = g_iCurTrack; ::send(sk,(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,(char *)&rti,sizeof(rti),0); } break; } } else { //::MessageBox(g_hWnd,"Connection broken","NSM Error",MB_OK); return 0; } } } //////////////////////////////////////////////////////////////////////////// // // // //////////////////////////////////////////////////////////////////////////// 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; #ifdef CD0 sin.sin_port = htons(CD0_PORT_NUM); #else sin.sin_port = htons(CD1_PORT_NUM); #endif 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); // process commands for the new socket indefinitely CommandLoop(sk_connect); 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); } //////////////////////////////////////////////////////////////////////////// // // iSel: 0..99 // //////////////////////////////////////////////////////////////////////////// void FillTrackList(int iSel) { HWND hLVTracks = GetDlgItem(g_hWnd,IDC_LIST2); int j; DISCINFO &di = g_di[iSel]; LV_ITEM item; char buf[15]; ListView_DeleteAllItems(hLVTracks); if (iSel>=0 && iSeliFirst + pdi->cTracks) { nsmCommand(NSM_AUTOPAUSE_ON); g_status = RANDOM; } } } ::ReleaseMutex(g_mxStatusInfo); ::InvalidateRect(hWnd,&g_rStatus,TRUE); // Update the status every 1.2 seconds ::Sleep(1200); } ::ReleaseDC(hWnd,hdc); return 0; } //////////////////////////////////////////////////////////////////////////// // // // //////////////////////////////////////////////////////////////////////////// LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam) { switch (message) { case WM_CREATE: CreateDlgControls(hWnd, NULL, MAKEINTRESOURCE(IDD_PANEL), g_hInst); { char buf[128]; GetWindowText(hWnd,buf,sizeof(buf)); #ifdef CD0 lstrcat(buf," #0 on "); #else lstrcat(buf," #1 on "); #endif lstrcat(buf,g_szCommPort); SetWindowText(hWnd,buf); } { HWND hLV = GetDlgItem(hWnd,IDC_LIST1); int iCol = 0; LV_COLUMN lvc; int unit; // Calculate column width units { HDC hdc; TEXTMETRIC tm; hdc = GetDC(NULL); SelectObject(hdc,(HFONT)GetStockObject(ANSI_VAR_FONT)); GetTextMetrics(hdc,&tm); ReleaseDC(NULL,hdc); unit = tm.tmAveCharWidth+1; } lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; lvc.fmt = LVCFMT_LEFT; lvc.pszText = "Disc"; lvc.cx = unit * 6; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "ID"; lvc.cx = unit * 14; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Tracks"; lvc.cx = unit * 10; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Time"; lvc.cx = unit * 9; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Artist"; lvc.cx = unit * 15; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Title"; lvc.cx = unit * 15; ListView_InsertColumn(hLV, iCol++, &lvc); // Do the TRACKS listview hLV = GetDlgItem(hWnd,IDC_LIST2); iCol = 0; lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT; lvc.fmt = LVCFMT_LEFT; lvc.pszText = "Track"; lvc.cx = unit * 7; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Start"; lvc.cx = unit * 9; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Time"; lvc.cx = unit * 9; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Title"; lvc.cx = unit * 15; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Style"; lvc.cx = unit * 15; ListView_InsertColumn(hLV, iCol++, &lvc); lvc.pszText = "Played"; lvc.cx = unit * 15; ListView_InsertColumn(hLV, iCol++, &lvc); } { // Start status updating thread. DWORD tid; HANDLE hThread; hThread = CreateThread(NULL,0,(unsigned long (__stdcall *)(void *))StatusThread,hWnd,0,&tid); CloseHandle(hThread); } break; case WM_PAINT: { PAINTSTRUCT ps; char buf[100]; TEXTMETRIC tm; char *p; RECT r; BeginPaint(hWnd,&ps); ::SetBkMode(ps.hdc,TRANSPARENT); ::SelectObject(ps.hdc,GetStockObject(ANSI_VAR_FONT)); ::GetTextMetrics(ps.hdc,&tm); ::FrameRect(ps.hdc,&g_rStatus,(HBRUSH)GetStockObject(BLACK_BRUSH)); r = g_rStatus; ::InflateRect(&r,-2,-2); ::WaitForSingleObject(g_mxStatusInfo,INFINITE); strcpy(buf,"Disc: "); g_cdtCurDisc.Render(&buf[6]); strcat(buf," Track: "); g_cdtCurTrack.Render(&buf[strlen(buf)]); strcat(buf," Autopause: "); strcat(buf,g_fAutopauseMode?"on":"off"); ::TextOut(ps.hdc,r.left,r.top,buf,strlen(buf)); sprintf(buf,"Disc %d Track %d Index %d",g_iCurDisc,g_iCurTrack,g_iCurIndex); ::TextOut(ps.hdc,r.left,r.top+tm.tmHeight,buf,strlen(buf)); p = g_pszStatus[g_nsmStatus*2+1]; ::TextOut(ps.hdc,r.left,r.top+tm.tmHeight*2,p,strlen(p)); if (g_hPlaylist) { PLAYLISTITEM *ppl = (PLAYLISTITEM *)::GlobalLock(g_hPlaylist); ::WaitForSingleObject(g_mxPlaylist,INFINITE); sprintf(buf,"Next: Disc %d Track %d (%d of %d)", ppl[g_iPlaylistCur].Disc, ppl[g_iPlaylistCur].Track, g_iPlaylistCur+1, g_cPlaylist); ::GlobalUnlock(g_hPlaylist); ::ReleaseMutex(g_mxPlaylist); ::TextOut(ps.hdc,r.left,r.top+tm.tmHeight*3,buf,strlen(buf)); } ::ReleaseMutex(g_mxStatusInfo); EndPaint(hWnd,&ps); } break; case WM_COMMAND: switch (uParam) { case IDCANCEL: DestroyWindow(hWnd); break; case IDC_LOAD: { int i = LVGetSelected(GetDlgItem(hWnd,IDC_LIST1)); //if (i != -1) { char buf[5]; sprintf(buf,"A%.2d",i==-1?2:i+1); nsmCommand(NSM_LOAD_DISC,buf); } } break; case IDC_UNLOAD: nsmCommand(NSM_RETURN_DISC); g_status = NODISC; break; case IDC_SCAN: Scan(hWnd); EnableWindow(GetDlgItem(hWnd,IDC_RANDOM),TRUE); EnableWindow(GetDlgItem(hWnd,IDC_NEXT),TRUE); break; case IDC_PLAY: if (g_status==NODISC) break; if (g_status==STOP) nsmCommand(NSM_TRACK_POS,"A01"); nsmCommand(NSM_PAUSE_OFF); g_status = PLAY; break; case IDC_PAUSE: if (g_status==NODISC) break; nsmCommand(NSM_PAUSE_ON); g_status = PAUSE; break; case IDC_STOP: if (g_status==NODISC) break; nsmCommand(NSM_STOP_DISC); g_status = STOP; break; case IDC_RANDOM: GenerateRndPlaylist(); g_status=RANDOM; g_iPlaylistCur = 0; NextPlaylist(); break; case IDC_NEXT: NextPlaylist(); break; } break; case WM_NOTIFY: { if (uParam==IDC_LIST1) { const NMHDR* const& pnmhdr = (NMHDR*)lParam; if (pnmhdr->code == NM_CLICK) FillTrackList(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()) { char buf[80]; wsprintf(buf,"Port %s in use.",g_szCommPort); MessageBox(NULL,buf,g_szError,MB_OK); return 0; } //Trace(6) // Mutex synchronizes access to the status global variables g_mxStatusInfo = CreateInheritMutex(); if (!g_mxStatusInfo) return 0; g_mxPlaylist = CreateInheritMutex(); if (!g_mxPlaylist) return 0; #ifndef TEST nsmCommand(NSM_STOP_DISC); g_status = STOP; #endif //Trace(7) // 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); //Trace(8) 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; }