foo
authorJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 17 Jun 2022 02:50:34 +0000 (05:50 +0300)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Fri, 17 Jun 2022 02:50:34 +0000 (05:50 +0300)
.gitignore
bootcard.asm
tools/mididump/Makefile [new file with mode: 0644]
tools/mididump/midi.c [new file with mode: 0644]
tools/mididump/midi.h [new file with mode: 0644]
tools/mididump/mididump.c [new file with mode: 0644]

index 52f0c45..b239ad4 100644 (file)
@@ -1,3 +1,7 @@
 *.swp
+*.o
 *.img
 *.bin
+RUN
+tools/mididump/mididump
+*.mid
index e0955ed..d07c107 100644 (file)
@@ -7,6 +7,9 @@
        bits 16
 
 data_start     equ 7e00h
+nticks         equ data_start
+muscur         equ nticks + 4
+
 osc_freq       equ 1193182
 PIT_DATA0      equ 40h
 PIT_DATA2      equ 42h
@@ -44,6 +47,8 @@ KB_CTRL               equ 61h
        mov ss, ax
        mov sp, 7c00h
 
+       mov dword [nticks], 0
+       mov dword [muscur], 0
        call init_spk
 
        mov ax, 13h
@@ -63,25 +68,15 @@ KB_CTRL             equ 61h
        mov si, str2
        call textout
 
+       sti
+
 infloop:
        hlt
        jmp infloop
 
 init_spk:
-       xor ax, ax
-       mov [32], ax
-       mov word [34], timer_intr
-
-       mov al, PIT_CMD_CHAN2 | PIT_CMD_HILO | PIT_CMD_SQWAVE
-       out PIT_CMD, al
-       mov ax, DIV_ROUND(osc_freq, 440)
-       out PIT_DATA2, al
-       mov al, ah
-       out PIT_DATA2, al
-
-       spkon
-
-       sti
+       mov word [32], timer_intr
+       mov word [34], 0
        ret
 
 textout:
@@ -96,14 +91,53 @@ textout:
 .done: ret
 
 timer_intr:
-       spkoff
-       mov al, 20h
+       mov ax, [nticks]
+       inc ax
+       mov [nticks], ax
+
+       mov bx, [muscur]
+       shl bx, 2
+       mov cx, [music + bx]
+       cmp cx, 0ffffh
+       jz .off
+       cmp ax, cx
+       jb .eoi
+
+       inc dword [muscur]
+       mov ax, [music + 2 + bx] ; grab timeout
+       test ax, ax
+       jz .off
+       mov bx, ax
+
+       mov al, PIT_CMD_CHAN2 | PIT_CMD_HILO | PIT_CMD_SQWAVE
+       out PIT_CMD, al
+       mov ax, bx
+       out PIT_DATA2, al
+       mov al, ah
+       out PIT_DATA2, al
+       spkon
+       jmp .eoi
+
+.off:  spkoff
+
+.eoi:  mov al, 20h
        out 20h, al     ; EOI
        iret
 
 str1:  db 'message message blah',0
 str2:  db 'Michael & Athina',0
 
+music:
+       dw 0, 500
+       dw 10, 0
+       dw 20, 2000
+       dw 30, 0
+       dw 40, 500
+       dw 50, 0
+       dw 60, 2000
+       dw 70, 0
+       dw 0ffffh, 0
+
        times 446-($-$$) db 0
        dd 00212080h
        dd 0820280ch
