git ssb

0+

cel / foostudio



Commit e03a630124fa47eb03d508fcfd576f8ebe4f28f1

Playback MVP

Charles Lehner committed on 10/15/2016, 12:25:57 AM
Parent: fdc9644582b3e2b875598b772c5bdda6c74ef9e5

Files changed

Makefilechanged
studio.cchanged
tune.cchanged
tune.hchanged
README.mdadded
MakefileView
@@ -1,14 +1,16 @@
1-CFLAGS = -std=c99 -fPIC
2-LDLIBS = -ldl -lm
1 +CFLAGS = -std=c99
2 +LIB_CFLAGS = -fPIC
3 +LDLIBS = -ldl -lasound -lm
4 +LIB_LDLIBS = -lm
35
46 all: studio tune.so
57
68 studio: studio.c tune.h
79 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)
810
911 tune.so : tune.c tune.h
10- $(CC) $(CFLAGS) -shared $(LDFLAGS) -o $@ $< $(LDLIBS)
12 + $(CC) $(LIB_CFLAGS) -shared $(LDFLAGS) -o $@ $< $(LIB_LDLIBS)
1113
1214 clean:
1315 $(RM) *.o *.so studio
1416
studio.cView
@@ -1,7 +1,9 @@
11 #define _DEFAULT_SOURCE
2 +#include <alsa/asoundlib.h>
23 #include <dlfcn.h>
34 #include <err.h>
5 +#include <math.h>
46 #include <stdbool.h>
57 #include <stdio.h>
68 #include <sys/stat.h>
79 #include <sys/types.h>
@@ -13,8 +15,14 @@
1315 ino_t inode;
1416 struct tune *tune;
1517 };
1618
19 +static unsigned int rate = 44100;
20 +static unsigned int channels = 1;
21 +static unsigned int latency = 500000; /* ring buffer length in us */
22 +static snd_pcm_format_t format = SND_PCM_FORMAT_FLOAT;
23 +static const char *dl_fname = "./tune.so";
24 +
1725 static int tune_dl_load(struct tune_dl *dl, const char *libfname)
1826 {
1927 struct stat st;
2028 if (stat(libfname, &st) < 0) {
@@ -47,29 +55,231 @@
4755 }
4856 return 0;
4957 }
5058
59 +/*
60 + * Underrun and suspend recovery, from alsa-lib/test/pcm.c
61 + */
62 +
63 +static int xrun_recovery(snd_pcm_t *handle, int err)
64 +{
65 + if (err == -EPIPE) { /* under-run */
66 + err = snd_pcm_prepare(handle);
67 + if (err < 0)
68 + warnx("Can't recovery from underrun, prepare failed: %s", snd_strerror(err));
69 + return 0;
70 + } else if (err == -ESTRPIPE) {
71 + while ((err = snd_pcm_resume(handle)) == -EAGAIN)
72 + sleep(1); /* wait until the suspend flag is released */
73 + if (err < 0) {
74 + err = snd_pcm_prepare(handle);
75 + if (err < 0)
76 + warnx("Can't recovery from suspend, prepare failed: %s", snd_strerror(err));
77 + }
78 + return 0;
79 + }
80 + return err;
81 +}
82 +
83 +static void generate(struct tune_dl *dl,
84 + const snd_pcm_channel_area_t *areas,
85 + snd_pcm_uframes_t offset,
86 + int count, double *_phase)
87 +{
88 + double phase = *_phase;
89 + double step = 2. * M_PI/(double)rate;
90 + unsigned char *samples[channels];
91 + int steps[channels];
92 + unsigned int chn;
93 + int format_bits = snd_pcm_format_width(format);
94 + unsigned int maxval = (1 << (format_bits - 1)) - 1;
95 + int bps = format_bits / 8; /* bytes per sample */
96 + int phys_bps = snd_pcm_format_physical_width(format) / 8;
97 + int big_endian = snd_pcm_format_big_endian(format) == 1;
98 + int to_unsigned = snd_pcm_format_unsigned(format) == 1;
99 + int is_float = (
100 + format == SND_PCM_FORMAT_FLOAT_LE ||
101 + format == SND_PCM_FORMAT_FLOAT_BE ||
102 + format == SND_PCM_FORMAT_FLOAT64_LE ||
103 + format == SND_PCM_FORMAT_FLOAT64_BE);
104 +
105 + /* verify and prepare the contents of areas */
106 + for (chn = 0; chn < channels; chn++) {
107 + if ((areas[chn].first % 8) != 0) {
108 + errx(1, "areas[%i].first == %i, aborting...", chn, areas[chn].first);
109 + }
110 + samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
111 + if ((areas[chn].step % 16) != 0) {
112 + errx(1, "areas[%i].step == %i, aborting...", chn, areas[chn].step);
113 + }
114 + steps[chn] = areas[chn].step / 8;
115 + samples[chn] += offset * steps[chn];
116 + }
117 +
118 + tune_dl_load(dl, dl_fname);
119 +
120 + /* fill the channel areas */
121 + while (count-- > 0) {
122 + union {
123 + float f;
124 + int i;
125 + } fval;
126 + int res, i;
127 + if (is_float) {
128 + if (dl->handle && dl->tune->play) {
129 + fval.f = dl->tune->play(dl->tune, phase);
130 + } else {
131 + // val.f = sin(phase);
132 + fval.f = 0;
133 + }
134 + res = fval.i;
135 + } else
136 + res = sin(phase) * maxval;
137 + if (to_unsigned)
138 + res ^= 1U << (format_bits - 1);
139 + for (chn = 0; chn < channels; chn++) {
140 + /* Generate data in native endian format */
141 + if (big_endian) {
142 + for (i = 0; i < bps; i++)
143 + *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
144 + } else {
145 + for (i = 0; i < bps; i++)
146 + *(samples[chn] + i) = (res >> i * 8) & 0xff;
147 + }
148 + samples[chn] += steps[chn];
149 + }
150 + phase += step;
151 + /*
152 + if (phase >= max_phase)
153 + phase -= max_phase;
154 + */
155 + }
156 + *_phase = phase;
157 +}
158 +
159 +
51160 int main(int argc, char *argv[])
52161 {
53162 struct tune_dl dl = {0};
54- const char *fname = "./tune.so";
55- if (tune_dl_load(&dl, fname) < 0) {
163 + int err;
164 + snd_pcm_t *pcm_out;
165 + const char *device_out = "default";
166 +
167 + if (argc > 2) {
168 + device_out = argv[2];
169 + }
170 +
171 + if (tune_dl_load(&dl, dl_fname) < 0) {
56172 return 1;
57173 }
174 +
175 + if ((err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
176 + warnx("Playback open error: %s", snd_strerror(err));
177 + return 1;
178 + }
179 +
180 + if ((err = snd_pcm_set_params(pcm_out,
181 + format,
182 + SND_PCM_ACCESS_MMAP_INTERLEAVED,
183 + channels,
184 + rate,
185 + 1,
186 + latency)) < 0) {
187 + warnx("Playback open error: %s", snd_strerror(err));
188 + return 1;
189 + }
190 +
58191 if (dl.tune->init) {
59192 dl.tune->init(dl.tune);
60193 }
61- int t = 0;
194 +
195 + snd_pcm_uframes_t buffer_size;
196 + snd_pcm_uframes_t period_size;
197 + if ((err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size)) < 0) {
198 + warnx("Playback get params error: %s", snd_strerror(err));
199 + return 1;
200 + }
201 +
202 + double phase = 0;
203 + const snd_pcm_channel_area_t *my_areas;
204 + snd_pcm_uframes_t offset, frames, size;
205 + snd_pcm_sframes_t avail, commitres;
206 + snd_pcm_state_t state;
207 + int first = 1;
208 +
62209 while (1) {
63- (void) tune_dl_load(&dl, fname);
64- if (dl.handle && dl.tune->play) {
65- double sample = dl.tune->play(dl.tune, t);
66- printf("%d %f\n", t, sample);
210 + state = snd_pcm_state(pcm_out);
211 + if (state == SND_PCM_STATE_XRUN) {
212 + err = xrun_recovery(pcm_out, -EPIPE);
213 + if (err < 0) {
214 + printf("XRUN recovery failed: %s\n", snd_strerror(err));
215 + return err;
216 + }
217 + first = 1;
218 + } else if (state == SND_PCM_STATE_SUSPENDED) {
219 + err = xrun_recovery(pcm_out, -ESTRPIPE);
220 + if (err < 0) {
221 + printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
222 + return err;
223 + }
67224 }
68- (void) tune_dl_load(&dl, fname);
69- usleep(100000);
70- t++;
225 + avail = snd_pcm_avail_update(pcm_out);
226 + if (avail < 0) {
227 + err = xrun_recovery(pcm_out, avail);
228 + if (err < 0) {
229 + printf("avail update failed: %s\n", snd_strerror(err));
230 + return err;
231 + }
232 + first = 1;
233 + continue;
234 + }
235 + if (avail < period_size) {
236 + if (first) {
237 + first = 0;
238 + err = snd_pcm_start(pcm_out);
239 + if (err < 0) {
240 + printf("Start error: %s\n", snd_strerror(err));
241 + exit(EXIT_FAILURE);
242 + }
243 + } else {
244 + err = snd_pcm_wait(pcm_out, -1);
245 + if (err < 0) {
246 + if ((err = xrun_recovery(pcm_out, err)) < 0) {
247 + printf("snd_pcm_wait error: %s\n", snd_strerror(err));
248 + exit(EXIT_FAILURE);
249 + }
250 + first = 1;
251 + }
252 + }
253 + continue;
254 + }
255 + size = period_size;
256 + while (size > 0) {
257 + frames = size;
258 + err = snd_pcm_mmap_begin(pcm_out, &my_areas, &offset, &frames);
259 + if (err < 0) {
260 + if ((err = xrun_recovery(pcm_out, err)) < 0) {
261 + printf("MMAP begin avail error: %s\n", snd_strerror(err));
262 + exit(EXIT_FAILURE);
263 + }
264 + first = 1;
265 + }
266 + generate(&dl, my_areas, offset, frames, &phase);
267 + // generate_sine(my_areas, offset, frames, &phase);
268 + commitres = snd_pcm_mmap_commit(pcm_out, offset, frames);
269 + if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
270 + if ((err = xrun_recovery(pcm_out, commitres >= 0 ? -EPIPE : commitres)) < 0) {
271 + printf("MMAP commit error: %s\n", snd_strerror(err));
272 + exit(EXIT_FAILURE);
273 + }
274 + first = 1;
275 + }
276 + size -= frames;
277 + }
278 +
71279 }
72280 if (dl.tune->deinit) {
73281 dl.tune->deinit(dl.tune);
74282 }
283 +
284 + snd_pcm_close(pcm_out);
75285 }
tune.cView
@@ -8,11 +8,11 @@
88 static void tune_reload(struct tune *tune)
99 {
1010 }
1111
12-static double tune_play(struct tune *tune, int time)
12 +static float tune_play(struct tune *tune, double time)
1313 {
14- return sin(time);
14 + return 0.99 * sin(time*440);
1515 }
1616
1717 static void tune_deinit(struct tune *tune)
1818 {
tune.hView
@@ -3,8 +3,8 @@
33 struct tune {
44 void (*init)(struct tune *);
55 void (*deinit)(struct tune *);
66 void (*reload)(struct tune *);
7- double (*play)(struct tune *, int time);
7 + float (*play)(struct tune *, double time);
88 };
99
1010 extern const struct tune TUNE;
README.mdView
@@ -1,0 +1,3 @@
1 +http://nullprogram.com/blog/2014/12/23/
2 +http://www.alsa-project.org/alsa-doc/alsa-lib/pcm.html
3 +/home/cel/src/alsa-lib/test/pcm_min.c

Built with git-ssb-web