/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ #pragma off (unreferenced) static char rcsid[] = "$Id: rbaudio.c,v 1.1.1.1 2006/03/17 20:00:41 zicodxx Exp $"; #pragma on (unreferenced) #define _WIN32 #define WIN32_LEAN_AND_MEAN #include #include #include #include #include "winapp.h" #include "mono.h" #include "error.h" #include "rbaudio.h" #include "fix.h" #include "winapp.h" #define HSG_CD_PLAYBACK 0 #define MSF_CD_PLAYBACK 1 #pragma pack(1) typedef struct CDTrack { DWORD msf; DWORD length; } CDTrack; int RBCDROM_State = 0; // CD is not used = 0 // CD is used = 1 // CD is already used = -1 static CDTrack CDTrackInfo[255]; static UINT CDDiscID = 0; static int RedBookEnabled=0, RedBookInstalled = 0; static UINT CDDeviceID = 0; static DWORD CDNumTracks = 0; static fix Playback_Start_Time = 0; static fix Playback_Pause_Time = 0; static fix Playback_Length = 0; static int Playback_first_track,Playback_last_track; static int AUXDevice = -1; static char MCIErrorMsg[256]; // Function Prototypes unsigned long lsn_to_msf(unsigned long lsn); unsigned long msf_to_lsn(unsigned long msf); unsigned long msf_add(unsigned long msf1, unsigned long msf2); unsigned long msf_sub(unsigned long msf1, unsigned long msf2); UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1); UINT GetCDDiscID(); // Functions void RBClose() { if (RedBookInstalled) { mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL); RedBookEnabled = 0; RedBookInstalled = 0; } } void RBAInit(void) { MCI_OPEN_PARMS mciOpenParms; MCI_SET_PARMS mciSetParms; DWORD retval = 0; int num_devs, i; atexit(RBClose); num_devs = auxGetNumDevs(); // Find CD-AUDIO device if there is one. for (i = 0; i < num_devs; i++) { AUXCAPS caps; auxGetDevCaps(i, &caps, sizeof(caps)); if (caps.wTechnology == AUXCAPS_CDAUDIO) { AUXDevice = i; mprintf((0, "CD operating on AUX %d.\n", AUXDevice)); break; } } if (AUXDevice == -1) { mprintf((1, "Unable to find CD-AUDIO device. No Redbook music.\n")); RedBookEnabled = 0; return; } // We need to identify that a cd audio driver exists mciOpenParms.lpstrDeviceType = "cdaudio"; retval = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD)(LPVOID)&mciOpenParms); if (retval) { if (retval == MCIERR_MUST_USE_SHAREABLE) RBCDROM_State = -1; else RBCDROM_State = 0; mciGetErrorString(retval, MCIErrorMsg, 256); mprintf((1,"RBA (%x) MCI:%s.\n", retval, MCIErrorMsg)); RedBookEnabled = 0; } else { // Now we need to set the time format to MSF for Redbook Compatablity. RedBookInstalled = 1; RBCDROM_State = 1; CDDeviceID = mciOpenParms.wDeviceID; logentry("Initializing Redbook device %d.\n", CDDeviceID); mciSetParms.dwTimeFormat = MCI_FORMAT_MSF; retval = mciSendCommand(CDDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&mciSetParms); if (retval) { mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL); mprintf((0, "Error %d. Unable to set Redbook Format.\n", retval)); RedBookEnabled = 0; } else RedBookEnabled = 1; } } void RBARegisterCD(void) { DWORD i, numtracks, retval = 0; MCI_STATUS_PARMS mciStatusParms; // Insure that CD is Redbook, then get track information mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms); if (retval) { mciGetErrorString(retval, MCIErrorMsg, 256); mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg)); CDNumTracks = 0; return; } CDNumTracks = numtracks = mciStatusParms.dwReturn; for (i = 0; i < numtracks; i++) { mciStatusParms.dwTrack = i+1; mciStatusParms.dwItem = MCI_STATUS_POSITION; retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms); if (retval) { mprintf((0, "Could not retrieve information on track %d.\n", i+1)); } CDTrackInfo[i].msf = mciStatusParms.dwReturn; mciStatusParms.dwTrack = i+1; mciStatusParms.dwItem = MCI_STATUS_LENGTH; retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms); if (retval) { mprintf((0, "Couldn't get length of track %d.\n", i+1)); } CDTrackInfo[i].length = mciStatusParms.dwReturn; // logentry("[%d] (%d:%d):(%d:%d)\n", i+1, MCI_MSF_MINUTE(CDTrackInfo[i].msf), MCI_MSF_SECOND(CDTrackInfo[i].msf), MCI_MSF_MINUTE(CDTrackInfo[i].length), MCI_MSF_SECOND(CDTrackInfo[i].length)); } CDDiscID = GetCDDiscID(); } long RBAGetDeviceStatus() { return 0; } int get_track_time(int first,int last) { unsigned long playlen; int min, sec; playlen = msf_sub(msf_add(CDTrackInfo[last-1].msf,CDTrackInfo[last-1].length), CDTrackInfo[first-1].msf); min = MCI_MSF_MINUTE(playlen); sec = MCI_MSF_SECOND(playlen); return (min*60+sec); } int RBAPlayTrack(int track) { MCI_PLAY_PARMS mciPlayParms; int min=0; int retval; // Register CD if media has changed if (!RedBookEnabled) return 0; if (RBACheckMediaChange()) { mprintf((0, "Unable to play track due to CD change.\n")); return 0; } // Play the track min = min + MCI_MSF_MINUTE(CDTrackInfo[track-1].msf) + MCI_MSF_MINUTE(CDTrackInfo[track-1].length); mciPlayParms.dwFrom = CDTrackInfo[track-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0); mciPlayParms.dwTo = msf_add(CDTrackInfo[track-1].msf, CDTrackInfo[track-1].length); retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms); if (retval) { mciGetErrorString(retval, MCIErrorMsg, 256); mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg)); mprintf((0, "Unable to play CD track %d.\n", track)); return 0; } else { Playback_Length = i2f(get_track_time(track,track)); Playback_Start_Time = timer_get_fixed_seconds(); Playback_first_track = Playback_last_track = track; mprintf( (0,"Playing Track %d (len: %d secs)\n", track, f2i(Playback_Length)) ); } return 1; } int RBAPlayTracks(int start, int end) { MCI_PLAY_PARMS mciPlayParms; int retval; Playback_Start_Time = 0; Playback_Length = 0; // Register CD if media has changed if (!RedBookEnabled) return 0; if (RBACheckMediaChange()) { mprintf((0, "Unable to play track due to CD change.\n")); return 0; } // Play the track mciPlayParms.dwFrom = CDTrackInfo[start-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0); mciPlayParms.dwTo = msf_add(CDTrackInfo[end-1].msf, CDTrackInfo[end-1].length); retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms); if (retval) { mciGetErrorString(retval, MCIErrorMsg, 256); mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg)); mprintf((0, "Unable to play CD tracks %d-%d.\n", start,end)); return 0; } else { Playback_Length = i2f(get_track_time(start,end)); Playback_Start_Time = timer_get_fixed_seconds(); Playback_first_track = start; Playback_last_track = end; mprintf( (0,"Playing Tracks %d-%d (len: %d secs)\n", start,end,f2i(Playback_Length)) ); } return 1; } void RBAStop() { int retval; if (!RedBookEnabled) return; retval = mciSendCommand(CDDeviceID, MCI_STOP,0,NULL); if (retval) { mciGetErrorString(retval, MCIErrorMsg, 256); mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg)); mprintf((0, "CD Stop command failed.\n")); } } void RBASetStereoAudio(RBACHANNELCTL *channels) { } void RBASetQuadAudio(RBACHANNELCTL *channels) { } void RBAGetAudioInfo(RBACHANNELCTL *channels) { } static unsigned rba_paused_head_loc = 0; void RBAPause() { int retval; MCI_GENERIC_PARMS mciGenParms; int frame, sec, min; if (!RedBookEnabled) return; rba_paused_head_loc = RBAGetHeadLoc(&min, &sec, &frame); mciGenParms.dwCallback = GetLibraryWindow(); retval = mciSendCommand(CDDeviceID, MCI_PAUSE, MCI_NOTIFY, (DWORD)(LPVOID)&mciGenParms); if (retval) { mprintf((1,"ERROR: Unable to pause CD.\n")); } else { mprintf((0, "Pausing CD...\n")); Playback_Pause_Time = timer_get_fixed_seconds(); } } int RBAResume() { int retval; MCI_PLAY_PARMS mciPlayParms; if (!RedBookEnabled) return 0; if (RBACheckMediaChange()) { RBARegisterCD(); return RBA_MEDIA_CHANGED; } mciPlayParms.dwFrom = rba_paused_head_loc; // MCI_MAKE_TMSF(track, 0, 0, 0); mciPlayParms.dwTo = msf_add(CDTrackInfo[Playback_last_track-1].msf, CDTrackInfo[Playback_last_track-1].length); retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms); if (retval) { mprintf((1, "ERROR: Resume CD play failed.\n")); } else Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time; return 1; } void RBASetChannelVolume(int channel, int volume) { DWORD vol; volume = volume << 8; if (channel == 0) vol = MAKELONG(0,volume); else if (channel == 1) vol = MAKELONG(volume,0); auxSetVolume(AUXDevice, vol); } void RBASetVolume(int volume) { DWORD vol; WORD wvol; wvol = (WORD)(volume) << 8; vol = MAKELONG(wvol, wvol); auxSetVolume(AUXDevice, vol); } long RBAGetHeadLoc(int *min, int *sec, int *frame) { MCI_STATUS_PARMS mciStatusParms; int retval; if (!RedBookEnabled) return 0; if (RBACheckMediaChange()) return RBA_MEDIA_CHANGED; mciStatusParms.dwItem = MCI_STATUS_POSITION; retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms); if (retval) { mprintf((0, "Couldn't get location of CD head.\n")); } *min = MCI_MSF_MINUTE(mciStatusParms.dwReturn); *sec = MCI_MSF_SECOND(mciStatusParms.dwReturn); *frame = MCI_MSF_FRAME(mciStatusParms.dwReturn); return mciStatusParms.dwReturn; } int RBAGetTrackNum() { int track; int delta_time; //in seconds if (!RBAPeekPlayStatus()) return 0; delta_time = f2i(timer_get_fixed_seconds()-Playback_Start_Time+f1_0/2); for (track=Playback_first_track;track<=Playback_last_track && delta_time>0;track++) { delta_time -= get_track_time(track,track); if (delta_time < 0) break; } Assert(track <= Playback_last_track); return track; } int RBAPeekPlayStatus() { //@@ MCI_STATUS_PARMS mciStatusParms; //@@ int retval; if (!RedBookEnabled) return 0; if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0; else return 1; //@@ mciStatusParms.dwItem = MCI_STATUS_MODE; //@@ retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms); //@@ if (retval) { //@@ mprintf((0, "Unable to obtain current status of CD.\n")); //@@ } //@@ //@@ if (mciStatusParms.dwReturn == MCI_MODE_PLAY) return 1; //@@ else return 0; } int RBAEnabled() { if (RedBookEnabled < 1) return 0; else return 1; } void RBAEnable() { RedBookEnabled = 1; mprintf((0, "Enabling Redbook...\n")); } void RBADisable() { RedBookEnabled = 0; mprintf((0, "Disabling Redbook...\n")); } int RBAGetNumberOfTracks(void) { MCI_STATUS_PARMS mciStatusParms; int retval; if (!RedBookEnabled) return 0; if (RBACheckMediaChange()) RBARegisterCD(); mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms); if (retval) { mprintf((0, "Get number of CD tracks failed.\n")); return 0; } return mciStatusParms.dwReturn; } // RB functions: Internal to RBA library ----------------------------- int RBACheckMediaChange() { if (!RedBookEnabled) return 0; if (CDDiscID != GetCDDiscID() || !CDDiscID) return 1; else return 0; } // Converts Logical Sector Number to Minutes Seconds Frame Format unsigned long lsn_to_msf(unsigned long lsn) { unsigned long min,sec,frame; lsn += 150; min = lsn / (60*75); lsn = lsn % (60*75); sec = lsn / 75; lsn = lsn % 75; frame = lsn; return((min << 16) + (sec << 8) + (frame << 0)); } // convert minutes seconds frame format to a logical sector number. unsigned long msf_to_lsn(unsigned long msf) { unsigned long min,sec,frame; min = (msf >> 16) & 0xFF; sec = (msf >> 8) & 0xFF; frame = (msf >> 0) & 0xFF; return( (min * 60*75) + (sec * 75) + frame - 150 ); } unsigned long msf_add(unsigned long msf1, unsigned long msf2) { uint min1, sec1, frame1; uint min2, sec2, frame2; // we don't take frames into account, which may not be right. min1 = MCI_MSF_MINUTE(msf1); sec1 = MCI_MSF_SECOND(msf1); frame1 = MCI_MSF_FRAME(msf1); frame2 = 0; min2 = 0; if ((sec1 + MCI_MSF_SECOND(msf2)) > 59) { sec2 = (sec1+MCI_MSF_SECOND(msf2)) - 60; min1++; } else sec2 = sec1 + MCI_MSF_SECOND(msf2); min2 = min1+MCI_MSF_MINUTE(msf2); frame2 = 0; // logentry("msf_add:(%d:%d)\n", min2, sec2); return MCI_MAKE_MSF(min2, sec2, 0); } unsigned long msf_sub(unsigned long msf1, unsigned long msf2) { int min1, sec1, frame1; int min2, sec2, frame2; // we don't take frames into account, which may not be right. min1 = MCI_MSF_MINUTE(msf1); sec1 = MCI_MSF_SECOND(msf1); frame1 = MCI_MSF_FRAME(msf1); frame2 = 0; min2 = 0; if ((sec1 - (int)MCI_MSF_SECOND(msf2)) < 0) { sec2 = 60 - ((int)MCI_MSF_SECOND(msf2)-sec1); min1--; // This is a shortcut to min2 = min1-(MSF_MIN(msf2)-1) } else sec2 = sec1 - (int)MCI_MSF_SECOND(msf2); min2 = min1-(int)MCI_MSF_MINUTE(msf2); frame2 = 0; // logentry("msf_sub:(%d:%d)\n", min2, sec2); return MCI_MAKE_MSF(min2, sec2, 0); } unsigned long msf_to_sec(unsigned long msf) { unsigned long min,sec,frame; min = (msf >> 16) & 0xFF; sec = (msf >> 8) & 0xFF; frame = (msf >> 0) & 0xFF; return (min*60) + sec; } UINT GetCDDiscID() { MCI_STATUS_PARMS mciStatusParms; int retval; unsigned long msflen, tracks, msftrack1; mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; if (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms)) { mprintf((1, "RBA: Get number of CD tracks failed.\n")); return 0; } tracks = mciStatusParms.dwReturn; mciStatusParms.dwItem = MCI_STATUS_LENGTH; if (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms)) { mprintf((1, "RBA: Get media length failed.\n")); return 0; } msflen = mciStatusParms.dwReturn; mciStatusParms.dwTrack = 1; mciStatusParms.dwItem = MCI_STATUS_POSITION; if (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms)) { mprintf((1, "Could not retrieve information on track %d.\n", 1)); return 0; } msftrack1 = mciStatusParms.dwReturn; return MakeCDDiscID(tracks, msflen, msftrack1); } UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1) { UINT code=0; code = (UINT)(msf_to_sec(msftrack1) << 19); code |= (UINT)(msf_to_sec(msflen) << 6); code |= (UINT)(tracks&0xffffffc0); return code; }