mirror of
https://github.com/fabiangreffrath/woof.git
synced 2025-08-03 04:37:39 -04:00
unify native MIDI modules (#1635)
* remove midiStream functions, adapt for realtime output * implement I_SleepUS * add midiout.c, implement WINMM, ALSA, CoreMIDI and DLS Synth output * simplify I_DeviceList functions * display ALSA items last * remove `midi_player` variable
This commit is contained in:
parent
572cd3c3b1
commit
6f62dc5829
@ -67,6 +67,13 @@ find_package(SDL2 2.0.18 REQUIRED)
|
||||
find_package(SDL2_net REQUIRED)
|
||||
find_package(OpenAL REQUIRED)
|
||||
find_package(SndFile 1.0.29 REQUIRED)
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
find_package(ALSA REQUIRED)
|
||||
endif()
|
||||
|
||||
if(ALSA_FOUND)
|
||||
set(HAVE_ALSA TRUE)
|
||||
endif()
|
||||
|
||||
if(OPENAL_VERSION_STRING VERSION_GREATER_EQUAL "1.22.0")
|
||||
set(HAVE_AL_BUFFER_CALLBACK TRUE)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#cmakedefine HAVE_DIRENT_H
|
||||
#cmakedefine01 HAVE_DECL_STRCASECMP
|
||||
#cmakedefine01 HAVE_DECL_STRNCASECMP
|
||||
#cmakedefine HAVE_ALSA
|
||||
#cmakedefine HAVE_FLUIDSYNTH
|
||||
#cmakedefine HAVE_LIBXMP
|
||||
#cmakedefine HAVE_SNDFILE_MPEG
|
||||
|
@ -38,6 +38,7 @@ set(WOOF_SOURCES
|
||||
i_input.c i_input.h
|
||||
i_main.c
|
||||
i_mbfsound.c
|
||||
i_midimusic.c
|
||||
i_oalmusic.c
|
||||
i_oalsound.c i_oalsound.h
|
||||
i_oalstream.h
|
||||
@ -67,6 +68,7 @@ set(WOOF_SOURCES
|
||||
memio.c memio.h
|
||||
midifallback.c midifallback.h
|
||||
midifile.c midifile.h
|
||||
midiout.c midiout.h
|
||||
mus2mid.c mus2mid.h
|
||||
nano_bsp.c nano_bsp.h
|
||||
net_client.c net_client.h
|
||||
@ -186,7 +188,6 @@ if(HAVE_AL_BUFFER_CALLBACK)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
target_sources(woof PRIVATE i_winmusic.c)
|
||||
target_include_directories(woof PRIVATE "../win32")
|
||||
target_link_libraries(woof PRIVATE winmm)
|
||||
if(MSVC)
|
||||
@ -196,9 +197,15 @@ if(WIN32)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
target_sources(woof PRIVATE i_macmusic.c)
|
||||
target_link_libraries(woof PRIVATE
|
||||
-Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,CoreServices)
|
||||
-Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit
|
||||
-Wl,-framework,CoreServices -Wl,-framework,CoreAudio
|
||||
-Wl,-framework,CoreMIDI)
|
||||
endif()
|
||||
|
||||
if(ALSA_FOUND)
|
||||
target_include_directories(woof PRIVATE ${ALSA_INCLUDE_DIR})
|
||||
target_link_libraries(woof PRIVATE ALSA::ALSA)
|
||||
endif()
|
||||
|
||||
if(FluidSynth_FOUND)
|
||||
|
@ -2915,8 +2915,6 @@ void D_DoomMain(void)
|
||||
// Update display, next frame, with current state.
|
||||
if (screenvisible)
|
||||
D_Display();
|
||||
|
||||
S_UpdateMusic();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,11 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if __bool_true_false_are_defined
|
||||
typedef int boolean;
|
||||
#else
|
||||
typedef enum {false, true} boolean;
|
||||
#endif
|
||||
|
||||
typedef uint8_t byte;
|
||||
|
||||
|
@ -43,7 +43,6 @@ typedef fluid_long_long_t fluid_int_t;
|
||||
#include "w_wad.h"
|
||||
#include "z_zone.h"
|
||||
|
||||
const char *soundfont_path = "";
|
||||
char *soundfont_dir = "";
|
||||
boolean mus_chorus;
|
||||
boolean mus_reverb;
|
||||
@ -137,10 +136,7 @@ static void GetSoundFonts(void)
|
||||
{
|
||||
char *left, *p, *dup_path;
|
||||
|
||||
if (array_size(soundfonts))
|
||||
{
|
||||
return;
|
||||
}
|
||||
array_clear(soundfonts);
|
||||
|
||||
// Split into individual dirs within the list.
|
||||
dup_path = M_StringDuplicate(soundfont_dir);
|
||||
@ -254,40 +250,23 @@ static boolean I_FL_InitStream(int device)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (device == DEFAULT_MIDI_DEVICE)
|
||||
GetSoundFonts();
|
||||
|
||||
if (device >= array_size(soundfonts))
|
||||
{
|
||||
GetSoundFonts();
|
||||
|
||||
device = 0;
|
||||
|
||||
for (int i = 0; i < array_size(soundfonts); ++i)
|
||||
{
|
||||
if (!strcasecmp(soundfonts[i], soundfont_path))
|
||||
{
|
||||
device = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
FreeSynthAndSettings();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_size(soundfonts))
|
||||
{
|
||||
if (device >= array_size(soundfonts))
|
||||
{
|
||||
device = 0;
|
||||
}
|
||||
soundfont_path = soundfonts[device];
|
||||
}
|
||||
|
||||
sf_id = fluid_synth_sfload(synth, soundfont_path, true);
|
||||
sf_id = fluid_synth_sfload(synth, soundfonts[device], true);
|
||||
}
|
||||
|
||||
if (sf_id == FLUID_FAILED)
|
||||
{
|
||||
char *errmsg;
|
||||
errmsg =
|
||||
M_StringJoin("Error loading FluidSynth soundfont: ",
|
||||
lumpnum >= 0 ? "SNDFONT lump" : soundfont_path, NULL);
|
||||
errmsg = M_StringJoin(
|
||||
"Error loading FluidSynth soundfont: ",
|
||||
lumpnum >= 0 ? "SNDFONT lump" : soundfonts[device], NULL);
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_WARNING, PROJECT_STRING, errmsg,
|
||||
NULL);
|
||||
free(errmsg);
|
||||
@ -296,7 +275,7 @@ static boolean I_FL_InitStream(int device)
|
||||
}
|
||||
|
||||
I_Printf(VB_INFO, "FluidSynth Init: Using '%s'.",
|
||||
lumpnum >= 0 ? "SNDFONT lump" : soundfont_path);
|
||||
lumpnum >= 0 ? "SNDFONT lump" : soundfonts[device]);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -304,6 +283,11 @@ static boolean I_FL_InitStream(int device)
|
||||
static boolean I_FL_OpenStream(void *data, ALsizei size, ALenum *format,
|
||||
ALsizei *freq, ALsizei *frame_size)
|
||||
{
|
||||
if (!IsMid(data, size) && !IsMus(data, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!synth)
|
||||
{
|
||||
return false;
|
||||
@ -409,20 +393,15 @@ static void I_FL_ShutdownStream(void)
|
||||
|
||||
#define NAME_MAX_LENGTH 25
|
||||
|
||||
static const char **I_FL_DeviceList(int *current_device)
|
||||
static const char **I_FL_DeviceList(void)
|
||||
{
|
||||
static const char **devices = NULL;
|
||||
|
||||
if (devices)
|
||||
if (array_size(devices))
|
||||
{
|
||||
return devices;
|
||||
}
|
||||
|
||||
if (current_device)
|
||||
{
|
||||
*current_device = 0;
|
||||
}
|
||||
|
||||
if (W_CheckNumForName("SNDFONT") >= 0)
|
||||
{
|
||||
array_push(devices, "FluidSynth (SNDFONT)");
|
||||
@ -438,15 +417,10 @@ static const char **I_FL_DeviceList(int *current_device)
|
||||
{
|
||||
name[NAME_MAX_LENGTH] = '\0';
|
||||
}
|
||||
|
||||
array_push(devices, M_StringJoin("FluidSynth (", name, ")", NULL));
|
||||
|
||||
if (current_device && !strcasecmp(soundfonts[i], soundfont_path))
|
||||
{
|
||||
*current_device = i;
|
||||
}
|
||||
free(name);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
|
405
src/i_macmusic.c
405
src/i_macmusic.c
@ -1,405 +0,0 @@
|
||||
//
|
||||
// Copyright (C) 2006-2020 by The Odamex Team.
|
||||
// Copyright(C) 2023 Roman Fomin
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
|
||||
/*
|
||||
native_midi_macosx: Native Midi support on Mac OS X for the SDL_mixer library
|
||||
Copyright (C) 2009 Ryan C. Gordon <icculus@icculus.org>
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "i_printf.h"
|
||||
#include "i_sound.h"
|
||||
#include "m_array.h"
|
||||
#include "memio.h"
|
||||
#include "mus2mid.h"
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
static MusicPlayer player;
|
||||
static MusicSequence sequence;
|
||||
static AudioUnit unit;
|
||||
static AUGraph graph;
|
||||
static AUNode synth;
|
||||
static AUNode output;
|
||||
|
||||
static MusicTimeStamp endtime;
|
||||
|
||||
static boolean music_initialized;
|
||||
|
||||
static boolean is_playing, is_looping;
|
||||
|
||||
static boolean I_MAC_InitMusic(int device)
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
ComponentDescription d;
|
||||
#else
|
||||
AudioComponentDescription d;
|
||||
#endif
|
||||
|
||||
NewAUGraph(&graph);
|
||||
|
||||
d.componentType = kAudioUnitType_MusicDevice;
|
||||
d.componentSubType = kAudioUnitSubType_DLSSynth;
|
||||
d.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
d.componentFlags = 0;
|
||||
d.componentFlagsMask = 0;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
AUGraphNewNode(graph, &d, 0, NULL, &synth);
|
||||
#else
|
||||
AUGraphAddNode(graph, &d, &synth);
|
||||
#endif
|
||||
|
||||
d.componentType = kAudioUnitType_Output;
|
||||
d.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
d.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
d.componentFlags = 0;
|
||||
d.componentFlagsMask = 0;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
AUGraphNewNode(graph, &d, 0, NULL, &output);
|
||||
#else
|
||||
AUGraphAddNode(graph, &d, &output);
|
||||
#endif
|
||||
|
||||
if (AUGraphConnectNodeInput(graph, synth, 0, output, 0) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_InitMusic: AUGraphConnectNodeInput failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AUGraphOpen(graph) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_InitMusic: AUGraphOpen failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (AUGraphInitialize(graph) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_InitMusic: AUGraphInitialize failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050 // this is deprecated, but works back to 10.0
|
||||
if (AUGraphGetNodeInfo(graph, output, NULL, NULL, NULL, &unit) != noErr)
|
||||
#else // not deprecated, but requires 10.5 or later
|
||||
if (AUGraphNodeInfo(graph, output, NULL, &unit) != noErr)
|
||||
#endif
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_InitMusic: AUGraphGetNodeInfo failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NewMusicPlayer(&player) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_InitMusic: Music player creation failed "
|
||||
"using AudioToolbox.");
|
||||
return false;
|
||||
}
|
||||
|
||||
I_Printf(VB_INFO,
|
||||
"I_MAC_InitMusic: Music playback enabled using AudioToolbox.");
|
||||
music_initialized = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void I_MAC_SetMusicVolume(int volume)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AudioUnitSetParameter(unit, kAudioUnitParameterUnit_LinearGain,
|
||||
kAudioUnitScope_Output, 0, (float)volume / 15, 0)
|
||||
!= noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR,
|
||||
"I_MAC_SetMusicVolume: AudioUnitSetParameter failed.");
|
||||
}
|
||||
}
|
||||
|
||||
static void I_MAC_PauseSong(void *handle)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MusicPlayerStop(player);
|
||||
}
|
||||
|
||||
static void I_MAC_ResumeSong(void *handle)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MusicPlayerStart(player);
|
||||
}
|
||||
|
||||
static void I_MAC_PlaySong(void *handle, boolean looping)
|
||||
{
|
||||
UInt32 i, ntracks;
|
||||
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicSequenceSetAUGraph(sequence, graph) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_PlaySong: MusicSequenceSetAUGraph failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicPlayerSetSequence(player, sequence) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_PlaySong: MusicPlayerSetSequence failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicPlayerPreroll(player) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_PlaySong: MusicPlayerPreroll failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicSequenceGetTrackCount(sequence, &ntracks) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR,
|
||||
"I_MAC_PlaySong: MusicSequenceGetTrackCount failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
endtime = 0;
|
||||
|
||||
for (i = 0; i < ntracks; i++)
|
||||
{
|
||||
MusicTimeStamp time;
|
||||
MusicTrack track;
|
||||
UInt32 size = sizeof(time);
|
||||
|
||||
if (MusicSequenceGetIndTrack(sequence, i, &track) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR,
|
||||
"I_MAC_PlaySong: MusicSequenceGetIndTrack failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength,
|
||||
&time, &size)
|
||||
!= noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_PlaySong: MusicTrackGetProperty failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (time > endtime)
|
||||
{
|
||||
endtime = time;
|
||||
}
|
||||
}
|
||||
|
||||
if (MusicPlayerSetTime(player, 0) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_PlaySong: MusicPlayerSetTime failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicPlayerStart(player) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_PlaySong: MusicPlayerStart failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
is_playing = true;
|
||||
is_looping = looping;
|
||||
}
|
||||
|
||||
static void I_MAC_StopSong(void *handle)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MusicPlayerStop(player);
|
||||
|
||||
// needed to prevent error and memory leak when disposing sequence
|
||||
MusicPlayerSetSequence(player, NULL);
|
||||
|
||||
is_playing = false;
|
||||
}
|
||||
|
||||
static void *I_MAC_RegisterSong(void *data, int len)
|
||||
{
|
||||
CFDataRef data_ref = NULL;
|
||||
|
||||
if (!music_initialized)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (NewMusicSequence(&sequence) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR,
|
||||
"I_MAC_RegisterSong: Unable to create AudioUnit sequence.");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IsMid(data, len))
|
||||
{
|
||||
data_ref = CFDataCreate(NULL, (const UInt8 *)data, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Assume a MUS file and try to convert
|
||||
MEMFILE *instream;
|
||||
MEMFILE *outstream;
|
||||
void *outbuf;
|
||||
size_t outbuf_len;
|
||||
|
||||
instream = mem_fopen_read(data, len);
|
||||
outstream = mem_fopen_write();
|
||||
|
||||
if (mus2mid(instream, outstream) == 0)
|
||||
{
|
||||
mem_get_buf(outstream, &outbuf, &outbuf_len);
|
||||
data_ref = CFDataCreate(NULL, (const UInt8 *)outbuf, outbuf_len);
|
||||
}
|
||||
|
||||
mem_fclose(instream);
|
||||
mem_fclose(outstream);
|
||||
}
|
||||
|
||||
if (data_ref == NULL)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_MAC_RegisterSong: Failed to load MID.");
|
||||
DisposeMusicSequence(sequence);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
// MusicSequenceLoadSMFData() (avail. in 10.2, no 64 bit) is equivalent to
|
||||
// calling MusicSequenceLoadSMFDataWithFlags() with a flags value of 0
|
||||
// (avail. in 10.3, avail. 64 bit). So, we use MusicSequenceLoadSMFData() for
|
||||
// powerpc versions but the *WithFlags() on intel which require 10.4 anyway.
|
||||
# if defined(__ppc__) || defined(__POWERPC__)
|
||||
if (MusicSequenceLoadSMFData(sequence, data_ref) != noErr)
|
||||
# else
|
||||
if (MusicSequenceLoadSMFDataWithFlags(sequence, data_ref, 0) != noErr)
|
||||
# endif
|
||||
#else // MusicSequenceFileLoadData() requires 10.5 or later.
|
||||
if (MusicSequenceFileLoadData(sequence, data_ref, 0, 0) != noErr)
|
||||
#endif
|
||||
{
|
||||
DisposeMusicSequence(sequence);
|
||||
CFRelease(data_ref);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFRelease(data_ref);
|
||||
|
||||
return (void *)1;
|
||||
}
|
||||
|
||||
static void I_MAC_UnRegisterSong(void *handle)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DisposeMusicSequence(sequence);
|
||||
}
|
||||
|
||||
static void I_MAC_ShutdownMusic(void)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
I_MAC_StopSong(NULL);
|
||||
I_MAC_UnRegisterSong(NULL);
|
||||
|
||||
DisposeMusicPlayer(player);
|
||||
DisposeAUGraph(graph);
|
||||
|
||||
music_initialized = false;
|
||||
}
|
||||
|
||||
static const char **I_MAC_DeviceList(int *current_device)
|
||||
{
|
||||
const char **devices = NULL;
|
||||
*current_device = 0;
|
||||
array_push(devices, "Native");
|
||||
return devices;
|
||||
}
|
||||
|
||||
static void I_MAC_UpdateMusic(void)
|
||||
{
|
||||
MusicTimeStamp time;
|
||||
|
||||
if (!music_initialized || !is_playing)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
MusicPlayerGetTime(player, &time);
|
||||
|
||||
if (time < endtime)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (is_looping)
|
||||
{
|
||||
MusicPlayerSetTime(player, 0);
|
||||
}
|
||||
}
|
||||
|
||||
music_module_t music_mac_module =
|
||||
{
|
||||
I_MAC_InitMusic,
|
||||
I_MAC_ShutdownMusic,
|
||||
I_MAC_SetMusicVolume,
|
||||
I_MAC_PauseSong,
|
||||
I_MAC_ResumeSong,
|
||||
I_MAC_RegisterSong,
|
||||
I_MAC_PlaySong,
|
||||
I_MAC_UpdateMusic,
|
||||
I_MAC_StopSong,
|
||||
I_MAC_UnRegisterSong,
|
||||
I_MAC_DeviceList,
|
||||
};
|
1502
src/i_midimusic.c
Normal file
1502
src/i_midimusic.c
Normal file
File diff suppressed because it is too large
Load Diff
175
src/i_oalmusic.c
175
src/i_oalmusic.c
@ -25,6 +25,7 @@
|
||||
#include "i_oalstream.h"
|
||||
#include "i_printf.h"
|
||||
#include "i_sound.h"
|
||||
#include "m_array.h"
|
||||
|
||||
// Define the number of buffers and buffer size (in milliseconds) to use. 4
|
||||
// buffers with 4096 samples each gives a nice per-chunk size, and lets the
|
||||
@ -32,6 +33,18 @@
|
||||
#define NUM_BUFFERS 4
|
||||
#define BUFFER_SAMPLES 4096
|
||||
|
||||
static stream_module_t *all_modules[] =
|
||||
{
|
||||
#if defined (HAVE_FLUIDSYNTH)
|
||||
&stream_fl_module,
|
||||
#endif
|
||||
&stream_opl_module,
|
||||
&stream_snd_module,
|
||||
#if defined(HAVE_LIBXMP)
|
||||
&stream_xmp_module,
|
||||
#endif
|
||||
};
|
||||
|
||||
static stream_module_t *stream_modules[] =
|
||||
{
|
||||
&stream_snd_module,
|
||||
@ -40,6 +53,14 @@ static stream_module_t *stream_modules[] =
|
||||
#endif
|
||||
};
|
||||
|
||||
static stream_module_t *midi_modules[] =
|
||||
{
|
||||
#if defined (HAVE_FLUIDSYNTH)
|
||||
&stream_fl_module,
|
||||
#endif
|
||||
&stream_opl_module,
|
||||
};
|
||||
|
||||
static stream_module_t *active_module;
|
||||
|
||||
typedef struct
|
||||
@ -60,12 +81,10 @@ typedef struct
|
||||
static stream_player_t player;
|
||||
|
||||
static SDL_Thread *player_thread_handle;
|
||||
static int player_thread_running;
|
||||
static SDL_atomic_t player_thread_running;
|
||||
|
||||
static boolean music_initialized;
|
||||
|
||||
static SDL_mutex *music_lock;
|
||||
|
||||
static boolean UpdatePlayer(void)
|
||||
{
|
||||
ALint processed, state;
|
||||
@ -177,22 +196,11 @@ static int PlayerThread(void *unused)
|
||||
|
||||
StartPlayer();
|
||||
|
||||
while (true)
|
||||
while (SDL_AtomicGet(&player_thread_running))
|
||||
{
|
||||
boolean keep_going;
|
||||
|
||||
if (!UpdatePlayer())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
SDL_LockMutex(music_lock);
|
||||
keep_going = player_thread_running;
|
||||
SDL_UnlockMutex(music_lock);
|
||||
|
||||
if (!keep_going)
|
||||
{
|
||||
break;
|
||||
SDL_AtomicSet(&player_thread_running, 0);
|
||||
}
|
||||
|
||||
SDL_Delay(1);
|
||||
@ -201,15 +209,13 @@ static int PlayerThread(void *unused)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static boolean I_OAL_InitMusic(int device)
|
||||
boolean I_OAL_InitStream(void)
|
||||
{
|
||||
if (alcGetCurrentContext() == NULL)
|
||||
if (alcGetCurrentContext() == NULL || music_initialized)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
active_module = &stream_snd_module;
|
||||
|
||||
alGenBuffers(NUM_BUFFERS, player.buffers);
|
||||
alGenSources(1, &player.source);
|
||||
|
||||
@ -243,6 +249,50 @@ static boolean I_OAL_InitMusic(int device)
|
||||
return true;
|
||||
}
|
||||
|
||||
void I_OAL_ShutdownStream(void)
|
||||
{
|
||||
if (!music_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < arrlen(stream_modules); ++i)
|
||||
{
|
||||
stream_modules[i]->I_ShutdownStream();
|
||||
}
|
||||
|
||||
alDeleteSources(1, &player.source);
|
||||
alDeleteBuffers(NUM_BUFFERS, player.buffers);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_OAL_ShutdownMusic: Failed to delete object IDs.");
|
||||
}
|
||||
|
||||
memset(&player, 0, sizeof(stream_player_t));
|
||||
|
||||
music_initialized = false;
|
||||
}
|
||||
|
||||
static boolean I_OAL_InitMusic(int device)
|
||||
{
|
||||
int count_devices = 0;
|
||||
|
||||
for (int i = 0; i < arrlen(midi_modules); ++i)
|
||||
{
|
||||
const char **strings = midi_modules[i]->I_DeviceList();
|
||||
|
||||
if (device >= count_devices
|
||||
&& device < count_devices + array_size(strings))
|
||||
{
|
||||
return midi_modules[i]->I_InitStream(device - count_devices);
|
||||
}
|
||||
|
||||
count_devices += array_size(strings);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int mus_gain = 100;
|
||||
int opl_gain = 200;
|
||||
|
||||
@ -305,38 +355,26 @@ static void I_OAL_PlaySong(void *handle, boolean looping)
|
||||
return;
|
||||
}
|
||||
|
||||
music_lock = SDL_CreateMutex();
|
||||
|
||||
player_thread_running = true;
|
||||
SDL_AtomicSet(&player_thread_running, 1);
|
||||
player_thread_handle = SDL_CreateThread(PlayerThread, NULL, NULL);
|
||||
if (player_thread_handle == NULL)
|
||||
{
|
||||
I_Printf(VB_ERROR, "Error creating thread: %s", SDL_GetError());
|
||||
player_thread_running = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void I_OAL_StopSong(void *handle)
|
||||
{
|
||||
if (!music_initialized || !player_thread_running)
|
||||
if (!music_initialized || !SDL_AtomicGet(&player_thread_running))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
alSourceStop(player.source);
|
||||
|
||||
SDL_LockMutex(music_lock);
|
||||
player_thread_running = false;
|
||||
SDL_UnlockMutex(music_lock);
|
||||
|
||||
SDL_AtomicSet(&player_thread_running, 0);
|
||||
SDL_WaitThread(player_thread_handle, NULL);
|
||||
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_OAL_StopSong: Error stopping playback.");
|
||||
}
|
||||
|
||||
SDL_DestroyMutex(music_lock);
|
||||
}
|
||||
|
||||
static void I_OAL_UnRegisterSong(void *handle)
|
||||
@ -346,7 +384,11 @@ static void I_OAL_UnRegisterSong(void *handle)
|
||||
return;
|
||||
}
|
||||
|
||||
active_module->I_CloseStream();
|
||||
if (active_module)
|
||||
{
|
||||
active_module->I_CloseStream();
|
||||
active_module = NULL;
|
||||
}
|
||||
|
||||
if (player.data)
|
||||
{
|
||||
@ -365,53 +407,27 @@ static void I_OAL_ShutdownMusic(void)
|
||||
I_OAL_StopSong(NULL);
|
||||
I_OAL_UnRegisterSong(NULL);
|
||||
|
||||
if (midi_stream_module)
|
||||
for (int i = 0; i < arrlen(midi_modules); ++i)
|
||||
{
|
||||
midi_stream_module->I_ShutdownStream();
|
||||
midi_modules[i]->I_ShutdownStream();
|
||||
}
|
||||
active_module->I_ShutdownStream();
|
||||
|
||||
alDeleteSources(1, &player.source);
|
||||
alDeleteBuffers(NUM_BUFFERS, player.buffers);
|
||||
if (alGetError() != AL_NO_ERROR)
|
||||
{
|
||||
I_Printf(VB_ERROR, "I_OAL_ShutdownMusic: Failed to delete object IDs.");
|
||||
}
|
||||
|
||||
memset(&player, 0, sizeof(stream_player_t));
|
||||
|
||||
music_initialized = false;
|
||||
}
|
||||
|
||||
// Prebuffers some audio from the file, and starts playing the source.
|
||||
|
||||
static void *I_OAL_RegisterSong(void *data, int len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!music_initialized)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (IsMid(data, len) || IsMus(data, len))
|
||||
for (int i = 0; i < arrlen(all_modules); ++i)
|
||||
{
|
||||
if (midi_stream_module
|
||||
&& midi_stream_module->I_OpenStream(data, len, &player.format,
|
||||
&player.freq, &player.frame_size))
|
||||
{
|
||||
active_module = midi_stream_module;
|
||||
return (void *)1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < arrlen(stream_modules); ++i)
|
||||
{
|
||||
if (stream_modules[i]->I_OpenStream(data, len, &player.format,
|
||||
if (all_modules[i]->I_OpenStream(data, len, &player.format,
|
||||
&player.freq, &player.frame_size))
|
||||
{
|
||||
active_module = stream_modules[i];
|
||||
active_module = all_modules[i];
|
||||
return (void *)1;
|
||||
}
|
||||
}
|
||||
@ -419,14 +435,26 @@ static void *I_OAL_RegisterSong(void *data, int len)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static const char **I_OAL_DeviceList(int *current_device)
|
||||
static const char **I_OAL_DeviceList(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static const char **devices = NULL;
|
||||
|
||||
static void I_OAL_UpdateMusic(void)
|
||||
{
|
||||
;
|
||||
if (array_size(devices))
|
||||
{
|
||||
return devices;
|
||||
}
|
||||
|
||||
for (int i = 0; i < arrlen(midi_modules); ++i)
|
||||
{
|
||||
const char **strings = midi_modules[i]->I_DeviceList();
|
||||
|
||||
for (int k = 0; k < array_size(strings); ++k)
|
||||
{
|
||||
array_push(devices, strings[k]);
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
music_module_t music_oal_module =
|
||||
@ -438,7 +466,6 @@ music_module_t music_oal_module =
|
||||
I_OAL_ResumeSong,
|
||||
I_OAL_RegisterSong,
|
||||
I_OAL_PlaySong,
|
||||
I_OAL_UpdateMusic,
|
||||
I_OAL_StopSong,
|
||||
I_OAL_UnRegisterSong,
|
||||
I_OAL_DeviceList,
|
||||
|
@ -21,7 +21,7 @@
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
typedef struct stream_module_s
|
||||
typedef struct
|
||||
{
|
||||
boolean (*I_InitStream)(int device);
|
||||
boolean (*I_OpenStream)(void *data, ALsizei size, ALenum *format,
|
||||
@ -30,7 +30,7 @@ typedef struct stream_module_s
|
||||
void (*I_PlayStream)(boolean looping);
|
||||
void (*I_CloseStream)(void);
|
||||
void (*I_ShutdownStream)(void);
|
||||
const char **(*I_DeviceList)(int *current_device);
|
||||
const char **(*I_DeviceList)(void);
|
||||
} stream_module_t;
|
||||
|
||||
extern stream_module_t stream_opl_module;
|
||||
@ -38,4 +38,8 @@ extern stream_module_t stream_fl_module;
|
||||
extern stream_module_t stream_snd_module;
|
||||
extern stream_module_t stream_xmp_module;
|
||||
|
||||
boolean I_OAL_InitStream(void);
|
||||
|
||||
void I_OAL_ShutdownStream(void);
|
||||
|
||||
#endif
|
||||
|
@ -1503,6 +1503,11 @@ static midi_file_t *midifile;
|
||||
static boolean I_OPL_OpenStream(void *data, ALsizei size, ALenum *format,
|
||||
ALsizei *freq, ALsizei *frame_size)
|
||||
{
|
||||
if (!IsMid(data, size) && !IsMus(data, size))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!music_initialized)
|
||||
{
|
||||
return false;
|
||||
@ -1652,19 +1657,13 @@ static void I_OPL_ShutdownStream(void)
|
||||
}
|
||||
}
|
||||
|
||||
static const char **I_OPL_DeviceList(int *current_device)
|
||||
static const char **I_OPL_DeviceList(void)
|
||||
{
|
||||
static const char **devices = NULL;
|
||||
|
||||
if (devices)
|
||||
{
|
||||
return devices;
|
||||
}
|
||||
|
||||
if (current_device)
|
||||
{
|
||||
*current_device = 0;
|
||||
}
|
||||
array_push(devices, "OPL3 Emulation");
|
||||
return devices;
|
||||
}
|
||||
|
@ -704,7 +704,7 @@ static void I_SND_ShutdownStream(void)
|
||||
;
|
||||
}
|
||||
|
||||
static const char **I_SND_DeviceList(int *current_device)
|
||||
static const char **I_SND_DeviceList(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
216
src/i_sound.c
216
src/i_sound.c
@ -29,6 +29,7 @@
|
||||
#include "i_printf.h"
|
||||
#include "i_system.h"
|
||||
#include "m_array.h"
|
||||
#include "mn_setup.h"
|
||||
#include "p_mobj.h"
|
||||
#include "sounds.h"
|
||||
#include "w_wad.h"
|
||||
@ -46,28 +47,19 @@ static const sound_module_t *sound_modules[] =
|
||||
|
||||
static const sound_module_t *sound_module;
|
||||
|
||||
static music_module_t *native_midi_module =
|
||||
#if defined(_WIN32)
|
||||
&music_win_module;
|
||||
#elif defined(__APPLE__)
|
||||
&music_mac_module;
|
||||
#else
|
||||
NULL;
|
||||
#endif
|
||||
|
||||
static boolean native_midi;
|
||||
|
||||
static stream_module_t *stream_modules[] =
|
||||
static music_module_t *music_modules[] =
|
||||
{
|
||||
#if defined(HAVE_FLUIDSYNTH)
|
||||
&stream_fl_module,
|
||||
#if defined(HAVE_ALSA)
|
||||
&music_oal_module,
|
||||
&music_mid_module,
|
||||
#else
|
||||
&music_mid_module,
|
||||
&music_oal_module,
|
||||
#endif
|
||||
&stream_opl_module,
|
||||
};
|
||||
|
||||
stream_module_t *midi_stream_module = NULL;
|
||||
|
||||
static music_module_t *active_module = NULL;
|
||||
static music_module_t *midi_module = NULL;
|
||||
|
||||
// haleyjd: safety variables to keep changes to *_card from making
|
||||
// these routines think that sound has been initialized when it hasn't
|
||||
@ -519,39 +511,6 @@ void I_SetSoundModule(int device)
|
||||
}
|
||||
}
|
||||
|
||||
int midi_player; // current music module
|
||||
|
||||
static void MidiPlayerFallback(void)
|
||||
{
|
||||
// Fall back the the first module that initializes, device 0.
|
||||
|
||||
midi_player = 0;
|
||||
|
||||
if (native_midi_module)
|
||||
{
|
||||
if (native_midi_module->I_InitMusic(0))
|
||||
{
|
||||
native_midi = true;
|
||||
return;
|
||||
}
|
||||
midi_player = 1;
|
||||
}
|
||||
|
||||
native_midi = false;
|
||||
|
||||
for (int i = 0; i < arrlen(stream_modules); ++i)
|
||||
{
|
||||
if (stream_modules[i]->I_InitStream(0))
|
||||
{
|
||||
midi_player += i;
|
||||
midi_stream_module = stream_modules[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
I_Error("MidiPlayerFallback: No music module could be initialized");
|
||||
}
|
||||
|
||||
void I_SetMidiPlayer(int device)
|
||||
{
|
||||
if (nomusicparm)
|
||||
@ -559,49 +518,42 @@ void I_SetMidiPlayer(int device)
|
||||
return;
|
||||
}
|
||||
|
||||
int num_devices = 0;
|
||||
|
||||
midi_player = 0;
|
||||
|
||||
if (native_midi_module)
|
||||
if (midi_module)
|
||||
{
|
||||
const char **strings = native_midi_module->I_DeviceList(NULL);
|
||||
num_devices = array_size(strings);
|
||||
|
||||
native_midi_module->I_ShutdownMusic();
|
||||
|
||||
if (device < num_devices)
|
||||
{
|
||||
if (native_midi_module->I_InitMusic(device))
|
||||
{
|
||||
native_midi = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
midi_player = 1;
|
||||
midi_module->I_ShutdownMusic();
|
||||
}
|
||||
|
||||
native_midi = false;
|
||||
int count_devices = 0;
|
||||
|
||||
for (int i = 0, accum = num_devices; i < arrlen(stream_modules); ++i)
|
||||
for (int i = 0; i < arrlen(music_modules); ++i)
|
||||
{
|
||||
const char **strings = stream_modules[i]->I_DeviceList(NULL);
|
||||
num_devices = array_size(strings);
|
||||
const char **strings = music_modules[i]->I_DeviceList();
|
||||
|
||||
if (device >= accum && device < accum + num_devices)
|
||||
if (device >= count_devices
|
||||
&& device < count_devices + array_size(strings))
|
||||
{
|
||||
midi_player += i;
|
||||
if (stream_modules[i]->I_InitStream(device - accum))
|
||||
if (music_modules[i]->I_InitMusic(device - count_devices))
|
||||
{
|
||||
midi_stream_module = stream_modules[i];
|
||||
midi_module = music_modules[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
accum += num_devices;
|
||||
count_devices += array_size(strings);
|
||||
}
|
||||
|
||||
MidiPlayerFallback();
|
||||
// Fall back the the first module that initializes, device 0.
|
||||
|
||||
for (int i = 0; i < arrlen(music_modules); ++i)
|
||||
{
|
||||
if (music_modules[i]->I_InitMusic(0))
|
||||
{
|
||||
midi_module = music_modules[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
I_Error("I_SetMidiPlayer: No music module could be initialized");
|
||||
}
|
||||
|
||||
boolean I_InitMusic(void)
|
||||
@ -614,55 +566,31 @@ boolean I_InitMusic(void)
|
||||
// Always initialize the OpenAL module, it is used for software synth and
|
||||
// non-MIDI music streaming.
|
||||
|
||||
music_oal_module.I_InitMusic(0);
|
||||
|
||||
active_module = &music_oal_module;
|
||||
I_OAL_InitStream();
|
||||
|
||||
I_AtExit(I_ShutdownMusic, true);
|
||||
|
||||
int module_index = 0;
|
||||
|
||||
if (native_midi_module)
|
||||
{
|
||||
if (midi_player == 0
|
||||
&& native_midi_module->I_InitMusic(DEFAULT_MIDI_DEVICE))
|
||||
{
|
||||
native_midi = true;
|
||||
return true;
|
||||
}
|
||||
module_index = 1;
|
||||
}
|
||||
|
||||
native_midi = false;
|
||||
|
||||
module_index = midi_player - module_index;
|
||||
|
||||
if (module_index < arrlen(stream_modules))
|
||||
{
|
||||
if (stream_modules[module_index]->I_InitStream(DEFAULT_MIDI_DEVICE))
|
||||
{
|
||||
midi_stream_module = stream_modules[module_index];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
MidiPlayerFallback();
|
||||
I_SetMidiPlayer(midi_player_menu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void I_ShutdownMusic(void)
|
||||
{
|
||||
music_oal_module.I_ShutdownMusic();
|
||||
if (native_midi && native_midi_module)
|
||||
if (active_module != midi_module)
|
||||
{
|
||||
native_midi_module->I_ShutdownMusic();
|
||||
midi_module->I_ShutdownMusic();
|
||||
}
|
||||
active_module->I_ShutdownMusic();
|
||||
I_OAL_ShutdownStream();
|
||||
}
|
||||
|
||||
void I_SetMusicVolume(int volume)
|
||||
{
|
||||
active_module->I_SetMusicVolume(volume);
|
||||
if (active_module)
|
||||
{
|
||||
active_module->I_SetMusicVolume(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void I_PauseSong(void *handle)
|
||||
@ -687,16 +615,17 @@ boolean IsMus(byte *mem, int len)
|
||||
|
||||
void *I_RegisterSong(void *data, int size)
|
||||
{
|
||||
active_module = &music_oal_module;
|
||||
|
||||
if (native_midi && (IsMid(data, size) || IsMus(data, size)))
|
||||
for (int i = 0; i < arrlen(music_modules); ++i)
|
||||
{
|
||||
active_module = native_midi_module;
|
||||
void *result = music_modules[i]->I_RegisterSong(data, size);
|
||||
if (result)
|
||||
{
|
||||
active_module = music_modules[i];
|
||||
active_module->I_SetMusicVolume(snd_MusicVolume);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
void *result = active_module->I_RegisterSong(data, size);
|
||||
active_module->I_SetMusicVolume(snd_MusicVolume);
|
||||
return result;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void I_PlaySong(void *handle, boolean looping)
|
||||
@ -704,11 +633,6 @@ void I_PlaySong(void *handle, boolean looping)
|
||||
active_module->I_PlaySong(handle, looping);
|
||||
}
|
||||
|
||||
void I_UpdateMusic(void)
|
||||
{
|
||||
active_module->I_UpdateMusic();
|
||||
}
|
||||
|
||||
void I_StopSong(void *handle)
|
||||
{
|
||||
active_module->I_StopSong(handle);
|
||||
@ -719,50 +643,20 @@ void I_UnRegisterSong(void *handle)
|
||||
active_module->I_UnRegisterSong(handle);
|
||||
}
|
||||
|
||||
// Get a list of devices for all music modules. Retrieve the selected device, as
|
||||
// each module manages and stores its own devices independently.
|
||||
|
||||
const char **I_DeviceList(int *current_device)
|
||||
const char **I_DeviceList(void)
|
||||
{
|
||||
const char **devices = NULL;
|
||||
static const char **devices = NULL;
|
||||
|
||||
*current_device = 0;
|
||||
array_clear(devices);
|
||||
|
||||
int module_index = 0;
|
||||
|
||||
if (native_midi_module)
|
||||
for (int i = 0; i < arrlen(music_modules); ++i)
|
||||
{
|
||||
int device;
|
||||
const char **strings = native_midi_module->I_DeviceList(&device);
|
||||
|
||||
if (midi_player == module_index)
|
||||
{
|
||||
*current_device = device;
|
||||
}
|
||||
|
||||
for (int i = 0; i < array_size(strings); ++i)
|
||||
{
|
||||
array_push(devices, strings[i]);
|
||||
}
|
||||
module_index = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < arrlen(stream_modules); ++i)
|
||||
{
|
||||
int device;
|
||||
const char **strings = stream_modules[i]->I_DeviceList(&device);
|
||||
|
||||
if (midi_player == module_index)
|
||||
{
|
||||
*current_device = array_size(devices) + device;
|
||||
}
|
||||
const char **strings = music_modules[i]->I_DeviceList();
|
||||
|
||||
for (int k = 0; k < array_size(strings); ++k)
|
||||
{
|
||||
array_push(devices, strings[k]);
|
||||
}
|
||||
|
||||
module_index++;
|
||||
}
|
||||
|
||||
return devices;
|
||||
|
@ -167,26 +167,18 @@ typedef struct
|
||||
void (*I_ResumeSong)(void *handle);
|
||||
void *(*I_RegisterSong)(void *data, int size);
|
||||
void (*I_PlaySong)(void *handle, boolean looping);
|
||||
void (*I_UpdateMusic)(void);
|
||||
void (*I_StopSong)(void *handle);
|
||||
void (*I_UnRegisterSong)(void *handle);
|
||||
const char **(*I_DeviceList)(int *current_device);
|
||||
const char **(*I_DeviceList)(void);
|
||||
} music_module_t;
|
||||
|
||||
// Music modules
|
||||
extern music_module_t music_oal_module;
|
||||
extern music_module_t music_win_module;
|
||||
extern music_module_t music_mac_module;
|
||||
|
||||
extern int midi_player;
|
||||
|
||||
extern struct stream_module_s *midi_stream_module;
|
||||
extern music_module_t music_mid_module;
|
||||
|
||||
boolean I_InitMusic(void);
|
||||
void I_ShutdownMusic(void);
|
||||
|
||||
#define DEFAULT_MIDI_DEVICE -1 // use saved music module device
|
||||
|
||||
void I_SetMidiPlayer(int device);
|
||||
|
||||
// Volume.
|
||||
@ -205,15 +197,13 @@ void *I_RegisterSong(void *data, int size);
|
||||
// Horrible thing to do, considering.
|
||||
void I_PlaySong(void *handle, boolean looping);
|
||||
|
||||
void I_UpdateMusic(void);
|
||||
|
||||
// Stops a song over 3 seconds.
|
||||
void I_StopSong(void *handle);
|
||||
|
||||
// See above (register), then think backwards
|
||||
void I_UnRegisterSong(void *handle);
|
||||
|
||||
const char **I_DeviceList(int *current_device);
|
||||
const char **I_DeviceList(void);
|
||||
|
||||
// Determine whether memory block is a .mid file
|
||||
boolean IsMid(byte *mem, int len);
|
||||
|
@ -24,6 +24,15 @@
|
||||
#include "i_system.h"
|
||||
#include "m_fixed.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
|
||||
HANDLE hTimer = NULL;
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
static uint64_t basecounter = 0;
|
||||
static uint64_t basefreq = 0;
|
||||
|
||||
@ -136,6 +145,15 @@ void I_InitTimer(void)
|
||||
I_Error("I_InitTimer: Failed to initialize timer: %s", SDL_GetError());
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
// Create an unnamed waitable timer.
|
||||
hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
if (hTimer == NULL)
|
||||
{
|
||||
I_Error("I_InitTimer: CreateWaitableTimer failed");
|
||||
}
|
||||
#endif
|
||||
|
||||
I_AtExit(I_ShutdownTimer, true);
|
||||
|
||||
basefreq = SDL_GetPerformanceFrequency();
|
||||
@ -184,6 +202,20 @@ void I_Sleep(int ms)
|
||||
SDL_Delay(ms);
|
||||
}
|
||||
|
||||
void I_SleepUS(uint64_t us)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
LARGE_INTEGER liDueTime;
|
||||
liDueTime.QuadPart = -(LONGLONG)(us * 1000 / 100);
|
||||
if (SetWaitableTimer(hTimer, &liDueTime, 0, NULL, NULL, 0))
|
||||
{
|
||||
WaitForSingleObject(hTimer, INFINITE);
|
||||
}
|
||||
#else
|
||||
usleep(us);
|
||||
#endif
|
||||
}
|
||||
|
||||
void I_WaitVBL(int count)
|
||||
{
|
||||
// haleyjd
|
||||
|
@ -44,6 +44,8 @@ void I_EnableWarp(boolean warp);
|
||||
// Pause for a specified number of ms
|
||||
void I_Sleep(int ms);
|
||||
|
||||
void I_SleepUS(uint64_t us);
|
||||
|
||||
// Initialize timer
|
||||
void I_InitTimer(void);
|
||||
|
||||
|
1955
src/i_winmusic.c
1955
src/i_winmusic.c
File diff suppressed because it is too large
Load Diff
@ -60,6 +60,11 @@ static void PrintError(int e)
|
||||
|
||||
static boolean I_XMP_InitStream(int device)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
context = xmp_create_context();
|
||||
|
||||
if (!context)
|
||||
@ -127,6 +132,7 @@ static void I_XMP_CloseStream(void)
|
||||
xmp_stop_module(context);
|
||||
xmp_end_player(context);
|
||||
xmp_release_module(context);
|
||||
context = NULL;
|
||||
}
|
||||
|
||||
static void I_XMP_ShutdownStream(void)
|
||||
@ -140,7 +146,7 @@ static void I_XMP_ShutdownStream(void)
|
||||
context = NULL;
|
||||
}
|
||||
|
||||
static const char **I_XMP_DeviceList(int *current_device)
|
||||
static const char **I_XMP_DeviceList(void)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -85,18 +85,14 @@ extern int mouse_acceleration;
|
||||
extern int mouse_acceleration_threshold;
|
||||
extern int show_endoom;
|
||||
#if defined(HAVE_FLUIDSYNTH)
|
||||
extern char *soundfont_path;
|
||||
extern char *soundfont_dir;
|
||||
extern boolean mus_chorus;
|
||||
extern boolean mus_reverb;
|
||||
extern int mus_gain;
|
||||
#endif
|
||||
#if defined(_WIN32)
|
||||
extern char *winmm_device;
|
||||
extern int winmm_complevel;
|
||||
extern int winmm_reset_type;
|
||||
extern int winmm_reset_delay;
|
||||
#endif
|
||||
extern int midi_complevel;
|
||||
extern int midi_reset_type;
|
||||
extern int midi_reset_delay;
|
||||
extern int opl_gain;
|
||||
extern boolean demobar;
|
||||
extern boolean smoothlight;
|
||||
@ -458,13 +454,6 @@ default_t defaults[] = {
|
||||
"[OpenAL 3D] Doppler effect (0 = Off, 10 = Max)"
|
||||
},
|
||||
|
||||
{
|
||||
"midi_player",
|
||||
(config_t *) &midi_player, NULL,
|
||||
{0}, {0, 2}, number, ss_none, wad_no,
|
||||
"MIDI Player backend (Native if available, FluidSynth if available, OPL Emulation)"
|
||||
},
|
||||
|
||||
{
|
||||
"midi_player_menu",
|
||||
(config_t *) &midi_player_menu, NULL,
|
||||
@ -476,9 +465,9 @@ default_t defaults[] = {
|
||||
{
|
||||
"soundfont_dir",
|
||||
(config_t *) &soundfont_dir, NULL,
|
||||
#if defined(_WIN32)
|
||||
# if defined(_WIN32)
|
||||
{.s = "soundfonts"},
|
||||
#else
|
||||
# else
|
||||
/* RedHat/Fedora/Arch */
|
||||
{.s = "/usr/share/soundfonts:"
|
||||
/* Debian/Ubuntu/OpenSUSE */
|
||||
@ -486,18 +475,11 @@ default_t defaults[] = {
|
||||
"/usr/share/sounds/sf3:"
|
||||
/* AppImage */
|
||||
"../share/" PROJECT_SHORTNAME "/soundfonts"},
|
||||
#endif
|
||||
# endif
|
||||
{0}, string, ss_none, wad_no,
|
||||
"FluidSynth soundfont directories"
|
||||
},
|
||||
|
||||
{
|
||||
"soundfont_path",
|
||||
(config_t *) &soundfont_path, NULL,
|
||||
{.s = ""}, {0}, string, ss_none, wad_no,
|
||||
"FluidSynth current soundfont path"
|
||||
},
|
||||
|
||||
{
|
||||
"mus_chorus",
|
||||
(config_t *) &mus_chorus, NULL,
|
||||
@ -527,35 +509,26 @@ default_t defaults[] = {
|
||||
"fine tune OPL emulation output level (default 200%)"
|
||||
},
|
||||
|
||||
#if defined(_WIN32)
|
||||
{
|
||||
"winmm_device",
|
||||
(config_t *) &winmm_device, NULL,
|
||||
{.s = ""}, {0}, string, ss_none, wad_no,
|
||||
"Native MIDI device"
|
||||
},
|
||||
|
||||
{
|
||||
"winmm_complevel",
|
||||
(config_t *) &winmm_complevel, NULL,
|
||||
"midi_complevel",
|
||||
(config_t *) &midi_complevel, NULL,
|
||||
{1}, {0, 2}, number, ss_none, wad_no,
|
||||
"Native MIDI compatibility level (0 = Vanilla, 1 = Standard, 2 = Full)"
|
||||
},
|
||||
|
||||
{
|
||||
"winmm_reset_type",
|
||||
(config_t *) &winmm_reset_type, NULL,
|
||||
"midi_reset_type",
|
||||
(config_t *) &midi_reset_type, NULL,
|
||||
{1}, {0, 3}, number, ss_none, wad_no,
|
||||
"SysEx reset for native MIDI (0 = None, 1 = GM, 2 = GS, 3 = XG)"
|
||||
},
|
||||
|
||||
{
|
||||
"winmm_reset_delay",
|
||||
(config_t *) &winmm_reset_delay, NULL,
|
||||
"midi_reset_delay",
|
||||
(config_t *) &midi_reset_delay, NULL,
|
||||
{0}, {0, 2000}, number, ss_none, wad_no,
|
||||
"Delay after reset for native MIDI (milliseconds)"
|
||||
},
|
||||
#endif
|
||||
|
||||
//
|
||||
// QOL features
|
||||
|
@ -18,8 +18,8 @@
|
||||
//
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef __M_MISC__
|
||||
#define __M_MISC__
|
||||
#ifndef __M_CONFIG__
|
||||
#define __M_CONFIG__
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "doomdef.h"
|
||||
|
@ -16,8 +16,8 @@
|
||||
// [FG] miscellaneous helper functions from Chocolate Doom.
|
||||
//
|
||||
|
||||
#ifndef __M_MISC2__
|
||||
#define __M_MISC2__
|
||||
#ifndef __M_MISC__
|
||||
#define __M_MISC__
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
|
676
src/midiout.c
Normal file
676
src/midiout.c
Normal file
@ -0,0 +1,676 @@
|
||||
//
|
||||
// Copyright(C) 2024 Roman Fomin
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
#include "midiout.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "doomtype.h"
|
||||
#include "i_printf.h"
|
||||
|
||||
#define MIDI_DEFAULT_BUFFER_SIZE 32
|
||||
|
||||
//---------------------------------------------------------
|
||||
// WINMM
|
||||
//---------------------------------------------------------
|
||||
#if defined(_WIN32)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <mmreg.h>
|
||||
|
||||
#include "m_io.h"
|
||||
|
||||
static HMIDIOUT hMidiOut;
|
||||
static HANDLE hCallbackEvent;
|
||||
|
||||
static void MidiError(const char *prefix, MMRESULT result)
|
||||
{
|
||||
wchar_t werror[MAXERRORLENGTH];
|
||||
|
||||
if (midiOutGetErrorTextW(result, (LPWSTR)werror, MAXERRORLENGTH)
|
||||
== MMSYSERR_NOERROR)
|
||||
{
|
||||
char *error = M_ConvertWideToUtf8(werror);
|
||||
I_Printf(VB_ERROR, "%s: %s", prefix, error);
|
||||
free(error);
|
||||
}
|
||||
else
|
||||
{
|
||||
I_Printf(VB_ERROR, "%s: Unknown error", prefix);
|
||||
}
|
||||
}
|
||||
|
||||
static void CALLBACK MidiOutProc(HMIDIOUT hmo, UINT wMsg, DWORD_PTR dwInstance,
|
||||
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
|
||||
{
|
||||
if (wMsg == MOM_DONE)
|
||||
{
|
||||
SetEvent(hCallbackEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void MIDI_SendShortMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
MMRESULT result;
|
||||
|
||||
// Pack MIDI bytes into double word.
|
||||
DWORD packet = 0;
|
||||
byte *ptr = (byte *)&packet;
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
*ptr = message[i];
|
||||
++ptr;
|
||||
}
|
||||
|
||||
result = midiOutShortMsg(hMidiOut, packet);
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
MidiError("MIDI_SendShortMsg", result);
|
||||
}
|
||||
}
|
||||
|
||||
void MIDI_SendLongMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
MMRESULT result;
|
||||
MIDIHDR hdr = {0};
|
||||
|
||||
hdr.lpData = (LPSTR)message;
|
||||
hdr.dwBufferLength = length;
|
||||
hdr.dwFlags = 0;
|
||||
|
||||
result = midiOutPrepareHeader(hMidiOut, &hdr, sizeof(MIDIHDR));
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
MidiError("MIDI_SendLongMsg", result);
|
||||
return;
|
||||
}
|
||||
|
||||
result = midiOutLongMsg(hMidiOut, &hdr, sizeof(MIDIHDR));
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
MidiError("MIDI_SendLongMsg", result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(hCallbackEvent, INFINITE) == WAIT_OBJECT_0)
|
||||
{
|
||||
result = midiOutUnprepareHeader(hMidiOut, &hdr, sizeof(MIDIHDR));
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
MidiError("MIDI_SendLongMsg", result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int MIDI_CountDevices(void)
|
||||
{
|
||||
return midiOutGetNumDevs();
|
||||
}
|
||||
|
||||
const char *MIDI_GetDeviceName(int device)
|
||||
{
|
||||
MMRESULT result;
|
||||
MIDIOUTCAPSW caps;
|
||||
|
||||
result = midiOutGetDevCapsW(device, &caps, sizeof(caps));
|
||||
if (result == MMSYSERR_NOERROR)
|
||||
{
|
||||
return M_ConvertWideToUtf8(caps.szPname);
|
||||
}
|
||||
else
|
||||
{
|
||||
MidiError("MIDI_GetDeviceName", result);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
boolean MIDI_OpenDevice(int device)
|
||||
{
|
||||
MMRESULT result = midiOutOpen(&hMidiOut, device, (DWORD_PTR)&MidiOutProc,
|
||||
(DWORD_PTR)NULL, CALLBACK_FUNCTION);
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
MidiError("MIDI_OpenDevice", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
hCallbackEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MIDI_CloseDevice(void)
|
||||
{
|
||||
MMRESULT result = midiOutClose(hMidiOut);
|
||||
if (result != MMSYSERR_NOERROR)
|
||||
{
|
||||
MidiError("MIDI_CloseDevice", result);
|
||||
}
|
||||
CloseHandle(hCallbackEvent);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// ALSA
|
||||
//---------------------------------------------------------
|
||||
#elif defined(HAVE_ALSA)
|
||||
|
||||
#include <alsa/asoundlib.h>
|
||||
|
||||
#include "m_array.h"
|
||||
#include "m_misc.h"
|
||||
|
||||
static snd_seq_t *seq;
|
||||
static snd_seq_port_subscribe_t *subscription;
|
||||
static snd_midi_event_t *coder;
|
||||
static size_t buffer_size;
|
||||
static int vport = -1;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const char *name;
|
||||
int port_id;
|
||||
int client_id;
|
||||
} port_info_t;
|
||||
|
||||
static port_info_t *ports;
|
||||
|
||||
static boolean Init(void)
|
||||
{
|
||||
if (seq)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int err;
|
||||
snd_seq_client_info_t *cinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
|
||||
err = snd_seq_open(&seq, "default", SND_SEQ_OPEN_OUTPUT, 0);
|
||||
if (err < 0)
|
||||
{
|
||||
I_Printf(VB_ERROR, "Init: %s", snd_strerror(err));
|
||||
seq = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
snd_seq_client_info_alloca(&cinfo);
|
||||
snd_seq_port_info_alloca(&pinfo);
|
||||
|
||||
snd_seq_client_info_set_client(cinfo, -1);
|
||||
while (snd_seq_query_next_client(seq, cinfo) == 0)
|
||||
{
|
||||
snd_seq_port_info_set_client(pinfo,
|
||||
snd_seq_client_info_get_client(cinfo));
|
||||
snd_seq_port_info_set_port(pinfo, -1);
|
||||
while (snd_seq_query_next_port(seq, pinfo) == 0)
|
||||
{
|
||||
if (snd_seq_port_info_get_client(pinfo) == SND_SEQ_CLIENT_SYSTEM)
|
||||
{
|
||||
continue; // ignore Timer and Announce ports on client 0
|
||||
}
|
||||
unsigned int caps = snd_seq_port_info_get_capability(pinfo);
|
||||
if (caps & SND_SEQ_PORT_CAP_SUBS_WRITE)
|
||||
{
|
||||
port_info_t port;
|
||||
port.name =
|
||||
M_StringDuplicate(snd_seq_port_info_get_name(pinfo));
|
||||
port.port_id = snd_seq_port_info_get_port(pinfo);
|
||||
port.client_id = snd_seq_port_info_get_client(pinfo);
|
||||
array_push(ports, port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buffer_size = MIDI_DEFAULT_BUFFER_SIZE;
|
||||
snd_midi_event_new(buffer_size, &coder);
|
||||
snd_midi_event_init(coder);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Cleanup(void)
|
||||
{
|
||||
if (!seq)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (subscription)
|
||||
{
|
||||
snd_seq_unsubscribe_port(seq, subscription);
|
||||
snd_seq_port_subscribe_free(subscription);
|
||||
subscription = NULL;
|
||||
}
|
||||
if (vport >= 0)
|
||||
{
|
||||
snd_seq_delete_port(seq, vport);
|
||||
vport = -1;
|
||||
}
|
||||
if (coder)
|
||||
{
|
||||
snd_midi_event_free(coder);
|
||||
coder = NULL;
|
||||
}
|
||||
snd_seq_close(seq);
|
||||
seq = NULL;
|
||||
}
|
||||
|
||||
static void SendMessage(const byte *message, unsigned int length)
|
||||
{
|
||||
if (length > buffer_size)
|
||||
{
|
||||
buffer_size = length;
|
||||
snd_midi_event_resize_buffer(coder, buffer_size);
|
||||
}
|
||||
|
||||
snd_seq_event_t ev;
|
||||
snd_seq_ev_clear(&ev);
|
||||
snd_seq_ev_set_source(&ev, vport);
|
||||
snd_seq_ev_set_subs(&ev);
|
||||
snd_seq_ev_set_direct(&ev);
|
||||
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
if (snd_midi_event_encode_byte(coder, message[i], &ev) == 1)
|
||||
{
|
||||
// Send the event
|
||||
int err = snd_seq_event_output(seq, &ev);
|
||||
if (err < 0)
|
||||
{
|
||||
I_Printf(VB_ERROR, "SendMessage: %s", snd_strerror(err));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snd_seq_drain_output(seq);
|
||||
}
|
||||
|
||||
void MIDI_SendShortMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
SendMessage(message, length);
|
||||
}
|
||||
|
||||
void MIDI_SendLongMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
SendMessage(message, length);
|
||||
}
|
||||
|
||||
int MIDI_CountDevices(void)
|
||||
{
|
||||
Init();
|
||||
return array_size(ports);
|
||||
}
|
||||
|
||||
const char *MIDI_GetDeviceName(int device)
|
||||
{
|
||||
if (device >= array_size(ports))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return ports[device].name;
|
||||
}
|
||||
|
||||
boolean MIDI_OpenDevice(int device)
|
||||
{
|
||||
if (!Init() || device >= array_size(ports))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int err;
|
||||
snd_seq_addr_t sender, receiver;
|
||||
receiver.client = ports[device].client_id;
|
||||
receiver.port = ports[device].port_id;
|
||||
sender.client = snd_seq_client_id(seq);
|
||||
|
||||
vport = snd_seq_create_simple_port(
|
||||
seq, NULL,
|
||||
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ,
|
||||
SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
if (vport < 0)
|
||||
{
|
||||
I_Printf(VB_ERROR, "MIDI_OpenDevice: %s", snd_strerror(vport));
|
||||
Cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
sender.port = vport;
|
||||
|
||||
// Make subscription
|
||||
snd_seq_port_subscribe_malloc(&subscription);
|
||||
snd_seq_port_subscribe_set_sender(subscription, &sender);
|
||||
snd_seq_port_subscribe_set_dest(subscription, &receiver);
|
||||
snd_seq_port_subscribe_set_time_update(subscription, 1);
|
||||
snd_seq_port_subscribe_set_time_real(subscription, 1);
|
||||
err = snd_seq_subscribe_port(seq, subscription);
|
||||
if (err < 0)
|
||||
{
|
||||
I_Printf(VB_ERROR, "MIDI_OpenDevice: %s", snd_strerror(err));
|
||||
Cleanup();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MIDI_CloseDevice(void)
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// CoreMIDI and DLS Synth
|
||||
//---------------------------------------------------------
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
#include <CoreAudio/HostTime.h>
|
||||
#include <CoreMIDI/MIDIServices.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <AvailabilityMacros.h>
|
||||
|
||||
#include "m_array.h"
|
||||
#include "m_misc.h"
|
||||
|
||||
static AUGraph graph;
|
||||
static AudioUnit unit;
|
||||
|
||||
static MIDIPortRef port;
|
||||
static MIDIClientRef client;
|
||||
static MIDIEndpointRef endpoint;
|
||||
|
||||
// Maximum buffer size that CoreMIDI can handle for MIDIPacketList
|
||||
#define PACKET_BUFFER_SIZE 65536
|
||||
static Byte *packet_buffer = NULL;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *name;
|
||||
int id;
|
||||
} device_t;
|
||||
|
||||
static device_t *devices = NULL;
|
||||
static int current_device;
|
||||
|
||||
static void Init(void)
|
||||
{
|
||||
if (array_size(devices))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
device_t device;
|
||||
device.name = "DLS Synth";
|
||||
device.id = -1;
|
||||
array_push(devices, device);
|
||||
|
||||
int num_dest = MIDIGetNumberOfDestinations();
|
||||
for (int i = 0; i < num_dest; ++i)
|
||||
{
|
||||
MIDIEndpointRef dest = MIDIGetDestination(i);
|
||||
if (!dest)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
CFStringRef name;
|
||||
if (MIDIObjectGetStringProperty(dest, kMIDIPropertyName, &name)
|
||||
== noErr)
|
||||
{
|
||||
CFIndex length;
|
||||
CFRange range = {0, CFStringGetLength(name)};
|
||||
|
||||
CFStringGetBytes(name, range, kCFStringEncodingASCII, '?', false,
|
||||
NULL, INT_MAX, &length);
|
||||
|
||||
char *buffer = malloc(length + 1);
|
||||
|
||||
CFStringGetBytes(name, range, kCFStringEncodingASCII, '?', false,
|
||||
(UInt8 *)buffer, length, NULL);
|
||||
|
||||
buffer[length] = '\0';
|
||||
|
||||
device.name = buffer;
|
||||
device.id = i;
|
||||
array_push(devices, device);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void Cleanup(void)
|
||||
{
|
||||
if (graph)
|
||||
{
|
||||
AUGraphStop(graph);
|
||||
DisposeAUGraph(graph);
|
||||
graph = NULL;
|
||||
}
|
||||
if (port)
|
||||
{
|
||||
MIDIPortDispose(port);
|
||||
port = 0;
|
||||
}
|
||||
if (client)
|
||||
{
|
||||
MIDIClientDispose(client);
|
||||
client = 0;
|
||||
}
|
||||
if (packet_buffer)
|
||||
{
|
||||
free(packet_buffer);
|
||||
packet_buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void SendMessage(const byte *message, unsigned int length)
|
||||
{
|
||||
MIDITimeStamp time_stamp = AudioGetCurrentHostTime();
|
||||
MIDIPacketList *packet_list = (MIDIPacketList *)packet_buffer;
|
||||
MIDIPacket *packet = MIDIPacketListInit(packet_list);
|
||||
|
||||
// MIDIPacketList and MIDIPacket consume extra buffer areas for meta
|
||||
// information, and available size is smaller than buffer size. Here, we
|
||||
// simply assume that at least half size is available for data payload.
|
||||
ByteCount send_size = MIN(length, PACKET_BUFFER_SIZE / 2);
|
||||
|
||||
// Add message to the MIDIPacketList
|
||||
MIDIPacketListAdd(packet_list, PACKET_BUFFER_SIZE, packet, time_stamp,
|
||||
send_size, message);
|
||||
|
||||
if (MIDISend(port, endpoint, packet_list) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "SendMessage: MIDISend failed");
|
||||
}
|
||||
}
|
||||
|
||||
void MIDI_SendShortMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
if (current_device > 0)
|
||||
{
|
||||
SendMessage(message, length);
|
||||
return;
|
||||
}
|
||||
|
||||
UInt32 data[3] = {0};
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
data[i] = message[i];
|
||||
}
|
||||
|
||||
if (MusicDeviceMIDIEvent(unit, data[0], data[1], data[2], 0)
|
||||
!= noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "MIDI_SendShortMsg: MusicDeviceMIDIEvent failed");
|
||||
}
|
||||
}
|
||||
|
||||
void MIDI_SendLongMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
if (current_device > 0)
|
||||
{
|
||||
SendMessage(message, length);
|
||||
return;
|
||||
}
|
||||
|
||||
if (MusicDeviceSysEx(unit, message, length) != noErr)
|
||||
{
|
||||
I_Printf(VB_ERROR, "MIDI_SendLongMsg: MusicDeviceSysEx failed");
|
||||
}
|
||||
}
|
||||
|
||||
int MIDI_CountDevices(void)
|
||||
{
|
||||
Init();
|
||||
return array_size(devices);
|
||||
}
|
||||
|
||||
const char *MIDI_GetDeviceName(int device)
|
||||
{
|
||||
if (device >= array_size(devices))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return devices[device].name;
|
||||
}
|
||||
|
||||
#define CHECK_ERR(stmt) \
|
||||
do \
|
||||
{ \
|
||||
if ((stmt) != noErr) \
|
||||
{ \
|
||||
I_Printf(VB_ERROR, "%s: " #stmt " failed", __func__); \
|
||||
Cleanup(); \
|
||||
return false; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static boolean OpenDLSSynth(void)
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
ComponentDescription d;
|
||||
#else
|
||||
AudioComponentDescription d;
|
||||
#endif
|
||||
AUNode synth, output;
|
||||
|
||||
CHECK_ERR(NewAUGraph(&graph));
|
||||
|
||||
// The default output device
|
||||
d.componentType = kAudioUnitType_Output;
|
||||
d.componentSubType = kAudioUnitSubType_DefaultOutput;
|
||||
d.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
d.componentFlags = 0;
|
||||
d.componentFlagsMask = 0;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
CHECK_ERR(AUGraphNewNode(graph, &d, 0, NULL, &output));
|
||||
#else
|
||||
CHECK_ERR(AUGraphAddNode(graph, &d, &output));
|
||||
#endif
|
||||
|
||||
// The built-in default (softsynth) music device
|
||||
d.componentType = kAudioUnitType_MusicDevice;
|
||||
d.componentSubType = kAudioUnitSubType_DLSSynth;
|
||||
d.componentManufacturer = kAudioUnitManufacturer_Apple;
|
||||
d.componentFlags = 0;
|
||||
d.componentFlagsMask = 0;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
CHECK_ERR(AUGraphNewNode(graph, &d, 0, NULL, &synth));
|
||||
#else
|
||||
CHECK_ERR(AUGraphAddNode(graph, &d, &synth));
|
||||
#endif
|
||||
|
||||
CHECK_ERR(AUGraphConnectNodeInput(graph, synth, 0, output, 0));
|
||||
CHECK_ERR(AUGraphOpen(graph));
|
||||
CHECK_ERR(AUGraphInitialize(graph));
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED < 1050
|
||||
CHECK_ERR(AUGraphGetNodeInfo(graph, synth, NULL, NULL, NULL, &unit));
|
||||
#else
|
||||
CHECK_ERR(AUGraphNodeInfo(graph, synth, NULL, &unit));
|
||||
#endif
|
||||
CHECK_ERR(AUGraphStart(graph));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean MIDI_OpenDevice(int device)
|
||||
{
|
||||
Init();
|
||||
if (device >= array_size(devices))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
current_device = device;
|
||||
|
||||
if (current_device == 0)
|
||||
{
|
||||
return OpenDLSSynth();
|
||||
}
|
||||
|
||||
endpoint = MIDIGetDestination(devices[current_device].id);
|
||||
|
||||
// Create a MIDI client and port
|
||||
CHECK_ERR(MIDIClientCreate(CFSTR(PROJECT_NAME), NULL, NULL, &client));
|
||||
CHECK_ERR(MIDIOutputPortCreate(client, CFSTR(PROJECT_NAME"Port"), &port));
|
||||
|
||||
packet_buffer = malloc(PACKET_BUFFER_SIZE);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MIDI_CloseDevice(void)
|
||||
{
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// DUMMY
|
||||
//---------------------------------------------------------
|
||||
#else
|
||||
|
||||
void MIDI_SendShortMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
void MIDI_SendLongMsg(const byte *message, unsigned int length)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
int MIDI_CountDevices(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *MIDI_GetDeviceName(int device)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
boolean MIDI_OpenDevice(int device)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void MIDI_CloseDevice(void)
|
||||
{
|
||||
;
|
||||
}
|
||||
|
||||
#endif
|
31
src/midiout.h
Normal file
31
src/midiout.h
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// Copyright(C) 2024 Roman Fomin
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 2
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
#ifndef MIDIOUT_H
|
||||
#define MIDIOUT_H
|
||||
|
||||
#include "doomtype.h"
|
||||
|
||||
void MIDI_SendShortMsg(const byte *message, unsigned int length);
|
||||
|
||||
void MIDI_SendLongMsg(const byte *message, unsigned int length);
|
||||
|
||||
int MIDI_CountDevices(void);
|
||||
|
||||
const char *MIDI_GetDeviceName(int device);
|
||||
|
||||
boolean MIDI_OpenDevice(int device);
|
||||
|
||||
void MIDI_CloseDevice(void);
|
||||
|
||||
#endif
|
@ -1373,7 +1373,7 @@ static const char *screensize_strings[] = {
|
||||
|
||||
static const char *hudtype_strings[] = {"Crispy", "Boom No Bars", "Boom"};
|
||||
|
||||
static const char **M_GetHUDModeStrings(void)
|
||||
static const char **GetHUDModeStrings(void)
|
||||
{
|
||||
static const char *crispy_strings[] = {"Off", "Original", "Widescreen"};
|
||||
static const char *boom_strings[] = {"Minimal", "Compact", "Distributed"};
|
||||
@ -2059,7 +2059,12 @@ int midi_player_menu;
|
||||
|
||||
static const char **GetMidiDevicesStrings(void)
|
||||
{
|
||||
return I_DeviceList(&midi_player_menu);
|
||||
const char **devices = I_DeviceList();
|
||||
if (midi_player_menu >= array_size(devices))
|
||||
{
|
||||
midi_player_menu = 0;
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
static void SetMidiPlayer(void)
|
||||
@ -3791,7 +3796,7 @@ static const char **GetStrings(int id)
|
||||
|
||||
static void UpdateHUDModeStrings(void)
|
||||
{
|
||||
selectstrings[str_hudmode] = M_GetHUDModeStrings();
|
||||
selectstrings[str_hudmode] = GetHUDModeStrings();
|
||||
}
|
||||
|
||||
void MN_InitMenuStrings(void)
|
||||
|
@ -486,16 +486,6 @@ void S_UpdateSounds(const mobj_t *listener)
|
||||
I_ProcessSoundUpdates();
|
||||
}
|
||||
|
||||
void S_UpdateMusic(void)
|
||||
{
|
||||
if (nomusicparm)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
I_UpdateMusic();
|
||||
}
|
||||
|
||||
void S_SetMusicVolume(int volume)
|
||||
{
|
||||
// jff 1/22/98 return if music is not enabled
|
||||
|
@ -68,8 +68,6 @@ void S_ResumeSound(void);
|
||||
|
||||
void S_RestartMusic(void);
|
||||
|
||||
void S_UpdateMusic(void);
|
||||
|
||||
//
|
||||
// Updates music & sounds
|
||||
//
|
||||
|
Loading…
x
Reference in New Issue
Block a user