2 * $Logfile: /Freespace2/code/Sound/RBAudio.cpp $
7 * source file for redbook audio playback
10 * Revision 1.2 2002/05/07 03:16:52 theoddone33
11 * The Great Newline Fix
13 * Revision 1.1.1.1 2002/05/03 03:28:10 root
17 * 2 10/07/98 10:54a Dave
20 * 1 10/07/98 10:51a Dave
22 * 6 6/13/98 1:45p Sandeep
24 * 5 5/15/98 4:12p Allender
25 * removed redbook code. Put back in ingame join timer. Major fixups for
26 * stats in multiplayer. Pass correct score, medals, etc when leaving
27 * game. Be sure clients display medals, badges, etc.
29 * 4 1/19/98 11:37p Lawrance
30 * Fixing Optimization build warnings
32 * 3 9/09/97 3:39p Sandeep
33 * warning level 4 bugs
35 * 2 4/28/97 5:13p John
36 * more moving sound/movie code out of osapi.
38 * 1 4/28/97 4:45p John
39 * Initial version of ripping sound & movie out of OsAPI.
41 * 3 2/03/97 2:05p Allender
42 * don't stop playing redbook when exiting
44 * 2 1/30/97 9:57a Allender
45 * basic Redbook audio implemented.
47 * 1 1/28/97 9:54a Allender
70 #define HSG_CD_PLAYBACK 0
71 #define MSF_CD_PLAYBACK 1
76 typedef struct CDTrack {
81 int RBCDROM_State = 0; // CD is not used = 0
83 // CD is already used = -1
85 static CDTrack CDTrackInfo[255];
86 static UINT CDDiscID = 0;
88 static int RedBookEnabled=0, RedBookInstalled = 0;
89 static UINT CDDeviceID = 0;
90 static DWORD CDNumTracks = 0;
91 static fix Playback_Start_Time = 0;
92 static fix Playback_Pause_Time = 0;
93 static fix Playback_Length = 0;
94 static int Playback_first_track,Playback_last_track;
95 static int AUXDevice = -1;
96 static char MCIErrorMsg[256];
98 // Function Prototypes
100 unsigned long lsn_to_msf(unsigned long lsn);
101 unsigned long msf_to_lsn(unsigned long msf);
102 unsigned long msf_add(unsigned long msf1, unsigned long msf2);
103 unsigned long msf_sub(unsigned long msf1, unsigned long msf2);
105 UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1);
113 if (RedBookInstalled) {
114 //RBAStop(); // removed by MWA 2/3 -- will probably be handled by higher level code
115 mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL);
117 RedBookInstalled = 0;
124 MCI_OPEN_PARMS mciOpenParms;
125 MCI_SET_PARMS mciSetParms;
130 // removed by MWA since we can't seem to get this to work under NT...needs a little work I guess
134 num_devs = auxGetNumDevs();
136 // Find CD-AUDIO device if there is one.
137 for (i = 0; i < num_devs; i++)
140 auxGetDevCaps(i, &caps, sizeof(caps));
141 if (caps.wTechnology == AUXCAPS_CDAUDIO) {
143 mprintf(("CD operating on AUX %d.\n", AUXDevice));
148 if (AUXDevice == -1) {
149 mprintf(("Unable to find CD-AUDIO device. No Redbook music.\n"));
155 // We need to identify that a cd audio driver exists
156 mciOpenParms.lpstrDeviceType = NOX("cdaudio");
158 retval = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD)(LPVOID)&mciOpenParms);
160 if (retval == MCIERR_MUST_USE_SHAREABLE) RBCDROM_State = -1;
161 else RBCDROM_State = 0;
162 mciGetErrorString(retval, MCIErrorMsg, 256);
163 mprintf(("RBA (%x) MCI:%s.\n", retval, MCIErrorMsg));
167 // Now we need to set the time format to MSF for Redbook Compatablity.
168 RedBookInstalled = 1;
170 CDDeviceID = mciOpenParms.wDeviceID;
172 // mwa ?? logentry("Initializing Redbook device %d.\n", CDDeviceID);
174 mciSetParms.dwTimeFormat = MCI_FORMAT_MSF;
175 retval = mciSendCommand(CDDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&mciSetParms);
177 mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL);
178 mprintf(("Error %d. Unable to set Redbook Format.\n", retval));
181 else RedBookEnabled = 1;
186 void RBARegisterCD(void)
188 DWORD i, numtracks, retval = 0;
189 MCI_STATUS_PARMS mciStatusParms;
191 // Insure that CD is Redbook, then get track information
192 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
193 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
194 (DWORD)(LPVOID)&mciStatusParms);
196 mciGetErrorString(retval, MCIErrorMsg, 256);
197 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
202 CDNumTracks = numtracks = mciStatusParms.dwReturn;
204 for (i = 0; i < numtracks; i++)
206 mciStatusParms.dwTrack = i+1;
207 mciStatusParms.dwItem = MCI_STATUS_POSITION;
208 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms);
210 mprintf(("Could not retrieve information on track %d.\n", i+1));
212 CDTrackInfo[i].msf = mciStatusParms.dwReturn;
214 mciStatusParms.dwTrack = i+1;
215 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
216 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms);
218 mprintf(("Couldn't get length of track %d.\n", i+1));
220 CDTrackInfo[i].length = mciStatusParms.dwReturn;
222 // 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));
225 CDDiscID = GetCDDiscID();
229 long RBAGetDeviceStatus()
235 int get_track_time(int first,int last)
237 unsigned long playlen;
240 playlen = msf_sub(msf_add(CDTrackInfo[last-1].msf,CDTrackInfo[last-1].length), CDTrackInfo[first-1].msf);
242 min = MCI_MSF_MINUTE(playlen);
243 sec = MCI_MSF_SECOND(playlen);
249 int RBAPlayTrack(int track)
251 MCI_PLAY_PARMS mciPlayParms;
255 // Register CD if media has changed
256 if (!RedBookEnabled) return 0;
257 if (RBACheckMediaChange()) {
258 mprintf(("Unable to play track due to CD change.\n"));
263 min = min + MCI_MSF_MINUTE(CDTrackInfo[track-1].msf) + MCI_MSF_MINUTE(CDTrackInfo[track-1].length);
264 mciPlayParms.dwFrom = CDTrackInfo[track-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0);
265 mciPlayParms.dwTo = msf_add(CDTrackInfo[track-1].msf, CDTrackInfo[track-1].length);
266 retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
269 mciGetErrorString(retval, MCIErrorMsg, 256);
270 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
271 mprintf(("Unable to play CD track %d.\n", track));
275 Playback_Length = i2f(get_track_time(track,track));
276 Playback_Start_Time = timer_get_fixed_seconds();
278 Playback_first_track = Playback_last_track = track;
280 mprintf( ("Playing Track %d (len: %d secs)\n", track, f2i(Playback_Length)) );
287 int RBAPlayTracks(int start, int end)
289 MCI_PLAY_PARMS mciPlayParms;
292 Playback_Start_Time = 0;
295 // Register CD if media has changed
296 if (!RedBookEnabled) return 0;
297 if (RBACheckMediaChange()) {
298 mprintf(("Unable to play track due to CD change.\n"));
303 mciPlayParms.dwFrom = CDTrackInfo[start-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0);
304 mciPlayParms.dwTo = msf_add(CDTrackInfo[end-1].msf, CDTrackInfo[end-1].length);
305 retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
308 mciGetErrorString(retval, MCIErrorMsg, 256);
309 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
310 mprintf(("Unable to play CD tracks %d-%d.\n", start,end));
314 Playback_Length = i2f(get_track_time(start,end));
315 Playback_Start_Time = timer_get_fixed_seconds();
317 Playback_first_track = start;
318 Playback_last_track = end;
320 mprintf( ("Playing Tracks %d-%d (len: %d secs)\n", start,end,f2i(Playback_Length)) );
331 if (!RedBookEnabled) return;
333 retval = mciSendCommand(CDDeviceID, MCI_STOP,0,NULL);
335 mciGetErrorString(retval, MCIErrorMsg, 256);
336 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
337 mprintf(("CD Stop command failed.\n"));
342 void RBASetStereoAudio(RBACHANNELCTL *channels)
348 void RBASetQuadAudio(RBACHANNELCTL *channels)
354 void RBAGetAudioInfo(RBACHANNELCTL *channels)
360 static unsigned rba_paused_head_loc = 0;
365 MCI_GENERIC_PARMS mciGenParms;
369 if (!RedBookEnabled) return;
371 rba_paused_head_loc = RBAGetHeadLoc(&min, &sec, &frame);
373 // mwa ??? mciGenParms.dwCallback = GetLibraryWindow();
374 retval = mciSendCommand(CDDeviceID, MCI_PAUSE, MCI_NOTIFY,
375 (DWORD)(LPVOID)&mciGenParms);
377 mprintf(("ERROR: Unable to pause CD.\n"));
380 mprintf(("Pausing CD...\n"));
381 Playback_Pause_Time = timer_get_fixed_seconds();
390 MCI_PLAY_PARMS mciPlayParms;
392 if (!RedBookEnabled) return 0;
394 if (RBACheckMediaChange()) {
396 return RBA_MEDIA_CHANGED;
399 mciPlayParms.dwFrom = rba_paused_head_loc; // MCI_MAKE_TMSF(track, 0, 0, 0);
400 mciPlayParms.dwTo = msf_add(CDTrackInfo[Playback_last_track-1].msf,
401 CDTrackInfo[Playback_last_track-1].length);
402 retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
405 mprintf(("ERROR: Resume CD play failed.\n"));
408 Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time;
414 void RBASetChannelVolume(int channel, int volume)
418 if ( AUXDevice == -1 ) // added because we might not be able to do this under NT
421 volume = volume << 8;
424 vol = MAKELONG(0,volume);
425 else if (channel == 1)
426 vol = MAKELONG(volume,0);
428 auxSetVolume(AUXDevice, vol);
432 void RBASetVolume(int volume)
437 if ( AUXDevice == -1 ) // added because we might not be able to do this under NT
440 wvol = (unsigned short)((volume) << 8);
442 vol = MAKELONG(wvol, wvol);
443 auxSetVolume(AUXDevice, vol);
447 long RBAGetHeadLoc(int *min, int *sec, int *frame)
449 MCI_STATUS_PARMS mciStatusParms;
452 if (!RedBookEnabled) return 0;
454 if (RBACheckMediaChange())
455 return RBA_MEDIA_CHANGED;
457 mciStatusParms.dwItem = MCI_STATUS_POSITION;
458 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms);
461 mprintf(("Couldn't get location of CD head.\n"));
464 *min = MCI_MSF_MINUTE(mciStatusParms.dwReturn);
465 *sec = MCI_MSF_SECOND(mciStatusParms.dwReturn);
466 *frame = MCI_MSF_FRAME(mciStatusParms.dwReturn);
468 return mciStatusParms.dwReturn;
475 int delta_time; //in seconds
477 if (!RBAPeekPlayStatus())
480 delta_time = f2i(timer_get_fixed_seconds()-Playback_Start_Time+f1_0/2);
482 for (track=Playback_first_track;track<=Playback_last_track && delta_time>0;track++) {
484 delta_time -= get_track_time(track,track);
490 Assert(track <= Playback_last_track);
496 int RBAPeekPlayStatus()
498 //@@ MCI_STATUS_PARMS mciStatusParms;
501 if (!RedBookEnabled) return 0;
504 if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0;
508 //@@ mciStatusParms.dwItem = MCI_STATUS_MODE;
509 //@@ retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms);
511 //@@ mprintf((0, "Unable to obtain current status of CD.\n"));
514 //@@ if (mciStatusParms.dwReturn == MCI_MODE_PLAY) return 1;
522 if (RedBookEnabled < 1) return 0;
530 mprintf(("Enabling Redbook...\n"));
537 mprintf(("Disabling Redbook...\n"));
541 int RBAGetNumberOfTracks(void)
543 MCI_STATUS_PARMS mciStatusParms;
546 if (!RedBookEnabled) return 0;
548 if (RBACheckMediaChange())
551 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
552 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
553 (DWORD)(LPVOID)&mciStatusParms);
556 mprintf(("Get number of CD tracks failed.\n"));
560 return mciStatusParms.dwReturn;
564 // RB functions: Internal to RBA library -----------------------------
566 int RBACheckMediaChange()
568 if (!RedBookEnabled) return 0;
570 if (CDDiscID != GetCDDiscID() || !CDDiscID) return 1;
575 // Converts Logical Sector Number to Minutes Seconds Frame Format
577 unsigned long lsn_to_msf(unsigned long lsn)
579 unsigned long min,sec,frame;
588 return((min << 16) + (sec << 8) + (frame << 0));
591 // convert minutes seconds frame format to a logical sector number.
593 unsigned long msf_to_lsn(unsigned long msf)
595 unsigned long min,sec,frame;
597 min = (msf >> 16) & 0xFF;
598 sec = (msf >> 8) & 0xFF;
599 frame = (msf >> 0) & 0xFF;
601 return( (min * 60*75) + (sec * 75) + frame - 150 );
604 unsigned long msf_add(unsigned long msf1, unsigned long msf2)
606 uint min1, sec1, frame1;
607 uint min2, sec2, frame2;
609 // we don't take frames into account, which may not be right.
610 min1 = MCI_MSF_MINUTE(msf1);
611 sec1 = MCI_MSF_SECOND(msf1);
612 frame1 = MCI_MSF_FRAME(msf1);
617 if ((sec1 + MCI_MSF_SECOND(msf2)) > 59) {
618 sec2 = (sec1+MCI_MSF_SECOND(msf2)) - 60;
621 else sec2 = sec1 + MCI_MSF_SECOND(msf2);
622 min2 = min1+MCI_MSF_MINUTE(msf2);
625 // logentry("msf_add:(%d:%d)\n", min2, sec2);
627 return MCI_MAKE_MSF(min2, sec2, 0);
631 unsigned long msf_sub(unsigned long msf1, unsigned long msf2)
633 int min1, sec1, frame1;
634 int min2, sec2, frame2;
636 // we don't take frames into account, which may not be right.
637 min1 = MCI_MSF_MINUTE(msf1);
638 sec1 = MCI_MSF_SECOND(msf1);
639 frame1 = MCI_MSF_FRAME(msf1);
644 if ((sec1 - (int)MCI_MSF_SECOND(msf2)) < 0) {
645 sec2 = 60 - ((int)MCI_MSF_SECOND(msf2)-sec1);
646 min1--; // This is a shortcut to min2 = min1-(MSF_MIN(msf2)-1)
648 else sec2 = sec1 - (int)MCI_MSF_SECOND(msf2);
649 min2 = min1-(int)MCI_MSF_MINUTE(msf2);
652 // logentry("msf_sub:(%d:%d)\n", min2, sec2);
654 return MCI_MAKE_MSF(min2, sec2, 0);
658 unsigned long msf_to_sec(unsigned long msf)
660 unsigned long min,sec,frame;
662 min = (msf >> 16) & 0xFF;
663 sec = (msf >> 8) & 0xFF;
664 frame = (msf >> 0) & 0xFF;
666 return (min*60) + sec;
672 MCI_STATUS_PARMS mciStatusParms;
674 unsigned long msflen, tracks, msftrack1;
676 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
677 if ((retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
678 (DWORD)(LPVOID)&mciStatusParms))>0) {
679 mprintf(("RBA: Get number of CD tracks failed.\n"));
682 tracks = mciStatusParms.dwReturn;
684 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
685 if ((retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
686 (DWORD)(LPVOID)&mciStatusParms))>0) {
687 mprintf(("RBA: Get media length failed.\n"));
690 msflen = mciStatusParms.dwReturn;
692 mciStatusParms.dwTrack = 1;
693 mciStatusParms.dwItem = MCI_STATUS_POSITION;
694 if ( (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms)) >0) {
695 mprintf(("Could not retrieve information on track %d.\n", 1));
698 msftrack1 = mciStatusParms.dwReturn;
700 return MakeCDDiscID(tracks, msflen, msftrack1);
704 UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1)
708 code = (UINT)(msf_to_sec(msftrack1) << 19);
709 code |= (UINT)(msf_to_sec(msflen) << 6);
711 code |= (UINT)(tracks&0xffffffc0);