seq.c (3409B)
1 /* 2 Primitive sequencer / music-tracker 3 reads lines from stdin and prints them at controllable rate 4 */ 5 6 #include <u.h> 7 #include <libc.h> 8 #include <bio.h> 9 10 typedef struct List List; 11 12 struct List { 13 void *v; 14 List *next; 15 }; 16 17 List *list, *rstack[64], **rp = rstack; 18 char *file; 19 vlong t, dt; 20 long bpm = 120, steps = 1; 21 22 23 List * loadlist(char *); 24 void clearlist(List *); 25 26 void pause(void); 27 28 #define CMD(X) void X(List **, int , char **) 29 CMD(cmdbpm); 30 CMD(cmdpause); 31 CMD(cmdjump); 32 CMD(cmdret); 33 CMD(cmdreload); 34 35 struct Cmds { 36 char *name; 37 void (*func)(List **, int, char **); 38 } cmds[] = { 39 { "#", nil }, /* comment */ 40 { ":", nil }, /* label */ 41 { ".bpm", cmdbpm }, /* update bpm to new value */ 42 // { "----", cmdpause }, 43 { ".jmp", cmdjump }, 44 { ".ret", cmdret }, 45 { ".rld", cmdreload }, /* reload file and start from the beginning */ 46 }; 47 48 void 49 cmdbpm(List **, int n, char **args) 50 { 51 switch(n){ 52 case 0: /* what */ 53 case 1: 54 fprint(2, ".bpm: not enough arguments\n"); 55 break; 56 default: 57 fprint(2, ".bpm: too much arguments\n"); 58 case 2: 59 case 3: 60 bpm = strtol(args[1], nil, 10); 61 if (n == 3) steps = strtol(args[2], nil, 10); 62 } 63 if (bpm <= 0) { 64 fprint(2, "bpm is nonsence (%ld), restoring to 120\n", bpm); 65 bpm = 120; 66 } 67 if (steps <= 0) { 68 fprint(2, "steps is nonsence (%ld), restoring to 1\n", steps); 69 steps = 1; 70 } 71 dt = 60000000000 / (bpm * steps); 72 } 73 74 void 75 cmdpause(List **, int, char **) 76 { 77 pause(); 78 } 79 80 void 81 cmdjump(List **lp, int n, char **args) 82 { 83 if (n != 2) { 84 fprint(2, "cmdjump: wrong argument count %d\n", n); 85 return; 86 } 87 List *jp = list; 88 while (strcmp(args[1], (char *)jp->v) != 0) { 89 jp = jp->next; 90 if (jp == nil) return; 91 } 92 *rp = (*lp); 93 rp ++; 94 if (rp > rstack + 64) exits("rstack overflow"); 95 *lp = jp->next; 96 } 97 98 void 99 cmdret(List **lp, int n, char **) 100 { 101 if (n != 1) { 102 fprint(2, "cmdret: too much arguments\n"); 103 }; 104 rp --; 105 if (rp < rstack) exits("rstack underflow"); 106 *lp = (*rp); 107 } 108 109 void 110 cmdreload(List **lp, int, char **) 111 { 112 clearlist(list); 113 list = loadlist(file); 114 *lp = list; 115 } 116 117 List * 118 loadlist(char *file) 119 { 120 List start, *lp; 121 char *s; 122 Biobuf *in = Bopen(file, OREAD); 123 if (in == nil) sysfatal("%r"); 124 lp = &start; 125 while((s = Brdstr(in, '\n', 1)) != nil) { 126 lp->next = mallocz(sizeof(List), 1); 127 lp = lp->next; 128 lp->v = s; 129 } 130 Bterm(in); 131 return start.next; 132 } 133 134 void 135 clearlist(List *lp) 136 { 137 while (lp != nil) { 138 free(lp->v); 139 List *next = lp->next; 140 free(lp); 141 lp = next; 142 } 143 } 144 145 void 146 pause(void) 147 { 148 t += dt; 149 sleep((t - nsec()) / 1000000); 150 } 151 152 void 153 trkstep(List **lp) 154 { 155 char *s = (char *)(*lp)->v; 156 *lp = (*lp)->next; 157 int i; 158 for (i = 0; i < sizeof(cmds) / sizeof(struct Cmds); i++) { 159 if (strlen(s) == 0) return; 160 if (strncmp(s, cmds[i].name, strlen(cmds[i].name)) == 0) { 161 if (cmds[i].func != nil) { 162 char *dup = strdup(s); 163 char *args[64]; 164 int n = tokenize(dup, args, 64); 165 cmds[i].func(lp, n, args); 166 free(dup); 167 } 168 return; 169 } 170 } 171 if (i >= sizeof(cmds) / sizeof(struct Cmds)) { 172 print("%s\n", s); 173 pause(); 174 } 175 } 176 177 void 178 trkexec(List *start) 179 { 180 List *lp = start; 181 while (lp != nil) trkstep(&lp); 182 } 183 184 void 185 usage(void) 186 { 187 fprint(2, "usage: %s file\n", argv0); 188 exits("usage"); 189 } 190 191 void 192 main(int argc, char **argv) 193 { 194 ARGBEGIN { 195 default: 196 usage(); 197 } ARGEND; 198 if (argc != 1) usage(); 199 file = argv[0]; 200 list = loadlist(file); 201 202 t = nsec(); 203 dt = 60000000000 / (bpm * steps); 204 205 trkexec(list); 206 clearlist(list); 207 }