git ssb

0+

cel / foostudio



Tree: 5786b0b025e423f956afb157f1478abd951ddb2a

Files: 5786b0b025e423f956afb157f1478abd951ddb2a / studio.c

6132 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#include "ccdl.h"
13
14static unsigned int rate = 44100;
15static unsigned int channels = 1;
16static unsigned int latency = 500000; /* ring buffer length in us */
17static snd_pcm_format_t format = SND_PCM_FORMAT_FLOAT;
18
19/*
20 * Underrun and suspend recovery, from alsa-lib/test/pcm.c
21 */
22
23static int xrun_recovery(snd_pcm_t *handle, int err)
24{
25 if (err == -EPIPE) { /* under-run */
26 err = snd_pcm_prepare(handle);
27 if (err < 0)
28 warnx("Can't recovery from underrun, prepare failed: %s", snd_strerror(err));
29 return 0;
30 } else if (err == -ESTRPIPE) {
31 while ((err = snd_pcm_resume(handle)) == -EAGAIN)
32 sleep(1); /* wait until the suspend flag is released */
33 if (err < 0) {
34 err = snd_pcm_prepare(handle);
35 if (err < 0)
36 warnx("Can't recovery from suspend, prepare failed: %s", snd_strerror(err));
37 }
38 return 0;
39 }
40 return err;
41}
42
43static void generate(struct ccdl *ccdl,
44 const snd_pcm_channel_area_t *areas,
45 snd_pcm_uframes_t offset,
46 int count, double *_phase)
47{
48 double phase = *_phase;
49 double step = 2. * M_PI/(double)rate;
50 unsigned char *samples[channels];
51 int steps[channels];
52 unsigned int chn;
53 int format_bits = snd_pcm_format_width(format);
54 unsigned int maxval = (1 << (format_bits - 1)) - 1;
55 int bps = format_bits / 8; /* bytes per sample */
56 int phys_bps = snd_pcm_format_physical_width(format) / 8;
57 int big_endian = snd_pcm_format_big_endian(format) == 1;
58 int to_unsigned = snd_pcm_format_unsigned(format) == 1;
59
60 /* verify and prepare the contents of areas */
61 for (chn = 0; chn < channels; chn++) {
62 if ((areas[chn].first % 8) != 0) {
63 errx(1, "areas[%i].first == %i, aborting...", chn, areas[chn].first);
64 }
65 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
66 if ((areas[chn].step % 16) != 0) {
67 errx(1, "areas[%i].step == %i, aborting...", chn, areas[chn].step);
68 }
69 steps[chn] = areas[chn].step / 8;
70 samples[chn] += offset * steps[chn];
71 }
72
73 struct tune *tune = ccdl_get(ccdl);
74
75 /* fill the channel areas */
76 while (count-- > 0) {
77 union {
78 float f;
79 int i;
80 } fval;
81 int res, i;
82 if (tune && tune->play) {
83 fval.f = tune->play(tune, phase);
84 } else {
85 fval.f = 0;
86 }
87 res = fval.i;
88 if (to_unsigned)
89 res ^= 1U << (format_bits - 1);
90 for (chn = 0; chn < channels; chn++) {
91 /* Generate data in native endian format */
92 if (big_endian) {
93 for (i = 0; i < bps; i++)
94 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
95 } else {
96 for (i = 0; i < bps; i++)
97 *(samples[chn] + i) = (res >> i * 8) & 0xff;
98 }
99 samples[chn] += steps[chn];
100 }
101 phase += step;
102 /*
103 if (phase >= max_phase)
104 phase -= max_phase;
105 */
106 }
107 *_phase = phase;
108}
109
110
111int main(int argc, char *argv[])
112{
113 struct ccdl ccdl;
114 int err;
115 snd_pcm_t *pcm_out;
116 const char *src_fname = "tune.c";
117 const char *device_out = "default";
118
119 if (argc > 1) src_fname = argv[1];
120 if (argc > 2) device_out = argv[2];
121
122 ccdl_init(&ccdl, src_fname, "TUNE");
123
124 if (ccdl_watch(&ccdl)) {
125 errx(1, "ccdl_watch");
126 }
127
128 if ((err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
129 warnx("Playback open error: %s", snd_strerror(err));
130 return 1;
131 }
132
133 if ((err = snd_pcm_set_params(pcm_out,
134 format,
135 SND_PCM_ACCESS_MMAP_INTERLEAVED,
136 channels,
137 rate,
138 1,
139 latency)) < 0) {
140 warnx("Playback open error: %s", snd_strerror(err));
141 return 1;
142 }
143
144 snd_pcm_uframes_t buffer_size;
145 snd_pcm_uframes_t period_size;
146 if ((err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size)) < 0) {
147 warnx("Playback get params error: %s", snd_strerror(err));
148 return 1;
149 }
150
151 double phase = 0;
152 const snd_pcm_channel_area_t *my_areas;
153 snd_pcm_uframes_t offset, frames, size;
154 snd_pcm_sframes_t avail, commitres;
155 snd_pcm_state_t state;
156 int first = 1;
157
158 while (1) {
159 state = snd_pcm_state(pcm_out);
160 if (state == SND_PCM_STATE_XRUN) {
161 err = xrun_recovery(pcm_out, -EPIPE);
162 if (err < 0) {
163 printf("XRUN recovery failed: %s\n", snd_strerror(err));
164 return err;
165 }
166 first = 1;
167 } else if (state == SND_PCM_STATE_SUSPENDED) {
168 err = xrun_recovery(pcm_out, -ESTRPIPE);
169 if (err < 0) {
170 printf("SUSPEND recovery failed: %s\n", snd_strerror(err));
171 return err;
172 }
173 }
174 avail = snd_pcm_avail_update(pcm_out);
175 if (avail < 0) {
176 err = xrun_recovery(pcm_out, avail);
177 if (err < 0) {
178 printf("avail update failed: %s\n", snd_strerror(err));
179 return err;
180 }
181 first = 1;
182 continue;
183 }
184 if (avail < period_size) {
185 if (first) {
186 first = 0;
187 err = snd_pcm_start(pcm_out);
188 if (err < 0) {
189 printf("Start error: %s\n", snd_strerror(err));
190 exit(EXIT_FAILURE);
191 }
192 } else {
193 err = snd_pcm_wait(pcm_out, -1);
194 if (err < 0) {
195 if ((err = xrun_recovery(pcm_out, err)) < 0) {
196 printf("snd_pcm_wait error: %s\n", snd_strerror(err));
197 exit(EXIT_FAILURE);
198 }
199 first = 1;
200 }
201 }
202 continue;
203 }
204 size = period_size;
205 while (size > 0) {
206 frames = size;
207 err = snd_pcm_mmap_begin(pcm_out, &my_areas, &offset, &frames);
208 if (err < 0) {
209 if ((err = xrun_recovery(pcm_out, err)) < 0) {
210 printf("MMAP begin avail error: %s\n", snd_strerror(err));
211 exit(EXIT_FAILURE);
212 }
213 first = 1;
214 }
215 generate(&ccdl, my_areas, offset, frames, &phase);
216 // generate_sine(my_areas, offset, frames, &phase);
217 commitres = snd_pcm_mmap_commit(pcm_out, offset, frames);
218 if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) {
219 if ((err = xrun_recovery(pcm_out, commitres >= 0 ? -EPIPE : commitres)) < 0) {
220 printf("MMAP commit error: %s\n", snd_strerror(err));
221 exit(EXIT_FAILURE);
222 }
223 first = 1;
224 }
225 size -= frames;
226 }
227
228 }
229
230 snd_pcm_close(pcm_out);
231 return ccdl_deinit(&ccdl);
232}
233

Built with git-ssb-web