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; | ... | ... |