diff --git a/CMakeLists.txt b/CMakeLists.txt index efd2bb1..b225643 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,8 +9,9 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") find_library(COCOA Cocoa) find_library(CARBON Carbon) find_library(AUDIO_TOOLBOX AudioToolbox) + find_library(CORE_MIDI CoreMIDI) add_library(gral gral_macos.m) - target_link_libraries(gral ${COCOA} ${CARBON} ${AUDIO_TOOLBOX}) + target_link_libraries(gral ${COCOA} ${CARBON} ${AUDIO_TOOLBOX} ${CORE_MIDI}) target_compile_definitions(gral PUBLIC GRAL_MACOS) elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") find_package(PkgConfig REQUIRED) diff --git a/gral.h b/gral.h index 3739290..b5c098d 100644 --- a/gral.h +++ b/gral.h @@ -98,6 +98,11 @@ struct gral_window_interface { struct gral_timer; struct gral_file; struct gral_directory_watcher; +struct gral_midi; +struct gral_midi_interface { + void (*note_on)(unsigned char note, unsigned char velocity, void *user_data); + void (*note_off)(unsigned char note, void *user_data); +}; /*================ @@ -207,6 +212,15 @@ double gral_time_get_monotonic(void); void gral_audio_play(int (*callback)(float *buffer, int frames, void *user_data), void *user_data); + +/*========= + MIDI + =========*/ + +struct gral_midi *gral_midi_create(struct gral_application *application, char const *name, struct gral_midi_interface const *interface, void *user_data); +void gral_midi_delete(struct gral_midi *midi); + + #ifdef __cplusplus } #endif diff --git a/gral_linux.c b/gral_linux.c index 1668b0b..689e23a 100644 --- a/gral_linux.c +++ b/gral_linux.c @@ -860,3 +860,60 @@ void gral_audio_play(int (*callback)(float *buffer, int frames, void *user_data) snd_pcm_drain(pcm); snd_pcm_close(pcm); } + + +/*========= + MIDI + =========*/ + +struct gral_midi { + struct gral_midi_interface interface; + void *user_data; + snd_seq_t *seq; + int port; + guint source_id; +}; + +static void connect_midi() { + +} + +static gboolean midi_callback(gint fd, GIOCondition condition, gpointer user_data) { + struct gral_midi *midi = user_data; + snd_seq_event_t *event; + while (snd_seq_event_input(midi->seq, &event) > 0) { + switch (event->type) { + case SND_SEQ_EVENT_NOTEON: + midi->interface.note_on(event->data.note.note, event->data.note.velocity, midi->user_data); + break; + case SND_SEQ_EVENT_NOTEOFF: + midi->interface.note_off(event->data.note.note, midi->user_data); + break; + case SND_SEQ_EVENT_CONTROLLER: + break; + default: + break; + } + } + return G_SOURCE_CONTINUE; +} + +struct gral_midi *gral_midi_create(struct gral_application *application, char const *name, struct gral_midi_interface const *interface, void *user_data) { + struct gral_midi *midi = malloc(sizeof(struct gral_midi)); + midi->interface = *interface; + midi->user_data = user_data; + snd_seq_open(&midi->seq, "default", SND_SEQ_OPEN_INPUT, SND_SEQ_NONBLOCK); + snd_seq_set_client_name(midi->seq, name); + int client_id = snd_seq_client_id(midi->seq); + midi->port = snd_seq_create_simple_port(midi->seq, name, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC); + struct pollfd pfd; + snd_seq_poll_descriptors(midi->seq, &pfd, 1, POLLIN); + midi->source_id = g_unix_fd_add(pfd.fd, pfd.events, &midi_callback, midi); + return midi; +} + +void gral_midi_delete(struct gral_midi *midi) { + g_source_remove(midi->source_id); + snd_seq_close(midi->seq); + free(midi); +} diff --git a/gral_macos.m b/gral_macos.m index ce6a33f..b627c1d 100644 --- a/gral_macos.m +++ b/gral_macos.m @@ -921,3 +921,29 @@ void gral_audio_play(int (*callback)(float *buffer, int frames, void *user_data) CFRunLoopRun(); AudioQueueDispose(queue, NO); } + + +/*========= + MIDI + =========*/ + +static void midi_callback(MIDIPacketList const *pktlist, void *readProcRefCon, void *srcConnRefCon) { + +} + +struct gral_midi *gral_midi_create(struct gral_application *application, char const *name, struct gral_midi_interface const *interface, void *user_data) { + CFStringRef string = CFStringCreateWithCString(kCFAllocatorDefault, name, kCFStringEncodingUTF8); + MIDIClientRef client; + MIDIClientCreate(string, NULL, NULL, &client); + MIDIPortRef port; + MIDIInputPortCreate(client, string, &midi_callback, NULL, &port); + CFRelease(string); + // TODO + return NULL; +} + +void gral_midi_delete(struct gral_midi *midi) { + // TODO + // MIDIPortDispose(); + // MIDIClientDispose(); +} diff --git a/gral_windows.cpp b/gral_windows.cpp index 6c2125b..2043db7 100644 --- a/gral_windows.cpp +++ b/gral_windows.cpp @@ -1390,3 +1390,17 @@ void gral_audio_play(int (*callback)(float *buffer, int frames, void *user_data) render_client->Release(); CoTaskMemFree(format); } + + +/*========= + MIDI + =========*/ + +gral_midi *gral_midi_create(gral_application *application, char const *name, gral_midi_interface const *iface, void *user_data) { + // TODO + return NULL; +} + +void gral_midi_delete(gral_midi *midi) { + // TODO +}