Commit ec36b695b0bd054e673854fd1f02fa83e4bebea2
1 parent
63301264
audio capture to wab files (malc)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2059 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
8 changed files
with
319 additions
and
91 deletions
Makefile.target
audio/audio.c
... | ... | @@ -510,7 +510,8 @@ static void audio_print_settings (audsettings_t *as) |
510 | 510 | AUD_log (NULL, "invalid(%d)", as->fmt); |
511 | 511 | break; |
512 | 512 | } |
513 | - AUD_log (NULL, "endianness="); | |
513 | + | |
514 | + AUD_log (NULL, " endianness="); | |
514 | 515 | switch (as->endianness) { |
515 | 516 | case 0: |
516 | 517 | AUD_log (NULL, "little"); |
... | ... | @@ -525,7 +526,7 @@ static void audio_print_settings (audsettings_t *as) |
525 | 526 | AUD_log (NULL, "\n"); |
526 | 527 | } |
527 | 528 | |
528 | -static int audio_validate_settigs (audsettings_t *as) | |
529 | +static int audio_validate_settings (audsettings_t *as) | |
529 | 530 | { |
530 | 531 | int invalid; |
531 | 532 | |
... | ... | @@ -654,15 +655,25 @@ static CaptureVoiceOut *audio_pcm_capture_find_specific ( |
654 | 655 | return NULL; |
655 | 656 | } |
656 | 657 | |
657 | -static void audio_notify_capture (CaptureVoiceOut *cap, int enabled) | |
658 | +static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd) | |
658 | 659 | { |
659 | - if (cap->hw.enabled != enabled) { | |
660 | - struct capture_callback *cb; | |
660 | + struct capture_callback *cb; | |
661 | + | |
662 | +#ifdef DEBUG_CAPTURE | |
663 | + dolog ("notification %d sent\n", cmd); | |
664 | +#endif | |
665 | + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { | |
666 | + cb->ops.notify (cb->opaque, cmd); | |
667 | + } | |
668 | +} | |
661 | 669 | |
670 | +static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled) | |
671 | +{ | |
672 | + if (cap->hw.enabled != enabled) { | |
673 | + audcnotification_e cmd; | |
662 | 674 | cap->hw.enabled = enabled; |
663 | - for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { | |
664 | - cb->ops.state (cb->opaque, enabled); | |
665 | - } | |
675 | + cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE; | |
676 | + audio_notify_capture (cap, cmd); | |
666 | 677 | } |
667 | 678 | } |
668 | 679 | |
... | ... | @@ -672,29 +683,40 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap) |
672 | 683 | SWVoiceOut *sw; |
673 | 684 | int enabled = 0; |
674 | 685 | |
675 | - for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) { | |
686 | + for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
676 | 687 | if (sw->active) { |
677 | 688 | enabled = 1; |
678 | 689 | break; |
679 | 690 | } |
680 | 691 | } |
681 | - audio_notify_capture (cap, enabled); | |
692 | + audio_capture_maybe_changed (cap, enabled); | |
682 | 693 | } |
683 | 694 | |
684 | 695 | static void audio_detach_capture (HWVoiceOut *hw) |
685 | 696 | { |
686 | - SWVoiceOut *sw; | |
697 | + SWVoiceCap *sc = hw->cap_head.lh_first; | |
698 | + | |
699 | + while (sc) { | |
700 | + SWVoiceCap *sc1 = sc->entries.le_next; | |
701 | + SWVoiceOut *sw = &sc->sw; | |
702 | + CaptureVoiceOut *cap = sc->cap; | |
703 | + int was_active = sw->active; | |
687 | 704 | |
688 | - for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) { | |
689 | 705 | if (sw->rate) { |
690 | 706 | st_rate_stop (sw->rate); |
691 | 707 | sw->rate = NULL; |
692 | 708 | } |
693 | 709 | |
694 | 710 | LIST_REMOVE (sw, entries); |
695 | - LIST_REMOVE (sw, cap_entries); | |
696 | - qemu_free (sw); | |
697 | - audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw); | |
711 | + LIST_REMOVE (sc, entries); | |
712 | + qemu_free (sc); | |
713 | + if (was_active) { | |
714 | + /* We have removed soft voice from the capture: | |
715 | + this might have changed the overall status of the capture | |
716 | + since this might have been the only active voice */ | |
717 | + audio_recalc_and_notify_capture (cap); | |
718 | + } | |
719 | + sc = sc1; | |
698 | 720 | } |
699 | 721 | } |
700 | 722 | |
... | ... | @@ -704,19 +726,21 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) |
704 | 726 | |
705 | 727 | audio_detach_capture (hw); |
706 | 728 | for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { |
729 | + SWVoiceCap *sc; | |
707 | 730 | SWVoiceOut *sw; |
708 | - HWVoiceOut *hw_cap; | |
731 | + HWVoiceOut *hw_cap = &cap->hw; | |
709 | 732 | |
710 | - hw_cap = &cap->hw; | |
711 | - sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw)); | |
712 | - if (!sw) { | |
733 | + sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc)); | |
734 | + if (!sc) { | |
713 | 735 | dolog ("Could not allocate soft capture voice (%zu bytes)\n", |
714 | - sizeof (*sw)); | |
736 | + sizeof (*sc)); | |
715 | 737 | return -1; |
716 | 738 | } |
717 | 739 | |
718 | - sw->info = hw->info; | |
740 | + sc->cap = cap; | |
741 | + sw = &sc->sw; | |
719 | 742 | sw->hw = hw_cap; |
743 | + sw->info = hw->info; | |
720 | 744 | sw->empty = 1; |
721 | 745 | sw->active = hw->enabled; |
722 | 746 | sw->conv = noop_conv; |
... | ... | @@ -728,12 +752,14 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) |
728 | 752 | return -1; |
729 | 753 | } |
730 | 754 | LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); |
731 | - LIST_INSERT_HEAD (&hw->sw_cap_head, sw, cap_entries); | |
755 | + LIST_INSERT_HEAD (&hw->cap_head, sc, entries); | |
756 | +#ifdef DEBUG_CAPTURE | |
757 | + asprintf (&sw->name, "for %p %d,%d,%d", | |
758 | + hw, sw->info.freq, sw->info.bits, sw->info.nchannels); | |
759 | + dolog ("Added %s active = %d\n", sw->name, sw->active); | |
760 | +#endif | |
732 | 761 | if (sw->active) { |
733 | - audio_notify_capture (cap, 1); | |
734 | - } | |
735 | - else { | |
736 | - audio_recalc_and_notify_capture (cap); | |
762 | + audio_capture_maybe_changed (cap, 1); | |
737 | 763 | } |
738 | 764 | } |
739 | 765 | return 0; |
... | ... | @@ -915,6 +941,9 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) |
915 | 941 | } |
916 | 942 | |
917 | 943 | if (live == hwsamples) { |
944 | +#ifdef DEBUG_OUT | |
945 | + dolog ("%s is full %d\n", sw->name, live); | |
946 | +#endif | |
918 | 947 | return 0; |
919 | 948 | } |
920 | 949 | |
... | ... | @@ -1033,6 +1062,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) |
1033 | 1062 | hw = sw->hw; |
1034 | 1063 | if (sw->active != on) { |
1035 | 1064 | SWVoiceOut *temp_sw; |
1065 | + SWVoiceCap *sc; | |
1036 | 1066 | |
1037 | 1067 | if (on) { |
1038 | 1068 | hw->pending_disable = 0; |
... | ... | @@ -1053,11 +1083,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) |
1053 | 1083 | hw->pending_disable = nb_active == 1; |
1054 | 1084 | } |
1055 | 1085 | } |
1056 | - for (temp_sw = hw->sw_cap_head.lh_first; temp_sw; | |
1057 | - temp_sw = temp_sw->entries.le_next) { | |
1058 | - temp_sw->active = hw->enabled; | |
1086 | + | |
1087 | + for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { | |
1088 | + sc->sw.active = hw->enabled; | |
1059 | 1089 | if (hw->enabled) { |
1060 | - audio_notify_capture ((CaptureVoiceOut *) temp_sw->hw, 1); | |
1090 | + audio_capture_maybe_changed (sc->cap, 1); | |
1061 | 1091 | } |
1062 | 1092 | } |
1063 | 1093 | sw->active = on; |
... | ... | @@ -1156,9 +1186,10 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) |
1156 | 1186 | int n; |
1157 | 1187 | |
1158 | 1188 | if (hw->enabled) { |
1159 | - SWVoiceOut *sw; | |
1189 | + SWVoiceCap *sc; | |
1160 | 1190 | |
1161 | - for (sw = hw->sw_cap_head.lh_first; sw; sw = sw->cap_entries.le_next) { | |
1191 | + for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { | |
1192 | + SWVoiceOut *sw = &sc->sw; | |
1162 | 1193 | int rpos2 = rpos; |
1163 | 1194 | |
1164 | 1195 | n = samples; |
... | ... | @@ -1171,8 +1202,9 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) |
1171 | 1202 | sw->buf = hw->mix_buf + rpos2; |
1172 | 1203 | written = audio_pcm_sw_write (sw, NULL, bytes); |
1173 | 1204 | if (written - bytes) { |
1174 | - dolog ("Could not mix %d bytes into a capture buffer", | |
1175 | - bytes); | |
1205 | + dolog ("Could not mix %d bytes into a capture " | |
1206 | + "buffer, mixed %d\n", | |
1207 | + bytes, written); | |
1176 | 1208 | break; |
1177 | 1209 | } |
1178 | 1210 | n -= to_write; |
... | ... | @@ -1206,16 +1238,16 @@ static void audio_run_out (AudioState *s) |
1206 | 1238 | } |
1207 | 1239 | |
1208 | 1240 | if (hw->pending_disable && !nb_live) { |
1241 | + SWVoiceCap *sc; | |
1209 | 1242 | #ifdef DEBUG_OUT |
1210 | 1243 | dolog ("Disabling voice\n"); |
1211 | 1244 | #endif |
1212 | 1245 | hw->enabled = 0; |
1213 | 1246 | hw->pending_disable = 0; |
1214 | 1247 | hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); |
1215 | - for (sw = hw->sw_cap_head.lh_first; sw; | |
1216 | - sw = sw->cap_entries.le_next) { | |
1217 | - sw->active = 0; | |
1218 | - audio_recalc_and_notify_capture ((CaptureVoiceOut *) sw->hw); | |
1248 | + for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) { | |
1249 | + sc->sw.active = 0; | |
1250 | + audio_recalc_and_notify_capture (sc->cap); | |
1219 | 1251 | } |
1220 | 1252 | continue; |
1221 | 1253 | } |
... | ... | @@ -1277,15 +1309,18 @@ static void audio_run_out (AudioState *s) |
1277 | 1309 | } |
1278 | 1310 | |
1279 | 1311 | if (cleanup_required) { |
1280 | - restart: | |
1281 | - for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) { | |
1312 | + SWVoiceOut *sw1; | |
1313 | + | |
1314 | + sw = hw->sw_head.lh_first; | |
1315 | + while (sw) { | |
1316 | + sw1 = sw->entries.le_next; | |
1282 | 1317 | if (!sw->active && !sw->callback.fn) { |
1283 | 1318 | #ifdef DEBUG_PLIVE |
1284 | 1319 | dolog ("Finishing with old voice\n"); |
1285 | 1320 | #endif |
1286 | 1321 | audio_close_out (s, sw); |
1287 | - goto restart; /* play it safe */ | |
1288 | 1322 | } |
1323 | + sw = sw1; | |
1289 | 1324 | } |
1290 | 1325 | } |
1291 | 1326 | } |
... | ... | @@ -1537,13 +1572,18 @@ static void audio_atexit (void) |
1537 | 1572 | HWVoiceIn *hwi = NULL; |
1538 | 1573 | |
1539 | 1574 | while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { |
1540 | - SWVoiceOut *sw; | |
1575 | + SWVoiceCap *sc; | |
1541 | 1576 | |
1542 | 1577 | hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); |
1543 | 1578 | hwo->pcm_ops->fini_out (hwo); |
1544 | 1579 | |
1545 | - for (sw = hwo->sw_cap_head.lh_first; sw; sw = sw->entries.le_next) { | |
1546 | - audio_notify_capture ((CaptureVoiceOut *) sw->hw, 0); | |
1580 | + for (sc = hwo->cap_head.lh_first; sc; sc = sc->entries.le_next) { | |
1581 | + CaptureVoiceOut *cap = sc->cap; | |
1582 | + struct capture_callback *cb; | |
1583 | + | |
1584 | + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { | |
1585 | + cb->ops.destroy (cb->opaque); | |
1586 | + } | |
1547 | 1587 | } |
1548 | 1588 | } |
1549 | 1589 | |
... | ... | @@ -1697,7 +1737,7 @@ AudioState *AUD_init (void) |
1697 | 1737 | return s; |
1698 | 1738 | } |
1699 | 1739 | |
1700 | -int AUD_add_capture ( | |
1740 | +CaptureVoiceOut *AUD_add_capture ( | |
1701 | 1741 | AudioState *s, |
1702 | 1742 | audsettings_t *as, |
1703 | 1743 | struct audio_capture_ops *ops, |
... | ... | @@ -1712,10 +1752,10 @@ int AUD_add_capture ( |
1712 | 1752 | s = &glob_audio_state; |
1713 | 1753 | } |
1714 | 1754 | |
1715 | - if (audio_validate_settigs (as)) { | |
1755 | + if (audio_validate_settings (as)) { | |
1716 | 1756 | dolog ("Invalid settings were passed when trying to add capture\n"); |
1717 | 1757 | audio_print_settings (as); |
1718 | - return -1; | |
1758 | + goto err0; | |
1719 | 1759 | } |
1720 | 1760 | |
1721 | 1761 | cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); |
... | ... | @@ -1730,7 +1770,7 @@ int AUD_add_capture ( |
1730 | 1770 | cap = audio_pcm_capture_find_specific (s, as); |
1731 | 1771 | if (cap) { |
1732 | 1772 | LIST_INSERT_HEAD (&cap->cb_head, cb, entries); |
1733 | - return 0; | |
1773 | + return cap; | |
1734 | 1774 | } |
1735 | 1775 | else { |
1736 | 1776 | HWVoiceOut *hw; |
... | ... | @@ -1780,7 +1820,7 @@ int AUD_add_capture ( |
1780 | 1820 | while ((hw = audio_pcm_hw_find_any_out (s, hw))) { |
1781 | 1821 | audio_attach_capture (s, hw); |
1782 | 1822 | } |
1783 | - return 0; | |
1823 | + return cap; | |
1784 | 1824 | |
1785 | 1825 | err3: |
1786 | 1826 | qemu_free (cap->hw.mix_buf); |
... | ... | @@ -1789,6 +1829,38 @@ int AUD_add_capture ( |
1789 | 1829 | err1: |
1790 | 1830 | qemu_free (cb); |
1791 | 1831 | err0: |
1792 | - return -1; | |
1832 | + return NULL; | |
1833 | + } | |
1834 | +} | |
1835 | + | |
1836 | +void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque) | |
1837 | +{ | |
1838 | + struct capture_callback *cb; | |
1839 | + | |
1840 | + for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) { | |
1841 | + if (cb->opaque == cb_opaque) { | |
1842 | + cb->ops.destroy (cb_opaque); | |
1843 | + LIST_REMOVE (cb, entries); | |
1844 | + qemu_free (cb); | |
1845 | + | |
1846 | + if (!cap->cb_head.lh_first) { | |
1847 | + SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1; | |
1848 | + while (sw) { | |
1849 | +#ifdef DEBUG_CAPTURE | |
1850 | + dolog ("freeing %s\n", sw->name); | |
1851 | +#endif | |
1852 | + sw1 = sw->entries.le_next; | |
1853 | + if (sw->rate) { | |
1854 | + st_rate_stop (sw->rate); | |
1855 | + sw->rate = NULL; | |
1856 | + } | |
1857 | + LIST_REMOVE (sw, entries); | |
1858 | + sw = sw1; | |
1859 | + } | |
1860 | + LIST_REMOVE (cap, entries); | |
1861 | + qemu_free (cap); | |
1862 | + } | |
1863 | + return; | |
1864 | + } | |
1793 | 1865 | } |
1794 | 1866 | } | ... | ... |
audio/audio.h
... | ... | @@ -49,13 +49,31 @@ typedef struct { |
49 | 49 | int endianness; |
50 | 50 | } audsettings_t; |
51 | 51 | |
52 | +typedef enum { | |
53 | + AUD_CNOTIFY_ENABLE, | |
54 | + AUD_CNOTIFY_DISABLE | |
55 | +} audcnotification_e; | |
56 | + | |
52 | 57 | struct audio_capture_ops { |
53 | - void (*state) (void *opaque, int enabled); | |
58 | + void (*notify) (void *opaque, audcnotification_e cmd); | |
54 | 59 | void (*capture) (void *opaque, void *buf, int size); |
60 | + void (*destroy) (void *opaque); | |
55 | 61 | }; |
56 | 62 | |
63 | +struct capture_ops { | |
64 | + void (*info) (void *opaque); | |
65 | + void (*destroy) (void *opaque); | |
66 | +}; | |
67 | + | |
68 | +typedef struct CaptureState { | |
69 | + void *opaque; | |
70 | + struct capture_ops ops; | |
71 | + LIST_ENTRY (CaptureState) entries; | |
72 | +} CaptureState; | |
73 | + | |
57 | 74 | typedef struct AudioState AudioState; |
58 | 75 | typedef struct SWVoiceOut SWVoiceOut; |
76 | +typedef struct CaptureVoiceOut CaptureVoiceOut; | |
59 | 77 | typedef struct SWVoiceIn SWVoiceIn; |
60 | 78 | |
61 | 79 | typedef struct QEMUSoundCard { |
... | ... | @@ -79,12 +97,13 @@ AudioState *AUD_init (void); |
79 | 97 | void AUD_help (void); |
80 | 98 | void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); |
81 | 99 | void AUD_remove_card (QEMUSoundCard *card); |
82 | -int AUD_add_capture ( | |
100 | +CaptureVoiceOut *AUD_add_capture ( | |
83 | 101 | AudioState *s, |
84 | 102 | audsettings_t *as, |
85 | 103 | struct audio_capture_ops *ops, |
86 | 104 | void *opaque |
87 | 105 | ); |
106 | +void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque); | |
88 | 107 | |
89 | 108 | SWVoiceOut *AUD_open_out ( |
90 | 109 | QEMUSoundCard *card, | ... | ... |
audio/audio_int.h
... | ... | @@ -64,10 +64,11 @@ struct audio_pcm_info { |
64 | 64 | int swap_endianness; |
65 | 65 | }; |
66 | 66 | |
67 | +typedef struct SWVoiceCap SWVoiceCap; | |
68 | + | |
67 | 69 | typedef struct HWVoiceOut { |
68 | 70 | int enabled; |
69 | 71 | int pending_disable; |
70 | - int valid; | |
71 | 72 | struct audio_pcm_info info; |
72 | 73 | |
73 | 74 | f_sample *clip; |
... | ... | @@ -79,7 +80,7 @@ typedef struct HWVoiceOut { |
79 | 80 | |
80 | 81 | int samples; |
81 | 82 | LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; |
82 | - LIST_HEAD (sw_cap_listhead, SWVoiceOut) sw_cap_head; | |
83 | + LIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head; | |
83 | 84 | struct audio_pcm_ops *pcm_ops; |
84 | 85 | LIST_ENTRY (HWVoiceOut) entries; |
85 | 86 | } HWVoiceOut; |
... | ... | @@ -116,7 +117,6 @@ struct SWVoiceOut { |
116 | 117 | volume_t vol; |
117 | 118 | struct audio_callback callback; |
118 | 119 | LIST_ENTRY (SWVoiceOut) entries; |
119 | - LIST_ENTRY (SWVoiceOut) cap_entries; | |
120 | 120 | }; |
121 | 121 | |
122 | 122 | struct SWVoiceIn { |
... | ... | @@ -168,12 +168,18 @@ struct capture_callback { |
168 | 168 | LIST_ENTRY (capture_callback) entries; |
169 | 169 | }; |
170 | 170 | |
171 | -typedef struct CaptureVoiceOut { | |
171 | +struct CaptureVoiceOut { | |
172 | 172 | HWVoiceOut hw; |
173 | 173 | void *buf; |
174 | 174 | LIST_HEAD (cb_listhead, capture_callback) cb_head; |
175 | 175 | LIST_ENTRY (CaptureVoiceOut) entries; |
176 | -} CaptureVoiceOut; | |
176 | +}; | |
177 | + | |
178 | +struct SWVoiceCap { | |
179 | + SWVoiceOut sw; | |
180 | + CaptureVoiceOut *cap; | |
181 | + LIST_ENTRY (SWVoiceCap) entries; | |
182 | +}; | |
177 | 183 | |
178 | 184 | struct AudioState { |
179 | 185 | struct audio_driver *drv; | ... | ... |
audio/audio_template.h
... | ... | @@ -269,7 +269,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) |
269 | 269 | hw->pcm_ops = drv->pcm_ops; |
270 | 270 | LIST_INIT (&hw->sw_head); |
271 | 271 | #ifdef DAC |
272 | - LIST_INIT (&hw->sw_cap_head); | |
272 | + LIST_INIT (&hw->cap_head); | |
273 | 273 | #endif |
274 | 274 | if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { |
275 | 275 | goto err0; |
... | ... | @@ -426,7 +426,7 @@ SW *glue (AUD_open_, TYPE) ( |
426 | 426 | |
427 | 427 | s = card->audio; |
428 | 428 | |
429 | - if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) { | |
429 | + if (audio_bug (AUDIO_FUNC, audio_validate_settings (as))) { | |
430 | 430 | audio_print_settings (as); |
431 | 431 | goto fail; |
432 | 432 | } | ... | ... |
audio/noaudio.c
... | ... | @@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw) |
102 | 102 | NoVoiceIn *no = (NoVoiceIn *) hw; |
103 | 103 | int live = audio_pcm_hw_get_live_in (hw); |
104 | 104 | int dead = hw->samples - live; |
105 | - int samples; | |
105 | + int samples = 0; | |
106 | 106 | |
107 | 107 | if (dead) { |
108 | 108 | int64_t now = qemu_get_clock (vm_clock); | ... | ... |
audio/wavcapture.c
... | ... | @@ -3,6 +3,11 @@ |
3 | 3 | typedef struct { |
4 | 4 | QEMUFile *f; |
5 | 5 | int bytes; |
6 | + char *path; | |
7 | + int freq; | |
8 | + int bits; | |
9 | + int nchannels; | |
10 | + CaptureVoiceOut *cap; | |
6 | 11 | } WAVState; |
7 | 12 | |
8 | 13 | /* VICE code: Store number as little endian. */ |
... | ... | @@ -15,35 +20,39 @@ static void le_store (uint8_t *buf, uint32_t val, int len) |
15 | 20 | } |
16 | 21 | } |
17 | 22 | |
18 | -static void wav_state_cb (void *opaque, int enabled) | |
23 | +static void wav_notify (void *opaque, audcnotification_e cmd) | |
19 | 24 | { |
20 | - WAVState *wav = opaque; | |
25 | + (void) opaque; | |
26 | + (void) cmd; | |
27 | +} | |
21 | 28 | |
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; | |
29 | +static void wav_destroy (void *opaque) | |
30 | +{ | |
31 | + WAVState *wav = opaque; | |
32 | + uint8_t rlen[4]; | |
33 | + uint8_t dlen[4]; | |
34 | + uint32_t datalen = wav->bytes; | |
35 | + uint32_t rifflen = datalen + 36; | |
27 | 36 | |
28 | - if (!wav->f) { | |
29 | - return; | |
30 | - } | |
37 | + if (!wav->f) { | |
38 | + return; | |
39 | + } | |
31 | 40 | |
32 | - le_store (rlen, rifflen, 4); | |
33 | - le_store (dlen, datalen, 4); | |
41 | + le_store (rlen, rifflen, 4); | |
42 | + le_store (dlen, datalen, 4); | |
34 | 43 | |
35 | - qemu_fseek (wav->f, 4, SEEK_SET); | |
36 | - qemu_put_buffer (wav->f, rlen, 4); | |
44 | + qemu_fseek (wav->f, 4, SEEK_SET); | |
45 | + qemu_put_buffer (wav->f, rlen, 4); | |
37 | 46 | |
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); | |
47 | + qemu_fseek (wav->f, 32, SEEK_CUR); | |
48 | + qemu_put_buffer (wav->f, dlen, 4); | |
49 | + fclose (wav->f); | |
50 | + if (wav->path) { | |
51 | + qemu_free (wav->path); | |
43 | 52 | } |
44 | 53 | } |
45 | 54 | |
46 | -static void wav_capture_cb (void *opaque, void *buf, int size) | |
55 | +static void wav_capture (void *opaque, void *buf, int size) | |
47 | 56 | { |
48 | 57 | WAVState *wav = opaque; |
49 | 58 | |
... | ... | @@ -51,7 +60,30 @@ static void wav_capture_cb (void *opaque, void *buf, int size) |
51 | 60 | wav->bytes += size; |
52 | 61 | } |
53 | 62 | |
54 | -void wav_capture (const char *path, int freq, int bits16, int stereo) | |
63 | +static void wav_capture_destroy (void *opaque) | |
64 | +{ | |
65 | + WAVState *wav = opaque; | |
66 | + | |
67 | + AUD_del_capture (wav->cap, wav); | |
68 | +} | |
69 | + | |
70 | +static void wav_capture_info (void *opaque) | |
71 | +{ | |
72 | + WAVState *wav = opaque; | |
73 | + char *path = wav->path; | |
74 | + | |
75 | + term_printf ("Capturing audio(%d,%d,%d) to %s: %d bytes\n", | |
76 | + wav->freq, wav->bits, wav->nchannels, | |
77 | + path ? path : "<not available>", wav->bytes); | |
78 | +} | |
79 | + | |
80 | +static struct capture_ops wav_capture_ops = { | |
81 | + .destroy = wav_capture_destroy, | |
82 | + .info = wav_capture_info | |
83 | +}; | |
84 | + | |
85 | +int wav_start_capture (CaptureState *s, const char *path, int freq, | |
86 | + int bits, int nchannels) | |
55 | 87 | { |
56 | 88 | WAVState *wav; |
57 | 89 | uint8_t hdr[] = { |
... | ... | @@ -62,23 +94,35 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) |
62 | 94 | }; |
63 | 95 | audsettings_t as; |
64 | 96 | struct audio_capture_ops ops; |
65 | - int shift; | |
97 | + int stereo, bits16, shift; | |
98 | + CaptureVoiceOut *cap; | |
99 | + | |
100 | + if (bits != 8 && bits != 16) { | |
101 | + term_printf ("incorrect bit count %d, must be 8 or 16\n", bits); | |
102 | + return -1; | |
103 | + } | |
104 | + | |
105 | + if (nchannels != 1 && nchannels != 2) { | |
106 | + term_printf ("incorrect channel count %d, must be 1 or 2\n", bits); | |
107 | + return -1; | |
108 | + } | |
66 | 109 | |
67 | - stereo = !!stereo; | |
68 | - bits16 = !!bits16; | |
110 | + stereo = nchannels == 2; | |
111 | + bits16 = bits == 16; | |
69 | 112 | |
70 | 113 | as.freq = freq; |
71 | 114 | as.nchannels = 1 << stereo; |
72 | 115 | as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; |
73 | 116 | as.endianness = 0; |
74 | 117 | |
75 | - ops.state = wav_state_cb; | |
76 | - ops.capture = wav_capture_cb; | |
118 | + ops.notify = wav_notify; | |
119 | + ops.capture = wav_capture; | |
120 | + ops.destroy = wav_destroy; | |
77 | 121 | |
78 | 122 | wav = qemu_mallocz (sizeof (*wav)); |
79 | 123 | if (!wav) { |
80 | 124 | AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav)); |
81 | - return; | |
125 | + return -1; | |
82 | 126 | } |
83 | 127 | |
84 | 128 | shift = bits16 + stereo; |
... | ... | @@ -91,12 +135,28 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) |
91 | 135 | |
92 | 136 | wav->f = fopen (path, "wb"); |
93 | 137 | if (!wav->f) { |
94 | - AUD_log ("wav", "Failed to open wave file `%s'\nReason: %s\n", | |
95 | - path, strerror (errno)); | |
138 | + term_printf ("Failed to open wave file `%s'\nReason: %s\n", | |
139 | + path, strerror (errno)); | |
96 | 140 | qemu_free (wav); |
97 | - return; | |
141 | + return -1; | |
98 | 142 | } |
99 | 143 | |
144 | + wav->path = qemu_strdup (path); | |
145 | + wav->bits = bits; | |
146 | + wav->nchannels = nchannels; | |
147 | + wav->freq = freq; | |
148 | + | |
100 | 149 | qemu_put_buffer (wav->f, hdr, sizeof (hdr)); |
101 | - AUD_add_capture (NULL, &as, &ops, wav); | |
150 | + | |
151 | + cap = AUD_add_capture (NULL, &as, &ops, wav); | |
152 | + if (!cap) { | |
153 | + term_printf ("Failed to add audio capture\n"); | |
154 | + qemu_free (wav); | |
155 | + return -1; | |
156 | + } | |
157 | + | |
158 | + wav->cap = cap; | |
159 | + s->opaque = wav; | |
160 | + s->ops = wav_capture_ops; | |
161 | + return 0; | |
102 | 162 | } | ... | ... |
monitor.c
... | ... | @@ -1077,6 +1077,64 @@ static void do_info_profile(void) |
1077 | 1077 | } |
1078 | 1078 | #endif |
1079 | 1079 | |
1080 | +/* Capture support */ | |
1081 | +static LIST_HEAD (capture_list_head, CaptureState) capture_head; | |
1082 | + | |
1083 | +static void do_info_capture (void) | |
1084 | +{ | |
1085 | + int i; | |
1086 | + CaptureState *s; | |
1087 | + | |
1088 | + for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { | |
1089 | + term_printf ("[%d]: ", i); | |
1090 | + s->ops.info (s->opaque); | |
1091 | + } | |
1092 | +} | |
1093 | + | |
1094 | +static void do_stop_capture (int n) | |
1095 | +{ | |
1096 | + int i; | |
1097 | + CaptureState *s; | |
1098 | + | |
1099 | + for (s = capture_head.lh_first, i = 0; s; s = s->entries.le_next, ++i) { | |
1100 | + if (i == n) { | |
1101 | + s->ops.destroy (s->opaque); | |
1102 | + LIST_REMOVE (s, entries); | |
1103 | + qemu_free (s); | |
1104 | + return; | |
1105 | + } | |
1106 | + } | |
1107 | +} | |
1108 | + | |
1109 | +#ifdef HAS_AUDIO | |
1110 | +int wav_start_capture (CaptureState *s, const char *path, int freq, | |
1111 | + int bits, int nchannels); | |
1112 | + | |
1113 | +static void do_wav_capture (const char *path, | |
1114 | + int has_freq, int freq, | |
1115 | + int has_bits, int bits, | |
1116 | + int has_channels, int nchannels) | |
1117 | +{ | |
1118 | + CaptureState *s; | |
1119 | + | |
1120 | + s = qemu_mallocz (sizeof (*s)); | |
1121 | + if (!s) { | |
1122 | + term_printf ("Not enough memory to add wave capture\n"); | |
1123 | + return; | |
1124 | + } | |
1125 | + | |
1126 | + freq = has_freq ? freq : 44100; | |
1127 | + bits = has_bits ? bits : 16; | |
1128 | + nchannels = has_channels ? nchannels : 2; | |
1129 | + | |
1130 | + if (wav_start_capture (s, path, freq, bits, nchannels)) { | |
1131 | + term_printf ("Faied to add wave capture\n"); | |
1132 | + qemu_free (s); | |
1133 | + } | |
1134 | + LIST_INSERT_HEAD (&capture_head, s, entries); | |
1135 | +} | |
1136 | +#endif | |
1137 | + | |
1080 | 1138 | static term_cmd_t term_cmds[] = { |
1081 | 1139 | { "help|?", "s?", do_help, |
1082 | 1140 | "[cmd]", "show the help" }, |
... | ... | @@ -1133,6 +1191,13 @@ static term_cmd_t term_cmds[] = { |
1133 | 1191 | "dx dy [dz]", "send mouse move events" }, |
1134 | 1192 | { "mouse_button", "i", do_mouse_button, |
1135 | 1193 | "state", "change mouse button state (1=L, 2=M, 4=R)" }, |
1194 | +#ifdef HAS_AUDIO | |
1195 | + { "wavcapture", "si?i?i?", do_wav_capture, | |
1196 | + "path [frequency bits channels]", | |
1197 | + "capture audio to a wave file (default frequency=44100 bits=16 channels=2)" }, | |
1198 | +#endif | |
1199 | + { "stopcapture", "i", do_stop_capture, | |
1200 | + "capture index", "stop capture" }, | |
1136 | 1201 | { NULL, NULL, }, |
1137 | 1202 | }; |
1138 | 1203 | |
... | ... | @@ -1171,6 +1236,8 @@ static term_cmd_t info_cmds[] = { |
1171 | 1236 | "", "show host USB devices", }, |
1172 | 1237 | { "profile", "", do_info_profile, |
1173 | 1238 | "", "show profiling information", }, |
1239 | + { "capture", "", do_info_capture, | |
1240 | + "show capture information" }, | |
1174 | 1241 | { NULL, NULL, }, |
1175 | 1242 | }; |
1176 | 1243 | |
... | ... | @@ -2081,6 +2148,9 @@ static void monitor_handle_command(const char *cmdline) |
2081 | 2148 | case 6: |
2082 | 2149 | cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]); |
2083 | 2150 | break; |
2151 | + case 7: | |
2152 | + cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); | |
2153 | + break; | |
2084 | 2154 | default: |
2085 | 2155 | term_printf("unsupported number of arguments: %d\n", nb_args); |
2086 | 2156 | goto fail; | ... | ... |