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 Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 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    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18
19    Sam Lantinga
20    slouken@libsdl.org
21
22    This file written by Ryan C. Gordon (icculus@icculus.org)
23*/
24#include "SDL_config.h"
25
26/* Output raw audio data to a file. */
27
28#if HAVE_STDIO_H
29#include <stdio.h>
30#endif
31
32#include "SDL_rwops.h"
33#include "SDL_timer.h"
34#include "SDL_audio.h"
35#include "../SDL_audiomem.h"
36#include "../SDL_audio_c.h"
37#include "../SDL_audiodev_c.h"
38#include "SDL_diskaudio.h"
39
40/* The tag name used by DISK audio */
41#define DISKAUD_DRIVER_NAME         "disk"
42
43/* environment variables and defaults. */
44#define DISKENVR_OUTFILE         "SDL_DISKAUDIOFILE"
45#define DISKDEFAULT_OUTFILE      "sdlaudio.raw"
46#define DISKENVR_WRITEDELAY      "SDL_DISKAUDIODELAY"
47#define DISKDEFAULT_WRITEDELAY   150
48
49/* Audio driver functions */
50static int DISKAUD_OpenAudio(_THIS, SDL_AudioSpec *spec);
51static void DISKAUD_WaitAudio(_THIS);
52static void DISKAUD_PlayAudio(_THIS);
53static Uint8 *DISKAUD_GetAudioBuf(_THIS);
54static void DISKAUD_CloseAudio(_THIS);
55
56static const char *DISKAUD_GetOutputFilename(void)
57{
58	const char *envr = SDL_getenv(DISKENVR_OUTFILE);
59	return((envr != NULL) ? envr : DISKDEFAULT_OUTFILE);
60}
61
62/* Audio driver bootstrap functions */
63static int DISKAUD_Available(void)
64{
65	const char *envr = SDL_getenv("SDL_AUDIODRIVER");
66	if (envr && (SDL_strcmp(envr, DISKAUD_DRIVER_NAME) == 0)) {
67		return(1);
68	}
69	return(0);
70}
71
72static void DISKAUD_DeleteDevice(SDL_AudioDevice *device)
73{
74	SDL_free(device->hidden);
75	SDL_free(device);
76}
77
78static SDL_AudioDevice *DISKAUD_CreateDevice(int devindex)
79{
80	SDL_AudioDevice *this;
81	const char *envr;
82
83	/* Initialize all variables that we clean on shutdown */
84	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
85	if ( this ) {
86		SDL_memset(this, 0, (sizeof *this));
87		this->hidden = (struct SDL_PrivateAudioData *)
88				SDL_malloc((sizeof *this->hidden));
89	}
90	if ( (this == NULL) || (this->hidden == NULL) ) {
91		SDL_OutOfMemory();
92		if ( this ) {
93			SDL_free(this);
94		}
95		return(0);
96	}
97	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
98
99	envr = SDL_getenv(DISKENVR_WRITEDELAY);
100	this->hidden->write_delay = (envr) ? SDL_atoi(envr) : DISKDEFAULT_WRITEDELAY;
101
102	/* Set the function pointers */
103	this->OpenAudio = DISKAUD_OpenAudio;
104	this->WaitAudio = DISKAUD_WaitAudio;
105	this->PlayAudio = DISKAUD_PlayAudio;
106	this->GetAudioBuf = DISKAUD_GetAudioBuf;
107	this->CloseAudio = DISKAUD_CloseAudio;
108
109	this->free = DISKAUD_DeleteDevice;
110
111	return this;
112}
113
114AudioBootStrap DISKAUD_bootstrap = {
115	DISKAUD_DRIVER_NAME, "direct-to-disk audio",
116	DISKAUD_Available, DISKAUD_CreateDevice
117};
118
119/* This function waits until it is possible to write a full sound buffer */
120static void DISKAUD_WaitAudio(_THIS)
121{
122	SDL_Delay(this->hidden->write_delay);
123}
124
125static void DISKAUD_PlayAudio(_THIS)
126{
127	int written;
128
129	/* Write the audio data */
130	written = SDL_RWwrite(this->hidden->output,
131                        this->hidden->mixbuf, 1,
132                        this->hidden->mixlen);
133
134	/* If we couldn't write, assume fatal error for now */
135	if ( (Uint32)written != this->hidden->mixlen ) {
136		this->enabled = 0;
137	}
138#ifdef DEBUG_AUDIO
139	fprintf(stderr, "Wrote %d bytes of audio data\n", written);
140#endif
141}
142
143static Uint8 *DISKAUD_GetAudioBuf(_THIS)
144{
145	return(this->hidden->mixbuf);
146}
147
148static void DISKAUD_CloseAudio(_THIS)
149{
150	if ( this->hidden->mixbuf != NULL ) {
151		SDL_FreeAudioMem(this->hidden->mixbuf);
152		this->hidden->mixbuf = NULL;
153	}
154	if ( this->hidden->output != NULL ) {
155		SDL_RWclose(this->hidden->output);
156		this->hidden->output = NULL;
157	}
158}
159
160static int DISKAUD_OpenAudio(_THIS, SDL_AudioSpec *spec)
161{
162	const char *fname = DISKAUD_GetOutputFilename();
163
164	/* Open the audio device */
165	this->hidden->output = SDL_RWFromFile(fname, "wb");
166	if ( this->hidden->output == NULL ) {
167		return(-1);
168	}
169
170#if HAVE_STDIO_H
171	fprintf(stderr, "WARNING: You are using the SDL disk writer"
172                    " audio driver!\n Writing to file [%s].\n", fname);
173#endif
174
175	/* Allocate mixing buffer */
176	this->hidden->mixlen = spec->size;
177	this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
178	if ( this->hidden->mixbuf == NULL ) {
179		return(-1);
180	}
181	SDL_memset(this->hidden->mixbuf, spec->silence, spec->size);
182
183	/* We're ready to rock and roll. :-) */
184	return(0);
185}
186
187