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 | 61 | .size_in_usec_in = 1, |
62 | 62 | .size_in_usec_out = 1, |
63 | 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 | 66 | #ifdef HIGH_LATENCY |
67 | 67 | .buffer_size_in = 400000, |
68 | 68 | .period_size_in = 400000 / 4, |
... | ... | @@ -606,7 +606,6 @@ static int alsa_run_out (HWVoiceOut *hw) |
606 | 606 | } |
607 | 607 | } |
608 | 608 | |
609 | - mixeng_clear (src, written); | |
610 | 609 | rpos = (rpos + written) % hw->samples; |
611 | 610 | samples -= written; |
612 | 611 | len -= written; | ... | ... |
audio/audio.c
... | ... | @@ -29,6 +29,7 @@ |
29 | 29 | /* #define DEBUG_PLIVE */ |
30 | 30 | /* #define DEBUG_LIVE */ |
31 | 31 | /* #define DEBUG_OUT */ |
32 | +/* #define DEBUG_CAPTURE */ | |
32 | 33 | |
33 | 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 | 138 | if (cond) { |
138 | 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 | 142 | if (!shown) { |
142 | 143 | shown = 1; |
143 | 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 | 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 | 740 | * Hard voice (capture) |
625 | 741 | */ |
626 | 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 | 1032 | SWVoiceOut *temp_sw; |
917 | 1033 | |
918 | 1034 | if (on) { |
919 | - int total; | |
920 | - | |
921 | 1035 | hw->pending_disable = 0; |
922 | 1036 | if (!hw->enabled) { |
923 | 1037 | hw->enabled = 1; |
924 | 1038 | hw->pcm_ops->ctl_out (hw, VOICE_ENABLE); |
925 | 1039 | } |
926 | - | |
927 | - if (sw->empty) { | |
928 | - total = 0; | |
929 | - } | |
930 | 1040 | } |
931 | 1041 | else { |
932 | 1042 | if (hw->enabled) { |
... | ... | @@ -940,6 +1050,13 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) |
940 | 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 | 1060 | sw->active = on; |
944 | 1061 | } |
945 | 1062 | } |
... | ... | @@ -1031,6 +1148,41 @@ static int audio_get_free (SWVoiceOut *sw) |
1031 | 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 | 1186 | static void audio_run_out (AudioState *s) |
1035 | 1187 | { |
1036 | 1188 | HWVoiceOut *hw = NULL; |
... | ... | @@ -1038,7 +1190,7 @@ static void audio_run_out (AudioState *s) |
1038 | 1190 | |
1039 | 1191 | while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) { |
1040 | 1192 | int played; |
1041 | - int live, free, nb_live, cleanup_required; | |
1193 | + int live, free, nb_live, cleanup_required, prev_rpos; | |
1042 | 1194 | |
1043 | 1195 | live = audio_pcm_hw_get_live_out2 (hw, &nb_live); |
1044 | 1196 | if (!nb_live) { |
... | ... | @@ -1057,6 +1209,11 @@ static void audio_run_out (AudioState *s) |
1057 | 1209 | hw->enabled = 0; |
1058 | 1210 | hw->pending_disable = 0; |
1059 | 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 | 1217 | continue; |
1061 | 1218 | } |
1062 | 1219 | |
... | ... | @@ -1072,6 +1229,7 @@ static void audio_run_out (AudioState *s) |
1072 | 1229 | continue; |
1073 | 1230 | } |
1074 | 1231 | |
1232 | + prev_rpos = hw->rpos; | |
1075 | 1233 | played = hw->pcm_ops->run_out (hw); |
1076 | 1234 | if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) { |
1077 | 1235 | dolog ("hw->rpos=%d hw->samples=%d played=%d\n", |
... | ... | @@ -1085,6 +1243,7 @@ static void audio_run_out (AudioState *s) |
1085 | 1243 | |
1086 | 1244 | if (played) { |
1087 | 1245 | hw->ts_helper += played; |
1246 | + audio_capture_mix_and_clear (hw, prev_rpos, played); | |
1088 | 1247 | } |
1089 | 1248 | |
1090 | 1249 | cleanup_required = 0; |
... | ... | @@ -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 | 1367 | static void audio_timer (void *opaque) |
1162 | 1368 | { |
1163 | 1369 | AudioState *s = opaque; |
1164 | 1370 | |
1165 | 1371 | audio_run_out (s); |
1166 | 1372 | audio_run_in (s); |
1373 | + audio_run_capture (s); | |
1167 | 1374 | |
1168 | 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 | 1534 | HWVoiceIn *hwi = NULL; |
1328 | 1535 | |
1329 | 1536 | while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { |
1537 | + SWVoiceOut *sw; | |
1538 | + | |
1330 | 1539 | hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); |
1331 | 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 | 1547 | while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) { |
... | ... | @@ -1383,6 +1596,7 @@ AudioState *AUD_init (void) |
1383 | 1596 | |
1384 | 1597 | LIST_INIT (&s->hw_head_out); |
1385 | 1598 | LIST_INIT (&s->hw_head_in); |
1599 | + LIST_INIT (&s->cap_head); | |
1386 | 1600 | atexit (audio_atexit); |
1387 | 1601 | |
1388 | 1602 | s->ts = qemu_new_timer (vm_clock, audio_timer, s); |
... | ... | @@ -1479,3 +1693,100 @@ AudioState *AUD_init (void) |
1479 | 1693 | qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); |
1480 | 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 | 41 | audfmt_e fmt; |
42 | 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 | 49 | typedef struct AudioState AudioState; |
45 | 50 | typedef struct SWVoiceOut SWVoiceOut; |
46 | 51 | typedef struct SWVoiceIn SWVoiceIn; |
... | ... | @@ -66,6 +71,13 @@ AudioState *AUD_init (void); |
66 | 71 | void AUD_help (void); |
67 | 72 | void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); |
68 | 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 | 82 | SWVoiceOut *AUD_open_out ( |
71 | 83 | QEMUSoundCard *card, |
... | ... | @@ -111,7 +123,7 @@ static inline void *advance (void *p, int incr) |
111 | 123 | } |
112 | 124 | |
113 | 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 | 128 | #ifdef __GNUC__ |
117 | 129 | #define audio_MIN(a, b) ( __extension__ ({ \ | ... | ... |
audio/audio_int.h
... | ... | @@ -79,6 +79,7 @@ typedef struct HWVoiceOut { |
79 | 79 | |
80 | 80 | int samples; |
81 | 81 | LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; |
82 | + LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head; | |
82 | 83 | struct audio_pcm_ops *pcm_ops; |
83 | 84 | LIST_ENTRY (HWVoiceOut) entries; |
84 | 85 | } HWVoiceOut; |
... | ... | @@ -115,6 +116,7 @@ struct SWVoiceOut { |
115 | 116 | volume_t vol; |
116 | 117 | struct audio_callback callback; |
117 | 118 | LIST_ENTRY (SWVoiceOut) entries; |
119 | + LIST_ENTRY (SWVoiceOut) cap_entries; | |
118 | 120 | }; |
119 | 121 | |
120 | 122 | struct SWVoiceIn { |
... | ... | @@ -160,14 +162,28 @@ struct audio_pcm_ops { |
160 | 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 | 178 | struct AudioState { |
164 | 179 | struct audio_driver *drv; |
165 | 180 | void *drv_opaque; |
166 | 181 | |
167 | 182 | QEMUTimer *ts; |
168 | - LIST_HEAD (card_head, QEMUSoundCard) card_head; | |
183 | + LIST_HEAD (card_listhead, QEMUSoundCard) card_head; | |
169 | 184 | LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in; |
170 | 185 | LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out; |
186 | + LIST_HEAD (cap_listhead, CaptureVoiceOut) cap_head; | |
171 | 187 | int nb_hw_voices_out; |
172 | 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 | 200 | HW *hw = *hwp; |
201 | 201 | |
202 | 202 | if (!hw->sw_head.lh_first) { |
203 | +#ifdef DAC | |
204 | + audio_detach_capture (hw); | |
205 | +#endif | |
203 | 206 | LIST_REMOVE (hw, entries); |
204 | 207 | glue (s->nb_hw_voices_, TYPE) += 1; |
205 | 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 | 269 | |
267 | 270 | hw->pcm_ops = drv->pcm_ops; |
268 | 271 | LIST_INIT (&hw->sw_head); |
269 | - | |
272 | +#ifdef DAC | |
273 | + LIST_INIT (&hw->sw_cap_head); | |
274 | +#endif | |
270 | 275 | if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { |
271 | 276 | goto err0; |
272 | 277 | } |
... | ... | @@ -292,6 +297,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) |
292 | 297 | |
293 | 298 | LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries); |
294 | 299 | glue (s->nb_hw_voices_, TYPE) -= 1; |
300 | +#ifdef DAC | |
301 | + audio_attach_capture (s, hw); | |
302 | +#endif | |
295 | 303 | return hw; |
296 | 304 | |
297 | 305 | err1: |
... | ... | @@ -542,7 +550,7 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts) |
542 | 550 | |
543 | 551 | cur_ts = sw->hw->ts_helper; |
544 | 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 | 555 | if (cur_ts >= old_ts) { |
548 | 556 | delta = cur_ts - old_ts; | ... | ... |
audio/coreaudio.c
audio/dsound_template.h
... | ... | @@ -70,7 +70,13 @@ static int glue (dsound_lock_, TYPE) ( |
70 | 70 | int i; |
71 | 71 | LPVOID p1 = NULL, p2 = NULL; |
72 | 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 | 80 | for (i = 0; i < conf.lock_retries; ++i) { |
75 | 81 | hr = glue (IFACE, _Lock) ( |
76 | 82 | buf, |
... | ... | @@ -80,13 +86,7 @@ static int glue (dsound_lock_, TYPE) ( |
80 | 86 | &blen1, |
81 | 87 | &p2, |
82 | 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 | 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 | 453 | |
454 | 454 | if (src_len1) { |
455 | 455 | hw->clip (dst, src1, src_len1); |
456 | - mixeng_clear (src1, src_len1); | |
457 | 456 | } |
458 | 457 | |
459 | 458 | if (src_len2) { |
460 | 459 | dst = advance (dst, src_len1 << hw->info.shift); |
461 | 460 | hw->clip (dst, src2, src_len2); |
462 | - mixeng_clear (src2, src_len2); | |
463 | 461 | } |
464 | 462 | |
465 | 463 | hw->rpos = pos % hw->samples; |
... | ... | @@ -987,6 +985,12 @@ static void *dsound_audio_init (void) |
987 | 985 | hr = IDirectSound_Initialize (s->dsound, NULL); |
988 | 986 | if (FAILED (hr)) { |
989 | 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 | 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 | 153 | |
154 | 154 | if (src_len1) { |
155 | 155 | hw->clip (dst, src1, src_len1); |
156 | - mixeng_clear (src1, src_len1); | |
157 | 156 | } |
158 | 157 | |
159 | 158 | if (src_len2) { |
160 | 159 | dst = advance (dst, src_len1 << hw->info.shift); |
161 | 160 | hw->clip (dst, src2, src_len2); |
162 | - mixeng_clear (src2, src_len2); | |
163 | 161 | } |
164 | 162 | |
165 | 163 | hw->rpos = pos % hw->samples; | ... | ... |
audio/noaudio.c
... | ... | @@ -40,22 +40,21 @@ static int no_run_out (HWVoiceOut *hw) |
40 | 40 | { |
41 | 41 | NoVoiceOut *no = (NoVoiceOut *) hw; |
42 | 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 | 47 | live = audio_pcm_hw_get_live_out (&no->hw); |
55 | 48 | if (!live) { |
56 | 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 | 58 | no->old_ticks = now; |
60 | 59 | decr = audio_MIN (live, samples); |
61 | 60 | hw->rpos = (hw->rpos + decr) % hw->samples; |
... | ... | @@ -101,17 +100,20 @@ static void no_fini_in (HWVoiceIn *hw) |
101 | 100 | static int no_run_in (HWVoiceIn *hw) |
102 | 101 | { |
103 | 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 | 103 | int live = audio_pcm_hw_get_live_in (hw); |
108 | 104 | int dead = hw->samples - live; |
109 | 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 | 117 | return samples; |
116 | 118 | } |
117 | 119 | ... | ... |
audio/ossaudio.c
... | ... | @@ -55,12 +55,14 @@ static struct { |
55 | 55 | int fragsize; |
56 | 56 | const char *devpath_out; |
57 | 57 | const char *devpath_in; |
58 | + int debug; | |
58 | 59 | } conf = { |
59 | 60 | .try_mmap = 0, |
60 | 61 | .nfrags = 4, |
61 | 62 | .fragsize = 4096, |
62 | 63 | .devpath_out = "/dev/dsp", |
63 | - .devpath_in = "/dev/dsp" | |
64 | + .devpath_in = "/dev/dsp", | |
65 | + .debug = 0 | |
64 | 66 | }; |
65 | 67 | |
66 | 68 | struct oss_params { |
... | ... | @@ -324,9 +326,20 @@ static int oss_run_out (HWVoiceOut *hw) |
324 | 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 | 343 | return 0; |
331 | 344 | } |
332 | 345 | |
... | ... | @@ -369,15 +382,12 @@ static int oss_run_out (HWVoiceOut *hw) |
369 | 382 | "alignment %d\n", |
370 | 383 | wbytes, written, hw->info.align + 1); |
371 | 384 | } |
372 | - mixeng_clear (src, wsamples); | |
373 | 385 | decr -= wsamples; |
374 | 386 | rpos = (rpos + wsamples) % hw->samples; |
375 | 387 | break; |
376 | 388 | } |
377 | 389 | } |
378 | 390 | |
379 | - mixeng_clear (src, convert_samples); | |
380 | - | |
381 | 391 | rpos = (rpos + convert_samples) % hw->samples; |
382 | 392 | samples -= convert_samples; |
383 | 393 | } |
... | ... | @@ -730,6 +740,8 @@ static struct audio_option oss_options[] = { |
730 | 740 | "Path to DAC device", NULL, 0}, |
731 | 741 | {"ADC_DEV", AUD_OPT_STR, &conf.devpath_in, |
732 | 742 | "Path to ADC device", NULL, 0}, |
743 | + {"DEBUG", AUD_OPT_BOOL, &conf.debug, | |
744 | + "Turn on some debugging messages", NULL, 0}, | |
733 | 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 | 240 | |
241 | 241 | /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ |
242 | 242 | hw->clip (buf, src, chunk); |
243 | - mixeng_clear (src, chunk); | |
244 | 243 | sdl->rpos = (sdl->rpos + chunk) % hw->samples; |
245 | 244 | to_mix -= chunk; |
246 | 245 | buf += chunk << hw->info.shift; | ... | ... |
audio/wavaudio.c
... | ... | @@ -81,7 +81,6 @@ static int wav_run_out (HWVoiceOut *hw) |
81 | 81 | |
82 | 82 | hw->clip (dst, src, convert_samples); |
83 | 83 | qemu_put_buffer (wav->f, dst, convert_samples << hw->info.shift); |
84 | - mixeng_clear (src, convert_samples); | |
85 | 84 | |
86 | 85 | rpos = (rpos + convert_samples) % hw->samples; |
87 | 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 | 479 | IO_WRITE_PROTO (es1370_writeb) |
480 | 480 | { |
481 | 481 | ES1370State *s = opaque; |
482 | - addr = es1370_fixup (s, addr); | |
483 | 482 | uint32_t shift, mask; |
484 | 483 | |
484 | + addr = es1370_fixup (s, addr); | |
485 | + | |
485 | 486 | switch (addr) { |
486 | 487 | case ES1370_REG_CONTROL: |
487 | 488 | case ES1370_REG_CONTROL + 1: | ... | ... |