git ssb

0+

cel / foostudio



Tree: 4be6db6a6163e3a0ccd85e7e193f54406c80a29a

Files: 4be6db6a6163e3a0ccd85e7e193f54406c80a29a / studio.c

6862 bytesRaw
1#define _DEFAULT_SOURCE
2#include <alsa/asoundlib.h>
3#include <dlfcn.h>
4#include <err.h>
5#include <math.h>
6#include <stdbool.h>
7#include <stdio.h>
8#include <sys/stat.h>
9#include <sys/types.h>
10#include <unistd.h>
11#include "tune.h"
12
13struct tune_dl {
14 void *handle;
15 ino_t inode;
16 struct tune *tune;
17};
18
19static unsigned int rate = 44100;
20static unsigned int channels = 1;
21static unsigned int latency = 500000; /* ring buffer length in us */
22static snd_pcm_format_t format = SND_PCM_FORMAT_FLOAT;
23static const char *dl_fname = "./tune.so";
24
25static int tune_dl_load(struct tune_dl *dl, const char *libfname)
26{
27 struct stat st;
28 if (stat(libfname, &st) < 0) {
29 warn("stat");
30 return -1;
31 }
32 if (dl->inode == st.st_ino) {
33 // file unchanged
34 return 0;
35 }
36 // reload the library
37 if (dl->handle) {
38 dlclose(dl->handle);
39 dl->inode = 0;
40 }
41 dl->handle = dlopen(libfname, RTLD_NOW);
42 if (dl->handle == NULL) {
43 // warnx("dlopen: %s", dlerror());
44 return 0;
45 }
46 dl->tune = dlsym(dl->handle, "TUNE");
47 if (dl->tune == NULL) {
48 warn("dlsym");
49 dlclose(dl->handle);
50 return 0;
51 }
52 dl->inode = st.st_ino;
53 if (dl->tune->reload) {
54 dl->tune->reload(dl->tune);
55 }
56 return 0;
57}
58
59/*
60 * Underrun and suspend recovery, from alsa-lib/test/pcm.c
61 */
62
63static 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
83static 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
100 /* verify and prepare the contents of areas */
101 for (chn = 0; chn < channels; chn++) {
102 if ((areas[chn].first % 8) != 0) {
103 errx(1, "areas[%i].first == %i, aborting...", chn, areas[chn].first);
104 }
105 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
106 if ((areas[chn].step % 16) != 0) {
107 errx(1, "areas[%i].step == %i, aborting...", chn, areas[chn].step);
108 }
109 steps[chn] = areas[chn].step / 8;
110 samples[chn] += offset * steps[chn];
111 }
112
113 tune_dl_load(dl, dl_fname);
114
115 /* fill the channel areas */
116 while (count-- > 0) {
117 union {
118 float f;
119 int i;
120 } fval;
121 int res, i;
122 if (dl->handle && dl->tune->play) {
123 fval.f = dl->tune->play(dl->tune, phase);
124 } else {
125 fval.f = 0;
126 }
127 res = fval.i;
128 if (to_unsigned)
129 res ^= 1U << (format_bits - 1);
130 for (chn = 0; chn < channels; chn++) {
131 /* Generate data in native endian format */
132 if (big_endian) {
133 for (i = 0; i < bps; i++)
134 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
135 } else {
136 for (i = 0; i < bps; i++)
137 *(samples[chn] + i) = (res >> i * 8) & 0xff;
138 }
139 samples[chn] += steps[chn];
140 }
141 phase += step;
142 /*
143 if (phase >= max_phase)
144 phase -= max_phase;
145 */
146 }
147 *_phase = phase;
148}
149
150
151int main(int argc, char *argv[])
152{
153 struct tune_dl dl = {0};
154 int err;
155 snd_pcm_t *pcm_out;
156 const char *device_out = "default";
157
158 if (argc > 2) {
159 device_out = argv[2];
160 }
161
162 if (tune_dl_load(&dl, dl_fname) < 0) {
163 return 1;
164 }
165
166 if ((err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
167 warnx("Playback open error: %s", snd_strerror(err));
168 return 1;
169 }
170
171 if ((err = snd_pcm_set_params(pcm_out,
172 format,
173 SND_PCM_ACCESS_MMAP_INTERLEAVED,
174 channels,
175 rate,
176 1,
177 latency)) < 0) {
178 warnx("Playback open error: %s", snd_strerror(err));
179 return 1;
180 }
181
182 if (dl.tune->init) {
183 dl.tune->init(dl.tune);
184 }
185
186 snd_pcm_uframes_t buffer_size;
187 snd_pcm_uframes_t period_size;
188 if ((err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size)) < 0) {
189 warnx("Playback get params error: %s", snd_strerror(err));
190 return 1;
191 }
192
193 double phase = 0;
194 const snd_pcm_channel_area_t *my_areas;
195 snd_pcm_uframes_t offset, frames, size;
196 snd_pcm_sframes_t avail, commitres;
197 snd_pcm_state_t state;
198 int first = 1;
199
200 while (1) {
201 state = snd_pcm_state(pcm_out);
202 if (state == SND_PCM_STATE_XRUN) {
203 err = xrun_recovery(pcm_out, -EPIPE);
204 if (err < 0) {
205 printf("XRUN recovery failed: %s\n", snd_strerror(err));
206 return err;
207 }
208 first = 1;
209 } else if (state == SND_PCM_STATE_SUSPENDED) {
210 err = xrun_recovery(pcm_out, -ESTRPIPE);
211 if (err < 0) {
212 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
213 return err;
214 }
215 }
216 avail = snd_pcm_avail_update(pcm_out);
217 if (avail < 0) {
218 err = xrun_recovery(pcm_out, avail);
219 if (err < 0) {
220 printf("avail update failed: %s\n", snd_strerror(err));
221 return err;
222 }
223 first = 1;
224 continue;
225 }
226 if (avail < period_size) {
227 if (first) {
228 first = 0;
229 err = snd_pcm_start(pcm_out);
230 if (err < 0) {
231 printf("Start error: %s\n", snd_strerror(err));
232 exit(EXIT_FAILURE);
233 }
234 } else {
235 err = snd_pcm_wait(pcm_out, -1);
236 if (err < 0) {
237 if ((err = xrun_recovery(pcm_out, err)) < 0) {
238 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
239 exit(EXIT_FAILURE);
240 }
241 first = 1;
242 }
243 }
244 continue;
245 }
246 size = period_size;
247 while (size > 0) {
248 frames = size;
249 err = snd_pcm_mmap_begin(pcm_out, &my_areas, &offset, &frames);
250 if (err < 0) {
251 if ((err = xrun_recovery(pcm_out, err)) < 0) {
252 printf("MMAP begin avail error: %s\n", snd_strerror(err));
253 exit(EXIT_FAILURE);
254 }
255 first = 1;
256 }
257 generate(&dl, my_areas, offset, frames, &phase);
258 // generate_sine(my_areas, offset, frames, &phase);
259 commitres = snd_pcm_mmap_commit(pcm_out, offset, frames);
260 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
261 if ((err = xrun_recovery(pcm_out, commitres >= 0 ? -EPIPE : commitres)) < 0) {
262 printf("MMAP commit error: %s\n", snd_strerror(err));
263 exit(EXIT_FAILURE);
264 }
265 first = 1;
266 }
267 size -= frames;
268 }
269
270 }
271 if (dl.tune->deinit) {
272 dl.tune->deinit(dl.tune);
273 }
274
275 snd_pcm_close(pcm_out);
276}
277

Built with git-ssb-web