diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | main.c | 7 | ||||
| -rw-r--r-- | midi.c | 16 | ||||
| -rw-r--r-- | midi.h | 4 | ||||
| -rw-r--r-- | mutex.c | 19 | ||||
| -rw-r--r-- | mutex.h | 24 | ||||
| -rw-r--r-- | soundfonts/general-808.sf2 | bin | 0 -> 71680 bytes | |||
| -rw-r--r-- | synth.c | 31 | ||||
| -rw-r--r-- | synth.h | 4 |
9 files changed, 96 insertions, 11 deletions
| @@ -1,7 +1,7 @@ | |||
| 1 | CC := cc | 1 | CC := cc |
| 2 | CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb | 2 | CFLAGS := -Wall -Wextra -Wshadow -Wunused -Wswitch-enum -Wpedantic -ggdb |
| 3 | LDFLAGS := -lm -ldl -lpthread -lasound | 3 | LDFLAGS := -lm -ldl -lpthread -lasound |
| 4 | FILES := main.c midi.c synth.c minisdl_audio.c | 4 | FILES := main.c midi.c synth.c mutex.c minisdl_audio.c |
| 5 | PROG := ttdaw | 5 | PROG := ttdaw |
| 6 | 6 | ||
| 7 | $(PROG): main.c | 7 | $(PROG): main.c |
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "version.h" | 7 | #include "version.h" |
| 8 | #include "midi.h" | 8 | #include "midi.h" |
| 9 | #include "synth.h" | 9 | #include "synth.h" |
| 10 | #include "mutex.h" | ||
| 10 | 11 | ||
| 11 | void help(const char *argv0) { | 12 | void help(const char *argv0) { |
| 12 | printf("Usage: %s [options]\n" | 13 | printf("Usage: %s [options]\n" |
| @@ -70,6 +71,9 @@ int main(int argc, char *argv[]) { | |||
| 70 | fprintf(stdout, "> Device port: %s\n", port_name); | 71 | fprintf(stdout, "> Device port: %s\n", port_name); |
| 71 | fprintf(stdout, "> Soundfont: %s\n", soundfont_file); | 72 | fprintf(stdout, "> Soundfont: %s\n", soundfont_file); |
| 72 | 73 | ||
| 74 | // Create mutex. | ||
| 75 | initialize_mutex(); | ||
| 76 | |||
| 73 | // Create synth thread. | 77 | // Create synth thread. |
| 74 | pthread_t synth_thread; | 78 | pthread_t synth_thread; |
| 75 | SynthArgs synth_args = { soundfont_file }; | 79 | SynthArgs synth_args = { soundfont_file }; |
| @@ -91,6 +95,9 @@ int main(int argc, char *argv[]) { | |||
| 91 | pthread_join(midi_thread, NULL); | 95 | pthread_join(midi_thread, NULL); |
| 92 | pthread_join(synth_thread, NULL); | 96 | pthread_join(synth_thread, NULL); |
| 93 | 97 | ||
| 98 | // Destroy mutex. | ||
| 99 | destroy_mutex(); | ||
| 100 | |||
| 94 | fprintf(stdout, "Exiting...\n"); | 101 | fprintf(stdout, "Exiting...\n"); |
| 95 | return 0; | 102 | return 0; |
| 96 | } | 103 | } |
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <alsa/asoundlib.h> | 5 | #include <alsa/asoundlib.h> |
| 6 | 6 | ||
| 7 | #include "midi.h" | 7 | #include "midi.h" |
| 8 | #include "mutex.h" | ||
| 8 | 9 | ||
| 9 | static snd_seq_t *seq_handle; | 10 | static snd_seq_t *seq_handle; |
| 10 | static snd_seq_addr_t *ports; | 11 | static snd_seq_addr_t *ports; |
| @@ -81,8 +82,12 @@ void *midi(void *arg) { | |||
| 81 | } | 82 | } |
| 82 | 83 | ||
| 83 | if (ev) { | 84 | if (ev) { |
| 85 | pthread_mutex_lock(&mutex); | ||
| 84 | switch (ev->type) { | 86 | switch (ev->type) { |
| 85 | case SND_SEQ_EVENT_NOTEON: | 87 | case SND_SEQ_EVENT_NOTEON: |
| 88 | shared_data.note = ev->data.note.note; | ||
| 89 | shared_data.state = 1; | ||
| 90 | shared_data.velocity = ev->data.note.velocity; | ||
| 86 | printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", | 91 | printf("%3d:%-3dNote on %2d, note %d, velocity: %3d\n", |
| 87 | ev->source.client, ev->source.port, | 92 | ev->source.client, ev->source.port, |
| 88 | ev->data.note.channel, | 93 | ev->data.note.channel, |
| @@ -91,14 +96,23 @@ void *midi(void *arg) { | |||
| 91 | break; | 96 | break; |
| 92 | 97 | ||
| 93 | case SND_SEQ_EVENT_NOTEOFF: | 98 | case SND_SEQ_EVENT_NOTEOFF: |
| 99 | shared_data.note = ev->data.note.note; | ||
| 100 | shared_data.state = 0; | ||
| 101 | shared_data.velocity = 0; | ||
| 94 | printf("%3d:%-3dNote off\t%2d, note %d\n", | 102 | printf("%3d:%-3dNote off\t%2d, note %d\n", |
| 95 | ev->source.client, ev->source.port, | 103 | ev->source.client, ev->source.port, |
| 96 | ev->data.note.channel, | 104 | ev->data.note.channel, |
| 97 | ev->data.note.note); | 105 | ev->data.note.note); |
| 98 | break; | 106 | break; |
| 107 | default: | ||
| 108 | break; | ||
| 109 | } | ||
| 99 | 110 | ||
| 111 | shared_data.action = 1; | ||
| 112 | shared_data.preset = 3; | ||
| 100 | 113 | ||
| 101 | } | 114 | pthread_cond_signal(&cond_synth); |
| 115 | pthread_mutex_unlock(&mutex); | ||
| 102 | } | 116 | } |
| 103 | } | 117 | } |
| 104 | 118 | ||
| @@ -1,8 +1,8 @@ | |||
| 1 | #include <alsa/asoundlib.h> | ||
| 2 | |||
| 3 | #ifndef MIDI_H_ | 1 | #ifndef MIDI_H_ |
| 4 | #define MIDI_H_ | 2 | #define MIDI_H_ |
| 5 | 3 | ||
| 4 | #include <alsa/asoundlib.h> | ||
| 5 | |||
| 6 | #define CLIENT_NAME "ttdaw" | 6 | #define CLIENT_NAME "ttdaw" |
| 7 | #define MAX_MIDI_PORTS 1 | 7 | #define MAX_MIDI_PORTS 1 |
| 8 | 8 | ||
| @@ -0,0 +1,19 @@ | |||
| 1 | #include <pthread.h> | ||
| 2 | #include "mutex.h" | ||
| 3 | |||
| 4 | SharedData shared_data; | ||
| 5 | |||
| 6 | pthread_mutex_t mutex; | ||
| 7 | pthread_cond_t cond_midi; | ||
| 8 | pthread_cond_t cond_synth; | ||
| 9 | |||
| 10 | void initialize_mutex() { | ||
| 11 | pthread_mutex_init(&mutex, NULL); | ||
| 12 | pthread_cond_init(&cond_synth, NULL); | ||
| 13 | } | ||
| 14 | |||
| 15 | void destroy_mutex() { | ||
| 16 | pthread_mutex_destroy(&mutex); | ||
| 17 | pthread_cond_destroy(&cond_synth); | ||
| 18 | } | ||
| 19 | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #ifndef MUTEX_H | ||
| 2 | #define MUTEX_H | ||
| 3 | |||
| 4 | #include <pthread.h> | ||
| 5 | |||
| 6 | typedef struct { | ||
| 7 | int note; | ||
| 8 | int state; | ||
| 9 | int velocity; | ||
| 10 | int preset; | ||
| 11 | int action; | ||
| 12 | } SharedData; | ||
| 13 | |||
| 14 | extern SharedData shared_data; | ||
| 15 | |||
| 16 | extern pthread_mutex_t mutex; | ||
| 17 | extern pthread_cond_t cond_midi; | ||
| 18 | extern pthread_cond_t cond_synth; | ||
| 19 | |||
| 20 | void initialize_mutex(); | ||
| 21 | void destroy_mutex(); | ||
| 22 | |||
| 23 | #endif // MUTEX_H | ||
| 24 | |||
diff --git a/soundfonts/general-808.sf2 b/soundfonts/general-808.sf2 new file mode 100644 index 0000000..3e128d8 --- /dev/null +++ b/soundfonts/general-808.sf2 | |||
| Binary files differ | |||
| @@ -2,6 +2,7 @@ | |||
| 2 | #include <unistd.h> | 2 | #include <unistd.h> |
| 3 | 3 | ||
| 4 | #include "synth.h" | 4 | #include "synth.h" |
| 5 | #include "mutex.h" | ||
| 5 | #include "minisdl_audio.h" | 6 | #include "minisdl_audio.h" |
| 6 | 7 | ||
| 7 | #define TSF_IMPLEMENTATION | 8 | #define TSF_IMPLEMENTATION |
| @@ -12,7 +13,7 @@ static SDL_mutex* g_Mutex; | |||
| 12 | 13 | ||
| 13 | // Render the audio samples in float format. | 14 | // Render the audio samples in float format. |
| 14 | static void AudioCallback(void* data, Uint8 *stream, int len) { | 15 | static void AudioCallback(void* data, Uint8 *stream, int len) { |
| 15 | int SampleCount = (len / (2 * sizeof(float))); // 2 output channels. | 16 | int SampleCount = (len / (AUDIO_CHANNELS * sizeof(float))); // 2 output channels. |
| 16 | SDL_LockMutex(g_Mutex); // Get exclusive lock. | 17 | SDL_LockMutex(g_Mutex); // Get exclusive lock. |
| 17 | tsf_render_float(g_TinySoundFont, (float*)stream, SampleCount, 0); | 18 | tsf_render_float(g_TinySoundFont, (float*)stream, SampleCount, 0); |
| 18 | SDL_UnlockMutex(g_Mutex); | 19 | SDL_UnlockMutex(g_Mutex); |
| @@ -22,10 +23,10 @@ void *synth(void *arg) { | |||
| 22 | SynthArgs* args = (SynthArgs*)arg; | 23 | SynthArgs* args = (SynthArgs*)arg; |
| 23 | 24 | ||
| 24 | SDL_AudioSpec OutputAudioSpec; | 25 | SDL_AudioSpec OutputAudioSpec; |
| 25 | OutputAudioSpec.freq = 44100; | 26 | OutputAudioSpec.freq = AUDIO_FREQ; |
| 26 | OutputAudioSpec.format = AUDIO_F32; | 27 | OutputAudioSpec.format = AUDIO_F32; |
| 27 | OutputAudioSpec.channels = 2; | 28 | OutputAudioSpec.channels = AUDIO_CHANNELS; |
| 28 | OutputAudioSpec.samples = 4096; | 29 | OutputAudioSpec.samples = AUDIO_SAMPLES; |
| 29 | OutputAudioSpec.callback = AudioCallback; | 30 | OutputAudioSpec.callback = AudioCallback; |
| 30 | 31 | ||
| 31 | // Initialize the audio system. | 32 | // Initialize the audio system. |
| @@ -56,12 +57,28 @@ void *synth(void *arg) { | |||
| 56 | SDL_PauseAudio(0); | 57 | SDL_PauseAudio(0); |
| 57 | 58 | ||
| 58 | while (1) { | 59 | while (1) { |
| 59 | sleep(1); | 60 | pthread_mutex_lock(&mutex); |
| 61 | while (shared_data.action == 0) { | ||
| 62 | pthread_cond_wait(&cond_synth, &mutex); | ||
| 63 | } | ||
| 60 | 64 | ||
| 61 | SDL_LockMutex(g_Mutex); | 65 | SDL_LockMutex(g_Mutex); |
| 62 | tsf_note_off(g_TinySoundFont, 1, 50); | 66 | |
| 63 | tsf_note_on(g_TinySoundFont, 1, 50, 1.0f); | 67 | if (shared_data.state == 0) { |
| 68 | tsf_note_off(g_TinySoundFont, shared_data.preset, shared_data.note); | ||
| 69 | } else { | ||
| 70 | float normalized_velocity = (float)shared_data.velocity / 127.0f; | ||
| 71 | tsf_note_on(g_TinySoundFont, shared_data.preset, shared_data.note, normalized_velocity); | ||
| 72 | } | ||
| 73 | |||
| 64 | SDL_UnlockMutex(g_Mutex); | 74 | SDL_UnlockMutex(g_Mutex); |
| 75 | |||
| 76 | printf("Consumed: note=%d, state=%d velocity:%d\n", shared_data.note, shared_data.state, shared_data.velocity); | ||
| 77 | |||
| 78 | // Reset state to indicate data has been consumed. | ||
| 79 | shared_data.action = 0; | ||
| 80 | |||
| 81 | pthread_mutex_unlock(&mutex); | ||
| 65 | } | 82 | } |
| 66 | } | 83 | } |
| 67 | 84 | ||
| @@ -1,6 +1,10 @@ | |||
| 1 | #ifndef SYNTH_H_ | 1 | #ifndef SYNTH_H_ |
| 2 | #define SYNTH_H_ | 2 | #define SYNTH_H_ |
| 3 | 3 | ||
| 4 | #define AUDIO_FREQ 44100 | ||
| 5 | #define AUDIO_SAMPLES 64 | ||
| 6 | #define AUDIO_CHANNELS 2 | ||
| 7 | |||
| 4 | typedef struct { | 8 | typedef struct { |
| 5 | char *soundfont_file; | 9 | char *soundfont_file; |
| 6 | } SynthArgs; | 10 | } SynthArgs; |
