initial commit
[midikeys] / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <conio.h>
5 #include "midi.h"
6 #include "keyb.h"
7
8 static int parse_args(int argc, char **argv);
9
10 static int bottom[] = {
11         0, 2, 4, 5, 7, 9, 11,  12, 14, 16
12 };
13 static int top[] = {
14         -1, 1, 3, -1, 6, 8, 10, -1, 13, 15
15 };
16
17 static int port = 0x330;
18 static int chan = 0;
19 static int octave = 4;
20 static int octave_offs = 60;
21 static int prog = 0;
22 static int vel = 127;
23
24 static int note_state[128];
25
26 static const char *helptext =
27         "Controls:\n"
28         " Play using the bottom two rows of the keyboard. Esc to exit.\n"
29         " change MIDI channel: 1-8    change instrument: (/)\n"
30         " shift octave: [/]           change velocity: +/-\n";
31
32
33 int main(int argc, char **argv)
34 {
35         int i, note, c;
36         char *env, *ptr, *endp;
37
38         if((env = getenv("BLASTER")) && ((ptr = strstr(env, "P:")) ||
39                                 (ptr = strstr(env, "p:")))) {
40                 port = strtol(ptr + 2, &endp, 16);
41                 if(endp == ptr + 2) {
42                         fprintf(stderr, "invalid MPU port specified in BLASTER environment variable, ignoring\n");
43                         port = 0x330;
44                 }
45         }
46
47         if(parse_args(argc, argv) == -1) {
48                 return 1;
49         }
50
51         kb_init();
52
53         printf("Initializing MIDI interface at port %x\n", port);
54         if(midi_init(port) == -1) {
55                 fprintf(stderr, "failed to initialize MPU port\n");
56                 return 1;
57         }
58         midi_chprog(chan, prog);
59
60         fputs(helptext, stdout);
61
62         for(;;) {
63                 struct kb_event ev;
64                 c = kb_event(&ev);
65                 if(c == 27) {
66                         break;
67                 }
68
69                 if(ev.code >= 44 && ev.code <= 53) {
70                         note = bottom[ev.code - 44] + octave_offs;
71                         if(note_state[note] != ev.press) {
72                                 midi_note(chan, note, ev.press ? vel : 0);
73                         }
74                         note_state[note] = ev.press;
75
76                 } else if(ev.code >= 30 && ev.code <= 40) {
77                         if((note = top[ev.code - 30]) != -1) {
78                                 note += octave_offs;
79                                 if(note_state[note] != ev.press) {
80                                         midi_note(chan, note, ev.press ? vel : 0);
81                                 }
82                                 note_state[note] = ev.press;
83                         }
84                 } else if(c >= '1' && c <= '8') {
85                         chan = c - '1';
86                 } else {
87                         switch(c) {
88                         case '[':
89                                 if(octave > 1) {
90                                         octave_offs = --octave * 12 + 12;
91                                         printf("octave: %d (midi offset: %d)\n", octave, octave_offs);
92                                 }
93                                 break;
94
95                         case ']':
96                                 if(octave < 7) {
97                                         octave_offs = ++octave * 12 + 12;
98                                         printf("octave: %d (midi offset: %d)\n", octave, octave_offs);
99                                 }
100                                 break;
101
102                         case '9':
103                                 prog = (prog - 1) & 0x7f;
104                                 printf("[%d] instrument: %d\n", chan, prog);
105                                 midi_chprog(chan, prog);
106                                 break;
107
108                         case '0':
109                                 prog = (prog + 1) & 0x7f;
110                                 printf("[%d] instrument: %d\n", chan, prog);
111                                 midi_chprog(chan, prog);
112                                 break;
113
114                         case '-':
115                                 if(vel > 0) {
116                                         vel--;
117                                         printf("velocity: %d\n", vel);
118                                 }
119                                 break;
120
121                         case '=':
122                                 if(vel < 127) {
123                                         vel++;
124                                         printf("velocity: %d\n", vel);
125                                 }
126                                 break;
127                         }
128                 }
129         }
130
131         midi_shutdown();
132
133         kb_shutdown();
134         return 0;
135 }
136
137 static const char *usage_fmt = "Usage: %s [options]\n"
138         "Options:\n"
139         "  -p <port>: set MIDI base I/O port\n"
140         "  -c <channel>: set MIDI channel\n"
141         "  -i <instrument>: MIDI instrument\n"
142         "  -o <octave>: select octave (1-7)\n"
143         "  -h: print usage information and exit\n"
144         "\n";
145
146 static int parse_args(int argc, char **argv)
147 {
148         int i;
149         char *endp;
150
151         for(i=1; i<argc; i++) {
152                 if(argv[i][0] == '-') {
153                         if(argv[i][2]) {
154                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
155                                 printf(usage_fmt, argv[0]);
156                                 return -1;
157                         }
158                         switch(argv[i][1]) {
159                         case 'p':
160                                 if(!argv[++i] || ((port = strtol(argv[i], &endp, 16)), endp == argv[i])) {
161                                         fprintf(stderr, "-p should be followed by the port number in hex\n");
162                                         return -1;
163                                 }
164                                 break;
165
166                         case 'c':
167                                 if(!argv[++i] || !(chan = atoi(argv[i]))) {
168                                         fprintf(stderr, "-c should be followed by the channel number\n");
169                                         return -1;
170                                 }
171                                 break;
172
173                         case 'i':
174                                 if(!argv[++i] || !(prog = atoi(argv[i]))) {
175                                         fprintf(stderr, "-i should be followed by a valid channel number\n");
176                                         return -1;
177                                 }
178                                 break;
179
180                         case 'o':
181                                 if(!argv[++i] || !(octave = atoi(argv[i]))) {
182                                         fprintf(stderr, "-o should be followed by the octane number.\n");
183                                         return -1;
184                                 }
185
186                         case 'h':
187                                 printf(usage_fmt, argv[0]);
188                                 exit(0);
189
190                         default:
191                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
192                                 printf(usage_fmt, argv[0]);
193                                 return -1;
194                         }
195                 }
196                 fprintf(stderr, "unexpected argument: %s\n", argv[i]);
197                 printf(usage_fmt, argv[0]);
198                 return -1;
199         }
200         return 0;
201 }