osezer patch 010
[theoddone33/hhexen.git] / base / oss.c
1 /*  XMMS - Cross-platform multimedia player
2  *  Copyright (C) 1998-1999  Peter Alm, Mikael Alm, Olle Hallnas, Thomas Nilsson and 4Front Technologies
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (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.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18
19
20 #include "oss.h"
21 #include <errno.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <pthread.h>
30 #include <sys/ioctl.h>
31 #include <sys/mman.h>
32 #include <sys/soundcard.h>
33 //#include <machine/soundcard.h>
34
35
36 void oss_set_audio_params(void);
37
38
39 /* All that remains of glib usage... */
40 #define FALSE   0
41 #define TRUE    1
42 typedef int    gboolean;
43 typedef unsigned char   guchar;
44 typedef unsigned short  gushort;
45 typedef unsigned int    guint;
46 typedef unsigned long   gulong;
47
48 #define min(x,y) ((x)<(y)?(x):(y))
49
50 static int audio_fd = 0;
51 static void* buffer;
52 static gboolean going = FALSE, prebuffer = FALSE, remove_prebuffer = FALSE;
53 static gboolean paused = FALSE, unpause = FALSE, do_pause = FALSE;
54 static int buffer_size, prebuffer_size, blk_size;
55 static int rd_index = 0, wr_index = 0;
56 static int output_time_offset = 0, written = 0, output_bytes = 0;
57 static int bps, ebps;
58 static int flush;
59 static int fragsize, format, channels;
60 static int frequency, efrequency, device_buffer_size;
61 static char device_name[ 16 ];
62 static pthread_t buffer_thread;
63 static gboolean realtime = FALSE;
64
65
66 OSSConfig oss_cfg;
67
68
69 void oss_about(void)
70 {
71         printf( "XMMS OSS Driver 0.9\n" );
72 }
73
74
75 int oss_get_written_time(void)
76 {
77         if (!going)
78                 return 0;
79         return (int) (((float) written * 1000) / (float) (bps));
80 }
81
82 int oss_get_output_time(void)
83 {
84         audio_buf_info buf_info;
85         int bytes;
86
87         if (!audio_fd || !going)
88                 return 0;
89
90         if (!paused)
91         {
92                 if (!ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &buf_info))
93                         bytes = output_bytes - ((buf_info.fragstotal - buf_info.fragments) * buf_info.fragsize);
94                 else
95                         bytes = output_bytes;
96         }
97         else
98                 bytes = output_bytes;
99
100         if (bytes < 0)
101                 bytes = 0;
102         return output_time_offset + (int) ((float) ((bytes) * 1000.0) / (float) ebps);
103 }
104
105 int oss_used(void)
106 {
107         if (realtime)
108                 return 0;
109         else
110         {
111                 if (wr_index >= rd_index)
112                         return wr_index - rd_index;
113                 return buffer_size - (rd_index - wr_index);
114         }
115 }
116
117 int oss_playing(void)
118 {
119         audio_buf_info buf_info;
120         int bytes;
121
122         if (!ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &buf_info))
123                 bytes = ((buf_info.fragstotal - buf_info.fragments - 3) * buf_info.fragsize);
124         else
125                 bytes = 0;
126
127         if (!oss_used() && bytes <= 0)
128                 return FALSE;
129
130         return TRUE;
131 }
132
133 int oss_free(void)
134 {
135         if (!realtime)
136         {
137                 if (remove_prebuffer && prebuffer)
138                 {
139                         prebuffer = FALSE;
140                         remove_prebuffer = FALSE;
141                 }
142                 if (prebuffer)
143                         remove_prebuffer = TRUE;
144
145                 if (rd_index > wr_index)
146                         return (rd_index - wr_index) - device_buffer_size - 1;
147                 return (buffer_size - (wr_index - rd_index)) - device_buffer_size - 1;
148         }
149         else
150                 if (paused)
151                         return 0;
152                 else
153                         return 1000000;
154 }
155
156 int oss_downsample(guchar * ob, guint length, guint speed, guint espeed)
157 {
158         guint nlen, i, off, d, w;
159
160         if ((format == AFMT_U16_BE || format == AFMT_U16_LE || format == AFMT_S16_BE || format == AFMT_S16_LE) && channels == 2)
161         {
162                 gulong *nbuffer, *obuffer, *ptr;
163
164                 obuffer = (gulong *) ob;
165                 length >>= 2;
166
167                 nlen = (length * espeed) / speed;
168                 d = (speed << 8) / espeed;
169
170                 nbuffer = malloc(nlen << 2);
171                 for (i = 0, off = 0, ptr = nbuffer; i < nlen; i++)
172                 {
173                         *ptr++ = obuffer[off >> 8];
174                         off += d;
175                 }
176                 w = write(audio_fd, nbuffer, nlen << 2);
177                 free(nbuffer);
178         }
179         else if (((format == AFMT_U16_BE || format == AFMT_U16_LE || format == AFMT_S16_BE || format == AFMT_S16_LE) && channels == 1)
180              || ((format == AFMT_U8 || format == AFMT_S8) && channels == 2))
181         {
182                 gushort *nbuffer, *obuffer, *ptr;
183
184                 obuffer = (gushort *) ob;
185                 length >>= 1;
186
187                 nlen = (length * espeed) / speed;
188                 d = (speed << 8) / espeed;
189
190                 nbuffer = malloc(nlen << 1);
191                 for (i = 0, off = 0, ptr = nbuffer; i < nlen; i++)
192                 {
193                         *ptr++ = obuffer[off >> 8];
194                         off += d;
195                 }
196                 w = write(audio_fd, nbuffer, nlen << 1);
197                 free(nbuffer);
198         }
199         else
200         {
201                 guchar *nbuffer, *obuffer, *ptr;
202
203                 obuffer = ob;
204
205                 nlen = (length * espeed) / speed;
206                 d = (speed << 8) / espeed;
207
208                 nbuffer = malloc(nlen);
209                 for (i = 0, off = 0, ptr = nbuffer; i < nlen; i++)
210                 {
211                         *ptr++ = obuffer[off >> 8];
212                         off += d;
213                 }
214                 w = write(audio_fd, nbuffer, nlen);
215                 free(nbuffer);
216         }
217         return w;
218
219 }
220
221 void oss_write(void *ptr, int length)
222 {
223         int cnt, off = 0, w;
224
225         if (!realtime)
226         {
227                 while( oss_free() < length )
228                         usleep(10000);
229
230                 remove_prebuffer = FALSE;
231                 written += length;
232                 while (length > 0)
233                 {
234                         cnt = min(length, buffer_size - wr_index);
235                         memcpy(buffer + wr_index, ptr + off, cnt);
236                         wr_index = (wr_index + cnt) % buffer_size;
237                         length -= cnt;
238                         off = cnt;
239
240                 }
241         }
242         else
243         {
244                 if (paused)
245                         return;
246
247                 if (frequency == efrequency)
248                         w = write(audio_fd, ptr, length);
249                 else
250                         w = oss_downsample(ptr, length, frequency, efrequency);
251
252                 if (w == -1 && errno == EIO)
253                 {
254                         close(audio_fd);
255                         audio_fd = open(device_name, O_WRONLY);
256                         oss_set_audio_params();
257                         if (frequency == efrequency)
258                                 w = write(audio_fd, ptr, length);
259                         else
260                                 w = oss_downsample(ptr, length, frequency, efrequency);
261                 }
262                 written += length;
263                 output_bytes += w;
264         }
265
266 }
267
268 void oss_close(void)
269 {
270         wr_index = 0;
271         rd_index = 0;
272         going = 0;
273         if (!realtime)
274                 pthread_join(buffer_thread, NULL);
275         else
276         {
277                 ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
278                 close(audio_fd);
279         }
280 }
281
282 void oss_flush(int time)
283 {
284         if (!realtime)
285         {
286                 flush = time;
287                 while( flush != -1 )
288                         usleep(10000);
289         }
290         else
291         {
292                 ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
293                 close(audio_fd);
294                 audio_fd = open(device_name, O_WRONLY);
295                 oss_set_audio_params();
296                 output_time_offset = time;
297                 written = (time / 10) * (bps / 100);
298                 output_bytes = 0;
299         }
300 }
301
302 void oss_pause(short p)
303 {
304         if (!realtime)
305         {
306                 if (p == TRUE)
307                         do_pause = TRUE;
308                 else
309                         unpause = TRUE;
310         }
311         else
312                 paused = p;
313
314 }
315
316 void *oss_loop(void *arg)
317 {
318         int length, cnt, w;
319         audio_buf_info abuf_info;
320
321         while (going)
322         {
323                 if (oss_used() > prebuffer_size)
324                 {
325                         prebuffer = FALSE;
326                 }
327                 if (oss_used() > 0 && !paused && !prebuffer)
328                 {
329                         length = min(blk_size, oss_used());
330                         while (length > 0)
331                         {
332                                 cnt = min(length, buffer_size - rd_index);
333
334                                 if (frequency == efrequency)
335                                         w = write(audio_fd, buffer + rd_index, cnt);
336                                 else
337                                         w = oss_downsample(buffer + rd_index, cnt, frequency, efrequency);
338                                 if (w == -1 && errno == EIO)
339                                 {
340                                         close(audio_fd);
341                                         audio_fd = open(device_name, O_WRONLY);
342                                         oss_set_audio_params();
343                                         if (frequency == efrequency)
344                                                 w = write(audio_fd, buffer + rd_index, cnt);
345                                         else
346                                                 w = oss_downsample(buffer + rd_index, cnt, frequency, efrequency);
347                                 }
348                                 output_bytes += w;
349                                 rd_index = (rd_index + cnt) % buffer_size;
350                                 length -= cnt;
351                         }
352 /*                      if (!oss_used())
353                                 ioctl(audio_fd, SNDCTL_DSP_POST, 0);*/
354                 }
355                 else
356                         usleep( 10000 );
357                 if (do_pause && !paused)
358                 {
359                         do_pause = FALSE;
360                         paused = TRUE;
361                         if (!ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &abuf_info))
362                         {
363                                 rd_index -= (abuf_info.fragstotal - abuf_info.fragments) * abuf_info.fragsize;
364                                 output_bytes -= (abuf_info.fragstotal - abuf_info.fragments) * abuf_info.fragsize;
365                         }
366                         if (rd_index < 0)
367                                 rd_index += buffer_size;
368                         ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
369
370                 }
371                 if (unpause && paused)
372                 {
373                         unpause = FALSE;
374                         close(audio_fd);
375                         audio_fd = open(device_name, O_WRONLY);
376                         oss_set_audio_params();
377                         paused = FALSE;
378                 }
379
380                 if (flush != -1)
381                 {
382                         /*
383                          * This close and open is a work around of a bug that exists in some drivers which 
384                          * cause the driver to get fucked up by a reset
385                          */
386
387                         ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
388                         close(audio_fd);
389                         audio_fd = open(device_name, O_WRONLY);
390                         oss_set_audio_params();
391                         output_time_offset = flush;
392                         written = (flush / 10) * (bps / 100);
393                         rd_index = wr_index = output_bytes = 0;
394                         flush = -1;
395                         prebuffer = TRUE;
396                 }
397
398         }
399
400         ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
401         close(audio_fd);
402         munlock( buffer, buffer_size );
403         free(buffer);
404         pthread_exit(NULL);
405 }
406
407 void oss_set_audio_params(void)
408 {
409         int frag, stereo;
410
411         ioctl(audio_fd, SNDCTL_DSP_RESET, 0);
412         frag = (oss_cfg.fragment_count << 16) | fragsize;
413         ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, &frag);
414         ioctl(audio_fd, SNDCTL_DSP_SETFMT, &format);
415         stereo = channels - 1;
416         ioctl(audio_fd, SNDCTL_DSP_STEREO, &stereo);
417         efrequency = frequency;
418         ioctl(audio_fd, SNDCTL_DSP_SPEED, &efrequency);
419         ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &blk_size);
420
421         ebps = efrequency * channels;
422         if (format == AFMT_U16_BE || format == AFMT_U16_LE || format == AFMT_S16_BE || format == AFMT_S16_LE)
423                 ebps *= 2;
424 }
425
426 int oss_open(AFormat fmt, int rate, int nch)
427 {
428         switch( fmt )
429         {
430                 case FMT_U8:
431                         format = AFMT_U8;
432                         break;
433                 case FMT_S8:
434                         format = AFMT_S8;
435                         break;
436                 case FMT_U16_LE:
437                         format = AFMT_U16_LE;
438                         break;
439                 case FMT_U16_BE:
440                         format = AFMT_U16_BE;
441                         break;
442                 case FMT_U16_NE:
443 #ifdef AFMT_U16_NE
444                         format = AFMT_U16_NE;
445 #else
446 #ifdef WORDS_BIGENDIAN
447                         format = AFMT_U16_BE;
448 #else
449                         format = AFMT_U16_LE;
450 #endif
451 #endif
452                         break;
453                 case FMT_S16_LE:
454                         format = AFMT_S16_LE;
455                         break;
456                 case FMT_S16_BE:
457                         format = AFMT_S16_BE;
458                         break;
459                 case FMT_S16_NE:
460 #ifdef AFMT_S16_NE
461                         format = AFMT_S16_NE;
462 #else
463 #ifdef WORDS_BIGENDIAN
464                         format = AFMT_S16_BE;
465 #else
466                         format = AFMT_S16_LE;
467 #endif
468 #endif
469                         break;
470         }
471
472         bps = rate * nch;
473         if (format == AFMT_U16_BE || format == AFMT_U16_LE || format == AFMT_S16_BE || format == AFMT_S16_LE)
474                 bps *= 2;
475         fragsize = 0;
476         while ((1L << fragsize) < bps / 25)
477                 fragsize++;
478         fragsize--;
479
480         device_buffer_size = ((1L << fragsize) * (oss_cfg.fragment_count + 1));
481
482         channels = nch;
483         frequency = rate;
484         buffer_size = (oss_cfg.buffer_size * bps) / 1000;
485         if (buffer_size < 8192)
486                 buffer_size = 8192;
487         prebuffer_size = (buffer_size * oss_cfg.prebuffer) / 100;
488         if (buffer_size - prebuffer_size < 4096)
489                 prebuffer_size = buffer_size - 4096;
490
491         buffer_size += device_buffer_size;
492         buffer = calloc( 1, buffer_size );
493         mlock(buffer, buffer_size);
494
495         going = 1;
496         flush = -1;
497         prebuffer = 1;
498         wr_index = rd_index = output_time_offset = written = output_bytes = 0;
499         paused = FALSE;
500         do_pause = FALSE;
501         unpause = FALSE;
502         remove_prebuffer = FALSE;
503
504         /*realtime = xmms_check_realtime_priority();*/
505         /*realtime = FALSE;*/
506         realtime = TRUE;
507
508         if (oss_cfg.audio_device > 0)
509                 sprintf( device_name, "/dev/dsp%d", oss_cfg.audio_device );
510         else
511                 strcpy( device_name, "/dev/dsp" );
512
513         audio_fd = open(device_name, O_WRONLY);
514         if (audio_fd == -1)
515         {
516                 free(buffer);
517                 return 0;
518         }
519         oss_set_audio_params();
520         if (!realtime)
521                 pthread_create(&buffer_thread, NULL, oss_loop, NULL);
522         return 1;
523 }
524
525
526 static void scan_devices( char* type )
527 {
528         FILE* file;
529         char buf[256];
530         char* tmp2;
531         int found = 0;
532         int index = 0;
533
534         printf( "%s\n", type );
535
536         file = fopen( "/dev/sndstat", "r" );
537         if( file )
538         {
539                 while (fgets(buf, 255, file))
540                 {
541                         if (found && buf[0] == '\n')
542                                 break;
543                         if (buf[strlen(buf) - 1] == '\n')
544                                 buf[strlen(buf) - 1] = '\0';
545                         if (found)
546                         {
547                                 if (index == 0)
548                                 {
549                                         tmp2 = strchr(buf, ':');
550                                         if (tmp2)
551                                         {
552                                                 tmp2++;
553                                                 while (*tmp2 == ' ')
554                                                         tmp2++;
555                                         }
556                                         else
557                                                 tmp2 = buf;
558
559                                         printf( "  %s (default)\n", tmp2 );
560                                 }
561                                 else
562                                 {
563                                         printf( "  %s\n", buf );
564                                 }
565                         }
566                         if( ! strcasecmp(buf, type) )
567                                 found = 1;
568                 }
569                 fclose(file);
570         }
571 }
572
573
574 void oss_configure(void)
575 {
576     printf( "OSS configure not implemented - here are the current devices:\n" );
577     scan_devices( "Audio devices:" );
578     scan_devices( "Mixers:" );
579
580     /*
581         oss_cfg.audio_device = audio_device;
582         oss_cfg.mixer_device = mixer_device;
583         oss_cfg.buffer_size = (gint) GTK_ADJUSTMENT(buffer_size_adj)->value;
584         oss_cfg.prebuffer = (gint) GTK_ADJUSTMENT(buffer_pre_adj)->value;
585         oss_cfg.fragment_count = 32;
586     */
587 }
588
589
590 void oss_init(void)
591 {
592         /*
593     ConfigFile *cfgfile;
594         char* filename;
595     */
596
597         memset(&oss_cfg, 0, sizeof (OSSConfig));
598
599         oss_cfg.audio_device = 0;
600         oss_cfg.mixer_device = 0;
601         oss_cfg.buffer_size = 3000;
602         oss_cfg.prebuffer = 25;
603         oss_cfg.fragment_count = 3;  /*32;*/
604
605 #if 0
606         filename = g_strconcat(g_get_home_dir(), "/.xmms/config", NULL);
607         if (cfgfile = xmms_cfg_open_file(filename))
608         {
609                 xmms_cfg_read_int(cfgfile, "OSS", "audio_device", &oss_cfg.audio_device);
610                 xmms_cfg_read_int(cfgfile, "OSS", "mixer_device", &oss_cfg.mixer_device);
611                 xmms_cfg_read_int(cfgfile, "OSS", "buffer_size", &oss_cfg.buffer_size);
612                 xmms_cfg_read_int(cfgfile, "OSS", "prebuffer", &oss_cfg.prebuffer);
613                 xmms_cfg_free(cfgfile);
614         }
615 #endif
616 }
617
618
619 void oss_get_volume(int *l, int *r)
620 {
621         int fd, v, cmd, devs;
622         char devname[ 20 ];
623
624         if (oss_cfg.mixer_device > 0)
625                 sprintf( devname, "/dev/mixer%d", oss_cfg.mixer_device );
626         else
627                 strcpy( devname, "/dev/mixer" );
628
629         fd = open(devname, O_RDONLY);
630         if (fd != -1)
631         {
632                 ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
633                 if (devs & SOUND_MASK_PCM)
634                         cmd = SOUND_MIXER_READ_PCM;
635                 else if (devs & SOUND_MASK_VOLUME)
636                         cmd = SOUND_MIXER_READ_VOLUME;
637                 else
638                 {
639                         close(fd);
640                         return;
641                 }
642                 ioctl(fd, cmd, &v);
643                 *r = (v & 0xFF00) >> 8;
644                 *l = (v & 0x00FF);
645                 close(fd);
646         }
647 }
648
649
650 void oss_set_volume(int l, int r)
651 {
652         int fd, v, cmd, devs;
653         char devname[ 20 ];
654
655         if (oss_cfg.mixer_device > 0)
656                 sprintf( devname, "/dev/mixer%d", oss_cfg.mixer_device );
657         else
658                 strcpy( devname, "/dev/mixer" );
659
660         fd = open(devname, O_RDONLY);
661         if (fd != -1)
662         {
663                 ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devs);
664                 if (devs & SOUND_MASK_PCM)
665                         cmd = SOUND_MIXER_WRITE_PCM;
666                 else if (devs & SOUND_MASK_VOLUME)
667                         cmd = SOUND_MIXER_WRITE_VOLUME;
668                 else
669                 {
670                         close(fd);
671                         return;
672                 }
673                 v = (r << 8) | l;
674                 ioctl(fd, cmd, &v);
675                 close(fd);
676         }
677 }
678
679
680 OutputPlugin oss_op =
681 {
682         NULL,
683         NULL,
684         "OSS Driver 0.9",
685         oss_init,
686         oss_about,
687         oss_configure,
688         oss_get_volume,
689         oss_set_volume,
690         oss_open,
691         oss_write,
692         oss_close,
693         oss_flush,
694         oss_pause,
695         oss_free,
696         oss_playing,
697         oss_get_output_time,
698         oss_get_written_time,
699 };
700
701
702 OutputPlugin *get_oplugin_info(void)
703 {
704         return &oss_op;
705 }
706
707
708 /* EOF */
709