1 /**
2 * @file alsa_src.c ALSA sound driver - recorder
3 *
4 * Copyright (C) 2010 Creytiv.com
5 */
6 #define _DEFAULT_SOURCE 1
7 #define _POSIX_SOURCE 1
8 #include <sys/types.h>
9 #include <sys/time.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <alsa/asoundlib.h>
13 #include <pthread.h>
14 #include <re.h>
15 #include <rem.h>
16 #include <baresip.h>
17 #include "alsa.h"
18
19
20 struct ausrc_st {
21 const struct ausrc *as; /* pointer to base-class (inheritance) */
22 pthread_t thread;
23 bool run;
24 snd_pcm_t *read;
25 void *sampv;
26 size_t sampc;
27 ausrc_read_h *rh;
28 void *arg;
29 struct ausrc_prm prm;
30 char *device;
31 };
32
33
ausrc_destructor(void * arg)34 static void ausrc_destructor(void *arg)
35 {
36 struct ausrc_st *st = arg;
37
38 /* Wait for termination of other thread */
39 if (st->run) {
40 debug("alsa: stopping recording thread (%s)\n", st->device);
41 st->run = false;
42 (void)pthread_join(st->thread, NULL);
43 }
44
45 if (st->read)
46 snd_pcm_close(st->read);
47
48 mem_deref(st->sampv);
49 mem_deref(st->device);
50 }
51
52
read_thread(void * arg)53 static void *read_thread(void *arg)
54 {
55 struct ausrc_st *st = arg;
56 int num_frames;
57 int err;
58
59 num_frames = st->prm.srate * st->prm.ptime / 1000;
60
61 /* Start */
62 err = snd_pcm_start(st->read);
63 if (err) {
64 warning("alsa: could not start ausrc device '%s' (%s)\n",
65 st->device, snd_strerror(err));
66 goto out;
67 }
68
69 while (st->run) {
70 size_t sampc;
71 void *sampv;
72
73 sampv = st->sampv;
74
75 err = snd_pcm_readi(st->read, sampv, num_frames);
76 if (err == -EPIPE) {
77 snd_pcm_prepare(st->read);
78 continue;
79 }
80 else if (err <= 0) {
81 continue;
82 }
83
84 sampc = err * st->prm.ch;
85
86 st->rh(st->sampv, sampc, st->arg);
87 }
88
89 out:
90 return NULL;
91 }
92
93
alsa_src_alloc(struct ausrc_st ** stp,const struct ausrc * as,struct media_ctx ** ctx,struct ausrc_prm * prm,const char * device,ausrc_read_h * rh,ausrc_error_h * errh,void * arg)94 int alsa_src_alloc(struct ausrc_st **stp, const struct ausrc *as,
95 struct media_ctx **ctx,
96 struct ausrc_prm *prm, const char *device,
97 ausrc_read_h *rh, ausrc_error_h *errh, void *arg)
98 {
99 struct ausrc_st *st;
100 snd_pcm_format_t pcmfmt;
101 int num_frames;
102 int err;
103 (void)ctx;
104 (void)errh;
105
106 if (!stp || !as || !prm || !rh)
107 return EINVAL;
108
109 if (!str_isset(device))
110 device = alsa_dev;
111
112 st = mem_zalloc(sizeof(*st), ausrc_destructor);
113 if (!st)
114 return ENOMEM;
115
116 err = str_dup(&st->device, device);
117 if (err)
118 goto out;
119
120 st->prm = *prm;
121 st->as = as;
122 st->rh = rh;
123 st->arg = arg;
124
125 st->sampc = prm->srate * prm->ch * prm->ptime / 1000;
126 num_frames = st->prm.srate * st->prm.ptime / 1000;
127
128 st->sampv = mem_alloc(aufmt_sample_size(prm->fmt) * st->sampc, NULL);
129 if (!st->sampv) {
130 err = ENOMEM;
131 goto out;
132 }
133
134 err = snd_pcm_open(&st->read, st->device, SND_PCM_STREAM_CAPTURE, 0);
135 if (err < 0) {
136 warning("alsa: could not open ausrc device '%s' (%s)\n",
137 st->device, snd_strerror(err));
138 goto out;
139 }
140
141 pcmfmt = aufmt_to_alsaformat(prm->fmt);
142 if (pcmfmt == SND_PCM_FORMAT_UNKNOWN) {
143 warning("alsa: unknown sample format '%s'\n",
144 aufmt_name(prm->fmt));
145 err = EINVAL;
146 goto out;
147 }
148
149 err = alsa_reset(st->read, st->prm.srate, st->prm.ch, num_frames,
150 pcmfmt);
151 if (err) {
152 warning("alsa: could not reset source '%s' (%s)\n",
153 st->device, snd_strerror(err));
154 goto out;
155 }
156
157 st->run = true;
158 err = pthread_create(&st->thread, NULL, read_thread, st);
159 if (err) {
160 st->run = false;
161 goto out;
162 }
163
164 debug("alsa: recording started (%s) format=%s\n",
165 st->device, aufmt_name(prm->fmt));
166
167 out:
168 if (err)
169 mem_deref(st);
170 else
171 *stp = st;
172
173 return err;
174 }
175