1 /* Sysdep Wave File Output sound dsp driver
2
3 Written by Donald King.
4 This file is placed in the Public Domain.
5 */
6
7 /* Changelog
8 Version 0.1, September 2002
9 - Initial release
10 */
11
12 #ifdef SYSDEP_DSP_WAVEOUT
13
14 #include <errno.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include "sysdep/sysdep_dsp.h"
19 #include "sysdep/sysdep_dsp_priv.h"
20 #include "sysdep/plugin_manager.h"
21
22 struct waveout_dsp_priv_data {
23 const char *name;
24 FILE *file;
25 };
26
27 static void *waveout_dsp_create(const void *flags);
28 static void waveout_dsp_destroy(struct sysdep_dsp_struct *dsp);
29 static int waveout_dsp_write(struct sysdep_dsp_struct *dsp, unsigned char *data, int count);
30
31 const struct plugin_struct sysdep_dsp_waveout = {
32 "waveout",
33 "sysdep_dsp",
34 "Wave File Output DSP plugin",
35 NULL, /* no options */
36 NULL, /* no init */
37 NULL, /* no exit */
38 waveout_dsp_create,
39 0 /* very low priority */
40 };
41
42 static int waveout_dsp_bytes_per_sample[4] = SYSDEP_DSP_BYTES_PER_SAMPLE;
43
44 static const unsigned char wave_template[44] = {
45 'R', 'I', 'F', 'F', /* 0: 'RIFF' magic */
46 0, 0, 0, 0, /* 4: 'RIFF' length */
47 'W', 'A', 'V', 'E', /* 8: 'RIFF' type */
48 'f', 'm', 't', ' ', /* 12: 'fmt ' chunk-type */
49 16, 0, 0, 0, /* 16: 'fmt ' chunk-length */
50 1, 0, /* 20: WAVE_FORMAT_PCM */
51 1, 0, /* 22: Channels */
52 0, 0, 0, 0, /* 24: Samples per second */
53 0, 0, 0, 0, /* 28: Bytes per second */
54 1, 0, /* 32: Aligned bytes per sample group */
55 8, 0, /* 34: Bits per sample */
56 'd', 'a', 't', 'a', /* 36: 'data' chunk-type */
57 0, 0, 0, 0, /* 40: 'data' chunk-length */
58 };
59
waveout_dsp_create(const void * flags)60 static void *waveout_dsp_create(const void *flags)
61 {
62 const struct sysdep_dsp_create_params *params;
63 struct sysdep_dsp_struct *dsp;
64 struct waveout_dsp_priv_data *priv;
65 unsigned char *header;
66 unsigned long tmp;
67
68 params = flags;
69
70 /* Allocate DSP structure */
71 if (!(dsp = calloc(1, sizeof(struct sysdep_dsp_struct))))
72 {
73 fprintf(stderr, "error: failed to malloc struct sysdep_dsp_struct\n");
74 return NULL;
75 }
76
77 /* Allocate private storage */
78 if (!(priv = calloc(1, sizeof(struct waveout_dsp_priv_data))))
79 {
80 fprintf(stderr, "error: failed to malloc struct waveout_dsp_priv_data\n");
81 free(dsp);
82 return NULL;
83 }
84
85 /* Allocate temporary storage for RIFF WAVE header */
86 if (!(header = malloc(44)))
87 {
88 fprintf(stderr, "error: failed to malloc WAVE header storage\n");
89 free(priv);
90 free(dsp);
91 return NULL;
92 }
93
94 /* Fill in DSP structure */
95 dsp->hw_info.type = params->type;
96 dsp->hw_info.samplerate = params->samplerate;
97 dsp->write = waveout_dsp_write;
98 dsp->destroy = waveout_dsp_destroy;
99 dsp->_priv = priv;
100
101 /* Fill in private storage */
102 priv->name = params->device;
103 if (!priv->name)
104 priv->name = "xmameout.wav";
105
106 /* Compute RIFF WAVE header */
107 memcpy(header, wave_template, 44);
108 tmp = dsp->hw_info.samplerate;
109 if (dsp->hw_info.type & SYSDEP_DSP_STEREO)
110 header[22] = 2;
111 header[24] = (unsigned char)tmp;
112 header[25] = (unsigned char)(tmp >> 8);
113 header[26] = (unsigned char)(tmp >> 16);
114 header[27] = (unsigned char)(tmp >> 24);
115 tmp *= waveout_dsp_bytes_per_sample[dsp->hw_info.type];
116 header[28] = (unsigned char)tmp;
117 header[29] = (unsigned char)(tmp >> 8);
118 header[30] = (unsigned char)(tmp >> 16);
119 header[31] = (unsigned char)(tmp >> 24);
120 header[32] = waveout_dsp_bytes_per_sample[dsp->hw_info.type];
121 if (dsp->hw_info.type & SYSDEP_DSP_16BIT)
122 header[34] = 16;
123
124 /* Open output file */
125 if (!(priv->file = fopen(priv->name, "wb")))
126 {
127 fprintf(stderr, "error: failed to open \"%s\" for writing: %s\n", priv->name, strerror(errno));
128 free(header);
129 free(priv);
130 free(dsp);
131 return NULL;
132 }
133
134 /* Write computed header to disk */
135 if (fwrite(header, 1, 44, priv->file) != 44)
136 {
137 fprintf(stderr, "error: failed to write WAVE header to \"%s\": %s\n", priv->name, strerror(errno));
138 fclose(priv->file);
139 free(header);
140 free(priv);
141 free(dsp);
142 }
143
144 /* Free the header */
145 free(header);
146
147 fprintf(stderr, "info: Writing sound output to file \"%s\" in %dHz/%d/%c PCM format\n",
148 priv->name,
149 dsp->hw_info.samplerate,
150 (dsp->hw_info.type & SYSDEP_DSP_16BIT) ? 16 : 8,
151 (dsp->hw_info.type & SYSDEP_DSP_STEREO) ? 'S' : 'M'
152 );
153
154 return dsp;
155 }
156
waveout_dsp_destroy(struct sysdep_dsp_struct * dsp)157 static void waveout_dsp_destroy(struct sysdep_dsp_struct *dsp)
158 {
159 struct waveout_dsp_priv_data *priv = dsp->_priv;
160
161 if (priv)
162 {
163 if (priv->file)
164 {
165 unsigned char tmp[4];
166 long pos;
167
168 /* Find out where we are in the file */
169 pos = ftell(priv->file);
170
171 /* Update the RIFF file length */
172 pos -= 8L;
173 tmp[0] = (unsigned char)pos;
174 tmp[1] = (unsigned char)(pos >> 8);
175 tmp[2] = (unsigned char)(pos >> 16);
176 tmp[3] = (unsigned char)(pos >> 24);
177 fseek(priv->file, 4L, SEEK_SET);
178 fwrite(tmp, 4, 1, priv->file);
179
180 /* Update the data chunk length */
181 pos -= 36L;
182 tmp[0] = (unsigned char)pos;
183 tmp[1] = (unsigned char)(pos >> 8);
184 tmp[2] = (unsigned char)(pos >> 16);
185 tmp[3] = (unsigned char)(pos >> 24);
186 fseek(priv->file, 40L, SEEK_SET);
187 fwrite(tmp, 4, 1, priv->file);
188
189 /* All done, so close the file */
190 fclose(priv->file);
191 }
192 free(priv);
193 }
194 free(dsp);
195 }
196
waveout_dsp_write(struct sysdep_dsp_struct * dsp,unsigned char * data,int count)197 static int waveout_dsp_write(struct sysdep_dsp_struct *dsp, unsigned char *data, int count)
198 {
199 if(count > 0)
200 {
201 struct waveout_dsp_priv_data *priv;
202 size_t result;
203
204 priv = dsp->_priv;
205
206 /* FIXME: This produces invalid PCM WAVE files on big-endian systems */
207 result = fwrite(data, waveout_dsp_bytes_per_sample[dsp->hw_info.type], count, priv->file);
208
209 if (result != count)
210 fprintf(stderr, "error: failed to write %d samples to \"%s\": %s\n", count, priv->name, strerror(errno));
211 return result;
212 }
213 return -1;
214 }
215
216 #endif /* SYSDEP_DSP_WAVEOUT */
217