qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

audio: proper support for float samples in mixeng

This adds proper support for float samples in mixeng by adding a new
audio format for it.

Limitations: only native endianness is supported. None of the virtual
sound cards support float samples (it looks like most of them only
support 8 and 16 bit, only hda supports 32 bit), it is only used for the
audio backends (i.e. host side).

Signed-off-by: Kővágó, Zoltán <DirtY.iCE.hu@gmail.com>
Acked-by: Markus Armbruster <armbru@redhat.com>
Message-id: 8a8b0b5698401b78d3c4c8ec90aef83b95babb06.1580672076.git.DirtY.iCE.hu@gmail.com
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>

authored by

Kővágó, Zoltán and committed by
Gerd Hoffmann
ed2a4a79 180b044f

+180 -79
+17
audio/alsaaudio.c
··· 307 307 return SND_PCM_FORMAT_U32_LE; 308 308 } 309 309 310 + case AUDIO_FORMAT_F32: 311 + if (endianness) { 312 + return SND_PCM_FORMAT_FLOAT_BE; 313 + } else { 314 + return SND_PCM_FORMAT_FLOAT_LE; 315 + } 316 + 310 317 default: 311 318 dolog ("Internal logic error: Bad audio format %d\n", fmt); 312 319 #ifdef DEBUG_AUDIO ··· 368 375 case SND_PCM_FORMAT_U32_BE: 369 376 *endianness = 1; 370 377 *fmt = AUDIO_FORMAT_U32; 378 + break; 379 + 380 + case SND_PCM_FORMAT_FLOAT_LE: 381 + *endianness = 0; 382 + *fmt = AUDIO_FORMAT_F32; 383 + break; 384 + 385 + case SND_PCM_FORMAT_FLOAT_BE: 386 + *endianness = 1; 387 + *fmt = AUDIO_FORMAT_F32; 371 388 break; 372 389 373 390 default:
+38 -18
audio/audio.c
··· 218 218 case AUDIO_FORMAT_U32: 219 219 AUD_log (NULL, "U32"); 220 220 break; 221 + case AUDIO_FORMAT_F32: 222 + AUD_log (NULL, "F32"); 223 + break; 221 224 default: 222 225 AUD_log (NULL, "invalid(%d)", as->fmt); 223 226 break; ··· 252 255 case AUDIO_FORMAT_U16: 253 256 case AUDIO_FORMAT_S32: 254 257 case AUDIO_FORMAT_U32: 258 + case AUDIO_FORMAT_F32: 255 259 break; 256 260 default: 257 261 invalid = 1; ··· 264 268 265 269 static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as) 266 270 { 267 - int bits = 8, sign = 0; 271 + int bits = 8; 272 + bool is_signed = false, is_float = false; 268 273 269 274 switch (as->fmt) { 270 275 case AUDIO_FORMAT_S8: 271 - sign = 1; 276 + is_signed = true; 272 277 /* fall through */ 273 278 case AUDIO_FORMAT_U8: 274 279 break; 275 280 276 281 case AUDIO_FORMAT_S16: 277 - sign = 1; 282 + is_signed = true; 278 283 /* fall through */ 279 284 case AUDIO_FORMAT_U16: 280 285 bits = 16; 281 286 break; 282 287 288 + case AUDIO_FORMAT_F32: 289 + is_float = true; 290 + /* fall through */ 283 291 case AUDIO_FORMAT_S32: 284 - sign = 1; 292 + is_signed = true; 285 293 /* fall through */ 286 294 case AUDIO_FORMAT_U32: 287 295 bits = 32; ··· 292 300 } 293 301 return info->freq == as->freq 294 302 && info->nchannels == as->nchannels 295 - && info->sign == sign 303 + && info->is_signed == is_signed 304 + && info->is_float == is_float 296 305 && info->bits == bits 297 306 && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS); 298 307 } 299 308 300 309 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) 301 310 { 302 - int bits = 8, sign = 0, mul; 311 + int bits = 8, mul; 312 + bool is_signed = false, is_float = false; 303 313 304 314 switch (as->fmt) { 305 315 case AUDIO_FORMAT_S8: 306 - sign = 1; 316 + is_signed = true; 307 317 /* fall through */ 308 318 case AUDIO_FORMAT_U8: 309 319 mul = 1; 310 320 break; 311 321 312 322 case AUDIO_FORMAT_S16: 313 - sign = 1; 323 + is_signed = true; 314 324 /* fall through */ 315 325 case AUDIO_FORMAT_U16: 316 326 bits = 16; 317 327 mul = 2; 318 328 break; 319 329 330 + case AUDIO_FORMAT_F32: 331 + is_float = true; 332 + /* fall through */ 320 333 case AUDIO_FORMAT_S32: 321 - sign = 1; 334 + is_signed = true; 322 335 /* fall through */ 323 336 case AUDIO_FORMAT_U32: 324 337 bits = 32; ··· 331 344 332 345 info->freq = as->freq; 333 346 info->bits = bits; 334 - info->sign = sign; 347 + info->is_signed = is_signed; 348 + info->is_float = is_float; 335 349 info->nchannels = as->nchannels; 336 350 info->bytes_per_frame = as->nchannels * mul; 337 351 info->bytes_per_second = info->freq * info->bytes_per_frame; ··· 344 358 return; 345 359 } 346 360 347 - if (info->sign) { 361 + if (info->is_signed || info->is_float) { 348 362 memset(buf, 0x00, len * info->bytes_per_frame); 349 363 } 350 364 else { ··· 770 784 #ifdef DEBUG_AUDIO 771 785 static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info) 772 786 { 773 - dolog ("%s: bits %d, sign %d, freq %d, nchan %d\n", 774 - cap, info->bits, info->sign, info->freq, info->nchannels); 787 + dolog("%s: bits %d, sign %d, float %d, freq %d, nchan %d\n", 788 + cap, info->bits, info->is_signed, info->is_float, info->freq, 789 + info->nchannels); 775 790 } 776 791 #endif 777 792 ··· 1832 1847 1833 1848 cap->buf = g_malloc0_n(hw->mix_buf->size, hw->info.bytes_per_frame); 1834 1849 1835 - hw->clip = mixeng_clip 1836 - [hw->info.nchannels == 2] 1837 - [hw->info.sign] 1838 - [hw->info.swap_endianness] 1839 - [audio_bits_to_index (hw->info.bits)]; 1850 + if (hw->info.is_float) { 1851 + hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; 1852 + } else { 1853 + hw->clip = mixeng_clip 1854 + [hw->info.nchannels == 2] 1855 + [hw->info.is_signed] 1856 + [hw->info.swap_endianness] 1857 + [audio_bits_to_index(hw->info.bits)]; 1858 + } 1840 1859 1841 1860 QLIST_INSERT_HEAD (&s->cap_head, cap, entries); 1842 1861 QLIST_INSERT_HEAD (&cap->cb_head, cb, entries); ··· 2075 2094 2076 2095 case AUDIO_FORMAT_U32: 2077 2096 case AUDIO_FORMAT_S32: 2097 + case AUDIO_FORMAT_F32: 2078 2098 return 4; 2079 2099 2080 2100 case AUDIO_FORMAT__MAX:
+2 -1
audio/audio_int.h
··· 40 40 41 41 struct audio_pcm_info { 42 42 int bits; 43 - int sign; 43 + bool is_signed; 44 + bool is_float; 44 45 int freq; 45 46 int nchannels; 46 47 int bytes_per_frame;
+25 -16
audio/audio_template.h
··· 153 153 sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq; 154 154 #endif 155 155 156 + if (sw->info.is_float) { 156 157 #ifdef DAC 157 - sw->conv = mixeng_conv 158 + sw->conv = mixeng_conv_float[sw->info.nchannels == 2]; 158 159 #else 159 - sw->clip = mixeng_clip 160 + sw->clip = mixeng_clip_float[sw->info.nchannels == 2]; 160 161 #endif 161 - [sw->info.nchannels == 2] 162 - [sw->info.sign] 163 - [sw->info.swap_endianness] 164 - [audio_bits_to_index (sw->info.bits)]; 162 + } else { 163 + #ifdef DAC 164 + sw->conv = mixeng_conv 165 + #else 166 + sw->clip = mixeng_clip 167 + #endif 168 + [sw->info.nchannels == 2] 169 + [sw->info.is_signed] 170 + [sw->info.swap_endianness] 171 + [audio_bits_to_index(sw->info.bits)]; 172 + } 165 173 166 174 sw->name = g_strdup (name); 167 175 err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw); ··· 276 284 goto err1; 277 285 } 278 286 279 - if (s->dev->driver == AUDIODEV_DRIVER_COREAUDIO) { 287 + if (hw->info.is_float) { 280 288 #ifdef DAC 281 - hw->clip = clip_natural_float_from_stereo; 289 + hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; 282 290 #else 283 - hw->conv = conv_natural_float_to_stereo; 291 + hw->conv = mixeng_conv_float[hw->info.nchannels == 2]; 284 292 #endif 285 - } else 293 + } else { 286 294 #ifdef DAC 287 - hw->clip = mixeng_clip 295 + hw->clip = mixeng_clip 288 296 #else 289 - hw->conv = mixeng_conv 297 + hw->conv = mixeng_conv 290 298 #endif 291 - [hw->info.nchannels == 2] 292 - [hw->info.sign] 293 - [hw->info.swap_endianness] 294 - [audio_bits_to_index (hw->info.bits)]; 299 + [hw->info.nchannels == 2] 300 + [hw->info.is_signed] 301 + [hw->info.swap_endianness] 302 + [audio_bits_to_index(hw->info.bits)]; 303 + } 295 304 296 305 glue(audio_pcm_hw_alloc_resources_, TYPE)(hw); 297 306
+1 -6
audio/coreaudio.c
··· 491 491 return -1; 492 492 } 493 493 494 - /* 495 - * The canonical audio format for CoreAudio on macOS is float. Currently 496 - * there is no generic code for AUDIO_FORMAT_F32 in qemu. Here we select 497 - * AUDIO_FORMAT_S32 instead because only the sample size has to match. 498 - */ 499 494 fake_as = *as; 500 495 as = &fake_as; 501 - as->fmt = AUDIO_FORMAT_S32; 496 + as->fmt = AUDIO_FORMAT_F32; 502 497 audio_pcm_init_info (&hw->info, as); 503 498 504 499 status = coreaudio_get_voice(&core->outputDeviceID);
+55 -33
audio/mixeng.c
··· 267 267 } 268 268 }; 269 269 270 - void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, 271 - int samples) 270 + #ifdef FLOAT_MIXENG 271 + #define FLOAT_CONV_TO(x) (x) 272 + #define FLOAT_CONV_FROM(x) (x) 273 + #else 274 + static const float float_scale = UINT_MAX; 275 + #define FLOAT_CONV_TO(x) ((x) * float_scale) 276 + 277 + #ifdef RECIPROCAL 278 + static const float float_scale_reciprocal = 1.f / UINT_MAX; 279 + #define FLOAT_CONV_FROM(x) ((x) * float_scale_reciprocal) 280 + #else 281 + #define FLOAT_CONV_FROM(x) ((x) / float_scale) 282 + #endif 283 + #endif 284 + 285 + static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, 286 + int samples) 272 287 { 273 288 float *in = (float *)src; 274 - #ifndef FLOAT_MIXENG 275 - const float scale = UINT_MAX; 276 - #endif 277 289 278 290 while (samples--) { 279 - #ifdef FLOAT_MIXENG 280 - dst->l = *in++; 281 - dst->r = *in++; 282 - #else 283 - dst->l = *in++ * scale; 284 - dst->r = *in++ * scale; 285 - #endif 291 + dst->r = dst->l = FLOAT_CONV_TO(*in++); 286 292 dst++; 287 293 } 288 294 } 289 295 290 - void clip_natural_float_from_stereo(void *dst, const struct st_sample *src, 291 - int samples) 296 + static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, 297 + int samples) 298 + { 299 + float *in = (float *)src; 300 + 301 + while (samples--) { 302 + dst->l = FLOAT_CONV_TO(*in++); 303 + dst->r = FLOAT_CONV_TO(*in++); 304 + dst++; 305 + } 306 + } 307 + 308 + t_sample *mixeng_conv_float[2] = { 309 + conv_natural_float_to_mono, 310 + conv_natural_float_to_stereo, 311 + }; 312 + 313 + static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, 314 + int samples) 292 315 { 293 316 float *out = (float *)dst; 294 - #ifndef FLOAT_MIXENG 295 - #ifdef RECIPROCAL 296 - const float scale = 1.f / UINT_MAX; 297 - #else 298 - const float scale = UINT_MAX; 299 - #endif 300 - #endif 301 317 302 318 while (samples--) { 303 - #ifdef FLOAT_MIXENG 304 - *out++ = src->l; 305 - *out++ = src->r; 306 - #else 307 - #ifdef RECIPROCAL 308 - *out++ = src->l * scale; 309 - *out++ = src->r * scale; 310 - #else 311 - *out++ = src->l / scale; 312 - *out++ = src->r / scale; 313 - #endif 314 - #endif 319 + *out++ = FLOAT_CONV_FROM(src->l) + FLOAT_CONV_FROM(src->r); 315 320 src++; 316 321 } 317 322 } 323 + 324 + static void clip_natural_float_from_stereo( 325 + void *dst, const struct st_sample *src, int samples) 326 + { 327 + float *out = (float *)dst; 328 + 329 + while (samples--) { 330 + *out++ = FLOAT_CONV_FROM(src->l); 331 + *out++ = FLOAT_CONV_FROM(src->r); 332 + src++; 333 + } 334 + } 335 + 336 + f_sample *mixeng_clip_float[2] = { 337 + clip_natural_float_from_mono, 338 + clip_natural_float_from_stereo, 339 + }; 318 340 319 341 void audio_sample_to_uint64(void *samples, int pos, 320 342 uint64_t *left, uint64_t *right)
+4 -4
audio/mixeng.h
··· 38 38 typedef void (t_sample) (struct st_sample *dst, const void *src, int samples); 39 39 typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); 40 40 41 + /* indices: [stereo][signed][swap endiannes][8, 16 or 32-bits] */ 41 42 extern t_sample *mixeng_conv[2][2][2][3]; 42 43 extern f_sample *mixeng_clip[2][2][2][3]; 43 44 44 - void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, 45 - int samples); 46 - void clip_natural_float_from_stereo(void *dst, const struct st_sample *src, 47 - int samples); 45 + /* indices: [stereo] */ 46 + extern t_sample *mixeng_conv_float[2]; 47 + extern f_sample *mixeng_clip_float[2]; 48 48 49 49 void *st_rate_start (int inrate, int outrate); 50 50 void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
+9
audio/paaudio.c
··· 277 277 case AUDIO_FORMAT_U32: 278 278 format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; 279 279 break; 280 + case AUDIO_FORMAT_F32: 281 + format = endianness ? PA_SAMPLE_FLOAT32BE : PA_SAMPLE_FLOAT32LE; 282 + break; 280 283 default: 281 284 dolog ("Internal logic error: Bad audio format %d\n", afmt); 282 285 format = PA_SAMPLE_U8; ··· 302 305 case PA_SAMPLE_S32LE: 303 306 *endianness = 0; 304 307 return AUDIO_FORMAT_S32; 308 + case PA_SAMPLE_FLOAT32BE: 309 + *endianness = 1; 310 + return AUDIO_FORMAT_F32; 311 + case PA_SAMPLE_FLOAT32LE: 312 + *endianness = 0; 313 + return AUDIO_FORMAT_F32; 305 314 default: 306 315 dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); 307 316 return AUDIO_FORMAT_U8;
+28
audio/sdlaudio.c
··· 77 77 case AUDIO_FORMAT_U16: 78 78 return AUDIO_U16LSB; 79 79 80 + case AUDIO_FORMAT_S32: 81 + return AUDIO_S32LSB; 82 + 83 + /* no unsigned 32-bit support in SDL */ 84 + 85 + case AUDIO_FORMAT_F32: 86 + return AUDIO_F32LSB; 87 + 80 88 default: 81 89 dolog ("Internal logic error: Bad audio format %d\n", fmt); 82 90 #ifdef DEBUG_AUDIO ··· 117 125 case AUDIO_U16MSB: 118 126 *endianness = 1; 119 127 *fmt = AUDIO_FORMAT_U16; 128 + break; 129 + 130 + case AUDIO_S32LSB: 131 + *endianness = 0; 132 + *fmt = AUDIO_FORMAT_S32; 133 + break; 134 + 135 + case AUDIO_S32MSB: 136 + *endianness = 1; 137 + *fmt = AUDIO_FORMAT_S32; 138 + break; 139 + 140 + case AUDIO_F32LSB: 141 + *endianness = 0; 142 + *fmt = AUDIO_FORMAT_F32; 143 + break; 144 + 145 + case AUDIO_F32MSB: 146 + *endianness = 1; 147 + *fmt = AUDIO_FORMAT_F32; 120 148 break; 121 149 122 150 default:
+1 -1
qapi/audio.json
··· 276 276 # Since: 4.0 277 277 ## 278 278 { 'enum': 'AudioFormat', 279 - 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] } 279 + 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32', 'f32' ] } 280 280 281 281 ## 282 282 # @AudiodevDriver: