2 * Copyright (C) Volition, Inc. 1999. All rights reserved.
4 * All source code herein is the property of Volition, Inc. You may not sell
5 * or otherwise commercially exploit the source or things you created based on
10 * $Logfile: /Freespace2/code/Sound/RBAudio.cpp $
15 * source file for redbook audio playback
18 * Revision 1.3 2002/06/09 04:41:27 relnev
19 * added copyright header
21 * Revision 1.2 2002/05/07 03:16:52 theoddone33
22 * The Great Newline Fix
24 * Revision 1.1.1.1 2002/05/03 03:28:10 root
28 * 2 10/07/98 10:54a Dave
31 * 1 10/07/98 10:51a Dave
33 * 6 6/13/98 1:45p Sandeep
35 * 5 5/15/98 4:12p Allender
36 * removed redbook code. Put back in ingame join timer. Major fixups for
37 * stats in multiplayer. Pass correct score, medals, etc when leaving
38 * game. Be sure clients display medals, badges, etc.
40 * 4 1/19/98 11:37p Lawrance
41 * Fixing Optimization build warnings
43 * 3 9/09/97 3:39p Sandeep
44 * warning level 4 bugs
46 * 2 4/28/97 5:13p John
47 * more moving sound/movie code out of osapi.
49 * 1 4/28/97 4:45p John
50 * Initial version of ripping sound & movie out of OsAPI.
52 * 3 2/03/97 2:05p Allender
53 * don't stop playing redbook when exiting
55 * 2 1/30/97 9:57a Allender
56 * basic Redbook audio implemented.
58 * 1 1/28/97 9:54a Allender
81 #define HSG_CD_PLAYBACK 0
82 #define MSF_CD_PLAYBACK 1
87 typedef struct CDTrack {
92 int RBCDROM_State = 0; // CD is not used = 0
94 // CD is already used = -1
96 static CDTrack CDTrackInfo[255];
97 static UINT CDDiscID = 0;
99 static int RedBookEnabled=0, RedBookInstalled = 0;
100 static UINT CDDeviceID = 0;
101 static DWORD CDNumTracks = 0;
102 static fix Playback_Start_Time = 0;
103 static fix Playback_Pause_Time = 0;
104 static fix Playback_Length = 0;
105 static int Playback_first_track,Playback_last_track;
106 static int AUXDevice = -1;
107 static char MCIErrorMsg[256];
109 // Function Prototypes
111 unsigned long lsn_to_msf(unsigned long lsn);
112 unsigned long msf_to_lsn(unsigned long msf);
113 unsigned long msf_add(unsigned long msf1, unsigned long msf2);
114 unsigned long msf_sub(unsigned long msf1, unsigned long msf2);
116 UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1);
124 if (RedBookInstalled) {
125 //RBAStop(); // removed by MWA 2/3 -- will probably be handled by higher level code
126 mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL);
128 RedBookInstalled = 0;
135 MCI_OPEN_PARMS mciOpenParms;
136 MCI_SET_PARMS mciSetParms;
141 // removed by MWA since we can't seem to get this to work under NT...needs a little work I guess
145 num_devs = auxGetNumDevs();
147 // Find CD-AUDIO device if there is one.
148 for (i = 0; i < num_devs; i++)
151 auxGetDevCaps(i, &caps, sizeof(caps));
152 if (caps.wTechnology == AUXCAPS_CDAUDIO) {
154 mprintf(("CD operating on AUX %d.\n", AUXDevice));
159 if (AUXDevice == -1) {
160 mprintf(("Unable to find CD-AUDIO device. No Redbook music.\n"));
166 // We need to identify that a cd audio driver exists
167 mciOpenParms.lpstrDeviceType = NOX("cdaudio");
169 retval = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD)(LPVOID)&mciOpenParms);
171 if (retval == MCIERR_MUST_USE_SHAREABLE) RBCDROM_State = -1;
172 else RBCDROM_State = 0;
173 mciGetErrorString(retval, MCIErrorMsg, 256);
174 mprintf(("RBA (%x) MCI:%s.\n", retval, MCIErrorMsg));
178 // Now we need to set the time format to MSF for Redbook Compatablity.
179 RedBookInstalled = 1;
181 CDDeviceID = mciOpenParms.wDeviceID;
183 // mwa ?? logentry("Initializing Redbook device %d.\n", CDDeviceID);
185 mciSetParms.dwTimeFormat = MCI_FORMAT_MSF;
186 retval = mciSendCommand(CDDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&mciSetParms);
188 mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL);
189 mprintf(("Error %d. Unable to set Redbook Format.\n", retval));
192 else RedBookEnabled = 1;
197 void RBARegisterCD(void)
199 DWORD i, numtracks, retval = 0;
200 MCI_STATUS_PARMS mciStatusParms;
202 // Insure that CD is Redbook, then get track information
203 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
204 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
205 (DWORD)(LPVOID)&mciStatusParms);
207 mciGetErrorString(retval, MCIErrorMsg, 256);
208 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
213 CDNumTracks = numtracks = mciStatusParms.dwReturn;
215 for (i = 0; i < numtracks; i++)
217 mciStatusParms.dwTrack = i+1;
218 mciStatusParms.dwItem = MCI_STATUS_POSITION;
219 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms);
221 mprintf(("Could not retrieve information on track %d.\n", i+1));
223 CDTrackInfo[i].msf = mciStatusParms.dwReturn;
225 mciStatusParms.dwTrack = i+1;
226 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
227 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms);
229 mprintf(("Couldn't get length of track %d.\n", i+1));
231 CDTrackInfo[i].length = mciStatusParms.dwReturn;
233 // 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));
236 CDDiscID = GetCDDiscID();
240 long RBAGetDeviceStatus()
246 int get_track_time(int first,int last)
248 unsigned long playlen;
251 playlen = msf_sub(msf_add(CDTrackInfo[last-1].msf,CDTrackInfo[last-1].length), CDTrackInfo[first-1].msf);
253 min = MCI_MSF_MINUTE(playlen);
254 sec = MCI_MSF_SECOND(playlen);
260 int RBAPlayTrack(int track)
262 MCI_PLAY_PARMS mciPlayParms;
266 // Register CD if media has changed
267 if (!RedBookEnabled) return 0;
268 if (RBACheckMediaChange()) {
269 mprintf(("Unable to play track due to CD change.\n"));
274 min = min + MCI_MSF_MINUTE(CDTrackInfo[track-1].msf) + MCI_MSF_MINUTE(CDTrackInfo[track-1].length);
275 mciPlayParms.dwFrom = CDTrackInfo[track-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0);
276 mciPlayParms.dwTo = msf_add(CDTrackInfo[track-1].msf, CDTrackInfo[track-1].length);
277 retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
280 mciGetErrorString(retval, MCIErrorMsg, 256);
281 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
282 mprintf(("Unable to play CD track %d.\n", track));
286 Playback_Length = i2f(get_track_time(track,track));
287 Playback_Start_Time = timer_get_fixed_seconds();
289 Playback_first_track = Playback_last_track = track;
291 mprintf( ("Playing Track %d (len: %d secs)\n", track, f2i(Playback_Length)) );
298 int RBAPlayTracks(int start, int end)
300 MCI_PLAY_PARMS mciPlayParms;
303 Playback_Start_Time = 0;
306 // Register CD if media has changed
307 if (!RedBookEnabled) return 0;
308 if (RBACheckMediaChange()) {
309 mprintf(("Unable to play track due to CD change.\n"));
314 mciPlayParms.dwFrom = CDTrackInfo[start-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0);
315 mciPlayParms.dwTo = msf_add(CDTrackInfo[end-1].msf, CDTrackInfo[end-1].length);
316 retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
319 mciGetErrorString(retval, MCIErrorMsg, 256);
320 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
321 mprintf(("Unable to play CD tracks %d-%d.\n", start,end));
325 Playback_Length = i2f(get_track_time(start,end));
326 Playback_Start_Time = timer_get_fixed_seconds();
328 Playback_first_track = start;
329 Playback_last_track = end;
331 mprintf( ("Playing Tracks %d-%d (len: %d secs)\n", start,end,f2i(Playback_Length)) );
342 if (!RedBookEnabled) return;
344 retval = mciSendCommand(CDDeviceID, MCI_STOP,0,NULL);
346 mciGetErrorString(retval, MCIErrorMsg, 256);
347 mprintf(("RBA MCI:%s.\n", MCIErrorMsg));
348 mprintf(("CD Stop command failed.\n"));
353 void RBASetStereoAudio(RBACHANNELCTL *channels)
359 void RBASetQuadAudio(RBACHANNELCTL *channels)
365 void RBAGetAudioInfo(RBACHANNELCTL *channels)
371 static unsigned rba_paused_head_loc = 0;
376 MCI_GENERIC_PARMS mciGenParms;
380 if (!RedBookEnabled) return;
382 rba_paused_head_loc = RBAGetHeadLoc(&min, &sec, &frame);
384 // mwa ??? mciGenParms.dwCallback = GetLibraryWindow();
385 retval = mciSendCommand(CDDeviceID, MCI_PAUSE, MCI_NOTIFY,
386 (DWORD)(LPVOID)&mciGenParms);
388 mprintf(("ERROR: Unable to pause CD.\n"));
391 mprintf(("Pausing CD...\n"));
392 Playback_Pause_Time = timer_get_fixed_seconds();
401 MCI_PLAY_PARMS mciPlayParms;
403 if (!RedBookEnabled) return 0;
405 if (RBACheckMediaChange()) {
407 return RBA_MEDIA_CHANGED;
410 mciPlayParms.dwFrom = rba_paused_head_loc; // MCI_MAKE_TMSF(track, 0, 0, 0);
411 mciPlayParms.dwTo = msf_add(CDTrackInfo[Playback_last_track-1].msf,
412 CDTrackInfo[Playback_last_track-1].length);
413 retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
416 mprintf(("ERROR: Resume CD play failed.\n"));
419 Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time;
425 void RBASetChannelVolume(int channel, int volume)
429 if ( AUXDevice == -1 ) // added because we might not be able to do this under NT
432 volume = volume << 8;
435 vol = MAKELONG(0,volume);
436 else if (channel == 1)
437 vol = MAKELONG(volume,0);
439 auxSetVolume(AUXDevice, vol);
443 void RBASetVolume(int volume)
448 if ( AUXDevice == -1 ) // added because we might not be able to do this under NT
451 wvol = (unsigned short)((volume) << 8);
453 vol = MAKELONG(wvol, wvol);
454 auxSetVolume(AUXDevice, vol);
458 long RBAGetHeadLoc(int *min, int *sec, int *frame)
460 MCI_STATUS_PARMS mciStatusParms;
463 if (!RedBookEnabled) return 0;
465 if (RBACheckMediaChange())
466 return RBA_MEDIA_CHANGED;
468 mciStatusParms.dwItem = MCI_STATUS_POSITION;
469 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms);
472 mprintf(("Couldn't get location of CD head.\n"));
475 *min = MCI_MSF_MINUTE(mciStatusParms.dwReturn);
476 *sec = MCI_MSF_SECOND(mciStatusParms.dwReturn);
477 *frame = MCI_MSF_FRAME(mciStatusParms.dwReturn);
479 return mciStatusParms.dwReturn;
486 int delta_time; //in seconds
488 if (!RBAPeekPlayStatus())
491 delta_time = f2i(timer_get_fixed_seconds()-Playback_Start_Time+f1_0/2);
493 for (track=Playback_first_track;track<=Playback_last_track && delta_time>0;track++) {
495 delta_time -= get_track_time(track,track);
501 Assert(track <= Playback_last_track);
507 int RBAPeekPlayStatus()
509 //@@ MCI_STATUS_PARMS mciStatusParms;
512 if (!RedBookEnabled) return 0;
515 if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0;
519 //@@ mciStatusParms.dwItem = MCI_STATUS_MODE;
520 //@@ retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms);
522 //@@ mprintf((0, "Unable to obtain current status of CD.\n"));
525 //@@ if (mciStatusParms.dwReturn == MCI_MODE_PLAY) return 1;
533 if (RedBookEnabled < 1) return 0;
541 mprintf(("Enabling Redbook...\n"));
548 mprintf(("Disabling Redbook...\n"));
552 int RBAGetNumberOfTracks(void)
554 MCI_STATUS_PARMS mciStatusParms;
557 if (!RedBookEnabled) return 0;
559 if (RBACheckMediaChange())
562 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
563 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
564 (DWORD)(LPVOID)&mciStatusParms);
567 mprintf(("Get number of CD tracks failed.\n"));
571 return mciStatusParms.dwReturn;
575 // RB functions: Internal to RBA library -----------------------------
577 int RBACheckMediaChange()
579 if (!RedBookEnabled) return 0;
581 if (CDDiscID != GetCDDiscID() || !CDDiscID) return 1;
586 // Converts Logical Sector Number to Minutes Seconds Frame Format
588 unsigned long lsn_to_msf(unsigned long lsn)
590 unsigned long min,sec,frame;
599 return((min << 16) + (sec << 8) + (frame << 0));
602 // convert minutes seconds frame format to a logical sector number.
604 unsigned long msf_to_lsn(unsigned long msf)
606 unsigned long min,sec,frame;
608 min = (msf >> 16) & 0xFF;
609 sec = (msf >> 8) & 0xFF;
610 frame = (msf >> 0) & 0xFF;
612 return( (min * 60*75) + (sec * 75) + frame - 150 );
615 unsigned long msf_add(unsigned long msf1, unsigned long msf2)
617 uint min1, sec1, frame1;
618 uint min2, sec2, frame2;
620 // we don't take frames into account, which may not be right.
621 min1 = MCI_MSF_MINUTE(msf1);
622 sec1 = MCI_MSF_SECOND(msf1);
623 frame1 = MCI_MSF_FRAME(msf1);
628 if ((sec1 + MCI_MSF_SECOND(msf2)) > 59) {
629 sec2 = (sec1+MCI_MSF_SECOND(msf2)) - 60;
632 else sec2 = sec1 + MCI_MSF_SECOND(msf2);
633 min2 = min1+MCI_MSF_MINUTE(msf2);
636 // logentry("msf_add:(%d:%d)\n", min2, sec2);
638 return MCI_MAKE_MSF(min2, sec2, 0);
642 unsigned long msf_sub(unsigned long msf1, unsigned long msf2)
644 int min1, sec1, frame1;
645 int min2, sec2, frame2;
647 // we don't take frames into account, which may not be right.
648 min1 = MCI_MSF_MINUTE(msf1);
649 sec1 = MCI_MSF_SECOND(msf1);
650 frame1 = MCI_MSF_FRAME(msf1);
655 if ((sec1 - (int)MCI_MSF_SECOND(msf2)) < 0) {
656 sec2 = 60 - ((int)MCI_MSF_SECOND(msf2)-sec1);
657 min1--; // This is a shortcut to min2 = min1-(MSF_MIN(msf2)-1)
659 else sec2 = sec1 - (int)MCI_MSF_SECOND(msf2);
660 min2 = min1-(int)MCI_MSF_MINUTE(msf2);
663 // logentry("msf_sub:(%d:%d)\n", min2, sec2);
665 return MCI_MAKE_MSF(min2, sec2, 0);
669 unsigned long msf_to_sec(unsigned long msf)
671 unsigned long min,sec,frame;
673 min = (msf >> 16) & 0xFF;
674 sec = (msf >> 8) & 0xFF;
675 frame = (msf >> 0) & 0xFF;
677 return (min*60) + sec;
683 MCI_STATUS_PARMS mciStatusParms;
685 unsigned long msflen, tracks, msftrack1;
687 mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
688 if ((retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
689 (DWORD)(LPVOID)&mciStatusParms))>0) {
690 mprintf(("RBA: Get number of CD tracks failed.\n"));
693 tracks = mciStatusParms.dwReturn;
695 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
696 if ((retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM,
697 (DWORD)(LPVOID)&mciStatusParms))>0) {
698 mprintf(("RBA: Get media length failed.\n"));
701 msflen = mciStatusParms.dwReturn;
703 mciStatusParms.dwTrack = 1;
704 mciStatusParms.dwItem = MCI_STATUS_POSITION;
705 if ( (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms)) >0) {
706 mprintf(("Could not retrieve information on track %d.\n", 1));
709 msftrack1 = mciStatusParms.dwReturn;
711 return MakeCDDiscID(tracks, msflen, msftrack1);
715 UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1)
719 code = (UINT)(msf_to_sec(msftrack1) << 19);
720 code |= (UINT)(msf_to_sec(msflen) << 6);
722 code |= (UINT)(tracks&0xffffffc0);