1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or(at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10 *
11 * See the GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
16 */
17 
18 /*
19 ==========================================================
20 
21   OGG Vorbis decoding
22 
23 ==========================================================
24 */
25 
26 #include <sys/time.h>
27 #include <errno.h>
28 #include <vorbis/vorbisfile.h>
29 
30 #include "client.h"
31 #include "snd_loc.h"
32 #include "snd_ogg.h"
33 
34 extern int	sound_started;		/* Sound initialization flag. */
35 extern cvar_t	*fs_basedir;		/* Path to "music". */
36 
37 qboolean	ogg_first_init = true;	/* First initialization flag. */
38 qboolean	ogg_started = false;	/* Initialization flag. */
39 byte		*ogg_buffer;		/* File buffer. */
40 char		**ogg_filelist;		/* List of Ogg Vorbis files. */
41 char		ovBuf[4096];		/* Buffer for sound. */
42 int		ogg_curfile;		/* Index of currently played file. */
43 int		ogg_numfiles;		/* Number of Ogg Vorbis files. */
44 int		ovSection;		/* Position in Ogg Vorbis file. */
45 ogg_status_t	ogg_status;		/* Status indicator. */
46 cvar_t		*ogg_autoplay;		/* Play this song when started. */
47 cvar_t		*ogg_check;		/* Check Ogg files or not. */
48 cvar_t		*ogg_playlist;		/* Playlist. */
49 cvar_t		*ogg_sequence;		/* Sequence play indicator. */
50 cvar_t		*ogg_volume;		/* Music volume. */
51 OggVorbis_File	ovFile;			/* Ogg Vorbis file. */
52 
53 /*
54 ==========
55 OGG_Init
56 
57 Initialize the Ogg Vorbis subsystem.
58 ==========
59 */
OGG_Init(void)60 void OGG_Init(void)
61 {
62 	cvar_t	*cv;	/* Cvar pointer. */
63 
64 	if (ogg_started)
65 		return;
66 
67 	Com_Printf("Starting Ogg Vorbis.\n");
68 
69 	/* Skip initialization if disabled. */
70 	cv = Cvar_Get ("ogg_enable", "1", 0);
71 	if (cv->value != 1) {
72 		Com_Printf ("Ogg Vorbis not initializing.\n");
73 		return;
74 	}
75 
76 	/* Cvars. */
77 	ogg_autoplay = Cvar_Get("ogg_autoplay", "?", CVAR_ARCHIVE);
78 	ogg_check = Cvar_Get("ogg_check", "0", CVAR_ARCHIVE);
79 	ogg_playlist = Cvar_Get("ogg_playlist", "playlist", CVAR_ARCHIVE);
80 	ogg_sequence = Cvar_Get("ogg_sequence", "next", CVAR_ARCHIVE);
81 	ogg_volume = Cvar_Get("ogg_volume", "0.7", CVAR_ARCHIVE);
82 
83 	/* Console commands. */
84 	Cmd_AddCommand("ogg_list", OGG_ListCmd);
85 	Cmd_AddCommand("ogg_pause", OGG_PauseCmd);
86 	Cmd_AddCommand("ogg_play", OGG_PlayCmd);
87 	Cmd_AddCommand("ogg_reinit", OGG_Reinit);
88 	Cmd_AddCommand("ogg_resume", OGG_ResumeCmd);
89 	Cmd_AddCommand("ogg_seek", OGG_SeekCmd);
90 	Cmd_AddCommand("ogg_status", OGG_StatusCmd);
91 	Cmd_AddCommand("ogg_stop", OGG_Stop);
92 
93 	/* Build list of files. */
94 	ogg_numfiles = 0;
95 	if (ogg_playlist->string[0] != '\0')
96 		OGG_LoadPlaylist(ogg_playlist->string);
97 	if (ogg_numfiles == 0)
98 		OGG_LoadFileList();
99 
100 	/* Check if we have Ogg Vorbis files. */
101 	if (ogg_numfiles <= 0) {
102 		Com_Printf("No Ogg Vorbis files found.\n");
103 		ogg_started = true;	/* For OGG_Shutdown(). */
104 		OGG_Shutdown();
105 		return;
106 	}
107 
108 	/* Initialize variables. */
109 	if (ogg_first_init) {
110 		srand(time(NULL));
111 		ogg_buffer = NULL;
112 		ogg_curfile = -1;
113 		ogg_status = STOP;
114 		ogg_first_init = false;
115 	}
116 
117 	ogg_started = true;
118 
119 	Com_Printf("%d Ogg Vorbis files found.\n", ogg_numfiles);
120 
121 	/* Autoplay support. */
122 	if (ogg_autoplay->string[0] != '\0')
123 		OGG_ParseCmd(ogg_autoplay->string);
124 }
125 
126 /*
127 ==========
128 OGG_Shutdown
129 
130 Shutdown the Ogg Vorbis subsystem.
131 ==========
132 */
OGG_Shutdown(void)133 void OGG_Shutdown(void)
134 {
135 
136 	if (!ogg_started)
137 		return;
138 
139 	Com_Printf("Shutting down Ogg Vorbis.\n");
140 
141 	OGG_Stop();
142 
143 	/* Free the list of files. */
144 	FS_FreeList(ogg_filelist, ogg_numfiles + 1);
145 
146 	/* Remove console commands. */
147 	Cmd_RemoveCommand("ogg_list");
148 	Cmd_RemoveCommand("ogg_pause");
149 	Cmd_RemoveCommand("ogg_play");
150 	Cmd_RemoveCommand("ogg_reinit");
151 	Cmd_RemoveCommand("ogg_resume");
152 	Cmd_RemoveCommand("ogg_seek");
153 	Cmd_RemoveCommand("ogg_status");
154 	Cmd_RemoveCommand("ogg_stop");
155 
156 	ogg_started = false;
157 }
158 
159 /*
160 ==========
161 OGG_Reinit
162 
163 Reinitialize the Ogg Vorbis subsystem.
164 ==========
165 */
OGG_Reinit(void)166 void OGG_Reinit(void)
167 {
168 
169 	OGG_Shutdown();
170 	OGG_Init();
171 }
172 
173 /*
174 ==========
175 OGG_Check
176 
177 Check if the file is a valid Ogg Vorbis file.
178 ==========
179 */
OGG_Check(char * name)180 qboolean OGG_Check(char *name)
181 {
182 	qboolean	 res;		/* Return value. */
183 	byte		*buffer;	/* File buffer. */
184 	int		 size;		/* File size. */
185 	OggVorbis_File	 ovf;		/* Ogg Vorbis file. */
186 
187 	if (ogg_check->value == 0)
188 		return (true);
189 
190 	res = false;
191 	if ((size = FS_LoadFile(name, (void**)&buffer)) > 0) {
192 		if (ov_test(NULL, &ovf, (char *)buffer, size) == 0) {
193 			res = true;
194 			ov_clear(&ovf);
195 		}
196 		FS_FreeFile(buffer);
197 	}
198 
199 	return (res);
200 }
201 
202 /*
203 ==========
204 OGG_Seek
205 
206 Change position in the file.
207 ==========
208 */
OGG_Seek(ogg_seek_t type,double offset)209 void OGG_Seek(ogg_seek_t type, double offset)
210 {
211 	double pos;	/* Position in file (in seconds). */
212 	double total;	/* Length of file (in seconds). */
213 
214 	/* Check if the file is seekable. */
215 	if (ov_seekable(&ovFile) == 0) {
216 		Com_Printf("OGG_Seek: file is not seekable.\n");
217 		return;
218 	}
219 
220 	/* Get file information. */
221 	pos = ov_time_tell(&ovFile);
222 	total = ov_time_total(&ovFile, -1);
223 
224 	switch (type) {
225 	case ABS:
226 		if (offset >= 0 && offset <= total) {
227 			if (ov_time_seek(&ovFile, offset) != 0)
228 				Com_Printf("OGG_Seek: could not seek.\n");
229 			else
230 				Com_Printf("%0.2f -> %0.2f of %0.2f.\n", pos, offset, total);
231 		} else
232 			Com_Printf("OGG_Seek: invalid offset.\n");
233 		break;
234 	case REL:
235 		if (pos + offset >= 0 && pos + offset <= total) {
236 			if (ov_time_seek(&ovFile, pos + offset) != 0)
237 				Com_Printf("OGG_Seek: could not seek.\n");
238 			else
239 				Com_Printf("%0.2f -> %0.2f of %0.2f.\n", pos, pos + offset, total);
240 		} else
241 			Com_Printf("OGG_Seek: invalid offset.\n");
242 		break;
243 	}
244 }
245 
246 /*
247 ==========
248 OGG_LoadFileList
249 
250 Load list of Ogg Vorbis files in "music".
251 ==========
252 */
OGG_LoadFileList(void)253 void OGG_LoadFileList(void)
254 {
255 	char	**list;			/* List of .ogg files. */
256 	int	  i;			/* Loop counter. */
257 	int	  j;			/* Real position in list. */
258 
259 	/* Get file list. */
260 	list = FS_ListFiles2(va("%s/*.ogg", OGG_DIR), &ogg_numfiles, 0,
261 	    SFF_SUBDIR | SFF_HIDDEN | SFF_SYSTEM);
262 	ogg_numfiles--;
263 
264 	/* Check if there are posible Ogg files. */
265 	if (list == NULL)
266 		return;
267 
268 	/* Allocate list of files. */
269 	ogg_filelist = malloc(sizeof(char *) * ogg_numfiles);
270 
271 	/* Add valid Ogg Vorbis file to the list. */
272 	for (i = 0, j = 0; i < ogg_numfiles; i++) {
273 		if (!OGG_Check(list[i])) {
274 			free(list[i]);
275 			continue;
276 		}
277 		ogg_filelist[j++] = list[i];
278 	}
279 
280 	/* Free the file list. */
281 	free(list);
282 
283 	/* Adjust the list size (remove space for invalid music files). */
284 	ogg_numfiles = j;
285 	ogg_filelist = realloc(ogg_filelist, sizeof(char *) * ogg_numfiles);
286 }
287 
288 /*
289 ==========
290 OGG_LoadPlaylist
291 
292 Load playlist.
293 ==========
294 */
OGG_LoadPlaylist(char * playlist)295 void OGG_LoadPlaylist(char *playlist)
296 {
297 	byte	*buffer;	/* Buffer to read the file. */
298 	char	*ptr;		/* Pointer for parsing the file. */
299 	int	 i;		/* Loop counter. */
300 	int	 size;		/* Length of buffer and strings. */
301 
302 	/* Open playlist. */
303 	if ((size = FS_LoadFile(va("%s/%s.lst", OGG_DIR, ogg_playlist->string),
304 	    (void **)&buffer)) < 0) {
305 		Com_Printf("OGG_LoadPlaylist: could not open playlist: %s.\n", strerror(errno));
306 		return;
307 	}
308 
309 	/* Count the files in playlist. */
310 	for (ptr = strtok((char *)buffer, "\n");
311 	    ptr != NULL;
312 	    ptr = strtok(NULL, "\n")) {
313 		if ((byte *)ptr != buffer)
314 			ptr[-1] = '\n';
315 		if (OGG_Check(va("%s/%s", OGG_DIR, ptr)))
316 			ogg_numfiles++;
317 	}
318 
319 	/* Allocate file list. */
320 	ogg_filelist = malloc(sizeof(char *) * ogg_numfiles);
321 
322 	i = 0;
323 	for (ptr = strtok((char *)buffer, "\n");
324 	    ptr != NULL;
325 	    ptr = strtok(NULL, "\n"))
326 		if (OGG_Check(va("%s/%s", OGG_DIR, ptr)))
327 			ogg_filelist[i++] = strdup(va("%s/%s", OGG_DIR, ptr));
328 
329 	/* Free file buffer. */
330 	FS_FreeFile(buffer);
331 }
332 
333 /*
334 ==========
335 OGG_Open
336 
337 Play Ogg Vorbis file (with absolute or relative index).
338 ==========
339 */
OGG_Open(ogg_seek_t type,int offset)340 qboolean OGG_Open(ogg_seek_t type, int offset)
341 {
342 	int		 size;		/* File size. */
343 	int		 pos;		/* Absolute position. */
344 	int		 res;		/* Error indicator. */
345 
346 	pos = -1;
347 
348 	switch (type) {
349 	case ABS:
350 		/* Absolute index. */
351 		if (offset < 0 || offset >= ogg_numfiles) {
352 			Com_Printf("OGG_Open: %d out of range.\n", offset+1);
353 			return (false);
354 		} else
355 			pos = offset;
356 		break;
357 	case REL:
358 		/* Simulate a loopback. */
359 		if (ogg_curfile == -1 && offset < 0)
360 			offset++;
361 		while (ogg_curfile + offset < 0)
362 			offset += ogg_numfiles;
363 		while (ogg_curfile + offset >= ogg_numfiles)
364 			offset -= ogg_numfiles;
365 		pos = ogg_curfile + offset;
366 		break;
367 	}
368 
369 	/* Check running music. */
370 	if (ogg_status == PLAY) {
371 		if (ogg_curfile == pos)
372 			return (true);
373 		else
374 			OGG_Stop();
375 	}
376 
377 	/* Find file. */
378 	if ((size = FS_LoadFile(ogg_filelist[pos], (void **)&ogg_buffer)) == -1) {
379 		Com_Printf("OGG_Open: could not open %d (%s): %s.\n", pos, ogg_filelist[pos], strerror(errno));
380 		return (false);
381 	}
382 
383 	/* Open ogg vorbis file. */
384 	if ((res = ov_open(NULL, &ovFile, (char *)ogg_buffer, size)) < 0) {
385 		Com_Printf("OGG_Open: '%s' is not a valid Ogg Vorbis file (error %i).\n", ogg_filelist[pos], res);
386 		FS_FreeFile(ogg_buffer);
387 		return (false);
388 	}
389 
390 	/* Play file. */
391 	ovSection = 0;
392 	ogg_curfile = pos;
393 	ogg_status = PLAY;
394 
395 	Com_Printf("Playing file %d '%s'\n", pos, ogg_filelist[pos]);
396 
397 	return (true);
398 }
399 
400 /*
401 ==========
402 OGG_OpenName
403 
404 Play Ogg Vorbis file (with name only).
405 ==========
406 */
OGG_OpenName(char * filename)407 qboolean OGG_OpenName(char *filename)
408 {
409 	char	*name;	/* File name. */
410 	int	 i;	/* Loop counter. */
411 
412 	name = va("%s/%s.ogg", OGG_DIR, filename);
413 
414 	for (i = 0; i < ogg_numfiles; i++)
415 		if (strcmp(name, ogg_filelist[i]) == 0)
416 			break;
417 
418 	if (i < ogg_numfiles)
419 		return (OGG_Open(ABS, i));
420 	else {
421 		Com_Printf("OGG_OpenName: '%s' not in the list.\n", filename);
422 		return (false);
423 	}
424 }
425 
426 /*
427 ==========
428 OGG_Read
429 
430 Play a portion of the currently opened file.
431 ==========
432 */
OGG_Read(void)433 int OGG_Read(void)
434 {
435 	int res;	/* Number of bytes read. */
436 
437 	/* Read and resample. */
438 	res = ov_read(&ovFile, ovBuf, sizeof(ovBuf), 0, 2, 1, &ovSection);
439 	S_RawSamplesVol(res>>2, 44100, 2, 2, (byte*)ovBuf, ogg_volume->value);
440 
441 	/* Check for end of file. */
442 	if (res == 0) {
443 		OGG_Stop();
444 		OGG_Sequence();
445 	}
446 
447 	return (res);
448 }
449 
450 /*
451 ==========
452 OGG_Sequence
453 
454 Play files in sequence.
455 ==========
456 */
OGG_Sequence(void)457 void OGG_Sequence(void)
458 {
459 
460 	if (strcmp(ogg_sequence->string, "next") == 0)
461 		OGG_Open(REL, 1);
462 	else if (strcmp(ogg_sequence->string, "prev") == 0)
463 		OGG_Open(REL, -1);
464 	else if (strcmp(ogg_sequence->string, "random") == 0)
465 		OGG_Open(ABS, rand() % ogg_numfiles);
466 	else if (strcmp(ogg_sequence->string, "loop") == 0)
467 		OGG_Open(REL, 0);
468 	else if (strcmp(ogg_sequence->string, "none") != 0) {
469 		Com_Printf("Invalid value of ogg_sequence: %s\n", ogg_sequence->string);
470 		Cvar_Set("ogg_sequence", "none");
471 	}
472 }
473 
474 /*
475 ==========
476 OGG_Stop
477 
478 Stop playing the current file.
479 ==========
480 */
OGG_Stop(void)481 void OGG_Stop(void)
482 {
483 
484 	if (ogg_status == STOP)
485 		return;
486 
487 	ov_clear(&ovFile);
488 	ogg_status = STOP;
489 
490 	if (ogg_buffer != NULL) {
491 		FS_FreeFile(ogg_buffer);
492 		ogg_buffer = NULL;
493 	}
494 }
495 
496 /*
497 ==========
498 OGG_Stream
499 
500 Stream music.
501 ==========
502 */
OGG_Stream(void)503 void OGG_Stream(void)
504 {
505 
506 	if (!ogg_started)
507 		return;
508 
509 	while (ogg_status == PLAY && paintedtime + MAX_RAW_SAMPLES - 2048 > s_rawend)
510 		OGG_Read();
511 }
512 
513 /*
514 ============
515 S_RawSamplesVol
516 
517 Cinematic streaming and voice over network (with volume)
518 ============
519 */
S_RawSamplesVol(int samples,int rate,int width,int channels,byte * data,float volume)520 void S_RawSamplesVol (int samples, int rate, int width, int channels, byte *data, float volume)
521 {
522 	int	i;
523 	int	src, dst;
524 	float	scale;
525 
526 	if (!sound_started)
527 		return;
528 
529 	if (s_rawend < paintedtime)
530 		s_rawend = paintedtime;
531 
532 	scale = (float)rate / dma.speed;
533 
534 //Com_Printf ("%i < %i < %i\n", soundtime, paintedtime, s_rawend);
535 	if (channels == 2 && width == 2)
536 	{
537 		if (scale == 1.0)
538 		{	// optimized case
539 			for (i=0 ; i<samples ; i++)
540 			{
541 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
542 				s_rawend++;
543 				s_rawsamples[dst].left =
544 				    (int)(volume * LittleShort(((short *)data)[i*2])) << 8;
545 				s_rawsamples[dst].right =
546 				    (int)(volume * LittleShort(((short *)data)[i*2+1])) << 8;
547 			}
548 		}
549 		else
550 		{
551 			for (i=0 ; ; i++)
552 			{
553 				src = i*scale;
554 				if (src >= samples)
555 					break;
556 				dst = s_rawend&(MAX_RAW_SAMPLES-1);
557 				s_rawend++;
558 				s_rawsamples[dst].left =
559 				    (int)(volume * LittleShort(((short *)data)[src*2])) << 8;
560 				s_rawsamples[dst].right =
561 				    (int)(volume * LittleShort(((short *)data)[src*2+1])) << 8;
562 			}
563 		}
564 	}
565 	else if (channels == 1 && width == 2)
566 	{
567 		for (i=0 ; ; i++)
568 		{
569 			src = i*scale;
570 			if (src >= samples)
571 				break;
572 			dst = s_rawend&(MAX_RAW_SAMPLES-1);
573 			s_rawend++;
574 			s_rawsamples[dst].left =
575 			    (int)(volume * LittleShort(((short *)data)[src])) << 8;
576 			s_rawsamples[dst].right =
577 			    (int)(volume * LittleShort(((short *)data)[src])) << 8;
578 		}
579 	}
580 	else if (channels == 2 && width == 1)
581 	{
582 		for (i=0 ; ; i++)
583 		{
584 			src = i*scale;
585 			if (src >= samples)
586 				break;
587 			dst = s_rawend&(MAX_RAW_SAMPLES-1);
588 			s_rawend++;
589 			s_rawsamples[dst].left =
590 			    (int)(volume * ((char *)data)[src*2]) << 16;
591 			s_rawsamples[dst].right =
592 			    (int)(volume * ((char *)data)[src*2+1]) << 16;
593 		}
594 	}
595 	else if (channels == 1 && width == 1)
596 	{
597 		for (i=0 ; ; i++)
598 		{
599 			src = i*scale;
600 			if (src >= samples)
601 				break;
602 			dst = s_rawend&(MAX_RAW_SAMPLES-1);
603 			s_rawend++;
604 			s_rawsamples[dst].left =
605 			    (int)(volume * (((byte *)data)[src]-128)) << 16;
606 			s_rawsamples[dst].right =
607 				(int)(volume * (((byte *)data)[src]-128)) << 16;
608 		}
609 	}
610 }
611 
612 /* Console commands. */
613 
614 /*
615 ==========
616 OGG_ListCmd
617 
618 List Ogg Vorbis files.
619 ==========
620 */
OGG_ListCmd(void)621 void OGG_ListCmd(void)
622 {
623 	int i;
624 
625 	for (i = 0; i < ogg_numfiles; i++)
626 		Com_Printf("%d %s\n", i+1, ogg_filelist[i]);
627 
628 	Com_Printf("%d Ogg Vorbis files.\n", ogg_numfiles);
629 }
630 
631 /*
632 ==========
633 OGG_ParseCmd
634 
635 Parse play controls.
636 ==========
637 */
OGG_ParseCmd(char * arg)638 void OGG_ParseCmd(char *arg)
639 {
640 	int n;
641 
642 	switch (arg[0]) {
643 	case '#':
644 		n = atoi(arg+1) - 1;
645 		OGG_Open(ABS, n);
646 		break;
647 	case '?':
648 		OGG_Open(ABS, rand() % ogg_numfiles);
649 		break;
650 	case '>':
651 		if (strlen(arg) > 1)
652 			OGG_Open(REL, atoi(arg+1));
653 		else
654 			OGG_Open(REL, 1);
655 		break;
656 	case '<':
657 		if (strlen(arg) > 1)
658 			OGG_Open(REL, -atoi(arg+1));
659 		else
660 			OGG_Open(REL, -1);
661 		break;
662 	default:
663 		OGG_OpenName(arg);
664 		break;
665 	}
666 }
667 
668 /*
669 ==========
670 OGG_PauseCmd
671 
672 Pause current song.
673 ==========
674 */
OGG_PauseCmd(void)675 void OGG_PauseCmd(void)
676 {
677 
678 	if (ogg_status == PLAY)
679 		ogg_status = PAUSE;
680 }
681 
682 /*
683 ==========
684 OGG_PlayCmd
685 
686 Play control.
687 ==========
688 */
OGG_PlayCmd(void)689 void OGG_PlayCmd( void )
690 {
691 
692 	if (Cmd_Argc() < 2) {
693 		Com_Printf("Usage: ogg_play {filename | #n | ? | >n | <n}\n");
694 		return;
695 	}
696 
697 	OGG_ParseCmd(Cmd_Argv(1));
698 }
699 
700 /*
701 ==========
702 OGG_ResumeCmd
703 
704 Resume current song.
705 ==========
706 */
OGG_ResumeCmd(void)707 void OGG_ResumeCmd(void)
708 {
709 
710 	if (ogg_status == PAUSE)
711 		ogg_status = PLAY;
712 }
713 
714 /*
715 ==========
716 OGG_SeekCmd
717 
718 Change position in the file being played.
719 ==========
720 */
OGG_SeekCmd(void)721 void OGG_SeekCmd(void)
722 {
723 
724 	if (ogg_status != STOP)
725 		return;
726 
727 	if (Cmd_Argc() < 2) {
728 		Com_Printf("Usage: ogg_seek {n | <n | >n}\n");
729 		return;
730 	}
731 
732 	switch (Cmd_Argv(1)[0]) {
733 		case '>':
734 			OGG_Seek(REL, atof(Cmd_Argv(1)+1));
735 			break;
736 		case '<':
737 			OGG_Seek(REL, -atof(Cmd_Argv(1)+1));
738 			break;
739 		default:
740 			OGG_Seek(ABS, atof(Cmd_Argv(1)));
741 			break;
742 	}
743 }
744 
745 /*
746 ==========
747 OGG_StatusCmd
748 
749 Display status.
750 ==========
751 */
OGG_StatusCmd(void)752 void OGG_StatusCmd(void)
753 {
754 
755 	switch (ogg_status) {
756 	case PLAY:
757 		Com_Printf("Playing file %d (%s) at %0.2f seconds.\n",
758 		    ogg_curfile+1, ogg_filelist[ogg_curfile], ov_time_tell(&ovFile));
759 		break;
760 	case PAUSE:
761 		Com_Printf("Paused file %d (%s) at %0.2f seconds.\n",
762 		    ogg_curfile+1, ogg_filelist[ogg_curfile], ov_time_tell(&ovFile));
763 		break;
764 	case STOP:
765 		if (ogg_curfile == -1)
766 			Com_Printf("Stopped.\n");
767 		else
768 			Com_Printf("Stopped file %d (%s).\n",
769 			    ogg_curfile+1, ogg_filelist[ogg_curfile]);
770 		break;
771 	}
772 }
773