xref: /qemu/audio/wavaudio.c (revision 85571bc7415c3fa9390f5edc3720ec7975219a68)
1*85571bc7Sbellard /*
2*85571bc7Sbellard  * QEMU WAV audio output driver
3*85571bc7Sbellard  *
4*85571bc7Sbellard  * Copyright (c) 2004 Vassili Karpov (malc)
5*85571bc7Sbellard  *
6*85571bc7Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
7*85571bc7Sbellard  * of this software and associated documentation files (the "Software"), to deal
8*85571bc7Sbellard  * in the Software without restriction, including without limitation the rights
9*85571bc7Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10*85571bc7Sbellard  * copies of the Software, and to permit persons to whom the Software is
11*85571bc7Sbellard  * furnished to do so, subject to the following conditions:
12*85571bc7Sbellard  *
13*85571bc7Sbellard  * The above copyright notice and this permission notice shall be included in
14*85571bc7Sbellard  * all copies or substantial portions of the Software.
15*85571bc7Sbellard  *
16*85571bc7Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*85571bc7Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*85571bc7Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19*85571bc7Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*85571bc7Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21*85571bc7Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22*85571bc7Sbellard  * THE SOFTWARE.
23*85571bc7Sbellard  */
24*85571bc7Sbellard #include "vl.h"
25*85571bc7Sbellard 
26*85571bc7Sbellard #define AUDIO_CAP "wav"
27*85571bc7Sbellard #include "audio/audio.h"
28*85571bc7Sbellard #include "audio/wavaudio.h"
29*85571bc7Sbellard 
30*85571bc7Sbellard static struct {
31*85571bc7Sbellard     const char *wav_path;
32*85571bc7Sbellard } conf = {
33*85571bc7Sbellard     .wav_path = "qemu.wav"
34*85571bc7Sbellard };
35*85571bc7Sbellard 
36*85571bc7Sbellard static void wav_hw_run (HWVoice *hw)
37*85571bc7Sbellard {
38*85571bc7Sbellard     WAVVoice *wav = (WAVVoice *) hw;
39*85571bc7Sbellard     int rpos, live, decr, samples;
40*85571bc7Sbellard     uint8_t *dst;
41*85571bc7Sbellard     st_sample_t *src;
42*85571bc7Sbellard     int64_t now = qemu_get_clock (vm_clock);
43*85571bc7Sbellard     int64_t ticks = now - wav->old_ticks;
44*85571bc7Sbellard     int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
45*85571bc7Sbellard     wav->old_ticks = now;
46*85571bc7Sbellard 
47*85571bc7Sbellard     if (bytes > INT_MAX)
48*85571bc7Sbellard         samples = INT_MAX >> hw->shift;
49*85571bc7Sbellard     else
50*85571bc7Sbellard         samples = bytes >> hw->shift;
51*85571bc7Sbellard 
52*85571bc7Sbellard     live = pcm_hw_get_live (hw);
53*85571bc7Sbellard     if (live <= 0)
54*85571bc7Sbellard         return;
55*85571bc7Sbellard 
56*85571bc7Sbellard     decr = audio_MIN (live, samples);
57*85571bc7Sbellard     samples = decr;
58*85571bc7Sbellard     rpos = hw->rpos;
59*85571bc7Sbellard     while (samples) {
60*85571bc7Sbellard         int left_till_end_samples = hw->samples - rpos;
61*85571bc7Sbellard         int convert_samples = audio_MIN (samples, left_till_end_samples);
62*85571bc7Sbellard 
63*85571bc7Sbellard         src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
64*85571bc7Sbellard         dst = advance (wav->pcm_buf, rpos << hw->shift);
65*85571bc7Sbellard 
66*85571bc7Sbellard         hw->clip (dst, src, convert_samples);
67*85571bc7Sbellard         qemu_put_buffer (wav->f, dst, convert_samples << hw->shift);
68*85571bc7Sbellard         memset (src, 0, convert_samples * sizeof (st_sample_t));
69*85571bc7Sbellard 
70*85571bc7Sbellard         rpos = (rpos + convert_samples) % hw->samples;
71*85571bc7Sbellard         samples -= convert_samples;
72*85571bc7Sbellard         wav->total_samples += convert_samples;
73*85571bc7Sbellard     }
74*85571bc7Sbellard 
75*85571bc7Sbellard     pcm_hw_dec_live (hw, decr);
76*85571bc7Sbellard     hw->rpos = rpos;
77*85571bc7Sbellard }
78*85571bc7Sbellard 
79*85571bc7Sbellard static int wav_hw_write (SWVoice *sw, void *buf, int len)
80*85571bc7Sbellard {
81*85571bc7Sbellard     return pcm_hw_write (sw, buf, len);
82*85571bc7Sbellard }
83*85571bc7Sbellard 
84*85571bc7Sbellard 
85*85571bc7Sbellard /* VICE code: Store number as little endian. */
86*85571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len)
87*85571bc7Sbellard {
88*85571bc7Sbellard     int i;
89*85571bc7Sbellard     for (i = 0; i < len; i++) {
90*85571bc7Sbellard         buf[i] = (uint8_t) (val & 0xff);
91*85571bc7Sbellard         val >>= 8;
92*85571bc7Sbellard     }
93*85571bc7Sbellard }
94*85571bc7Sbellard 
95*85571bc7Sbellard static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
96*85571bc7Sbellard {
97*85571bc7Sbellard     WAVVoice *wav = (WAVVoice *) hw;
98*85571bc7Sbellard     int bits16 = 0, stereo = audio_state.fixed_channels == 2;
99*85571bc7Sbellard     uint8_t hdr[] = {
100*85571bc7Sbellard         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
101*85571bc7Sbellard         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
102*85571bc7Sbellard         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
103*85571bc7Sbellard         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
104*85571bc7Sbellard     };
105*85571bc7Sbellard 
106*85571bc7Sbellard     switch (audio_state.fixed_fmt) {
107*85571bc7Sbellard     case AUD_FMT_S8:
108*85571bc7Sbellard     case AUD_FMT_U8:
109*85571bc7Sbellard         break;
110*85571bc7Sbellard 
111*85571bc7Sbellard     case AUD_FMT_S16:
112*85571bc7Sbellard     case AUD_FMT_U16:
113*85571bc7Sbellard         bits16 = 1;
114*85571bc7Sbellard         break;
115*85571bc7Sbellard     }
116*85571bc7Sbellard 
117*85571bc7Sbellard     hdr[34] = bits16 ? 0x10 : 0x08;
118*85571bc7Sbellard     hw->freq = 44100;
119*85571bc7Sbellard     hw->nchannels = stereo ? 2 : 1;
120*85571bc7Sbellard     hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
121*85571bc7Sbellard     hw->bufsize = 4096;
122*85571bc7Sbellard     wav->pcm_buf = qemu_mallocz (hw->bufsize);
123*85571bc7Sbellard     if (!wav->pcm_buf)
124*85571bc7Sbellard         return -1;
125*85571bc7Sbellard 
126*85571bc7Sbellard     le_store (hdr + 22, hw->nchannels, 2);
127*85571bc7Sbellard     le_store (hdr + 24, hw->freq, 4);
128*85571bc7Sbellard     le_store (hdr + 28, hw->freq << (bits16 + stereo), 4);
129*85571bc7Sbellard     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
130*85571bc7Sbellard 
131*85571bc7Sbellard     wav->f = fopen (conf.wav_path, "wb");
132*85571bc7Sbellard     if (!wav->f) {
133*85571bc7Sbellard         dolog ("failed to open wave file `%s'\nReason: %s\n",
134*85571bc7Sbellard                conf.wav_path, strerror (errno));
135*85571bc7Sbellard         return -1;
136*85571bc7Sbellard     }
137*85571bc7Sbellard 
138*85571bc7Sbellard     qemu_put_buffer (wav->f, hdr, sizeof (hdr));
139*85571bc7Sbellard     return 0;
140*85571bc7Sbellard }
141*85571bc7Sbellard 
142*85571bc7Sbellard static void wav_hw_fini (HWVoice *hw)
143*85571bc7Sbellard {
144*85571bc7Sbellard     WAVVoice *wav = (WAVVoice *) hw;
145*85571bc7Sbellard     int stereo = hw->nchannels == 2;
146*85571bc7Sbellard     uint8_t rlen[4];
147*85571bc7Sbellard     uint8_t dlen[4];
148*85571bc7Sbellard     uint32_t rifflen = (wav->total_samples << stereo) + 36;
149*85571bc7Sbellard     uint32_t datalen = wav->total_samples << stereo;
150*85571bc7Sbellard 
151*85571bc7Sbellard     if (!wav->f || !hw->active)
152*85571bc7Sbellard         return;
153*85571bc7Sbellard 
154*85571bc7Sbellard     le_store (rlen, rifflen, 4);
155*85571bc7Sbellard     le_store (dlen, datalen, 4);
156*85571bc7Sbellard 
157*85571bc7Sbellard     qemu_fseek (wav->f, 4, SEEK_SET);
158*85571bc7Sbellard     qemu_put_buffer (wav->f, rlen, 4);
159*85571bc7Sbellard 
160*85571bc7Sbellard     qemu_fseek (wav->f, 32, SEEK_CUR);
161*85571bc7Sbellard     qemu_put_buffer (wav->f, dlen, 4);
162*85571bc7Sbellard 
163*85571bc7Sbellard     fclose (wav->f);
164*85571bc7Sbellard     wav->f = NULL;
165*85571bc7Sbellard }
166*85571bc7Sbellard 
167*85571bc7Sbellard static int wav_hw_ctl (HWVoice *hw, int cmd, ...)
168*85571bc7Sbellard {
169*85571bc7Sbellard     (void) hw;
170*85571bc7Sbellard     (void) cmd;
171*85571bc7Sbellard     return 0;
172*85571bc7Sbellard }
173*85571bc7Sbellard 
174*85571bc7Sbellard static void *wav_audio_init (void)
175*85571bc7Sbellard {
176*85571bc7Sbellard     return &conf;
177*85571bc7Sbellard }
178*85571bc7Sbellard 
179*85571bc7Sbellard static void wav_audio_fini (void *opaque)
180*85571bc7Sbellard {
181*85571bc7Sbellard     ldebug ("wav_fini");
182*85571bc7Sbellard }
183*85571bc7Sbellard 
184*85571bc7Sbellard struct pcm_ops wav_pcm_ops = {
185*85571bc7Sbellard     wav_hw_init,
186*85571bc7Sbellard     wav_hw_fini,
187*85571bc7Sbellard     wav_hw_run,
188*85571bc7Sbellard     wav_hw_write,
189*85571bc7Sbellard     wav_hw_ctl
190*85571bc7Sbellard };
191*85571bc7Sbellard 
192*85571bc7Sbellard struct audio_output_driver wav_output_driver = {
193*85571bc7Sbellard     "wav",
194*85571bc7Sbellard     wav_audio_init,
195*85571bc7Sbellard     wav_audio_fini,
196*85571bc7Sbellard     &wav_pcm_ops,
197*85571bc7Sbellard     1,
198*85571bc7Sbellard     1,
199*85571bc7Sbellard     sizeof (WAVVoice)
200*85571bc7Sbellard };
201