diff options
| -rw-r--r-- | documents/SoundFont Technical Specification, v2.04.pdf (renamed from documents/sfspec24.pdf) | bin | 530464 -> 530464 bytes | |||
| -rw-r--r-- | documents/Standard MIDI Files Specification, v1.0.pdf | bin | 0 -> 84070 bytes | |||
| -rw-r--r-- | examples/sandbox.lua | 12 | ||||
| -rw-r--r-- | interface.c | 100 | ||||
| -rw-r--r-- | main.c | 45 | ||||
| -rw-r--r-- | midi.c | 30 | ||||
| -rw-r--r-- | synth.c | 3 |
7 files changed, 149 insertions, 41 deletions
diff --git a/documents/sfspec24.pdf b/documents/SoundFont Technical Specification, v2.04.pdf index 252aff9..252aff9 100644 --- a/documents/sfspec24.pdf +++ b/documents/SoundFont Technical Specification, v2.04.pdf | |||
| Binary files differ | |||
diff --git a/documents/Standard MIDI Files Specification, v1.0.pdf b/documents/Standard MIDI Files Specification, v1.0.pdf new file mode 100644 index 0000000..e422bf8 --- /dev/null +++ b/documents/Standard MIDI Files Specification, v1.0.pdf | |||
| Binary files differ | |||
diff --git a/examples/sandbox.lua b/examples/sandbox.lua index 43c3ec7..d42eb6e 100644 --- a/examples/sandbox.lua +++ b/examples/sandbox.lua | |||
| @@ -5,16 +5,16 @@ | |||
| 5 | -- note_on(note, sf_preset, effect) | 5 | -- note_on(note, sf_preset, effect) |
| 6 | -- note_off(note) | 6 | -- note_off(note) |
| 7 | -- delay(millisecons) | 7 | -- delay(millisecons) |
| 8 | -- play_pattern(pattern_name, repeat) | 8 | -- play_block(pattern_name, repeat) |
| 9 | 9 | ||
| 10 | -- Effects: | 10 | -- Effects: |
| 11 | -- delay | 11 | -- delay |
| 12 | -- reverb | 12 | -- reverb |
| 13 | 13 | ||
| 14 | -- This is a pattern definition. | 14 | -- This is a block/pattern definition. |
| 15 | -- Columns go from A..F and rows go from 1..4. | 15 | -- Columns go from A..F and rows go from 1..4. |
| 16 | -- This blocks will be visualized in the DAW. | 16 | -- This blocks will be visualized in the DAW. |
| 17 | pattern("A1", function(self) | 17 | block("A1", function(self) |
| 18 | for i = 1, 10 do | 18 | for i = 1, 10 do |
| 19 | note_on(40 + i, 1, nil) | 19 | note_on(40 + i, 1, nil) |
| 20 | delay(10) | 20 | delay(10) |
| @@ -23,8 +23,8 @@ end) | |||
| 23 | 23 | ||
| 24 | -- This is the actual song timeline. | 24 | -- This is the actual song timeline. |
| 25 | timeline(function(self) | 25 | timeline(function(self) |
| 26 | play_pattern("A1", 1) | 26 | play_block("A1", 1) |
| 27 | play_pattern("F2", 3) | 27 | play_block("F2", 3) |
| 28 | play_pattern("D4", 2) | 28 | play_block("D4", 2) |
| 29 | end) | 29 | end) |
| 30 | 30 | ||
diff --git a/interface.c b/interface.c index 26aca55..1347ce2 100644 --- a/interface.c +++ b/interface.c | |||
| @@ -1,16 +1,108 @@ | |||
| 1 | #include <stdlib.h> | ||
| 1 | #include <stdio.h> | 2 | #include <stdio.h> |
| 3 | #include <locale.h> | ||
| 2 | #include <unistd.h> | 4 | #include <unistd.h> |
| 3 | 5 | ||
| 4 | #include "interface.h" | 6 | #include "interface.h" |
| 5 | #include "mutex.h" | 7 | #include "mutex.h" |
| 6 | 8 | ||
| 9 | #define TB_IMPL | ||
| 10 | #include "termbox2.h" | ||
| 11 | |||
| 12 | static int block_width = 14; | ||
| 13 | static int block_height = 4; | ||
| 14 | |||
| 15 | void draw_block(int x, int y, const char *label, int empty) { | ||
| 16 | |||
| 17 | tb_set_cell(x, y, 0x250C, TB_WHITE, TB_DEFAULT); | ||
| 18 | tb_set_cell(x+block_width-1, y, 0x2510, TB_WHITE, TB_DEFAULT); | ||
| 19 | tb_set_cell(x, y+block_height-1, 0x2514, TB_WHITE, TB_DEFAULT); | ||
| 20 | tb_set_cell(x+block_width-1, y+block_height-1, 0x2518, TB_WHITE, TB_DEFAULT); | ||
| 21 | |||
| 22 | tb_printf(x+(block_width/2) - (strlen(label)/2), y+(block_height/2)-1 ,TB_YELLOW, 0, label); | ||
| 23 | tb_printf(x+(block_width/2) - (strlen("empty")/2), y+(block_height/2) ,TB_DIM, 0, "empty"); | ||
| 24 | |||
| 25 | tb_present(); | ||
| 26 | } | ||
| 27 | |||
| 28 | void draw_blocks() { | ||
| 29 | int offset_x = 0; | ||
| 30 | int offset_y = 6; | ||
| 31 | char col_names[6] = {'A', 'B', 'C', 'D', 'E', 'F'}; | ||
| 32 | |||
| 33 | for (int r = 0; r < 4; r++) { | ||
| 34 | for (int c = 0; c < 6; c++) { | ||
| 35 | char label[3]; | ||
| 36 | label[0] = col_names[c]; | ||
| 37 | label[1] = '1' + r; | ||
| 38 | label[2] = '\0'; | ||
| 39 | draw_block(offset_x+(block_width*c), offset_y+(block_height*r), label, 0); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | } | ||
| 43 | |||
| 44 | void draw_help_tooltip(const char* tooltip_text) { | ||
| 45 | tb_printf(tb_width() - strlen(tooltip_text) - 1, tb_height()-1, TB_DIM, 0, tooltip_text); | ||
| 46 | tb_present(); | ||
| 47 | } | ||
| 48 | |||
| 7 | void *interface(void *arg) { | 49 | void *interface(void *arg) { |
| 8 | InterfaceArgs* args = (InterfaceArgs*)arg; | 50 | InterfaceArgs* args = (InterfaceArgs*)arg; |
| 9 | (void)args; | 51 | |
| 52 | int ret; | ||
| 53 | setlocale(LC_ALL, ""); | ||
| 54 | |||
| 55 | ret = tb_init(); | ||
| 56 | if (ret) { | ||
| 57 | fprintf(stderr, "tb_init() failed with error code %d\n", ret); | ||
| 58 | exit(1); | ||
| 59 | } | ||
| 60 | |||
| 61 | tb_set_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE); | ||
| 62 | struct tb_event ev; | ||
| 63 | |||
| 64 | tb_clear(); | ||
| 65 | tb_present(); | ||
| 66 | |||
| 67 | // Show currently selected soundfont. | ||
| 68 | tb_printf(0, 0, TB_GREEN, 0, "Soundfont: %s", args->soundfont_file); | ||
| 69 | tb_printf(0, 1, TB_GREEN, 0, "Preset: %s", args->soundfont_preset); | ||
| 70 | tb_present(); | ||
| 71 | |||
| 72 | // Draw interface. | ||
| 73 | draw_help_tooltip("Ctrl+q - Quit"); | ||
| 74 | draw_blocks(); | ||
| 75 | /* draw_block(10, 10, "A1"); */ | ||
| 10 | 76 | ||
| 11 | while(1) { | 77 | while(1) { |
| 12 | // Do the thread stuff here. | 78 | ret = tb_poll_event(&ev); |
| 13 | sleep(1); | 79 | |
| 14 | fprintf(stdout, "hi from interface thread\n"); | 80 | if (ret != TB_OK) { |
| 81 | if (ret == TB_ERR_POLL && tb_last_errno() == EINTR) { | ||
| 82 | /* Poll was interrupted, maybe by a SIGWINCH; try again */ | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | /* Some other error occurred; bail */ | ||
| 86 | break; | ||
| 87 | } | ||
| 88 | |||
| 89 | switch (ev.type) { | ||
| 90 | case TB_EVENT_KEY: | ||
| 91 | if (ev.key == TB_KEY_CTRL_Q) { | ||
| 92 | tb_shutdown(); | ||
| 93 | exit(0); | ||
| 94 | } | ||
| 95 | |||
| 96 | if (ev.key == TB_KEY_ARROW_UP) { | ||
| 97 | |||
| 98 | } | ||
| 99 | |||
| 100 | if (ev.key == TB_KEY_ARROW_DOWN) { | ||
| 101 | |||
| 102 | } | ||
| 103 | |||
| 104 | break; | ||
| 105 | } | ||
| 15 | } | 106 | } |
| 16 | } | 107 | } |
| 108 | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | void help(const char *argv0) { | 13 | void help(const char *argv0) { |
| 14 | printf("Usage: %s [options]\n" | 14 | printf("Usage: %s [options]\n" |
| 15 | "\nAvailable options:\n" | 15 | "\nAvailable options:\n" |
| 16 | " -n,--new creates a new song file\n" | ||
| 16 | " -l,--list list available devices\n" | 17 | " -l,--list list available devices\n" |
| 17 | " -c,--client=client:port device client and port\n" | 18 | " -c,--client=client:port device client and port\n" |
| 18 | " -s,--soundfont=file.sf2 soundfont file\n" | 19 | " -s,--soundfont=file.sf2 soundfont file\n" |
| @@ -23,8 +24,9 @@ void help(const char *argv0) { | |||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | int main(int argc, char *argv[]) { | 26 | int main(int argc, char *argv[]) { |
| 26 | const char short_options[] = "lc:s:p:hv"; | 27 | const char short_options[] = "n:lc:s:p:hv"; |
| 27 | const struct option long_options[] = { | 28 | const struct option long_options[] = { |
| 29 | { "new", 1, NULL, 'n' }, | ||
| 28 | { "list", 0, NULL, 'l' }, | 30 | { "list", 0, NULL, 'l' }, |
| 29 | { "client", 1, NULL, 'c' }, | 31 | { "client", 1, NULL, 'c' }, |
| 30 | { "soundfont", 1, NULL, 's' }, | 32 | { "soundfont", 1, NULL, 's' }, |
| @@ -34,6 +36,7 @@ int main(int argc, char *argv[]) { | |||
| 34 | { 0 }, | 36 | { 0 }, |
| 35 | }; | 37 | }; |
| 36 | 38 | ||
| 39 | char *new_song_name = NULL; | ||
| 37 | char *port_name = NULL; | 40 | char *port_name = NULL; |
| 38 | char *soundfont_file = NULL; | 41 | char *soundfont_file = NULL; |
| 39 | int soundfont_preset = 0; | 42 | int soundfont_preset = 0; |
| @@ -41,6 +44,8 @@ int main(int argc, char *argv[]) { | |||
| 41 | int opt; | 44 | int opt; |
| 42 | while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { | 45 | while ((opt = getopt_long(argc, argv, short_options, long_options, NULL)) != -1) { |
| 43 | switch (opt) { | 46 | switch (opt) { |
| 47 | case 'n': | ||
| 48 | new_song_name = optarg; | ||
| 44 | case 'l': | 49 | case 'l': |
| 45 | fprintf(stderr, "List feature is NOT implemented yet.\n"); | 50 | fprintf(stderr, "List feature is NOT implemented yet.\n"); |
| 46 | return 0; | 51 | return 0; |
| @@ -69,19 +74,37 @@ int main(int argc, char *argv[]) { | |||
| 69 | } | 74 | } |
| 70 | } | 75 | } |
| 71 | 76 | ||
| 77 | if (new_song_name != NULL) { | ||
| 78 | fprintf(stdout, "Creating new song file with name %s\n", new_song_name); | ||
| 79 | return 0; | ||
| 80 | } | ||
| 81 | |||
| 72 | if (port_name == NULL || soundfont_file == NULL || soundfont_preset < 0) { | 82 | if (port_name == NULL || soundfont_file == NULL || soundfont_preset < 0) { |
| 73 | fprintf(stdout, "Missing options. Check help.\n"); | 83 | fprintf(stdout, "Missing options. Check help.\n"); |
| 74 | fprintf(stdout, "Port, soundfile and preset are required fields.\n"); | 84 | fprintf(stdout, "Port, soundfile and preset are required fields.\n"); |
| 75 | return 1; | 85 | return 1; |
| 76 | } | 86 | } |
| 77 | 87 | ||
| 78 | fprintf(stdout, "> Device port: %s\n", port_name); | 88 | /* fprintf(stdout, "> Device port: %s\n", port_name); */ |
| 79 | fprintf(stdout, "> Soundfont: %s\n", soundfont_file); | 89 | /* fprintf(stdout, "> Soundfont: %s\n", soundfont_file); */ |
| 80 | fprintf(stdout, "> SF preset: %d\n", soundfont_preset); | 90 | /* fprintf(stdout, "> SF preset: %d\n", soundfont_preset); */ |
| 91 | /* fprintf(stdout, "> Song name: %s\n", new_song_name); */ | ||
| 81 | 92 | ||
| 82 | // Create mutex. | 93 | // Create mutex. |
| 83 | initialize_mutex(); | 94 | initialize_mutex(); |
| 84 | 95 | ||
| 96 | // Create UI thread. | ||
| 97 | pthread_t interface_thread; | ||
| 98 | InterfaceArgs interface_args = { | ||
| 99 | .soundfont_file = soundfont_file, | ||
| 100 | .soundfont_preset = soundfont_preset, | ||
| 101 | }; | ||
| 102 | |||
| 103 | if (pthread_create(&interface_thread, NULL, interface, (void*)&interface_args) != 0) { | ||
| 104 | fprintf(stderr, "Error creating interface thread\n"); | ||
| 105 | return 1; | ||
| 106 | } | ||
| 107 | |||
| 85 | // Create synth thread. | 108 | // Create synth thread. |
| 86 | pthread_t synth_thread; | 109 | pthread_t synth_thread; |
| 87 | SynthArgs synth_args = { | 110 | SynthArgs synth_args = { |
| @@ -103,22 +126,10 @@ int main(int argc, char *argv[]) { | |||
| 103 | return 1; | 126 | return 1; |
| 104 | } | 127 | } |
| 105 | 128 | ||
| 106 | // Create UI thread. | ||
| 107 | pthread_t interface_thread; | ||
| 108 | InterfaceArgs interface_args = { | ||
| 109 | .soundfont_file = soundfont_file, | ||
| 110 | .soundfont_preset = soundfont_preset, | ||
| 111 | }; | ||
| 112 | |||
| 113 | if (pthread_create(&interface_thread, NULL, interface, (void*)&interface_args) != 0) { | ||
| 114 | fprintf(stderr, "Error creating interface thread\n"); | ||
| 115 | return 1; | ||
| 116 | } | ||
| 117 | |||
| 118 | // Start threads. | 129 | // Start threads. |
| 130 | pthread_join(interface_thread, NULL); | ||
| 119 | pthread_join(midi_thread, NULL); | 131 | pthread_join(midi_thread, NULL); |
| 120 | pthread_join(synth_thread, NULL); | 132 | pthread_join(synth_thread, NULL); |
| 121 | pthread_join(interface_thread, NULL); | ||
| 122 | 133 | ||
| 123 | // Destroy mutex. | 134 | // Destroy mutex. |
| 124 | destroy_mutex(); | 135 | destroy_mutex(); |
| @@ -6,6 +6,7 @@ | |||
| 6 | 6 | ||
| 7 | #include "midi.h" | 7 | #include "midi.h" |
| 8 | #include "mutex.h" | 8 | #include "mutex.h" |
| 9 | #include "termbox2.h" | ||
| 9 | 10 | ||
| 10 | static snd_seq_t *seq_handle; | 11 | static snd_seq_t *seq_handle; |
| 11 | static snd_seq_addr_t *ports; | 12 | static snd_seq_addr_t *ports; |
| @@ -40,10 +41,10 @@ void *midi(void *arg) { | |||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | // Listing assigned ports. | 43 | // Listing assigned ports. |
| 43 | fprintf(stdout, "Ports:\n"); | 44 | /* fprintf(stdout, "Ports:\n"); */ |
| 44 | for (int j = 0; j < MAX_MIDI_PORTS; j++) { | 45 | /* for (int j = 0; j < MAX_MIDI_PORTS; j++) { */ |
| 45 | fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); | 46 | /* fprintf(stdout, " client: %d, port: %d\n", ports[j].client, ports[j].port); */ |
| 46 | } | 47 | /* } */ |
| 47 | 48 | ||
| 48 | // Connecting ports. | 49 | // Connecting ports. |
| 49 | for (int i = 0; i < MAX_MIDI_PORTS; ++i) { | 50 | for (int i = 0; i < MAX_MIDI_PORTS; ++i) { |
| @@ -87,21 +88,24 @@ void *midi(void *arg) { | |||
| 87 | shared_data.note = ev->data.note.note; | 88 | shared_data.note = ev->data.note.note; |
| 88 | shared_data.state = 1; | 89 | shared_data.state = 1; |
| 89 | shared_data.velocity = ev->data.note.velocity; | 90 | shared_data.velocity = ev->data.note.velocity; |
| 90 | printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", | 91 | tb_printf(0, 3, TB_CYAN, 0, "Note: %3d", ev->data.note.note); |
| 91 | ev->source.client, ev->source.port, | 92 | tb_printf(0, 4, TB_CYAN, 0, "Velocity: %3d", ev->data.note.velocity); |
| 92 | ev->data.note.channel, | 93 | tb_present(); |
| 93 | ev->data.note.note, | 94 | /* printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", */ |
| 94 | ev->data.note.velocity); | 95 | /* ev->source.client, ev->source.port, */ |
| 96 | /* ev->data.note.channel, */ | ||
| 97 | /* ev->data.note.note, */ | ||
| 98 | /* ev->data.note.velocity); */ | ||
| 95 | break; | 99 | break; |
| 96 | 100 | ||
| 97 | case SND_SEQ_EVENT_NOTEOFF: | 101 | case SND_SEQ_EVENT_NOTEOFF: |
| 98 | shared_data.note = ev->data.note.note; | 102 | shared_data.note = ev->data.note.note; |
| 99 | shared_data.state = 0; | 103 | shared_data.state = 0; |
| 100 | shared_data.velocity = 0; | 104 | shared_data.velocity = 0; |
| 101 | printf("%3d:%-3dNote off\t%2d, note %d\n", | 105 | /* printf("%3d:%-3dNote off\t%2d, note %d\n", */ |
| 102 | ev->source.client, ev->source.port, | 106 | /* ev->source.client, ev->source.port, */ |
| 103 | ev->data.note.channel, | 107 | /* ev->data.note.channel, */ |
| 104 | ev->data.note.note); | 108 | /* ev->data.note.note); */ |
| 105 | break; | 109 | break; |
| 106 | 110 | ||
| 107 | 111 | ||
| @@ -4,6 +4,7 @@ | |||
| 4 | #include "synth.h" | 4 | #include "synth.h" |
| 5 | #include "mutex.h" | 5 | #include "mutex.h" |
| 6 | #include "minisdl_audio.h" | 6 | #include "minisdl_audio.h" |
| 7 | #include "termbox2.h" | ||
| 7 | 8 | ||
| 8 | #define TSF_IMPLEMENTATION | 9 | #define TSF_IMPLEMENTATION |
| 9 | #include "tsf.h" | 10 | #include "tsf.h" |
| @@ -73,7 +74,7 @@ void *synth(void *arg) { | |||
| 73 | 74 | ||
| 74 | SDL_UnlockMutex(g_Mutex); | 75 | SDL_UnlockMutex(g_Mutex); |
| 75 | 76 | ||
| 76 | printf("Consumed: note=%d, state=%d velocity:%d\n", shared_data.note, shared_data.state, shared_data.velocity); | 77 | /* printf("Consumed: note=%d, state=%d velocity:%d\n", shared_data.note, shared_data.state, shared_data.velocity); */ |
| 77 | 78 | ||
| 78 | // Reset state to indicate data has been consumed. | 79 | // Reset state to indicate data has been consumed. |
| 79 | shared_data.action = 0; | 80 | shared_data.action = 0; |
