Files: 2afb25b2316215a66cf266f58b979bd0ce84e04a / foostudio.c
6234 bytesRaw
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | static unsigned int rate = 44100; |
15 | static unsigned int channels = 1; |
16 | static unsigned int latency = 500000; /* ring buffer length in us */ |
17 | static snd_pcm_format_t format = SND_PCM_FORMAT_FLOAT; |
18 | |
19 | /* |
20 | * Underrun and suspend recovery, from alsa-lib/test/pcm.c |
21 | */ |
22 | |
23 | static 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 */ |
44 | static 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 | |
110 | int 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