]> git.xonotic.org Git - xonotic/darkplaces.git/blob - cd_shared.c
Make CDAudio_Play_byName static.
[xonotic/darkplaces.git] / cd_shared.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20 // Quake is a trademark of Id Software, Inc., (c) 1996 Id Software, Inc. All
21 // rights reserved.
22
23 #include "quakedef.h"
24 #include "cdaudio.h"
25 #include "sound.h"
26
27 // used by menu to ghost CD audio slider
28 cvar_t cdaudioinitialized = {CVAR_READONLY,"cdaudioinitialized","0","indicates if CD Audio system is active"};
29
30 #define MAX_PLAYLISTS 10
31 int music_playlist_active = -1;
32 int music_playlist_playing = 0; // 0 = not playing, 1 = playing, -1 = tried and failed
33
34 cvar_t music_playlist_index = {0, "music_playlist_index", "-1", "selects which of the music_playlist_ variables is the active one, -1 disables playlists"};
35 cvar_t music_playlist_list[MAX_PLAYLISTS] =
36 {
37         {0, "music_playlist_list0", "", "list of tracks to play"},
38         {0, "music_playlist_list1", "", "list of tracks to play"},
39         {0, "music_playlist_list2", "", "list of tracks to play"},
40         {0, "music_playlist_list3", "", "list of tracks to play"},
41         {0, "music_playlist_list4", "", "list of tracks to play"},
42         {0, "music_playlist_list5", "", "list of tracks to play"},
43         {0, "music_playlist_list6", "", "list of tracks to play"},
44         {0, "music_playlist_list7", "", "list of tracks to play"},
45         {0, "music_playlist_list8", "", "list of tracks to play"},
46         {0, "music_playlist_list9", "", "list of tracks to play"}
47 };
48 cvar_t music_playlist_current[MAX_PLAYLISTS] =
49 {
50         {0, "music_playlist_current0", "0", "current track index to play in list"},
51         {0, "music_playlist_current1", "0", "current track index to play in list"},
52         {0, "music_playlist_current2", "0", "current track index to play in list"},
53         {0, "music_playlist_current3", "0", "current track index to play in list"},
54         {0, "music_playlist_current4", "0", "current track index to play in list"},
55         {0, "music_playlist_current5", "0", "current track index to play in list"},
56         {0, "music_playlist_current6", "0", "current track index to play in list"},
57         {0, "music_playlist_current7", "0", "current track index to play in list"},
58         {0, "music_playlist_current8", "0", "current track index to play in list"},
59         {0, "music_playlist_current9", "0", "current track index to play in list"},
60 };
61 cvar_t music_playlist_random[MAX_PLAYLISTS] =
62 {
63         {0, "music_playlist_random0", "0", "enables random play order if 1, 0 is sequential play"},
64         {0, "music_playlist_random1", "0", "enables random play order if 1, 0 is sequential play"},
65         {0, "music_playlist_random2", "0", "enables random play order if 1, 0 is sequential play"},
66         {0, "music_playlist_random3", "0", "enables random play order if 1, 0 is sequential play"},
67         {0, "music_playlist_random4", "0", "enables random play order if 1, 0 is sequential play"},
68         {0, "music_playlist_random5", "0", "enables random play order if 1, 0 is sequential play"},
69         {0, "music_playlist_random6", "0", "enables random play order if 1, 0 is sequential play"},
70         {0, "music_playlist_random7", "0", "enables random play order if 1, 0 is sequential play"},
71         {0, "music_playlist_random8", "0", "enables random play order if 1, 0 is sequential play"},
72         {0, "music_playlist_random9", "0", "enables random play order if 1, 0 is sequential play"},
73 };
74 cvar_t music_playlist_sampleposition[MAX_PLAYLISTS] =
75 {
76         {0, "music_playlist_sampleposition0", "-1", "resume position for track, -1 restarts every time"},
77         {0, "music_playlist_sampleposition1", "-1", "resume position for track, -1 restarts every time"},
78         {0, "music_playlist_sampleposition2", "-1", "resume position for track, -1 restarts every time"},
79         {0, "music_playlist_sampleposition3", "-1", "resume position for track, -1 restarts every time"},
80         {0, "music_playlist_sampleposition4", "-1", "resume position for track, -1 restarts every time"},
81         {0, "music_playlist_sampleposition5", "-1", "resume position for track, -1 restarts every time"},
82         {0, "music_playlist_sampleposition6", "-1", "resume position for track, -1 restarts every time"},
83         {0, "music_playlist_sampleposition7", "-1", "resume position for track, -1 restarts every time"},
84         {0, "music_playlist_sampleposition8", "-1", "resume position for track, -1 restarts every time"},
85         {0, "music_playlist_sampleposition9", "-1", "resume position for track, -1 restarts every time"},
86 };
87
88 static qboolean wasPlaying = false;
89 static qboolean initialized = false;
90 static qboolean enabled = false;
91 static float cdvolume;
92 typedef char filename_t[MAX_QPATH];
93 #ifdef MAXTRACKS
94 static filename_t remap[MAXTRACKS];
95 #endif
96 static int faketrack = -1;
97
98 // exported variables
99 qboolean cdPlaying = false;
100 qboolean cdPlayLooping = false;
101 unsigned char cdPlayTrack;
102
103 cl_cdstate_t cd;
104
105 static void CDAudio_Eject (void)
106 {
107 }
108
109
110 static void CDAudio_CloseDoor (void)
111 {
112 }
113
114 static int CDAudio_GetAudioDiskInfo (void)
115 {
116         return -1;
117 }
118
119 // Helper for CDAudio_Play, the "cd" command, and the music_playlist system.
120 // Does _not_ act as NOP when a playlist is active, simply because this is used
121 // _by_ playlist code. So beware when calling this.
122 static void CDAudio_Play_byName (const char *trackname, qboolean looping, qboolean tryreal, float startposition)
123 {
124         unsigned int track;
125         sfx_t* sfx;
126         char filename[MAX_QPATH];
127
128         Host_StartVideo();
129
130         if (!enabled)
131                 return;
132
133         if(tryreal && strspn(trackname, "0123456789") == strlen(trackname))
134         {
135                 track = (unsigned int) atoi(trackname);
136 #ifdef MAXTRACKS
137                 if(track > 0 && track < MAXTRACKS && *remap[track])
138                         trackname = remap[track];
139 #endif
140         }
141
142         if(tryreal && strspn(trackname, "0123456789") == strlen(trackname))
143         {
144                 track = (unsigned int) atoi(trackname);
145                 if (track < 1)
146                 {
147                         Con_DPrintf("CDAudio: Bad track number %u.\n", track);
148                         return;
149                 }
150         }
151         else
152                 track = 0;
153
154         // div0: I assume this code was intentionally there. Maybe turn it into a cvar?
155         if (cdPlaying && cdPlayTrack == track && faketrack == -1)
156                 return;
157         CDAudio_Stop ();
158
159         // Try playing a fake track (sound file) first
160         if(track >= 1)
161         {
162                                               dpsnprintf(filename, sizeof(filename), "sound/cdtracks/track%03u.wav", track);
163                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/cdtracks/track%03u.ogg", track);
164                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/track%03u.ogg", track);// added by motorsep
165                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/cdtracks/track%03u.ogg", track);// added by motorsep
166                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/cdtracks/track%02u.wav", track);
167                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/cdtracks/track%02u.ogg", track);
168                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/track%02u.ogg", track);// added by motorsep
169                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/cdtracks/track%02u.ogg", track);// added by motorsep
170         }
171         else
172         {
173                                               dpsnprintf(filename, sizeof(filename), "%s", trackname);
174                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "%s.wav", trackname);
175                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "%s.ogg", trackname);
176                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/%s", trackname);
177                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/%s.wav", trackname);
178                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/%s.ogg", trackname);
179                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/cdtracks/%s", trackname);
180                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/cdtracks/%s.wav", trackname);
181                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "sound/cdtracks/%s.ogg", trackname);
182                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/%s.ogg", trackname); // added by motorsep
183                 if (!FS_FileExists(filename)) dpsnprintf(filename, sizeof(filename), "music/cdtracks/%s.ogg", trackname); // added by motorsep
184         }
185         if (FS_FileExists(filename) && (sfx = S_PrecacheSound (filename, false, false)))
186         {
187                 faketrack = S_StartSound_StartPosition_Flags (-1, 0, sfx, vec3_origin, cdvolume, 0, startposition, (looping ? CHANNELFLAG_FORCELOOP : 0) | CHANNELFLAG_FULLVOLUME | CHANNELFLAG_LOCALSOUND, 1.0f);
188                 if (faketrack != -1)
189                 {
190                         if(track >= 1)
191                                 Con_DPrintf ("BGM track %u playing...\n", track);
192                         else
193                                 Con_DPrintf ("BGM track %s playing...\n", trackname);
194                 }
195         }
196
197         if (faketrack == -1)
198         {
199                 if(track >= 1)
200                         Con_DPrintf ("Could not load BGM track %u.\n", track);
201                 else
202                         Con_DPrintf ("Could not load BGM track %s.\n", trackname);
203                 return;
204         }
205
206         cdPlayLooping = looping;
207         cdPlayTrack = track;
208         cdPlaying = true;
209
210         if (cdvolume == 0.0 || bgmvolume.value == 0)
211                 CDAudio_Pause ();
212 }
213
214 void CDAudio_Play (int track, qboolean looping)
215 {
216         char buf[20];
217         if (music_playlist_index.integer >= 0)
218                 return;
219         dpsnprintf(buf, sizeof(buf), "%d", (int) track);
220         CDAudio_Play_byName(buf, looping, true, 0);
221 }
222
223 float CDAudio_GetPosition (void)
224 {
225         if(faketrack != -1)
226                 return S_GetChannelPosition(faketrack);
227         return -1;
228 }
229
230 static void CDAudio_StopPlaylistTrack(void);
231
232 void CDAudio_Stop (void)
233 {
234         if (!enabled)
235                 return;
236
237         // save the playlist position
238         CDAudio_StopPlaylistTrack();
239
240         if (faketrack != -1)
241         {
242                 S_StopChannel (faketrack, true, true);
243                 faketrack = -1;
244         }
245
246         wasPlaying = false;
247         cdPlaying = false;
248 }
249
250 void CDAudio_Pause (void)
251 {
252         if (!enabled || !cdPlaying || faketrack == -1)
253                 return;
254
255         S_SetChannelFlag (faketrack, CHANNELFLAG_PAUSED, true);
256         wasPlaying = cdPlaying;
257         cdPlaying = false;
258 }
259
260
261 void CDAudio_Resume (void)
262 {
263         if (!enabled || cdPlaying || !wasPlaying || faketrack == -1)
264                 return;
265
266         S_SetChannelFlag (faketrack, CHANNELFLAG_PAUSED, false);
267         cdPlaying = true;
268 }
269
270 static void CD_f (void)
271 {
272         const char *command;
273 #ifdef MAXTRACKS
274         int ret;
275         int n;
276 #endif
277
278         command = Cmd_Argv (1);
279
280         if (strcasecmp(command, "remap") != 0)
281                 Host_StartVideo();
282
283         if (strcasecmp(command, "on") == 0)
284         {
285                 enabled = true;
286                 return;
287         }
288
289         if (strcasecmp(command, "off") == 0)
290         {
291                 CDAudio_Stop();
292                 enabled = false;
293                 return;
294         }
295
296         if (strcasecmp(command, "reset") == 0)
297         {
298                 enabled = true;
299                 CDAudio_Stop();
300 #ifdef MAXTRACKS
301                 for (n = 0; n < MAXTRACKS; n++)
302                         *remap[n] = 0; // empty string, that is, unremapped
303 #endif
304                 CDAudio_GetAudioDiskInfo();
305                 return;
306         }
307
308         if (strcasecmp(command, "rescan") == 0)
309         {
310                 CDAudio_Shutdown();
311                 CDAudio_Startup();
312                 return;
313         }
314
315         if (strcasecmp(command, "remap") == 0)
316         {
317 #ifdef MAXTRACKS
318                 ret = Cmd_Argc() - 2;
319                 if (ret <= 0)
320                 {
321                         for (n = 1; n < MAXTRACKS; n++)
322                                 if (*remap[n])
323                                         Con_Printf("  %u -> %s\n", n, remap[n]);
324                         return;
325                 }
326                 for (n = 1; n <= ret; n++)
327                         strlcpy(remap[n], Cmd_Argv (n+1), sizeof(*remap));
328 #endif
329                 return;
330         }
331
332         if (strcasecmp(command, "close") == 0)
333         {
334                 CDAudio_CloseDoor();
335                 return;
336         }
337
338         if (strcasecmp(command, "play") == 0)
339         {
340                 if (music_playlist_index.integer >= 0)
341                         return;
342                 CDAudio_Play_byName(Cmd_Argv (2), false, true, (Cmd_Argc() > 3) ? atof( Cmd_Argv(3) ) : 0);
343                 return;
344         }
345
346         if (strcasecmp(command, "loop") == 0)
347         {
348                 if (music_playlist_index.integer >= 0)
349                         return;
350                 CDAudio_Play_byName(Cmd_Argv (2), true, true, (Cmd_Argc() > 3) ? atof( Cmd_Argv(3) ) : 0);
351                 return;
352         }
353
354         if (strcasecmp(command, "stop") == 0)
355         {
356                 if (music_playlist_index.integer >= 0)
357                         return;
358                 CDAudio_Stop();
359                 return;
360         }
361
362         if (strcasecmp(command, "pause") == 0)
363         {
364                 if (music_playlist_index.integer >= 0)
365                         return;
366                 CDAudio_Pause();
367                 return;
368         }
369
370         if (strcasecmp(command, "resume") == 0)
371         {
372                 if (music_playlist_index.integer >= 0)
373                         return;
374                 CDAudio_Resume();
375                 return;
376         }
377
378         if (strcasecmp(command, "eject") == 0)
379         {
380                 if (faketrack == -1)
381                         CDAudio_Stop();
382                 CDAudio_Eject();
383                 return;
384         }
385
386         if (strcasecmp(command, "info") == 0)
387         {
388                 CDAudio_GetAudioDiskInfo ();
389                 if (cdPlaying)
390                         Con_Printf("Currently %s track %u\n", cdPlayLooping ? "looping" : "playing", cdPlayTrack);
391                 else if (wasPlaying)
392                         Con_Printf("Paused %s track %u\n", cdPlayLooping ? "looping" : "playing", cdPlayTrack);
393                 if (cdvolume >= 0)
394                         Con_Printf("Volume is %f\n", cdvolume);
395                 else
396                         Con_Printf("Can't get CD volume\n");
397                 return;
398         }
399
400         Con_Printf("CD commands:\n");
401         Con_Printf("cd on - enables CD audio system\n");
402         Con_Printf("cd off - stops and disables CD audio system\n");
403         Con_Printf("cd reset - resets CD audio system (clears track remapping and re-reads disc information)\n");
404         Con_Printf("cd rescan - rescans disks in drives (to use another disc)\n");
405         Con_Printf("cd remap <remap1> [remap2] [remap3] [...] - chooses emulated CD tracks to play when a map asks for a particular track, this has many uses\n");
406         Con_Printf("cd close - closes CD tray\n");
407         Con_Printf("cd eject - stops playing music and opens CD tray to allow you to change disc\n");
408         Con_Printf("cd play <tracknumber> <startposition> - plays selected track in remapping table\n");
409         Con_Printf("cd loop <tracknumber> <startposition> - plays and repeats selected track in remapping table\n");
410         Con_Printf("cd stop - stops playing current CD track\n");
411         Con_Printf("cd pause - pauses CD playback\n");
412         Con_Printf("cd resume - unpauses CD playback\n");
413         Con_Printf("cd info - prints basic disc information (number of tracks, currently playing track, volume level)\n");
414 }
415
416 static void CDAudio_SetVolume (float newvol)
417 {
418         // If the volume hasn't changed
419         if (newvol == cdvolume)
420                 return;
421
422         // If the CD has been muted
423         if (newvol == 0.0f)
424                 CDAudio_Pause ();
425         else
426         {
427                 // If the CD has been unmuted
428                 if (cdvolume == 0.0f)
429                         CDAudio_Resume ();
430
431                 if (faketrack != -1)
432                         S_SetChannelVolume (faketrack, newvol);
433         }
434
435         cdvolume = newvol;
436 }
437
438 static void CDAudio_StopPlaylistTrack(void)
439 {
440         if (music_playlist_active >= 0 && music_playlist_active < MAX_PLAYLISTS && music_playlist_sampleposition[music_playlist_active].value >= 0)
441         {
442                 // save position for resume
443                 float position = CDAudio_GetPosition();
444                 Cvar_SetValueQuick(&music_playlist_sampleposition[music_playlist_active], position >= 0 ? position : 0);
445         }
446         music_playlist_active = -1;
447         music_playlist_playing = 0; // not playing
448 }
449
450 void CDAudio_StartPlaylist(qboolean resume)
451 {
452         const char *list;
453         const char *t;
454         int index;
455         int current;
456         int randomplay;
457         int count;
458         int listindex;
459         float position;
460         char trackname[MAX_QPATH];
461         CDAudio_Stop();
462         index = music_playlist_index.integer;
463         if (index >= 0 && index < MAX_PLAYLISTS && bgmvolume.value > 0)
464         {
465                 list = music_playlist_list[index].string;
466                 current = music_playlist_current[index].integer;
467                 randomplay = music_playlist_random[index].integer;
468                 position = music_playlist_sampleposition[index].value;
469                 count = 0;
470                 trackname[0] = 0;
471                 if (list && list[0])
472                 {
473                         for (t = list;;count++)
474                         {
475                                 if (!COM_ParseToken_Console(&t))
476                                         break;
477                                 // if we don't find the desired track, use the first one
478                                 if (count == 0)
479                                         strlcpy(trackname, com_token, sizeof(trackname));
480                         }
481                 }
482                 if (count > 0)
483                 {
484                         // position < 0 means never resume track
485                         if (position < 0)
486                                 position = 0;
487                         // advance to next track in playlist if the last one ended
488                         if (!resume)
489                         {
490                                 position = 0;
491                                 current++;
492                                 if (randomplay)
493                                         current = (int)lhrandom(0, count);
494                         }
495                         // wrap playlist position if needed
496                         if (current >= count)
497                                 current = 0;
498                         // set current
499                         Cvar_SetValueQuick(&music_playlist_current[index], current);
500                         // get the Nth trackname
501                         if (current >= 0 && current < count)
502                         {
503                                 for (listindex = 0, t = list;;listindex++)
504                                 {
505                                         if (!COM_ParseToken_Console(&t))
506                                                 break;
507                                         if (listindex == current)
508                                         {
509                                                 strlcpy(trackname, com_token, sizeof(trackname));
510                                                 break;
511                                         }
512                                 }
513                         }
514                         if (trackname[0])
515                         {
516                                 CDAudio_Play_byName(trackname, false, false, position);
517                                 if (faketrack != -1)
518                                         music_playlist_active = index;
519                         }
520                 }
521         }
522         music_playlist_playing = music_playlist_active >= 0 ? 1 : -1;
523 }
524
525 void CDAudio_Update (void)
526 {
527         static int lastplaylist = -1;
528         if (!enabled)
529                 return;
530
531         CDAudio_SetVolume (bgmvolume.value);
532         if (music_playlist_playing > 0 && CDAudio_GetPosition() < 0)
533         {
534                 // this track ended, start a new track from the beginning
535                 CDAudio_StartPlaylist(false);
536                 lastplaylist = music_playlist_index.integer;
537         }
538         else if (lastplaylist != music_playlist_index.integer
539         || (bgmvolume.value > 0 && !music_playlist_playing && music_playlist_index.integer >= 0))
540         {
541                 // active playlist changed, save position and switch track
542                 CDAudio_StartPlaylist(true);
543                 lastplaylist = music_playlist_index.integer;
544         }
545 }
546
547 int CDAudio_Init (void)
548 {
549         int i;
550
551         if (cls.state == ca_dedicated)
552                 return -1;
553
554 // COMMANDLINEOPTION: Sound: -nocdaudio disables CD audio support
555         if (COM_CheckParm("-nocdaudio"))
556                 return -1;
557
558 #ifdef MAXTRACKS
559         for (i = 0; i < MAXTRACKS; i++)
560                 *remap[i] = 0;
561 #endif
562
563         Cvar_RegisterVariable(&cdaudioinitialized);
564         Cvar_SetValueQuick(&cdaudioinitialized, true);
565         enabled = true;
566
567         Cvar_RegisterVariable(&music_playlist_index);
568         for (i = 0;i < MAX_PLAYLISTS;i++)
569         {
570                 Cvar_RegisterVariable(&music_playlist_list[i]);
571                 Cvar_RegisterVariable(&music_playlist_current[i]);
572                 Cvar_RegisterVariable(&music_playlist_random[i]);
573                 Cvar_RegisterVariable(&music_playlist_sampleposition[i]);
574         }
575
576         Cmd_AddCommand("cd", CD_f, "execute a CD drive command (cd on/off/reset/remap/close/play/loop/stop/pause/resume/eject/info) - use cd by itself for usage");
577
578         return 0;
579 }
580
581 int CDAudio_Startup (void)
582 {
583         if (COM_CheckParm("-nocdaudio"))
584                 return -1;
585
586         initialized = true;
587
588         Con_Print("CD Audio Initialized\n");
589
590         return 0;
591 }
592
593 void CDAudio_Shutdown (void)
594 {
595         if (!initialized)
596                 return;
597
598         CDAudio_Stop();
599         initialized = false;
600 }