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