1 |
/* Copyright (c) 2012, Benoit Rouits <brouits@free.fr> |
2 |
* Some rights reserved. |
3 |
* |
4 |
* Redistribution and use in source and binary forms, with or without |
5 |
* modification, are permitted provided that the following conditions |
6 |
* are met: |
7 |
* 1. Redistributions of source code must retain the above copyright |
8 |
* notice, this list of conditions and the following disclaimer. |
9 |
* 2. Redistributions in binary form must reproduce the above copyright |
10 |
* notice, this list of conditions and the following disclaimer in the |
11 |
* documentation and/or other materials provided with the distribution. |
12 |
* |
13 |
* ALTHOUGH THIS SOFTWARE IS MADE OF WIN AND SCIENCE, IT IS PROVIDED BY THE |
14 |
* AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, |
15 |
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY |
16 |
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL |
17 |
* THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
18 |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED |
19 |
* TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, |
20 |
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
21 |
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
22 |
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
23 |
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
24 |
* |
25 |
*/ |
26 |
|
27 |
#include <stdio.h> |
28 |
#include <stdlib.h> |
29 |
#include <string.h> |
30 |
|
31 |
#include <jack/jack.h> |
32 |
#include <jack/midiport.h> |
33 |
|
34 |
#include <config.h> |
35 |
#include "graph.h" |
36 |
|
37 |
#define MAX_VOICE 64 |
38 |
#define MAX_INST 128 |
39 |
|
40 |
#define error(str) fprintf(stderr, "[error]\t%s\n", str) |
41 |
|
42 |
struct jack { |
43 |
jack_client_t *client; |
44 |
jack_transport_state_t state; |
45 |
}; |
46 |
|
47 |
struct voice { |
48 |
struct event* route; |
49 |
int count; |
50 |
int cur; |
51 |
jack_time_t press; |
52 |
}; |
53 |
|
54 |
struct instrument { |
55 |
struct voice voices[MAX_VOICE]; |
56 |
int count; |
57 |
int end; |
58 |
|
59 |
jack_port_t *out; |
60 |
void* out_buf; |
61 |
|
62 |
struct graph *graph; |
63 |
}; |
64 |
|
65 |
struct app { |
66 |
struct instrument* instruments; |
67 |
int count; |
68 |
int end; |
69 |
|
70 |
struct jack *jack; |
71 |
}; |
72 |
|
73 |
static int jack_process_callback(jack_nframes_t nframes, void *ctx) |
74 |
{ |
75 |
struct app *app; |
76 |
jack_transport_state_t state; |
77 |
int i, voice, inst; |
78 |
|
79 |
app = ctx; |
80 |
|
81 |
if (nframes <= 0) { |
82 |
error("called process w/o frames"); |
83 |
return 0; |
84 |
} |
85 |
|
86 |
/* prepare all out buffers before writing into them */ |
87 |
for (i = 0; i < app->count; i++) { |
88 |
void *port_buf = jack_port_get_buffer(app->instruments[i].out, nframes); |
89 |
jack_midi_clear_buffer(port_buf); |
90 |
app->instruments[i].out_buf = port_buf; |
91 |
} |
92 |
|
93 |
/* get transport state, act in consequence */ |
94 |
|
95 |
/* check end of all voices of all instruments */ |
96 |
if (app->end >= app->count) |
97 |
return 0; |
98 |
|
99 |
/* go browse all voice states and send events */ |
100 |
for (i = 0; i < nframes; i++) { |
101 |
if (app->end >= app->count) { |
102 |
fprintf(stderr, "tune ended (%d instruments)\n", app->end); |
103 |
return 0; |
104 |
} |
105 |
for (inst = 0; inst < app->count; inst++) { |
106 |
if (app->instruments[inst].end >= app->instruments[inst].count) { |
107 |
app->end++; |
108 |
fprintf(stderr, "instrument %d ended (%d voices)\n", inst, app->instruments[inst].end); |
109 |
continue; |
110 |
} |
111 |
for (voice = 0; voice < app->instruments[inst].count; voice++) { |
112 |
jack_time_t *press = &app->instruments[inst].voices[voice].press; |
113 |
int *cur = &app->instruments[inst].voices[voice].cur; |
114 |
if (*cur >= app->instruments[inst].voices[voice].count) { |
115 |
app->instruments[inst].end++; |
116 |
fprintf(stderr, "voice %d ended (%d notes)\n", voice, *cur); |
117 |
continue; |
118 |
} |
119 |
struct event *ev = &app->instruments[inst].voices[voice].route[*cur]; |
120 |
unsigned char *buf = jack_midi_event_reserve(app->instruments[inst].out_buf, i, 3); |
121 |
if (!buf) { |
122 |
error("no memory, note lost"); |
123 |
return 0; |
124 |
} |
125 |
|
126 |
jack_time_t now = jack_get_time(); |
127 |
|
128 |
if (*press) { /* note must be released before passing to next note */ |
129 |
if (*press + ev->duration < now) { |
130 |
buf[2] = ev->velocity; |
131 |
buf[1] = ev->pitch; |
132 |
buf[0] = 0x80; /* note off */ |
133 |
press = 0; /* release */ |
134 |
(*cur)++; /* go to next node */ |
135 |
fprintf(stderr, "off %d (%p)\n", ev->pitch, ev); |
136 |
} |
137 |
} else { /* this is a new note */ |
138 |
buf[2] = ev->velocity; |
139 |
buf[1] = ev->pitch; |
140 |
buf[0] = 0x90; /* note on */ |
141 |
*press = now; |
142 |
fprintf(stderr, "on %d (%p)\n", ev->pitch, ev); |
143 |
} |
144 |
} |
145 |
} |
146 |
} |
147 |
|
148 |
return 0; |
149 |
} |
150 |
|
151 |
static int jack_sync_callback(jack_transport_state_t state, jack_position_t *position, void *ctx) |
152 |
{ |
153 |
struct instrument *instrument; |
154 |
|
155 |
instrument = ctx; |
156 |
|
157 |
if (state == JackTransportStarting) { |
158 |
/* seek in paths (???) according to position->frame */ |
159 |
} else if (state == JackTransportStopped) { |
160 |
/* pause */ |
161 |
} |
162 |
|
163 |
return 1; |
164 |
} |
165 |
|
166 |
void jack_shutdown(void* ctx) |
167 |
{ |
168 |
puts("jack vanished..."); |
169 |
} |
170 |
|
171 |
int main (int argc, char *argv[]) |
172 |
{ |
173 |
struct graph *graph; |
174 |
struct instrument instrument; |
175 |
struct voice voice; |
176 |
struct jack jack; |
177 |
struct app app; |
178 |
|
179 |
int i, err; |
180 |
|
181 |
/* create random graph */ |
182 |
graph = graph_new(100); |
183 |
graph_randomize2(graph, 3); |
184 |
|
185 |
/* initialize a solo instrument */ |
186 |
memset(&instrument, 0, sizeof(instrument)); |
187 |
instrument.graph = graph; |
188 |
|
189 |
/* initialize application context */ |
190 |
memset(&app, 0, sizeof(app)); |
191 |
app.jack = &jack; |
192 |
app.instruments = &instrument; |
193 |
app.count = 1; |
194 |
|
195 |
/* create a random route (lol) */ |
196 |
memset(&voice, 0, sizeof(voice)); |
197 |
voice.route = &graph->nodes[0].event; |
198 |
voice.count = 1; |
199 |
instrument.voices[0] = voice; |
200 |
instrument.count = 1; |
201 |
|
202 |
/* mark it (...) */ |
203 |
app.instruments[0].voices[0].route[0].pitch = 42; |
204 |
app.instruments[0].voices[0].route[0].duration = 42; |
205 |
|
206 |
/* init jack */ |
207 |
jack.client = jack_client_open(PACKAGE_NAME, JackNullOption, NULL); |
208 |
if (jack.client == NULL) { |
209 |
error("could not connect to jackd"); |
210 |
exit(EXIT_FAILURE); |
211 |
} |
212 |
|
213 |
err = jack_set_process_callback(jack.client, jack_process_callback, &app); |
214 |
|
215 |
if (err) { |
216 |
error("could not set jack process callback"); |
217 |
exit(EXIT_FAILURE); |
218 |
} |
219 |
|
220 |
err = jack_set_sync_callback(jack.client, jack_sync_callback, &app); |
221 |
|
222 |
if (err) { |
223 |
error("could not set jack sync callback"); |
224 |
exit(EXIT_FAILURE); |
225 |
} |
226 |
|
227 |
jack_on_shutdown(jack.client, jack_shutdown, &app); |
228 |
|
229 |
/* allocate one jack port per instrument (midi outs) */ |
230 |
for (i = 0; i < app.count; i++) { |
231 |
char name[32]; |
232 |
snprintf(name, 32, "out %d", i); |
233 |
app.instruments[i].out = jack_port_register(jack.client, name, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); |
234 |
if (app.instruments[i].out == NULL) { |
235 |
error("could not register a jack midi out port"); |
236 |
exit(EXIT_FAILURE); |
237 |
} |
238 |
} |
239 |
|
240 |
/* activate jack */ |
241 |
if (jack_activate(jack.client)) { |
242 |
error("could not activate jack client"); |
243 |
exit(EXIT_FAILURE); |
244 |
} |
245 |
|
246 |
/* start playing (event loop) */ |
247 |
sleep(1); |
248 |
|
249 |
/* close jack */ |
250 |
jack_client_close (jack.client); |
251 |
|
252 |
/* exit */ |
253 |
graph_destroy(graph); |
254 |
|
255 |
exit(EXIT_SUCCESS); |
256 |
} |