Commit 8ead62cfc21f61a32677892c721674e06e9f6153
1 parent
feea13e1
audio fixes + initial audio capture support (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2040 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
15 changed files
with
514 additions
and
54 deletions
audio/alsaaudio.c
| @@ -61,8 +61,8 @@ static struct { | @@ -61,8 +61,8 @@ static struct { | ||
| 61 | .size_in_usec_in = 1, | 61 | .size_in_usec_in = 1, |
| 62 | .size_in_usec_out = 1, | 62 | .size_in_usec_out = 1, |
| 63 | #endif | 63 | #endif |
| 64 | - .pcm_name_out = "hw:0,0", | ||
| 65 | - .pcm_name_in = "hw:0,0", | 64 | + .pcm_name_out = "default", |
| 65 | + .pcm_name_in = "default", | ||
| 66 | #ifdef HIGH_LATENCY | 66 | #ifdef HIGH_LATENCY |
| 67 | .buffer_size_in = 400000, | 67 | .buffer_size_in = 400000, |
| 68 | .period_size_in = 400000 / 4, | 68 | .period_size_in = 400000 / 4, |
| @@ -606,7 +606,6 @@ static int alsa_run_out (HWVoiceOut *hw) | @@ -606,7 +606,6 @@ static int alsa_run_out (HWVoiceOut *hw) | ||
| 606 | } | 606 | } |
| 607 | } | 607 | } |
| 608 | 608 | ||
| 609 | - mixeng_clear (src, written); | ||
| 610 | rpos = (rpos + written) % hw->samples; | 609 | rpos = (rpos + written) % hw->samples; |
| 611 | samples -= written; | 610 | samples -= written; |
| 612 | len -= written; | 611 | len -= written; |
audio/audio.c
| @@ -29,6 +29,7 @@ | @@ -29,6 +29,7 @@ | ||
| 29 | /* #define DEBUG_PLIVE */ | 29 | /* #define DEBUG_PLIVE */ |
| 30 | /* #define DEBUG_LIVE */ | 30 | /* #define DEBUG_LIVE */ |
| 31 | /* #define DEBUG_OUT */ | 31 | /* #define DEBUG_OUT */ |
| 32 | +/* #define DEBUG_CAPTURE */ | ||
| 32 | 33 | ||
| 33 | #define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown" | 34 | #define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown" |
| 34 | 35 | ||
| @@ -137,7 +138,7 @@ int audio_bug (const char *funcname, int cond) | @@ -137,7 +138,7 @@ int audio_bug (const char *funcname, int cond) | ||
| 137 | if (cond) { | 138 | if (cond) { |
| 138 | static int shown; | 139 | static int shown; |
| 139 | 140 | ||
| 140 | - AUD_log (NULL, "Error a bug that was just triggered in %s\n", funcname); | 141 | + AUD_log (NULL, "A bug was just triggered in %s\n", funcname); |
| 141 | if (!shown) { | 142 | if (!shown) { |
| 142 | shown = 1; | 143 | shown = 1; |
| 143 | AUD_log (NULL, "Save all your work and restart without audio\n"); | 144 | AUD_log (NULL, "Save all your work and restart without audio\n"); |
| @@ -621,6 +622,121 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) | @@ -621,6 +622,121 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len) | ||
| 621 | } | 622 | } |
| 622 | 623 | ||
| 623 | /* | 624 | /* |
| 625 | + * Capture | ||
| 626 | + */ | ||
| 627 | +static void noop_conv (st_sample_t *dst, const void *src, | ||
| 628 | + int samples, volume_t *vol) | ||
| 629 | +{ | ||
| 630 | + (void) src; | ||
| 631 | + (void) dst; | ||
| 632 | + (void) samples; | ||
| 633 | + (void) vol; | ||
| 634 | +} | ||
| 635 | + | ||
| 636 | +static CaptureVoiceOut *audio_pcm_capture_find_specific ( | ||
| 637 | + AudioState *s, | ||
| 638 | + audsettings_t *as, | ||
| 639 | + int endian | ||
| 640 | + ) | ||
| 641 | +{ | ||
| 642 | + CaptureVoiceOut *cap; | ||
| 643 | + int swap_endian = audio_need_to_swap_endian (endian); | ||
| 644 | + | ||
| 645 | + for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { | ||
| 646 | + if ((cap->hw.info.swap_endian == swap_endian) | ||
| 647 | + && audio_pcm_info_eq (&cap->hw.info, as)) { | ||
| 648 | + return cap; | ||
| 649 | + } | ||
| 650 | + } | ||
| 651 | + return NULL; | ||
| 652 | +} | ||
| 653 | + | ||
| 654 | +static void audio_notify_capture (CaptureVoiceOut *cap, int enabled) | ||
| 655 | +{ | ||
| 656 | + if (cap->hw.enabled != enabled) { | ||
| 657 | + struct capture_callback *cb; | ||
| 658 | + | ||
| 659 | + cap->hw.enabled = enabled; | ||
| 660 | + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { | ||
| 661 | + cb->ops.state (cb->opaque, enabled); | ||
| 662 | + } | ||
| 663 | + } | ||
| 664 | +} | ||
| 665 | + | ||
| 666 | +static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap) | ||
| 667 | +{ | ||
| 668 | + HWVoiceOut *hw = &cap->hw; | ||
| 669 | + SWVoiceOut *sw; | ||
| 670 | + int enabled = 0; | ||
| 671 | + | ||
| 672 | + for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) { | ||
| 673 | + if (sw->active) { | ||
| 674 | + enabled = 1; | ||
| 675 | + break; | ||
| 676 | + } | ||
| 677 | + } | ||
| 678 | + audio_notify_capture (cap, enabled); | ||
| 679 | +} | ||
| 680 | + | ||
| 681 | +static void audio_detach_capture (HWVoiceOut *hw) | ||
| 682 | +{ | ||
| 683 | + SWVoiceOut *sw; | ||
| 684 | + | ||
| 685 | + for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) { | ||
| 686 | + if (sw->rate) { | ||
| 687 | + st_rate_stop (sw->rate); | ||
| 688 | + sw->rate = NULL; | ||
| 689 | + } | ||
| 690 | + | ||
| 691 | + LIST_REMOVE (sw, entries); | ||
| 692 | + LIST_REMOVE (sw, cap_entries); | ||
| 693 | + qemu_free (sw); | ||
| 694 | + audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw); | ||
| 695 | + } | ||
| 696 | +} | ||
| 697 | + | ||
| 698 | +static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) | ||
| 699 | +{ | ||
| 700 | + CaptureVoiceOut *cap; | ||
| 701 | + | ||
| 702 | + audio_detach_capture (hw); | ||
| 703 | + for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { | ||
| 704 | + SWVoiceOut *sw; | ||
| 705 | + HWVoiceOut *hw_cap; | ||
| 706 | + | ||
| 707 | + hw_cap = &cap->hw; | ||
| 708 | + sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw)); | ||
| 709 | + if (!sw) { | ||
| 710 | + dolog ("Could not allocate soft capture voice (%zu bytes)\n", | ||
| 711 | + sizeof (*sw)); | ||
| 712 | + return -1; | ||
| 713 | + } | ||
| 714 | + | ||
| 715 | + sw->info = hw->info; | ||
| 716 | + sw->hw = hw_cap; | ||
| 717 | + sw->empty = 1; | ||
| 718 | + sw->active = hw->enabled; | ||
| 719 | + sw->conv = noop_conv; | ||
| 720 | + sw->ratio = ((int64_t) hw_cap->info.freq << 32) / sw->info.freq; | ||
| 721 | + sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq); | ||
| 722 | + if (!sw->rate) { | ||
| 723 | + dolog ("Could not start rate conversion for `%s'\n", SW_NAME (sw)); | ||
| 724 | + qemu_free (sw); | ||
| 725 | + return -1; | ||
| 726 | + } | ||
| 727 | + LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); | ||
| 728 | + LIST_INSERT_HEAD (&hw->sw_cap_head, sw, cap_entries); | ||
| 729 | + if (sw->active) { | ||
| 730 | + audio_notify_capture (cap, 1); | ||
| 731 | + } | ||
| 732 | + else { | ||
| 733 | + audio_recalc_and_notify_capture (cap); | ||
| 734 | + } | ||
| 735 | + } | ||
| 736 | + return 0; | ||
| 737 | +} | ||
| 738 | + | ||
| 739 | +/* | ||
| 624 | * Hard voice (capture) | 740 | * Hard voice (capture) |
| 625 | */ | 741 | */ |
| 626 | static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) | 742 | static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) |
| @@ -916,17 +1032,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | @@ -916,17 +1032,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | ||
| 916 | SWVoiceOut *temp_sw; | 1032 | SWVoiceOut *temp_sw; |
| 917 | 1033 | ||
| 918 | if (on) { | 1034 | if (on) { |
| 919 | - int total; | ||
| 920 | - | ||
| 921 | hw->pending_disable = 0; | 1035 | hw->pending_disable = 0; |
| 922 | if (!hw->enabled) { | 1036 | if (!hw->enabled) { |
| 923 | hw->enabled = 1; | 1037 | hw->enabled = 1; |
| 924 | hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); | 1038 | hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); |
| 925 | } | 1039 | } |
| 926 | - | ||
| 927 | - if (sw->empty) { | ||
| 928 | - total = 0; | ||
| 929 | - } | ||
| 930 | } | 1040 | } |
| 931 | else { | 1041 | else { |
| 932 | if (hw->enabled) { | 1042 | if (hw->enabled) { |
| @@ -940,6 +1050,13 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | @@ -940,6 +1050,13 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | ||
| 940 | hw->pending_disable = nb_active == 1; | 1050 | hw->pending_disable = nb_active == 1; |
| 941 | } | 1051 | } |
| 942 | } | 1052 | } |
| 1053 | + for (temp_sw = hw->sw_cap_head.lh_first; temp_sw; | ||
| 1054 | + temp_sw = temp_sw->entries.le_next) { | ||
| 1055 | + temp_sw->active = hw->enabled; | ||
| 1056 | + if (hw->enabled) { | ||
| 1057 | + audio_notify_capture ((CaptureVoiceOut *) temp_sw->hw, 1); | ||
| 1058 | + } | ||
| 1059 | + } | ||
| 943 | sw->active = on; | 1060 | sw->active = on; |
| 944 | } | 1061 | } |
| 945 | } | 1062 | } |
| @@ -1031,6 +1148,41 @@ static int audio_get_free (SWVoiceOut *sw) | @@ -1031,6 +1148,41 @@ static int audio_get_free (SWVoiceOut *sw) | ||
| 1031 | return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; | 1148 | return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift; |
| 1032 | } | 1149 | } |
| 1033 | 1150 | ||
| 1151 | +static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) | ||
| 1152 | +{ | ||
| 1153 | + int n; | ||
| 1154 | + | ||
| 1155 | + if (hw->enabled) { | ||
| 1156 | + SWVoiceOut *sw; | ||
| 1157 | + | ||
| 1158 | + for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) { | ||
| 1159 | + int rpos2 = rpos; | ||
| 1160 | + | ||
| 1161 | + n = samples; | ||
| 1162 | + while (n) { | ||
| 1163 | + int till_end_of_hw = hw->samples - rpos2; | ||
| 1164 | + int to_write = audio_MIN (till_end_of_hw, n); | ||
| 1165 | + int bytes = to_write << hw->info.shift; | ||
| 1166 | + int written; | ||
| 1167 | + | ||
| 1168 | + sw->buf = hw->mix_buf + rpos2; | ||
| 1169 | + written = audio_pcm_sw_write (sw, NULL, bytes); | ||
| 1170 | + if (written - bytes) { | ||
| 1171 | + dolog ("Could not mix %d bytes into a capture buffer", | ||
| 1172 | + bytes); | ||
| 1173 | + break; | ||
| 1174 | + } | ||
| 1175 | + n -= to_write; | ||
| 1176 | + rpos2 = (rpos2 + to_write) % hw->samples; | ||
| 1177 | + } | ||
| 1178 | + } | ||
| 1179 | + } | ||
| 1180 | + | ||
| 1181 | + n = audio_MIN (samples, hw->samples - rpos); | ||
| 1182 | + mixeng_clear (hw->mix_buf + rpos, n); | ||
| 1183 | + mixeng_clear (hw->mix_buf, samples - n); | ||
| 1184 | +} | ||
| 1185 | + | ||
| 1034 | static void audio_run_out (AudioState *s) | 1186 | static void audio_run_out (AudioState *s) |
| 1035 | { | 1187 | { |
| 1036 | HWVoiceOut *hw = NULL; | 1188 | HWVoiceOut *hw = NULL; |
| @@ -1038,7 +1190,7 @@ static void audio_run_out (AudioState *s) | @@ -1038,7 +1190,7 @@ static void audio_run_out (AudioState *s) | ||
| 1038 | 1190 | ||
| 1039 | while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) { | 1191 | while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) { |
| 1040 | int played; | 1192 | int played; |
| 1041 | - int live, free, nb_live, cleanup_required; | 1193 | + int live, free, nb_live, cleanup_required, prev_rpos; |
| 1042 | 1194 | ||
| 1043 | live = audio_pcm_hw_get_live_out2 (hw, &nb_live); | 1195 | live = audio_pcm_hw_get_live_out2 (hw, &nb_live); |
| 1044 | if (!nb_live) { | 1196 | if (!nb_live) { |
| @@ -1057,6 +1209,11 @@ static void audio_run_out (AudioState *s) | @@ -1057,6 +1209,11 @@ static void audio_run_out (AudioState *s) | ||
| 1057 | hw->enabled = 0; | 1209 | hw->enabled = 0; |
| 1058 | hw->pending_disable = 0; | 1210 | hw->pending_disable = 0; |
| 1059 | hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); | 1211 | hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); |
| 1212 | + for (sw = hw->sw_cap_head.lh_first; sw; | ||
| 1213 | + sw = sw->cap_entries.le_next) { | ||
| 1214 | + sw->active = 0; | ||
| 1215 | + audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw); | ||
| 1216 | + } | ||
| 1060 | continue; | 1217 | continue; |
| 1061 | } | 1218 | } |
| 1062 | 1219 | ||
| @@ -1072,6 +1229,7 @@ static void audio_run_out (AudioState *s) | @@ -1072,6 +1229,7 @@ static void audio_run_out (AudioState *s) | ||
| 1072 | continue; | 1229 | continue; |
| 1073 | } | 1230 | } |
| 1074 | 1231 | ||
| 1232 | + prev_rpos = hw->rpos; | ||
| 1075 | played = hw->pcm_ops->run_out (hw); | 1233 | played = hw->pcm_ops->run_out (hw); |
| 1076 | if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { | 1234 | if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { |
| 1077 | dolog ("hw->rpos=%d hw->samples=%d played=%d\n", | 1235 | dolog ("hw->rpos=%d hw->samples=%d played=%d\n", |
| @@ -1085,6 +1243,7 @@ static void audio_run_out (AudioState *s) | @@ -1085,6 +1243,7 @@ static void audio_run_out (AudioState *s) | ||
| 1085 | 1243 | ||
| 1086 | if (played) { | 1244 | if (played) { |
| 1087 | hw->ts_helper += played; | 1245 | hw->ts_helper += played; |
| 1246 | + audio_capture_mix_and_clear (hw, prev_rpos, played); | ||
| 1088 | } | 1247 | } |
| 1089 | 1248 | ||
| 1090 | cleanup_required = 0; | 1249 | cleanup_required = 0; |
| @@ -1158,12 +1317,60 @@ static void audio_run_in (AudioState *s) | @@ -1158,12 +1317,60 @@ static void audio_run_in (AudioState *s) | ||
| 1158 | } | 1317 | } |
| 1159 | } | 1318 | } |
| 1160 | 1319 | ||
| 1320 | +static void audio_run_capture (AudioState *s) | ||
| 1321 | +{ | ||
| 1322 | + CaptureVoiceOut *cap; | ||
| 1323 | + | ||
| 1324 | + for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { | ||
| 1325 | + int live, rpos, captured; | ||
| 1326 | + HWVoiceOut *hw = &cap->hw; | ||
| 1327 | + SWVoiceOut *sw; | ||
| 1328 | + | ||
| 1329 | + captured = live = audio_pcm_hw_get_live_out (hw); | ||
| 1330 | + rpos = hw->rpos; | ||
| 1331 | + while (live) { | ||
| 1332 | + int left = hw->samples - rpos; | ||
| 1333 | + int to_capture = audio_MIN (live, left); | ||
| 1334 | + st_sample_t *src; | ||
| 1335 | + struct capture_callback *cb; | ||
| 1336 | + | ||
| 1337 | + src = hw->mix_buf + rpos; | ||
| 1338 | + hw->clip (cap->buf, src, to_capture); | ||
| 1339 | + mixeng_clear (src, to_capture); | ||
| 1340 | + | ||
| 1341 | + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { | ||
| 1342 | + cb->ops.capture (cb->opaque, cap->buf, | ||
| 1343 | + to_capture << hw->info.shift); | ||
| 1344 | + } | ||
| 1345 | + rpos = (rpos + to_capture) % hw->samples; | ||
| 1346 | + live -= to_capture; | ||
| 1347 | + } | ||
| 1348 | + hw->rpos = rpos; | ||
| 1349 | + | ||
| 1350 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | ||
| 1351 | + if (!sw->active && sw->empty) { | ||
| 1352 | + continue; | ||
| 1353 | + } | ||
| 1354 | + | ||
| 1355 | + if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) { | ||
| 1356 | + dolog ("captured=%d sw->total_hw_samples_mixed=%d\n", | ||
| 1357 | + captured, sw->total_hw_samples_mixed); | ||
| 1358 | + captured = sw->total_hw_samples_mixed; | ||
| 1359 | + } | ||
| 1360 | + | ||
| 1361 | + sw->total_hw_samples_mixed -= captured; | ||
| 1362 | + sw->empty = sw->total_hw_samples_mixed == 0; | ||
| 1363 | + } | ||
| 1364 | + } | ||
| 1365 | +} | ||
| 1366 | + | ||
| 1161 | static void audio_timer (void *opaque) | 1367 | static void audio_timer (void *opaque) |
| 1162 | { | 1368 | { |
| 1163 | AudioState *s = opaque; | 1369 | AudioState *s = opaque; |
| 1164 | 1370 | ||
| 1165 | audio_run_out (s); | 1371 | audio_run_out (s); |
| 1166 | audio_run_in (s); | 1372 | audio_run_in (s); |
| 1373 | + audio_run_capture (s); | ||
| 1167 | 1374 | ||
| 1168 | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); | 1375 | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); |
| 1169 | } | 1376 | } |
| @@ -1327,8 +1534,14 @@ static void audio_atexit (void) | @@ -1327,8 +1534,14 @@ static void audio_atexit (void) | ||
| 1327 | HWVoiceIn *hwi = NULL; | 1534 | HWVoiceIn *hwi = NULL; |
| 1328 | 1535 | ||
| 1329 | while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { | 1536 | while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { |
| 1537 | + SWVoiceOut *sw; | ||
| 1538 | + | ||
| 1330 | hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); | 1539 | hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); |
| 1331 | hwo->pcm_ops->fini_out (hwo); | 1540 | hwo->pcm_ops->fini_out (hwo); |
| 1541 | + | ||
| 1542 | + for (sw = hwo->sw_cap_head.lh_first; sw; sw = sw->entries.le_next) { | ||
| 1543 | + audio_notify_capture ((CaptureVoiceOut *) sw->hw, 0); | ||
| 1544 | + } | ||
| 1332 | } | 1545 | } |
| 1333 | 1546 | ||
| 1334 | while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { | 1547 | while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { |
| @@ -1383,6 +1596,7 @@ AudioState *AUD_init (void) | @@ -1383,6 +1596,7 @@ AudioState *AUD_init (void) | ||
| 1383 | 1596 | ||
| 1384 | LIST_INIT (&s->hw_head_out); | 1597 | LIST_INIT (&s->hw_head_out); |
| 1385 | LIST_INIT (&s->hw_head_in); | 1598 | LIST_INIT (&s->hw_head_in); |
| 1599 | + LIST_INIT (&s->cap_head); | ||
| 1386 | atexit (audio_atexit); | 1600 | atexit (audio_atexit); |
| 1387 | 1601 | ||
| 1388 | s->ts = qemu_new_timer (vm_clock, audio_timer, s); | 1602 | s->ts = qemu_new_timer (vm_clock, audio_timer, s); |
| @@ -1479,3 +1693,100 @@ AudioState *AUD_init (void) | @@ -1479,3 +1693,100 @@ AudioState *AUD_init (void) | ||
| 1479 | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); | 1693 | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); |
| 1480 | return s; | 1694 | return s; |
| 1481 | } | 1695 | } |
| 1696 | + | ||
| 1697 | +int AUD_add_capture ( | ||
| 1698 | + AudioState *s, | ||
| 1699 | + audsettings_t *as, | ||
| 1700 | + int endian, | ||
| 1701 | + struct audio_capture_ops *ops, | ||
| 1702 | + void *cb_opaque | ||
| 1703 | + ) | ||
| 1704 | +{ | ||
| 1705 | + CaptureVoiceOut *cap; | ||
| 1706 | + struct capture_callback *cb; | ||
| 1707 | + | ||
| 1708 | + if (!s) { | ||
| 1709 | + /* XXX suppress */ | ||
| 1710 | + s = &glob_audio_state; | ||
| 1711 | + } | ||
| 1712 | + | ||
| 1713 | + if (audio_validate_settigs (as)) { | ||
| 1714 | + dolog ("Invalid settings were passed when trying to add capture\n"); | ||
| 1715 | + audio_print_settings (as); | ||
| 1716 | + return -1; | ||
| 1717 | + } | ||
| 1718 | + | ||
| 1719 | + cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); | ||
| 1720 | + if (!cb) { | ||
| 1721 | + dolog ("Could not allocate capture callback information, size %zu\n", | ||
| 1722 | + sizeof (*cb)); | ||
| 1723 | + goto err0; | ||
| 1724 | + } | ||
| 1725 | + cb->ops = *ops; | ||
| 1726 | + cb->opaque = cb_opaque; | ||
| 1727 | + | ||
| 1728 | + cap = audio_pcm_capture_find_specific (s, as, endian); | ||
| 1729 | + if (cap) { | ||
| 1730 | + LIST_INSERT_HEAD (&cap->cb_head, cb, entries); | ||
| 1731 | + return 0; | ||
| 1732 | + } | ||
| 1733 | + else { | ||
| 1734 | + HWVoiceOut *hw; | ||
| 1735 | + CaptureVoiceOut *cap; | ||
| 1736 | + | ||
| 1737 | + cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap)); | ||
| 1738 | + if (!cap) { | ||
| 1739 | + dolog ("Could not allocate capture voice, size %zu\n", | ||
| 1740 | + sizeof (*cap)); | ||
| 1741 | + goto err1; | ||
| 1742 | + } | ||
| 1743 | + | ||
| 1744 | + hw = &cap->hw; | ||
| 1745 | + LIST_INIT (&hw->sw_head); | ||
| 1746 | + LIST_INIT (&cap->cb_head); | ||
| 1747 | + | ||
| 1748 | + /* XXX find a more elegant way */ | ||
| 1749 | + hw->samples = 4096 * 4; | ||
| 1750 | + hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples, | ||
| 1751 | + sizeof (st_sample_t)); | ||
| 1752 | + if (!hw->mix_buf) { | ||
| 1753 | + dolog ("Could not allocate capture mix buffer (%d samples)\n", | ||
| 1754 | + hw->samples); | ||
| 1755 | + goto err2; | ||
| 1756 | + } | ||
| 1757 | + | ||
| 1758 | + audio_pcm_init_info (&hw->info, as, endian); | ||
| 1759 | + | ||
| 1760 | + cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); | ||
| 1761 | + if (!cap->buf) { | ||
| 1762 | + dolog ("Could not allocate capture buffer " | ||
| 1763 | + "(%d samples, each %d bytes)\n", | ||
| 1764 | + hw->samples, 1 << hw->info.shift); | ||
| 1765 | + goto err3; | ||
| 1766 | + } | ||
| 1767 | + | ||
| 1768 | + hw->clip = mixeng_clip | ||
| 1769 | + [hw->info.nchannels == 2] | ||
| 1770 | + [hw->info.sign] | ||
| 1771 | + [hw->info.swap_endian] | ||
| 1772 | + [hw->info.bits == 16]; | ||
| 1773 | + | ||
| 1774 | + LIST_INSERT_HEAD (&s->cap_head, cap, entries); | ||
| 1775 | + LIST_INSERT_HEAD (&cap->cb_head, cb, entries); | ||
| 1776 | + | ||
| 1777 | + hw = NULL; | ||
| 1778 | + while ((hw = audio_pcm_hw_find_any_out (s, hw))) { | ||
| 1779 | + audio_attach_capture (s, hw); | ||
| 1780 | + } | ||
| 1781 | + return 0; | ||
| 1782 | + | ||
| 1783 | + err3: | ||
| 1784 | + qemu_free (cap->hw.mix_buf); | ||
| 1785 | + err2: | ||
| 1786 | + qemu_free (cap); | ||
| 1787 | + err1: | ||
| 1788 | + qemu_free (cb); | ||
| 1789 | + err0: | ||
| 1790 | + return -1; | ||
| 1791 | + } | ||
| 1792 | +} |
audio/audio.h
| @@ -41,6 +41,11 @@ typedef struct { | @@ -41,6 +41,11 @@ typedef struct { | ||
| 41 | audfmt_e fmt; | 41 | audfmt_e fmt; |
| 42 | } audsettings_t; | 42 | } audsettings_t; |
| 43 | 43 | ||
| 44 | +struct audio_capture_ops { | ||
| 45 | + void (*state) (void *opaque, int enabled); | ||
| 46 | + void (*capture) (void *opaque, void *buf, int size); | ||
| 47 | +}; | ||
| 48 | + | ||
| 44 | typedef struct AudioState AudioState; | 49 | typedef struct AudioState AudioState; |
| 45 | typedef struct SWVoiceOut SWVoiceOut; | 50 | typedef struct SWVoiceOut SWVoiceOut; |
| 46 | typedef struct SWVoiceIn SWVoiceIn; | 51 | typedef struct SWVoiceIn SWVoiceIn; |
| @@ -66,6 +71,13 @@ AudioState *AUD_init (void); | @@ -66,6 +71,13 @@ AudioState *AUD_init (void); | ||
| 66 | void AUD_help (void); | 71 | void AUD_help (void); |
| 67 | void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); | 72 | void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); |
| 68 | void AUD_remove_card (QEMUSoundCard *card); | 73 | void AUD_remove_card (QEMUSoundCard *card); |
| 74 | +int AUD_add_capture ( | ||
| 75 | + AudioState *s, | ||
| 76 | + audsettings_t *as, | ||
| 77 | + int endian, | ||
| 78 | + struct audio_capture_ops *ops, | ||
| 79 | + void *opaque | ||
| 80 | + ); | ||
| 69 | 81 | ||
| 70 | SWVoiceOut *AUD_open_out ( | 82 | SWVoiceOut *AUD_open_out ( |
| 71 | QEMUSoundCard *card, | 83 | QEMUSoundCard *card, |
| @@ -111,7 +123,7 @@ static inline void *advance (void *p, int incr) | @@ -111,7 +123,7 @@ static inline void *advance (void *p, int incr) | ||
| 111 | } | 123 | } |
| 112 | 124 | ||
| 113 | uint32_t popcount (uint32_t u); | 125 | uint32_t popcount (uint32_t u); |
| 114 | -inline uint32_t lsbindex (uint32_t u); | 126 | +uint32_t lsbindex (uint32_t u); |
| 115 | 127 | ||
| 116 | #ifdef __GNUC__ | 128 | #ifdef __GNUC__ |
| 117 | #define audio_MIN(a, b) ( __extension__ ({ \ | 129 | #define audio_MIN(a, b) ( __extension__ ({ \ |
audio/audio_int.h
| @@ -79,6 +79,7 @@ typedef struct HWVoiceOut { | @@ -79,6 +79,7 @@ typedef struct HWVoiceOut { | ||
| 79 | 79 | ||
| 80 | int samples; | 80 | int samples; |
| 81 | LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; | 81 | LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; |
| 82 | + LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head; | ||
| 82 | struct audio_pcm_ops *pcm_ops; | 83 | struct audio_pcm_ops *pcm_ops; |
| 83 | LIST_ENTRY (HWVoiceOut) entries; | 84 | LIST_ENTRY (HWVoiceOut) entries; |
| 84 | } HWVoiceOut; | 85 | } HWVoiceOut; |
| @@ -115,6 +116,7 @@ struct SWVoiceOut { | @@ -115,6 +116,7 @@ struct SWVoiceOut { | ||
| 115 | volume_t vol; | 116 | volume_t vol; |
| 116 | struct audio_callback callback; | 117 | struct audio_callback callback; |
| 117 | LIST_ENTRY (SWVoiceOut) entries; | 118 | LIST_ENTRY (SWVoiceOut) entries; |
| 119 | + LIST_ENTRY (SWVoiceOut) cap_entries; | ||
| 118 | }; | 120 | }; |
| 119 | 121 | ||
| 120 | struct SWVoiceIn { | 122 | struct SWVoiceIn { |
| @@ -160,14 +162,28 @@ struct audio_pcm_ops { | @@ -160,14 +162,28 @@ struct audio_pcm_ops { | ||
| 160 | int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); | 162 | int (*ctl_in) (HWVoiceIn *hw, int cmd, ...); |
| 161 | }; | 163 | }; |
| 162 | 164 | ||
| 165 | +struct capture_callback { | ||
| 166 | + struct audio_capture_ops ops; | ||
| 167 | + void *opaque; | ||
| 168 | + LIST_ENTRY (capture_callback) entries; | ||
| 169 | +}; | ||
| 170 | + | ||
| 171 | +typedef struct CaptureVoiceOut { | ||
| 172 | + HWVoiceOut hw; | ||
| 173 | + void *buf; | ||
| 174 | + LIST_HEAD (cb_listhead, capture_callback) cb_head; | ||
| 175 | + LIST_ENTRY (CaptureVoiceOut) entries; | ||
| 176 | +} CaptureVoiceOut; | ||
| 177 | + | ||
| 163 | struct AudioState { | 178 | struct AudioState { |
| 164 | struct audio_driver *drv; | 179 | struct audio_driver *drv; |
| 165 | void *drv_opaque; | 180 | void *drv_opaque; |
| 166 | 181 | ||
| 167 | QEMUTimer *ts; | 182 | QEMUTimer *ts; |
| 168 | - LIST_HEAD (card_head, QEMUSoundCard) card_head; | 183 | + LIST_HEAD (card_listhead, QEMUSoundCard) card_head; |
| 169 | LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; | 184 | LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; |
| 170 | LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; | 185 | LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; |
| 186 | + LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head; | ||
| 171 | int nb_hw_voices_out; | 187 | int nb_hw_voices_out; |
| 172 | int nb_hw_voices_in; | 188 | int nb_hw_voices_in; |
| 173 | }; | 189 | }; |
audio/audio_template.h
| @@ -200,6 +200,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp) | @@ -200,6 +200,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp) | ||
| 200 | HW *hw = *hwp; | 200 | HW *hw = *hwp; |
| 201 | 201 | ||
| 202 | if (!hw->sw_head.lh_first) { | 202 | if (!hw->sw_head.lh_first) { |
| 203 | +#ifdef DAC | ||
| 204 | + audio_detach_capture (hw); | ||
| 205 | +#endif | ||
| 203 | LIST_REMOVE (hw, entries); | 206 | LIST_REMOVE (hw, entries); |
| 204 | glue (s->nb_hw_voices_, TYPE) += 1; | 207 | glue (s->nb_hw_voices_, TYPE) += 1; |
| 205 | glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); | 208 | glue (audio_pcm_hw_free_resources_ ,TYPE) (hw); |
| @@ -266,7 +269,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) | @@ -266,7 +269,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) | ||
| 266 | 269 | ||
| 267 | hw->pcm_ops = drv->pcm_ops; | 270 | hw->pcm_ops = drv->pcm_ops; |
| 268 | LIST_INIT (&hw->sw_head); | 271 | LIST_INIT (&hw->sw_head); |
| 269 | - | 272 | +#ifdef DAC |
| 273 | + LIST_INIT (&hw->sw_cap_head); | ||
| 274 | +#endif | ||
| 270 | if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { | 275 | if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { |
| 271 | goto err0; | 276 | goto err0; |
| 272 | } | 277 | } |
| @@ -292,6 +297,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) | @@ -292,6 +297,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) | ||
| 292 | 297 | ||
| 293 | LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); | 298 | LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); |
| 294 | glue (s->nb_hw_voices_, TYPE) -= 1; | 299 | glue (s->nb_hw_voices_, TYPE) -= 1; |
| 300 | +#ifdef DAC | ||
| 301 | + audio_attach_capture (s, hw); | ||
| 302 | +#endif | ||
| 295 | return hw; | 303 | return hw; |
| 296 | 304 | ||
| 297 | err1: | 305 | err1: |
| @@ -542,7 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | @@ -542,7 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) | ||
| 542 | 550 | ||
| 543 | cur_ts = sw->hw->ts_helper; | 551 | cur_ts = sw->hw->ts_helper; |
| 544 | old_ts = ts->old_ts; | 552 | old_ts = ts->old_ts; |
| 545 | - /* dolog ("cur %" PRId64 " old %" PRId64 "\n", cur_ts, old_ts); */ | 553 | + /* dolog ("cur %lld old %lld\n", cur_ts, old_ts); */ |
| 546 | 554 | ||
| 547 | if (cur_ts >= old_ts) { | 555 | if (cur_ts >= old_ts) { |
| 548 | delta = cur_ts - old_ts; | 556 | delta = cur_ts - old_ts; |
audio/coreaudio.c
| @@ -275,8 +275,6 @@ static OSStatus audioDeviceIOProc( | @@ -275,8 +275,6 @@ static OSStatus audioDeviceIOProc( | ||
| 275 | #endif | 275 | #endif |
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | - /* cleanup */ | ||
| 279 | - mixeng_clear (src, frameCount); | ||
| 280 | rpos = (rpos + frameCount) % hw->samples; | 278 | rpos = (rpos + frameCount) % hw->samples; |
| 281 | core->decr += frameCount; | 279 | core->decr += frameCount; |
| 282 | core->rpos = rpos; | 280 | core->rpos = rpos; |
audio/dsound_template.h
| @@ -70,7 +70,13 @@ static int glue (dsound_lock_, TYPE) ( | @@ -70,7 +70,13 @@ static int glue (dsound_lock_, TYPE) ( | ||
| 70 | int i; | 70 | int i; |
| 71 | LPVOID p1 = NULL, p2 = NULL; | 71 | LPVOID p1 = NULL, p2 = NULL; |
| 72 | DWORD blen1 = 0, blen2 = 0; | 72 | DWORD blen1 = 0, blen2 = 0; |
| 73 | + DWORD flag; | ||
| 73 | 74 | ||
| 75 | +#ifdef DSBTYPE_IN | ||
| 76 | + flag = entire ? DSCBLOCK_ENTIREBUFFER : 0; | ||
| 77 | +#else | ||
| 78 | + flag = entire ? DSBLOCK_ENTIREBUFFER : 0; | ||
| 79 | +#endif | ||
| 74 | for (i = 0; i < conf.lock_retries; ++i) { | 80 | for (i = 0; i < conf.lock_retries; ++i) { |
| 75 | hr = glue (IFACE, _Lock) ( | 81 | hr = glue (IFACE, _Lock) ( |
| 76 | buf, | 82 | buf, |
| @@ -80,13 +86,7 @@ static int glue (dsound_lock_, TYPE) ( | @@ -80,13 +86,7 @@ static int glue (dsound_lock_, TYPE) ( | ||
| 80 | &blen1, | 86 | &blen1, |
| 81 | &p2, | 87 | &p2, |
| 82 | &blen2, | 88 | &blen2, |
| 83 | - (entire | ||
| 84 | -#ifdef DSBTYPE_IN | ||
| 85 | - ? DSCBLOCK_ENTIREBUFFER | ||
| 86 | -#else | ||
| 87 | - ? DSBLOCK_ENTIREBUFFER | ||
| 88 | -#endif | ||
| 89 | - : 0) | 89 | + flag |
| 90 | ); | 90 | ); |
| 91 | 91 | ||
| 92 | if (FAILED (hr)) { | 92 | if (FAILED (hr)) { |
audio/dsoundaudio.c
| @@ -453,13 +453,11 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | @@ -453,13 +453,11 @@ static void dsound_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | ||
| 453 | 453 | ||
| 454 | if (src_len1) { | 454 | if (src_len1) { |
| 455 | hw->clip (dst, src1, src_len1); | 455 | hw->clip (dst, src1, src_len1); |
| 456 | - mixeng_clear (src1, src_len1); | ||
| 457 | } | 456 | } |
| 458 | 457 | ||
| 459 | if (src_len2) { | 458 | if (src_len2) { |
| 460 | dst = advance (dst, src_len1 << hw->info.shift); | 459 | dst = advance (dst, src_len1 << hw->info.shift); |
| 461 | hw->clip (dst, src2, src_len2); | 460 | hw->clip (dst, src2, src_len2); |
| 462 | - mixeng_clear (src2, src_len2); | ||
| 463 | } | 461 | } |
| 464 | 462 | ||
| 465 | hw->rpos = pos % hw->samples; | 463 | hw->rpos = pos % hw->samples; |
| @@ -987,6 +985,12 @@ static void *dsound_audio_init (void) | @@ -987,6 +985,12 @@ static void *dsound_audio_init (void) | ||
| 987 | hr = IDirectSound_Initialize (s->dsound, NULL); | 985 | hr = IDirectSound_Initialize (s->dsound, NULL); |
| 988 | if (FAILED (hr)) { | 986 | if (FAILED (hr)) { |
| 989 | dsound_logerr (hr, "Could not initialize DirectSound\n"); | 987 | dsound_logerr (hr, "Could not initialize DirectSound\n"); |
| 988 | + | ||
| 989 | + hr = IDirectSound_Release (s->dsound); | ||
| 990 | + if (FAILED (hr)) { | ||
| 991 | + dsound_logerr (hr, "Could not release DirectSound\n"); | ||
| 992 | + } | ||
| 993 | + s->dsound = NULL; | ||
| 990 | return NULL; | 994 | return NULL; |
| 991 | } | 995 | } |
| 992 | 996 |
audio/fmodaudio.c
| @@ -153,13 +153,11 @@ static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | @@ -153,13 +153,11 @@ static void fmod_write_sample (HWVoiceOut *hw, uint8_t *dst, int dst_len) | ||
| 153 | 153 | ||
| 154 | if (src_len1) { | 154 | if (src_len1) { |
| 155 | hw->clip (dst, src1, src_len1); | 155 | hw->clip (dst, src1, src_len1); |
| 156 | - mixeng_clear (src1, src_len1); | ||
| 157 | } | 156 | } |
| 158 | 157 | ||
| 159 | if (src_len2) { | 158 | if (src_len2) { |
| 160 | dst = advance (dst, src_len1 << hw->info.shift); | 159 | dst = advance (dst, src_len1 << hw->info.shift); |
| 161 | hw->clip (dst, src2, src_len2); | 160 | hw->clip (dst, src2, src_len2); |
| 162 | - mixeng_clear (src2, src_len2); | ||
| 163 | } | 161 | } |
| 164 | 162 | ||
| 165 | hw->rpos = pos % hw->samples; | 163 | hw->rpos = pos % hw->samples; |
audio/noaudio.c
| @@ -40,22 +40,21 @@ static int no_run_out (HWVoiceOut *hw) | @@ -40,22 +40,21 @@ static int no_run_out (HWVoiceOut *hw) | ||
| 40 | { | 40 | { |
| 41 | NoVoiceOut *no = (NoVoiceOut *) hw; | 41 | NoVoiceOut *no = (NoVoiceOut *) hw; |
| 42 | int live, decr, samples; | 42 | int live, decr, samples; |
| 43 | - int64_t now = qemu_get_clock (vm_clock); | ||
| 44 | - int64_t ticks = now - no->old_ticks; | ||
| 45 | - int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||
| 46 | - | ||
| 47 | - if (bytes > INT_MAX) { | ||
| 48 | - samples = INT_MAX >> hw->info.shift; | ||
| 49 | - } | ||
| 50 | - else { | ||
| 51 | - samples = bytes >> hw->info.shift; | ||
| 52 | - } | 43 | + int64_t now; |
| 44 | + int64_t ticks; | ||
| 45 | + int64_t bytes; | ||
| 53 | 46 | ||
| 54 | live = audio_pcm_hw_get_live_out (&no->hw); | 47 | live = audio_pcm_hw_get_live_out (&no->hw); |
| 55 | if (!live) { | 48 | if (!live) { |
| 56 | return 0; | 49 | return 0; |
| 57 | } | 50 | } |
| 58 | 51 | ||
| 52 | + now = qemu_get_clock (vm_clock); | ||
| 53 | + ticks = now - no->old_ticks; | ||
| 54 | + bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||
| 55 | + bytes = audio_MIN (bytes, INT_MAX); | ||
| 56 | + samples = bytes >> hw->info.shift; | ||
| 57 | + | ||
| 59 | no->old_ticks = now; | 58 | no->old_ticks = now; |
| 60 | decr = audio_MIN (live, samples); | 59 | decr = audio_MIN (live, samples); |
| 61 | hw->rpos = (hw->rpos + decr) % hw->samples; | 60 | hw->rpos = (hw->rpos + decr) % hw->samples; |
| @@ -101,17 +100,20 @@ static void no_fini_in (HWVoiceIn *hw) | @@ -101,17 +100,20 @@ static void no_fini_in (HWVoiceIn *hw) | ||
| 101 | static int no_run_in (HWVoiceIn *hw) | 100 | static int no_run_in (HWVoiceIn *hw) |
| 102 | { | 101 | { |
| 103 | NoVoiceIn *no = (NoVoiceIn *) hw; | 102 | NoVoiceIn *no = (NoVoiceIn *) hw; |
| 104 | - int64_t now = qemu_get_clock (vm_clock); | ||
| 105 | - int64_t ticks = now - no->old_ticks; | ||
| 106 | - int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||
| 107 | int live = audio_pcm_hw_get_live_in (hw); | 103 | int live = audio_pcm_hw_get_live_in (hw); |
| 108 | int dead = hw->samples - live; | 104 | int dead = hw->samples - live; |
| 109 | int samples; | 105 | int samples; |
| 110 | 106 | ||
| 111 | - bytes = audio_MIN (bytes, INT_MAX); | ||
| 112 | - samples = bytes >> hw->info.shift; | ||
| 113 | - samples = audio_MIN (samples, dead); | 107 | + if (dead) { |
| 108 | + int64_t now = qemu_get_clock (vm_clock); | ||
| 109 | + int64_t ticks = now - no->old_ticks; | ||
| 110 | + int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec; | ||
| 114 | 111 | ||
| 112 | + no->old_ticks = now; | ||
| 113 | + bytes = audio_MIN (bytes, INT_MAX); | ||
| 114 | + samples = bytes >> hw->info.shift; | ||
| 115 | + samples = audio_MIN (samples, dead); | ||
| 116 | + } | ||
| 115 | return samples; | 117 | return samples; |
| 116 | } | 118 | } |
| 117 | 119 |
audio/ossaudio.c
| @@ -55,12 +55,14 @@ static struct { | @@ -55,12 +55,14 @@ static struct { | ||
| 55 | int fragsize; | 55 | int fragsize; |
| 56 | const char *devpath_out; | 56 | const char *devpath_out; |
| 57 | const char *devpath_in; | 57 | const char *devpath_in; |
| 58 | + int debug; | ||
| 58 | } conf = { | 59 | } conf = { |
| 59 | .try_mmap = 0, | 60 | .try_mmap = 0, |
| 60 | .nfrags = 4, | 61 | .nfrags = 4, |
| 61 | .fragsize = 4096, | 62 | .fragsize = 4096, |
| 62 | .devpath_out = "/dev/dsp", | 63 | .devpath_out = "/dev/dsp", |
| 63 | - .devpath_in = "/dev/dsp" | 64 | + .devpath_in = "/dev/dsp", |
| 65 | + .debug = 0 | ||
| 64 | }; | 66 | }; |
| 65 | 67 | ||
| 66 | struct oss_params { | 68 | struct oss_params { |
| @@ -324,9 +326,20 @@ static int oss_run_out (HWVoiceOut *hw) | @@ -324,9 +326,20 @@ static int oss_run_out (HWVoiceOut *hw) | ||
| 324 | return 0; | 326 | return 0; |
| 325 | } | 327 | } |
| 326 | 328 | ||
| 327 | - if (abinfo.bytes < 0 || abinfo.bytes > bufsize) { | ||
| 328 | - ldebug ("warning: Invalid available size, size=%d bufsize=%d\n", | ||
| 329 | - abinfo.bytes, bufsize); | 329 | + if (abinfo.bytes > bufsize) { |
| 330 | + if (conf.debug) { | ||
| 331 | + dolog ("warning: Invalid available size, size=%d bufsize=%d\n" | ||
| 332 | + "please report your OS/audio hw to malc@pulsesoft.com\n", | ||
| 333 | + abinfo.bytes, bufsize); | ||
| 334 | + } | ||
| 335 | + abinfo.bytes = bufsize; | ||
| 336 | + } | ||
| 337 | + | ||
| 338 | + if (abinfo.bytes < 0) { | ||
| 339 | + if (conf.debug) { | ||
| 340 | + dolog ("warning: Invalid available size, size=%d bufsize=%d\n", | ||
| 341 | + abinfo.bytes, bufsize); | ||
| 342 | + } | ||
| 330 | return 0; | 343 | return 0; |
| 331 | } | 344 | } |
| 332 | 345 | ||
| @@ -369,15 +382,12 @@ static int oss_run_out (HWVoiceOut *hw) | @@ -369,15 +382,12 @@ static int oss_run_out (HWVoiceOut *hw) | ||
| 369 | "alignment %d\n", | 382 | "alignment %d\n", |
| 370 | wbytes, written, hw->info.align + 1); | 383 | wbytes, written, hw->info.align + 1); |
| 371 | } | 384 | } |
| 372 | - mixeng_clear (src, wsamples); | ||
| 373 | decr -= wsamples; | 385 | decr -= wsamples; |
| 374 | rpos = (rpos + wsamples) % hw->samples; | 386 | rpos = (rpos + wsamples) % hw->samples; |
| 375 | break; | 387 | break; |
| 376 | } | 388 | } |
| 377 | } | 389 | } |
| 378 | 390 | ||
| 379 | - mixeng_clear (src, convert_samples); | ||
| 380 | - | ||
| 381 | rpos = (rpos + convert_samples) % hw->samples; | 391 | rpos = (rpos + convert_samples) % hw->samples; |
| 382 | samples -= convert_samples; | 392 | samples -= convert_samples; |
| 383 | } | 393 | } |
| @@ -730,6 +740,8 @@ static struct audio_option oss_options[] = { | @@ -730,6 +740,8 @@ static struct audio_option oss_options[] = { | ||
| 730 | "Path to DAC device", NULL, 0}, | 740 | "Path to DAC device", NULL, 0}, |
| 731 | {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, | 741 | {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, |
| 732 | "Path to ADC device", NULL, 0}, | 742 | "Path to ADC device", NULL, 0}, |
| 743 | + {"DEBUG", AUD_OPT_BOOL, &conf.debug, | ||
| 744 | + "Turn on some debugging messages", NULL, 0}, | ||
| 733 | {NULL, 0, NULL, NULL, NULL, 0} | 745 | {NULL, 0, NULL, NULL, NULL, 0} |
| 734 | }; | 746 | }; |
| 735 | 747 |
audio/sdlaudio.c
| @@ -240,7 +240,6 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | @@ -240,7 +240,6 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) | ||
| 240 | 240 | ||
| 241 | /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ | 241 | /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ |
| 242 | hw->clip (buf, src, chunk); | 242 | hw->clip (buf, src, chunk); |
| 243 | - mixeng_clear (src, chunk); | ||
| 244 | sdl->rpos = (sdl->rpos + chunk) % hw->samples; | 243 | sdl->rpos = (sdl->rpos + chunk) % hw->samples; |
| 245 | to_mix -= chunk; | 244 | to_mix -= chunk; |
| 246 | buf += chunk << hw->info.shift; | 245 | buf += chunk << hw->info.shift; |
audio/wavaudio.c
| @@ -81,7 +81,6 @@ static int wav_run_out (HWVoiceOut *hw) | @@ -81,7 +81,6 @@ static int wav_run_out (HWVoiceOut *hw) | ||
| 81 | 81 | ||
| 82 | hw->clip (dst, src, convert_samples); | 82 | hw->clip (dst, src, convert_samples); |
| 83 | qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); | 83 | qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); |
| 84 | - mixeng_clear (src, convert_samples); | ||
| 85 | 84 | ||
| 86 | rpos = (rpos + convert_samples) % hw->samples; | 85 | rpos = (rpos + convert_samples) % hw->samples; |
| 87 | samples -= convert_samples; | 86 | samples -= convert_samples; |
audio/wavcapture.c
0 โ 100644
| 1 | +#include "vl.h" | ||
| 2 | + | ||
| 3 | +typedef struct { | ||
| 4 | + QEMUFile *f; | ||
| 5 | + int bytes; | ||
| 6 | +} WAVState; | ||
| 7 | + | ||
| 8 | +/* VICE code: Store number as little endian. */ | ||
| 9 | +static void le_store (uint8_t *buf, uint32_t val, int len) | ||
| 10 | +{ | ||
| 11 | + int i; | ||
| 12 | + for (i = 0; i < len; i++) { | ||
| 13 | + buf[i] = (uint8_t) (val & 0xff); | ||
| 14 | + val >>= 8; | ||
| 15 | + } | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +static void wav_state_cb (void *opaque, int enabled) | ||
| 19 | +{ | ||
| 20 | + WAVState *wav = opaque; | ||
| 21 | + | ||
| 22 | + if (!enabled) { | ||
| 23 | + uint8_t rlen[4]; | ||
| 24 | + uint8_t dlen[4]; | ||
| 25 | + uint32_t datalen = wav->bytes; | ||
| 26 | + uint32_t rifflen = datalen + 36; | ||
| 27 | + | ||
| 28 | + if (!wav->f) { | ||
| 29 | + return; | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + le_store (rlen, rifflen, 4); | ||
| 33 | + le_store (dlen, datalen, 4); | ||
| 34 | + | ||
| 35 | + qemu_fseek (wav->f, 4, SEEK_SET); | ||
| 36 | + qemu_put_buffer (wav->f, rlen, 4); | ||
| 37 | + | ||
| 38 | + qemu_fseek (wav->f, 32, SEEK_CUR); | ||
| 39 | + qemu_put_buffer (wav->f, dlen, 4); | ||
| 40 | + } | ||
| 41 | + else { | ||
| 42 | + qemu_fseek (wav->f, 0, SEEK_END); | ||
| 43 | + } | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +static void wav_capture_cb (void *opaque, void *buf, int size) | ||
| 47 | +{ | ||
| 48 | + WAVState *wav = opaque; | ||
| 49 | + | ||
| 50 | + qemu_put_buffer (wav->f, buf, size); | ||
| 51 | + wav->bytes += size; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +void wav_capture (const char *path, int freq, int bits16, int stereo) | ||
| 55 | +{ | ||
| 56 | + WAVState *wav; | ||
| 57 | + uint8_t hdr[] = { | ||
| 58 | + 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, | ||
| 59 | + 0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, | ||
| 60 | + 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, | ||
| 61 | + 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 | ||
| 62 | + }; | ||
| 63 | + audsettings_t as; | ||
| 64 | + struct audio_capture_ops ops; | ||
| 65 | + int shift; | ||
| 66 | + | ||
| 67 | + stereo = !!stereo; | ||
| 68 | + bits16 = !!bits16; | ||
| 69 | + | ||
| 70 | + as.freq = freq; | ||
| 71 | + as.nchannels = 1 << stereo; | ||
| 72 | + as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | ||
| 73 | + | ||
| 74 | + ops.state = wav_state_cb; | ||
| 75 | + ops.capture = wav_capture_cb; | ||
| 76 | + | ||
| 77 | + wav = qemu_mallocz (sizeof (*wav)); | ||
| 78 | + if (!wav) { | ||
| 79 | + AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav)); | ||
| 80 | + return; | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + shift = bits16 + stereo; | ||
| 84 | + hdr[34] = bits16 ? 0x10 : 0x08; | ||
| 85 | + | ||
| 86 | + le_store (hdr + 22, as.nchannels, 2); | ||
| 87 | + le_store (hdr + 24, freq, 4); | ||
| 88 | + le_store (hdr + 28, freq << shift, 4); | ||
| 89 | + le_store (hdr + 32, 1 << shift, 2); | ||
| 90 | + | ||
| 91 | + wav->f = fopen (path, "wb"); | ||
| 92 | + if (!wav->f) { | ||
| 93 | + AUD_log ("wav", "Failed to open wave file `%s'\nReason: %s\n", | ||
| 94 | + path, strerror (errno)); | ||
| 95 | + qemu_free (wav); | ||
| 96 | + return; | ||
| 97 | + } | ||
| 98 | + | ||
| 99 | + qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | ||
| 100 | + AUD_add_capture (NULL, &as, 0, &ops, wav); | ||
| 101 | +} |
hw/es1370.c
| @@ -479,9 +479,10 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) | @@ -479,9 +479,10 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) | ||
| 479 | IO_WRITE_PROTO (es1370_writeb) | 479 | IO_WRITE_PROTO (es1370_writeb) |
| 480 | { | 480 | { |
| 481 | ES1370State *s = opaque; | 481 | ES1370State *s = opaque; |
| 482 | - addr = es1370_fixup (s, addr); | ||
| 483 | uint32_t shift, mask; | 482 | uint32_t shift, mask; |
| 484 | 483 | ||
| 484 | + addr = es1370_fixup (s, addr); | ||
| 485 | + | ||
| 485 | switch (addr) { | 486 | switch (addr) { |
| 486 | case ES1370_REG_CONTROL: | 487 | case ES1370_REG_CONTROL: |
| 487 | case ES1370_REG_CONTROL + 1: | 488 | case ES1370_REG_CONTROL + 1: |