├── jazz.mid ├── bohemian.mid ├── gmstriving.mid ├── Makefile ├── README.md ├── techref ├── MIDI-MT32 ├── MIDI-GM └── MIDI-FORMAT ├── patchdump.c ├── readmidi.c ├── loadsf2.c ├── playmidi.1 ├── coremidi.c ├── gmvoices.h ├── playevents.c ├── alsamidi.c ├── playmidi.c ├── soundfont2.h ├── io_ncurses.c ├── playmidi.h ├── COPYING └── emumidi.c /jazz.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nlaredo/playmidi/HEAD/jazz.mid -------------------------------------------------------------------------------- /bohemian.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nlaredo/playmidi/HEAD/bohemian.mid -------------------------------------------------------------------------------- /gmstriving.mid: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nlaredo/playmidi/HEAD/gmstriving.mid -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifeq ($(shell uname -s),Darwin) 2 | INCLUDES = -I/Library/Frameworks/SDL2.framework/Headers -F/Library/Frameworks 3 | SDL = -F/Library/Frameworks -framework SDL2 4 | MIDIDEP = coremidi.o 5 | MIDILIB = -framework CoreMIDI -framework CoreFoundation 6 | else 7 | INCLUDES = $(shell sdl2-config --cflags) 8 | SDL = $(shell sdl2-config --libs) 9 | MIDIDEP = alsamidi.o 10 | MIDILIB = -lasound 11 | endif 12 | CFLAGS = -O2 -Wall -Wno-multichar -g $(INCLUDES) 13 | TFLAGS = -DTEST_TARGET 14 | LDFLAGS = -lm -lncurses $(SDL) $(MIDILIB) 15 | CC = gcc 16 | 17 | PROG = playmidi 18 | 19 | DEPS = playmidi.o 20 | DEPS += loadsf2.o 21 | DEPS += emumidi.o 22 | DEPS += playmidi.o 23 | DEPS += readmidi.o 24 | DEPS += playevents.o 25 | DEPS += io_ncurses.o 26 | DEPS += $(MIDIDEP) 27 | 28 | TESTS = loadsf2-test patchdump-test 29 | 30 | all: $(PROG) $(TESTS) 31 | 32 | #emumidi-test: loadsf2.o 33 | 34 | %-test: %.c 35 | $(CC) $(TFLAGS) $(CFLAGS) $^ -o $@ $(LDFLAGS) 36 | 37 | $(PROG): $(DEPS) 38 | $(CC) $^ -o $@ $(CFLAGS) $(LDFLAGS) 39 | 40 | clean: 41 | rm -f $(PROG) $(TESTS) $(DEPS) 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # playmidi 2 | Playmidi is a web and curses and SDL-based MIDI file player for Linux and MacOS and Chrome (via Web MIDI and Web Audio apis). The live version for use in chrome is found at https://nlaredo.github.io/playmidi.html 3 | 4 | Play Roland Integra-7 demo, Wormhole (from Roland 1993): 5 | 6 | [![Roland Integra-7 demo, Wormhole (from Roland 1993)](https://img.youtube.com/vi/ZviJcNXnBZo/mqdefault.jpg)](https://www.youtube.com/watch?v=ZviJcNXnBZo) 7 | 8 | It supports software rendering of midi files via SDL audio and can also output midi events to external midi devices (in time with SDL audio soft synth) via both alsa sequencer api (linux) and coremidi api (osx). 9 | 10 | Math-based synthesis (when no sf2 is loaded and no external midi output is set) is planned to evolve over time to support most of the features found in a minimoog voyager, and wavetable based sf2 support is expected to evolve to properly emulate all the features of a roland sc88 (but not there yet). 11 | 12 | Not all sf2 files are yet fully supported, but currently most development in the git tree has happened with one you can find via google search called Scc1t2.sf2. It provides a good starting point for those without external midi hardware 13 | 14 | [Discord](https://discord.gg/EUfcrzb) 15 | -------------------------------------------------------------------------------- /techref/MIDI-MT32: -------------------------------------------------------------------------------- 1 | Roland MT-32, LAPC-1 or CM-32L 2 | 0 Acou Piano 1 3 | 1 Acou Piano 2 4 | 2 Acou Piano 3 5 | 3 Elec Piano 1 6 | 4 Elec Piano 2 7 | 5 Elec Piano 3 8 | 6 Elec Piano 4 9 | 7 Honkytonk 10 | 8 Elec Org 1 11 | 9 Elec Org 2 12 | 10 Elec Org 3 13 | 11 Elec Org 4 14 | 12 Pipe Org 1 15 | 13 Pipe Org 2 16 | 14 Pipe Org 3 17 | 15 Accordion 18 | 16 Harpsi 1 19 | 17 Harpsi 2 20 | 18 Harpsi 3 21 | 19 Clavi 1 22 | 20 Clavi 2 23 | 21 Clavi 3 24 | 22 Celesta 1 25 | 23 Celesta 2 26 | 24 Syn Brass 1 27 | 25 Syn Brass 2 28 | 26 Syn Brass 3 29 | 27 Syn Brass 4 30 | 28 Syn Bass 1 31 | 29 Syn Bass 2 32 | 30 Syn Bass 3 33 | 31 Syn Bass 4 34 | 32 Fantasy 35 | 33 Harmo Pan 36 | 34 Chorale 37 | 35 Glasses 38 | 36 Soundtrack 39 | 37 Atmosphere 40 | 38 Warm Bell 41 | 39 Funny Vox 42 | 40 Echo Bell 43 | 41 Ice Rain 44 | 42 Oboe 2001 45 | 43 Echo Pan 46 | 44 Doctor Solo 47 | 45 School Daze 48 | 46 Bellsinger 49 | 47 Square Wave 50 | 48 Str Sect 1 51 | 49 Str Sect 2 52 | 50 Str Sect 3 53 | 51 Pizzicato 54 | 52 Violin 1 55 | 53 Violin 2 56 | 54 Cello 1 57 | 55 Cello 2 58 | 56 Contrabass 59 | 57 Harp 1 60 | 58 Harp 2 61 | 59 Guitar 1 62 | 60 Guitar 2 63 | 61 Elec Gtr 1 64 | 62 Elec Gtr 2 65 | 63 Sitar 66 | 64 Acou Bass 1 67 | 65 Acou Bass 2 68 | 66 Elec Bass 1 69 | 67 Elec Bass 2 70 | 68 Slap Bass 1 71 | 69 Slap Bass 2 72 | 70 Fretless 1 73 | 71 Fretless 2 74 | 72 Flute 1 75 | 73 Flute 2 76 | 74 Piccolo 1 77 | 75 Piccolo 2 78 | 76 Recorder 79 | 77 Pan Pipes 80 | 78 Sax 1 81 | 79 Sax 2 82 | 80 Sax 3 83 | 81 Sax 4 84 | 82 Clarinet 1 85 | 83 Clarinet 2 86 | 84 Oboe 87 | 85 Engl Horn 88 | 86 Bassoon 89 | 87 Harmonica 90 | 88 Trumpet 1 91 | 89 Trumpet 2 92 | 90 Trombone 1 93 | 91 Trombone 2 94 | 92 Fr Horn 1 95 | 93 Fr Horn 2 96 | 94 Tuba 97 | 95 Brs Sect 1 98 | 96 Brs Sect 2 99 | 97 Vibe 1 100 | 98 Vibe 2 101 | 99 Syn Mallet 102 | 100 Windbell 103 | 101 Glock 104 | 102 Tube Bell 105 | 103 Xylophone 106 | 104 Marimba 107 | 105 Koto 108 | 106 Sho 109 | 107 Shakuhachi 110 | 108 Whistle 1 111 | 109 Whistle 2 112 | 110 Bottleblow 113 | 111 Breathpipe 114 | 112 Timpani 115 | 113 Melodic Tom 116 | 114 Deep Snare 117 | 115 Elec Perc 1 118 | 116 Elec Perc 2 119 | 117 Taiko 120 | 118 Taiko Rim 121 | 119 Cymbal 122 | 120 Castanets 123 | 121 Triangle 124 | 122 Orche Hit 125 | 123 Telephone 126 | 124 Bird Tweet 127 | 125 One Note Jam 128 | 126 Water Bell 129 | 127 Jungle Tune 130 | -------------------------------------------------------------------------------- /patchdump.c: -------------------------------------------------------------------------------- 1 | /* patchdump.c - split apart a patch dump from roland sysex reply 2 | * 3 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 4 | * 5 | * This file may be freely distributed under the terms of 6 | * the GNU General Public Licence (GPL). 7 | * 8 | */ 9 | 10 | #include "SDL2/SDL.h" 11 | #include "SDL2/SDL_audio.h" 12 | 13 | #include 14 | #include 15 | 16 | /* parse dump from 0xf0 to 0xf7, return pointer to just after last byte */ 17 | /* 18 | patches: 0c 00 01 19 | 0000000: f0 41 10 42 12 0c 00 01 00 01 00 00 50 69 61 6e .A.B........Pian 20 | 0000010: 6f 20 31 20 20 20 20 20 00 01 01 00 50 69 61 6e o 1 ....Pian 21 | drums: 0c 00 02 22 | 0000000: f0 41 10 42 12 0c 00 02 00 01 00 00 53 54 41 4e .A.B........STAN 23 | 0000010: 44 41 52 44 20 20 20 20 00 01 08 00 52 4f 4f 4d DARD ....ROOM 24 | druminst: 0c 00 03 25 | *todo* 26 | */ 27 | 28 | Uint8 patch_prefix[] = { 0xf0, 0x41, 0x10, 0x42, 0x12, 0x0c, 0x00, 0x01 }; 29 | Uint8 drums_prefix[] = { 0xf0, 0x41, 0x10, 0x42, 0x12, 0x0c, 0x00, 0x02 }; 30 | Uint8 dinst_prefix[] = { 0xf0, 0x41, 0x10, 0x42, 0x12, 0x0c, 0x00, 0x03 }; 31 | typedef enum { 32 | DtUnknown, 33 | DtPatch, 34 | DtDrums, 35 | DtDInst, 36 | } dump_type; 37 | 38 | Uint8 *parse_dump(Uint8 *buf) 39 | { 40 | dump_type type = DtUnknown; 41 | if (memcmp(buf, patch_prefix, sizeof(patch_prefix)) == 0) { 42 | type = DtPatch; 43 | buf += sizeof(patch_prefix); 44 | } else if (memcmp(buf, drums_prefix, sizeof(drums_prefix)) == 0) { 45 | type = DtDrums; 46 | buf += sizeof(drums_prefix); 47 | } else if (memcmp(buf, dinst_prefix, sizeof(dinst_prefix)) == 0) { 48 | type = DtDInst; 49 | buf += sizeof(dinst_prefix); 50 | } else { 51 | fprintf(stderr, "magic bytes not found\n"); 52 | exit(1); 53 | } 54 | while (buf[1] == 1 || buf[1] == 2) { /* only 2 valid maps */ 55 | int cc0 = buf[0], map = buf[1], pc = buf[2], key = buf[3]; 56 | char *name = (char *)&buf[4]; 57 | buf += 16; 58 | if (map == 1) 59 | cc0 += 128; 60 | printf("{ %3d /*pgm*/, %3d /*bank*/, %3d /*key*/, " 61 | "\"%.12s\" },\n", pc, cc0, key, name); 62 | } 63 | return buf + 2; 64 | } 65 | 66 | /* load patchdump into memory */ 67 | void load_dump(char *filename) 68 | { 69 | SDL_RWops *rw = SDL_RWFromFile(filename, "r"); 70 | Uint8 *buf = NULL, *cur; 71 | Sint64 size; 72 | 73 | if (!rw) { 74 | perror(filename); 75 | return; 76 | } 77 | size = SDL_RWsize(rw); 78 | if (!(buf = malloc(size))) { 79 | perror("malloc"); 80 | return; 81 | } 82 | if (SDL_RWread(rw, buf, size, 1) < 1) { 83 | /* done reading the file, or read in error */ 84 | free(buf); 85 | return; 86 | } 87 | SDL_RWclose(rw); 88 | cur = buf; 89 | while (cur < buf + size) { 90 | cur = parse_dump(cur); 91 | } 92 | } 93 | 94 | #ifdef TEST_TARGET 95 | /* stand alone testing of above file parsing */ 96 | int main(int argc, char **argv) 97 | { 98 | if (argc > 1) { 99 | load_dump(argv[1]); 100 | //signal (SIGTRAP, SIG_IGN); // if not debugging, ignore the int3 101 | //asm volatile ("int3"); 102 | } 103 | exit(0); 104 | } 105 | #endif 106 | -------------------------------------------------------------------------------- /techref/MIDI-GM: -------------------------------------------------------------------------------- 1 | General MIDI 2 | 0 Acoustic Grand Piano 3 | 1 Bright Acoustic Piano 4 | 2 Electric Grand Piano 5 | 3 Honky-Tonk Piano 6 | 4 Rhodes Piano 7 | 5 Chorused Piano 8 | 6 Harpsichord 9 | 7 Clavinet 10 | 8 Celesta 11 | 9 Glockenspiel 12 | 10 Music Box 13 | 11 Vibraphone 14 | 12 Marimba 15 | 13 Xylophone 16 | 14 Tubular Bells 17 | 15 Dulcimer 18 | 16 Hammond Organ 19 | 17 Percussive Organ 20 | 18 Rock Organ 21 | 19 Church Organ 22 | 20 Reed Organ 23 | 21 Accordion 24 | 22 Harmonica 25 | 23 Tango Accordion 26 | 24 Acoustic Guitar (nylon) 27 | 25 Acoustic Guitar (steel) 28 | 26 Electric Guitar (jazz) 29 | 27 Electric Guitar (clean) 30 | 28 Electric Guitar (muted) 31 | 29 Overdriven Guitar 32 | 30 Distortion Guitar 33 | 31 Guitar Harmonics 34 | 32 Acoustic Bass 35 | 33 Electric Bass (finger) 36 | 34 Electric Bass (pick) 37 | 35 Fretless Bass 38 | 36 Slap Bass 1 39 | 37 Slap Bass 2 40 | 38 Synth Bass 1 41 | 39 Synth Bass 2 42 | 40 Violin 43 | 41 Viola 44 | 42 Cello 45 | 43 Contrabass 46 | 44 Tremolo Strings 47 | 45 Pizzicato Strings 48 | 46 Orchestral Harp 49 | 47 Timpani 50 | 48 String Ensemble 1 51 | 49 String Ensemble 2 52 | 50 SynthStrings 1 53 | 51 SynthStrings 2 54 | 52 Choir Aahs 55 | 53 Voice Oohs 56 | 54 Synth Voice 57 | 55 Orchestra Hit 58 | 56 Trumpet 59 | 57 Trombone 60 | 58 Tuba 61 | 59 Muted Trumpet 62 | 60 French Horn 63 | 61 Brass Section 64 | 62 Synth Brass 1 65 | 63 Synth Brass 2 66 | 64 Soprano Sax 67 | 65 Alto Sax 68 | 66 Tenor Sax 69 | 67 Baritone Sax 70 | 68 Oboe 71 | 69 English Horn 72 | 70 Bassoon 73 | 71 Clarinet 74 | 72 Piccolo 75 | 73 Flute 76 | 74 Recorder 77 | 75 Pan Flute 78 | 76 Blown Bottle 79 | 77 Skakuhachi 80 | 78 Whistle 81 | 79 Ocarina 82 | 80 Lead 1 (square) 83 | 81 Lead 2 (sawtooth) 84 | 82 Lead 3 (calliope lead) 85 | 83 Lead 4 (chiff lead) 86 | 84 Lead 5 (charang) 87 | 85 Lead 6 (voice) 88 | 86 Lead 7 (fifths) 89 | 87 Lead 8 (brass + lead) 90 | 88 Pad 1 (new age) 91 | 89 Pad 2 (warm) 92 | 90 Pad 3 (polysynth) 93 | 91 Pad 4 (choir) 94 | 92 Pad 5 (bowed) 95 | 93 Pad 6 (metallic) 96 | 94 Pad 7 (halo) 97 | 95 Pad 8 (sweep) 98 | 96 FX 1 (rain) 99 | 97 FX 2 (soundtrack) 100 | 98 FX 3 (crystal) 101 | 99 FX 4 (atmosphere) 102 | 100 FX 5 (brightness) 103 | 101 FX 6 (goblins) 104 | 102 FX 7 (echoes) 105 | 103 FX 8 (sci-fi) 106 | 104 Sitar 107 | 105 Banjo 108 | 106 Shamisen 109 | 107 Koto 110 | 108 Kalimba 111 | 109 Bagpipe 112 | 110 Fiddle 113 | 111 Shanai 114 | 112 Tinkle Bell 115 | 113 Agogo 116 | 114 Steel Drums 117 | 115 Woodblock 118 | 116 Taiko Drum 119 | 117 Melodic Tom 120 | 118 Synth Drum 121 | 119 Reverse Cymbal 122 | 120 Guitar Fret Noise 123 | 121 Breath Noise 124 | 122 Seashore 125 | 123 Bird Tweet 126 | 124 Telephone Ring 127 | 125 Helicopter 128 | 126 Applause 129 | 127 Gunshot 130 | -------------------------------------------------------------------------------- /techref/MIDI-FORMAT: -------------------------------------------------------------------------------- 1 | MIDI File Format 2 | ================ 3 | 4 | By Nathan Laredo, last revision: 25 August 1996 5 | 6 | NOTE: THIS IS NOT THE OFFICIAL MIDI FILE FORMAT SPECIFICATION. THESE 7 | ARE PERSONAL NOTES I WROTE WHEN I FIRST STARTED TO WRITE MY OWN MIDI 8 | FILE READING ROUTINES. THIS INFORMATION IS PROVIDED IN THE HOPE THAT 9 | IT MAY SAVE SOMEONE ELSE THE AMOUNT OF RESEARCH THAT WENT INTO IT. I 10 | MAKE NO GUARANTEES OR WARRANTIES PERTAINING TO USEFULNESS OR ACCURACY. 11 | 12 | ----------------------------------------------------------------------- 13 | (MThd = 0x4d546864) 14 | (32-bit big endian length = 6) (16-bit big endian format) 15 | (16-bit big endian tracks) (16-bit big-endian division) 16 | 17 | format is either 0 - one track, 1 - many tracks, one sequence 18 | or 2 - many tracks with one sequence per track. 19 | format 1 files should have all tempo changes in the first track 20 | 21 | (MTrk = 0x4d54726b) 22 | (32-bit big endian length of track) 23 | 24 | (variable-length encoded ticks since previous event) 25 | (Event data -- see below) 26 | 27 | ticks and event data are repeated for length bytes. 28 | 29 | MTrk block is repeated for as many tracks as indicated in header. 30 | ------------------------------------------------------------------------ 31 | Variable length quantities are a series of 7 bit values encoded from 32 | msb to lsb, with the lsb bit 7 clear, all prior have bit 7 set. 33 | ------------------------------------------------------------------------ 34 | Event data is either midi data (with running status) that is to be 35 | sent to the midi device, a sysex (two types), or a meta-event 36 | ------------------------------------------------------------------------- 37 | A sysex event contains information to be sent directly to the midi synth. 38 | It consists of (0xf0) (variable-length encoded length) (data after 0xf0) 39 | or (0xf7) (variable-length encoded length) (all data to be sent) 40 | Sysex messages may or may not be terminated with 0xf7 (EOX), EOX should 41 | not be added automagically when messages are output to a midi synth, as 42 | there may be a following 0xf7-type sysex after a delay that may be 43 | required by the synth. The 0xf7-type sysex may contain data > 0x7f so 44 | programs shouldn't stop sending when a byte value is > 0x7f. 45 | ------------------------------------------------------------------------- 46 | A meta event contaions information such as text, tempo, and key signature. 47 | It consists of (0xff) (type) (variable length encoded deta length) (data) 48 | Text is not usually null-terminated. You must depend on the length 49 | when reading it. 50 | 51 | meta event types range from 0x00 to 0x7f 52 | Type Contents 53 | ==== ======== 54 | 0x00: (16-bit big endian sequence number) 55 | 0x01: (any text) 56 | 0x02: (Copyright Message text) 57 | 0x03: (Sequence/Track Name text) 58 | 0x04: (Instrument Name text) 59 | 0x05: (Lyric text) 60 | 0x06: (Marker text) 61 | 0x07: (Cue-point text) 62 | 0x2f: End of Track -- no data for this type 63 | 0x51: (24-bit big endian tempo) -- microseconds per midi quarter note 64 | 0x54: (hour) (min) (second) (frame) (fractional-frame) - SMPTE track start 65 | 0x58: (numerator) (denominator) (clocks per metronome click) 66 | (32nd notes notated per midi quarter note) -- Time Signature 67 | denominator is a power of two, ie 0x03 = 2^3 = 8 68 | 0x59: (sharps/flats) (major/minor flag) -- Key Signature 69 | if sharp key, first byte represents number of sharps 70 | if flat key, first byte represents negative number of flats 71 | the major/minor flag is 0 if minor, 1 if major. 72 | 0x7f: (Sequencer Specific data) -- I've never used this type, I ignore it. 73 | I have never seen it used, and If someone would let me know why it 74 | exists, I would appreciate it. 75 | ----------------------------------------------------------------------------- 76 | Many thanks to the authors of the many free midi references available on 77 | ftp.ucsd.edu that I used in creating this file format specification which 78 | I use for my Playmidi package for Linux. 79 | 80 | If you find any errors in this document, please email me. 81 | 82 | Nathan Laredo -- laredo@gnu.ai.mit.edu 83 | 84 | -- 85 | (C)1995, 1996 Nathan Laredo 86 | This document may be freely distributed in its original form. 87 | -------------------------------------------------------------------------------- /readmidi.c: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | readmidi.c -- last change: 25 Jan 2015 3 | 4 | Creates an array of pointers to each chunk in a midi file. 5 | ENTIRE MIDI FILE IS RETAINED IN MEMORY so that no additional malloc 6 | calls need be made to store the data of the events in the midi file. 7 | 8 | Copyright 2015 Nathan I. Laredo 9 | 10 | This program is modifiable/redistributable under the terms 11 | of the GNU General Public Licence. 12 | 13 | You should have received a copy of the GNU General Public License 14 | along with this program; if not, write to the Free Software 15 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 16 | *************************************************************************/ 17 | #include "playmidi.h" 18 | #include "SDL2/SDL.h" 19 | 20 | int format, ntrks, division; 21 | unsigned char *midifilebuf; 22 | 23 | extern struct miditrack seq[MAXTRKS]; 24 | extern int find_header; 25 | extern unsigned long int default_tempo; 26 | 27 | unsigned short Read16() 28 | { 29 | register unsigned short x; 30 | 31 | x = (*(midifilebuf) << 8) | midifilebuf[1]; 32 | midifilebuf += 2; 33 | return x; 34 | } 35 | 36 | unsigned long Read32() 37 | { 38 | register unsigned long x; 39 | 40 | x = (*(midifilebuf) << 24) | (midifilebuf[1] << 16) | 41 | (midifilebuf[2] << 8) | midifilebuf[3]; 42 | midifilebuf += 4; 43 | return x; 44 | } 45 | 46 | int readmidi(filebuf, filelength) 47 | unsigned char *filebuf; 48 | off_t filelength; 49 | { 50 | unsigned long int i = 0, track, tracklen; 51 | 52 | midifilebuf = filebuf; 53 | /* allow user to specify header number in from large archive */ 54 | while (i != find_header && midifilebuf < (filebuf + filelength - 32)) { 55 | if (strncmp((char *)midifilebuf, "MThd", 4) == 0) { 56 | i++; 57 | midifilebuf += 4; 58 | } else 59 | midifilebuf++; 60 | } 61 | if (i != find_header) { /* specified header was not found */ 62 | midifilebuf = filebuf; 63 | return find_header = 0; 64 | } 65 | if (midifilebuf != filebuf) 66 | midifilebuf -= 4; 67 | i = Read32(); 68 | if (i == RIFF) { 69 | midifilebuf += 16; 70 | i = Read32(); 71 | } 72 | if (i == MThd) { 73 | tracklen = Read32(); 74 | format = Read16(); 75 | ntrks = Read16(); 76 | division = Read16(); 77 | } else if (i == CTMF) { 78 | /* load a creative labs CMF file, with instruments for fm */ 79 | tracklen = midifilebuf[4] | (midifilebuf[5] << 8); 80 | format = 0; 81 | ntrks = 1; 82 | division = midifilebuf[6] | (midifilebuf[7] << 8); 83 | default_tempo = 1000000 * division / 84 | (midifilebuf[8] | (midifilebuf[9] << 8)); 85 | seq[0].data = filebuf + tracklen; 86 | seq[0].length = filelength - tracklen; 87 | i = (unsigned long int) (*(short *) &midifilebuf[2]) - 4; 88 | return ntrks; 89 | } else { 90 | int found = 0; 91 | while (!found && midifilebuf < (filebuf + filelength - 8)) 92 | if (strncmp((char *)midifilebuf, "MThd", 4) == 0) 93 | found++; 94 | else 95 | midifilebuf++; 96 | if (found) { 97 | midifilebuf += 4; 98 | tracklen = Read32(); 99 | format = Read16(); 100 | ntrks = Read16(); 101 | division = Read16(); 102 | } else { 103 | #ifndef DISABLE_RAW_MIDI_FILES 104 | /* this allows playing ANY file, so watch out */ 105 | midifilebuf -= 4; 106 | format = 0; /* assume it's .mus file ? */ 107 | ntrks = 1; 108 | division = 40; 109 | #else 110 | return -1; 111 | #endif 112 | } 113 | } 114 | if (ntrks > MAXTRKS) { 115 | fprintf(stderr, "\nWARNING: %d TRACKS IGNORED!\n", ntrks - MAXTRKS); 116 | ntrks = MAXTRKS; 117 | } 118 | for (track = 0; track < ntrks; track++) { 119 | if (Read32() != MTrk) { 120 | /* MTrk isn't where it's supposed to be, search rest of file */ 121 | int fuzz, found = 0; 122 | midifilebuf -= 4; 123 | if (strncmp((char *)midifilebuf, "MThd", 4) == 0) 124 | continue; 125 | else { 126 | if (!track) { 127 | seq[0].length = filebuf + filelength - midifilebuf; 128 | seq[0].data = midifilebuf; 129 | continue; /* assume raw midi data file */ 130 | } 131 | midifilebuf -= seq[track - 1].length; 132 | for (fuzz = 0; (fuzz + midifilebuf) < 133 | (filebuf + filelength - 8) && !found; fuzz++) 134 | if (strncmp((char *)&midifilebuf[fuzz], "MTrk", 4) == 0) 135 | found++; 136 | seq[track - 1].length = fuzz; 137 | midifilebuf += fuzz; 138 | if (!found) 139 | continue; 140 | } 141 | } 142 | tracklen = Read32(); 143 | if (midifilebuf + tracklen > filebuf + filelength) 144 | tracklen = filebuf + filelength - midifilebuf; 145 | seq[track].length = tracklen; 146 | seq[track].data = midifilebuf; 147 | midifilebuf += tracklen; 148 | } 149 | ntrks = track; 150 | return ntrks; 151 | } 152 | -------------------------------------------------------------------------------- /loadsf2.c: -------------------------------------------------------------------------------- 1 | /* loadsf2.c - load/split a sf2 riff file into component chunks 2 | * 3 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 4 | * 5 | * This file may be freely distributed under the terms of 6 | * the GNU General Public Licence (GPL). 7 | * 8 | * CFLAGS += -Wno-multichar 9 | */ 10 | 11 | #include "SDL2/SDL.h" 12 | #include "SDL2/SDL_audio.h" 13 | #include "soundfont2.h" 14 | 15 | #include 16 | #include 17 | 18 | struct sfSFBK sf2; /* pointers to everything loaded go here */ 19 | extern int verbose; 20 | 21 | /* for each tag value found, describe where to stick a pointer to the data */ 22 | struct fillSFBK { char *tag; void *dest; }; 23 | struct fillSFBK filldata[] = { 24 | { "ifil", &sf2.ifil }, { "isng", &sf2.isng }, { "INAM", &sf2.INAM }, 25 | { "irom", &sf2.irom }, { "iver", &sf2.iver }, { "ICRD", &sf2.ICRD }, 26 | { "IENG", &sf2.IENG }, { "IPRD", &sf2.IPRD }, { "ICOP", &sf2.ICOP }, 27 | { "ICMT", &sf2.ICMT }, { "ISFT", &sf2.ISFT }, { "smpl", &sf2.smpl }, 28 | { "sm24", &sf2.sm24 }, { "phdr", &sf2.phdr }, { "pbag", &sf2.pbag }, 29 | { "pmod", &sf2.pmod }, { "pgen", &sf2.pgen }, { "inst", &sf2.inst }, 30 | { "ibag", &sf2.ibag }, { "imod", &sf2.imod }, { "igen", &sf2.igen }, 31 | { "shdr", &sf2.shdr }, { NULL, NULL } 32 | }; 33 | 34 | static void fill_sf2(Uint32 tag, void *buf, Uint32 size) 35 | { 36 | int i; 37 | for (i = 0; filldata[i].tag != NULL && filldata[i].dest != NULL; i++) { 38 | if (*(Uint32 *)filldata[i].tag == tag) { 39 | memcpy(filldata[i].dest, &buf, sizeof(void *)); 40 | memcpy(sizeof(void *) + filldata[i].dest, &size, sizeof(Uint32)); 41 | return; 42 | } 43 | } 44 | fprintf(stderr, "Unhandled %.4s in sf2 file.\n", (char *)&tag); 45 | } 46 | 47 | static void parse_subchunk(struct riffChunk *parent, Uint32 level) 48 | { 49 | Uint32 offset = 4; /* skip the data tag */ 50 | struct riffChunk *buf = (struct riffChunk *)(parent->data + offset); 51 | 52 | if (level > 3) { 53 | fprintf(stderr, "ignored unexpectedly deep recursion in RIFF file\n"); 54 | return; 55 | } 56 | while (parent->len - offset >= sizeof(struct riffChunk)) { 57 | buf = (struct riffChunk *)(parent->data + offset); 58 | if (0) { 59 | fprintf(stderr, "%*sTAG = %.4s LEN = %d\n", 2 + level * 2, "", 60 | (char *)&buf->tag, buf->len); 61 | } 62 | if (buf->tag == SDL_SwapBE32('LIST')) { 63 | parse_subchunk(buf, level + 1); /* look for chunks inside this chunk */ 64 | } else { 65 | fill_sf2(buf->tag, &buf->data, buf->len); 66 | } 67 | offset += sizeof(struct riffChunk) + buf->len; 68 | } 69 | return; 70 | } 71 | 72 | /* load soundfont2 riff file into sf2 struct, return pointer to raw riff data */ 73 | struct riffChunk *load_sf2(char *filename) 74 | { 75 | SDL_RWops *rw = SDL_RWFromFile(filename, "r"); 76 | Uint32 tag, len; 77 | Sint64 size; 78 | struct riffChunk *buf = NULL; 79 | int error = 0; 80 | 81 | if (!rw) { 82 | if (verbose) 83 | perror(filename); 84 | return NULL; 85 | } 86 | size = SDL_RWsize(rw); 87 | /* at the top level there should only be one chunk */ 88 | /* but loop anyway in case someone concatenated riff files */ 89 | while (size >= sizeof(struct riffChunk)) { 90 | tag = SDL_ReadLE32(rw); 91 | len = SDL_ReadLE32(rw); 92 | if (!(buf = malloc(sizeof(struct riffChunk) + len))) { 93 | perror("malloc"); 94 | return NULL; 95 | } 96 | buf->tag = tag; 97 | buf->len = len; 98 | if (SDL_RWread(rw, buf->data, len, 1) < 1) { 99 | /* done reading the file, or read in error */ 100 | free(buf); 101 | return NULL; 102 | } 103 | if (0) { 104 | fprintf(stderr, "TAG = %.4s LEN = %d\n", (char *)&buf->tag, buf->len); 105 | } 106 | if (*(Uint32 *)buf->data == SDL_SwapBE32('sfbk')) { 107 | parse_subchunk(buf, 0); /* look for chunks inside this chunk */ 108 | } 109 | size -= sizeof(struct riffChunk) + buf->len; 110 | }; 111 | SDL_RWclose(rw); 112 | if (sf2.phdr_size < sizeof(struct sfPresetHeader) * 2) { error++; } 113 | if (sf2.pbag_size < sizeof(struct sfPresetBag) * 2) { error++; } 114 | if (sf2.pgen_size < sizeof(struct sfGenList) * 2) { error++; } 115 | if (sf2.inst_size < sizeof(struct sfInst) * 2) { error++; } 116 | if (sf2.igen_size < sizeof(struct sfInstGenList) * 2) { error++; } 117 | if (sf2.shdr_size < sizeof(struct sfSample) * 2) { error++; } 118 | if (error) { 119 | fprintf(stderr, "%s: malformed sf2 file, ignoring\n", filename); 120 | memset(&sf2, 0, sizeof(sf2)); 121 | free(buf); 122 | return NULL; 123 | } 124 | return buf; 125 | } 126 | 127 | #ifdef TEST_TARGET 128 | int verbose = 1; 129 | /* stand alone testing of above file parsing */ 130 | int main(int argc, char **argv) 131 | { 132 | if (argc > 1) { 133 | load_sf2(argv[1]); 134 | signal (SIGTRAP, SIG_IGN); // if not debugging, ignore the int3 135 | asm volatile ("int3"); 136 | } 137 | exit(0); 138 | } 139 | #endif 140 | -------------------------------------------------------------------------------- /playmidi.1: -------------------------------------------------------------------------------- 1 | .Dd 1 August 1994 (Modified 5 March 2015) 2 | .Dt PLAYMIDI 1 3 | .Os "Linux, OSX" 4 | .Sh NAME 5 | .Nm playmidi 6 | .Nd midi file player 7 | .Sh SYNOPSIS 8 | .Nm playmidi 9 | .Op Fl vblicxpVtdPeDhEzMIRCr 10 | .Op Ar 11 | .Sh DESCRIPTION 12 | .Nm playmidi 13 | is a full-featured midi file player for all systems using 14 | sdl2.0 and later for soft synth rendering (using sf2 or math 15 | if no sf2 file found or specified) and supports external midi output 16 | on both Linux (via ALSA) and OSX (via CoreMidi) with more 17 | platforms (including Windows, iOS, Android) in a future release. 18 | When no options are specified, 19 | .Nm playmidi 20 | will give a summary of all command line options. 21 | If more than one file is specified, you can use 22 | -r mode for interactive control, allowing 23 | you to skip to the previous song, next song, speed up 24 | or slow down the midi file, or repeat a midi file while viewing 25 | a real-time display of data in the midi file. 26 | .Sh OPTIONS 27 | Command line options are described below. 28 | (make sure to precede them with a dash (``-'')) 29 | .Bl -hang -width 8 30 | .It Fl c# 31 | 32 | set the channel mask (in hexidecimal) of which channels to 33 | play from the midi file. This is useful if you have a midi file 34 | with some channels that don't sound very good on your hardware. 35 | .It Fl d 36 | 37 | ignore any drum (percussion) tracks in a midi file. 38 | This is useful for non-sf2 soft synth or hardware where percussion 39 | sounds especially bad. Also useful for midi files where the 40 | percussion is poorly written. 41 | .It Fl e 42 | 43 | send output to external midi. This is what you'll want to do 44 | if you have any midi hardware connected to your system. The 45 | software synth is the default when this is not specified. 46 | .It Fl E# 47 | 48 | set mask of channels to always output to external midi. If you want 49 | to use more than one playback device, this option allows you to specify 50 | what channels to send to the external midi port. For example, 51 | 00FF would send channels 1 - 8 to external midi. Channels not specified 52 | in this mask will be sent to the software synth (using sf2 if available, 53 | see -b option). 54 | .It Fl b 55 | filename 56 | 57 | set filename of the sf2 file to use for soft synth renderer. 58 | .It Fl D# 59 | 60 | select the external device number to ouput to for 61 | .Fl e 62 | or 63 | .Fl E 64 | options. 65 | You can use the 66 | .Fl l 67 | option to get a list of available devices. 68 | .It Fl p[chan,]prog[,chan,prog...] 69 | 70 | forces a given program number (1-128) to be used for all output 71 | on given channel, or if no channel is specified, program will 72 | be used for all channels. For example: 73 | .Fl p33 74 | sets all channels to program 33, 75 | .Fl p5,124 76 | sets just channel 5 to program 124, and 77 | .Fl p1,33,2,55,9,22,10,17 78 | sets channel 1 to program 33, channel 2 to program 55, channel 9 79 | to program 22, and channel 10 (percussive) to use the Power Drum Set. 80 | .It Fl t# 81 | 82 | skews tempo by a factor (float). This is good for files you think 83 | the author wrote too slow or two fast. Also good if you want to listen 84 | to lots of files at high-speeds, or play a file at slow speeds in order 85 | to learn to play a song on some instrument (like piano). 86 | .It Fl r 87 | 88 | real time ncurses terminal playback graphics tracking of all 89 | notes on each channel and the current playback clock. 90 | .It Fl Px,[x] 91 | 92 | treat channel x as percussion in soft renderer. This 93 | is useful if you have a file with percussion on multiple midi 94 | channels and support for the required sysex to set it up is missing. 95 | .It Fl R# 96 | 97 | set initial reverb level. Valid range is 0 - 127. 98 | For fm, the setting is either "on" (nonzero), or "off" (zero). 99 | .It Fl C# 100 | 101 | set initial chorus level. Valid range is 0 - 127. 102 | .It Fl Vchn,vel[,chn,vel...] 103 | 104 | set velocity for all notes in a channel. All velocity information 105 | for the given channel will be replaced by the given velocity. One 106 | day I'll change this option to allow all channels to be modified 107 | as with the 108 | .Fl p 109 | option. 110 | .It Fl x# 111 | 112 | excludes the given channel number from the mask of channels to 113 | load from the midi file. 114 | .It Fl z 115 | 116 | zero channel data in output stream -- for special applications. 117 | .Sh AUTHOR 118 | Nathan Laredo (laredo@gnu.org) 119 | .Sh HISTORY 120 | .Nm playmidi 121 | was originally designed out of impatience with other midi 122 | players. The startup time on modern hardware is effectively instant. 123 | The meaning of the various real-time displays is yet-to-be-documented. 124 | 125 | Playmidi 2.9+ is nearly a total re-write compared with the 1995 playmidi 126 | 2.5 release. 127 | .Sh BUGS 128 | Currently, playmidi without the 129 | .Fl r 130 | option is not supported properly. Please always use this option if you 131 | want to hear playback. 132 | 133 | Portamento is not yet implemented in the software synth. 134 | 135 | Some sf2 files will render poorly in the software renderer. It is 136 | highly recommended you search for the file Scc1t2.sf2 via Google and 137 | use that file with playmidi for software rendering as that was the 138 | one used during development and works the best (and so far gives the 139 | best soft synth experience). 140 | 141 | This is currently 142 | a work in progress after almost 20 years without a release, and expect 143 | more features to come as the 3.0 release approaches. Older features 144 | like OPL3 FM synth and Gravis Ultrasound and AWE32 support have been 145 | dropped in favor of the soft synth. Ultimately, there may be soft FM 146 | playback as well for nostalgia purposes. 147 | If you want new features or find undocumented 148 | ones (bugs), please email laredo@gnu.org. 149 | -------------------------------------------------------------------------------- /coremidi.c: -------------------------------------------------------------------------------- 1 | /* coremidi.c - abstract away os specific output routines for OSX/iOS 2 | * 3 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 4 | * 5 | * This file may be freely distributed under the terms of 6 | * the GNU General Public Licence (GPL). 7 | */ 8 | 9 | #include "playmidi.h" 10 | #include 11 | #include /* for mach_absolute_time() */ 12 | 13 | extern int ext_dev; 14 | 15 | /* the max size for packet buffer is 64KB, it could be smaller */ 16 | #define PKTBUF_SIZE 65536 17 | 18 | static MIDIClientRef midiclient = 0; 19 | static MIDIPortRef midiout = 0; 20 | static int midi_opened = 0; 21 | static MIDITimeStamp startstamp; 22 | static mach_timebase_info_data_t tinfo; 23 | static Byte pktbuffer[PKTBUF_SIZE]; 24 | extern Uint32 ticks; 25 | 26 | static void check_err(const char *msg, OSStatus err) 27 | { 28 | int i; 29 | Uint8 *e = (Uint8 *)&err; 30 | 31 | if (!err) { 32 | return; 33 | } 34 | /* check to see if the whole error code is four printable characters */ 35 | for (i = 0; i < 4; i++) { 36 | if (e[i] < 0x20 || e[i] > 0x7e) { 37 | break; 38 | } 39 | } 40 | if (i == 4) { /* if it's four printable characters, show the code */ 41 | fprintf(stderr, "%s: '%4.4s'\n", msg, (char *)e); 42 | } else { /* otherwise just show the numeric value */ 43 | fprintf(stderr, "%s: 0x%08x\n", msg, (Uint32)err); 44 | } 45 | exit(err); 46 | } 47 | 48 | /* Returns an allocated char * name of a given MIDIObjectRef, need to free */ 49 | static char *getObjName(MIDIObjectRef object) 50 | { 51 | CFStringRef name = nil; 52 | CFIndex length, maxSize; 53 | char *rv = NULL; 54 | if (noErr != MIDIObjectGetStringProperty(object, kMIDIPropertyName, &name)) 55 | return NULL; 56 | if (name == nil) 57 | return NULL; 58 | length = CFStringGetLength(name); 59 | maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8); 60 | rv = malloc(maxSize); 61 | if (rv == NULL) 62 | return NULL; 63 | if (CFStringGetCString(name, rv, maxSize, kCFStringEncodingUTF8)) 64 | return rv; 65 | free(rv); 66 | return NULL; 67 | } 68 | 69 | void show_ports(void) 70 | { 71 | ItemCount i, nDests = MIDIGetNumberOfDestinations(); 72 | 73 | printf("%-5s %s\n", "dev", "name"); 74 | for (i = 0 ; i < nDests ; i++) { 75 | MIDIEndpointRef dest = MIDIGetDestination(i); 76 | char *name = getObjName(dest); 77 | if (name != NULL) { 78 | printf("%-5lu %s\n", i, name); 79 | free(name); 80 | } else { 81 | printf("%-5lu %s\n", i, "(failed getting name)"); 82 | } 83 | } 84 | if (i == 0) { 85 | printf("[none]\n"); 86 | } 87 | } 88 | 89 | void init_midi(void) 90 | { 91 | OSStatus status; 92 | ItemCount nDests = MIDIGetNumberOfDestinations(); 93 | if (midi_opened) { 94 | return; 95 | } 96 | if (nDests == 0) { 97 | return; /* nothing to write to */ 98 | } 99 | if (ext_dev > nDests) { 100 | ext_dev = nDests - 1; 101 | } 102 | status = MIDIClientCreate(CFSTR(RELEASE), NULL, NULL, &midiclient); 103 | check_err("MIDIClientCreate", status); 104 | status = MIDIOutputPortCreate(midiclient, CFSTR("ext_dev"), &midiout); 105 | check_err("MIDIOutputPortCreate", status); 106 | if (mach_timebase_info(&tinfo) != KERN_SUCCESS) { 107 | tinfo.numer = 1; 108 | tinfo.denom = 1; 109 | } 110 | startstamp = mach_absolute_time(); 111 | midi_opened++; 112 | }; 113 | 114 | void close_midi(void) 115 | { 116 | OSStatus status; 117 | if (!midi_opened) 118 | return; 119 | status = MIDIPortDispose(midiout); 120 | check_err("MIDIPortDispose", status); 121 | status = MIDIClientDispose(midiclient); 122 | check_err("MIDIClientDispose", status); 123 | midi_opened = 0; 124 | } 125 | 126 | MIDITimeStamp msec_to_ts(Uint32 t) 127 | { 128 | MIDITimeStamp timestamp; 129 | timestamp = t; 130 | timestamp *= 1000000; 131 | timestamp *= tinfo.numer; 132 | timestamp /= tinfo.denom; 133 | return timestamp + startstamp; 134 | } 135 | 136 | void midi_add_pkt(struct midi_packet *p) 137 | { 138 | OSStatus status; 139 | // convert midi ticks to nanosecond timestamp 140 | MIDITimeStamp timestamp = msec_to_ts(ticks); 141 | MIDIPacketList *packetlist = (MIDIPacketList*)pktbuffer; 142 | MIDIPacket *currentpacket = MIDIPacketListInit(packetlist); 143 | currentpacket = MIDIPacketListAdd(packetlist, sizeof(pktbuffer), 144 | currentpacket, timestamp, p->len, p->data); 145 | status = MIDISend(midiout, MIDIGetDestination(ext_dev), packetlist); 146 | check_err("MIDISend", status); 147 | } 148 | 149 | #define SYSEX_SPLIT (PKTBUF_SIZE - 256) 150 | void midi_send_sysex(int length, Uint8 *data, int type) 151 | { 152 | OSStatus status; 153 | // convert midi ticks to nanosecond timestamp 154 | MIDITimeStamp timestamp = msec_to_ts(ticks); 155 | MIDIPacketList *packetlist; 156 | MIDIPacket *currentpacket; 157 | if (type == MIDI_SYSTEM_PREFIX) { 158 | Uint8 b = MIDI_SYSTEM_PREFIX; 159 | packetlist = (MIDIPacketList*)pktbuffer; 160 | currentpacket = MIDIPacketListInit(packetlist); 161 | currentpacket = MIDIPacketListAdd(packetlist, sizeof(pktbuffer), 162 | currentpacket, timestamp, 1, &b); 163 | status = MIDISend(midiout, MIDIGetDestination(ext_dev), packetlist); 164 | check_err("MIDISend", status); 165 | } 166 | while (length > 0) { 167 | int plen = length; 168 | if (plen > SYSEX_SPLIT) { 169 | plen = SYSEX_SPLIT; /* send in SYSEX_SPLIT byte chunks */ 170 | } 171 | packetlist = (MIDIPacketList*)pktbuffer; 172 | currentpacket = MIDIPacketListInit(packetlist); 173 | currentpacket = MIDIPacketListAdd(packetlist, sizeof(pktbuffer), 174 | currentpacket, timestamp, plen, data); 175 | status = MIDISend(midiout, MIDIGetDestination(ext_dev), packetlist); 176 | check_err("MIDISend", status); 177 | data += plen; 178 | length -= plen; 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /gmvoices.h: -------------------------------------------------------------------------------- 1 | /******************************************************************** 2 | gmvoices.h -- List of GM voice filenames for GUS. 3 | 4 | Copyright (C) 1994-1996 Nathan I. Laredo 5 | 6 | This program is modifiable/redistributable under the terms 7 | of the GNU General Public Licence. 8 | 9 | You should have received a copy of the GNU General Public License 10 | along with this program; if not, write to the Free Software 11 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 12 | Send your comments and all your spare pocket change to 13 | laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401 14 | Kelly Drive, Lackland AFB, TX 78236-5128, USA. 15 | 16 | ********************************************************************/ 17 | char *gmvoice[256] = { 18 | /* [Melodic Patches] */ 19 | /* 000 */ "acpiano", /* 001 */ "britepno", /* 002 */ "synpiano", 20 | /* 003 */ "honky", /* 004 */ "epiano1", /* 005 */ "epiano2", 21 | /* 006 */ "hrpschrd", /* 007 */ "clavinet", /* 008 */ "celeste", 22 | /* 009 */ "glocken", /* 010 */ "musicbox", /* 011 */ "vibes", 23 | /* 012 */ "marimba", /* 013 */ "xylophon", /* 014 */ "tubebell", 24 | /* 015 */ "santur", /* 016 */ "homeorg", /* 017 */ "percorg", 25 | /* 018 */ "rockorg", /* 019 */ "church", /* 020 */ "reedorg", 26 | /* 021 */ "accordn", /* 022 */ "harmonca", /* 023 */ "concrtna", 27 | /* 024 */ "nyguitar", /* 025 */ "acguitar", /* 026 */ "jazzgtr", 28 | /* 027 */ "cleangtr", /* 028 */ "mutegtr", /* 029 */ "odguitar", 29 | /* 030 */ "distgtr", /* 031 */ "gtrharm", /* 032 */ "acbass", 30 | /* 033 */ "fngrbass", /* 034 */ "pickbass", /* 035 */ "fretless", 31 | /* 036 */ "slapbas1", /* 037 */ "slapbas2", /* 038 */ "synbass1", 32 | /* 039 */ "synbass2", /* 040 */ "violin", /* 041 */ "viola", 33 | /* 042 */ "cello", /* 043 */ "contraba", /* 044 */ "marcato", 34 | /* 045 */ "pizzcato", /* 046 */ "harp", /* 047 */ "timpani", 35 | /* 048 */ "marcato", /* 049 */ "slowstr", /* 050 */ "synstr1", 36 | /* 051 */ "synstr2", /* 052 */ "choir", /* 053 */ "doo", 37 | /* 054 */ "voices", /* 055 */ "orchhit", /* 056 */ "trumpet", 38 | /* 057 */ "trombone", /* 058 */ "tuba", /* 059 */ "mutetrum", 39 | /* 060 */ "frenchrn", /* 061 */ "hitbrass", /* 062 */ "synbras1", 40 | /* 063 */ "synbras2", /* 064 */ "sprnosax", /* 065 */ "altosax", 41 | /* 066 */ "tenorsax", /* 067 */ "barisax", /* 068 */ "oboe", 42 | /* 069 */ "englhorn", /* 070 */ "bassoon", /* 071 */ "clarinet", 43 | /* 072 */ "piccolo", /* 073 */ "flute", /* 074 */ "recorder", 44 | /* 075 */ "woodflut", /* 076 */ "bottle", /* 077 */ "shakazul", 45 | /* 078 */ "whistle", /* 079 */ "ocarina", /* 080 */ "sqrwave", 46 | /* 081 */ "sawwave", /* 082 */ "calliope", /* 083 */ "chiflead", 47 | /* 084 */ "charang", /* 085 */ "voxlead", /* 086 */ "lead5th", 48 | /* 087 */ "basslead", /* 088 */ "fantasia", /* 089 */ "warmpad", 49 | /* 090 */ "polysyn", /* 091 */ "ghostie", /* 092 */ "bowglass", 50 | /* 093 */ "metalpad", /* 094 */ "halopad", /* 095 */ "sweeper", 51 | /* 096 */ "aurora", /* 097 */ "soundtrk", /* 098 */ "crystal", 52 | /* 099 */ "atmosphr", /* 100 */ "freshair", /* 101 */ "unicorn", 53 | /* 102 */ "sweeper", /* 103 */ "startrak", /* 104 */ "sitar", 54 | /* 105 */ "banjo", /* 106 */ "shamisen", /* 107 */ "koto", 55 | /* 108 */ "kalimba", /* 109 */ "bagpipes", /* 110 */ "fiddle", 56 | /* 111 */ "shannai", /* 112 */ "carillon", /* 113 */ "agogo", 57 | /* 114 */ "steeldrm", /* 115 */ "woodblk", /* 116 */ "taiko", 58 | /* 117 */ "toms", /* 118 */ "syntom", /* 119 */ "revcym", 59 | /* 120 */ "fx-fret", /* 121 */ "fx-blow", /* 122 */ "seashore", 60 | /* 123 */ "jungle", /* 124 */ "telephon", /* 125 */ "helicptr", 61 | /* 126 */ "applause", /* 127 */ "ringwhsl", 62 | /* [Drum Patches] */ 63 | /* C 0 */ NULL, /* C#0 */ NULL, /* D 0 */ NULL, 64 | /* D#0 */ NULL, /* E 0 */ NULL, /* F 0 */ NULL, 65 | /* F#0 */ NULL, /* G 0 */ NULL, /* G#0 */ NULL, 66 | /* A 0 */ NULL, /* A#0 */ NULL, /* B 0 */ NULL, 67 | /* C 1 */ NULL, /* C#1 */ NULL, /* D 1 */ NULL, 68 | /* D#1 */ NULL, /* E 1 */ NULL, /* F 1 */ NULL, 69 | /* F#1 */ NULL, /* G 1 */ NULL, /* G#1 */ NULL, 70 | /* A 1 */ NULL, /* A#1 */ NULL, /* B 1 */ NULL, 71 | /* C 2 */ NULL, /* C#2 */ NULL, /* D 2 */ NULL, 72 | /* D#2 */ "highq", /* E 2 */ "slap", /* F 2 */ "scratch1", 73 | /* F#2 */ "scratch2", /* G 2 */ "sticks", /* G#2 */ "sqrclick", 74 | /* A 2 */ "metclick", /* A#2 */ "metbell", /* B 2 */ "kick1", 75 | /* C 3 */ "kick2", /* C#3 */ "stickrim", /* D 3 */ "snare1", 76 | /* D#3 */ "claps", /* E 3 */ "snare2", /* F 3 */ "tomlo2", 77 | /* F#3 */ "hihatcl", /* G 3 */ "tomlo1", /* G#3 */ "hihatpd", 78 | /* A 3 */ "tommid2", /* A#3 */ "hihatop", /* B 3 */ "tommid1", 79 | /* C 4 */ "tomhi2", /* C#4 */ "cymcrsh1", /* D 4 */ "tomhi1", 80 | /* D#4 */ "cymride1", /* E 4 */ "cymchina", /* F 4 */ "cymbell", 81 | /* F#4 */ "tamborin", /* G 4 */ "cymsplsh", /* G#4 */ "cowbell", 82 | /* A 4 */ "cymcrsh2", /* A#4 */ "vibslap", /* B 4 */ "cymride2", 83 | /* C 5 */ "bongohi", /* C#5 */ "bongolo", /* D 5 */ "congahi1", 84 | /* D#5 */ "congahi2", /* E 5 */ "congalo", /* F 5 */ "timbaleh", 85 | /* F#5 */ "timbalel", /* G 5 */ "agogohi", /* G#5 */ "agogolo", 86 | /* A 5 */ "cabasa", /* A#5 */ "maracas", /* B 5 */ "whistle1", 87 | /* C 6 */ "whistle2", /* C#6 */ "guiro1", /* D 6 */ "guiro2", 88 | /* D#6 */ "clave", /* E 6 */ "woodblk1", /* F 6 */ "woodblk2", 89 | /* F#6 */ "cuica1", /* G 6 */ "cuica2", /* G#6 */ "triangl1", 90 | /* A 6 */ "triangl2", /* A#6 */ "shaker", /* B 6 */ "jingles", 91 | /* C 7 */ "belltree", /* C#7 */ "castinet", /* D 7 */ "surdo1", 92 | /* D#7 */ "surdo2", /* E 7 */ NULL, /* F 7 */ NULL, 93 | /* F#7 */ NULL, /* G 7 */ NULL, /* G#7 */ NULL, 94 | /* A 7 */ NULL, /* A#7 */ NULL, /* B 7 */ NULL, 95 | /* C 8 */ NULL, /* C#8 */ NULL, /* D 8 */ NULL, 96 | /* D#8 */ NULL, /* E 8 */ NULL, /* F 8 */ NULL, 97 | /* F#8 */ NULL, /* G 8 */ NULL, /* G#8 */ NULL, 98 | /* A 8 */ NULL, /* A#8 */ NULL, /* B 8 */ NULL, 99 | /* C 9 */ NULL, /* C#9 */ NULL, /* D 9 */ NULL, 100 | /* D#9 */ NULL, /* E 9 */ NULL, /* F 9 */ NULL, 101 | /* F#9 */ NULL, /* G 9 */ NULL, /* G#9 */ NULL, 102 | /* A 9 */ NULL, /* A#9 */ NULL, /* B 9 */ NULL, 103 | /* C 10*/ NULL, /* C#10*/ NULL, /* D 10*/ NULL, 104 | /* D#10*/ NULL, /* E 10*/ NULL, /* F 10*/ NULL, 105 | /* F#10*/ NULL, /* G 10*/ NULL 106 | }; 107 | -------------------------------------------------------------------------------- /playevents.c: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | playevents.c -- actually sends sorted list of events to device 3 | 4 | Copyright 2015 Nathan Laredo (laredo@gnu.org) 5 | 6 | This program is modifiable/redistributable under the terms 7 | of the GNU General Public Licence. 8 | 9 | You should have received a copy of the GNU General Public License 10 | along with this program; if not, write to the Free Software 11 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 12 | *************************************************************************/ 13 | #include "playmidi.h" 14 | #include 15 | 16 | extern void seq_set_patch(int, int); 17 | extern void seq_key_pressure(int, int, int); 18 | extern void seq_start_note(int, int, int); 19 | extern void seq_stop_note(int, int, int); 20 | extern void seq_control(int, int, int); 21 | extern void seq_chn_pressure(int, int); 22 | extern void seq_bender(int, int, int); 23 | extern void seq_reset(int); 24 | extern int graphics, verbose, division, ntrks, format; 25 | extern int perc; 26 | extern int play_ext, reverb, chorus, chanmask; 27 | extern int usevol[16]; 28 | extern int mt32pgm[128], MT32; 29 | extern struct miditrack seq[MAXTRKS]; 30 | extern float skew; 31 | extern unsigned long int default_tempo; 32 | extern void load_sysex(int, unsigned char *, int); 33 | extern void showevent(int, unsigned char *, int); 34 | extern void init_show(); 35 | extern int updatestatus(); 36 | 37 | Uint32 ticks, tempo; 38 | Uint32 start_tick; 39 | struct timeval start_time; 40 | extern struct midi_packet *tseqh, *tseqt; 41 | 42 | unsigned long int rvl(struct miditrack *s) 43 | { 44 | register unsigned long int value = 0; 45 | register unsigned char c; 46 | 47 | if (s->index < s->length && ((value = s->data[(s->index)++]) & 0x80)) { 48 | value &= 0x7f; 49 | do { 50 | if (s->index >= s->length) 51 | c = 0; 52 | else 53 | value = (value << 7) + 54 | ((c = s->data[(s->index)++]) & 0x7f); 55 | } while (c & 0x80); 56 | } 57 | return (value); 58 | } 59 | 60 | /* indexed by high nibble of command */ 61 | int cmdlen[16] = {0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 2, 0}; 62 | 63 | #define CHN (seq[track].running_st & 0xf) 64 | #define NOTE data[0] 65 | #define VEL data[1] 66 | 67 | int playevents(void) 68 | { 69 | unsigned long int tempo = default_tempo, lasttime = 0; 70 | unsigned int lowtime, track, best, length; 71 | unsigned char *data; 72 | double current = 0.0, dtime = 0.0; 73 | int play_status, playing = 1; 74 | 75 | init_show(); 76 | seq_reset(0); 77 | ticks = 0; 78 | start_tick = SDL_GetTicks(); 79 | gettimeofday(&start_time, NULL); 80 | for (track = 0; track < ntrks && seq[track].data; track++) { 81 | seq[track].index = seq[track].running_st = 0; 82 | seq[track].ticks = rvl(&seq[track]); 83 | } 84 | for (best = 0; best < 16; best++) { 85 | seq_control(best, CTL_BANK_SELECT, 0); 86 | seq_control(best, CTL_REVERB_DEPTH, reverb); 87 | seq_control(best, CTL_CHORUS_DEPTH, chorus); 88 | seq_control(best, CTL_MAIN_VOLUME, 127); 89 | seq_chn_pressure(best, 127); 90 | //seq_control(best, CTL_BRIGHTNESS, 127); 91 | } 92 | while (playing) { 93 | lowtime = ~0; 94 | for (best = track = 0; track < ntrks && seq[track].data; track++) 95 | if (seq[track].ticks < lowtime) { 96 | best = track; 97 | lowtime = seq[track].ticks; 98 | } 99 | if (lowtime == ~0) 100 | break; /* no more data to read */ 101 | track = best; 102 | 103 | /* this section parses data in midi file buffer */ 104 | if ((seq[track].data[seq[track].index] & 0x80) && 105 | (seq[track].index < seq[track].length)) 106 | seq[track].running_st = seq[track].data[seq[track].index++]; 107 | if (seq[track].running_st == 0xff && seq[track].index < seq[track].length) 108 | seq[track].running_st = seq[track].data[seq[track].index++]; 109 | if (seq[track].running_st > 0xf7) /* midi real-time message (ignored) */ 110 | length = 0; 111 | else if (!(length = cmdlen[(seq[track].running_st & 0xf0) >> 4])) 112 | length = rvl(&seq[track]); 113 | 114 | if (seq[track].index + length < seq[track].length) { 115 | /* use the parsed midi data */ 116 | data = &(seq[track].data[seq[track].index]); 117 | if (seq[track].running_st == SET_TEMPO) 118 | tempo = ((*(data) << 16) | (data[1] << 8) | data[2]); 119 | if (seq[track].ticks > lasttime) { 120 | if (division > 0) { 121 | dtime = ((double) ((seq[track].ticks - lasttime) * (tempo / 1000)) / 122 | (double) (division)) * skew; 123 | current += dtime; 124 | lasttime = seq[track].ticks; 125 | } else if (division < 0) 126 | current = ((double) seq[track].ticks / 127 | ((double) ((division & 0xff00 >> 8) * 128 | (division & 0xff)) * 1000.0)) * skew; 129 | /* stop if there's more than 40 seconds of nothing */ 130 | if (dtime > 40096.0) 131 | playing = 0; 132 | else if ((int) current > ticks) { 133 | Uint32 timeout = start_tick + current; 134 | while (0 && !SDL_TICKS_PASSED(SDL_GetTicks(), timeout)) { 135 | SDL_Delay(timeout - SDL_GetTicks()); 136 | } 137 | ticks = current; 138 | if (graphics) 139 | if ((play_status = updatestatus()) != NO_EXIT) 140 | return play_status; 141 | } 142 | } 143 | if (playing && seq[track].running_st > 0x7f && ISPLAYING(CHN)) { 144 | switch (seq[track].running_st & 0xf0) { 145 | case MIDI_KEY_PRESSURE: 146 | seq_key_pressure(CHN, NOTE, VEL); 147 | break; 148 | case MIDI_NOTEON: 149 | if (VEL && usevol[CHN]) 150 | VEL = usevol[CHN]; 151 | seq_start_note(CHN, NOTE, VEL); 152 | break; 153 | case MIDI_NOTEOFF: 154 | seq_stop_note(CHN, NOTE, VEL); 155 | break; 156 | case MIDI_CTL_CHANGE: 157 | seq_control(CHN, NOTE, VEL); 158 | break; 159 | case MIDI_CHN_PRESSURE: 160 | seq_chn_pressure(CHN, NOTE); 161 | break; 162 | case MIDI_PITCH_BEND: 163 | seq_bender(CHN, NOTE, VEL); 164 | break; 165 | case MIDI_PGM_CHANGE: 166 | seq_set_patch(CHN, NOTE); 167 | break; 168 | case MIDI_SYSTEM_PREFIX: 169 | if (length > 1) 170 | load_sysex(length, data, seq[track].running_st); 171 | break; 172 | default: 173 | break; 174 | } 175 | } 176 | if (verbose || graphics) { 177 | showevent(seq[track].running_st, data, length); 178 | } 179 | } 180 | /* this last little part queues up the next event time */ 181 | seq[track].index += length; 182 | if (seq[track].index >= seq[track].length) 183 | seq[track].ticks = ~0; /* mark track complete */ 184 | else 185 | seq[track].ticks += rvl(&seq[track]); 186 | } 187 | return 1; 188 | } 189 | -------------------------------------------------------------------------------- /alsamidi.c: -------------------------------------------------------------------------------- 1 | /* alsamidi.c - abstract away os specific output routines for linux/alsa 2 | * 3 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 4 | * 5 | * This file may be freely distributed under the terms of 6 | * the GNU General Public Licence (GPL). 7 | */ 8 | 9 | #include "playmidi.h" 10 | #include 11 | 12 | extern int ext_dev; 13 | 14 | static snd_seq_t *seq; 15 | static int queue, client; 16 | static int midi_opened = 0; 17 | static snd_seq_addr_t port; 18 | extern Uint32 ticks; 19 | 20 | static void check_snd(const char *msg, int err) 21 | { 22 | if (err < 0) { 23 | fprintf(stderr, "%s: %s\n", msg, snd_strerror(err)); 24 | exit(1); 25 | } 26 | } 27 | 28 | static void check_mem(void *buf) 29 | { 30 | if (buf == NULL) { 31 | fprintf(stderr, "allocation failed.\n"); 32 | exit(1); 33 | } 34 | } 35 | 36 | 37 | void show_ports(void) 38 | { 39 | int index = 0; 40 | snd_seq_client_info_t *cinfo; 41 | snd_seq_port_info_t *pinfo; 42 | 43 | snd_seq_client_info_alloca(&cinfo); 44 | snd_seq_port_info_alloca(&pinfo); 45 | check_mem(cinfo); 46 | check_mem(pinfo); 47 | 48 | printf("%-5s %-8s %-32s %s\n", 49 | "dev", "Port", "Client name", "Port name"); 50 | 51 | snd_seq_client_info_set_client(cinfo, -1); 52 | while (snd_seq_query_next_client(seq, cinfo) >= 0) { 53 | int client = snd_seq_client_info_get_client(cinfo); 54 | 55 | snd_seq_port_info_set_client(pinfo, client); 56 | snd_seq_port_info_set_port(pinfo, -1); 57 | while (snd_seq_query_next_port(seq, pinfo) >= 0) { 58 | /* port must understand MIDI messages */ 59 | if (!(snd_seq_port_info_get_type(pinfo) 60 | & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) 61 | continue; 62 | /* we need both WRITE and SUBS_WRITE */ 63 | if ((snd_seq_port_info_get_capability(pinfo) 64 | & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) 65 | != (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) 66 | continue; 67 | printf("%-5d %3d:%-3d %-32.32s %s\n", index, 68 | snd_seq_port_info_get_client(pinfo), 69 | snd_seq_port_info_get_port(pinfo), 70 | snd_seq_client_info_get_name(cinfo), 71 | snd_seq_port_info_get_name(pinfo)); 72 | index++; 73 | } 74 | } 75 | } 76 | 77 | /* abstract alsa device mess with simple device index request */ 78 | static void connect_port(int dev_index) 79 | { 80 | int index = 0; 81 | snd_seq_client_info_t *cinfo; 82 | snd_seq_port_info_t *pinfo; 83 | 84 | snd_seq_client_info_alloca(&cinfo); 85 | snd_seq_port_info_alloca(&pinfo); 86 | check_mem(cinfo); 87 | check_mem(pinfo); 88 | 89 | snd_seq_client_info_set_client(cinfo, -1); 90 | while (snd_seq_query_next_client(seq, cinfo) >= 0) { 91 | int client = snd_seq_client_info_get_client(cinfo); 92 | 93 | snd_seq_port_info_set_client(pinfo, client); 94 | snd_seq_port_info_set_port(pinfo, -1); 95 | while (snd_seq_query_next_port(seq, pinfo) >= 0) { 96 | /* port must understand MIDI messages */ 97 | if (!(snd_seq_port_info_get_type(pinfo) 98 | & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) 99 | continue; 100 | /* we need both WRITE and SUBS_WRITE */ 101 | if ((snd_seq_port_info_get_capability(pinfo) 102 | & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) 103 | != (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) 104 | continue; 105 | /* check if this device is the index requested */ 106 | port.client = snd_seq_port_info_get_client(pinfo); 107 | port.port = snd_seq_port_info_get_port(pinfo); 108 | if (index == dev_index) { 109 | int err = snd_seq_connect_to(seq, 0, port.client, port.port); 110 | check_snd("snd_seq_connect", err); 111 | return; 112 | } 113 | index++; 114 | } 115 | } 116 | } 117 | 118 | static void create_source_port(void) 119 | { 120 | snd_seq_port_info_t *pinfo; 121 | int err; 122 | 123 | snd_seq_port_info_alloca(&pinfo); 124 | check_mem(pinfo); 125 | 126 | /* the first created port is 0 anyway, but let's make sure ... */ 127 | snd_seq_port_info_set_port(pinfo, 0); 128 | snd_seq_port_info_set_port_specified(pinfo, 1); 129 | 130 | snd_seq_port_info_set_name(pinfo, "playmidi"); 131 | 132 | snd_seq_port_info_set_capability(pinfo, 0); 133 | snd_seq_port_info_set_type(pinfo, SND_SEQ_PORT_TYPE_MIDI_GENERIC | 134 | SND_SEQ_PORT_TYPE_APPLICATION); 135 | 136 | err = snd_seq_create_port(seq, pinfo); 137 | check_snd("snd_seq_create_port", err); 138 | } 139 | 140 | static void create_queue(void) 141 | { 142 | queue = snd_seq_alloc_named_queue(seq, "playmidi"); 143 | check_snd("snd_seq_alloc_named_queue", queue); 144 | } 145 | 146 | void init_midi(void) 147 | { 148 | int err; 149 | 150 | if (midi_opened) { 151 | return; 152 | } 153 | err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_DUPLEX, 0); 154 | check_snd("snd_seq_open", err); 155 | 156 | err = snd_seq_set_client_name(seq, RELEASE); 157 | check_snd("snd_seq_set_client_name", err); 158 | 159 | client = snd_seq_client_id(seq); 160 | check_snd("snd_seq_client_id", client); 161 | create_source_port(); 162 | create_queue(); 163 | connect_port(ext_dev); 164 | err = snd_seq_start_queue(seq, queue, NULL); 165 | check_snd("snd_seq_start_queee", err); 166 | midi_opened++; 167 | } 168 | 169 | /* give last notes a chance to get flushed out, proper close */ 170 | void close_midi(void) 171 | { 172 | int err; 173 | snd_seq_event_t ev; 174 | if (!midi_opened) { 175 | return; 176 | } 177 | 178 | /* schedule queue stop at end of playback */ 179 | snd_seq_ev_clear(&ev); 180 | ev.queue = queue; 181 | ev.source.port = 0; 182 | ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS; 183 | snd_seq_ev_set_fixed(&ev); 184 | ev.type = SND_SEQ_EVENT_STOP; 185 | ev.time.time.tv_sec = 1 + ticks / 1000; 186 | ev.time.time.tv_nsec = (ticks % 1000) * 1000000; 187 | ev.dest.client = SND_SEQ_CLIENT_SYSTEM; 188 | ev.dest.port = SND_SEQ_PORT_SYSTEM_TIMER; 189 | ev.data.queue.queue = queue; 190 | err = snd_seq_event_output(seq, &ev); 191 | check_snd("snd_seq_event_output", err); 192 | err = snd_seq_drain_output(seq); 193 | check_snd("snd_seq_drain_output", err); 194 | err = snd_seq_sync_output_queue(seq); 195 | check_snd("snd_seq_sync_output_queue", err); 196 | snd_seq_close(seq); 197 | midi_opened = 0; 198 | } 199 | 200 | static const unsigned char snd_cmd_type[] = { 201 | SND_SEQ_EVENT_NOTEOFF, /* 0x80 */ 202 | SND_SEQ_EVENT_NOTEON, /* 0x90 */ 203 | SND_SEQ_EVENT_KEYPRESS, /* 0xa0 */ 204 | SND_SEQ_EVENT_CONTROLLER, /* 0xb0 */ 205 | SND_SEQ_EVENT_PGMCHANGE, /* 0xc0 */ 206 | SND_SEQ_EVENT_CHANPRESS, /* 0xd0 */ 207 | SND_SEQ_EVENT_PITCHBEND /* 0xe0 */ 208 | }; 209 | 210 | void midi_add_pkt(struct midi_packet *p) 211 | { 212 | int err; 213 | snd_seq_event_t ev; 214 | snd_seq_ev_clear(&ev); 215 | ev.queue = queue; 216 | ev.source.port = 0; 217 | ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS; 218 | snd_seq_ev_set_fixed(&ev); 219 | if (p->data[0] >= 0xf0) 220 | return; /* sysex and/or realtime has another venue */ 221 | ev.type = snd_cmd_type[(p->data[0] >> 4) - 0x8]; 222 | ev.time.time.tv_sec = ticks / 1000; 223 | ev.time.time.tv_nsec = (ticks % 1000) * 1000000; 224 | ev.dest = port; 225 | switch (ev.type) { 226 | case SND_SEQ_EVENT_NOTEOFF: 227 | case SND_SEQ_EVENT_NOTEON: 228 | case SND_SEQ_EVENT_KEYPRESS: 229 | ev.data.note.channel = (p->data[0] & 0xf); 230 | ev.data.note.note = p->data[1]; 231 | ev.data.note.velocity = p->data[2]; 232 | break; 233 | case SND_SEQ_EVENT_CONTROLLER: 234 | ev.data.control.channel = (p->data[0] & 0xf); 235 | ev.data.control.param = p->data[1]; 236 | ev.data.control.value = p->data[2]; 237 | break; 238 | case SND_SEQ_EVENT_PGMCHANGE: 239 | case SND_SEQ_EVENT_CHANPRESS: 240 | ev.data.control.channel = (p->data[0] & 0xf); 241 | ev.data.control.value = p->data[1]; 242 | break; 243 | case SND_SEQ_EVENT_PITCHBEND: 244 | ev.data.control.channel = (p->data[0] & 0xf); 245 | ev.data.control.value = ((p->data[1]) | 246 | ((p->data[2]) <<7)) - 0x2000; 247 | break; 248 | } 249 | err = snd_seq_event_output(seq, &ev); 250 | check_snd("snd_seq_event_output", err); 251 | err = snd_seq_drain_output(seq); 252 | check_snd("snd_seq_drain_output", err); 253 | } 254 | 255 | #define SYSEX_SPLIT 32 256 | void midi_send_sysex(int length, Uint8 *data, int type) 257 | { 258 | int err; 259 | snd_seq_event_t ev; 260 | 261 | snd_seq_ev_clear(&ev); 262 | ev.queue = queue; 263 | ev.source.port = 0; 264 | ev.flags = SND_SEQ_TIME_STAMP_REAL | SND_SEQ_TIME_MODE_ABS; 265 | ev.type = SND_SEQ_EVENT_SYSEX; 266 | ev.time.time.tv_sec = ticks / 1000; 267 | ev.time.time.tv_nsec = (ticks % 1000) * 1000000; 268 | if (type == MIDI_SYSTEM_PREFIX) { 269 | Uint8 b = MIDI_SYSTEM_PREFIX; 270 | snd_seq_ev_set_variable(&ev, 1, &b); 271 | err = snd_seq_event_output(seq, &ev); 272 | check_snd("snd_seq_event_output", err); 273 | err = snd_seq_drain_output(seq); 274 | check_snd("snd_seq_drain_output", err); 275 | err = snd_seq_sync_output_queue(seq); 276 | check_snd("snd_seq_sync_output_queue", err); 277 | } 278 | snd_seq_ev_set_variable(&ev, length, data); 279 | while (length > 0) { 280 | if (length > SYSEX_SPLIT) { 281 | ev.data.ext.len = SYSEX_SPLIT; 282 | } else { 283 | ev.data.ext.len = length; 284 | } 285 | err = snd_seq_event_output(seq, &ev); 286 | check_snd("snd_seq_event_output", err); 287 | err = snd_seq_drain_output(seq); 288 | check_snd("snd_seq_drain_output", err); 289 | err = snd_seq_sync_output_queue(seq); 290 | check_snd("snd_seq_sync_output_queue", err); 291 | ev.data.ext.ptr += ev.data.ext.len; 292 | length -= ev.data.ext.len; 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /playmidi.c: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | playmidi.c -- last change: 1 Jan 96 3 | 4 | Plays a MIDI file to any supported synth (including midi) device 5 | 6 | Copyright (C) 1994-1996 Nathan I. Laredo 7 | 8 | This program is modifiable/redistributable under the terms 9 | of the GNU General Public Licence. 10 | 11 | You should have received a copy of the GNU General Public License 12 | along with this program; if not, write to the Free Software 13 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 14 | Send your comments and all your spare pocket change to 15 | laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401 16 | Kelly Drive, Lackland AFB, TX 78236-5128, USA. 17 | *************************************************************************/ 18 | #ifndef __FreeBSD__ 19 | #include 20 | #endif 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "playmidi.h" 26 | 27 | struct miditrack seq[MAXTRKS]; 28 | 29 | int verbose = 0, chanmask = 0xffff, perc = 0x0200; 30 | int dochan = 1, play_ext = 0; 31 | int useprog[16], usevol[16]; 32 | int graphics = 0, reverb = 0, chorus = 0; 33 | int find_header = 0, MT32 = 0; 34 | FILE *mfd; 35 | int ext_dev = 0; 36 | unsigned long int default_tempo; 37 | char *filename; 38 | char *sf2_filename = "inst.sf2"; 39 | float skew = 1.0; 40 | extern int ntrks; 41 | extern int mt32pgm[128]; 42 | extern int playevents(); 43 | extern int gus_load(int); 44 | extern int readmidi(unsigned char *, off_t); 45 | extern void loadfm(); 46 | extern void setup_show(int, char **); 47 | extern void close_show(int); 48 | 49 | int main(argc, argv) 50 | int argc; 51 | char **argv; 52 | { 53 | extern char *optarg; 54 | extern int optind; 55 | int i, error = 0, j, newprog; 56 | char *extra; 57 | char *filebuf; 58 | struct stat info; 59 | int piped = 0; 60 | 61 | printf("%s Copyright 2015 Nathan I. Laredo\n" 62 | "This is free software with ABSOLUTELY NO WARRANTY.\n" 63 | "For details please see the file COPYING.\n", RELEASE); 64 | for (i = 0; i < 16; i++) 65 | useprog[i] = usevol[i] = 0; /* reset options */ 66 | while ((i = getopt(argc, argv, 67 | "c:aA:b:C:dD:eE:F:gh:G:i:lMp:P:rR:t:vV:x:z")) != -1) 68 | switch (i) { 69 | case 'b': 70 | sf2_filename = strdup(optarg); 71 | break; 72 | case 'x': 73 | j = atoi(optarg); 74 | if (j < 1 || j > 16) { 75 | fprintf(stderr, "option -x channel must be 1 - 16\n"); 76 | exit(1); 77 | } 78 | j = 1 << (j - 1); 79 | chanmask &= ~j; 80 | break; 81 | case 'c': 82 | if (chanmask == 0xffff) 83 | chanmask = strtoul(optarg, NULL, 16); 84 | else 85 | chanmask |= strtoul(optarg, NULL, 16); 86 | break; 87 | case 'e': 88 | play_ext = 0xffff; 89 | break; 90 | case 'D': 91 | ext_dev = atoi(optarg); 92 | break; 93 | case 'h': 94 | find_header = atoi(optarg); 95 | if (find_header < 1) { 96 | fprintf(stderr, "option -h header must be > 0\n"); 97 | exit(1); 98 | } 99 | break; 100 | case 'i': 101 | chanmask &= ~strtoul(optarg, NULL, 16); 102 | break; 103 | case 'M': 104 | MT32++; 105 | break; 106 | case 'p': 107 | if (strchr(optarg, ',') == NULL) { /* set all channels */ 108 | newprog = atoi(optarg); 109 | if (newprog < 1 || newprog > 128) { 110 | fprintf(stderr, "option -p prog must be 1 - 128\n"); 111 | exit(1); 112 | } 113 | for (j = 0; j < 16; j++) 114 | useprog[j] = newprog; 115 | } else { /* set channels individually */ 116 | extra = optarg; 117 | while (extra != NULL) { 118 | j = atoi(extra); 119 | if (j < 1 || j > 16) { 120 | fprintf(stderr, "opton -p chan must be 1 - 16\n"); 121 | exit(1); 122 | } 123 | extra = strchr(extra, ','); 124 | if (extra == NULL) { 125 | fprintf(stderr, "option -p prog needed for chan %d\n", 126 | j); 127 | exit(1); 128 | } else 129 | extra++; 130 | newprog = atoi(extra); 131 | if (newprog < 1 || newprog > 128) { 132 | fprintf(stderr, "option -p prog must be 1 - 128\n"); 133 | exit(1); 134 | } 135 | useprog[j - 1] = newprog; 136 | extra = strchr(extra, ','); 137 | if (extra != NULL) 138 | extra++; 139 | } 140 | } 141 | break; 142 | case 'r': 143 | graphics++; 144 | break; 145 | case 't': 146 | if ((skew = atof(optarg)) < .25) { 147 | fprintf(stderr, "option -t skew under 0.25 unplayable\n"); 148 | exit(1); 149 | } 150 | break; 151 | case 'E': 152 | play_ext = strtoul(optarg, NULL, 16); 153 | break; 154 | case 'R': 155 | reverb = atoi(optarg); 156 | if (reverb < 0 || reverb > 127) { 157 | fprintf(stderr, "option -R reverb must be 0 - 127\n"); 158 | exit(1); 159 | } 160 | break; 161 | case 'C': 162 | chorus = atoi(optarg); 163 | if (chorus < 0 || chorus > 127) { 164 | fprintf(stderr, "option -C chorus must be 0 - 127\n"); 165 | exit(1); 166 | } 167 | break; 168 | case 'P': 169 | extra = optarg; 170 | perc = 0; 171 | do { 172 | j = atoi(extra); 173 | if (j < 1 || j > 16) { 174 | fprintf(stderr, "option -P channel must be 1 - 16\n"); 175 | exit(1); 176 | } 177 | perc |= (1 << (j - 1)); 178 | extra = strchr(extra, ','); 179 | if (extra) { 180 | extra++; 181 | } 182 | } while (extra); 183 | break; 184 | case 'l': 185 | init_midi(); 186 | show_ports(); 187 | exit(1); 188 | break; 189 | case 'v': 190 | verbose++; 191 | break; 192 | case 'V': 193 | if (strchr(optarg, ',') == NULL) { /* set all channels */ 194 | newprog = atoi(optarg); 195 | if (newprog < 1 || newprog > 128) { 196 | fprintf(stderr, "option -V vol must be 1 - 128\n"); 197 | exit(1); 198 | } 199 | for (j = 0; j < 16; j++) 200 | usevol[j] = newprog; 201 | } else { /* set channels individually */ 202 | extra = optarg; 203 | while (extra != NULL) { 204 | j = atoi(extra); 205 | if (j < 1 || j > 16) { 206 | fprintf(stderr, "opton -V chan must be 1 - 16\n"); 207 | exit(1); 208 | } 209 | extra = strchr(extra, ','); 210 | if (extra == NULL) { 211 | fprintf(stderr, "option -V vol needed for chan %d\n", 212 | j); 213 | exit(1); 214 | } else 215 | extra++; 216 | newprog = atoi(extra); 217 | if (newprog < 1 || newprog > 128) { 218 | fprintf(stderr, "option -p prog must be 1 - 128\n"); 219 | exit(1); 220 | } 221 | usevol[j - 1] = newprog; 222 | extra = strchr(extra, ','); 223 | if (extra != NULL) 224 | extra++; 225 | } 226 | } 227 | break; 228 | case 'z': 229 | dochan = 0; 230 | break; 231 | case 'd': 232 | chanmask &= ~perc; 233 | break; 234 | default: 235 | error++; 236 | break; 237 | } 238 | 239 | if (error || optind >= argc) { 240 | fprintf(stderr, "usage: %s [-options] file1 [file2 ...]\n", argv[0]); 241 | fprintf(stderr, " -v verbosity (additive)\n" 242 | " -b sf2fn use sf2fn as filename for sf2 file to use\n" 243 | " -l list available midi ports for -D x option\n" 244 | " -i x ignore channels set in bitmask x (hex)\n" 245 | " -c x play only channels set in bitmask x (hex)\n" 246 | " -x x exclude channel x from playable bitmask\n" 247 | " -p [c,]x play program x on channel c (all if no c)\n" 248 | " -V [c,]x play channel c with volume x (all if no c)\n" 249 | " -t x skew tempo by x (float)\n" 250 | " -d don't play any percussion\n" 251 | " -P x,[x] treat channel x as percussion\n" 252 | " -e output to external midi\n" 253 | " -D x output to midi device x\n" 254 | " -h x skip to header x in large archive\n" 255 | " -E x play channels in bitmask x external\n" 256 | " -z ignore channel of all events\n" 257 | " -M enable MT-32 to GM translation mode\n" 258 | " -I show list of all GM programs (see -p)\n" 259 | " -R x set initial reverb to x (0-127)\n" 260 | " -C x set initial chorus to x (0-127)\n" 261 | " -r real-time playback graphics\n"); 262 | exit(1); 263 | } 264 | setup_show(argc, argv); 265 | /* play all filenames listed on command line */ 266 | for (i = optind; i < argc;) { 267 | filename = argv[i]; 268 | if (stat(filename, &info) == -1) { 269 | if ((extra = malloc(strlen(filename) + 4)) == NULL) 270 | close_show(-1); 271 | sprintf(extra, "%s.mid", filename); 272 | if (stat(extra, &info) == -1) 273 | close_show(-1); 274 | if ((mfd = fopen(extra, "r")) == NULL) 275 | close_show(-1); 276 | free(extra); 277 | } else { 278 | char *ext = strrchr(filename, '.'); 279 | if (ext && strcmp(ext, ".gz") == 0) { 280 | char temp[1024]; 281 | piped = 1; 282 | sprintf(temp, "gzip -l %s", filename); 283 | if ((mfd = popen(temp, "r")) == NULL) 284 | close_show(-1); 285 | fgets(temp, sizeof(temp), mfd); /* skip 1st line */ 286 | fgets(temp, sizeof(temp), mfd); 287 | strtok(temp, " "); /* compressed size */ 288 | info.st_size = atoi(strtok(NULL, " ")); /* original size */ 289 | pclose(mfd); 290 | sprintf(temp, "gzip -d -c %s", filename); 291 | if ((mfd = popen(temp, "r")) == NULL) 292 | close_show(-1); 293 | } else if ((mfd = fopen(filename, "r")) == NULL) 294 | close_show(-1); 295 | } 296 | if ((filebuf = malloc(info.st_size)) == NULL) 297 | close_show(-1); 298 | fread(filebuf, 1, info.st_size, mfd); 299 | if (piped) 300 | pclose(mfd); 301 | else 302 | fclose(mfd); 303 | do { 304 | default_tempo = 500000; 305 | /* error holds number of tracks read */ 306 | error = readmidi((unsigned char *)filebuf, info.st_size); 307 | newprog = 1; /* if there's an error skip to next file */ 308 | if (error > 0) /* error holds number of tracks read */ 309 | while ((newprog = playevents()) == 0); 310 | if (find_header) /* play headers following selected */ 311 | find_header += newprog; 312 | } while (find_header); 313 | if ((i += newprog) < optind) 314 | i = optind; /* can't skip back past first file */ 315 | free(filebuf); 316 | } 317 | close_midi(); 318 | close_show(0); 319 | exit(0); /* this statement is here to keep the compiler happy */ 320 | } 321 | /* end of file */ 322 | -------------------------------------------------------------------------------- /soundfont2.h: -------------------------------------------------------------------------------- 1 | /* soundfont2.h - defines and structures used to handle SF2 files 2 | * 3 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 4 | * 5 | * This file may be freely distributed under the terms of 6 | * the GNU General Public Licence (GPL). 7 | */ 8 | 9 | /* source document: http://freepats.zenvoid.org/sf2/sfspec24.pdf */ 10 | 11 | /* 3.1 General RIFF File Structure */ 12 | struct __attribute__ ((__packed__)) riffChunk { 13 | Uint32 tag; /* chunk id identifies the type of data in chunk */ 14 | Uint32 len; /* the size of chunk data in bytes */ 15 | Uint8 data[0]; /* the data, end is aligned on 2 byte boundary */ 16 | }; 17 | 18 | /* 4.5 SoundFont 2 RIFF File Format Type Definitions */ 19 | 20 | typedef struct { 21 | Uint8 byLo; 22 | Uint8 byHi; 23 | } rangesType; 24 | 25 | typedef union { 26 | rangesType ranges; 27 | Sint16 shAmount; 28 | Uint16 wAmount; 29 | } genAmountType; 30 | 31 | typedef enum __attribute__ ((__packed__)) { 32 | monoSample = 1, 33 | rightSample = 2, 34 | leftSample = 4, 35 | linkedSample = 8, 36 | RomSample = 0x8000, 37 | RomMonoSample = 0x8001, 38 | RomRightSample = 0x8002, 39 | RomLeftSample = 0x8004, 40 | RomLinkedSample = 0x8008 41 | } SFSampleLink; 42 | 43 | typedef enum __attribute__ ((__packed__)) { 44 | /* 8.1.3 Generator Summary, SF2 specification */ 45 | /* 46 | * * Range depends on values of start, loop, and end points in sample header. 47 | * ** Range has discrete values based on bit flags 48 | * + This generator is only valid at the instrument level. 49 | * @ This generator is designated as a non-real-time parameter. 50 | */ 51 | SFG_startAddrsOffset = 0, /* + smpls, def:0 */ 52 | SFG_endAddrsOffset = 1, /* + smpls, def:0 */ 53 | SFG_startloopAddrsOffset = 2, /* + smpls, def:0 */ 54 | SFG_endloopAddrsOffset = 3, /* + smpls, def:0 */ 55 | SFG_startAddrsCoarseOffset = 4, /* + 32k smpls, def:0 */ 56 | SFG_modLfoToPitch = 5, /* cent fs, -12000 - 12000, def:0 */ 57 | SFG_vibLfoToPitch = 6, /* cent fs, -12000 - 12000, def:0 */ 58 | SFG_modEnvToPitch = 7, /* cent fs, -12000 - 12000, def:0 */ 59 | SFG_initialFilterFc = 8, /* cent, 1500 - 13500, def:13500 */ 60 | SFG_initialFilterQ = 9, /* cB, 0 - 960, def:0 */ 61 | SFG_modLfoToFilterFc = 10, /* cent fs, -12000 - 12000, def:0 */ 62 | SFG_modEnvToFilterFc = 11, /* cent fs, -12000 - 12000, def:0 */ 63 | SFG_endAddrsCoarseOffset = 12, /* + 32k smpls, def:0 */ 64 | SFG_modLfoToVolume = 13, /* cB fs, -960 - 960, def:0 */ 65 | SFG_unused1 = 14, /* Unused, reserved. ignore */ 66 | SFG_chorusEffectsSend = 15, /* 0.1%, 0 - 1000, def:0 */ 67 | SFG_reverbEffectsSend = 16, /* 0.1%, 0 - 1000, def:0 */ 68 | SFG_pan = 17, /* 0.1%, -500 - 500, def:0 */ 69 | SFG_unused2 = 18, /* Unused, reserved. ignore */ 70 | SFG_unused3 = 19, /* Unused, reserved. ignore */ 71 | SFG_unused4 = 20, /* Unused, reserved. ignore */ 72 | SFG_delayModLFO = 21, /* timecent, 0 - 1000, def:0 */ 73 | SFG_freqModLFO = 22, /* cent, -16000 - 4500, def:0 */ 74 | SFG_delayVibLFO = 23, /* timecent, -12000 - 5000, def:-12000 */ 75 | SFG_freqVibLFO = 24, /* cent, -16000 - 4500, def:0 */ 76 | SFG_delayModEnv = 25, /* timecent, -12000 - 5000, def:-12000 */ 77 | SFG_attackModEnv = 26, /* timecent, -12000 - 8000, def:-12000 */ 78 | SFG_holdModEnv = 27, /* timecent, -12000 - 5000, def:-12000 */ 79 | SFG_decayModEnv = 28, /* timecent, -12000 - 8000, def:-12000 */ 80 | SFG_sustainModEnv = 29, /* -0.1%, 0 - 1000, def:0 */ 81 | SFG_releaseModEnv = 30, /* timecent, -12000 - 8000, def:-12000 */ 82 | SFG_keynumToModEnvHold = 31, /* tcent/key, -1200 - 1200, def:0 */ 83 | SFG_keynumToModEnvDecay = 32, /* tcent/key, -1200 - 1200, def:0 */ 84 | SFG_delayVolEnv = 33, /* timecent, -12000 - 5000, def:-12000 */ 85 | SFG_attackVolEnv = 34, /* timecent, -12000 - 8000, def:-12000 */ 86 | SFG_holdVolEnv = 35, /* timecent, -12000 - 5000, def:-12000 */ 87 | SFG_decayVolEnv = 36, /* timecent, -12000 - 8000, def:-12000 */ 88 | SFG_sustainVolEnv = 37, /* cB attn, 0 - 1440, def:0 */ 89 | SFG_releaseVolEnv = 38, /* timecent 1 sec -12000 1 msec 8000 100sec -12000 <1 msec */ 90 | SFG_keynumToVolEnvHold = 39, /* tcent/key, -1200 - 1200, def:0 */ 91 | SFG_keynumToVolEnvDecay = 40, /* tcent/key, -1200 - 1200, def:0 */ 92 | SFG_instrument = 41, /* index into inst chunk to use */ 93 | SFG_reserved1 = 42, /* Unused, reserved. ignore */ 94 | SFG_keyRange = 43, /* @ MIDI ky#, 0 - 127, def:"0-127" */ 95 | SFG_velRange = 44, /* @ MIDI vel, 0 - 127, def:"0-127" */ 96 | SFG_startloopAddrsCoarseOffset= 45, /* + smpls, def:0 */ 97 | SFG_keynum = 46, /* +@ MIDI ky#, 0 - 127, def:-1 (none) */ 98 | SFG_velocity = 47, /* +@ MIDI vel, 0 - 128, def:-1 (none) */ 99 | SFG_initialAttenuation = 48, /* cB, 0 - 1440, def:0 (1440=144dB) */ 100 | SFG_reserved2 = 49, /* Unused, reserved. ignore */ 101 | SFG_endloopAddrsCoarseOffset = 50, /* + smpls, def:0 */ 102 | SFG_coarseTune = 51, /* semitone, -120 - 120, def:0 */ 103 | SFG_fineTune = 52, /* cent, -99 - 99, def:0 */ 104 | SFG_sampleID = 53, /* index into shdr chunk to use */ 105 | SFG_sampleModes = 54, /* +@ Bit Flags, Flags, def:0 */ 106 | SFG_reserved3 = 55, /* Unused, reserved. ignore */ 107 | SFG_scaleTuning = 56, /* @ cent/key, 0 - 1200, def:100 */ 108 | SFG_exclusiveClass = 57, /* +@ arbitrary#, 0 - 127, def:0 */ 109 | SFG_overridingRootKey = 58, /* +@ MIDI ky#, 0 - 127, def:-1 (none) */ 110 | SFG_unused5 = 59, /* Unused, reserved. ignore */ 111 | SFG_endOper = 60, /* Unused, reserved. terminates list */ 112 | SFG_bits = 0x7fff /* force to 16 bit enum */ 113 | } SFGenerator; 114 | 115 | typedef enum __attribute__ ((__packed__)) { 116 | /* 8.2.1 Source Enumerator Controller Palettes */ 117 | SFM_noController = 0, /* no controller is to be used, treated as if 1 */ 118 | SFM_noteOnVelocity = 2, /* midi velocity which generated sampled sound */ 119 | SFM_noteOnKeyNumber = 3, /* midi note number, generated sampled sound */ 120 | SFM_polyPressure = 10, /* poly-pressure amount sent from midi cmd */ 121 | SFM_channelPressure = 13, /* channel-pressure amount sent from midi cmd */ 122 | SFM_pitchWheel = 14, /* pitch wheel amount sent from midi cmd */ 123 | SFM_wheelSensitivity = 16, /* RPN0 amount sent from midi RPN0 cmd */ 124 | SFM_link = 127,/* controller source is output of another mod */ 125 | SFM_bits = 0x7fff /* force to 16 bit enum */ 126 | } SFModulator; 127 | 128 | typedef enum __attribute__ ((__packed__)) { 129 | /* 8.3 Modulator Transform Enumerators */ 130 | SFT_linear = 0, /* output value fed directly into the summing */ 131 | SFT_absoluteValue = 1, /* output value is abs value of the input */ 132 | SFT_bits = 0x7fff /* force to 16 bit enum */ 133 | } SFTransform; 134 | 135 | /* 4.4 SoundFont 2 RIFF File Format Level 3 */ 136 | /* specification to which the soundfont complies */ 137 | struct __attribute__ ((__packed__)) sfVersionTag { /* iver */ 138 | Uint16 wMajor; 139 | Uint16 wMinor; 140 | }; 141 | 142 | struct __attribute__ ((__packed__)) sfPresetHeader { /* phdr */ 143 | char achPresetName[20]; 144 | Uint16 wPreset; 145 | Uint16 wBank; 146 | Uint16 wPresetBagNdx; 147 | Uint32 dwLibrary; 148 | Uint32 dwGenre; 149 | Uint32 dwMorphology; 150 | }; 151 | 152 | struct __attribute__ ((__packed__)) sfPresetBag { /* pbag */ 153 | Uint16 wGenNdx; 154 | Uint16 wModNdx; 155 | }; 156 | 157 | struct __attribute__ ((__packed__)) sfModList { /* pmod */ 158 | SFModulator sfModSrcOper; 159 | SFGenerator sfModDestOper; 160 | Sint16 modAmount; 161 | SFModulator sfModAmtSrcOper; 162 | SFTransform sfModTransOper; 163 | }; 164 | 165 | struct __attribute__ ((__packed__)) sfGenList { /* pgen */ 166 | SFGenerator sfGenOper; 167 | genAmountType genAmount; 168 | }; 169 | 170 | struct __attribute__ ((__packed__)) sfInst { /* inst */ 171 | char achInstName[20]; 172 | Uint16 wInstBagNdx; 173 | }; 174 | 175 | struct __attribute__ ((__packed__)) sfInstBag { /* ibag */ 176 | Uint16 wInstGenNdx; 177 | Uint16 wInstModNdx; 178 | }; 179 | 180 | struct __attribute__ ((__packed__)) sfInstModList { /* imod */ 181 | SFModulator sfModSrcOper; 182 | SFGenerator sfModDestOper; 183 | Sint16 modAmount; 184 | SFModulator sfModAmtSrcOper; 185 | SFTransform sfModTransOper; 186 | }; 187 | 188 | struct __attribute__ ((__packed__)) sfInstGenList { /* igen */ 189 | SFGenerator sfGenOper; 190 | genAmountType genAmount; 191 | }; 192 | 193 | struct __attribute__ ((__packed__)) sfSample { /* shdr */ 194 | char achSampleName[20]; 195 | Uint32 dwStart; 196 | Uint32 dwEnd; 197 | Uint32 dwStartloop; 198 | Uint32 dwEndloop; 199 | Uint32 dwSampleRate; 200 | Uint8 byOriginalKey; 201 | Sint8 chCorrection; 202 | Uint16 wSampleLink; 203 | SFSampleLink sfSampleType; 204 | }; 205 | 206 | /* this structure is filled out pointing to relevant bits in the loaded file */ 207 | /* note: the Uint32 size *MUST* directly follow each section pointer */ 208 | struct sfSFBK { 209 | struct sfVersionTag *ifil; // version of the sound font riff file 210 | Uint32 ifil_size; // size of ifil chunk in bytes 211 | char *isng; // target sound engine 212 | Uint32 isng_size; // size of isng chunk in bytes 213 | char *INAM; // sound font bank name 214 | Uint32 INAM_size; // size of INAM chunk in bytes 215 | char *irom; // sound rom name (optional) 216 | Uint32 irom_size; // size of irom chunk in bytes 217 | struct sfVersionTag *iver; // sound rom version (opt) 218 | Uint32 iver_size; // size of iver chunk in bytes 219 | char *ICRD; // creation date of the bank 220 | Uint32 ICRD_size; // size of ICRD chunk in bytes 221 | char *IENG; // sound designers and engineers for the bank 222 | Uint32 IENG_size; // size of IENG chunk in bytes 223 | char *IPRD; // product for which the bank was intended 224 | Uint32 IPRD_size; // size of IPRD chunk in bytes 225 | char *ICOP; // copyright message 226 | Uint32 ICOP_size; // size of ICOP chunk in bytes 227 | char *ICMT; // bank comments, if any 228 | Uint32 ICMT_size; // size of ICMT chunk in bytes 229 | char *ISFT; // soundfont tool used to create/edit file 230 | Uint32 ISFT_size; // size of ISFT chunk in bytes 231 | short *smpl; // digital audio samples for upper 16 bits 232 | Uint32 smpl_size; // size of smpl chunk in bytes 233 | Uint8 *sm24; // samples for lower 8 bits (for 24-bit, opt) 234 | Uint32 sm24_size; // size of sm24 chunk in bytes 235 | struct sfPresetHeader *phdr; // array of all presets within file (req) 236 | Uint32 phdr_size; // size of phdr chunk in bytes 237 | struct sfPresetBag *pbag; // array of all preset zones within file (req) 238 | Uint32 pbag_size; // size of pbag chunk in bytes 239 | struct sfModList *pmod; // all preset zone modulators in file (req) 240 | Uint32 pmod_size; // size of pmod chunk in bytes 241 | struct sfGenList *pgen; // all preset zone generators in file (req) 242 | Uint32 pgen_size; // size of pgen chunk in bytes 243 | struct sfInst *inst; // array of all instruments in file (req) 244 | Uint32 inst_size; // size of inst chunk in bytes 245 | struct sfInstBag *ibag; // array of all instrument zones in file (req) 246 | Uint32 ibag_size; // size of ibag chunk in bytes 247 | struct sfModList *imod; // array of all inst zone mods in file (req) 248 | Uint32 imod_size; // size of imod chunk in bytes 249 | struct sfInstGenList *igen; // all insturment zone generators in file (req) 250 | Uint32 igen_size; // size of igen chunk in bytes 251 | struct sfSample *shdr; // array of all samples within smpl chunk (req) 252 | Uint32 shdr_size; // size of shdr chunk in bytes 253 | }; 254 | 255 | extern struct sfSFBK sf2; /* pointers to everything loaded go here */ 256 | extern struct riffChunk *load_riff(char *filename); /* load soundfont */ 257 | -------------------------------------------------------------------------------- /io_ncurses.c: -------------------------------------------------------------------------------- 1 | /************************************************************************ 2 | io_ncurses.c -- shows midi events using ncurses or printf 3 | 4 | Copyright (C) 1994-1996 Nathan I. Laredo 5 | 6 | This program is modifiable/redistributable under the terms 7 | of the GNU General Public Licence. 8 | 9 | You should have received a copy of the GNU General Public License 10 | along with this program; if not, write to the Free Software 11 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 12 | Send your comments and all your spare pocket change to 13 | laredo@gnu.ai.mit.edu (Nathan Laredo) or to PSC 1, BOX 709, 2401 14 | Kelly Drive, Lackland AFB, TX 78236-5128, USA. 15 | *************************************************************************/ 16 | #include "playmidi.h" 17 | #include 18 | #include "gsvoices.h" 19 | #include 20 | #include 21 | 22 | struct meta_event_names { 23 | int t; 24 | char *name; 25 | /* todo, add function pointer for handling */ 26 | }; 27 | 28 | struct meta_event_names metatype[] = { 29 | { SEQUENCE_NUMBER, "Sequence Number" }, 30 | { TEXT_EVENT, "Text" }, 31 | { COPYRIGHT_NOTICE, "Copyright Notice" }, 32 | { SEQUENCE_NAME, "Sequence/Track Name" }, 33 | { INSTRUMENT_NAME, "Instrument Name" }, 34 | { LYRIC, "Lyric" }, 35 | { MARKER, "Marker" }, 36 | { CUE_POINT, "Cue Point" }, 37 | { PROGRAM_NAME, "Program Name" }, 38 | { DEVICE_NAME, "Device Name" }, 39 | { CHANNEL_PREFIX, "Channel Prefix" }, 40 | { END_OF_TRACK, "End of Track" }, 41 | { SET_TEMPO, "Tempo" }, 42 | { SMPTE_OFFSET, "SMPTE Offset" }, 43 | { TIME_SIGNATURE, "Time Signature" }, 44 | { KEY_SIGNATURE, "Key Signature" }, 45 | { SEQUENCER_SPECIFIC, "Sequencer Specific" }, 46 | { META_EVENT, NULL } /* end of list */ 47 | }; 48 | 49 | char *sharps[12] = /* for a sharp key */ 50 | {"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"}; 51 | char *flats[12] = /* for a flat key */ 52 | {"C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"}; 53 | char *majflat[15] = /* name of major key with 'x' flats */ 54 | {"C", "F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb", "Fb", "Bbb", "Ebb", 55 | "Abb", "Gbb", "Cbb", "Fbb"}; /* only first 8 defined by file format */ 56 | char *majsharp[15] = /* name of major key with 'x' sharps */ 57 | {"C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#", 58 | "E#", "B#", "F##", "C##"}; /* only first 8 defined by file format */ 59 | char *minflat[15] = /* name of minor key with 'x' flats */ 60 | {"A", "D", "G", "C", "F", "Bb", "Eb", "Ab", "Db", "Gb", "Cb", 61 | "Fb", "Bbb", "Ebb", "Abb"}; /* only first 8 defined by file format */ 62 | char *minsharp[15] = /* name of minor key with 'x' sharps */ 63 | {"A", "E", "B", "F#", "C#", "G#", "D#", "A#", "E#", "B#", "F##", 64 | "C##", "G##", "D##", "A##"}; /* only first 8 defined by file format */ 65 | 66 | extern int graphics, verbose, perc; 67 | extern int format, ntrks, division; 68 | extern Uint32 ticks; 69 | extern char *filename; 70 | extern float skew; 71 | extern void seq_reset(int); 72 | extern struct timeval start_time; 73 | 74 | struct timeval now_time, want_time; 75 | char textbuf[1024], **nn; 76 | int i, ytxt, karaoke; 77 | 78 | void close_show(error) 79 | int error; 80 | { 81 | if (graphics) { 82 | attrset(A_NORMAL); 83 | refresh(); 84 | endwin(); 85 | } 86 | exit(error); 87 | } 88 | 89 | #define NOTE ((int)data[0]) 90 | #define VEL ((int)data[1]) 91 | #define OCTAVE (NOTE / 12) 92 | #define OCHAR (0x30 + OCTAVE) 93 | #define XPOS (14 + ((NOTE/2) % ((COLS - 16) / 4)) * 4) 94 | #define PXPOS (14 + ((NOTE) % ((COLS - 16) / 9)) * 9) 95 | #define YPOS (CHN(cmd) + 2) 96 | #define NNAME nn[NOTE % 12] 97 | 98 | extern struct chanstate channel[16]; // presently active channel state 99 | 100 | static char *gsfind(int pgm, int ch, int key) 101 | { 102 | int i, bank; 103 | char *rv = NULL; 104 | struct gsvoices *gs = key ? gs_drum : ISPERC(ch) ? gs_perc : gs_inst; 105 | bank = channel[ch].controller[CTL_BANK_SELECT]; 106 | bank <<= 7; 107 | bank |= channel[ch].controller[CTL_BANK_SELECT + CTL_LSB]; 108 | if (key != 0) { 109 | pgm = channel[ch].program; 110 | } 111 | for (i = 0; gs[i].name != NULL; i++) { 112 | if (gs[i].pgm <= pgm) { 113 | rv = gs[i].name; 114 | if (gs[i].pgm == pgm && gs[i].bank == bank && gs[i].key == key) { 115 | return gs[i].name; 116 | } 117 | } 118 | } 119 | return rv; 120 | } 121 | 122 | int cdeltat(t1, t2) 123 | struct timeval *t1; 124 | struct timeval *t2; 125 | { 126 | int d1, d2; 127 | 128 | d1 = t1->tv_sec - t2->tv_sec; 129 | if((d2 = t1->tv_usec - t2->tv_usec) < 0) 130 | (d2 += 1000000, d1 -= 1); 131 | d2 /= 10000; 132 | return (d2 + d1 * 100); 133 | } 134 | int updatestatus() 135 | { 136 | int ch, d1, d2; 137 | 138 | want_time.tv_sec = start_time.tv_sec + (ticks / 1000); 139 | want_time.tv_usec = start_time.tv_usec + (ticks % 1000) * 1000; 140 | if (want_time.tv_usec > 1000000) 141 | (want_time.tv_usec -= 1000000, want_time.tv_sec++); 142 | 143 | do { 144 | attrset(A_BOLD); 145 | if ((ch = getch()) != ERR) 146 | switch (ch) { 147 | case KEY_RIGHT: 148 | if ((skew -= 0.01) < 0.25) 149 | skew = 0.25; 150 | if (graphics) 151 | mvprintw(1, COLS - 6, "%0.2f", skew); 152 | break; 153 | case KEY_LEFT: 154 | if ((skew += 0.01) > 4) 155 | skew = 4.0; 156 | if (graphics) 157 | mvprintw(1, COLS - 6, "%0.2f", skew); 158 | break; 159 | case KEY_PPAGE: 160 | case KEY_UP: 161 | seq_reset(1); 162 | return (ch == KEY_UP ? 0 : -1); 163 | break; 164 | case 18: 165 | case 12: 166 | case KEY_RESIZE: 167 | wrefresh(curscr); 168 | break; 169 | case 'q': 170 | case 'Q': 171 | case 3: 172 | close_show(0); 173 | break; 174 | default: 175 | return 1; /* skip to next song */ 176 | break; 177 | } 178 | gettimeofday(&now_time, NULL); 179 | d1 = now_time.tv_sec - start_time.tv_sec; 180 | d2 = now_time.tv_usec - start_time.tv_usec; 181 | if (d2 < 0) 182 | (d2 += 1000000, d1 -= 1); 183 | mvprintw(1, 0, "%02d:%02d.%d", d1 / 60, d1 % 60, d2 / 100000); 184 | refresh(); 185 | d1 = cdeltat(&want_time, &now_time); 186 | if (0 && d1 > 10) 187 | usleep(100000); 188 | } while (1 && d1 > 30); 189 | return NO_EXIT; 190 | } 191 | 192 | void showevent(cmd, data, length) 193 | int cmd; 194 | unsigned char *data; 195 | int length; 196 | { 197 | if (cmd < CHANNEL_PREFIX) { 198 | int t; 199 | for (t = 0; metatype[t].name; t++) { 200 | if (metatype[t].t == cmd) { 201 | break; 202 | } 203 | } 204 | if (graphics && length > COLS) 205 | length = COLS; 206 | if (cmd != 1 && strncmp(textbuf, (char *)data, length - 1) == 0) 207 | return; /* ignore repeat messages, "WinJammer Demo" etc. */ 208 | if (verbose) { 209 | printf("%s: %.*s\n", metatype[t].name, length, (char *)data); 210 | } else { 211 | attrset(A_BOLD | COLOR_PAIR(cmd)); 212 | if (!karaoke || *data == '\\' || *data == '/' || 213 | (*data >= '@' && *data <= 'Z') || *data == '(' || 214 | karaoke + length > COLS || (cmd != 1 && karaoke)) { 215 | karaoke = 0; 216 | if ((++ytxt) > LINES - 1) { 217 | move(19, 0); 218 | deleteln(); 219 | ytxt = LINES - 1; 220 | } 221 | move(ytxt, 0); 222 | clrtoeol(); 223 | if (*data == '\\' || *data == '/') 224 | (data++, length--); /* karaoke newlines */ 225 | if (*data == '@') /* karaoke info */ 226 | (data += 2, length -= 2); 227 | } 228 | strncpy(textbuf, (char *)data, length < COLS - karaoke ? length : 229 | COLS - karaoke); 230 | if (length < 1024) 231 | textbuf[length] = 0; 232 | mvaddstr(ytxt, karaoke, textbuf); 233 | if (cmd == 1) { 234 | karaoke += strlen(textbuf); 235 | if (karaoke > COLS - 10) 236 | karaoke = 0; 237 | } else 238 | karaoke = 0; 239 | } 240 | } else if (cmd == KEY_SIGNATURE) { 241 | Sint8 sf = data[1]; 242 | Sint8 mi = data[2]; 243 | if (graphics || verbose) 244 | nn = ((sf & 0x80) ? flats : sharps); 245 | if (verbose) { 246 | if (mi == 0) /* major key */ 247 | printf("Key: %s major\n", (!(sf & 0x80) ? 248 | majsharp[sf & 0xf] : majflat[(-sf) & 0xf])); 249 | else /* minor key */ 250 | printf("Key: %s minor\n", (!(sf & 0x80) ? 251 | minsharp[sf & 0xf] : minflat[(-sf) & 0xf])); 252 | } 253 | if (graphics) { 254 | attrset(A_NORMAL); 255 | if (VEL) /* major key */ 256 | mvprintw(18, 36, "%3s major", (!(NOTE & 0x80) ? 257 | majsharp[NOTE] : majflat[256-NOTE])); 258 | else /* minor key */ 259 | mvprintw(18, 36, "%3s minor", (!(NOTE & 0x80) ? 260 | minsharp[NOTE] : minflat[256-NOTE])); 261 | } 262 | } else if (cmd == TIME_SIGNATURE) { 263 | attrset(A_NORMAL); 264 | mvprintw(18, 16, "%3d/%-3d", data[0], 1<<(data[1])); 265 | } else if (cmd == SET_TEMPO) { 266 | int t = ((*(data) << 16) | (data[1] << 8) | data[2]); 267 | attrset(A_NORMAL); 268 | mvprintw(18, 24, "%3d BPM", 60000000/t); 269 | } else 270 | switch (cmd & 0xf0) { 271 | case MIDI_KEY_PRESSURE: 272 | if (verbose > 4) 273 | printf("Chn %d Key Pressure %s%c=%d\n", 274 | 1 + (cmd & 0xf), NNAME, OCHAR, VEL); 275 | break; 276 | case MIDI_NOTEON: 277 | if (graphics) 278 | if (VEL) { 279 | attrset(A_BOLD | COLOR_PAIR((CHN(cmd) % 6 + 1))); 280 | if (!ISPERC(CHN(cmd)) || NOTE == 0) 281 | mvprintw(YPOS, XPOS, "%s%c", NNAME, OCHAR); 282 | else 283 | mvprintw(YPOS, PXPOS, "%8.8s", 284 | gsfind(0, CHN(cmd), NOTE)); 285 | } else { 286 | if (!ISPERC(CHN(cmd))) { 287 | mvaddstr(YPOS, XPOS, " "); 288 | } else { 289 | mvaddstr(YPOS, PXPOS, " "); 290 | } 291 | } 292 | else if (verbose > 5) 293 | printf("Chn %d Note On %s%c=%d\n", 294 | 1 + (cmd & 0xf), NNAME, OCHAR, VEL); 295 | break; 296 | case MIDI_NOTEOFF: 297 | if (graphics) { 298 | if (!ISPERC(CHN(cmd))) { 299 | mvaddstr(YPOS, XPOS, " "); 300 | } else { 301 | mvaddstr(YPOS, PXPOS, " "); 302 | } 303 | } 304 | else if (verbose > 5) 305 | printf("Chn %d Note Off %s%c=%d\n", 306 | 1 + (cmd & 0xf), NNAME, OCHAR, VEL); 307 | break; 308 | case MIDI_CTL_CHANGE: 309 | if (0 && graphics) { /* debug midi controller messages */ 310 | mvprintw(YPOS, PXPOS, "[%02x]=%02x", NOTE, VEL); 311 | } 312 | if (verbose > 5) 313 | printf("Chn %d Ctl Change %d=%d\n", 314 | 1 + (cmd & 0xf), NOTE, VEL); 315 | break; 316 | case MIDI_CHN_PRESSURE: 317 | if (verbose > 5) 318 | printf("Chn %d Pressure=%d\n", 319 | 1 + (cmd & 0xf), NOTE); 320 | break; 321 | case MIDI_PITCH_BEND: 322 | { 323 | int val = (VEL << 7) | NOTE; 324 | 325 | if (graphics) { 326 | attrset(A_BOLD); 327 | if (val > 0x2000) 328 | mvaddch(YPOS, 12, '>'); 329 | else if (val < 0x2000) 330 | mvaddch(YPOS, 12, '<'); 331 | else 332 | mvaddch(YPOS, 12, ' '); 333 | } else if (verbose > 4) 334 | printf("Chn %d Bender=0x%04x\n", 335 | 1 + CHN(cmd), val); 336 | } 337 | break; 338 | case MIDI_PGM_CHANGE: 339 | if (graphics) { 340 | attrset(COLOR_PAIR((CHN(cmd) % 6 + 1)) | A_BOLD); 341 | mvprintw(YPOS, 0, "%12.12s", gsfind(NOTE, CHN(cmd), 0)); 342 | } else if (verbose > 3) 343 | printf("Chn %d Program=%s %d\n", 1 + CHN(cmd), 344 | gsfind(NOTE, CHN(cmd), 0), NOTE + 1); 345 | break; 346 | case 0xf0: 347 | case 0xf7: 348 | if (verbose > 2) { 349 | printf("Sysex(%2x): ", cmd); 350 | for (i = 0; i < length; i++) 351 | printf("%02x", data[i]); 352 | putchar('\n'); 353 | } 354 | break; 355 | default: 356 | break; 357 | } 358 | } 359 | 360 | void init_show() 361 | { 362 | char *tmp; 363 | 364 | nn = flats; 365 | ytxt = 18; 366 | karaoke = 0; 367 | if (graphics) { 368 | clear(); 369 | attrset(A_NORMAL); 370 | mvprintw(0, 0, RELEASE " by Nathan Laredo"); 371 | mvprintw(0, 40, "Now Playing:"); 372 | mvprintw(1, 40, "[P]ause [N]ext [L]ast [O]ptions"); 373 | mvaddstr(ytxt, 0, "=-=-=-=-=-=-=-"); 374 | mvprintw(1, 0, "00:00.0 - 00:00.0, %d track%c", ntrks, 375 | ntrks > 1 ? 's' : ' '); 376 | for (i = 0; i < 16; i++) 377 | mvprintw(i + 2, 0, "Channel %2d |", i + 1); 378 | tmp = strrchr(filename, '/'); 379 | strncpy(textbuf, (tmp == NULL ? filename : tmp + 1), COLS - 53); 380 | attrset(A_BOLD); 381 | mvaddstr(0, 53, textbuf); 382 | mvaddch(1, 41, 'P'); 383 | mvaddch(1, 49, 'N'); 384 | mvaddch(1, 56, 'L'); 385 | mvaddch(1, 63, 'O'); 386 | mvaddstr(0, 0, RELEASE); 387 | refresh(); 388 | } else if (verbose) { 389 | printf("** Now Playing \"%s\"\n", filename); 390 | printf("** Format: %d, Tracks: %d, Division: %d\n", format, 391 | ntrks, division); 392 | } 393 | } 394 | 395 | void setup_show(argc, argv) 396 | int argc; 397 | char **argv; 398 | { 399 | if (graphics) { 400 | initscr(); 401 | start_color(); 402 | verbose = 0; 403 | init_pair(1, COLOR_RED, COLOR_BLACK); 404 | init_pair(2, COLOR_GREEN, COLOR_BLACK); 405 | init_pair(3, COLOR_YELLOW, COLOR_BLACK); 406 | init_pair(4, COLOR_BLUE, COLOR_BLACK); 407 | init_pair(5, COLOR_MAGENTA, COLOR_BLACK); 408 | init_pair(6, COLOR_CYAN, COLOR_BLACK); 409 | init_pair(7, COLOR_WHITE, COLOR_BLACK); 410 | raw(); 411 | noecho(); 412 | nodelay(stdscr, TRUE); 413 | keypad(stdscr, TRUE); 414 | } 415 | } 416 | -------------------------------------------------------------------------------- /playmidi.h: -------------------------------------------------------------------------------- 1 | /* playmidi.h - defines and structures used to play midi files 2 | * 3 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 4 | * 5 | * This file may be freely distributed under the terms of 6 | * the GNU General Public Licence (GPL). 7 | */ 8 | 9 | #define RELEASE "Playmidi 2.9" 10 | 11 | #include "SDL2/SDL.h" 12 | #include "SDL2/SDL_audio.h" 13 | 14 | #include "soundfont2.h" 15 | 16 | /* change this if you have really outrageous midi files > 128 tracks */ 17 | /* FIXME: should probably make this a malloc during midi file load */ 18 | #define MAXTRKS 128 19 | 20 | /* many of these constant names were previously used from linux includes */ 21 | /* now expanded vs linux system defines with new GM2 standard controllers */ 22 | enum midi_controller_numbers { 23 | CTL_BANK_SELECT = 0x00, /* not reset on "reset all controllers" */ 24 | CTL_MODWHEEL = 0x01, /* 0(default)-127(max) modulation */ 25 | CTL_BREATH = 0x02, 26 | CTL_LFO_RATE = 0x03, /* minimoog voyager: adjust LFO frequency */ 27 | CTL_FOOT = 0x04, 28 | CTL_PORTAMENTO_TIME = 0x05, 29 | CTL_DATA_ENTRY = 0x06, 30 | CTL_MAIN_VOLUME = 0x07, /* not reset on "reset all controllers" */ 31 | CTL_BALANCE = 0x08, 32 | CTL_PAN = 0x0a, /* 0(l)-127(r), default=64 (not reset) */ 33 | CTL_EXPRESSION = 0x0b, /* channel 0(0%) - 16384(100%, default) */ 34 | CTL_MOTIONAL_CTL1 = 0x0c, /* roland integra 7, Part L-R param */ 35 | CTL_MOTIONAL_CTL2 = 0x0d, /* roland integra 7, Part F-B param */ 36 | CTL_MOTIONAL_CTL3 = 0x0e, /* roland integra 7, Part ambience level */ 37 | CTL_GENERAL_PURPOSE1 = 0x10, 38 | CTL_TONE_MODIFY1 = 0x10, /* roland name */ 39 | CTL_GENERAL_PURPOSE2 = 0x11, 40 | CTL_TONE_MODIFY2 = 0x11, /* roland name */ 41 | CTL_GENERAL_PURPOSE3 = 0x12, 42 | CTL_GENERAL_PURPOSE4 = 0x13, 43 | CTL_MOTIONAL_EXT1 = 0x1c, /* roland integra 7, PartEx L-R param */ 44 | CTL_MOTIONAL_EXT2 = 0x1d, /* roland integra 7, PartEx F-B param */ 45 | CTL_MOTIONAL_EXT3 = 0x1e, /* roland integra 7, PartEx amb level */ 46 | CTL_LSB = 0x20, /* above all have LSB at (CTL_* + CTL_LSB) */ 47 | /* controllers #64 to #69 (0x40 to 0x45) are on/off switches. */ 48 | CTL_DAMPER_PEDAL = 0x40, /* 0(default)-63 = off, 64 - 127 = on */ 49 | CTL_SUSTAIN = 0x40, 50 | CTL_HOLD = 0x40, /* 0-63 = off(default), 64-127 sustain all */ 51 | CTL_HOLD1 = 0x40, 52 | CTL_PORTAMENTO = 0x41, /* 0(default)-63 = off, 64 - 127 = on */ 53 | CTL_SOSTENUTO = 0x42, /* 0(default)-63 = off, 64 - 127 = on */ 54 | CTL_SOFT_PEDAL = 0x43, /* 0(default)-63 = off, 64 - 127 = on */ 55 | CTL_LEGATO = 0x44, /* 0(default)-63 = off, 64 - 127 = on */ 56 | CTL_HOLD2 = 0x45, 57 | /* controllers #70 - #79 are not reset on "reset all controllers" */ 58 | CTL_SOUND_VARIATION = 0x46, /* RP-021: sound controller 1 */ 59 | CTL_RESONANCE = 0x47, /* sc2: timbre/harmonic intensity */ 60 | CTL_RELEASE_TIME = 0x48, /* RP-021: sound controller 3 */ 61 | CTL_ATTACK_TIME = 0x49, /* RP-021: sound controller 4 */ 62 | CTL_CUTOFF = 0x4a, /* RP-021: sound controller 5 */ 63 | CTL_BRIGHTNESS = 0x4a, /* RP-021: sound controller 5 */ 64 | CTL_DECAY_TIME = 0x4b, /* RP-021: sound controller 6 */ 65 | CTL_VIBRATO_RATE = 0x4c, /* RP-021: sound controller 7 */ 66 | CTL_VIBRATO_DEPTH = 0x4d, /* RP-021: sound controller 8 */ 67 | CTL_VIBRATO_DELAY = 0x4e, /* RP-021: sound controller 9 */ 68 | CTL_SOUND_CONTROLLER10= 0x4f, /* source: MMA recommended practice RP-021 */ 69 | CTL_GENERAL_PURPOSE5 = 0x50, 70 | CTL_TONE_VARIATION1 = 0x50, 71 | CTL_GENERAL_PURPOSE6 = 0x51, 72 | CTL_TONE_VARIATION2 = 0x51, 73 | CTL_GENERAL_PURPOSE7 = 0x52, 74 | CTL_TONE_VARIATION3 = 0x52, 75 | CTL_GENERAL_PURPOSE8 = 0x53, 76 | CTL_TONE_VARIATION4 = 0x53, 77 | CTL_PORTAMENTO_CTRL = 0x54, 78 | CTL_VELOCITY_LSB = 0x58, /* MMA CA-031: high resolution velocity lsb */ 79 | /* controllers #91 - #95 are not reset on "reset all controllers" */ 80 | CTL_REVERB_DEPTH = 0x5b, /* MMA recommended practice RP-023 renamed */ 81 | CTL_TREMOLO_DEPTH = 0x5c, 82 | CTL_CHORUS_DEPTH = 0x5d, /* MMA recommended practice RP-023 renamed */ 83 | CTL_DETUNE_DEPTH = 0x5e, 84 | CTL_CELESTE_DEPTH = 0x5e, 85 | CTL_PHASER_DEPTH = 0x5f, 86 | CTL_DATA_INCREMENT = 0x60, 87 | CTL_DATA_DECREMENT = 0x61, 88 | CTL_NRPN_LSB = 0x62, /* default = 127 (null value) */ 89 | CTL_NRPN_MSB = 0x63, /* default = 127 (null value) */ 90 | CTL_RPN_LSB = 0x64, /* default = 127 (null value) */ 91 | CTL_RPN_MSB = 0x65, /* default = 127 (null value) */ 92 | /* controllers #120 - #127 are not reset on "reset all controllers" */ 93 | CTL_ALL_SOUNDS_OFF = 0x78, 94 | CTL_RESET_ALL_CONTROLLERS = 0x79, /* 3rd byte 0x00 */ 95 | CTL_LOCAL = 0x7a, /* 0 = off, 127 = on */ 96 | CTL_ALL_NOTES_OFF = 0x7b, /* 3rd byte 0x00 */ 97 | CTL_OMNI_OFF = 0x7c, /* 3rd byte 0x00 */ 98 | CTL_OMNI_ON = 0x7d, /* 3rd byte 0x00 */ 99 | CTL_MONO = 0x7e, /* 3rd byte 0x00 - 0x10 (mono number) */ 100 | CTL_POLY = 0x7f, /* 3rd byte 0x00 */ 101 | }; 102 | 103 | enum midi_rpn_destinations { 104 | RPN_PITCH_BEND_RANGE = 0x00, /* mm = semitones, ll = cents, default=mm=2 */ 105 | /* old: master fine is adjusted, new gm2: only channel fine is adjusted */ 106 | RPN_CHN_FINE_TUNE = 0x01, /* -8192*50/8192 - 0 - +8192*50/8192 cent */ 107 | RPN_CHN_COARSE_TUNE = 0x02, /* -48 - 0 - +48 semitones, ll = ignored */ 108 | RPN_TUNING_PGM_SEL = 0x03, 109 | RPN_TUNING_BANK_SEL = 0x04, 110 | RPN_MOD_DEPTH_RANGE = 0x05, 111 | RPN_AZIMUTH_ANGLE = 0x3d00, /* see RP-049 for all 3D sound controllers */ 112 | RPN_ELEVATION_ANGLE = 0x3d01, 113 | RPN_GAIN = 0x3d02, 114 | RPN_DISTANCE_RATIO = 0x3d03, 115 | RPN_MAX_DISTANCE = 0x3d04, 116 | RPN_GAIN_AT_MAX_DISTANCE= 0x3d05, 117 | RPN_REF_DISTANCE_RATIO= 0x3d06, 118 | RPN_PAN_SPREAD_ANGLE = 0x3d07, 119 | RPN_ROLL_ANGLE = 0x3d08, 120 | RPN_NULL = 0x7f7f, 121 | }; 122 | 123 | enum midi_nrpn_destinations { 124 | NRPN_GS_VIBRATO_RATE = 0x0108, /* -64 - 0(0x40) - +63 (relative) */ 125 | NRPN_GS_VIBRATO_DEPTH = 0x0109, /* -64 - 0(0x40) - +63 (relative) */ 126 | NRPN_GS_VIBRATO_DELAY = 0x010a, /* -64 - 0(0x40) - +63 (relative) */ 127 | /* TVF = Time Variant Filter */ 128 | NRPN_GS_TVF_CUTOFF_FREQ = 0x0120, /* -64 - 0(0x40) - +63 (relative) */ 129 | NRPN_GS_TVF_RESONANCE = 0x0121, /* -64 - 0(0x40) - +63 (relative) */ 130 | /* TVA = Time Variant Amplifier */ 131 | NRPN_GS_TVFTVA_ATTACK = 0x0163, /* -64 - 0(0x40) - +63 (relative) */ 132 | NRPN_GS_TVFTVA_DECAY = 0x0164, /* -64 - 0(0x40) - +63 (relative) */ 133 | NRPN_GS_TVFTVA_RELEASE= 0x0166, /* -64 - 0(0x40) - +63 (relative) */ 134 | NRPN_GS_RHYTHM_PITCH_C= 0x1800, /* 0x18rr rr = note number (abs) */ 135 | NRPN_GS_RHYTHM_TVA_LVL= 0x1a00, /* 0x1arr rr = note number (abs) */ 136 | /* note: panpot -64 = "random", -63(L) - 0(Center) - +63(R) */ 137 | NRPN_GS_RHYTHM_PANPOT = 0x1c00, /* 0x1crr rr = note number (abs) */ 138 | NRPN_GS_RHYTHM_REVERB = 0x1d00, /* 0x1drr rr = note number (abs) */ 139 | NRPN_GS_RHYTHM_CHORUS = 0x1e00, /* 0x1err rr = note number (abs) */ 140 | NRPN_NULL = 0x7f7f, 141 | }; 142 | 143 | enum midi_command_prefixes { 144 | MIDI_NOTEOFF = 0x80, /* 0x8n 0xkk 0xvv n=ch, kk=key, vv=vel */ 145 | MIDI_NOTEON = 0x90, /* 0x9n 0xkk 0xvv n=ch, kk=key, vv=vel */ 146 | MIDI_KEY_PRESSURE = 0xa0, /* 0xan 0xkk 0xvv n=ch, kk=key, vv=value */ 147 | MIDI_CTL_CHANGE = 0xb0, /* 0xbn 0xll 0xnn n=ch, nn=ctl#, mm=value */ 148 | MIDI_PGM_CHANGE = 0xc0, /* 0xcn 0xpp n=ch, pp=program number */ 149 | MIDI_CHN_PRESSURE = 0xd0, /* 0xdn 0xvv n=ch, vv=value */ 150 | MIDI_PITCH_BEND = 0xe0, /* 0xen 0xll 0xmm n=ch, ll=lsb mm=msb */ 151 | MIDI_SYSTEM_PREFIX = 0xf0, 152 | }; 153 | 154 | enum midi_system_messages { 155 | MIDI_SYSTEM_EXCLUSIVE = 0xf0, 156 | MIDI_TIME_CODE_QF = 0xf1, 157 | MIDI_SONG_POSITION = 0xf2, 158 | MIDI_SONG_SELECT = 0xf3, 159 | MIDI_TUNE_REQUEST = 0xf6, 160 | MIDI_SYSEX_END = 0xf7, 161 | MIDI_TIMING_CLOCK = 0xf8, 162 | MIDI_START = 0xfa, 163 | MIDI_CONTINUE = 0xfb, 164 | MIDI_STOP = 0xfc, 165 | MIDI_ACTIVE_SENSING = 0xfe, 166 | MIDI_RESET = 0xff, 167 | }; 168 | 169 | #define CMD(x) ((x) & 0xf0) 170 | #define CHN(x) ((x) & 0x0f) 171 | 172 | #define ISPERC(x) (perc & (1 << (x))) 173 | #define ISMIDI(x) (play_ext & (1 << (x))) 174 | #define ISPLAYING(x) (chanmask & (1 << (x))) 175 | #define NO_EXIT 100 176 | 177 | struct lfostate { 178 | float r; // value to add to timebase each sample 179 | float t; // timebase for this generator, 0 - 2pi 180 | float v; // output value for this generator 181 | }; 182 | 183 | struct chanstate { 184 | float bender_mult; // value to multiply per tone 'r' by for pitchbend 185 | float mod_mult; // value to multiply lfo per tone 'r' by for modwheel 186 | int program; // midi program to play for this channel, < 0 = use math 187 | int bender; // midi pitch bend value in effect, default = 8192 188 | int bender_range; // pitchbend range in cents, default = 200 189 | int controller[256]; // up to 14-bit midi controller data values, see CTL_* 190 | int pressure; // 0(default)-127, has no effect by default 191 | struct lfostate mod; // modulation lfo generator 192 | struct lfostate vib; // vibrato lfo generator 193 | }; 194 | 195 | struct sf2gen { 196 | Uint32 dwStart; // final generated starting absolute sample in sdata 197 | Uint32 dwEnd; // final generated ending absolute sample in sdata 198 | Uint32 dwStartloop; // final generated loop start point sample in sdata 199 | Uint32 dwEndloop; // final generated loop end point sample in sdata 200 | Uint32 sampleModes; // 0 = no loop, 1 = loop, 2 = no loop, 3 = loop+finish 201 | }; 202 | 203 | struct voice_env { 204 | float a, h, d, r; // attack, hold, decay, release in units of samples 205 | float s; // velocity at which to sustain note 206 | 207 | }; 208 | 209 | struct voicestate { 210 | float f; // frequency for this voice 211 | float r; // value to add to timebase each sample 212 | float v; // velocity 0.0 - 1.0 (see also CTL_VELOCITY_LSB) 213 | float t; // timebase for this note, math 0 - 2pi, or sample pos 214 | float pan; // pan, 0.0(l) - 1.0(r), 0.5 = center 215 | int note; // midi note number being played 216 | int vel; // velocity from midi note on event 217 | int channel; // midi channel for this note 0-15 218 | int sustain; // if note off is deferred by CTL_SUSTAIN, sustain=1 219 | int exclusive_class; // if > 0, new notes terminate others of same ch+class 220 | Uint64 endstamp; // event end: sample position at note off plus release 221 | Uint64 timestamp; // event start: global running sample count position 222 | struct voice_env env; // volume envelope, adsr timed in sample units 223 | struct sf2gen s; // sf2 sample data, dwStart == dwEnd means no samples 224 | 225 | // sf2 access tracking, used at note-on time only to initialize voice 226 | int phdr; // index into phdr chunk 227 | int pbag, pbag_max; // current and last index into pbag chunk 228 | int pgen, pgen_max; // current and last index into pgen chunk 229 | int pmod, pmod_max; // current and last index into pmod chunk (unused) 230 | int inst; // current index into inst chunk 231 | int ibag, ibag_max; // current and last index into ibag chunk 232 | int igen, igen_max; // current and last index into igen chunk 233 | int imod, imod_max; // current and last index into imod chunk (unused) 234 | int shdr; // current index into shdr chunk 235 | }; 236 | 237 | struct midi_packet { 238 | Uint64 timestamp; // event start in samples 239 | Uint16 len; // length of event data in bytes 240 | Uint8 data[0]; // data for event 241 | }; 242 | 243 | /* Non-standard MIDI file formats */ 244 | #define RIFF 0x52494646 245 | #define CTMF 0x43544d46 246 | /* Standard MIDI file format definitions */ 247 | #define MThd 0x4d546864 248 | #define MTrk 0x4d54726b 249 | 250 | enum smf_meta_events { 251 | SEQUENCE_NUMBER = 0x00, 252 | TEXT_EVENT = 0x01, 253 | COPYRIGHT_NOTICE = 0x02, 254 | SEQUENCE_NAME = 0x03, 255 | INSTRUMENT_NAME = 0x04, 256 | LYRIC = 0x05, 257 | MARKER = 0x06, 258 | CUE_POINT = 0x07, 259 | PROGRAM_NAME = 0x08, /* added: MMA recommended practice RP-019 */ 260 | DEVICE_NAME = 0x09, /* added: MMA recommended practice RP-019 */ 261 | CHANNEL_PREFIX = 0x20, 262 | END_OF_TRACK = 0x2f, 263 | SET_TEMPO = 0x51, 264 | SMPTE_OFFSET = 0x54, 265 | TIME_SIGNATURE = 0x58, 266 | KEY_SIGNATURE = 0x59, 267 | SEQUENCER_SPECIFIC = 0x74, 268 | META_EVENT = 0xff /* prefixes all the above in the midi file */ 269 | }; 270 | 271 | struct miditrack { 272 | Uint8 *data; /* data of midi track */ 273 | Uint32 length; /* length of track data */ 274 | Uint32 index; /* current byte in track */ 275 | Uint32 ticks; /* current midi tick count */ 276 | Uint8 running_st; /* running status byte */ 277 | }; 278 | 279 | /* hardware specific midi access abstracted by the following */ 280 | extern void init_midi(void); 281 | extern void close_midi(void); 282 | extern void show_ports(void); 283 | extern void midi_add_pkt(struct midi_packet *p); 284 | extern void midi_send_sysex(int length, Uint8 *data, int type); 285 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 675 Mass Ave, Cambridge, MA 02139, USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | Appendix: How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 19yy 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) 19yy name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Library General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /emumidi.c: -------------------------------------------------------------------------------- 1 | /* emumidi.c - software synth engine with optional hardware midi output 2 | * CoreMIDI is the output assumed here, api emulated if needed for 3 | * cross-platform support. 4 | * 5 | * Copyright 2015 Nathan Laredo (laredo@gnu.org) 6 | * 7 | * This file may be freely distributed under the terms of 8 | * the GNU General Public Licence (GPL). 9 | * 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "playmidi.h" 21 | 22 | /* fixme: these things should move inside a structure and include */ 23 | extern int play_ext; 24 | extern int chanmask, perc, dochan, MT32; 25 | extern Uint32 ticks; 26 | extern int useprog[16]; 27 | extern char *sf2_filename; 28 | extern void seq_reset(int); 29 | extern void load_sf2(char *); 30 | 31 | #define CHANNEL (dochan ? chn : 0) 32 | 33 | #define SAMPLELEN 512 34 | #define SAMPLERATE 96000 35 | #define PACKET_LIST_BYTES 65536 36 | #define POLYMAX 128 37 | #define NOTE_MAXLEN 0x7fffffff 38 | 39 | static float rate = SAMPLERATE; 40 | int channels = 2; 41 | static SDL_AudioDeviceID sdl_dev = 0; 42 | 43 | static Uint8 pdata[PACKET_LIST_BYTES]; // space for queued midi events 44 | struct midi_packet *tseq = (void *)pdata; // queued midi events to play 45 | struct midi_packet *tseqh = (void *)pdata; // enqueue position in above 46 | struct midi_packet *tseqt = (void *)pdata; // dequeue position in above 47 | 48 | struct voicestate voice[POLYMAX]; // active voices, samples = 0 = inactive 49 | struct chanstate channel[16]; // presently active channel state 50 | Uint64 samplepos = 0; // current position in the sample output 51 | float atune = 440.0; // this will affect all midi note conversions 52 | static float scaletune[16][12]; // 16 channels of tuning adjust 53 | 54 | // convert a negative cB value to linear 0 - 1.0 55 | float cB_to_linear(float cB) 56 | { 57 | if (cB > 0.0) { 58 | return 1.0; 59 | } 60 | return pow(10, cB / 200.0); 61 | } 62 | 63 | // convert a floating point frequency to intger midi note number 64 | Uint8 freq_to_note(float freq) 65 | { 66 | float d = 69 + 12 * log2(freq / atune); 67 | return (Uint8) d; 68 | } 69 | 70 | // convert a controller scaled cents value to frequency multiplier 71 | float cents_to_freqmult(float cents, Uint16 num, Uint16 den) 72 | { 73 | return pow(2, (float)num * (cents / 1200.0) / (float)den); 74 | } 75 | 76 | // convert a midi note number to floating point frequency 77 | float note_to_freq(Uint8 note, Uint16 centsperkey, int ch) 78 | { 79 | float freq = pow(2, ((float)(note) - 69.0) * 80 | (float) centsperkey / 1200.0) * atune; 81 | freq *= scaletune[ch][note % 12]; 82 | return freq; 83 | } 84 | 85 | // convert a midi pitchbend to a frequency multiplier 86 | float pitchbend_to_freqmult(Uint16 pitchbend, Uint16 rpn0) 87 | { 88 | float mult, range = rpn0; 89 | 90 | if (range > 12) { // roland ignores ranges larger than one octave 91 | range = 2; // and seems to produce the default range instead 92 | } 93 | 94 | mult = pow(2, ((float)(pitchbend) - 8192.0) * (range / 12.0) / 8192.0); 95 | return mult; 96 | } 97 | 98 | void apply_generators(int min, int max, void *g, int j) 99 | { 100 | // static values are reinitialized after applying the final generators 101 | static int newnote = -1; 102 | static int coarseTune = 0, fineTune = 0, scaleTuning = 100; 103 | static int sOff = 0, eOff = 0, sLoopOff = 0, eLoopOff = 0; 104 | struct sfGenList *gen = g; 105 | int p, preset_level = (g == sf2.pgen); 106 | 107 | for (p = min; p < max; p++) { 108 | switch (gen[p].sfGenOper) { 109 | case SFG_startAddrsOffset: 110 | if (preset_level) break; // not valid at this level 111 | sOff += gen[p].genAmount.shAmount; 112 | break; 113 | case SFG_endAddrsOffset: 114 | if (preset_level) break; // not valid at this level 115 | eOff += gen[p].genAmount.shAmount; 116 | break; 117 | case SFG_startloopAddrsOffset: 118 | if (preset_level) break; // not valid at this level 119 | sLoopOff += gen[p].genAmount.shAmount; 120 | break; 121 | case SFG_endloopAddrsOffset: 122 | if (preset_level) break; // not valid at this level 123 | eLoopOff += gen[p].genAmount.shAmount; 124 | break; 125 | case SFG_startAddrsCoarseOffset: 126 | if (preset_level) break; // not valid at this level 127 | sOff += gen[p].genAmount.shAmount * 32768; 128 | break; 129 | case SFG_modLfoToPitch: 130 | break; 131 | case SFG_vibLfoToPitch: 132 | break; 133 | case SFG_modEnvToPitch: 134 | break; 135 | case SFG_initialFilterFc: 136 | break; 137 | case SFG_initialFilterQ: 138 | break; 139 | case SFG_modLfoToFilterFc: 140 | break; 141 | case SFG_endAddrsCoarseOffset: 142 | eOff += gen[p].genAmount.shAmount * 32768; 143 | break; 144 | case SFG_modLfoToVolume: 145 | break; 146 | case SFG_chorusEffectsSend: 147 | break; 148 | case SFG_reverbEffectsSend: 149 | break; 150 | case SFG_pan: 151 | { 152 | float pan = gen[p].genAmount.shAmount; 153 | pan /= 500.0; // new range -1.0 to 1.0 154 | pan += 1.0; // new range 0.0 to 2.0 155 | pan /= 2.0; // new range 0.0 to 1.0 156 | voice[j].pan = pan; 157 | } 158 | break; 159 | case SFG_delayModLFO: 160 | break; 161 | case SFG_freqModLFO: 162 | break; 163 | case SFG_delayVibLFO: 164 | break; 165 | case SFG_freqVibLFO: 166 | break; 167 | case SFG_delayModEnv: 168 | break; 169 | case SFG_attackModEnv: 170 | break; 171 | case SFG_holdModEnv: 172 | break; 173 | case SFG_decayModEnv: 174 | break; 175 | case SFG_sustainModEnv: 176 | break; 177 | case SFG_releaseModEnv: 178 | break; 179 | case SFG_keynumToModEnvHold: 180 | break; 181 | case SFG_keynumToModEnvDecay: 182 | break; 183 | case SFG_delayVolEnv: 184 | voice[j].timestamp += rate * 185 | cents_to_freqmult(gen[p].genAmount.shAmount, 1, 1); 186 | break; 187 | case SFG_attackVolEnv: 188 | voice[j].env.a = rate * 189 | cents_to_freqmult(gen[p].genAmount.shAmount, 1, 1); 190 | break; 191 | case SFG_holdVolEnv: 192 | voice[j].env.h = rate * 193 | cents_to_freqmult(gen[p].genAmount.shAmount, 1, 1); 194 | break; 195 | case SFG_decayVolEnv: 196 | voice[j].env.d = rate * 197 | cents_to_freqmult(gen[p].genAmount.shAmount, 1, 1); 198 | break; 199 | case SFG_sustainVolEnv: 200 | if (gen[p].genAmount.shAmount > 1440) { 201 | gen[p].genAmount.shAmount = 1440; 202 | } 203 | // values less than zero are effectively zero with no decay time 204 | if (gen[p].genAmount.shAmount <= 0) { 205 | voice[j].env.d = 0; 206 | voice[j].env.s = 1.0; 207 | } else { 208 | voice[j].env.s = cB_to_linear(0 - (float)gen[p].genAmount.shAmount); 209 | } 210 | break; 211 | case SFG_releaseVolEnv: 212 | voice[j].env.r = rate * 213 | cents_to_freqmult(gen[p].genAmount.shAmount, 1, 1); 214 | break; 215 | case SFG_keynumToVolEnvHold: 216 | voice[j].env.h *= 217 | cents_to_freqmult((float)(60 - voice[j].note) * 218 | (float)gen[p].genAmount.shAmount, 1, 1); 219 | break; 220 | case SFG_keynumToVolEnvDecay: 221 | voice[j].env.d *= 222 | cents_to_freqmult((float)(60 - voice[j].note) * 223 | (float)gen[p].genAmount.shAmount, 1, 1); 224 | break; 225 | case SFG_startloopAddrsCoarseOffset: 226 | sLoopOff += gen[p].genAmount.shAmount * 32768; 227 | break; 228 | case SFG_keynum: 229 | if (preset_level) break; // not valid at this level 230 | voice[j].note = gen[p].genAmount.wAmount; 231 | break; 232 | case SFG_velocity: 233 | voice[j].v = (float)gen[p].genAmount.wAmount / 127.0; 234 | break; 235 | case SFG_initialAttenuation: 236 | if (gen[p].genAmount.wAmount > 1440) { 237 | gen[p].genAmount.wAmount = 1440; 238 | } 239 | voice[j].v *= cB_to_linear(0 - (float)gen[p].genAmount.wAmount); 240 | break; 241 | case SFG_endloopAddrsCoarseOffset: 242 | if (preset_level) break; // not valid at this level 243 | eLoopOff += gen[p].genAmount.shAmount * 32768; 244 | break; 245 | case SFG_coarseTune: 246 | coarseTune = gen[p].genAmount.shAmount; 247 | break; 248 | case SFG_fineTune: 249 | fineTune = gen[p].genAmount.shAmount; 250 | break; 251 | case SFG_sampleModes: 252 | if (preset_level) break; // not valid at this level 253 | voice[j].s.sampleModes = gen[p].genAmount.wAmount; 254 | break; 255 | case SFG_scaleTuning: 256 | scaleTuning = gen[p].genAmount.wAmount; 257 | break; 258 | case SFG_exclusiveClass: 259 | if (preset_level) break; // not valid at this level 260 | voice[j].exclusive_class = gen[p].genAmount.wAmount; 261 | break; 262 | case SFG_overridingRootKey: 263 | if (preset_level) break; // not valid at this level 264 | newnote = gen[p].genAmount.shAmount; 265 | break; 266 | default: 267 | break; 268 | } 269 | } 270 | if (voice[j].shdr >= 0) { 271 | int s = voice[j].shdr; 272 | int ch = voice[j].channel; 273 | // finalize application of generator values 274 | voice[j].s.dwStart = sf2.shdr[s].dwStart + sOff; 275 | voice[j].s.dwEnd = sf2.shdr[s].dwEnd + eOff; 276 | voice[j].s.dwStartloop = sf2.shdr[s].dwStartloop + sLoopOff; 277 | voice[j].s.dwEndloop = sf2.shdr[s].dwEndloop + eLoopOff; 278 | voice[j].f = note_to_freq(voice[j].note, scaleTuning, ch); 279 | voice[j].r = voice[j].f / note_to_freq(newnote < 0 ? 280 | sf2.shdr[s].byOriginalKey : newnote, scaleTuning, ch) * 281 | ((float)sf2.shdr[s].dwSampleRate / rate); 282 | voice[j].r *= cents_to_freqmult(coarseTune * 100.0, 1, 1); 283 | voice[j].r *= cents_to_freqmult(fineTune, 1, 1); 284 | voice[j].r *= cents_to_freqmult(sf2.shdr[s].chCorrection, 1, 1); 285 | if (voice[j].exclusive_class) { 286 | for (s = 0; s < POLYMAX; s++) { 287 | if (s != j && voice[s].channel == ch && 288 | voice[s].exclusive_class == voice[j].exclusive_class) { 289 | voice[j].endstamp = samplepos + voice[j].env.r; 290 | } 291 | } 292 | } 293 | // reinitialize static generator values to sf2 defaults 294 | newnote = -1; coarseTune = 0; fineTune = 0; scaleTuning = 100; 295 | sOff = 0; eOff = 0; sLoopOff = 0; eLoopOff = 0; 296 | } 297 | } 298 | 299 | struct midi_packet *next_pkt(struct midi_packet *p) 300 | { 301 | p = (struct midi_packet *)&(p)->data[(p)->len]; 302 | if ((Uint8 *)p > 303 | pdata + PACKET_LIST_BYTES - (sizeof(struct midi_packet) + 5)) { 304 | p = tseq; /* wrap around to start of buffer */ 305 | } 306 | return p; 307 | } 308 | 309 | static struct midi_packet *add_pkt(struct midi_packet *p) 310 | { 311 | /* timestamp is in samples since start of output */ 312 | p->timestamp = ticks * rate / 1000.0; 313 | if (ISMIDI((p->data[0] & 0xf))) { 314 | midi_add_pkt(p); 315 | return p; 316 | } 317 | p = next_pkt(p); 318 | /* make sure there is room for at least one more 5 byte midi packet */ 319 | if ((Uint8 *)p > 320 | pdata + PACKET_LIST_BYTES - (sizeof(struct midi_packet) + 5)) { 321 | p->len = 0; /* mark last packet before wrap around */ 322 | p = tseq; /* wrap around to start of buffer */ 323 | } 324 | /* if head reaches tail after adding, buffer is full but looks empty */ 325 | if (p == tseqt) { 326 | fprintf(stderr, "midi packet buffer too small\n"); 327 | exit(1); 328 | } 329 | return p; 330 | } 331 | 332 | // fill_audio(): callback that will fill supplied buffer with audio data 333 | // udata: parameter supplied in SDL_AudioSpec userdata field 334 | // stream: pointer to the audio data buffer to be filled 335 | // len: the length of that buffer in bytes 336 | void fill_audio(void *udata, Uint8 *stream, int len) 337 | { 338 | float vmod; // volume mod for ADSR implementation 339 | static float tlfo = 0.0; 340 | static float rlfo = 0.0; 341 | float left, right, lfo; 342 | int i, j, ch, pgm, voices; 343 | int nindex_max = len; /* index of sample with the maximum value in window */ 344 | static float max_val = 0.0; /* actual max sample value in window */ 345 | static float normalize = 1.0; 346 | float *f32s = (float *)stream; 347 | len >>= 3; // convert from bytes to samples 348 | 349 | if (rlfo == 0) { 350 | rlfo = 2.0 * M_PI * 8.176 / rate; // 8.176hz lfo by default 351 | } 352 | for (i = 0; i < len; i++) { 353 | // triangle 354 | lfo = fabs(0.3184 * (tlfo - M_PI)) - 1.0; 355 | //lfo = sin(tlfo); 356 | tlfo += rlfo; 357 | if (tlfo > 2 * M_PI) { 358 | tlfo -= 2 * M_PI; 359 | } 360 | while (tseqh != tseqt && tseqt->timestamp <= samplepos) { 361 | /* found midi event starting at this sample position to process */ 362 | int cmd = tseqt->data[0]; 363 | ch = cmd & 0xf; 364 | switch (cmd & 0xf0) { 365 | case MIDI_NOTEOFF: 366 | for (j = 0; j < POLYMAX; j++) { 367 | if (voice[j].channel == ch && voice[j].note == tseqt->data[1] && 368 | voice[j].endstamp == NOTE_MAXLEN) { 369 | if (channel[ch].controller[CTL_SUSTAIN] >= 64) { 370 | voice[j].sustain = 1; 371 | continue; 372 | } 373 | voice[j].endstamp = samplepos + voice[j].env.r; 374 | if (voice[j].s.sampleModes != 1) { 375 | voice[j].s.sampleModes = 0; // tell voice to finish past loop 376 | } 377 | } 378 | } 379 | break; 380 | case MIDI_NOTEON: 381 | /* find an empty voice to use for note start */ 382 | pgm = POLYMAX; 383 | for (j = 0; j < POLYMAX; j++) { 384 | if (voice[j].channel == ch && voice[j].note == tseqt->data[1] && 385 | voice[j].endstamp == NOTE_MAXLEN) { 386 | /* stop any existing playing voice on the same note/chan */ 387 | voice[j].endstamp = samplepos + voice[j].env.r; 388 | if (voice[j].s.sampleModes != 1) { 389 | voice[j].s.sampleModes = 0; // tell voice to finish past loop 390 | } 391 | } 392 | if (voice[j].endstamp < samplepos) { 393 | pgm = j; 394 | } 395 | } 396 | if (j < 0) 397 | break; 398 | j = pgm; 399 | if (j >= POLYMAX) { /* steal oldest voice if none free */ 400 | Uint64 oldest = ~0; 401 | int jold = j; 402 | for (j = 0; j < POLYMAX; j++) { 403 | if (voice[j].timestamp < oldest) { 404 | oldest = voice[j].timestamp; 405 | jold = j; 406 | } 407 | } 408 | j = jold; 409 | } 410 | if (j < POLYMAX) { 411 | memset(&voice[j], 0, sizeof(voice[j])); 412 | voice[j].note = tseqt->data[1]; 413 | voice[j].f = note_to_freq(voice[j].note, 100, ch); 414 | voice[j].r = 2 * M_PI * voice[j].f / rate; 415 | voice[j].vel = tseqt->data[2]; 416 | voice[j].v = (float)tseqt->data[2] / 128.0; 417 | voice[j].t = 0.0; 418 | voice[j].env.a = cents_to_freqmult(-12000, 1, 1) * rate; 419 | voice[j].env.h = voice[j].env.a; 420 | voice[j].env.d = voice[j].env.a; 421 | voice[j].env.s = 1.0; 422 | voice[j].env.r = voice[j].env.a; 423 | voice[j].pan = (float)channel[ch].controller[CTL_PAN] / 127.0; 424 | voice[j].channel = ch; 425 | voice[j].endstamp = NOTE_MAXLEN; // set at noteoff event 426 | voice[j].timestamp = samplepos; 427 | voice[j].inst = -1; // not found 428 | voice[j].shdr = -1; // not found 429 | if (sf2.shdr) { 430 | int p, zone, bank, count, range, velrange; 431 | int vel = voice[j].vel; 432 | pgm = channel[ch].program; 433 | bank = channel[ch].controller[CTL_BANK_SELECT]; 434 | bank <<= 7; 435 | bank |= channel[ch].controller[CTL_BANK_SELECT + CTL_LSB]; 436 | if (bank == 128) { 437 | bank = 0; /* soundfonts use bank 128 for percussion */ 438 | } 439 | if (ISPERC(ch)) { 440 | bank = 128; /* soundfonts use bank 128 for percussion */ 441 | } 442 | // find the preset that matches the program 443 | count = sf2.phdr_size / sizeof(struct sfPresetHeader); 444 | for (p = 0; p + 1 < count; p++) { 445 | if (sf2.phdr[p].wBank == bank && bank == 128 && 446 | sf2.phdr[p].wPreset <= pgm) { 447 | voice[j].phdr = p; /* default to first percussion match */ 448 | } 449 | if (sf2.phdr[p].wPreset == pgm) { 450 | if (sf2.phdr[p].wBank == 0 && bank != 128) { 451 | voice[j].phdr = p; /* default to bank 0 match */ 452 | } 453 | if (sf2.phdr[p].wBank == bank) { 454 | break; 455 | } 456 | } 457 | } 458 | if (p + 1 < count) { 459 | voice[j].phdr = p; 460 | } 461 | voice[j].pbag = sf2.phdr[voice[j].phdr].wPresetBagNdx; 462 | voice[j].pbag_max = sf2.phdr[voice[j].phdr + 1].wPresetBagNdx; 463 | for (zone = voice[j].pbag; zone < voice[j].pbag_max; zone++) { 464 | voice[j].pgen = sf2.pbag[zone].wGenNdx; 465 | voice[j].pmod = sf2.pbag[zone].wModNdx; 466 | voice[j].pgen_max = sf2.pbag[zone + 1].wGenNdx; 467 | voice[j].pmod_max = sf2.pbag[zone + 1].wModNdx; 468 | range = velrange = 1; 469 | for (p = voice[j].pgen; p < voice[j].pgen_max; p++) { 470 | if (sf2.pgen[p].sfGenOper == SFG_keyRange) { 471 | if (sf2.pgen[p].genAmount.ranges.byLo <= voice[j].note && 472 | sf2.pgen[p].genAmount.ranges.byHi >= voice[j].note) { 473 | range = 1; 474 | } else { 475 | range = 0; 476 | } 477 | } 478 | if (sf2.pgen[p].sfGenOper == SFG_velRange) { 479 | if (sf2.pgen[p].genAmount.ranges.byLo <= vel && 480 | sf2.pgen[p].genAmount.ranges.byHi >= vel) { 481 | velrange = 1; 482 | } else { 483 | velrange = 0; 484 | } 485 | } 486 | if (sf2.pgen[p].sfGenOper == SFG_instrument) { 487 | if (range && velrange) { 488 | voice[j].inst = sf2.pgen[p].genAmount.wAmount; 489 | apply_generators(voice[j].pgen, voice[j].pgen_max, 490 | sf2.pgen, j); 491 | } 492 | break; // instrument is terminal for zone 493 | } 494 | } 495 | if (zone == voice[j].pgen && p == voice[j].pgen_max) { 496 | // apply global zone generotors 497 | apply_generators(voice[j].pgen, voice[j].pgen_max, 498 | sf2.pgen, j); 499 | } 500 | if (voice[j].inst >= 0) { 501 | break; // found relevant zone 502 | } 503 | } 504 | if (voice[j].inst < 0) { 505 | // failed to find suitable instrument 506 | // ibag/ibag_max were memset to 0 earlier 507 | // for loop below will exit early 508 | } else { 509 | voice[j].ibag = sf2.inst[voice[j].inst].wInstBagNdx; 510 | voice[j].ibag_max = sf2.inst[voice[j].inst + 1].wInstBagNdx; 511 | } 512 | for (zone = voice[j].ibag; zone < voice[j].ibag_max; zone++) { 513 | voice[j].igen = sf2.ibag[zone].wInstGenNdx; 514 | voice[j].imod = sf2.ibag[zone].wInstModNdx; 515 | voice[j].igen_max = sf2.ibag[zone + 1].wInstGenNdx; 516 | voice[j].imod_max = sf2.ibag[zone + 1].wInstModNdx; 517 | range = velrange = 1; 518 | for (p = voice[j].igen; p < voice[j].igen_max; p++) { 519 | if (sf2.igen[p].sfGenOper == SFG_keyRange) { 520 | if (sf2.igen[p].genAmount.ranges.byLo <= voice[j].note && 521 | sf2.igen[p].genAmount.ranges.byHi >= voice[j].note) { 522 | range = 1; 523 | } else { 524 | range = 0; 525 | } 526 | } 527 | if (sf2.igen[p].sfGenOper == SFG_velRange) { 528 | if (sf2.igen[p].genAmount.ranges.byLo <= vel && 529 | sf2.igen[p].genAmount.ranges.byHi >= vel) { 530 | velrange = 1; 531 | } else { 532 | velrange = 0; 533 | } 534 | } 535 | if (sf2.igen[p].sfGenOper == SFG_sampleID) { 536 | if (range && velrange) { 537 | voice[j].shdr = sf2.igen[p].genAmount.wAmount; 538 | apply_generators(voice[j].igen, voice[j].igen_max, 539 | sf2.igen, j); 540 | } 541 | break; // instrument is terminal for zone 542 | } 543 | } 544 | if (zone == voice[j].igen && p == voice[j].igen_max) { 545 | // apply global zone generotors 546 | apply_generators(voice[j].igen, voice[j].igen_max, 547 | sf2.igen, j); 548 | } 549 | if (voice[j].shdr >= 0) { 550 | break; // found relevant zone 551 | } 552 | } 553 | if (voice[j].shdr < 0) { 554 | /* failed to find suitable sampleID, free voice */ 555 | voice[j].endstamp = 0; 556 | } 557 | } else { 558 | if (ISPERC(ch)) { 559 | /* kill percussion for non-sf2 voice */ 560 | voice[j].endstamp = 0; 561 | } 562 | voice[j].env.r = rate/16; 563 | voice[j].env.d = rate/16; 564 | voice[j].env.s = 0.4; 565 | voice[j].env.a = rate/64; 566 | } 567 | } 568 | break; 569 | case MIDI_KEY_PRESSURE: 570 | // todo: find voice, do something to it 571 | break; 572 | case MIDI_CTL_CHANGE: 573 | channel[ch].controller[tseqt->data[1]] = tseqt->data[2]; 574 | /* handle RPN/NRPN */ 575 | if (tseqt->data[1] == CTL_DATA_ENTRY) { 576 | if (channel[ch].controller[CTL_RPN_LSB] == 0 && 577 | channel[ch].controller[CTL_RPN_MSB] == 0) { 578 | channel[ch].bender_range = tseqt->data[2]; 579 | } 580 | } 581 | if (tseqt->data[1] == CTL_MODWHEEL) { 582 | channel[ch].mod_mult = 583 | cents_to_freqmult(47, tseqt->data[2], 127) - 1.0; 584 | } 585 | if (tseqt->data[1] == CTL_SUSTAIN && tseqt->data[2] < 64) { 586 | for (j = 0; j < POLYMAX; j++) { 587 | if (voice[j].channel == ch && voice[j].sustain) { 588 | voice[j].sustain = 0; 589 | voice[j].endstamp = samplepos + voice[j].env.r; 590 | if (voice[j].s.sampleModes != 1) { 591 | voice[j].s.sampleModes = 0; // finish past loop 592 | } 593 | } 594 | } 595 | } 596 | break; 597 | case MIDI_PGM_CHANGE: 598 | channel[ch].program = tseqt->data[1]; 599 | break; 600 | case MIDI_CHN_PRESSURE: 601 | channel[ch].pressure = tseqt->data[1]; 602 | break; 603 | case MIDI_PITCH_BEND: 604 | channel[ch].bender = tseqt->data[2]; 605 | channel[ch].bender <<= 7; 606 | channel[ch].bender |= tseqt->data[1]; 607 | channel[ch].bender_mult = pitchbend_to_freqmult(channel[ch].bender, 608 | channel[ch].bender_range); 609 | break; 610 | default: 611 | fprintf(stderr, "\r(unhandled midi cmd = 0x%02x)\n", cmd); 612 | exit(1); 613 | } 614 | //memset(&tseqt->data[0], 0, tseqt->len); /* debug: kill off event data */ 615 | tseqt = next_pkt(tseqt); 616 | } 617 | left = 0.0; 618 | right = 0.0; 619 | for (j = voices = 0; j < POLYMAX; j++) { 620 | float sample, t; 621 | int tpos, rpos; 622 | if (voice[j].endstamp <= samplepos) { 623 | continue; 624 | } 625 | voices++; 626 | tpos = samplepos - voice[j].timestamp; // sample # since attack start 627 | rpos = voice[j].endstamp - samplepos; // release pos 628 | t = voice[j].t; // each voice has its own timebase 629 | if (tpos < voice[j].env.a) { 630 | // attack phase 631 | vmod = (float)tpos / voice[j].env.a; 632 | } else if (tpos < voice[j].env.a + voice[j].env.h) { 633 | // hold phase 634 | vmod = 1.0; 635 | } else if (tpos < voice[j].env.a + voice[j].env.h + voice[j].env.d) { 636 | // decay phase 637 | vmod = ((float)tpos - (voice[j].env.a + voice[j].env.h)) / 638 | voice[j].env.d; 639 | vmod *= 1.0 - voice[j].env.s; 640 | vmod = 1.0 - vmod; // range from 1.0 down to env.s 641 | } else { 642 | // sustain phase 643 | vmod = voice[j].env.s; 644 | if (vmod <= 0.000001) { // kill voice when it can't be heard anymore 645 | voice[j].endstamp = 0; 646 | } 647 | } 648 | if (rpos < voice[j].env.r) { 649 | // release phase, go from calculated envelope position down to zero 650 | // cubic decay, vmod *= (rpos/env.r)^3 651 | float x = (float)rpos / voice[j].env.r; 652 | vmod *= x * x * x; 653 | } 654 | ch = voice[j].channel; 655 | pgm = channel[ch].program; 656 | vmod *= (float)channel[ch].controller[CTL_MAIN_VOLUME] / 127.0; 657 | vmod *= (float)channel[ch].controller[CTL_EXPRESSION] / 127.0; 658 | if (voice[j].shdr < 0) { 659 | pgm = -pgm - 2; // do math based synthesis 660 | if (t > 2 * M_PI) { 661 | t -= 2 * M_PI; 662 | } 663 | if (t < 0) { 664 | t += 2 * M_PI; 665 | } 666 | } else { 667 | if ((voice[j].s.sampleModes & 1) && 668 | t + voice[j].s.dwStart >= voice[j].s.dwEndloop) { 669 | t = voice[j].s.dwStartloop - voice[j].s.dwStart; 670 | } 671 | if (t + voice[j].s.dwStart >= voice[j].s.dwEnd) { 672 | voice[j].endstamp = 0; // kill off voice when completely played 673 | } 674 | } 675 | if (pgm <= -1) { // sine 676 | sample = sin(t); 677 | } else if (pgm < 0) { // for all othe negative values be a minimoog 678 | // morph between tri, saw, square, rect wave full negative pgm value 679 | float tri, saw, squ; 680 | float pwm = (lfo + 1.0) * 0.98; 681 | tri = (fabs(0.3184 * (t - M_PI)) - 1.0); 682 | saw = 0.3184 * (t - M_PI); 683 | squ = (t > M_PI * pwm ? -1.0 : 1.0); 684 | sample = saw; //squ * pwm + saw * (2.0 - pwm); 685 | } else { // wavetable 686 | int index = voice[j].s.dwStart + (int)t; 687 | // cubic interpolate samples 688 | // more info: http://paulbourke.net/miscellaneous/interpolation/ 689 | float mu = t - (int)t, mu2 = mu * mu; 690 | float a0, a1, a2, a3; 691 | float y0, y1, y2, y3; 692 | y0 = (float)sf2.smpl[index++]; 693 | if ((voice[j].s.sampleModes & 1) && index >= voice[j].s.dwEndloop) { 694 | index = voice[j].s.dwStartloop; 695 | } else if (index > voice[j].s.dwEnd) { 696 | index = voice[j].s.dwEnd; 697 | } 698 | y1 = (float)sf2.smpl[index++]; 699 | if ((voice[j].s.sampleModes & 1) && index >= voice[j].s.dwEndloop) { 700 | index = voice[j].s.dwStartloop; 701 | } else if (index > voice[j].s.dwEnd) { 702 | index = voice[j].s.dwEnd; 703 | } 704 | y2 = (float)sf2.smpl[index++]; 705 | if ((voice[j].s.sampleModes & 1) && index >= voice[j].s.dwEndloop) { 706 | index = voice[j].s.dwStartloop; 707 | } else if (index > voice[j].s.dwEnd) { 708 | index = voice[j].s.dwEnd; 709 | } 710 | y3 = (float)sf2.smpl[index]; 711 | a0 = y3 - y2 - y0 + y1; 712 | a1 = y0 - y1 - a0; 713 | a2 = y2 - y0; 714 | a3 = y1; 715 | sample = a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3; 716 | sample *= (1.0 / 32767.0); 717 | } 718 | sample *= voice[j].v * vmod; 719 | /* if active voices are panned, hit target position over one second */ 720 | if (voice[j].pan < (float)channel[ch].controller[CTL_PAN] / 127.0) { 721 | float delta = (float)channel[ch].controller[CTL_PAN] / 127.0 - 722 | voice[j].pan; 723 | voice[j].pan += delta/rate; // smooth pan to target in 1s 724 | } 725 | if (voice[j].pan > (float)channel[ch].controller[CTL_PAN] / 127.0) { 726 | float delta = voice[j].pan - 727 | (float)channel[ch].controller[CTL_PAN] / 127.0; 728 | voice[j].pan -= delta/rate; // smooth pan to target in 1s 729 | } 730 | left += sample * (1.0 - voice[j].pan); 731 | right += sample * voice[j].pan ; 732 | t += voice[j].r * channel[ch].bender_mult * 733 | (channel[ch].mod_mult * lfo + 1.0); 734 | voice[j].t = t; // save in per-voice timebase 735 | } 736 | f32s[i * 2] = left; 737 | f32s[i * 2 + 1] = right; 738 | samplepos++; 739 | if (fabs(left) > max_val) { 740 | max_val = fabs(left); 741 | nindex_max = i; 742 | } 743 | if (fabs(right) > max_val) { 744 | max_val = fabs(right); 745 | nindex_max = i; 746 | } 747 | } 748 | /* 749 | * normalization pass: 750 | * 751 | * normalize will be the value used for the prior audio frame. 752 | * 753 | * A smooth transition is made from the old normalize value at the 754 | * first sample to the sample offset with the maximum value in the 755 | * current frame. The new value persists until the last sample 756 | * of the current frame. This can leave an audible artifact if 757 | * the max value is at the start of a given frame, but random chance 758 | * should make that only a one in "len" chance 759 | * 760 | * this is a good tradeoff vs always having output that is too quiet 761 | * because of the headroom reserved for hundreds of voices going. 762 | */ 763 | if (max_val < 1.0) { 764 | max_val = 1.0; /* don't apply any gain to very quiet sections */ 765 | } 766 | if (max_val > 0.0) { /* should always be true in normal operation */ 767 | float normalize_old = normalize; 768 | float normalize_diff = (1.0 / max_val) - normalize_old; 769 | for (i = 0; i < len; i++) { 770 | if (i < nindex_max) { 771 | normalize = normalize_old + 772 | normalize_diff * (float)i / (float)nindex_max; 773 | } else { 774 | normalize = 1.0 / max_val; 775 | } 776 | f32s[i * 2] *= normalize; 777 | f32s[i * 2 + 1] *= normalize; 778 | } 779 | } 780 | 781 | } 782 | 783 | void save_audio(char *filename) 784 | { 785 | SDL_RWops *rw = SDL_RWFromFile(filename, "w"); 786 | int len = 0; // TODO: calculate number of samples to save 787 | Uint8 *buf = malloc(len); // space for everything at once 788 | if (!buf) { 789 | perror("malloc"); 790 | return; 791 | } 792 | if (!rw) { 793 | perror(filename); 794 | return; 795 | } 796 | 797 | // wave file header, 16 bit stereo 798 | SDL_WriteBE32(rw, 'RIFF'); // RIFF chunk container 799 | SDL_WriteLE32(rw, len + 44 - 8); // count of 'RIFF' chunk data bytes 800 | SDL_WriteBE32(rw, 'WAVE'); // RIFF chunk data type = WAVE 801 | SDL_WriteBE32(rw, 'fmt '); // 'fmt ' chunk 802 | SDL_WriteLE32(rw, 16); // count of 'fmt ' chunk data bytes 803 | SDL_WriteLE16(rw, 1); // compression code: 1 = PCM, 3 = float 804 | SDL_WriteLE16(rw, 2); // number of channels = 2 805 | SDL_WriteLE32(rw, (int)rate); // sample rate = rate 806 | SDL_WriteLE32(rw, 2 * 2 * (int)rate); // bytes per second 807 | SDL_WriteLE16(rw, 2 * 2); // number of bytes per sample slice 808 | SDL_WriteLE16(rw, 16); // significant bits per sample, float=32 or 64 809 | SDL_WriteBE32(rw, 'data'); // 'data' chunk 810 | // assume no error on last header write means the previous writes all worked 811 | if (SDL_WriteLE32(rw, len) < 1) { // count of 'data' chunk data bytes 812 | perror(filename); 813 | } 814 | 815 | fill_audio(NULL, buf, len); // render entire sequence to memory 816 | 817 | if (SDL_RWwrite(rw, buf, 1, len) != len) { 818 | perror(filename); 819 | } else { 820 | fprintf(stderr, "Wrote %d bytes\n", len); 821 | } 822 | SDL_RWclose(rw); 823 | } 824 | 825 | void open_sdl_dev(void) 826 | { 827 | SDL_AudioSpec want, have; 828 | 829 | if (sdl_dev != 0) { 830 | return; /* already opened */ 831 | } 832 | SDL_zero(want); 833 | want.freq = SAMPLERATE; 834 | want.format = AUDIO_F32SYS; 835 | want.channels = 2; 836 | want.samples = SAMPLELEN; 837 | want.callback = fill_audio; 838 | want.userdata = NULL; 839 | 840 | load_sf2(sf2_filename); 841 | SDL_Init(SDL_INIT_AUDIO); 842 | sdl_dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 843 | SDL_AUDIO_ALLOW_FORMAT_CHANGE); 844 | if (sdl_dev == 0) { 845 | fprintf(stderr, "SDL_OpenAudioDevice: %s\n", SDL_GetError()); 846 | exit(1); 847 | } 848 | if (want.freq != have.freq) { 849 | fprintf(stderr, "warning: wanted %d, got %d\n", want.freq, have.freq); 850 | rate = have.freq; 851 | } 852 | if (want.format != have.format) { 853 | fprintf(stderr, "warning: wanted 0x%x, got 0x%x\n", 854 | want.format, have.format); 855 | } 856 | if (want.channels != have.channels) { 857 | fprintf(stderr, "warning: wanted %dch, got %dch\n", 858 | want.channels, have.channels); 859 | } 860 | } 861 | 862 | void start_sdl_dev(void) 863 | { 864 | SDL_PauseAudioDevice(sdl_dev, 0); /* start filling audio buffer */ 865 | } 866 | 867 | void stop_sdl_dev(void) 868 | { 869 | SDL_PauseAudioDevice(sdl_dev, 1); /* stop filling audio buffer */ 870 | } 871 | 872 | 873 | struct sysex_stuff { 874 | int bytes; // minimum bytes needed to dispatch handler 875 | void (*handler)(Uint8 *); // soft handler 876 | Uint32 match; // big endian data to match 877 | Uint32 mask; // big endian mask to apply before match 878 | }; 879 | 880 | static void sys_gm1_on(Uint8 *data) { seq_reset(1); } 881 | static void sys_gm2_on(Uint8 *data) { seq_reset(1); } 882 | static void sys_master_v(Uint8 *data) { /* no-op for now */ } 883 | 884 | static void sys_master_ft(Uint8 *data) 885 | { 886 | int bend = data[1]; 887 | bend <<= 7; 888 | bend |= data[0]; 889 | atune = 440 * pitchbend_to_freqmult(bend, 1); 890 | } 891 | 892 | static void sys_master_ct(Uint8 *data) 893 | { 894 | int cents = data[1]; 895 | cents -= 64; 896 | cents *= 100; 897 | atune = 440 * cents_to_freqmult(cents, 1, 1); 898 | } 899 | 900 | static void sys_scale_tune(Uint8 *data) 901 | { 902 | int note, ch, chmask; 903 | chmask = *data++; 904 | chmask <<= 7; 905 | chmask |= *data++; 906 | chmask <<= 7; 907 | chmask |= *data++; 908 | for (note = 0; note < 12; note++) { 909 | int cents; 910 | float f; 911 | cents = *data++; 912 | cents -= 64; 913 | f = cents_to_freqmult(cents, 1, 1); 914 | for (ch = 0; ch < 16; ch++) { 915 | if (chmask & (1<= 0x40 && data[2] <= 0x4b) { 989 | /* GS SCALE TUNING NOTE N (0x40(0) - 0x4B(11)) */ 990 | int ch = data[1] & 0xf; 991 | int note = data[2] - 0x40; 992 | int cents; 993 | /* convert from roland block number to midi part number 0-15 */ 994 | ch = ch == 0 ? 9 : ch < 10 ? ch - 1 : ch; 995 | data += 3; 996 | while (data[2] != 0xf7 && note < 12) { 997 | cents = *data++; 998 | cents -= 64; 999 | scaletune[ch][note++] = cents_to_freqmult(cents, 1, 1); 1000 | } 1001 | } 1002 | } 1003 | 1004 | /* fixme: realtime treated as non-realtime for now */ 1005 | struct sysex_stuff sysex[] = { 1006 | { 8, sys_gs_dt1, 0x41004212, 0xff00ffff }, 1007 | { 4, sys_gm1_on, 0x7e7f0901, 0xffffffff }, 1008 | { 4, sys_gm2_on, 0x7e7f0903, 0xffffffff }, 1009 | { 4, sys_master_v, 0x7f7f0401, 0xffffffff }, 1010 | { 6, sys_master_ft, 0x7f7f0403, 0xffffffff }, 1011 | { 6, sys_master_ct, 0x7f7f0404, 0xffffffff }, 1012 | { 19, sys_scale_tuner, 0x7f7f0808, 0xffffffff }, /*realtime*/ 1013 | { 19, sys_scale_tune, 0x7e7f0808, 0xffffffff }, 1014 | { 31, sys_scale_tune2r, 0x7f7f0809, 0xffffffff }, /*realtime*/ 1015 | { 31, sys_scale_tune2, 0x7e7f0809, 0xffffffff }, 1016 | { 0, NULL, 0, 0 }, // terminal record 1017 | }; 1018 | 1019 | void load_sysex(int length, Uint8 *data, int type) 1020 | { 1021 | int i; 1022 | 1023 | for (i = 0; sysex[i].handler; i++) { 1024 | Uint32 match = SDL_SwapBE32(sysex[i].match); 1025 | Uint32 mask = SDL_SwapBE32(sysex[i].mask); 1026 | if (length < sysex[i].bytes) { 1027 | // ignore messages not long enough for handler 1028 | continue; 1029 | } 1030 | if ((*(Uint32 *)data & mask) == match) { 1031 | sysex[i].handler(data + 4); 1032 | } 1033 | } 1034 | if (!play_ext) 1035 | return; 1036 | // send sysex to external midi device 1037 | midi_send_sysex(length, data, type); 1038 | } 1039 | 1040 | /* MT-32 emulation translate table */ 1041 | static int mt32pgm[128] = 1042 | { 1043 | 0, 1, 2, 4, 4, 5, 5, 3, 16, 16, 16, 16, 19, 1044 | 19, 19, 21, 6, 6, 6, 7, 7, 7, 8, 8, 62, 57, 1045 | 63, 58, 38, 38, 39, 39, 88, 33, 52, 35, 97, 100, 38, 1046 | 39, 14, 102, 68, 103, 44, 92, 46, 80, 48, 49, 51, 45, 1047 | 40, 40, 42, 42, 43, 46, 46, 24, 25, 28, 27, 104, 32, 1048 | 32, 34, 33, 36, 37, 39, 35, 79, 73, 76, 72, 74, 75, 1049 | 64, 65, 66, 67, 71, 71, 69, 70, 60, 22, 56, 59, 57, 1050 | 63, 60, 60, 58, 61, 61, 11, 11, 99, 100, 9, 14, 13, 1051 | 12, 107, 106, 77, 78, 78, 76, 111, 47, 117, 127, 115, 118, 1052 | 116, 118, 126, 121, 121, 55, 124, 120, 125, 126, 127 1053 | }; 1054 | 1055 | void seq_set_patch(int chn, int pgm) 1056 | { 1057 | if (MT32 && pgm < 128) 1058 | pgm = mt32pgm[pgm]; 1059 | if (useprog[chn]) 1060 | pgm = useprog[chn] - 1; 1061 | if (ISMIDI(chn)) { 1062 | /* need program data tracked for external synth too */ 1063 | channel[chn].program = pgm; 1064 | } 1065 | tseqh->len = 2; 1066 | tseqh->data[0] = MIDI_PGM_CHANGE | chn; 1067 | tseqh->data[1] = pgm; 1068 | tseqh = add_pkt(tseqh); 1069 | } 1070 | 1071 | void seq_stop_note(int chn, int note, int vel) 1072 | { 1073 | tseqh->len = 3; 1074 | tseqh->data[0] = MIDI_NOTEOFF | chn; 1075 | tseqh->data[1] = note; 1076 | tseqh->data[2] = vel; 1077 | tseqh = add_pkt(tseqh); 1078 | } 1079 | 1080 | void seq_key_pressure(int chn, int note, int vel) 1081 | { 1082 | tseqh->len = 3; 1083 | tseqh->data[0] = MIDI_KEY_PRESSURE | chn; 1084 | tseqh->data[1] = note; 1085 | tseqh->data[2] = vel; 1086 | tseqh = add_pkt(tseqh); 1087 | } 1088 | 1089 | void seq_start_note(int chn, int note, int vel) 1090 | { 1091 | if (vel == 0 && !ISMIDI(chn)) { 1092 | seq_stop_note(chn, note, 127); 1093 | return; 1094 | } 1095 | tseqh->len = 3; 1096 | tseqh->data[0] = MIDI_NOTEON | chn; 1097 | tseqh->data[1] = note; 1098 | tseqh->data[2] = vel; 1099 | tseqh = add_pkt(tseqh); 1100 | } 1101 | 1102 | void seq_control(int chn, int p1, int p2) 1103 | { 1104 | if (ISMIDI(chn)) { 1105 | /* need controller data tracked for external synth too */ 1106 | channel[chn].controller[p1] = p2; 1107 | } 1108 | tseqh->len = 3; 1109 | tseqh->data[0] = MIDI_CTL_CHANGE | chn; 1110 | tseqh->data[1] = p1; 1111 | tseqh->data[2] = p2; 1112 | tseqh = add_pkt(tseqh); 1113 | } 1114 | 1115 | void seq_chn_pressure(int chn, int vel) 1116 | { 1117 | tseqh->len = 2; 1118 | tseqh->data[0] = MIDI_CHN_PRESSURE | chn; 1119 | tseqh->data[1] = vel; 1120 | tseqh = add_pkt(tseqh); 1121 | } 1122 | 1123 | void seq_bender(int chn, int p1, int p2) 1124 | { 1125 | tseqh->len = 3; 1126 | tseqh->data[0] = MIDI_PITCH_BEND | chn; 1127 | tseqh->data[1] = p1; 1128 | tseqh->data[2] = p2; 1129 | tseqh = add_pkt(tseqh); 1130 | } 1131 | 1132 | void seq_reset(int keep_queue) 1133 | { 1134 | int i; 1135 | if (!keep_queue) { 1136 | tseqh = tseqt = tseq; 1137 | } 1138 | /* kill all playing voices */ 1139 | for (i = 0; i < POLYMAX; i++) { 1140 | voice[i].endstamp = 0; 1141 | } 1142 | /* to keep midi in sync with soft synth, initialize both here */ 1143 | if (play_ext != chanmask) { 1144 | /* if everything is not going to external midi */ 1145 | open_sdl_dev(); /* set up sdl audio device for soft playback */ 1146 | } 1147 | if (play_ext & chanmask) { 1148 | init_midi(); 1149 | } 1150 | if (sdl_dev != 0) { 1151 | /* if sdl_dev opend, start sdl audio to be in sync with external midi */ 1152 | start_sdl_dev(); 1153 | } 1154 | atune = 440.0; /* reset any master tune overrides in effect */ 1155 | for (i = 0; i < 16; i++) { /* set state info */ 1156 | int j; 1157 | /* reset scale tuning to default */ 1158 | for (j = 0; j < 12; j++) { 1159 | scaletune[i][j] = 1.0; 1160 | } 1161 | channel[i].bender_mult = 1.0; 1162 | channel[i].bender = 8192; 1163 | channel[i].bender_range = 2; 1164 | seq_control(i, CTL_PAN, 64); 1165 | seq_control(i, CTL_SUSTAIN, 0); 1166 | seq_control(i, CTL_EXPRESSION, 127); 1167 | seq_control(i, CTL_ALL_NOTES_OFF, 0); 1168 | seq_control(i, CTL_ALL_SOUNDS_OFF, 0); 1169 | seq_control(i, CTL_RESET_ALL_CONTROLLERS,0); 1170 | seq_control(i, CTL_BANK_SELECT, 0); 1171 | seq_control(i, CTL_BANK_SELECT + CTL_LSB, 0); 1172 | seq_set_patch(i, 0); 1173 | } 1174 | } 1175 | --------------------------------------------------------------------------------