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