]> icculus.org git repositories - btb/d2x.git/blob - unused/win95/rbaudio.c
Import of d2x-0.0.8
[btb/d2x.git] / unused / win95 / rbaudio.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #pragma off (unreferenced)
16 static char rcsid[] = "$Id: rbaudio.c,v 1.1.1.1 2001-01-19 03:30:15 bradleyb Exp $";
17 #pragma on (unreferenced)
18
19 #define _WIN32
20 #define WIN32_LEAN_AND_MEAN
21
22 #include <windows.h>
23 #include <mmsystem.h>
24
25 #include <stdlib.h>
26 #include <string.h>
27
28 #include "winapp.h"
29 #include "mono.h"
30 #include "error.h"
31 #include "rbaudio.h"
32 #include "fix.h"
33 #include "winapp.h"
34
35
36 #define HSG_CD_PLAYBACK 0
37 #define MSF_CD_PLAYBACK 1
38
39
40 #pragma pack(1)
41
42 typedef struct CDTrack {
43         DWORD msf;
44         DWORD length;
45 } CDTrack;
46
47 int RBCDROM_State = 0;                                                  // CD is not used = 0
48                                                                                                                 // CD is used = 1
49                                                                                                                 // CD is already used = -1
50
51 static CDTrack  CDTrackInfo[255];
52 static UINT CDDiscID = 0;
53
54 static int              RedBookEnabled=0, RedBookInstalled = 0;
55 static UINT     CDDeviceID = 0;
56 static DWORD    CDNumTracks = 0;
57 static fix Playback_Start_Time = 0;
58 static fix Playback_Pause_Time = 0;
59 static fix Playback_Length = 0;
60 static int Playback_first_track,Playback_last_track;
61 static int AUXDevice = -1;
62 static char MCIErrorMsg[256];
63
64 //      Function Prototypes
65
66 unsigned long lsn_to_msf(unsigned long lsn);
67 unsigned long msf_to_lsn(unsigned long msf);
68 unsigned long msf_add(unsigned long msf1, unsigned long msf2);
69 unsigned long msf_sub(unsigned long msf1, unsigned long msf2);
70
71 UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1);
72 UINT GetCDDiscID();
73
74
75 //      Functions
76
77 void RBClose()
78 {
79         if (RedBookInstalled) {
80                 mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL);
81                 RedBookEnabled = 0;
82                 RedBookInstalled = 0;
83         }
84 }
85
86
87 void RBAInit(void)
88 {
89         MCI_OPEN_PARMS  mciOpenParms;
90         MCI_SET_PARMS   mciSetParms;
91         DWORD                           retval = 0;
92         int                             num_devs, i;
93
94         atexit(RBClose);
95
96         num_devs = auxGetNumDevs();
97         
98 //      Find CD-AUDIO device if there is one.
99         for (i = 0; i < num_devs; i++)
100         {
101                 AUXCAPS caps;
102                 auxGetDevCaps(i, &caps, sizeof(caps));
103                 if (caps.wTechnology == AUXCAPS_CDAUDIO) {
104                         AUXDevice = i;
105                         mprintf((0, "CD operating on AUX %d.\n", AUXDevice));
106                         break;
107                 }
108         }
109                                         
110         if (AUXDevice == -1) {
111                 mprintf((1, "Unable to find CD-AUDIO device. No Redbook music.\n"));
112                 RedBookEnabled = 0;
113                 return;
114         }
115
116 //      We need to identify that a cd audio driver exists
117         mciOpenParms.lpstrDeviceType = "cdaudio";
118
119         retval = mciSendCommand(NULL, MCI_OPEN, MCI_OPEN_TYPE | MCI_OPEN_SHAREABLE, (DWORD)(LPVOID)&mciOpenParms);
120         if (retval) {
121                 if (retval == MCIERR_MUST_USE_SHAREABLE) RBCDROM_State = -1;
122                 else RBCDROM_State = 0;
123                 mciGetErrorString(retval, MCIErrorMsg, 256);
124                 mprintf((1,"RBA (%x) MCI:%s.\n", retval, MCIErrorMsg));
125                 RedBookEnabled = 0;
126         }
127         else {
128         // Now we need to set the time format to MSF for Redbook Compatablity.
129                 RedBookInstalled = 1;
130                 RBCDROM_State = 1;
131                 CDDeviceID = mciOpenParms.wDeviceID;
132
133                 logentry("Initializing Redbook device %d.\n", CDDeviceID);
134
135                 mciSetParms.dwTimeFormat = MCI_FORMAT_MSF;
136                 retval = mciSendCommand(CDDeviceID, MCI_SET, MCI_SET_TIME_FORMAT, (DWORD)(LPVOID)&mciSetParms);
137                 if (retval) {
138                         mciSendCommand(CDDeviceID, MCI_CLOSE, 0, NULL);
139                         mprintf((0, "Error %d.  Unable to set Redbook Format.\n", retval));
140                         RedBookEnabled = 0;
141                 }
142                 else RedBookEnabled = 1;
143         }
144 }
145
146
147 void RBARegisterCD(void)
148 {
149         DWORD                                   i, numtracks, retval = 0;
150         MCI_STATUS_PARMS        mciStatusParms;
151
152 //      Insure that CD is Redbook, then get track information
153         mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; 
154         retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, 
155                                                 (DWORD)(LPVOID)&mciStatusParms);
156         if (retval) {
157                 mciGetErrorString(retval, MCIErrorMsg, 256);
158                 mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg));
159                 CDNumTracks = 0;
160                 return;
161         }
162         
163         CDNumTracks = numtracks = mciStatusParms.dwReturn;
164
165         for (i = 0; i < numtracks; i++)
166         {
167                 mciStatusParms.dwTrack = i+1;
168                 mciStatusParms.dwItem = MCI_STATUS_POSITION;
169                 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms);   
170                 if (retval) {
171                         mprintf((0, "Could not retrieve information on track %d.\n", i+1));
172                 }
173                 CDTrackInfo[i].msf = mciStatusParms.dwReturn;
174                 
175                 mciStatusParms.dwTrack = i+1;
176                 mciStatusParms.dwItem = MCI_STATUS_LENGTH;
177                 retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms);
178                 if (retval) { 
179                         mprintf((0, "Couldn't get length of track %d.\n", i+1));
180                 }
181                 CDTrackInfo[i].length = mciStatusParms.dwReturn;
182
183 //              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));
184         }       
185
186         CDDiscID = GetCDDiscID();
187
188
189
190 long RBAGetDeviceStatus()
191 {
192         return 0;
193 }
194
195
196 int get_track_time(int first,int last)
197 {
198         unsigned long playlen;
199         int min, sec;
200
201         playlen = msf_sub(msf_add(CDTrackInfo[last-1].msf,CDTrackInfo[last-1].length), CDTrackInfo[first-1].msf);
202
203         min   = MCI_MSF_MINUTE(playlen);
204         sec   = MCI_MSF_SECOND(playlen);
205
206         return (min*60+sec);
207 }
208
209
210 int RBAPlayTrack(int track)
211 {
212         MCI_PLAY_PARMS  mciPlayParms;
213         int min=0; 
214         int retval;
215
216 //      Register CD if media has changed
217         if (!RedBookEnabled) return 0;
218         if (RBACheckMediaChange()) {
219                 mprintf((0, "Unable to play track due to CD change.\n"));
220                 return 0;
221         }
222
223 //      Play the track
224         min = min + MCI_MSF_MINUTE(CDTrackInfo[track-1].msf) + MCI_MSF_MINUTE(CDTrackInfo[track-1].length);
225         mciPlayParms.dwFrom = CDTrackInfo[track-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0);
226         mciPlayParms.dwTo = msf_add(CDTrackInfo[track-1].msf, CDTrackInfo[track-1].length);
227         retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
228
229         if (retval) {
230                 mciGetErrorString(retval, MCIErrorMsg, 256);
231                 mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg));
232                 mprintf((0, "Unable to play CD track %d.\n", track));
233                 return 0;
234         }
235         else {
236                 Playback_Length = i2f(get_track_time(track,track));
237                 Playback_Start_Time = timer_get_fixed_seconds();
238
239                 Playback_first_track = Playback_last_track = track;
240
241                 mprintf( (0,"Playing Track %d (len: %d secs)\n", track, f2i(Playback_Length)) );
242         }
243
244         return 1;
245 }
246
247
248 int RBAPlayTracks(int start, int end)
249 {
250         MCI_PLAY_PARMS  mciPlayParms;
251         int retval;
252
253         Playback_Start_Time = 0;
254         Playback_Length = 0;
255
256 //      Register CD if media has changed
257         if (!RedBookEnabled) return 0;
258         if (RBACheckMediaChange()) {
259                 mprintf((0, "Unable to play track due to CD change.\n"));
260                 return 0;
261         }
262
263 //      Play the track
264         mciPlayParms.dwFrom = CDTrackInfo[start-1].msf; // MCI_MAKE_TMSF(track, 0, 0, 0);
265         mciPlayParms.dwTo = msf_add(CDTrackInfo[end-1].msf, CDTrackInfo[end-1].length);
266         retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
267
268         if (retval) {
269                 mciGetErrorString(retval, MCIErrorMsg, 256);
270                 mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg));
271                 mprintf((0, "Unable to play CD tracks %d-%d.\n", start,end));
272                 return 0;               
273         }
274         else {
275                 Playback_Length = i2f(get_track_time(start,end));
276                 Playback_Start_Time = timer_get_fixed_seconds();
277
278                 Playback_first_track = start;
279                 Playback_last_track = end;
280
281                 mprintf( (0,"Playing Tracks %d-%d (len: %d secs)\n", start,end,f2i(Playback_Length)) );
282         }
283
284         return 1;
285 }
286
287
288 void RBAStop()
289 {
290         int retval;
291
292         if (!RedBookEnabled) return;
293         
294         retval = mciSendCommand(CDDeviceID, MCI_STOP,0,NULL);
295         if (retval) {
296                 mciGetErrorString(retval, MCIErrorMsg, 256);
297                 mprintf((1,"RBA MCI:%s.\n", MCIErrorMsg));
298                 mprintf((0, "CD Stop command failed.\n"));
299         } 
300 }
301
302
303 void RBASetStereoAudio(RBACHANNELCTL *channels)
304 {
305
306 }
307
308
309 void RBASetQuadAudio(RBACHANNELCTL *channels)
310 {
311
312 }
313
314
315 void RBAGetAudioInfo(RBACHANNELCTL *channels)
316 {
317
318 }
319
320
321 static unsigned rba_paused_head_loc = 0;
322
323 void RBAPause()
324 {
325         int retval;
326         MCI_GENERIC_PARMS mciGenParms;
327         int frame, sec, min;
328
329
330   if (!RedBookEnabled) return;
331
332         rba_paused_head_loc = RBAGetHeadLoc(&min, &sec, &frame);        
333
334         mciGenParms.dwCallback = GetLibraryWindow();
335         retval = mciSendCommand(CDDeviceID, MCI_PAUSE, MCI_NOTIFY, 
336                 (DWORD)(LPVOID)&mciGenParms);
337         if (retval)     {
338                 mprintf((1,"ERROR: Unable to pause CD.\n"));
339         }
340         else {
341                 mprintf((0, "Pausing CD...\n"));
342                 Playback_Pause_Time = timer_get_fixed_seconds();
343         }
344
345 }
346
347
348 int RBAResume()
349 {
350         int retval;
351         MCI_PLAY_PARMS mciPlayParms;
352
353         if (!RedBookEnabled) return 0;
354    
355         if (RBACheckMediaChange()) {
356                 RBARegisterCD();
357                 return RBA_MEDIA_CHANGED;
358         }
359
360         mciPlayParms.dwFrom = rba_paused_head_loc; // MCI_MAKE_TMSF(track, 0, 0, 0);
361         mciPlayParms.dwTo = msf_add(CDTrackInfo[Playback_last_track-1].msf, 
362                                                                         CDTrackInfo[Playback_last_track-1].length);
363         retval = mciSendCommand(CDDeviceID, MCI_PLAY, MCI_FROM | MCI_TO, (DWORD)(LPVOID)&mciPlayParms);
364
365         if (retval) {
366                 mprintf((1, "ERROR: Resume CD play failed.\n"));
367         }
368         else 
369                 Playback_Start_Time += timer_get_fixed_seconds() - Playback_Pause_Time;
370
371         return 1;
372 }
373
374
375 void RBASetChannelVolume(int channel, int volume) 
376 {
377         DWORD vol;
378         
379         volume = volume << 8;
380         
381         if (channel == 0) 
382                 vol = MAKELONG(0,volume);
383         else if (channel == 1) 
384                 vol = MAKELONG(volume,0);
385
386         auxSetVolume(AUXDevice, vol);
387 }
388
389
390 void RBASetVolume(int volume)
391 {
392         DWORD vol;
393         WORD wvol;
394
395         wvol = (WORD)(volume) << 8;
396
397         vol = MAKELONG(wvol, wvol);
398         auxSetVolume(AUXDevice, vol);
399 }
400
401
402 long RBAGetHeadLoc(int *min, int *sec, int *frame)
403 {
404         MCI_STATUS_PARMS mciStatusParms;
405         int retval;
406
407         if (!RedBookEnabled) return 0;
408
409         if (RBACheckMediaChange())
410                 return RBA_MEDIA_CHANGED;
411
412         mciStatusParms.dwItem = MCI_STATUS_POSITION;
413         retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms);
414         
415         if (retval) {
416                 mprintf((0, "Couldn't get location of CD head.\n"));
417         }
418
419         *min = MCI_MSF_MINUTE(mciStatusParms.dwReturn);
420         *sec = MCI_MSF_SECOND(mciStatusParms.dwReturn);
421         *frame = MCI_MSF_FRAME(mciStatusParms.dwReturn);
422
423         return mciStatusParms.dwReturn;
424 }               
425
426
427 int RBAGetTrackNum()
428 {
429         int track;
430         int delta_time; //in seconds
431
432         if (!RBAPeekPlayStatus())
433                 return 0;
434
435         delta_time = f2i(timer_get_fixed_seconds()-Playback_Start_Time+f1_0/2);
436
437         for (track=Playback_first_track;track<=Playback_last_track && delta_time>0;track++) {
438
439                 delta_time -= get_track_time(track,track);
440
441                 if (delta_time < 0)
442                         break;
443         }
444
445         Assert(track <= Playback_last_track);
446
447         return track;
448 }
449
450
451 int RBAPeekPlayStatus()
452 {
453 //@@    MCI_STATUS_PARMS mciStatusParms;
454 //@@    int retval;
455
456         if (!RedBookEnabled) return 0;
457
458
459         if ((timer_get_fixed_seconds()-Playback_Start_Time) > Playback_Length) return 0;
460         else return 1; 
461
462
463 //@@    mciStatusParms.dwItem = MCI_STATUS_MODE;
464 //@@    retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, (DWORD)(LPVOID)&mciStatusParms);
465 //@@    if (retval) {
466 //@@            mprintf((0, "Unable to obtain current status of CD.\n"));
467 //@@    }
468 //@@    
469 //@@    if (mciStatusParms.dwReturn == MCI_MODE_PLAY) return 1;
470 //@@    else return 0;
471 }
472         
473
474
475 int RBAEnabled()
476 {
477         if (RedBookEnabled < 1) return 0;
478         else return 1;
479 }
480
481
482 void RBAEnable()
483 {
484         RedBookEnabled = 1;
485         mprintf((0, "Enabling Redbook...\n"));
486 }
487
488
489 void RBADisable()
490 {
491         RedBookEnabled = 0;
492         mprintf((0, "Disabling Redbook...\n"));
493 }
494
495
496 int RBAGetNumberOfTracks(void)
497 {
498         MCI_STATUS_PARMS        mciStatusParms;
499         int retval;     
500
501         if (!RedBookEnabled) return 0;
502
503         if (RBACheckMediaChange())  
504                 RBARegisterCD();
505                 
506         mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; 
507         retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, 
508                                                 (DWORD)(LPVOID)&mciStatusParms);
509
510         if (retval) {
511                 mprintf((0, "Get number of CD tracks failed.\n"));
512                 return 0;
513         }
514
515         return mciStatusParms.dwReturn;
516 }
517
518
519 //      RB functions:  Internal to RBA library  -----------------------------
520
521 int RBACheckMediaChange()
522 {
523         if (!RedBookEnabled) return 0;
524
525         if (CDDiscID != GetCDDiscID() || !CDDiscID) return 1;
526         else return 0;
527 }
528
529
530 //      Converts Logical Sector Number to Minutes Seconds Frame Format
531
532 unsigned long lsn_to_msf(unsigned long lsn)
533 {
534    unsigned long min,sec,frame;
535    lsn += 150;
536
537         min   =  lsn / (60*75);
538    lsn   =  lsn % (60*75);
539         sec   =  lsn / 75;
540    lsn   =  lsn % 75;
541         frame =  lsn;
542
543         return((min << 16) + (sec << 8) + (frame << 0));
544 }
545
546 // convert minutes seconds frame format to a logical sector number.
547
548 unsigned long msf_to_lsn(unsigned long msf)
549 {
550         unsigned long min,sec,frame;
551
552         min   = (msf >> 16) & 0xFF;
553    sec   = (msf >>  8) & 0xFF;
554    frame = (msf >>  0) & 0xFF;
555
556         return(  (min * 60*75) + (sec * 75) + frame - 150  );
557 }
558
559 unsigned long msf_add(unsigned long msf1, unsigned long msf2)
560 {
561         uint min1, sec1, frame1;
562         uint min2, sec2, frame2;
563
564 //      we don't take frames into account, which may not be right.
565         min1 = MCI_MSF_MINUTE(msf1);
566         sec1 = MCI_MSF_SECOND(msf1);
567         frame1 = MCI_MSF_FRAME(msf1);
568
569         frame2 = 0;
570         min2 = 0;
571
572         if ((sec1 + MCI_MSF_SECOND(msf2)) > 59) {
573                 sec2 = (sec1+MCI_MSF_SECOND(msf2)) - 60;
574                 min1++;
575         }
576         else sec2 = sec1 + MCI_MSF_SECOND(msf2);
577         min2 = min1+MCI_MSF_MINUTE(msf2);
578         frame2 = 0;
579         
580 //      logentry("msf_add:(%d:%d)\n", min2, sec2);
581
582         return MCI_MAKE_MSF(min2, sec2, 0);
583
584
585
586 unsigned long msf_sub(unsigned long msf1, unsigned long msf2)
587 {
588         int min1, sec1, frame1;
589         int min2, sec2, frame2;
590
591 //      we don't take frames into account, which may not be right.
592         min1 = MCI_MSF_MINUTE(msf1);
593         sec1 = MCI_MSF_SECOND(msf1);
594         frame1 = MCI_MSF_FRAME(msf1);
595
596         frame2 = 0;
597         min2 = 0;
598
599         if ((sec1 - (int)MCI_MSF_SECOND(msf2)) < 0) {
600                 sec2 = 60 - ((int)MCI_MSF_SECOND(msf2)-sec1);
601                 min1--;                                 // This is a shortcut to min2 = min1-(MSF_MIN(msf2)-1)
602         }
603         else sec2 = sec1 - (int)MCI_MSF_SECOND(msf2);
604         min2 = min1-(int)MCI_MSF_MINUTE(msf2);
605         frame2 = 0;
606
607 //      logentry("msf_sub:(%d:%d)\n", min2, sec2);
608         
609         return MCI_MAKE_MSF(min2, sec2, 0);
610
611
612
613 unsigned long msf_to_sec(unsigned long msf)
614 {
615         unsigned long min,sec,frame;
616
617         min   = (msf >> 16) & 0xFF;
618    sec   = (msf >>  8) & 0xFF;
619    frame = (msf >>  0) & 0xFF;
620
621         return (min*60) + sec;  
622 }
623
624
625 UINT GetCDDiscID()
626 {
627         MCI_STATUS_PARMS        mciStatusParms;
628         int retval;     
629         unsigned long msflen, tracks, msftrack1;
630
631         mciStatusParms.dwItem = MCI_STATUS_NUMBER_OF_TRACKS; 
632         if (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, 
633                                                 (DWORD)(LPVOID)&mciStatusParms)) {
634                 mprintf((1, "RBA: Get number of CD tracks failed.\n"));
635                 return 0;
636         }
637         tracks = mciStatusParms.dwReturn;
638
639         mciStatusParms.dwItem = MCI_STATUS_LENGTH;
640         if (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM, 
641                                                 (DWORD)(LPVOID)&mciStatusParms)) {
642                 mprintf((1,     "RBA: Get media length failed.\n"));
643                 return 0;
644         }
645         msflen = mciStatusParms.dwReturn;
646
647         mciStatusParms.dwTrack = 1;
648         mciStatusParms.dwItem = MCI_STATUS_POSITION;
649         if (retval = mciSendCommand(CDDeviceID, MCI_STATUS, MCI_STATUS_ITEM | MCI_TRACK, (DWORD)(LPVOID)&mciStatusParms)) {     
650                 mprintf((1, "Could not retrieve information on track %d.\n", 1));
651                 return 0;
652         }
653         msftrack1 = mciStatusParms.dwReturn;
654
655         return MakeCDDiscID(tracks, msflen, msftrack1);
656 }
657
658
659 UINT MakeCDDiscID(int tracks, unsigned long msflen, unsigned long msftrack1)
660 {
661         UINT code=0;
662         
663         code = (UINT)(msf_to_sec(msftrack1) << 19);
664         code |= (UINT)(msf_to_sec(msflen) << 6);
665
666         code |= (UINT)(tracks&0xffffffc0);
667
668         return code;
669 }
670         
671
672