diff --git a/tools/mididump/Makefile b/tools/mididump/Makefile
new file mode 100644 (file)
index 0000000..08b7d74
--- /dev/null
@@ -0,0 +1,11 @@
+obj = mididump.o midi.o
+bin = mididump
+
+CFLAGS = -pedantic -Wall -g
+
+$(bin): $(obj)
+       $(CC) -o $@ $(obj) $(LDFLAGS)
+
+.PHONY: clean
+clean:
+       rm -f $(bin) $(obj)
diff --git a/tools/mididump/midi.c b/tools/mididump/midi.c
new file mode 100644 (file)
index 0000000..3acbb98
--- /dev/null
@@ -0,0 +1,419 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <inttypes.h>
+#include "midi.h"
+
+#define USE_MMAP
+
+#define FMT_SINGLE             0
+#define FMT_MULTI_TRACK        1
+#define FMT_MULTI_SEQ  2
+
+/* meta events */
+#define META_SEQ               0
+#define META_TEXT              1
+#define META_COPYRIGHT 2
+#define META_NAME              3
+#define META_INSTR             4
+#define META_LYRICS            5
+#define META_MARKER            6
+#define META_CUE               7
+#define META_CHANPREFIX        32
+#define META_END_TRACK 47
+#define META_TEMPO             81
+#define META_SMPTE_OFFS        84
+#define META_TMSIG             88
+#define META_KEYSIG            89
+#define META_SPECIFIC  127
+
+#define CHUNK_HDR_SIZE 8
+struct chunk_hdr {
+       char id[4];
+       uint32_t size;
+       unsigned char data[1];
+};
+
+struct midi_hdr {
+       uint16_t fmt;   /* 0: single, 1: multi-track, 2: multiple independent */
+       uint16_t num_tracks;
+       uint16_t tm_div;
+
+} __attribute__ ((packed));
+
+static void destroy_track(struct midi_track *trk);
+static int read_track(struct midi *midi, struct chunk_hdr *chunk);
+static long read_vardata(unsigned char **pptr);
+static int read_meta_event(struct midi *midi, struct midi_track *trk, unsigned char **pptr);
+static int read_sysex_event(struct midi *midi, unsigned char **pptr);
+static int ischunk(struct chunk_hdr *chunk, const char *name);
+static struct chunk_hdr *mkchunk(void *ptr);
+static struct chunk_hdr *skip_chunk(struct chunk_hdr *chunk);
+static struct midi_hdr *mkmidi(void *ptr);
+static void bigend(void *ptr, int sz);
+static void *map_file(const char *fname, int *size);
+static void unmap_file(void *mem, int size);
+
+#define IS_VALID_EVTYPE(x) ((x) >= MIDI_NOTE_OFF && (x) <= MIDI_PITCH_BEND)
+
+/* XXX the event arity table must match the MIDI_* defines in midi.h */
+static int ev_arity[] = {
+       0, 0, 0, 0, 0, 0, 0, 0,
+       2, /* note off (note, velocity)*/
+       2, /* note on (note, velocity)*/
+       2, /* note aftertouch (note, aftertouch value) */
+       2, /* controller (controller number, value) */
+       1, /* prog change (prog number) */
+       1, /* channel aftertouch (aftertouch value) */
+       2  /* pitch bend (pitch LSB, pitch MSB) */
+};
+
+
+struct midi *midi_load(const char *fname)
+{
+       struct midi *midi;
+       char *mem;
+       int size;
+       struct chunk_hdr *chunk;
+       struct midi_hdr *hdr;
+
+       if(!(mem = map_file(fname, &size))) {
+               return 0;
+       }
+       chunk = mkchunk(mem);
+
+       if(!ischunk(chunk, "MThd") || chunk->size != 6) {
+               fprintf(stderr, "invalid or corrupted midi file: %s\n", fname);
+               goto err;
+       }
+       hdr = mkmidi(chunk->data);
+
+       if(!(midi = malloc(sizeof *midi))) {
+               perror("failed to allocate memory");
+               goto err;
+       }
+
+       if((hdr->tm_div & 0x8000) == 0) {
+               /* division is in pulses / quarter note */
+               midi->ppqn = hdr->tm_div;
+               midi->fps = midi->ticks_per_frame = -1;
+       } else {
+               /* division in frames / sec */
+               midi->fps = (hdr->tm_div & 0x7f00) >> 8;
+               midi->ticks_per_frame = hdr->tm_div & 0xff;
+               midi->ppqn = -1;
+       }
+
+       if(!(midi->tracks = malloc(hdr->num_tracks * sizeof *midi->tracks))) {
+               perror("failed to allocate memory");
+               goto err;
+       }
+       midi->num_tracks = 0;
+
+       while((chunk = skip_chunk(chunk)) && ((char*)chunk < mem + size)) {
+               if(ischunk(chunk, "MTrk")) {
+                       if(read_track(midi, chunk) == -1) {
+                               fprintf(stderr, "failed to read track\n");
+                       }
+               }
+       }
+
+       unmap_file(mem, size);
+       return midi;
+
+err:
+       unmap_file(mem, size);
+       midi_free(midi);
+       return 0;
+}
+
+void midi_free(struct midi *midi)
+{
+       int i;
+
+       if(!midi) return;
+
+       for(i=0; i<midi->num_tracks; i++) {
+               destroy_track(midi->tracks + i);
+       }
+
+       free(midi->tracks);
+       free(midi);
+}
+
+int midi_num_tracks(struct midi *midi)
+{
+       return midi->num_tracks;
+}
+
+struct midi_track *midi_track(struct midi *midi, int idx)
+{
+       if(idx < 0 || idx >= midi->num_tracks) {
+               return 0;
+       }
+       return midi->tracks + idx;
+}
+
+static void destroy_track(struct midi_track *trk)
+{
+       free(trk->name);
+       while(trk->head) {
+               void *tmp = trk->head;
+               trk->head = trk->head->next;
+               free(tmp);
+       }
+}
+
+static int read_track(struct midi *midi, struct chunk_hdr *chunk)
+{
+       unsigned char *ptr;
+       struct midi_track trk = {0, 0, 0, 0};
+       unsigned char prev_stat = 0;
+       int type;
+       struct midi_event *ev;
+
+       if(!ischunk(chunk, "MTrk")) {
+               return -1;
+       }
+
+       ptr = chunk->data;
+       while(ptr < chunk->data + chunk->size) {
+               long dt;
+               unsigned char stat;
+
+               dt = read_vardata(&ptr);
+               stat = *ptr++;
+
+               if(stat == 0xff) {
+                       read_meta_event(midi, &trk, &ptr);
+               } else if(stat == 0xf0) {
+                       read_sysex_event(midi, &ptr);
+               } else {
+                       if(!(stat & 0x80)) {
+                               /* not a status byte, assume running status */
+                               stat = prev_stat;
+                               ptr--;
+                       }
+                       type = (stat >> 4) & 0xf;
+
+                       if(!IS_VALID_EVTYPE(type) || !(ev = malloc(sizeof *ev))) {
+                               /* unkwown message, skip all data bytes */
+                               while(ptr < chunk->data + chunk->size && !(*ptr & 0x80)) {
+                                       ptr++;
+                               }
+                               continue;
+                       }
+
+                       if(trk.head) {
+                               trk.tail->next = ev;
+                       } else {
+                               trk.head = ev;
+                       }
+                       trk.tail = ev;
+                       ev->next = 0;
+                       trk.num_ev++;
+
+                       ev->dt = dt;
+                       ev->type = type;
+                       ev->channel = stat & 0xf;
+
+                       ev->arg[0] = *ptr++;
+                       if(ev_arity[ev->type] > 1) {
+                               ev->arg[1] = *ptr++;
+                       }
+
+                       if(ev->type == MIDI_NOTE_ON && ev->arg[1] == 0) {
+                               ev->type = MIDI_NOTE_OFF;
+                       }
+
+                       prev_stat = stat;
+               }
+       }
+
+       /* if we did actually add any events ... */
+       if(trk.num_ev) {
+               midi->tracks[midi->num_tracks++] = trk;
+               /*printf("loaded track with %d events\n", trk.num_ev);*/
+       }
+       return 0;
+}
+
+static long read_vardata(unsigned char **pptr)
+{
+       int i;
+       long res = 0;
+       unsigned char *ptr = *pptr;
+
+       for(i=0; i<4; i++) {
+               res |= (long)(*ptr & 0x7f) << (i * 8);
+
+               /* if first bit is not set we're done */
+               if((*ptr++ & 0x80) == 0)
+                       break;
+       }
+       *pptr = ptr;
+       return res;
+}
+
+static int read_meta_event(struct midi *midi, struct midi_track *trk, unsigned char **pptr)
+{
+       unsigned char *ptr = *pptr;
+       unsigned char type;
+       long size;
+
+       type = *ptr++;
+       size = read_vardata(&ptr);
+
+       switch(type) {
+       case META_NAME:
+               free(trk->name);
+               trk->name = malloc(size + 1);
+               memcpy(trk->name, ptr, size);
+               trk->name[size] = 0;
+               break;
+
+       case META_TEMPO:
+               /* TODO add a tempo change event to the midi struct */
+               break;
+
+       default:
+               break;
+       }
+       *pptr = ptr + size;
+       return 0;
+}
+
+/* ignore sysex events */
+static int read_sysex_event(struct midi *midi, unsigned char **pptr)
+{
+       long size = read_vardata(pptr);
+       *pptr += size;
+       return 0;
+}
+
+static int ischunk(struct chunk_hdr *chunk, const char *name)
+{
+       return memcmp(chunk->id, name, 4) == 0;
+}
+
+static struct chunk_hdr *mkchunk(void *ptr)
+{
+       struct chunk_hdr *chdr = ptr;
+       bigend(&chdr->size, sizeof chdr->size);
+       return chdr;
+}
+
+static struct chunk_hdr *skip_chunk(struct chunk_hdr *chunk)
+{
+       return mkchunk((char*)chunk + CHUNK_HDR_SIZE + chunk->size);
+}
+
+static struct midi_hdr *mkmidi(void *ptr)
+{
+       struct midi_hdr *midi = ptr;
+
+       bigend(&midi->fmt, sizeof midi->fmt);
+       bigend(&midi->num_tracks, sizeof midi->num_tracks);
+       bigend(&midi->tm_div, sizeof midi->tm_div);
+       return midi;
+}
+
+static void bigend(void *ptr, int sz)
+{
+       static unsigned char test[] = {0x12, 0x34};
+       uint32_t val32;
+       uint16_t val16;
+
+       if(sz < 2 || *(uint16_t*)test == 0x1234) {
+               return;
+       }
+
+       switch(sz) {
+       case 4:
+               val32 = *(uint32_t*)ptr;
+               *(uint32_t*)ptr = (val32 << 24) | (val32 >> 24) | ((val32 & 0xff00) << 8) |
+                       ((val32 & 0xff0000) >> 8);
+               break;
+
+       case 2:
+               val16 = *(uint16_t*)ptr;
+               *(uint16_t*)ptr = (val16 >> 8) | (val16 << 8);
+               break;
+
+       case 1:
+       default:
+               break;
+       }
+}
+
+#if defined(__unix__) && defined(USE_MMAP)
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+static void *map_file(const char *fname, int *size)
+{
+       int fd;
+       struct stat st;
+       void *mem;
+
+       if((fd = open(fname, O_RDONLY)) == -1) {
+               fprintf(stderr, "failed to open midi file: %s: %s\n", fname, strerror(errno));
+               return 0;
+       }
+       fstat(fd, &st);
+
+       if((mem = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) == (void*)-1) {
+               fprintf(stderr, "failed to map midi file: %s: %s\n", fname, strerror(errno));
+               close(fd);
+               return 0;
+       }
+       close(fd);
+
+       *size = st.st_size;
+       return mem;
+}
+
+static void unmap_file(void *mem, int size)
+{
+       munmap(mem, size);
+}
+#else
+static void *map_file(const char *fname, int *size)
+{
+       FILE *fp;
+       long sz;
+       void *buf;
+
+       if(!(fp = fopen(fname, "rb"))) {
+               fprintf(stderr, "failed to open midi file: %s: %s\n", fname, strerror(errno));
+               return 0;
+       }
+       fseek(fp, 0, SEEK_END);
+       sz = ftell(fp);
+       rewind(fp);
+
+       if(!(buf = malloc(sz))) {
+               fprintf(stderr, "failed to allocate space for %s in memory (%ld bytes)\n", fname, sz);
+               fclose(fp);
+               return 0;
+       }
+       if(fread(buf, 1, sz, fp) != sz) {
+               fprintf(stderr, "failed to load midi file: %s: %s\n", fname, strerror(errno));
+               free(buf);
+               fclose(fp);
+               return 0;
+       }
+       fclose(fp);
+
+       *size = sz;
+       return buf;
+}
+
+static void unmap_file(void *mem, int size)
+{
+       free(mem);
+}
+#endif
diff --git a/tools/mididump/midi.h b/tools/mididump/midi.h
new file mode 100644 (file)
index 0000000..f9232ff
--- /dev/null
@@ -0,0 +1,54 @@
+#ifndef MIDI_H_
+#define MIDI_H_
+
+#include <stdio.h>
+
+struct midi {
+       int ppqn, fps, ticks_per_frame;
+
+       int num_tracks;
+       struct midi_track *tracks;
+};
+
+
+struct midi_track {
+       char *name;
+       struct midi_event *head, *tail;
+       int num_ev;
+};
+
+struct midi_event {
+       long dt;
+       int type;
+       int channel;
+       int arg[2];
+
+       struct midi_event *next;
+};
+
+#define MIDI_NOTE_OFF                  8
+#define MIDI_NOTE_ON                   9
+#define MIDI_NOTE_AFTERTOUCH   10
+#define MIDI_CONTROLLER                        11
+#define MIDI_PROG_CHANGE               12
+#define MIDI_CHAN_AFTERTOUCH   13
+#define MIDI_PITCH_BEND                        14
+
+#define MIDI_NOTE_NUM(ev)      ((ev)->arg[0])
+#define MIDI_NOTE_VEL(ev)      ((ev)->arg[1])
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct midi *midi_load(const char *fname);
+void midi_free(struct midi *midi);
+
+int midi_num_tracks(struct midi *midi);
+struct midi_track *midi_track(struct midi *midi, int idx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* MIDI_H_ */
diff --git a/tools/mididump/mididump.c b/tools/mididump/mididump.c
new file mode 100644 (file)
index 0000000..5502ed6
--- /dev/null
@@ -0,0 +1,106 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "midi.h"
+
+unsigned int calc_reload(int note);
+
+int main(int argc, char **argv)
+{
+       int i, chan = -1;
+       struct midi *midi;
+       struct midi_event *ev;
+       long ticks;
+
+       if(!argv[1]) {
+               fprintf(stderr, "pass the path to a midi file\n");
+               return 1;
+       }
+       if(argv[2]) {
+               if((chan = atoi(argv[2])) < 0 || chan > 127) {
+                       fprintf(stderr, "invalid channel: %d\n", chan);
+                       return 1;
+               }
+       }
+
+       if(!(midi = midi_load(argv[1]))) {
+               fprintf(stderr, "failed to load midi file: %s\n", argv[1]);
+               return 1;
+       }
+
+       if(chan < 0) {
+               printf("midi file: %s\n", argv[1]);
+               if(midi->ppqn > 0) {
+                       printf("  pulses per quarter-note: %d\n", midi->ppqn);
+               }
+               if(midi->fps > 0) {
+                       printf("  fps: %d (ticks per frame: %d)\n", midi->fps, midi->ticks_per_frame);
+               }
+               printf("  tracks: %d\n", midi->num_tracks);
+               for(i=0; i<midi->num_tracks; i++) {
+                       if(midi->tracks[i].name) {
+                               printf("  %d - \"%s\":", i, midi->tracks[i].name);
+                       } else {
+                               printf("  %d:", i);
+                       }
+                       printf(" %d events\n", midi->tracks[i].num_ev);
+               }
+               midi_free(midi);
+               return 0;
+       }
+
+       if(chan >= midi->num_tracks) {
+               fprintf(stderr, "invalid track: %d (file has %d tracks)\n", chan, midi->num_tracks);
+               midi_free(midi);
+               return 1;
+       }
+
+       ticks = 0;
+       ev = midi->tracks[chan].head;
+       while(ev) {
+               ticks += ev->dt;
+               switch(ev->type) {
+               case MIDI_NOTE_ON:
+                       /*printf("%ld: %d (%d)\n", ticks, MIDI_NOTE_NUM(ev), MIDI_NOTE_VEL(ev));*/
+                       if(ticks >= 0) {
+                               printf("\tdw %ld, %u\n", ticks, calc_reload(MIDI_NOTE_NUM(ev)));
+                       }
+                       break;
+
+               case MIDI_NOTE_OFF:
+                       /*printf("%ld: off\n", ticks);*/
+                       if(ticks >= 0) {
+                               printf("\tdw %ld, 0\n", ticks);
+                       }
+                       break;
+
+               default:
+                       break;
+               }
+               ev = ev->next;
+       }
+
+       midi_free(midi);
+       return 0;
+}
+
+
+static float note_freq[] = {
+       27.500, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249,
+       48.999, 51.913, 55.000, 58.270, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407,
+       87.307, 92.499, 97.999, 103.83, 110.00, 116.54, 123.47, 130.81, 138.59, 146.83,
+       155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94, 261.63,
+       277.18, 293.67, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16,
+       493.88, 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 739.99, 783.99, 830.61,
+       880.00, 932.33, 987.77, 1046.5, 1108.7, 1174.7, 1244.5, 1318.5, 1396.9, 1480.0,
+       1568.0, 1661.2, 1760.0, 1864.7, 1975.5, 2093.0, 2217.5, 2349.3, 2489.0, 2637.0,
+       2793.0, 2960.0, 3136.0, 3322.4, 3520.0, 3729.3, 3951.1, 4186.0
+};
+
+#define OSC    1193182
+
+unsigned int calc_reload(int note)
+{
+       if(note < 21 || note > 108) return 0;
+
+       return (int)(OSC / note_freq[note - 21] + 0.5f);
+}