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/* Tru64 UNIX MME support */
25#include <mme_api.h>
26
27#include "SDL_timer.h"
28#include "SDL_audio.h"
29#include "../SDL_audio_c.h"
30#include "SDL_mmeaudio.h"
31
32static BOOL inUse[NUM_BUFFERS];
33
34/* Audio driver functions */
35static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec);
36static void MME_WaitAudio(_THIS);
37static Uint8 *MME_GetAudioBuf(_THIS);
38static void MME_PlayAudio(_THIS);
39static void MME_WaitDone(_THIS);
40static void MME_CloseAudio(_THIS);
41
42/* Audio driver bootstrap functions */
43static int Audio_Available(void)
44{
45    return(1);
46}
47
48static void Audio_DeleteDevice(SDL_AudioDevice *device)
49{
50    if ( device ) {
51	if ( device->hidden ) {
52	    SDL_free(device->hidden);
53	    device->hidden = NULL;
54	}
55	SDL_free(device);
56	device = NULL;
57    }
58}
59
60static SDL_AudioDevice *Audio_CreateDevice(int devindex)
61{
62    SDL_AudioDevice *this;
63
64/* Initialize all variables that we clean on shutdown */
65    this = SDL_malloc(sizeof(SDL_AudioDevice));
66    if ( this ) {
67	SDL_memset(this, 0, (sizeof *this));
68	this->hidden = SDL_malloc((sizeof *this->hidden));
69    }
70    if ( (this == NULL) || (this->hidden == NULL) ) {
71	SDL_OutOfMemory();
72	if ( this ) {
73	    SDL_free(this);
74	}
75	return(0);
76    }
77    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
78    /* Set the function pointers */
79    this->OpenAudio       =       MME_OpenAudio;
80    this->WaitAudio       =       MME_WaitAudio;
81    this->PlayAudio       =       MME_PlayAudio;
82    this->GetAudioBuf     =     MME_GetAudioBuf;
83    this->WaitDone        =        MME_WaitDone;
84    this->CloseAudio      =      MME_CloseAudio;
85    this->free            =  Audio_DeleteDevice;
86
87    return this;
88}
89
90AudioBootStrap MMEAUDIO_bootstrap = {
91    "waveout", "Tru64 MME WaveOut",
92    Audio_Available, Audio_CreateDevice
93};
94
95static void SetMMerror(char *function, MMRESULT code)
96{
97    int len;
98    char errbuf[MAXERRORLENGTH];
99
100    SDL_snprintf(errbuf, SDL_arraysize(errbuf), "%s: ", function);
101    len = SDL_strlen(errbuf);
102    waveOutGetErrorText(code, errbuf+len, MAXERRORLENGTH-len);
103    SDL_SetError("%s",errbuf);
104}
105
106static void CALLBACK MME_CALLBACK(HWAVEOUT hwo,
107				  UINT uMsg,
108				  DWORD dwInstance,
109				  LPARAM dwParam1,
110				  LPARAM dwParam2)
111{
112    WAVEHDR *wp = (WAVEHDR *) dwParam1;
113
114    if ( uMsg == WOM_DONE )
115	inUse[wp->dwUser] = FALSE;
116}
117
118static int MME_OpenAudio(_THIS, SDL_AudioSpec *spec)
119{
120    MMRESULT result;
121    int i;
122
123    mixbuf = NULL;
124
125    /* Set basic WAVE format parameters */
126    shm = mmeAllocMem(sizeof(*shm));
127    if ( shm == NULL ) {
128	SDL_SetError("Out of memory: shm");
129	return(-1);
130    }
131    shm->sound = 0;
132    shm->wFmt.wf.wFormatTag = WAVE_FORMAT_PCM;
133
134    /* Determine the audio parameters from the AudioSpec */
135    switch ( spec->format & 0xFF ) {
136	case 8:
137	    /* Unsigned 8 bit audio data */
138	    spec->format = AUDIO_U8;
139	    shm->wFmt.wBitsPerSample = 8;
140	    break;
141	case 16:
142	    /* Signed 16 bit audio data */
143	    spec->format = AUDIO_S16;
144	    shm->wFmt.wBitsPerSample = 16;
145	    break;
146	    default:
147	    SDL_SetError("Unsupported audio format");
148	    return(-1);
149    }
150
151    shm->wFmt.wf.nChannels = spec->channels;
152    shm->wFmt.wf.nSamplesPerSec = spec->freq;
153    shm->wFmt.wf.nBlockAlign =
154	shm->wFmt.wf.nChannels * shm->wFmt.wBitsPerSample / 8;
155    shm->wFmt.wf.nAvgBytesPerSec =
156	shm->wFmt.wf.nSamplesPerSec * shm->wFmt.wf.nBlockAlign;
157
158    /* Check the buffer size -- minimum of 1/4 second (word aligned) */
159    if ( spec->samples < (spec->freq/4) )
160	spec->samples = ((spec->freq/4)+3)&~3;
161
162    /* Update the fragment size as size in bytes */
163    SDL_CalculateAudioSpec(spec);
164
165    /* Open the audio device */
166    result = waveOutOpen(&(shm->sound),
167			 WAVE_MAPPER,
168			 &(shm->wFmt.wf),
169			 MME_CALLBACK,
170			 NULL,
171			 (CALLBACK_FUNCTION|WAVE_OPEN_SHAREABLE));
172    if ( result != MMSYSERR_NOERROR ) {
173	    SetMMerror("waveOutOpen()", result);
174	    return(-1);
175    }
176
177    /* Create the sound buffers */
178    mixbuf = (Uint8 *)mmeAllocBuffer(NUM_BUFFERS * (spec->size));
179    if ( mixbuf == NULL ) {
180	SDL_SetError("Out of memory: mixbuf");
181	return(-1);
182    }
183
184    for (i = 0; i < NUM_BUFFERS; i++) {
185	shm->wHdr[i].lpData         = &mixbuf[i * (spec->size)];
186	shm->wHdr[i].dwBufferLength = spec->size;
187	shm->wHdr[i].dwFlags        = 0;
188	shm->wHdr[i].dwUser         = i;
189	shm->wHdr[i].dwLoops        = 0;       /* loop control counter */
190	shm->wHdr[i].lpNext         = NULL;    /* reserved for driver */
191	shm->wHdr[i].reserved       = 0;
192	inUse[i] = FALSE;
193    }
194    next_buffer = 0;
195    return 0;
196}
197
198static void MME_WaitAudio(_THIS)
199{
200    while ( inUse[next_buffer] ) {
201	mmeWaitForCallbacks();
202	mmeProcessCallbacks();
203    }
204}
205
206static Uint8 *MME_GetAudioBuf(_THIS)
207{
208    Uint8 *retval;
209
210    inUse[next_buffer] = TRUE;
211    retval = (Uint8 *)(shm->wHdr[next_buffer].lpData);
212    return retval;
213}
214
215static void MME_PlayAudio(_THIS)
216{
217    /* Queue it up */
218    waveOutWrite(shm->sound, &(shm->wHdr[next_buffer]), sizeof(WAVEHDR));
219    next_buffer = (next_buffer+1)%NUM_BUFFERS;
220}
221
222static void MME_WaitDone(_THIS)
223{
224    MMRESULT result;
225    int i;
226
227    if ( shm->sound ) {
228	for (i = 0; i < NUM_BUFFERS; i++)
229	    while ( inUse[i] ) {
230		mmeWaitForCallbacks();
231		mmeProcessCallbacks();
232	    }
233	result = waveOutReset(shm->sound);
234	if ( result != MMSYSERR_NOERROR )
235	    SetMMerror("waveOutReset()", result);
236	mmeProcessCallbacks();
237    }
238}
239
240static void MME_CloseAudio(_THIS)
241{
242    MMRESULT result;
243
244    if ( mixbuf ) {
245	result = mmeFreeBuffer(mixbuf);
246	if (result != MMSYSERR_NOERROR )
247	    SetMMerror("mmeFreeBuffer", result);
248	mixbuf = NULL;
249    }
250
251    if ( shm ) {
252	if ( shm->sound ) {
253	    result = waveOutClose(shm->sound);
254	    if (result != MMSYSERR_NOERROR )
255		SetMMerror("waveOutClose()", result);
256	    mmeProcessCallbacks();
257	}
258	result = mmeFreeMem(shm);
259	if (result != MMSYSERR_NOERROR )
260	    SetMMerror("mmeFreeMem()", result);
261	shm = NULL;
262    }
263}
264
265