1 /*
2 * sound_oss.c - Open Sound System driver
3 *
4 * Copyright (C) 1995-1998 David Firth
5 * Copyright (C) 1998-2013 Atari800 development team (see DOC/CREDITS)
6 *
7 * This file is part of the Atari800 emulator project which emulates
8 * the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
9 *
10 * Atari800 is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * Atari800 is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with Atari800; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23 */
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #include <sys/soundcard.h>
30 /* XXX: #include <machine/soundcard.h> */
31
32 #include "atari.h"
33 #include "log.h"
34 #include "platform.h"
35 #include "sound.h"
36
37 #define DEBUG 0
38
39 static const char * const dspname = "/dev/dsp";
40
41 static int dsp_fd;
42
43 /* When opening an OSS audio device, we don't limit number of sound fragments
44 that OSS creates. To minimise latency resulting from too many fragments,
45 we take advantage of the fact that OSS usually starts playback after fully
46 filling 2 fragments, and ensure that we never have more filled fragments
47 than MAX_FILLED_FRAGMENTS (which is <> 2 for some additional headroom). */
48 enum { MAX_FILLED_FRAGMENTS = 4 };
49
PLATFORM_SoundSetup(Sound_setup_t * setup)50 int PLATFORM_SoundSetup(Sound_setup_t *setup)
51 {
52 int format;
53 int frag_size;
54 int setfragment;
55
56 if (Sound_enabled)
57 close(dsp_fd);
58
59 dsp_fd = open(dspname, O_WRONLY);
60 if (dsp_fd == -1) {
61 perror(dspname);
62 return FALSE;
63 }
64
65 if (setup->frag_frames == 0)
66 /* Set frag_frames automatically. */
67 frag_size = setup->freq / 50;
68 else
69 frag_size = setup->frag_frames;
70 frag_size *= setup->channels * setup->sample_size;
71
72 /* By setting number of fragments to 0x7fff (ie. don't limit) we ensure
73 that the obtained fragment size will be as close to the requested value
74 as possible. */
75 setfragment = 0x7fff0000;
76 {
77 /* Compute the closest power of two. */
78 int pow_val = 1;
79 int val = frag_size;
80 while (val >>= 1) {
81 pow_val <<= 1;
82 ++setfragment;
83 }
84 if (pow_val < frag_size)
85 /* Ensure fragment size is not smaller than user-provided value. */
86 ++setfragment;
87 }
88 if (ioctl(dsp_fd, SNDCTL_DSP_SETFRAGMENT, &setfragment) == -1) {
89 Log_print("%s: SNDCTL_DSP_SETFRAGMENT(%.8x) failed", dspname, setfragment);
90 close(dsp_fd);
91 return FALSE;
92 }
93
94 format = setup->sample_size == 2 ? AFMT_S16_NE : AFMT_U8;
95 if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1) {
96 Log_print("%s: SNDCTL_DSP_SETFMT(%i) failed", dspname, format);
97 close(dsp_fd);
98 return FALSE;
99 }
100 if (format == AFMT_S16_NE)
101 setup->sample_size = 2;
102 else if (format == AFMT_U8)
103 setup->sample_size = 1;
104 else {
105 Log_print("%s: Obtained format %i not supported", dspname, format);
106 close(dsp_fd);
107 return FALSE;
108 }
109
110 if (ioctl(dsp_fd, SNDCTL_DSP_CHANNELS, &setup->channels) == -1) {
111 Log_print("%s: SNDCTL_DSP_CHANNELS(%u) failed", dspname, setup->channels);
112 close(dsp_fd);
113 return FALSE;
114 }
115
116 if (ioctl(dsp_fd, SNDCTL_DSP_SPEED, &setup->freq) == -1) {
117 Log_print("%s: SNDCTL_DSP_SPEED(%u) failed", dspname, setup->freq);
118 close(dsp_fd);
119 return FALSE;
120 }
121
122 if (ioctl(dsp_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size) == -1) {
123 Log_print("%s: SNDCTL_DSP_GETBLKSIZE failed", dspname);
124 close(dsp_fd);
125 return FALSE;
126 }
127
128 setup->frag_frames = frag_size / setup->channels / setup->sample_size;
129 {
130 audio_buf_info bi;
131 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
132 Log_print("%s: cannot retrieve ospace", dspname);
133 return 0;
134 }
135 #if DEBUG
136 Log_print("fragments=%i, fragstotal=%i, fragsize=%i, bytes=%i", bi.fragments, bi.fragstotal, bi.fragsize, bi.bytes);
137 Log_print("frag_size=%i, buf_Frames=%u", frag_size, setup->frag_frames);
138 #endif
139 }
140
141 return TRUE;
142 }
143
PLATFORM_SoundExit(void)144 void PLATFORM_SoundExit(void)
145 {
146 close(dsp_fd);
147 }
148
PLATFORM_SoundPause(void)149 void PLATFORM_SoundPause(void)
150 {
151 /* flush buffers */
152 ioctl(dsp_fd, SNDCTL_DSP_POST, NULL);
153 }
154
PLATFORM_SoundContinue(void)155 void PLATFORM_SoundContinue(void)
156 {
157 /* do nothing */
158 }
159
PLATFORM_SoundAvailable(void)160 unsigned int PLATFORM_SoundAvailable(void)
161 {
162 audio_buf_info bi;
163 int filled_frags;
164 enum { MAX_FILLED_FRAGMENTS = 4 };
165
166 if (ioctl(dsp_fd, SNDCTL_DSP_GETOSPACE, &bi) == -1) {
167 Log_print("%s: cannot retrieve ospace", dspname);
168 return 0;
169 }
170 #if DEBUG
171 Log_print("fragments=%i, fragstotal=%i, fragsize=%i, bytes=%i", bi.fragments, bi.fragstotal, bi.fragsize, bi.bytes);*/
172 #endif
173
174 /* Usually OSS playback starts when 2 fragments are fully filled. Take
175 advantage of it: write audio only if at most MAX_FILLED_FRAGS fragments
176 are filled, to minimize latency regardless of actual total number of
177 fragments. */
178 filled_frags = bi.fragstotal - bi.fragments;
179 if (filled_frags <= MAX_FILLED_FRAGMENTS)
180 return bi.fragsize * (MAX_FILLED_FRAGMENTS - filled_frags);
181 else if (bi.fragstotal <= MAX_FILLED_FRAGMENTS)
182 return bi.bytes;
183 else
184 return 0;
185 }
186
PLATFORM_SoundWrite(UBYTE const * buffer,unsigned int size)187 void PLATFORM_SoundWrite(UBYTE const *buffer, unsigned int size)
188 {
189 int wsize = write(dsp_fd, buffer, size);
190 if (wsize < size) {
191 /* TODO: handle problem */
192 }
193 }
194