shithub: choc

Download patch

ref: 88fe15083f4d55936c6d7708e35d65facd601a5c
parent: f8f21ffcc986ac8f79b81227321a0cd698f7cc6a
author: ceski <[email protected]>
date: Thu Sep 15 02:04:52 EDT 2022

Add selectable native MIDI devices to setup (#1506)

* Add selectable native MIDI devices to setup

* Fix formatting, free memory, remove extern from .c, change device to string

* Add WinMM to CMake

* Copy strings using M_StringDuplicate

* Add MIDI_MAPPER device ID comment

--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -229,6 +229,9 @@
 if(ENABLE_SDL2_NET)
     target_link_libraries("${PROGRAM_PREFIX}setup" SDL2::net)
 endif()
+if(WIN32)
+    target_link_libraries("${PROGRAM_PREFIX}setup" winmm)
+endif()
 
 if(MSVC)
     set_target_properties("${PROGRAM_PREFIX}setup" PROPERTIES
--- a/src/i_sound.c
+++ b/src/i_sound.c
@@ -518,6 +518,7 @@
     M_BindStringVariable("gus_patch_path",       &gus_patch_path);
     M_BindIntVariable("gus_ram_kb",              &gus_ram_kb);
 #ifdef _WIN32
+    M_BindStringVariable("winmm_midi_device",    &winmm_midi_device);
     M_BindIntVariable("winmm_reverb_level",      &winmm_reverb_level);
     M_BindIntVariable("winmm_chorus_level",      &winmm_chorus_level);
 #endif
--- a/src/i_sound.h
+++ b/src/i_sound.h
@@ -246,5 +246,9 @@
 
 void I_SetOPLDriverVer(opl_driver_ver_t ver);
 
+#ifdef _WIN32
+extern char *winmm_midi_device;
+#endif
+
 #endif
 
--- a/src/i_winmusic.c
+++ b/src/i_winmusic.c
@@ -33,6 +33,7 @@
 #define CHORUS_MIN 0
 #define CHORUS_MAX 127
 
+char *winmm_midi_device = NULL;
 int winmm_reverb_level = 40;
 int winmm_chorus_level = 0;
 
@@ -393,9 +394,48 @@
 
 boolean I_WIN_InitMusic(void)
 {
-    UINT MidiDevice = MIDI_MAPPER;
+    UINT MidiDevice;
+    int all_devices;
+    int i;
     MIDIHDR *hdr = &buffer.MidiStreamHdr;
+    MIDIOUTCAPS mcaps;
     MMRESULT mmr;
+
+    // find the midi device that matches the saved one
+    if (winmm_midi_device != NULL)
+    {
+        all_devices = midiOutGetNumDevs() + 1; // include MIDI_MAPPER
+        for (i = 0; i < all_devices; ++i)
+        {
+            // start from device id -1 (MIDI_MAPPER)
+            mmr = midiOutGetDevCaps(i - 1, &mcaps, sizeof(mcaps));
+            if (mmr == MMSYSERR_NOERROR)
+            {
+                if (strstr(winmm_midi_device, mcaps.szPname))
+                {
+                    MidiDevice = i - 1;
+                    break;
+                }
+            }
+
+            if (i == all_devices - 1)
+            {
+                // give up and use MIDI_MAPPER
+                free(winmm_midi_device);
+                winmm_midi_device = NULL;
+            }
+        }
+    }
+
+    if (winmm_midi_device == NULL)
+    {
+        MidiDevice = MIDI_MAPPER;
+        mmr = midiOutGetDevCaps(MIDI_MAPPER, &mcaps, sizeof(mcaps));
+        if (mmr == MMSYSERR_NOERROR)
+        {
+            winmm_midi_device = M_StringDuplicate(mcaps.szPname);
+        }
+    }
 
     mmr = midiStreamOpen(&hMidiStream, &MidiDevice, (DWORD)1,
                          (DWORD_PTR)MidiStreamProc, (DWORD_PTR)NULL,
--- a/src/m_config.c
+++ b/src/m_config.c
@@ -967,6 +967,12 @@
 
 #ifdef _WIN32
     //!
+    // MIDI device for native Windows MIDI.
+    //
+
+    CONFIG_VARIABLE_STRING(winmm_midi_device),
+
+    //!
     // Reverb level for native Windows MIDI, default 40, range 0-127.
     //
 
--- a/src/setup/sound.c
+++ b/src/setup/sound.c
@@ -14,6 +14,12 @@
 
 // Sound control menu
 
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.h>
+#endif
+
 #include <stdlib.h>
 #include <string.h>
 
@@ -71,6 +77,11 @@
 static char *gus_patch_path = NULL;
 static int gus_ram_kb = 1024;
 #ifdef _WIN32
+#define MAX_MIDI_DEVICES 20
+static char **midi_names;
+static int midi_num_devices;
+static int midi_index;
+char *winmm_midi_device = NULL;
 static int winmm_reverb_level = 40;
 static int winmm_chorus_level = 0;
 #endif
@@ -129,10 +140,99 @@
     }
 }
 
+#ifdef _WIN32
+static void UpdateMidiDevice(TXT_UNCAST_ARG(widget), TXT_UNCAST_ARG(data))
+{
+    free(winmm_midi_device);
+    winmm_midi_device = M_StringDuplicate(midi_names[midi_index]);
+}
+
+static txt_dropdown_list_t *MidiDeviceSelector(void)
+{
+    txt_dropdown_list_t *result;
+    int all_devices;
+    int device_ids[MAX_MIDI_DEVICES];
+    MMRESULT mmr;
+    MIDIOUTCAPS mcaps;
+    int i;
+
+    if (midi_num_devices > 0)
+    {
+        for (i = 0; i < midi_num_devices; ++i)
+        {
+            free(midi_names[i]);
+            midi_names[i] = NULL;
+        }
+        free(midi_names);
+        midi_names = NULL;
+    }
+    midi_num_devices = 0;
+
+    // get the number of midi devices on this system
+    all_devices = midiOutGetNumDevs() + 1; // include MIDI_MAPPER
+    if (all_devices > MAX_MIDI_DEVICES)
+    {
+        all_devices = MAX_MIDI_DEVICES;
+    }
+
+    // get the valid device ids only, starting from -1 (MIDI_MAPPER)
+    for (i = 0; i < all_devices; ++i)
+    {
+        mmr = midiOutGetDevCaps(i - 1, &mcaps, sizeof(mcaps));
+        if (mmr == MMSYSERR_NOERROR)
+        {
+            device_ids[midi_num_devices] = i - 1;
+            midi_num_devices++;
+        }
+    }
+
+    // get the device names
+    midi_names = malloc(midi_num_devices * sizeof(char *));
+    for (i = 0; i < midi_num_devices; ++i)
+    {
+        mmr = midiOutGetDevCaps(device_ids[i], &mcaps, sizeof(mcaps));
+        if (mmr == MMSYSERR_NOERROR)
+        {
+            midi_names[i] = M_StringDuplicate(mcaps.szPname);
+        }
+    }
+
+    // set the dropdown list index to the previously selected device
+    for (i = 0; i < midi_num_devices; ++i)
+    {
+        if (winmm_midi_device != NULL &&
+            strstr(winmm_midi_device, midi_names[i]))
+        {
+            midi_index = i;
+            break;
+        }
+        else if (winmm_midi_device == NULL || i == midi_num_devices - 1)
+        {
+            // give up and use MIDI_MAPPER
+            midi_index = 0;
+            free(winmm_midi_device);
+            winmm_midi_device = M_StringDuplicate(midi_names[0]);
+            break;
+        }
+    }
+
+    result = TXT_NewDropdownList(&midi_index, (const char **)midi_names,
+                                 midi_num_devices);
+    TXT_SignalConnect(result, "changed", UpdateMidiDevice, NULL);
+
+    return result;
+}
+#endif
+
 void ConfigSound(TXT_UNCAST_ARG(widget), void *user_data)
 {
     txt_window_t *window;
     txt_window_action_t *music_action;
+#ifdef _WIN32
+    int window_ypos = 2;
+#else
+    int window_ypos = 3;
+#endif
 
     // Build the window
 
@@ -141,7 +241,7 @@
 
     TXT_SetColumnWidths(window, 40);
     TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP,
-                                  TXT_SCREEN_W / 2, 3);
+                                  TXT_SCREEN_W / 2, window_ypos);
 
     music_action = TXT_NewWindowAction('m', "Music Packs");
     TXT_SetWindowAction(window, TXT_HORIZ_CENTER, music_action);
@@ -194,7 +294,15 @@
                 NULL)),
 
         TXT_NewRadioButton("Native MIDI", &snd_musicdevice, SNDDEVICE_GENMIDI),
+#ifdef _WIN32
         TXT_NewConditional(&snd_musicdevice, SNDDEVICE_GENMIDI,
+            TXT_NewHorizBox(
+                TXT_NewStrut(4, 0),
+                TXT_NewLabel("Device: "),
+                MidiDeviceSelector(),
+                NULL)),
+#endif
+        TXT_NewConditional(&snd_musicdevice, SNDDEVICE_GENMIDI,
             TXT_MakeTable(2,
                 TXT_NewStrut(4, 0),
                 TXT_NewLabel("Timidity configuration file: "),
@@ -230,6 +338,7 @@
     M_BindStringVariable("timidity_cfg_path",     &timidity_cfg_path);
     M_BindStringVariable("fluidsynth_sf_path",    &fluidsynth_sf_path);
 #ifdef _WIN32
+    M_BindStringVariable("winmm_midi_device",     &winmm_midi_device);
     M_BindIntVariable("winmm_reverb_level",       &winmm_reverb_level);
     M_BindIntVariable("winmm_chorus_level",       &winmm_chorus_level);
 #endif