1/*
2    SDL - Simple DirectMedia Layer
3    Copyright (C) 1997-2012 Sam Lantinga
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public
7    License as published by the Free Software Foundation; either
8    version 2 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public
16    License along with this library; if not, write to the Free
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21*/
22#include "SDL_config.h"
23
24/*
25	MiNT audio driver
26	using XBIOS functions (STFA driver)
27
28	Patrice Mandin
29*/
30
31/* Mint includes */
32#include <mint/osbind.h>
33#include <mint/falcon.h>
34#include <mint/cookie.h>
35
36#include "SDL_audio.h"
37#include "../SDL_audio_c.h"
38#include "../SDL_sysaudio.h"
39
40#include "../../video/ataricommon/SDL_atarimxalloc_c.h"
41#include "../../video/ataricommon/SDL_atarisuper.h"
42
43#include "SDL_mintaudio.h"
44#include "SDL_mintaudio_stfa.h"
45
46/*--- Defines ---*/
47
48#define MINT_AUDIO_DRIVER_NAME "mint_stfa"
49
50/* Debug print info */
51#define DEBUG_NAME "audio:stfa: "
52#if 0
53#define DEBUG_PRINT(what) \
54	{ \
55		printf what; \
56	}
57#else
58#define DEBUG_PRINT(what)
59#endif
60
61/*--- Static variables ---*/
62
63static long cookie_snd, cookie_mch;
64static cookie_stfa_t *cookie_stfa;
65
66static const int freqs[16]={
67	4995,	6269,	7493,	8192,
68	9830,	10971,	12538,	14985,
69	16384,	19819,	21943,	24576,
70	30720,	32336,	43885,	49152
71};
72
73/*--- Audio driver functions ---*/
74
75static void Mint_CloseAudio(_THIS);
76static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec);
77static void Mint_LockAudio(_THIS);
78static void Mint_UnlockAudio(_THIS);
79
80/* To check/init hardware audio */
81static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec);
82static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec);
83
84/*--- Audio driver bootstrap functions ---*/
85
86static int Audio_Available(void)
87{
88	long dummy;
89	const char *envr = SDL_getenv("SDL_AUDIODRIVER");
90
91	/* Check if user asked a different audio driver */
92	if ((envr) && (SDL_strcmp(envr, MINT_AUDIO_DRIVER_NAME)!=0)) {
93		DEBUG_PRINT((DEBUG_NAME "user asked a different audio driver\n"));
94		return(0);
95	}
96
97	/* Cookie _MCH present ? if not, assume ST machine */
98	if (Getcookie(C__MCH, &cookie_mch) == C_NOTFOUND) {
99		cookie_mch = MCH_ST;
100	}
101
102	/* Cookie _SND present ? if not, assume ST machine */
103	if (Getcookie(C__SND, &cookie_snd) == C_NOTFOUND) {
104		cookie_snd = SND_PSG;
105	}
106
107	/* Cookie STFA present ? */
108	if (Getcookie(C_STFA, &dummy) != C_FOUND) {
109		DEBUG_PRINT((DEBUG_NAME "no STFA audio\n"));
110		return(0);
111	}
112	cookie_stfa = (cookie_stfa_t *) dummy;
113
114	SDL_MintAudio_stfa = cookie_stfa;
115
116	DEBUG_PRINT((DEBUG_NAME "STFA audio available!\n"));
117	return(1);
118}
119
120static void Audio_DeleteDevice(SDL_AudioDevice *device)
121{
122    SDL_free(device->hidden);
123    SDL_free(device);
124}
125
126static SDL_AudioDevice *Audio_CreateDevice(int devindex)
127{
128	SDL_AudioDevice *this;
129
130	/* Initialize all variables that we clean on shutdown */
131	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
132    if ( this ) {
133        SDL_memset(this, 0, (sizeof *this));
134        this->hidden = (struct SDL_PrivateAudioData *)
135                SDL_malloc((sizeof *this->hidden));
136    }
137    if ( (this == NULL) || (this->hidden == NULL) ) {
138        SDL_OutOfMemory();
139        if ( this ) {
140            SDL_free(this);
141        }
142        return(0);
143    }
144    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
145
146    /* Set the function pointers */
147    this->OpenAudio   = Mint_OpenAudio;
148    this->CloseAudio  = Mint_CloseAudio;
149    this->LockAudio   = Mint_LockAudio;
150    this->UnlockAudio = Mint_UnlockAudio;
151    this->free        = Audio_DeleteDevice;
152
153    return this;
154}
155
156AudioBootStrap MINTAUDIO_STFA_bootstrap = {
157	MINT_AUDIO_DRIVER_NAME, "MiNT STFA audio driver",
158	Audio_Available, Audio_CreateDevice
159};
160
161static void Mint_LockAudio(_THIS)
162{
163	void *oldpile;
164
165	/* Stop replay */
166	oldpile=(void *)Super(0);
167	cookie_stfa->sound_enable=STFA_PLAY_DISABLE;
168	SuperToUser(oldpile);
169}
170
171static void Mint_UnlockAudio(_THIS)
172{
173	void *oldpile;
174
175	/* Restart replay */
176	oldpile=(void *)Super(0);
177	cookie_stfa->sound_enable=STFA_PLAY_ENABLE|STFA_PLAY_REPEAT;
178	SuperToUser(oldpile);
179}
180
181static void Mint_CloseAudio(_THIS)
182{
183	void *oldpile;
184
185	/* Stop replay */
186	oldpile=(void *)Super(0);
187	cookie_stfa->sound_enable=STFA_PLAY_DISABLE;
188	SuperToUser(oldpile);
189
190	/* Wait if currently playing sound */
191	while (SDL_MintAudio_mutex != 0) {
192	}
193
194	/* Clear buffers */
195	if (SDL_MintAudio_audiobuf[0]) {
196		Mfree(SDL_MintAudio_audiobuf[0]);
197		SDL_MintAudio_audiobuf[0] = SDL_MintAudio_audiobuf[1] = NULL;
198	}
199}
200
201static int Mint_CheckAudio(_THIS, SDL_AudioSpec *spec)
202{
203	int i;
204
205	DEBUG_PRINT((DEBUG_NAME "asked: %d bits, ",spec->format & 0x00ff));
206	DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
207	DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
208	DEBUG_PRINT(("channels=%d, ", spec->channels));
209	DEBUG_PRINT(("freq=%d\n", spec->freq));
210
211    if (spec->channels > 2) {
212        spec->channels = 2;  /* no more than stereo! */
213    }
214
215	/* Check formats available */
216	MINTAUDIO_freqcount=0;
217	for (i=0;i<16;i++) {
218		SDL_MintAudio_AddFrequency(this, freqs[i], 0, i, -1);
219	}
220
221#if 1
222	for (i=0; i<MINTAUDIO_freqcount; i++) {
223		DEBUG_PRINT((DEBUG_NAME "freq %d: %lu Hz, clock %lu, prediv %d\n",
224			i, MINTAUDIO_frequencies[i].frequency, MINTAUDIO_frequencies[i].masterclock,
225			MINTAUDIO_frequencies[i].predivisor
226		));
227	}
228#endif
229
230	MINTAUDIO_numfreq=SDL_MintAudio_SearchFrequency(this, spec->freq);
231	spec->freq=MINTAUDIO_frequencies[MINTAUDIO_numfreq].frequency;
232
233	DEBUG_PRINT((DEBUG_NAME "obtained: %d bits, ",spec->format & 0x00ff));
234	DEBUG_PRINT(("signed=%d, ", ((spec->format & 0x8000)!=0)));
235	DEBUG_PRINT(("big endian=%d, ", ((spec->format & 0x1000)!=0)));
236	DEBUG_PRINT(("channels=%d, ", spec->channels));
237	DEBUG_PRINT(("freq=%d\n", spec->freq));
238
239	return 0;
240}
241
242static void Mint_InitAudio(_THIS, SDL_AudioSpec *spec)
243{
244	void *buffer;
245	void *oldpile;
246
247	buffer = SDL_MintAudio_audiobuf[SDL_MintAudio_numbuf];
248
249	oldpile=(void *)Super(0);
250
251	/* Stop replay */
252	cookie_stfa->sound_enable=STFA_PLAY_DISABLE;
253
254	/* Select replay format */
255	cookie_stfa->sound_control = MINTAUDIO_frequencies[MINTAUDIO_numfreq].predivisor;
256	if ((spec->format & 0xff)==8) {
257		cookie_stfa->sound_control |= STFA_FORMAT_8BIT;
258	} else {
259		cookie_stfa->sound_control |= STFA_FORMAT_16BIT;
260	}
261	if (spec->channels==2) {
262		cookie_stfa->sound_control |= STFA_FORMAT_STEREO;
263	} else {
264		cookie_stfa->sound_control |= STFA_FORMAT_MONO;
265	}
266	if ((spec->format & 0x8000)!=0) {
267		cookie_stfa->sound_control |= STFA_FORMAT_SIGNED;
268	} else {
269		cookie_stfa->sound_control |= STFA_FORMAT_UNSIGNED;
270	}
271	if ((spec->format & 0x1000)!=0) {
272		cookie_stfa->sound_control |= STFA_FORMAT_BIGENDIAN;
273	} else {
274		cookie_stfa->sound_control |= STFA_FORMAT_LITENDIAN;
275	}
276
277	/* Set buffer */
278	cookie_stfa->sound_start = (unsigned long) buffer;
279	cookie_stfa->sound_end = (unsigned long) (buffer + spec->size);
280
281	/* Set interrupt */
282	cookie_stfa->stfa_it = SDL_MintAudio_StfaInterrupt;
283
284	/* Restart replay */
285	cookie_stfa->sound_enable=STFA_PLAY_ENABLE|STFA_PLAY_REPEAT;
286
287	SuperToUser(oldpile);
288
289	DEBUG_PRINT((DEBUG_NAME "hardware initialized\n"));
290}
291
292static int Mint_OpenAudio(_THIS, SDL_AudioSpec *spec)
293{
294	SDL_MintAudio_device = this;
295
296	/* Check audio capabilities */
297	if (Mint_CheckAudio(this, spec)==-1) {
298		return -1;
299	}
300
301	SDL_CalculateAudioSpec(spec);
302
303	/* Allocate memory for audio buffers in DMA-able RAM */
304	DEBUG_PRINT((DEBUG_NAME "buffer size=%d\n", spec->size));
305
306	SDL_MintAudio_audiobuf[0] = Atari_SysMalloc(spec->size *2, MX_STRAM);
307	if (SDL_MintAudio_audiobuf[0]==NULL) {
308		SDL_SetError("MINT_OpenAudio: Not enough memory for audio buffer");
309		return (-1);
310	}
311	SDL_MintAudio_audiobuf[1] = SDL_MintAudio_audiobuf[0] + spec->size ;
312	SDL_MintAudio_numbuf=0;
313	SDL_memset(SDL_MintAudio_audiobuf[0], spec->silence, spec->size *2);
314	SDL_MintAudio_audiosize = spec->size;
315	SDL_MintAudio_mutex = 0;
316
317	DEBUG_PRINT((DEBUG_NAME "buffer 0 at 0x%08x\n", SDL_MintAudio_audiobuf[0]));
318	DEBUG_PRINT((DEBUG_NAME "buffer 1 at 0x%08x\n", SDL_MintAudio_audiobuf[1]));
319
320	SDL_MintAudio_CheckFpu();
321
322	/* Setup audio hardware */
323	Mint_InitAudio(this, spec);
324
325    return(1);	/* We don't use threaded audio */
326}
327