xref: /openbmc/qemu/audio/wavaudio.c (revision f09744ddc2424bc6a76702e1951a8d24917062d6)
185571bc7Sbellard /*
21d14ffa9Sbellard  * QEMU WAV audio driver
385571bc7Sbellard  *
41d14ffa9Sbellard  * Copyright (c) 2004-2005 Vassili Karpov (malc)
585571bc7Sbellard  *
685571bc7Sbellard  * Permission is hereby granted, free of charge, to any person obtaining a copy
785571bc7Sbellard  * of this software and associated documentation files (the "Software"), to deal
885571bc7Sbellard  * in the Software without restriction, including without limitation the rights
985571bc7Sbellard  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
1085571bc7Sbellard  * copies of the Software, and to permit persons to whom the Software is
1185571bc7Sbellard  * furnished to do so, subject to the following conditions:
1285571bc7Sbellard  *
1385571bc7Sbellard  * The above copyright notice and this permission notice shall be included in
1485571bc7Sbellard  * all copies or substantial portions of the Software.
1585571bc7Sbellard  *
1685571bc7Sbellard  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1785571bc7Sbellard  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1885571bc7Sbellard  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
1985571bc7Sbellard  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
2085571bc7Sbellard  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2185571bc7Sbellard  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
2285571bc7Sbellard  * THE SOFTWARE.
2385571bc7Sbellard  */
240b8fa32fSMarkus Armbruster 
256086a565SPeter Maydell #include "qemu/osdep.h"
2687776ab7SPaolo Bonzini #include "qemu/host-utils.h"
270b8fa32fSMarkus Armbruster #include "qemu/module.h"
281de7afc9SPaolo Bonzini #include "qemu/timer.h"
290927d166SKővágó, Zoltán #include "qapi/opts-visitor.h"
3087ecb68bSpbrook #include "audio.h"
3185571bc7Sbellard 
321d14ffa9Sbellard #define AUDIO_CAP "wav"
331d14ffa9Sbellard #include "audio_int.h"
34fb065187Sbellard 
351d14ffa9Sbellard typedef struct WAVVoiceOut {
361d14ffa9Sbellard     HWVoiceOut hw;
3727acf660SJuan Quintela     FILE *f;
38857271a2SKővágó, Zoltán     RateCtl rate;
39fb065187Sbellard     int total_samples;
401d14ffa9Sbellard } WAVVoiceOut;
4185571bc7Sbellard 
wav_write_out(HWVoiceOut * hw,void * buf,size_t len)42ef3612e1SKővágó, Zoltán static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
4385571bc7Sbellard {
441d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
45613fe02bSVolker Rümelin     int64_t bytes = audio_rate_get_bytes(&wav->rate, &hw->info, len);
462b9cce8cSKővágó, Zoltán     assert(bytes % hw->info.bytes_per_frame == 0);
4785571bc7Sbellard 
48ef3612e1SKővágó, Zoltán     if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
49ef3612e1SKővágó, Zoltán         dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
50ef3612e1SKővágó, Zoltán               bytes, strerror(errno));
5127acf660SJuan Quintela     }
5285571bc7Sbellard 
532b9cce8cSKővágó, Zoltán     wav->total_samples += bytes / hw->info.bytes_per_frame;
54ef3612e1SKővágó, Zoltán     return bytes;
5585571bc7Sbellard }
5685571bc7Sbellard 
5785571bc7Sbellard /* VICE code: Store number as little endian. */
le_store(uint8_t * buf,uint32_t val,int len)5885571bc7Sbellard static void le_store (uint8_t *buf, uint32_t val, int len)
5985571bc7Sbellard {
6085571bc7Sbellard     int i;
6185571bc7Sbellard     for (i = 0; i < len; i++) {
6285571bc7Sbellard         buf[i] = (uint8_t) (val & 0xff);
6385571bc7Sbellard         val >>= 8;
6485571bc7Sbellard     }
6585571bc7Sbellard }
6685571bc7Sbellard 
wav_init_out(HWVoiceOut * hw,struct audsettings * as,void * drv_opaque)675706db1dSKővágó, Zoltán static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
685706db1dSKővágó, Zoltán                         void *drv_opaque)
6985571bc7Sbellard {
701d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
71c0fe3827Sbellard     int bits16 = 0, stereo = 0;
7285571bc7Sbellard     uint8_t hdr[] = {
7385571bc7Sbellard         0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
7485571bc7Sbellard         0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
7585571bc7Sbellard         0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
7685571bc7Sbellard         0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
7785571bc7Sbellard     };
780927d166SKővágó, Zoltán     Audiodev *dev = drv_opaque;
790927d166SKővágó, Zoltán     AudiodevWavOptions *wopts = &dev->u.wav;
800927d166SKővágó, Zoltán     struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out);
81ceb19c8fSMarkus Armbruster     const char *wav_path = wopts->path ?: "qemu.wav";
8285571bc7Sbellard 
83c0fe3827Sbellard     stereo = wav_as.nchannels == 2;
84c0fe3827Sbellard     switch (wav_as.fmt) {
8585bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S8:
8685bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U8:
871d14ffa9Sbellard         bits16 = 0;
8885571bc7Sbellard         break;
8985571bc7Sbellard 
9085bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S16:
9185bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U16:
9285571bc7Sbellard         bits16 = 1;
9385571bc7Sbellard         break;
94f941aa25Sths 
9585bc5852SKővágó, Zoltán     case AUDIO_FORMAT_S32:
9685bc5852SKővágó, Zoltán     case AUDIO_FORMAT_U32:
97f941aa25Sths         dolog ("WAVE files can not handle 32bit formats\n");
98f941aa25Sths         return -1;
9985bc5852SKővágó, Zoltán 
100*5b4edd72SDaniel P. Berrangé     case AUDIO_FORMAT_F32:
101*5b4edd72SDaniel P. Berrangé         dolog("WAVE files can not handle float formats\n");
102*5b4edd72SDaniel P. Berrangé         return -1;
103*5b4edd72SDaniel P. Berrangé 
10485bc5852SKővágó, Zoltán     default:
10585bc5852SKővágó, Zoltán         abort();
10685571bc7Sbellard     }
10785571bc7Sbellard 
10885571bc7Sbellard     hdr[34] = bits16 ? 0x10 : 0x08;
109c0fe3827Sbellard 
110d929eba5Sbellard     wav_as.endianness = 0;
111d929eba5Sbellard     audio_pcm_init_info (&hw->info, &wav_as);
112c0fe3827Sbellard 
113c0fe3827Sbellard     hw->samples = 1024;
1141d14ffa9Sbellard     le_store (hdr + 22, hw->info.nchannels, 2);
1151d14ffa9Sbellard     le_store (hdr + 24, hw->info.freq, 4);
116c0fe3827Sbellard     le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
117c0fe3827Sbellard     le_store (hdr + 32, 1 << (bits16 + stereo), 2);
11885571bc7Sbellard 
1190927d166SKővágó, Zoltán     wav->f = fopen(wav_path, "wb");
12085571bc7Sbellard     if (!wav->f) {
1211d14ffa9Sbellard         dolog ("Failed to open wave file `%s'\nReason: %s\n",
1220927d166SKővágó, Zoltán                wav_path, strerror(errno));
12385571bc7Sbellard         return -1;
12485571bc7Sbellard     }
12585571bc7Sbellard 
12627acf660SJuan Quintela     if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) {
12727acf660SJuan Quintela         dolog ("wav_init_out: failed to write header\nReason: %s\n",
12827acf660SJuan Quintela                strerror(errno));
12927acf660SJuan Quintela         return -1;
13027acf660SJuan Quintela     }
131857271a2SKővágó, Zoltán 
132857271a2SKővágó, Zoltán     audio_rate_start(&wav->rate);
13385571bc7Sbellard     return 0;
13485571bc7Sbellard }
13585571bc7Sbellard 
wav_fini_out(HWVoiceOut * hw)1361d14ffa9Sbellard static void wav_fini_out (HWVoiceOut *hw)
13785571bc7Sbellard {
1381d14ffa9Sbellard     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
13985571bc7Sbellard     uint8_t rlen[4];
14085571bc7Sbellard     uint8_t dlen[4];
1412b9cce8cSKővágó, Zoltán     uint32_t datalen = wav->total_samples * hw->info.bytes_per_frame;
14250903530Sbellard     uint32_t rifflen = datalen + 36;
14385571bc7Sbellard 
144c0fe3827Sbellard     if (!wav->f) {
14585571bc7Sbellard         return;
1461d14ffa9Sbellard     }
14785571bc7Sbellard 
14885571bc7Sbellard     le_store (rlen, rifflen, 4);
14985571bc7Sbellard     le_store (dlen, datalen, 4);
15085571bc7Sbellard 
15127acf660SJuan Quintela     if (fseek (wav->f, 4, SEEK_SET)) {
15227acf660SJuan Quintela         dolog ("wav_fini_out: fseek to rlen failed\nReason: %s\n",
15327acf660SJuan Quintela                strerror(errno));
15427acf660SJuan Quintela         goto doclose;
15527acf660SJuan Quintela     }
15627acf660SJuan Quintela     if (fwrite (rlen, 4, 1, wav->f) != 1) {
15727acf660SJuan Quintela         dolog ("wav_fini_out: failed to write rlen\nReason: %s\n",
15827acf660SJuan Quintela                strerror (errno));
15927acf660SJuan Quintela         goto doclose;
16027acf660SJuan Quintela     }
16127acf660SJuan Quintela     if (fseek (wav->f, 32, SEEK_CUR)) {
16227acf660SJuan Quintela         dolog ("wav_fini_out: fseek to dlen failed\nReason: %s\n",
16327acf660SJuan Quintela                strerror (errno));
16427acf660SJuan Quintela         goto doclose;
16527acf660SJuan Quintela     }
16627acf660SJuan Quintela     if (fwrite (dlen, 4, 1, wav->f) != 1) {
16727acf660SJuan Quintela         dolog ("wav_fini_out: failed to write dlen\nReaons: %s\n",
16827acf660SJuan Quintela                strerror (errno));
16927acf660SJuan Quintela         goto doclose;
17027acf660SJuan Quintela     }
17185571bc7Sbellard 
17227acf660SJuan Quintela  doclose:
17327acf660SJuan Quintela     if (fclose (wav->f))  {
17427acf660SJuan Quintela         dolog ("wav_fini_out: fclose %p failed\nReason: %s\n",
17527acf660SJuan Quintela                wav->f, strerror (errno));
17627acf660SJuan Quintela     }
17785571bc7Sbellard     wav->f = NULL;
17885571bc7Sbellard }
17985571bc7Sbellard 
wav_enable_out(HWVoiceOut * hw,bool enable)180571a8c52SKővágó, Zoltán static void wav_enable_out(HWVoiceOut *hw, bool enable)
18185571bc7Sbellard {
182857271a2SKővágó, Zoltán     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
183857271a2SKővágó, Zoltán 
184571a8c52SKővágó, Zoltán     if (enable) {
185857271a2SKővágó, Zoltán         audio_rate_start(&wav->rate);
186857271a2SKővágó, Zoltán     }
18785571bc7Sbellard }
18885571bc7Sbellard 
wav_audio_init(Audiodev * dev,Error ** errp)189f6061733SPaolo Bonzini static void *wav_audio_init(Audiodev *dev, Error **errp)
19085571bc7Sbellard {
1910927d166SKővágó, Zoltán     assert(dev->driver == AUDIODEV_DRIVER_WAV);
1920927d166SKővágó, Zoltán     return dev;
19385571bc7Sbellard }
19485571bc7Sbellard 
wav_audio_fini(void * opaque)19585571bc7Sbellard static void wav_audio_fini (void *opaque)
19685571bc7Sbellard {
19785571bc7Sbellard     ldebug ("wav_fini");
19885571bc7Sbellard }
19985571bc7Sbellard 
20035f4b58cSblueswir1 static struct audio_pcm_ops wav_pcm_ops = {
2011dd3e4d1SJuan Quintela     .init_out = wav_init_out,
2021dd3e4d1SJuan Quintela     .fini_out = wav_fini_out,
203ef3612e1SKővágó, Zoltán     .write    = wav_write_out,
2049833438eSVolker Rümelin     .buffer_get_free = audio_generic_buffer_get_free,
205fdc8c5f4SVolker Rümelin     .run_buffer_out = audio_generic_run_buffer_out,
206571a8c52SKővágó, Zoltán     .enable_out = wav_enable_out,
2071d14ffa9Sbellard };
2081d14ffa9Sbellard 
209d3893a39SGerd Hoffmann static struct audio_driver wav_audio_driver = {
210bee37f32SJuan Quintela     .name           = "wav",
211bee37f32SJuan Quintela     .descr          = "WAV renderer http://wikipedia.org/wiki/WAV",
212bee37f32SJuan Quintela     .init           = wav_audio_init,
213bee37f32SJuan Quintela     .fini           = wav_audio_fini,
214bee37f32SJuan Quintela     .pcm_ops        = &wav_pcm_ops,
215bee37f32SJuan Quintela     .max_voices_out = 1,
216bee37f32SJuan Quintela     .max_voices_in  = 0,
217bee37f32SJuan Quintela     .voice_size_out = sizeof (WAVVoiceOut),
218bee37f32SJuan Quintela     .voice_size_in  = 0
21985571bc7Sbellard };
220d3893a39SGerd Hoffmann 
register_audio_wav(void)221d3893a39SGerd Hoffmann static void register_audio_wav(void)
222d3893a39SGerd Hoffmann {
223d3893a39SGerd Hoffmann     audio_driver_register(&wav_audio_driver);
224d3893a39SGerd Hoffmann }
225d3893a39SGerd Hoffmann type_init(register_audio_wav);
226