Files: 033d2aa1890af2ff80fde819e83b034484c47cbc / foostudio.c
5298 bytesRaw
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | static unsigned int rate = 48000; |
18 | static unsigned int channels_out = 1; |
19 | static unsigned int channels_in = 1; |
20 | static unsigned int latency = 10000; // us |
21 | static int resample = 1; // allow resampling |
22 | static int16_t maxval = 0x7fff; |
23 | |
24 | static snd_pcm_format_t format = SND_PCM_FORMAT_S16; |
25 | |
26 | int main(int argc, char *argv[]) |
27 | { |
28 | struct ccdl ccdl; |
29 | void *tune_obj = NULL; |
30 | int err; |
31 | snd_pcm_t *pcm_out = NULL, *pcm_in = NULL; |
32 | snd_rawmidi_t *midi_in = NULL; |
33 | const char *src_fname = "tune.c"; |
34 | const char *device_out = "default"; |
35 | const char *device_in = "default"; |
36 | const char *midi_device_in = "default"; |
37 | |
38 | if (argc > 1) src_fname = argv[1]; |
39 | if (argc > 2) device_out = argv[2]; |
40 | if (argc > 3) device_in = argv[3]; |
41 | if (argc > 4) midi_device_in = argv[4]; |
42 | |
43 | if (device_in[0] == '\0') device_in = NULL; |
44 | if (midi_device_in[0] == '\0') midi_device_in = NULL; |
45 | |
46 | ccdl_init(&ccdl, src_fname, "play"); |
47 | |
48 | if (ccdl_watch(&ccdl)) { |
49 | errx(1, "ccdl_watch"); |
50 | } |
51 | |
52 | err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0); |
53 | if (err < 0) { |
54 | warnx("Playback open error: %s", snd_strerror(err)); |
55 | return 1; |
56 | } |
57 | |
58 | if (device_in) { |
59 | err = snd_pcm_open(&pcm_in, device_in, |
60 | SND_PCM_STREAM_CAPTURE, |
61 | SND_PCM_NONBLOCK); |
62 | if (err < 0) { |
63 | warnx("Capture open error: %s", snd_strerror(err)); |
64 | return 1; |
65 | } |
66 | } |
67 | |
68 | if (midi_device_in) { |
69 | err = snd_rawmidi_open(&midi_in, NULL, midi_device_in, |
70 | SND_RAWMIDI_NONBLOCK); |
71 | if (err < 0) { |
72 | warnx("Midi open error: %s", snd_strerror(err)); |
73 | } |
74 | } |
75 | |
76 | if ((err = snd_pcm_set_params(pcm_out, format, |
77 | SND_PCM_ACCESS_RW_INTERLEAVED, |
78 | channels_out, rate, resample, latency)) < 0) { |
79 | warnx("Playback params error: %s", snd_strerror(err)); |
80 | return 1; |
81 | } |
82 | |
83 | if (pcm_in) { |
84 | if ((err = snd_pcm_set_params(pcm_in, format, |
85 | SND_PCM_ACCESS_RW_INTERLEAVED, |
86 | channels_in, rate, resample, latency)) < 0) { |
87 | warnx("Capture params error: %s", snd_strerror(err)); |
88 | return 1; |
89 | } |
90 | } |
91 | |
92 | snd_pcm_uframes_t buffer_size, buffer_size_in; |
93 | snd_pcm_uframes_t period_size, period_size_in; |
94 | |
95 | err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size); |
96 | if (err < 0) { |
97 | warnx("Playback get params error: %s", snd_strerror(err)); |
98 | return 1; |
99 | } |
100 | if (pcm_in) { |
101 | err = snd_pcm_get_params(pcm_in, &buffer_size_in, &period_size_in); |
102 | if (err < 0) { |
103 | warnx("Capture get params error: %s", snd_strerror(err)); |
104 | return 1; |
105 | } |
106 | period_size = min(period_size_in, period_size); |
107 | buffer_size = min(buffer_size_in, buffer_size); |
108 | } |
109 | (void)buffer_size; |
110 | // printf("a %zu\n", (size_t)period_size); |
111 | |
112 | double time = 0; |
113 | double step = 1/(double)rate; |
114 | int midi_reopen_timer = 0; |
115 | |
116 | while (1) { |
117 | snd_pcm_sframes_t frames, frames_in = 0, frames_out; |
118 | // frames = snd_pcm_avail_update(pcm_out); |
119 | frames = period_size; |
120 | if (frames < 0) { |
121 | err = snd_pcm_recover(pcm_out, frames, 0); |
122 | if (err) err = snd_pcm_wait(pcm_out, 1000); |
123 | if (err) warnx("snd_pcm_wait(%s)", snd_strerror(err)); |
124 | continue; |
125 | } |
126 | int16_t buffer[frames]; |
127 | |
128 | if (pcm_in) { |
129 | if (snd_pcm_state(pcm_in) == SND_PCM_STATE_PREPARED) { |
130 | if ((err = snd_pcm_start(pcm_in)) < 0) { |
131 | warnx("snd_pcm_start: %s", snd_strerror(err)); |
132 | } |
133 | } |
134 | frames_in = snd_pcm_readi(pcm_in, buffer, frames); |
135 | if (frames_in < 0) { |
136 | err = snd_pcm_recover(pcm_in, frames_in, 0); |
137 | if (err == -EAGAIN) { |
138 | } else if (err < 0) { |
139 | warnx("snd_pcm_readi: %s\n", snd_strerror(err)); |
140 | } |
141 | } |
142 | } |
143 | |
144 | if (midi_reopen_timer > 0) { |
145 | if (--midi_reopen_timer == 0) { |
146 | err = snd_rawmidi_open(&midi_in, NULL, midi_device_in, |
147 | SND_RAWMIDI_NONBLOCK); |
148 | if (err < 0) { |
149 | warnx("midi device re-open failed: %s", snd_strerror(err)); |
150 | midi_reopen_timer = 5; |
151 | } |
152 | } |
153 | } |
154 | |
155 | unsigned char midi_buf[frames], *midi_bufp = NULL; |
156 | size_t midi_bytes = 0; |
157 | if (midi_in) { |
158 | ssize_t bytes = snd_rawmidi_read(midi_in, midi_buf, frames); |
159 | if (bytes > 0) { |
160 | midi_bufp = midi_buf; |
161 | midi_bytes = bytes; |
162 | } else if (bytes == -EAGAIN) { |
163 | } else if (bytes == -ENODEV) { |
164 | // lost device. queue reopening it |
165 | midi_in = NULL; |
166 | midi_reopen_timer = 5; |
167 | } else if (bytes < 0) { |
168 | warnx("snd_rawmidi_read: %s (%zd)", snd_strerror(bytes), bytes); |
169 | } |
170 | } |
171 | |
172 | play_fn *play; |
173 | *(void **)(&play) = ccdl_get(&ccdl); |
174 | if (play) { |
175 | for (size_t i = 0; i < (size_t)frames; i++) { |
176 | time += step; |
177 | int16_t frame_in = (ssize_t)i < frames_in ? buffer[i] : 0; |
178 | float in = (float)frame_in / maxval; |
179 | float val = play(&tune_obj, time, in, midi_bufp); |
180 | if (midi_bufp) { |
181 | if (midi_bytes <= 3) { |
182 | midi_bufp = NULL; |
183 | } else { |
184 | midi_bufp += 3; |
185 | midi_bytes -= 3; |
186 | } |
187 | } |
188 | buffer[i] = val * maxval; |
189 | } |
190 | } else { |
191 | time += step * frames; |
192 | } |
193 | |
194 | frames_out = snd_pcm_writei(pcm_out, buffer, frames); |
195 | if (frames_out < 0) snd_pcm_recover(pcm_out, frames_out, 0); |
196 | } |
197 | |
198 | snd_rawmidi_close(midi_in); |
199 | snd_pcm_close(pcm_out); |
200 | snd_pcm_close(pcm_in); |
201 | return ccdl_deinit(&ccdl); |
202 | } |
203 |
Built with git-ssb-web