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