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