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