| 31 |
HWVoiceOut hw; |
HWVoiceOut hw; |
| 32 |
void *pcm_buf; |
void *pcm_buf; |
| 33 |
snd_pcm_t *handle; |
snd_pcm_t *handle; |
|
int can_pause; |
|
|
int was_enabled; |
|
| 34 |
} ALSAVoiceOut; |
} ALSAVoiceOut; |
| 35 |
|
|
| 36 |
typedef struct ALSAVoiceIn { |
typedef struct ALSAVoiceIn { |
| 37 |
HWVoiceIn hw; |
HWVoiceIn hw; |
| 38 |
snd_pcm_t *handle; |
snd_pcm_t *handle; |
| 39 |
void *pcm_buf; |
void *pcm_buf; |
|
int can_pause; |
|
| 40 |
} ALSAVoiceIn; |
} ALSAVoiceIn; |
| 41 |
|
|
| 42 |
static struct { |
static struct { |
| 55 |
|
|
| 56 |
int buffer_size_out_overriden; |
int buffer_size_out_overriden; |
| 57 |
int period_size_out_overriden; |
int period_size_out_overriden; |
| 58 |
|
int verbose; |
| 59 |
} conf = { |
} conf = { |
| 60 |
#ifdef HIGH_LATENCY |
#ifdef HIGH_LATENCY |
| 61 |
.size_in_usec_in = 1, |
.size_in_usec_in = 1, |
| 71 |
#else |
#else |
| 72 |
#define DEFAULT_BUFFER_SIZE 1024 |
#define DEFAULT_BUFFER_SIZE 1024 |
| 73 |
#define DEFAULT_PERIOD_SIZE 256 |
#define DEFAULT_PERIOD_SIZE 256 |
| 74 |
.buffer_size_in = DEFAULT_BUFFER_SIZE, |
.buffer_size_in = DEFAULT_BUFFER_SIZE * 4, |
| 75 |
.period_size_in = DEFAULT_PERIOD_SIZE, |
.period_size_in = DEFAULT_PERIOD_SIZE * 4, |
| 76 |
.buffer_size_out = DEFAULT_BUFFER_SIZE, |
.buffer_size_out = DEFAULT_BUFFER_SIZE, |
| 77 |
.period_size_out = DEFAULT_PERIOD_SIZE, |
.period_size_out = DEFAULT_PERIOD_SIZE, |
| 78 |
.buffer_size_in_overriden = 0, |
.buffer_size_in_overriden = 0, |
| 80 |
.period_size_in_overriden = 0, |
.period_size_in_overriden = 0, |
| 81 |
.period_size_out_overriden = 0, |
.period_size_out_overriden = 0, |
| 82 |
#endif |
#endif |
| 83 |
.threshold = 0 |
.threshold = 0, |
| 84 |
|
.verbose = 0 |
| 85 |
}; |
}; |
| 86 |
|
|
| 87 |
struct alsa_params_req { |
struct alsa_params_req { |
| 96 |
int freq; |
int freq; |
| 97 |
audfmt_e fmt; |
audfmt_e fmt; |
| 98 |
int nchannels; |
int nchannels; |
|
int can_pause; |
|
| 99 |
snd_pcm_uframes_t samples; |
snd_pcm_uframes_t samples; |
| 100 |
}; |
}; |
| 101 |
|
|
| 472 |
goto err; |
goto err; |
| 473 |
} |
} |
| 474 |
|
|
|
obt->can_pause = snd_pcm_hw_params_can_pause (hw_params); |
|
|
if (obt->can_pause < 0) { |
|
|
alsa_logerr (err, "Could not get pause capability for %s\n", typ); |
|
|
obt->can_pause = 0; |
|
|
} |
|
|
|
|
| 475 |
if (!in && conf.threshold) { |
if (!in && conf.threshold) { |
| 476 |
snd_pcm_uframes_t threshold; |
snd_pcm_uframes_t threshold; |
| 477 |
int bytes_per_sec; |
int bytes_per_sec; |
| 519 |
return 0; |
return 0; |
| 520 |
} |
} |
| 521 |
|
|
| 522 |
|
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle) |
| 523 |
|
{ |
| 524 |
|
snd_pcm_sframes_t avail; |
| 525 |
|
|
| 526 |
|
avail = snd_pcm_avail_update (handle); |
| 527 |
|
if (avail < 0) { |
| 528 |
|
if (avail == -EPIPE) { |
| 529 |
|
if (!alsa_recover (handle)) { |
| 530 |
|
avail = snd_pcm_avail_update (handle); |
| 531 |
|
} |
| 532 |
|
} |
| 533 |
|
|
| 534 |
|
if (avail < 0) { |
| 535 |
|
alsa_logerr (avail, |
| 536 |
|
"Could not obtain number of available frames\n"); |
| 537 |
|
return -1; |
| 538 |
|
} |
| 539 |
|
} |
| 540 |
|
|
| 541 |
|
return avail; |
| 542 |
|
} |
| 543 |
|
|
| 544 |
static int alsa_run_out (HWVoiceOut *hw) |
static int alsa_run_out (HWVoiceOut *hw) |
| 545 |
{ |
{ |
| 546 |
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
| 555 |
return 0; |
return 0; |
| 556 |
} |
} |
| 557 |
|
|
| 558 |
avail = snd_pcm_avail_update (alsa->handle); |
avail = alsa_get_avail (alsa->handle); |
| 559 |
if (avail < 0) { |
if (avail < 0) { |
| 560 |
if (avail == -EPIPE) { |
dolog ("Could not get number of available playback frames\n"); |
|
if (!alsa_recover (alsa->handle)) { |
|
|
avail = snd_pcm_avail_update (alsa->handle); |
|
|
if (avail >= 0) { |
|
|
goto ok; |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
alsa_logerr (avail, "Could not get amount free space\n"); |
|
| 561 |
return 0; |
return 0; |
| 562 |
} |
} |
| 563 |
|
|
|
ok: |
|
| 564 |
decr = audio_MIN (live, avail); |
decr = audio_MIN (live, avail); |
| 565 |
samples = decr; |
samples = decr; |
| 566 |
rpos = hw->rpos; |
rpos = hw->rpos; |
| 567 |
while (samples) { |
while (samples) { |
| 568 |
int left_till_end_samples = hw->samples - rpos; |
int left_till_end_samples = hw->samples - rpos; |
| 569 |
int convert_samples = audio_MIN (samples, left_till_end_samples); |
int len = audio_MIN (samples, left_till_end_samples); |
| 570 |
snd_pcm_sframes_t written; |
snd_pcm_sframes_t written; |
| 571 |
|
|
| 572 |
src = hw->mix_buf + rpos; |
src = hw->mix_buf + rpos; |
| 573 |
dst = advance (alsa->pcm_buf, rpos << hw->info.shift); |
dst = advance (alsa->pcm_buf, rpos << hw->info.shift); |
| 574 |
|
|
| 575 |
hw->clip (dst, src, convert_samples); |
hw->clip (dst, src, len); |
| 576 |
|
|
| 577 |
while (convert_samples) { |
while (len) { |
| 578 |
written = snd_pcm_writei (alsa->handle, dst, convert_samples); |
written = snd_pcm_writei (alsa->handle, dst, len); |
| 579 |
|
|
| 580 |
if (written < 0) { |
if (written <= 0) { |
| 581 |
switch (written) { |
switch (written) { |
| 582 |
case -EPIPE: |
case 0: |
| 583 |
if (!alsa_recover (alsa->handle)) { |
if (conf.verbose) { |
| 584 |
continue; |
dolog ("Failed to write %d frames (wrote zero)\n", len); |
| 585 |
} |
} |
|
dolog ("Failed to write %d frames to %p, " |
|
|
"handle %p not prepared\n", |
|
|
convert_samples, |
|
|
dst, |
|
|
alsa->handle); |
|
| 586 |
goto exit; |
goto exit; |
| 587 |
|
|
| 588 |
case -EAGAIN: |
case -EPIPE: |
| 589 |
|
if (alsa_recover (alsa->handle)) { |
| 590 |
|
alsa_logerr (written, "Failed to write %d frames\n", |
| 591 |
|
len); |
| 592 |
|
goto exit; |
| 593 |
|
} |
| 594 |
|
if (conf.verbose) { |
| 595 |
|
dolog ("Recovering from playback xrun\n"); |
| 596 |
|
} |
| 597 |
continue; |
continue; |
| 598 |
|
|
| 599 |
|
case -EAGAIN: |
| 600 |
|
goto exit; |
| 601 |
|
|
| 602 |
default: |
default: |
| 603 |
alsa_logerr (written, "Failed to write %d frames to %p\n", |
alsa_logerr (written, "Failed to write %d frames to %p\n", |
| 604 |
convert_samples, dst); |
len, dst); |
| 605 |
goto exit; |
goto exit; |
| 606 |
} |
} |
| 607 |
} |
} |
| 609 |
mixeng_clear (src, written); |
mixeng_clear (src, written); |
| 610 |
rpos = (rpos + written) % hw->samples; |
rpos = (rpos + written) % hw->samples; |
| 611 |
samples -= written; |
samples -= written; |
| 612 |
convert_samples -= written; |
len -= written; |
| 613 |
dst = advance (dst, written << hw->info.shift); |
dst = advance (dst, written << hw->info.shift); |
| 614 |
src += written; |
src += written; |
| 615 |
} |
} |
| 669 |
&obt_as, |
&obt_as, |
| 670 |
audio_need_to_swap_endian (endianness) |
audio_need_to_swap_endian (endianness) |
| 671 |
); |
); |
|
alsa->can_pause = obt.can_pause; |
|
| 672 |
hw->samples = obt.samples; |
hw->samples = obt.samples; |
| 673 |
|
|
| 674 |
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); |
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); |
| 680 |
} |
} |
| 681 |
|
|
| 682 |
alsa->handle = handle; |
alsa->handle = handle; |
|
alsa->was_enabled = 0; |
|
| 683 |
return 0; |
return 0; |
| 684 |
} |
} |
| 685 |
|
|
| 686 |
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) |
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause) |
| 687 |
{ |
{ |
| 688 |
int err; |
int err; |
| 689 |
|
|
| 690 |
|
if (pause) { |
| 691 |
|
err = snd_pcm_drop (handle); |
| 692 |
|
if (err < 0) { |
| 693 |
|
alsa_logerr (err, "Could not stop %s", typ); |
| 694 |
|
return -1; |
| 695 |
|
} |
| 696 |
|
} |
| 697 |
|
else { |
| 698 |
|
err = snd_pcm_prepare (handle); |
| 699 |
|
if (err < 0) { |
| 700 |
|
alsa_logerr (err, "Could not prepare handle for %s", typ); |
| 701 |
|
return -1; |
| 702 |
|
} |
| 703 |
|
} |
| 704 |
|
|
| 705 |
|
return 0; |
| 706 |
|
} |
| 707 |
|
|
| 708 |
|
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) |
| 709 |
|
{ |
| 710 |
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; |
| 711 |
|
|
| 712 |
switch (cmd) { |
switch (cmd) { |
| 713 |
case VOICE_ENABLE: |
case VOICE_ENABLE: |
| 714 |
ldebug ("enabling voice\n"); |
ldebug ("enabling voice\n"); |
| 715 |
audio_pcm_info_clear_buf (&hw->info, alsa->pcm_buf, hw->samples); |
return alsa_voice_ctl (alsa->handle, "playback", 0); |
|
if (alsa->can_pause) { |
|
|
/* Why this was_enabled madness is needed at all?? */ |
|
|
if (alsa->was_enabled) { |
|
|
err = snd_pcm_pause (alsa->handle, 0); |
|
|
if (err < 0) { |
|
|
alsa_logerr (err, "Failed to resume playing\n"); |
|
|
/* not fatal really */ |
|
|
} |
|
|
} |
|
|
else { |
|
|
alsa->was_enabled = 1; |
|
|
} |
|
|
} |
|
|
break; |
|
| 716 |
|
|
| 717 |
case VOICE_DISABLE: |
case VOICE_DISABLE: |
| 718 |
ldebug ("disabling voice\n"); |
ldebug ("disabling voice\n"); |
| 719 |
if (alsa->can_pause) { |
return alsa_voice_ctl (alsa->handle, "playback", 1); |
|
err = snd_pcm_pause (alsa->handle, 1); |
|
|
if (err < 0) { |
|
|
alsa_logerr (err, "Failed to stop playing\n"); |
|
|
/* not fatal really */ |
|
|
} |
|
|
} |
|
|
break; |
|
| 720 |
} |
} |
| 721 |
return 0; |
|
| 722 |
|
return -1; |
| 723 |
} |
} |
| 724 |
|
|
| 725 |
static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) |
static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) |
| 758 |
&obt_as, |
&obt_as, |
| 759 |
audio_need_to_swap_endian (endianness) |
audio_need_to_swap_endian (endianness) |
| 760 |
); |
); |
|
alsa->can_pause = obt.can_pause; |
|
| 761 |
hw->samples = obt.samples; |
hw->samples = obt.samples; |
| 762 |
|
|
| 763 |
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); |
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); |
| 791 |
int i; |
int i; |
| 792 |
int live = audio_pcm_hw_get_live_in (hw); |
int live = audio_pcm_hw_get_live_in (hw); |
| 793 |
int dead = hw->samples - live; |
int dead = hw->samples - live; |
| 794 |
|
int decr; |
| 795 |
struct { |
struct { |
| 796 |
int add; |
int add; |
| 797 |
int len; |
int len; |
| 799 |
{ hw->wpos, 0 }, |
{ hw->wpos, 0 }, |
| 800 |
{ 0, 0 } |
{ 0, 0 } |
| 801 |
}; |
}; |
| 802 |
|
snd_pcm_sframes_t avail; |
| 803 |
snd_pcm_uframes_t read_samples = 0; |
snd_pcm_uframes_t read_samples = 0; |
| 804 |
|
|
| 805 |
if (!dead) { |
if (!dead) { |
| 806 |
return 0; |
return 0; |
| 807 |
} |
} |
| 808 |
|
|
| 809 |
if (hw->wpos + dead > hw->samples) { |
avail = alsa_get_avail (alsa->handle); |
| 810 |
|
if (avail < 0) { |
| 811 |
|
dolog ("Could not get number of captured frames\n"); |
| 812 |
|
return 0; |
| 813 |
|
} |
| 814 |
|
|
| 815 |
|
if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) { |
| 816 |
|
avail = hw->samples; |
| 817 |
|
} |
| 818 |
|
|
| 819 |
|
decr = audio_MIN (dead, avail); |
| 820 |
|
if (!decr) { |
| 821 |
|
return 0; |
| 822 |
|
} |
| 823 |
|
|
| 824 |
|
if (hw->wpos + decr > hw->samples) { |
| 825 |
bufs[0].len = (hw->samples - hw->wpos); |
bufs[0].len = (hw->samples - hw->wpos); |
| 826 |
bufs[1].len = (dead - (hw->samples - hw->wpos)); |
bufs[1].len = (decr - (hw->samples - hw->wpos)); |
| 827 |
} |
} |
| 828 |
else { |
else { |
| 829 |
bufs[0].len = dead; |
bufs[0].len = decr; |
| 830 |
} |
} |
| 831 |
|
|
|
|
|
| 832 |
for (i = 0; i < 2; ++i) { |
for (i = 0; i < 2; ++i) { |
| 833 |
void *src; |
void *src; |
| 834 |
st_sample_t *dst; |
st_sample_t *dst; |
| 843 |
while (len) { |
while (len) { |
| 844 |
nread = snd_pcm_readi (alsa->handle, src, len); |
nread = snd_pcm_readi (alsa->handle, src, len); |
| 845 |
|
|
| 846 |
if (nread < 0) { |
if (nread <= 0) { |
| 847 |
switch (nread) { |
switch (nread) { |
| 848 |
case -EPIPE: |
case 0: |
| 849 |
if (!alsa_recover (alsa->handle)) { |
if (conf.verbose) { |
| 850 |
continue; |
dolog ("Failed to read %ld frames (read zero)\n", len); |
| 851 |
} |
} |
|
dolog ( |
|
|
"Failed to read %ld frames from %p, " |
|
|
"handle %p not prepared\n", |
|
|
len, |
|
|
src, |
|
|
alsa->handle |
|
|
); |
|
| 852 |
goto exit; |
goto exit; |
| 853 |
|
|
| 854 |
case -EAGAIN: |
case -EPIPE: |
| 855 |
|
if (alsa_recover (alsa->handle)) { |
| 856 |
|
alsa_logerr (nread, "Failed to read %ld frames\n", len); |
| 857 |
|
goto exit; |
| 858 |
|
} |
| 859 |
|
if (conf.verbose) { |
| 860 |
|
dolog ("Recovering from capture xrun\n"); |
| 861 |
|
} |
| 862 |
continue; |
continue; |
| 863 |
|
|
| 864 |
|
case -EAGAIN: |
| 865 |
|
goto exit; |
| 866 |
|
|
| 867 |
default: |
default: |
| 868 |
alsa_logerr ( |
alsa_logerr ( |
| 869 |
nread, |
nread, |
| 897 |
|
|
| 898 |
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) |
| 899 |
{ |
{ |
| 900 |
(void) hw; |
ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; |
| 901 |
(void) cmd; |
|
| 902 |
return 0; |
switch (cmd) { |
| 903 |
|
case VOICE_ENABLE: |
| 904 |
|
ldebug ("enabling voice\n"); |
| 905 |
|
return alsa_voice_ctl (alsa->handle, "capture", 0); |
| 906 |
|
|
| 907 |
|
case VOICE_DISABLE: |
| 908 |
|
ldebug ("disabling voice\n"); |
| 909 |
|
return alsa_voice_ctl (alsa->handle, "capture", 1); |
| 910 |
|
} |
| 911 |
|
|
| 912 |
|
return -1; |
| 913 |
} |
} |
| 914 |
|
|
| 915 |
static void *alsa_audio_init (void) |
static void *alsa_audio_init (void) |
| 945 |
|
|
| 946 |
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, |
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, |
| 947 |
"ADC device name", NULL, 0}, |
"ADC device name", NULL, 0}, |
| 948 |
|
|
| 949 |
|
{"VERBOSE", AUD_OPT_BOOL, &conf.verbose, |
| 950 |
|
"Behave in a more verbose way", NULL, 0}, |
| 951 |
|
|
| 952 |
{NULL, 0, NULL, NULL, NULL, 0} |
{NULL, 0, NULL, NULL, NULL, 0} |
| 953 |
}; |
}; |
| 954 |
|
|