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
@@ -321,6 +321,7 @@ endif | @@ -321,6 +321,7 @@ endif | ||
321 | ifdef CONFIG_ADLIB | 321 | ifdef CONFIG_ADLIB |
322 | SOUND_HW += fmopl.o adlib.o | 322 | SOUND_HW += fmopl.o adlib.o |
323 | endif | 323 | endif |
324 | +AUDIODRV+= wavcapture.o | ||
324 | 325 | ||
325 | # SCSI layer | 326 | # SCSI layer |
326 | VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o | 327 | VL_OBJS+= scsi-disk.o cdrom.o lsi53c895a.o |
audio/audio.c
@@ -510,7 +510,8 @@ static void audio_print_settings (audsettings_t *as) | @@ -510,7 +510,8 @@ static void audio_print_settings (audsettings_t *as) | ||
510 | AUD_log (NULL, "invalid(%d)", as->fmt); | 510 | AUD_log (NULL, "invalid(%d)", as->fmt); |
511 | break; | 511 | break; |
512 | } | 512 | } |
513 | - AUD_log (NULL, "endianness="); | 513 | + |
514 | + AUD_log (NULL, " endianness="); | ||
514 | switch (as->endianness) { | 515 | switch (as->endianness) { |
515 | case 0: | 516 | case 0: |
516 | AUD_log (NULL, "little"); | 517 | AUD_log (NULL, "little"); |
@@ -525,7 +526,7 @@ static void audio_print_settings (audsettings_t *as) | @@ -525,7 +526,7 @@ static void audio_print_settings (audsettings_t *as) | ||
525 | AUD_log (NULL, "\n"); | 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 | int invalid; | 531 | int invalid; |
531 | 532 | ||
@@ -654,15 +655,25 @@ static CaptureVoiceOut *audio_pcm_capture_find_specific ( | @@ -654,15 +655,25 @@ static CaptureVoiceOut *audio_pcm_capture_find_specific ( | ||
654 | return NULL; | 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 | cap->hw.enabled = enabled; | 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,29 +683,40 @@ static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap) | ||
672 | SWVoiceOut *sw; | 683 | SWVoiceOut *sw; |
673 | int enabled = 0; | 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 | if (sw->active) { | 687 | if (sw->active) { |
677 | enabled = 1; | 688 | enabled = 1; |
678 | break; | 689 | break; |
679 | } | 690 | } |
680 | } | 691 | } |
681 | - audio_notify_capture (cap, enabled); | 692 | + audio_capture_maybe_changed (cap, enabled); |
682 | } | 693 | } |
683 | 694 | ||
684 | static void audio_detach_capture (HWVoiceOut *hw) | 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 | if (sw->rate) { | 705 | if (sw->rate) { |
690 | st_rate_stop (sw->rate); | 706 | st_rate_stop (sw->rate); |
691 | sw->rate = NULL; | 707 | sw->rate = NULL; |
692 | } | 708 | } |
693 | 709 | ||
694 | LIST_REMOVE (sw, entries); | 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,19 +726,21 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) | ||
704 | 726 | ||
705 | audio_detach_capture (hw); | 727 | audio_detach_capture (hw); |
706 | for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { | 728 | for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) { |
729 | + SWVoiceCap *sc; | ||
707 | SWVoiceOut *sw; | 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 | dolog ("Could not allocate soft capture voice (%zu bytes)\n", | 735 | dolog ("Could not allocate soft capture voice (%zu bytes)\n", |
714 | - sizeof (*sw)); | 736 | + sizeof (*sc)); |
715 | return -1; | 737 | return -1; |
716 | } | 738 | } |
717 | 739 | ||
718 | - sw->info = hw->info; | 740 | + sc->cap = cap; |
741 | + sw = &sc->sw; | ||
719 | sw->hw = hw_cap; | 742 | sw->hw = hw_cap; |
743 | + sw->info = hw->info; | ||
720 | sw->empty = 1; | 744 | sw->empty = 1; |
721 | sw->active = hw->enabled; | 745 | sw->active = hw->enabled; |
722 | sw->conv = noop_conv; | 746 | sw->conv = noop_conv; |
@@ -728,12 +752,14 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) | @@ -728,12 +752,14 @@ static int audio_attach_capture (AudioState *s, HWVoiceOut *hw) | ||
728 | return -1; | 752 | return -1; |
729 | } | 753 | } |
730 | LIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries); | 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 | if (sw->active) { | 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 | return 0; | 765 | return 0; |
@@ -915,6 +941,9 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) | @@ -915,6 +941,9 @@ int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) | ||
915 | } | 941 | } |
916 | 942 | ||
917 | if (live == hwsamples) { | 943 | if (live == hwsamples) { |
944 | +#ifdef DEBUG_OUT | ||
945 | + dolog ("%s is full %d\n", sw->name, live); | ||
946 | +#endif | ||
918 | return 0; | 947 | return 0; |
919 | } | 948 | } |
920 | 949 | ||
@@ -1033,6 +1062,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | @@ -1033,6 +1062,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | ||
1033 | hw = sw->hw; | 1062 | hw = sw->hw; |
1034 | if (sw->active != on) { | 1063 | if (sw->active != on) { |
1035 | SWVoiceOut *temp_sw; | 1064 | SWVoiceOut *temp_sw; |
1065 | + SWVoiceCap *sc; | ||
1036 | 1066 | ||
1037 | if (on) { | 1067 | if (on) { |
1038 | hw->pending_disable = 0; | 1068 | hw->pending_disable = 0; |
@@ -1053,11 +1083,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | @@ -1053,11 +1083,11 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) | ||
1053 | hw->pending_disable = nb_active == 1; | 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 | if (hw->enabled) { | 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 | sw->active = on; | 1093 | sw->active = on; |
@@ -1156,9 +1186,10 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) | @@ -1156,9 +1186,10 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) | ||
1156 | int n; | 1186 | int n; |
1157 | 1187 | ||
1158 | if (hw->enabled) { | 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 | int rpos2 = rpos; | 1193 | int rpos2 = rpos; |
1163 | 1194 | ||
1164 | n = samples; | 1195 | n = samples; |
@@ -1171,8 +1202,9 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) | @@ -1171,8 +1202,9 @@ static void audio_capture_mix_and_clear (HWVoiceOut *hw, int rpos, int samples) | ||
1171 | sw->buf = hw->mix_buf + rpos2; | 1202 | sw->buf = hw->mix_buf + rpos2; |
1172 | written = audio_pcm_sw_write (sw, NULL, bytes); | 1203 | written = audio_pcm_sw_write (sw, NULL, bytes); |
1173 | if (written - bytes) { | 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 | break; | 1208 | break; |
1177 | } | 1209 | } |
1178 | n -= to_write; | 1210 | n -= to_write; |
@@ -1206,16 +1238,16 @@ static void audio_run_out (AudioState *s) | @@ -1206,16 +1238,16 @@ static void audio_run_out (AudioState *s) | ||
1206 | } | 1238 | } |
1207 | 1239 | ||
1208 | if (hw->pending_disable && !nb_live) { | 1240 | if (hw->pending_disable && !nb_live) { |
1241 | + SWVoiceCap *sc; | ||
1209 | #ifdef DEBUG_OUT | 1242 | #ifdef DEBUG_OUT |
1210 | dolog ("Disabling voice\n"); | 1243 | dolog ("Disabling voice\n"); |
1211 | #endif | 1244 | #endif |
1212 | hw->enabled = 0; | 1245 | hw->enabled = 0; |
1213 | hw->pending_disable = 0; | 1246 | hw->pending_disable = 0; |
1214 | hw->pcm_ops->ctl_out (hw, VOICE_DISABLE); | 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 | continue; | 1252 | continue; |
1221 | } | 1253 | } |
@@ -1277,15 +1309,18 @@ static void audio_run_out (AudioState *s) | @@ -1277,15 +1309,18 @@ static void audio_run_out (AudioState *s) | ||
1277 | } | 1309 | } |
1278 | 1310 | ||
1279 | if (cleanup_required) { | 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 | if (!sw->active && !sw->callback.fn) { | 1317 | if (!sw->active && !sw->callback.fn) { |
1283 | #ifdef DEBUG_PLIVE | 1318 | #ifdef DEBUG_PLIVE |
1284 | dolog ("Finishing with old voice\n"); | 1319 | dolog ("Finishing with old voice\n"); |
1285 | #endif | 1320 | #endif |
1286 | audio_close_out (s, sw); | 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,13 +1572,18 @@ static void audio_atexit (void) | ||
1537 | HWVoiceIn *hwi = NULL; | 1572 | HWVoiceIn *hwi = NULL; |
1538 | 1573 | ||
1539 | while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { | 1574 | while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) { |
1540 | - SWVoiceOut *sw; | 1575 | + SWVoiceCap *sc; |
1541 | 1576 | ||
1542 | hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); | 1577 | hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE); |
1543 | hwo->pcm_ops->fini_out (hwo); | 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,7 +1737,7 @@ AudioState *AUD_init (void) | ||
1697 | return s; | 1737 | return s; |
1698 | } | 1738 | } |
1699 | 1739 | ||
1700 | -int AUD_add_capture ( | 1740 | +CaptureVoiceOut *AUD_add_capture ( |
1701 | AudioState *s, | 1741 | AudioState *s, |
1702 | audsettings_t *as, | 1742 | audsettings_t *as, |
1703 | struct audio_capture_ops *ops, | 1743 | struct audio_capture_ops *ops, |
@@ -1712,10 +1752,10 @@ int AUD_add_capture ( | @@ -1712,10 +1752,10 @@ int AUD_add_capture ( | ||
1712 | s = &glob_audio_state; | 1752 | s = &glob_audio_state; |
1713 | } | 1753 | } |
1714 | 1754 | ||
1715 | - if (audio_validate_settigs (as)) { | 1755 | + if (audio_validate_settings (as)) { |
1716 | dolog ("Invalid settings were passed when trying to add capture\n"); | 1756 | dolog ("Invalid settings were passed when trying to add capture\n"); |
1717 | audio_print_settings (as); | 1757 | audio_print_settings (as); |
1718 | - return -1; | 1758 | + goto err0; |
1719 | } | 1759 | } |
1720 | 1760 | ||
1721 | cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); | 1761 | cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb)); |
@@ -1730,7 +1770,7 @@ int AUD_add_capture ( | @@ -1730,7 +1770,7 @@ int AUD_add_capture ( | ||
1730 | cap = audio_pcm_capture_find_specific (s, as); | 1770 | cap = audio_pcm_capture_find_specific (s, as); |
1731 | if (cap) { | 1771 | if (cap) { |
1732 | LIST_INSERT_HEAD (&cap->cb_head, cb, entries); | 1772 | LIST_INSERT_HEAD (&cap->cb_head, cb, entries); |
1733 | - return 0; | 1773 | + return cap; |
1734 | } | 1774 | } |
1735 | else { | 1775 | else { |
1736 | HWVoiceOut *hw; | 1776 | HWVoiceOut *hw; |
@@ -1780,7 +1820,7 @@ int AUD_add_capture ( | @@ -1780,7 +1820,7 @@ int AUD_add_capture ( | ||
1780 | while ((hw = audio_pcm_hw_find_any_out (s, hw))) { | 1820 | while ((hw = audio_pcm_hw_find_any_out (s, hw))) { |
1781 | audio_attach_capture (s, hw); | 1821 | audio_attach_capture (s, hw); |
1782 | } | 1822 | } |
1783 | - return 0; | 1823 | + return cap; |
1784 | 1824 | ||
1785 | err3: | 1825 | err3: |
1786 | qemu_free (cap->hw.mix_buf); | 1826 | qemu_free (cap->hw.mix_buf); |
@@ -1789,6 +1829,38 @@ int AUD_add_capture ( | @@ -1789,6 +1829,38 @@ int AUD_add_capture ( | ||
1789 | err1: | 1829 | err1: |
1790 | qemu_free (cb); | 1830 | qemu_free (cb); |
1791 | err0: | 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,13 +49,31 @@ typedef struct { | ||
49 | int endianness; | 49 | int endianness; |
50 | } audsettings_t; | 50 | } audsettings_t; |
51 | 51 | ||
52 | +typedef enum { | ||
53 | + AUD_CNOTIFY_ENABLE, | ||
54 | + AUD_CNOTIFY_DISABLE | ||
55 | +} audcnotification_e; | ||
56 | + | ||
52 | struct audio_capture_ops { | 57 | struct audio_capture_ops { |
53 | - void (*state) (void *opaque, int enabled); | 58 | + void (*notify) (void *opaque, audcnotification_e cmd); |
54 | void (*capture) (void *opaque, void *buf, int size); | 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 | typedef struct AudioState AudioState; | 74 | typedef struct AudioState AudioState; |
58 | typedef struct SWVoiceOut SWVoiceOut; | 75 | typedef struct SWVoiceOut SWVoiceOut; |
76 | +typedef struct CaptureVoiceOut CaptureVoiceOut; | ||
59 | typedef struct SWVoiceIn SWVoiceIn; | 77 | typedef struct SWVoiceIn SWVoiceIn; |
60 | 78 | ||
61 | typedef struct QEMUSoundCard { | 79 | typedef struct QEMUSoundCard { |
@@ -79,12 +97,13 @@ AudioState *AUD_init (void); | @@ -79,12 +97,13 @@ AudioState *AUD_init (void); | ||
79 | void AUD_help (void); | 97 | void AUD_help (void); |
80 | void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); | 98 | void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card); |
81 | void AUD_remove_card (QEMUSoundCard *card); | 99 | void AUD_remove_card (QEMUSoundCard *card); |
82 | -int AUD_add_capture ( | 100 | +CaptureVoiceOut *AUD_add_capture ( |
83 | AudioState *s, | 101 | AudioState *s, |
84 | audsettings_t *as, | 102 | audsettings_t *as, |
85 | struct audio_capture_ops *ops, | 103 | struct audio_capture_ops *ops, |
86 | void *opaque | 104 | void *opaque |
87 | ); | 105 | ); |
106 | +void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque); | ||
88 | 107 | ||
89 | SWVoiceOut *AUD_open_out ( | 108 | SWVoiceOut *AUD_open_out ( |
90 | QEMUSoundCard *card, | 109 | QEMUSoundCard *card, |
audio/audio_int.h
@@ -64,10 +64,11 @@ struct audio_pcm_info { | @@ -64,10 +64,11 @@ struct audio_pcm_info { | ||
64 | int swap_endianness; | 64 | int swap_endianness; |
65 | }; | 65 | }; |
66 | 66 | ||
67 | +typedef struct SWVoiceCap SWVoiceCap; | ||
68 | + | ||
67 | typedef struct HWVoiceOut { | 69 | typedef struct HWVoiceOut { |
68 | int enabled; | 70 | int enabled; |
69 | int pending_disable; | 71 | int pending_disable; |
70 | - int valid; | ||
71 | struct audio_pcm_info info; | 72 | struct audio_pcm_info info; |
72 | 73 | ||
73 | f_sample *clip; | 74 | f_sample *clip; |
@@ -79,7 +80,7 @@ typedef struct HWVoiceOut { | @@ -79,7 +80,7 @@ typedef struct HWVoiceOut { | ||
79 | 80 | ||
80 | int samples; | 81 | int samples; |
81 | LIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head; | 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 | struct audio_pcm_ops *pcm_ops; | 84 | struct audio_pcm_ops *pcm_ops; |
84 | LIST_ENTRY (HWVoiceOut) entries; | 85 | LIST_ENTRY (HWVoiceOut) entries; |
85 | } HWVoiceOut; | 86 | } HWVoiceOut; |
@@ -116,7 +117,6 @@ struct SWVoiceOut { | @@ -116,7 +117,6 @@ struct SWVoiceOut { | ||
116 | volume_t vol; | 117 | volume_t vol; |
117 | struct audio_callback callback; | 118 | struct audio_callback callback; |
118 | LIST_ENTRY (SWVoiceOut) entries; | 119 | LIST_ENTRY (SWVoiceOut) entries; |
119 | - LIST_ENTRY (SWVoiceOut) cap_entries; | ||
120 | }; | 120 | }; |
121 | 121 | ||
122 | struct SWVoiceIn { | 122 | struct SWVoiceIn { |
@@ -168,12 +168,18 @@ struct capture_callback { | @@ -168,12 +168,18 @@ struct capture_callback { | ||
168 | LIST_ENTRY (capture_callback) entries; | 168 | LIST_ENTRY (capture_callback) entries; |
169 | }; | 169 | }; |
170 | 170 | ||
171 | -typedef struct CaptureVoiceOut { | 171 | +struct CaptureVoiceOut { |
172 | HWVoiceOut hw; | 172 | HWVoiceOut hw; |
173 | void *buf; | 173 | void *buf; |
174 | LIST_HEAD (cb_listhead, capture_callback) cb_head; | 174 | LIST_HEAD (cb_listhead, capture_callback) cb_head; |
175 | LIST_ENTRY (CaptureVoiceOut) entries; | 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 | struct AudioState { | 184 | struct AudioState { |
179 | struct audio_driver *drv; | 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,7 +269,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) | ||
269 | hw->pcm_ops = drv->pcm_ops; | 269 | hw->pcm_ops = drv->pcm_ops; |
270 | LIST_INIT (&hw->sw_head); | 270 | LIST_INIT (&hw->sw_head); |
271 | #ifdef DAC | 271 | #ifdef DAC |
272 | - LIST_INIT (&hw->sw_cap_head); | 272 | + LIST_INIT (&hw->cap_head); |
273 | #endif | 273 | #endif |
274 | if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { | 274 | if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { |
275 | goto err0; | 275 | goto err0; |
@@ -426,7 +426,7 @@ SW *glue (AUD_open_, TYPE) ( | @@ -426,7 +426,7 @@ SW *glue (AUD_open_, TYPE) ( | ||
426 | 426 | ||
427 | s = card->audio; | 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 | audio_print_settings (as); | 430 | audio_print_settings (as); |
431 | goto fail; | 431 | goto fail; |
432 | } | 432 | } |
audio/noaudio.c
@@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw) | @@ -102,7 +102,7 @@ static int no_run_in (HWVoiceIn *hw) | ||
102 | NoVoiceIn *no = (NoVoiceIn *) hw; | 102 | NoVoiceIn *no = (NoVoiceIn *) hw; |
103 | int live = audio_pcm_hw_get_live_in (hw); | 103 | int live = audio_pcm_hw_get_live_in (hw); |
104 | int dead = hw->samples - live; | 104 | int dead = hw->samples - live; |
105 | - int samples; | 105 | + int samples = 0; |
106 | 106 | ||
107 | if (dead) { | 107 | if (dead) { |
108 | int64_t now = qemu_get_clock (vm_clock); | 108 | int64_t now = qemu_get_clock (vm_clock); |
audio/wavcapture.c
@@ -3,6 +3,11 @@ | @@ -3,6 +3,11 @@ | ||
3 | typedef struct { | 3 | typedef struct { |
4 | QEMUFile *f; | 4 | QEMUFile *f; |
5 | int bytes; | 5 | int bytes; |
6 | + char *path; | ||
7 | + int freq; | ||
8 | + int bits; | ||
9 | + int nchannels; | ||
10 | + CaptureVoiceOut *cap; | ||
6 | } WAVState; | 11 | } WAVState; |
7 | 12 | ||
8 | /* VICE code: Store number as little endian. */ | 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,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 | WAVState *wav = opaque; | 57 | WAVState *wav = opaque; |
49 | 58 | ||
@@ -51,7 +60,30 @@ static void wav_capture_cb (void *opaque, void *buf, int size) | @@ -51,7 +60,30 @@ static void wav_capture_cb (void *opaque, void *buf, int size) | ||
51 | wav->bytes += size; | 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 | WAVState *wav; | 88 | WAVState *wav; |
57 | uint8_t hdr[] = { | 89 | uint8_t hdr[] = { |
@@ -62,23 +94,35 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) | @@ -62,23 +94,35 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) | ||
62 | }; | 94 | }; |
63 | audsettings_t as; | 95 | audsettings_t as; |
64 | struct audio_capture_ops ops; | 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 | as.freq = freq; | 113 | as.freq = freq; |
71 | as.nchannels = 1 << stereo; | 114 | as.nchannels = 1 << stereo; |
72 | as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; | 115 | as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; |
73 | as.endianness = 0; | 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 | wav = qemu_mallocz (sizeof (*wav)); | 122 | wav = qemu_mallocz (sizeof (*wav)); |
79 | if (!wav) { | 123 | if (!wav) { |
80 | AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav)); | 124 | AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav)); |
81 | - return; | 125 | + return -1; |
82 | } | 126 | } |
83 | 127 | ||
84 | shift = bits16 + stereo; | 128 | shift = bits16 + stereo; |
@@ -91,12 +135,28 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) | @@ -91,12 +135,28 @@ void wav_capture (const char *path, int freq, int bits16, int stereo) | ||
91 | 135 | ||
92 | wav->f = fopen (path, "wb"); | 136 | wav->f = fopen (path, "wb"); |
93 | if (!wav->f) { | 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 | qemu_free (wav); | 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 | qemu_put_buffer (wav->f, hdr, sizeof (hdr)); | 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,6 +1077,64 @@ static void do_info_profile(void) | ||
1077 | } | 1077 | } |
1078 | #endif | 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 | static term_cmd_t term_cmds[] = { | 1138 | static term_cmd_t term_cmds[] = { |
1081 | { "help|?", "s?", do_help, | 1139 | { "help|?", "s?", do_help, |
1082 | "[cmd]", "show the help" }, | 1140 | "[cmd]", "show the help" }, |
@@ -1133,6 +1191,13 @@ static term_cmd_t term_cmds[] = { | @@ -1133,6 +1191,13 @@ static term_cmd_t term_cmds[] = { | ||
1133 | "dx dy [dz]", "send mouse move events" }, | 1191 | "dx dy [dz]", "send mouse move events" }, |
1134 | { "mouse_button", "i", do_mouse_button, | 1192 | { "mouse_button", "i", do_mouse_button, |
1135 | "state", "change mouse button state (1=L, 2=M, 4=R)" }, | 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 | { NULL, NULL, }, | 1201 | { NULL, NULL, }, |
1137 | }; | 1202 | }; |
1138 | 1203 | ||
@@ -1171,6 +1236,8 @@ static term_cmd_t info_cmds[] = { | @@ -1171,6 +1236,8 @@ static term_cmd_t info_cmds[] = { | ||
1171 | "", "show host USB devices", }, | 1236 | "", "show host USB devices", }, |
1172 | { "profile", "", do_info_profile, | 1237 | { "profile", "", do_info_profile, |
1173 | "", "show profiling information", }, | 1238 | "", "show profiling information", }, |
1239 | + { "capture", "", do_info_capture, | ||
1240 | + "show capture information" }, | ||
1174 | { NULL, NULL, }, | 1241 | { NULL, NULL, }, |
1175 | }; | 1242 | }; |
1176 | 1243 | ||
@@ -2081,6 +2148,9 @@ static void monitor_handle_command(const char *cmdline) | @@ -2081,6 +2148,9 @@ static void monitor_handle_command(const char *cmdline) | ||
2081 | case 6: | 2148 | case 6: |
2082 | cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]); | 2149 | cmd->handler(args[0], args[1], args[2], args[3], args[4], args[5]); |
2083 | break; | 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 | default: | 2154 | default: |
2085 | term_printf("unsupported number of arguments: %d\n", nb_args); | 2155 | term_printf("unsupported number of arguments: %d\n", nb_args); |
2086 | goto fail; | 2156 | goto fail; |