Files: 1b5aafc84d39bf3a1c4dbe87e27f6d4938aae848 / studio.c
6146 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 | static 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 = 1/(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 | |
111 | int 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 ((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 | 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