git ssb

0+

cel / foostudio



Tree: 2afb25b2316215a66cf266f58b979bd0ce84e04a

Files: 2afb25b2316215a66cf266f58b979bd0ce84e04a / foostudio.c

6234 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
43/* generate(): adapted from generate_sine() from alsa-lib/test/pcm.c */
44static void generate(struct tune *tune,
45 const snd_pcm_channel_area_t *areas,
46 snd_pcm_uframes_t offset,
47 int count, double *_phase)
48{
49 double phase = *_phase;
50 double step = 1/(double)rate;
51 unsigned char *samples[channels];
52 int steps[channels];
53 unsigned int chn;
54 int format_bits = snd_pcm_format_width(format);
55 // unsigned int maxval = (1 << (format_bits - 1)) - 1;
56 int bps = format_bits / 8; /* bytes per sample */
57 int phys_bps = snd_pcm_format_physical_width(format) / 8;
58 int big_endian = snd_pcm_format_big_endian(format) == 1;
59 int to_unsigned = snd_pcm_format_unsigned(format) == 1;
60
61 /* verify and prepare the contents of areas */
62 for (chn = 0; chn < channels; chn++) {
63 if ((areas[chn].first % 8) != 0) {
64 errx(1, "areas[%i].first == %i, aborting...", chn, areas[chn].first);
65 }
66 samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
67 if ((areas[chn].step % 16) != 0) {
68 errx(1, "areas[%i].step == %i, aborting...", chn, areas[chn].step);
69 }
70 steps[chn] = areas[chn].step / 8;
71 samples[chn] += offset * steps[chn];
72 }
73
74 /* fill the channel areas */
75 while (count-- > 0) {
76 union {
77 float f;
78 int i;
79 } fval;
80 int res, i;
81 if (tune && tune->play) {
82 fval.f = tune->play(tune, phase);
83 } else {
84 fval.f = 0;
85 }
86 res = fval.i;
87 if (to_unsigned)
88 res ^= 1U << (format_bits - 1);
89 for (chn = 0; chn < channels; chn++) {
90 /* Generate data in native endian format */
91 if (big_endian) {
92 for (i = 0; i < bps; i++)
93 *(samples[chn] + phys_bps - 1 - i) = (res >> i * 8) & 0xff;
94 } else {
95 for (i = 0; i < bps; i++)
96 *(samples[chn] + i) = (res >> i * 8) & 0xff;
97 }
98 samples[chn] += steps[chn];
99 }
100 phase += step;
101 /*
102 if (phase >= max_phase)
103 phase -= max_phase;
104 */
105 }
106 *_phase = phase;
107}
108
109
110int main(int argc, char *argv[])
111{
112 struct ccdl ccdl;
113 int err;
114 snd_pcm_t *pcm_out;
115 const char *src_fname = "tune.c";
116 const char *device_out = "default";
117
118 if (argc > 1) src_fname = argv[1];
119 if (argc > 2) device_out = argv[2];
120
121 ccdl_init(&ccdl, src_fname, "TUNE");
122
123 if (ccdl_watch(&ccdl)) {
124 errx(1, "ccdl_watch");
125 }
126
127 if ((err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
128 warnx("Playback open error: %s", snd_strerror(err));
129 return 1;
130 }
131
132 if ((err = snd_pcm_set_params(pcm_out,
133 format,
134 SND_PCM_ACCESS_MMAP_INTERLEAVED,
135 channels,
136 rate,
137 1,
138 latency)) < 0) {
139 warnx("Playback open error: %s", snd_strerror(err));
140 return 1;
141 }
142
143 snd_pcm_uframes_t buffer_size;
144 snd_pcm_uframes_t period_size;
145 if ((err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size)) < 0) {
146 warnx("Playback get params error: %s", snd_strerror(err));
147 return 1;
148 }
149
150 double phase = 0;
151 const snd_pcm_channel_area_t *my_areas;
152 snd_pcm_uframes_t offset, frames, size;
153 snd_pcm_sframes_t avail, commitres;
154 snd_pcm_state_t state;
155 int first = 1;
156
157 /* main loop: adapted from direct_loop() from alsa-lib/test/pcm.c */
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 ((snd_pcm_uframes_t)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 struct tune *tune = ccdl_get(&ccdl);
216 generate(tune, 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