More audiolib work

git-svn-id: https://svn.eduke32.com/eduke32@8218 1a8010ca-5511-0410-912e-c29ae57300e0

# Conflicts:
#	source/audiolib/src/music.cpp

# Conflicts:
#	GNUmakefile
#	platform/Windows/audiolib.vcxproj
#	platform/Windows/audiolib.vcxproj.filters
#	source/duke3d/src/menus.cpp
This commit is contained in:
terminx 2019-10-19 23:48:08 +00:00 committed by Christoph Oelckers
parent 8b20118026
commit 8a1681fb5a
16 changed files with 412 additions and 719 deletions

View file

@ -1,68 +0,0 @@
/*
Copyright (C) 1994-1995 Apogee Software, Ltd.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au)
*/
#ifndef __MPU401_H
#define __MPU401_H
#include "compat.h"
#define MPU_DefaultAddress 0x330
enum MPU_ERRORS
{
MPU_Warning = -2,
MPU_Error = -1,
MPU_Ok = 0
};
#define MPU_NotFound -1
#define MPU_UARTFailed -2
#define MPU_ReadyToWrite 0x40
#define MPU_ReadyToRead 0x80
#define MPU_CmdEnterUART 0x3f
#define MPU_CmdReset 0xff
#define MPU_CmdAcknowledge 0xfe
extern int _MPU_CurrentBuffer;
extern int _MPU_BuffersWaiting;
void MPU_SendMidi( char *data, int count );
void MPU_SendMidiImmediate( char *data, int count );
int MPU_Reset( void );
int MPU_Init( int addr );
void MPU_NoteOff( int channel, int key, int velocity );
void MPU_NoteOn( int channel, int key, int velocity );
void MPU_PolyAftertouch( int channel, int key, int pressure );
void MPU_ControlChange( int channel, int number, int value );
void MPU_ProgramChange( int channel, int program );
void MPU_ChannelAftertouch( int channel, int pressure );
void MPU_PitchBend( int channel, int lsb, int msb );
void MPU_SetTempo(int tempo);
void MPU_SetDivision(int division);
void MPU_SetVolume(int volume);
int MPU_GetVolume(void);
void MPU_BeginPlayback( void );
void MPU_Pause(void);
void MPU_Unpause(void);
#endif

View file

@ -124,7 +124,7 @@ void MV_SetReverseStereo(int setting);
int MV_GetReverseStereo(void);
int MV_Init(int soundcard, int MixRate, int Voices, int numchannels, void *initdata);
int MV_Shutdown(void);
void MV_HookMusicRoutine(void (*callback)(char *buffer, int length));
void MV_HookMusicRoutine(void (*callback)(void));
void MV_UnhookMusicRoutine(void);
static inline void MV_SetPrintf(void (*function)(const char *, ...)) { if (function) MV_Printf = function; }

View file

@ -37,7 +37,6 @@ typedef enum
ASS_SDL,
ASS_DirectSound,
ASS_WinMM,
ASS_MPU401,
ASS_OPL3,
ASS_NumSoundCards,
ASS_AutoDetect = -2

View file

@ -48,6 +48,8 @@ namespace OPLMusic {
#define MIDI_PAN 10
#define MIDI_DETUNE 94
#define MIDI_RHYTHM_CHANNEL 9
#define MIDI_BANK_SELECT_MSB 0
#define MIDI_BANK_SELECT_LSB 32
#define MIDI_RPN_MSB 100
#define MIDI_RPN_LSB 101
#define MIDI_DATAENTRY_MSB 6
@ -67,8 +69,13 @@ namespace OPLMusic {
#define MIDI_SYSEX_CONTINUE 0xF7
#define MIDI_META_EVENT 0xFF
#define MIDI_END_OF_TRACK 0x2F
#define MIDI_HOLD1 0x40
#define MIDI_SOSTENUTO 0x42
#define MIDI_TEMPO_CHANGE 0x51
#define MIDI_TIME_SIGNATURE 0x58
#define MIDI_REVERB 0x5b
#define MIDI_CHORUS 0x5d
#define MIDI_ALL_SOUNDS_OFF 0x78
#define MIDI_RESET_ALL_CONTROLLERS 0x79
#define MIDI_ALL_NOTES_OFF 0x7b
#define MIDI_MONO_MODE_ON 0x7E

View file

@ -231,6 +231,11 @@ extern Pan MV_PanTable[ MV_NUMPANPOSITIONS ][ MV_MAXVOLUME + 1 ];
extern int MV_ErrorCode;
extern int MV_Installed;
extern int MV_MixRate;
extern char *MV_MusicBuffer;
extern int MV_BufferSize;
extern int MV_MIDIRenderTempo;
extern int MV_MIDIRenderTimer;
static FORCE_INLINE int MV_SetErrorCode(int status)
{

View file

@ -0,0 +1,103 @@
/**
* Adlib MIDI output
*/
#include "driver_adlib.h"
#include "al_midi.h"
#include "compat.h"
#include "midifuncs.h"
#include "midi.h"
#include "_multivc.h"
enum {
AdlibErr_Warning = -2,
AdlibErr_Error = -1,
AdlibErr_Ok = 0,
AdlibErr_Uninitialised,
};
static int ErrorCode = AdlibErr_Ok;
int AdlibDrv_GetError(void)
{
return ErrorCode;
}
const char *AdlibDrv_ErrorString( int ErrorNumber )
{
const char *ErrorString;
switch( ErrorNumber )
{
case AdlibErr_Warning :
case AdlibErr_Error :
ErrorString = AdlibDrv_ErrorString( ErrorCode );
break;
case AdlibErr_Ok :
ErrorString = "Adlib ok.";
break;
case AdlibErr_Uninitialised:
ErrorString = "Adlib uninitialised.";
break;
default:
ErrorString = "Unknown Adlib error.";
break;
}
return ErrorString;
}
int AdlibDrv_MIDI_Init(midifuncs *funcs)
{
AdlibDrv_MIDI_Shutdown();
Bmemset(funcs, 0, sizeof(midifuncs));
funcs->NoteOff = OPLMusic::AL_NoteOff;
funcs->NoteOn = OPLMusic::AL_NoteOn;
funcs->PolyAftertouch = NULL;
funcs->ControlChange = OPLMusic::AL_ControlChange;
funcs->ProgramChange = OPLMusic::AL_ProgramChange;
funcs->ChannelAftertouch = NULL;
funcs->PitchBend = OPLMusic::AL_SetPitchBend;
return AdlibErr_Ok;
}
void AdlibDrv_MIDI_Shutdown(void)
{
AdlibDrv_MIDI_HaltPlayback();
}
int AdlibDrv_MIDI_StartPlayback(void (*service)(void))
{
AdlibDrv_MIDI_HaltPlayback();
OPLMusic::AL_Init(MV_MixRate);
MV_HookMusicRoutine(service);
return MIDI_Ok;
}
void AdlibDrv_MIDI_HaltPlayback(void)
{
MV_UnhookMusicRoutine();
}
void AdlibDrv_MIDI_SetTempo(int tempo, int division)
{
MV_MIDIRenderTempo = (tempo * division)/60;
MV_MIDIRenderTimer = 0;
}
void AdlibDrv_MIDI_Lock(void)
{
}
void AdlibDrv_MIDI_Unlock(void)
{
}

View file

@ -0,0 +1,32 @@
/*
Copyright (C) 2009 Jonathon Fowler <jf@jonof.id.au>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "midifuncs.h"
int AdlibDrv_GetError(void);
const char *AdlibDrv_ErrorString( int ErrorNumber );
int AdlibDrv_MIDI_Init(midifuncs *);
void AdlibDrv_MIDI_Shutdown(void);
int AdlibDrv_MIDI_StartPlayback(void (*service)(void));
void AdlibDrv_MIDI_HaltPlayback(void);
void AdlibDrv_MIDI_SetTempo(int tempo, int division);
void AdlibDrv_MIDI_Lock(void);
void AdlibDrv_MIDI_Unlock(void);

View file

@ -65,7 +65,7 @@ static HANDLE midiMutex;
static BOOL midiStreamRunning;
static int midiLastDivision;
#define THREAD_QUEUE_INTERVAL 10 // 1/10 sec
#define MIDI_BUFFER_SPACE (12*128) // 128 note-on events
#define MIDI_BUFFER_SPACE (12*128u) // 128 note-on events
typedef struct MidiBuffer {
struct MidiBuffer *next;
@ -79,7 +79,6 @@ static volatile MidiBuffer activeMidiBuffers;
static volatile MidiBuffer spareMidiBuffers;
static MidiBuffer *currentMidiBuffer;
#define MIDI_NOTE_OFF 0x80
#define MIDI_NOTE_ON 0x90
#define MIDI_POLY_AFTER_TCH 0xA0
@ -94,8 +93,6 @@ static MidiBuffer *currentMidiBuffer;
#define MIDI_ALL_NOTES_OFF 0x7B
int WinMMDrv_GetError(void)
{
return ErrorCode;
@ -357,7 +354,7 @@ static void midi_setup_event(int length, unsigned char ** data)
*/
static BOOL midi_get_buffer(int length, unsigned char ** data)
{
int datalen;
unsigned int datalen;
MidiBuffer * node;
// determine the space to alloc.
@ -646,6 +643,8 @@ static DWORD midi_get_tick(void)
static DWORD WINAPI midiDataThread(LPVOID lpParameter)
{
UNREFERENCED_PARAMETER(lpParameter);
DWORD waitret;
DWORD sequenceTime;
DWORD sleepAmount = 100 / THREAD_QUEUE_INTERVAL;
@ -677,7 +676,7 @@ static DWORD WINAPI midiDataThread(LPVOID lpParameter)
sequenceTime = midi_get_tick();
sleepAmount = 100 / THREAD_QUEUE_INTERVAL;
if ((int)(midiThreadTimer - sequenceTime) > midiThreadQueueTicks) {
if ((midiThreadTimer - sequenceTime) > midiThreadQueueTicks) {
// we're running ahead, so sleep for half the usual
// amount and try again
sleepAmount /= 2;

View file

@ -26,6 +26,7 @@
#include "drivers.h"
#include "driver_nosound.h"
#include "driver_adlib.h"
#ifdef RENDERTYPESDL
# include "driver_sdl.h"
@ -142,49 +143,22 @@ static struct {
UNSUPPORTED_COMPLETELY
#endif
// TODO: create the driver functions for these
//
// older MPU-401 code...
#ifdef _WIN32
{
"MPU-401",
WinMMDrv_GetError,
WinMMDrv_ErrorString,
UNSUPPORTED_PCM,
WinMMDrv_MIDI_Init,
WinMMDrv_MIDI_Shutdown,
WinMMDrv_MIDI_StartPlayback,
WinMMDrv_MIDI_HaltPlayback,
WinMMDrv_MIDI_SetTempo,
WinMMDrv_MIDI_Lock,
WinMMDrv_MIDI_Unlock,
},
#else
UNSUPPORTED_COMPLETELY
#endif
#ifdef _WIN32
// OPL3 emulation
{
"OPL3",
WinMMDrv_GetError,
WinMMDrv_ErrorString,
"Nuked OPL3",
AdlibDrv_GetError,
AdlibDrv_ErrorString,
UNSUPPORTED_PCM,
WinMMDrv_MIDI_Init,
WinMMDrv_MIDI_Shutdown,
WinMMDrv_MIDI_StartPlayback,
WinMMDrv_MIDI_HaltPlayback,
WinMMDrv_MIDI_SetTempo,
WinMMDrv_MIDI_Lock,
WinMMDrv_MIDI_Unlock,
AdlibDrv_MIDI_Init,
AdlibDrv_MIDI_Shutdown,
AdlibDrv_MIDI_StartPlayback,
AdlibDrv_MIDI_HaltPlayback,
AdlibDrv_MIDI_SetTempo,
AdlibDrv_MIDI_Lock,
AdlibDrv_MIDI_Unlock,
},
#else
UNSUPPORTED_COMPLETELY
#endif
};

View file

@ -37,14 +37,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "midi.h"
#include "_midi.h"
#include "_multivc.h"
#include "al_midi.h"
#include "compat.h"
#include "mpu401.h"
#include "multivoc.h"
#include "music.h"
#include "pragmas.h"
#include "sndcards.h"
#include "windows_inc.h"
extern int MV_MixRate;
extern int ASS_MIDISoundDriver;
@ -87,11 +86,10 @@ static midifuncs *_MIDI_Funcs;
static int _MIDI_Reset;
int MIDI_Tempo = 120;
static int MIDI_Tempo = 120;
static int _MIDI_PlayRoutine = -1;
static int _MIDI_MixRate = 44100;
static int _MIDI_MixTimer;
int MV_MIDIRenderTempo = -1;
int MV_MIDIRenderTimer;
static int _MIDI_ReadNumber(void *from, size_t size)
{
@ -521,13 +519,18 @@ static int _MIDI_SendControlChange(int channel, int c1, int c2)
int MIDI_AllNotesOff(void)
{
SoundDriver_MIDI_Lock();
for (bssize_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
{
_MIDI_SendControlChange(channel, 0x40, 0);
_MIDI_SendControlChange(channel, MIDI_HOLD1, 0);
_MIDI_SendControlChange(channel, MIDI_SOSTENUTO, 0);
_MIDI_SendControlChange(channel, MIDI_ALL_NOTES_OFF, 0);
_MIDI_SendControlChange(channel, 0x78, 0);
_MIDI_SendControlChange(channel, MIDI_ALL_SOUNDS_OFF, 0);
}
SoundDriver_MIDI_Unlock();
return MIDI_Ok;
}
@ -554,6 +557,8 @@ int MIDI_Reset(void)
{
MIDI_AllNotesOff();
SoundDriver_MIDI_Lock();
for (bssize_t channel = 0; channel < NUM_MIDI_CHANNELS; channel++)
{
_MIDI_SendControlChange(channel, MIDI_RESET_ALL_CONTROLLERS, 0);
@ -566,6 +571,8 @@ int MIDI_Reset(void)
_MIDI_SendChannelVolumes();
SoundDriver_MIDI_Unlock();
_MIDI_Reset = TRUE;
return MIDI_Ok;
@ -615,8 +622,6 @@ void MIDI_ContinueSong(void)
return;
_MIDI_SongActive = TRUE;
if (ASS_MIDISoundDriver == ASS_MPU401)
MPU_Unpause();
}
void MIDI_PauseSong(void)
@ -626,8 +631,6 @@ void MIDI_PauseSong(void)
_MIDI_SongActive = FALSE;
MIDI_AllNotesOff();
if (ASS_MIDISoundDriver == ASS_MPU401)
MPU_Pause();
}
void MIDI_SetMidiFuncs(midifuncs *funcs) { _MIDI_Funcs = funcs; }
@ -637,15 +640,11 @@ void MIDI_StopSong(void)
if (!_MIDI_SongLoaded)
return;
SoundDriver_MIDI_HaltPlayback();
_MIDI_SongActive = FALSE;
_MIDI_SongLoaded = FALSE;
if (ASS_MIDISoundDriver == ASS_MPU401)
{
MPU_Reset();
MPU_Init(ASS_MIDISoundDriver);
}
MIDI_Reset();
_MIDI_ResetTracks();
@ -658,10 +657,6 @@ void MIDI_StopSong(void)
_MIDI_TotalTicks = 0;
_MIDI_TotalBeats = 0;
_MIDI_TotalMeasures = 0;
if (ASS_MIDISoundDriver == ASS_OPL3)
MV_UnhookMusicRoutine();
}
int MIDI_PlaySong(char *song, int loopflag)
@ -728,16 +723,6 @@ int MIDI_PlaySong(char *song, int loopflag)
if (_MIDI_SongLoaded)
MIDI_StopSong();
switch (ASS_MIDISoundDriver)
{
case ASS_MPU401:
MPU_Init(0/*MUSIC_SoundDevice*/);
break;
case ASS_OPL3:
OPLMusic::AL_Init(MV_MixRate);
break;
}
_MIDI_Loop = loopflag;
_MIDI_NumTracks = My_MIDI_NumTracks;
@ -753,59 +738,34 @@ int MIDI_PlaySong(char *song, int loopflag)
_MIDI_Reset = FALSE;
if (ASS_MIDISoundDriver == ASS_MPU401)
if (ASS_MIDISoundDriver == ASS_OPL3)
{
MIDI_SetDivision(_MIDI_Division);
//MIDI_SetTempo( 120 );
MV_MIDIRenderTempo = 100;
MV_MIDIRenderTimer = 0;
}
else
{
_MIDI_PlayRoutine = 100;
_MIDI_MixTimer = 0;
//MIDI_SetDivision(_MIDI_Division);
MIDI_SetTempo(120);
}
if (SoundDriver_MIDI_StartPlayback(ASS_MIDISoundDriver == ASS_OPL3 ? MV_RenderMIDI : _MIDI_ServiceRoutine) != MIDI_Ok)
return MIDI_DriverError;
_MIDI_SongLoaded = TRUE;
_MIDI_SongActive = TRUE;
if (ASS_MIDISoundDriver == ASS_MPU401)
{
while (_MPU_BuffersWaiting < 4) _MIDI_ServiceRoutine();
MPU_BeginPlayback();
}
else if (ASS_MIDISoundDriver == ASS_OPL3)
{
MV_HookMusicRoutine(MIDI_MusicMix);
_MIDI_MixRate = MV_MixRate;
}
return MIDI_Ok;
}
void MIDI_SetTempo(int tempo)
{
int tickspersecond;
MIDI_Tempo = tempo;
tickspersecond = ((tempo) * _MIDI_Division)/60;
SoundDriver_MIDI_SetTempo(tempo, _MIDI_Division);
int const tickspersecond = tempo * _MIDI_Division / 60;
_MIDI_FPSecondsPerTick = tabledivide32_noinline(1 << TIME_PRECISION, tickspersecond);
if (ASS_MIDISoundDriver == ASS_MPU401)
{
MPU_SetTempo(tempo);
}
else if (ASS_MIDISoundDriver == ASS_OPL3)
{
_MIDI_PlayRoutine = tickspersecond;
_MIDI_MixTimer = 0;
}
}
void MIDI_SetDivision(int division)
{
if (ASS_MIDISoundDriver == ASS_MPU401)
MPU_SetDivision(division);
UNREFERENCED_PARAMETER(division);
}
int MIDI_GetTempo(void) { return MIDI_Tempo; }
@ -816,9 +776,6 @@ static void _MIDI_InitEMIDI(void)
switch (ASS_MIDISoundDriver)
{
case ASS_MPU401:
type = EMIDI_GeneralMIDI;
break;
case ASS_OPL3:
type = EMIDI_Adlib;
break;
@ -1026,21 +983,21 @@ static void _MIDI_InitEMIDI(void)
_MIDI_ResetTracks();
}
void MIDI_MusicMix(char *buffer, int length)
void MV_RenderMIDI(void)
{
int16_t * buffer16 = (int16_t *)buffer;
int const samples = length >> 2;
int16_t * buffer16 = (int16_t *)MV_MusicBuffer;
int const samples = MV_BufferSize >> 2;
for (int i = 0; i < samples; i++)
{
Bit16s buf[2];
while (_MIDI_MixTimer >= _MIDI_MixRate)
while (MV_MIDIRenderTimer >= MV_MixRate)
{
if (_MIDI_PlayRoutine >= 0)
if (MV_MIDIRenderTempo >= 0)
_MIDI_ServiceRoutine();
_MIDI_MixTimer -= _MIDI_MixRate;
MV_MIDIRenderTimer -= MV_MixRate;
}
if (_MIDI_PlayRoutine >= 0) _MIDI_MixTimer += _MIDI_PlayRoutine;
if (MV_MIDIRenderTempo >= 0) MV_MIDIRenderTimer += MV_MIDIRenderTempo;
OPL3_GenerateResampled(&OPLMusic::chip, buf);
*buffer16++ = clamp(buf[0]<<1, INT16_MIN, INT16_MAX);
*buffer16++ = clamp(buf[1]<<1, INT16_MIN, INT16_MAX);
@ -1050,7 +1007,6 @@ void MIDI_MusicMix(char *buffer, int length)
void MIDI_UpdateMusic(void)
{
if (!_MIDI_SongLoaded || !_MIDI_SongActive) return;
while (_MPU_BuffersWaiting < 4) _MIDI_ServiceRoutine();
}

View file

@ -66,7 +66,7 @@ void MIDI_StopSong(void);
int MIDI_PlaySong(char *song, int loopflag);
void MIDI_SetTempo(int tempo);
int MIDI_GetTempo(void);
void MIDI_MusicMix(char *buffer, int length);
void MV_RenderMIDI(void);
void MIDI_UpdateMusic(void);
void MIDI_SetDivision(int division);

View file

@ -1,497 +0,0 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
/**********************************************************************
module: MPU401.C
author: James R. Dose
date: January 1, 1994
Low level routines to support sending of MIDI data to MPU401
compatible MIDI interfaces.
(c) Copyright 1994 James R. Dose. All Rights Reserved.
**********************************************************************/
// This object is shared by all Build games with MIDI playback!
#include "mpu401.h"
#include "compat.h"
#include "pragmas.h"
#define NEED_MMSYSTEM_H
#include "windows_inc.h"
static HMIDISTRM hmido = (HMIDISTRM)-1;
static MIDIOUTCAPS midicaps;
static DWORD mididevice = -1;
#define PAD(x) ((((x)+3)&(~3)))
#define BUFFERLEN (32*4*4)
#define NUMBUFFERS 6
static char eventbuf[NUMBUFFERS][BUFFERLEN];
static int eventcnt[NUMBUFFERS];
static MIDIHDR bufferheaders[NUMBUFFERS];
int _MPU_CurrentBuffer = 0;
int _MPU_BuffersWaiting = 0;
extern uint32_t _MIDI_GlobalPositionInTicks;
uint32_t _MPU_LastEvent=0;
#define MIDI_NOTE_OFF 0x80
#define MIDI_NOTE_ON 0x90
#define MIDI_POLY_AFTER_TCH 0xA0
#define MIDI_CONTROL_CHANGE 0xB0
#define MIDI_PROGRAM_CHANGE 0xC0
#define MIDI_AFTER_TOUCH 0xD0
#define MIDI_PITCH_BEND 0xE0
#define MIDI_META_EVENT 0xFF
#define MIDI_END_OF_TRACK 0x2F
#define MIDI_TEMPO_CHANGE 0x51
#define MIDI_MONO_MODE_ON 0x7E
#define MIDI_ALL_NOTES_OFF 0x7B
/**********************************************************************
Memory locked functions:
**********************************************************************/
void MPU_FinishBuffer(int buffer)
{
if (!eventcnt[buffer]) return;
ZeroMemory(&bufferheaders[buffer], sizeof(MIDIHDR));
bufferheaders[buffer].lpData = eventbuf[buffer];
bufferheaders[buffer].dwBufferLength =
bufferheaders[buffer].dwBytesRecorded = eventcnt[buffer];
midiOutPrepareHeader((HMIDIOUT)hmido, &bufferheaders[buffer], sizeof(MIDIHDR));
midiStreamOut(hmido, &bufferheaders[buffer], sizeof(MIDIHDR));
// printf("Sending %d bytes (buffer %d)\n",eventcnt[buffer],buffer);
_MPU_BuffersWaiting++;
}
void MPU_BeginPlayback(void)
{
_MPU_LastEvent = _MIDI_GlobalPositionInTicks;
if (hmido != (HMIDISTRM)-1) midiStreamRestart(hmido);
}
void MPU_Pause(void)
{
if (hmido != (HMIDISTRM)-1) midiStreamPause(hmido);
}
void MPU_Unpause(void)
{
if (hmido != (HMIDISTRM)-1) midiStreamRestart(hmido);
}
void CALLBACK MPU_MIDICallback(HMIDIOUT handle, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
int i;
UNREFERENCED_PARAMETER(dwInstance);
UNREFERENCED_PARAMETER(dwParam2);
switch (uMsg)
{
case MOM_DONE:
midiOutUnprepareHeader((HMIDIOUT)handle, (MIDIHDR *)dwParam1, sizeof(MIDIHDR));
for (i=0; i<NUMBUFFERS; i++)
{
if ((MIDIHDR *)dwParam1 == &bufferheaders[i])
{
eventcnt[i] = 0; // marks the buffer as free
// printf("Finished buffer %d\n",i);
_MPU_BuffersWaiting--;
break;
}
}
return;
default: return;
}
}
/*---------------------------------------------------------------------
Function: MPU_SendMidi
Queues a MIDI message to the music device.
---------------------------------------------------------------------*/
int MPU_GetNextBuffer(void)
{
int i;
for (i=0; i<NUMBUFFERS; i++)
{
if (eventcnt[i] == 0) return i;
}
return -1;
}
void MPU_SendMidi(char *data, int count)
{
char *p;
int padded, nextbuffer;
static int masks[3] = { 0x000000ffl, 0x0000ffffl, 0x00ffffffl };
if (count <= 0) return;
if (count <= 3)
{
if (eventcnt[_MPU_CurrentBuffer] + 12 > BUFFERLEN)
{
// buffer over-full
nextbuffer = MPU_GetNextBuffer();
if (nextbuffer < 0)
{
// printf("All buffers full!\n");
return;
}
MPU_FinishBuffer(_MPU_CurrentBuffer);
_MPU_CurrentBuffer = nextbuffer;
}
p = eventbuf[_MPU_CurrentBuffer] + eventcnt[_MPU_CurrentBuffer];
((int *)p)[0] = _MIDI_GlobalPositionInTicks - _MPU_LastEvent;
((int *)p)[1] = 0;
((int *)p)[2] = (MEVT_SHORTMSG << 24) | ((*((int *)data)) & masks[count-1]);
eventcnt[_MPU_CurrentBuffer] += 12;
}
else
{
padded = PAD(count);
if (eventcnt[_MPU_CurrentBuffer] + 12 + padded > BUFFERLEN)
{
// buffer over-full
nextbuffer = MPU_GetNextBuffer();
if (nextbuffer < 0)
{
// printf("All buffers full!\n");
return;
}
MPU_FinishBuffer(_MPU_CurrentBuffer);
_MPU_CurrentBuffer = nextbuffer;
}
p = eventbuf[_MPU_CurrentBuffer] + eventcnt[_MPU_CurrentBuffer];
((int *)p)[0] = _MIDI_GlobalPositionInTicks - _MPU_LastEvent;
((int *)p)[1] = 0;
((int *)p)[2] = (MEVT_LONGMSG<<24) | (count & 0xffffffl);
p+=12; eventcnt[_MPU_CurrentBuffer] += 12;
for (; count>0; count--, padded--, eventcnt[_MPU_CurrentBuffer]++)
*(p++) = *(data++);
for (; padded>0; padded--, eventcnt[_MPU_CurrentBuffer]++)
*(p++) = 0;
}
_MPU_LastEvent = _MIDI_GlobalPositionInTicks;
}
/*---------------------------------------------------------------------
Function: MPU_SendMidiImmediate
Sends a MIDI message immediately to the the music device.
---------------------------------------------------------------------*/
void MPU_SendMidiImmediate(char *data, int count)
{
MIDIHDR mhdr;
static int masks[3] = { 0x00ffffffl, 0x0000ffffl, 0x000000ffl };
if (!count) return;
if (count<=3) midiOutShortMsg((HMIDIOUT)hmido, (*((int *)data)) & masks[count-1]);
else
{
ZeroMemory(&mhdr, sizeof(mhdr));
mhdr.lpData = data;
mhdr.dwBufferLength = count;
midiOutPrepareHeader((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR));
midiOutLongMsg((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR));
while (!(mhdr.dwFlags & MHDR_DONE)) ;
midiOutUnprepareHeader((HMIDIOUT)hmido, &mhdr, sizeof(MIDIHDR));
}
}
/*---------------------------------------------------------------------
Function: MPU_Reset
Resets the MPU401 card.
---------------------------------------------------------------------*/
int MPU_Reset
(
void
)
{
midiStreamStop(hmido);
midiStreamClose(hmido);
hmido = (HMIDISTRM)-1;
return MPU_Ok;
}
/*---------------------------------------------------------------------
Function: MPU_Init
Detects and initializes the MPU401 card.
---------------------------------------------------------------------*/
int MPU_Init
(
int addr
)
{
if (hmido != (HMIDISTRM)-1)
return MPU_Ok;
int i;
for (i=0; i<NUMBUFFERS; i++) eventcnt[i]=0;
mididevice = addr;
if (midiOutGetDevCaps(mididevice, &midicaps, sizeof(MIDIOUTCAPS)) != MMSYSERR_NOERROR) return MPU_Error;
if (midiStreamOpen(&hmido,(LPUINT)&mididevice,1,(DWORD_PTR)MPU_MIDICallback,0L,CALLBACK_FUNCTION) != MMSYSERR_NOERROR) return MPU_Error;
return MPU_Ok;
}
/*---------------------------------------------------------------------
Function: MPU_NoteOff
Sends a full MIDI note off event out to the music device.
---------------------------------------------------------------------*/
void MPU_NoteOff
(
int channel,
int key,
int velocity
)
{
char msg[3];
msg[0] = (MIDI_NOTE_OFF | channel);
msg[1] = (key);
msg[2] = (velocity);
MPU_SendMidi(msg, 3);
}
/*---------------------------------------------------------------------
Function: MPU_NoteOn
Sends a full MIDI note on event out to the music device.
---------------------------------------------------------------------*/
void MPU_NoteOn
(
int channel,
int key,
int velocity
)
{
char msg[3];
msg[0] = (MIDI_NOTE_ON | channel);
msg[1] = (key);
msg[2] = (velocity);
MPU_SendMidi(msg, 3);
}
/*---------------------------------------------------------------------
Function: MPU_PolyAftertouch
Sends a full MIDI polyphonic aftertouch event out to the music device.
---------------------------------------------------------------------*/
void MPU_PolyAftertouch
(
int channel,
int key,
int pressure
)
{
char msg[3];
msg[0] = (MIDI_POLY_AFTER_TCH | channel);
msg[1] = (key);
msg[2] = (pressure);
MPU_SendMidi(msg, 3);
}
/*---------------------------------------------------------------------
Function: MPU_ControlChange
Sends a full MIDI control change event out to the music device.
---------------------------------------------------------------------*/
void MPU_ControlChange
(
int channel,
int number,
int value
)
{
char msg[3];
msg[0] = (MIDI_CONTROL_CHANGE | channel);
msg[1] = (number);
msg[2] = (value);
MPU_SendMidi(msg, 3);
}
/*---------------------------------------------------------------------
Function: MPU_ProgramChange
Sends a full MIDI program change event out to the music device.
---------------------------------------------------------------------*/
void MPU_ProgramChange
(
int channel,
int program
)
{
char msg[2];
msg[0] = (MIDI_PROGRAM_CHANGE | channel);
msg[1] = (program);
MPU_SendMidi(msg, 2);
}
/*---------------------------------------------------------------------
Function: MPU_ChannelAftertouch
Sends a full MIDI channel aftertouch event out to the music device.
---------------------------------------------------------------------*/
void MPU_ChannelAftertouch
(
int channel,
int pressure
)
{
char msg[2];
msg[0] = (MIDI_AFTER_TOUCH | channel);
msg[1] = (pressure);
MPU_SendMidi(msg, 2);
}
/*---------------------------------------------------------------------
Function: MPU_PitchBend
Sends a full MIDI pitch bend event out to the music device.
---------------------------------------------------------------------*/
void MPU_PitchBend
(
int channel,
int lsb,
int msb
)
{
char msg[3];
msg[0] = (MIDI_PITCH_BEND | channel);
msg[1] = (lsb);
msg[2] = (msb);
MPU_SendMidi(msg, 3);
}
void MPU_SetTempo(int tempo)
{
MIDIPROPTEMPO prop;
prop.cbStruct = sizeof(MIDIPROPTEMPO);
prop.dwTempo = tabledivide32_noinline(60000000l, tempo);
midiStreamProperty(hmido, (LPBYTE)&prop, MIDIPROP_SET|MIDIPROP_TEMPO);
}
void MPU_SetDivision(int division)
{
MIDIPROPTIMEDIV prop;
prop.cbStruct = sizeof(MIDIPROPTIMEDIV);
prop.dwTimeDiv = division;
midiStreamProperty(hmido, (LPBYTE)&prop, MIDIPROP_SET|MIDIPROP_TIMEDIV);
}
void MPU_SetVolume(int volume)
{
/*
HMIXER hmixer;
int mixerid;
MIXERCONTROLDETAILS mxcd;
MIXERCONTROLDETAILS_UNSIGNED mxcdu;
MMRESULT mme;
if (mididevice < 0) return;
mme = mixerOpen(&hmixer, mididevice, 0,0, MIXER_OBJECTF_MIDIOUT);
if (mme) {
puts("Failed opening mixer");
return;
}
mixerGetID(hmixer, &mixerid, MIXER_OBJECTF_HMIXER);
printf("mixerid=%d\n",mixerid);
ZeroMemory(&mxcd,sizeof(mxcd));
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = MIXERCONTROL_CONTROLTYPE_VOLUME;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = (LPVOID)&mxcdu;
mxcdu.dwValue = (volume << 8) & 0xffff;
printf("set %d\n",mixerSetControlDetails((HMIXEROBJ)mididevice, &mxcd,
MIXER_OBJECTF_MIDIOUT|MIXER_SETCONTROLDETAILSF_VALUE));
mixerClose(hmixer);
*/
UNREFERENCED_PARAMETER(volume);
}
int MPU_GetVolume(void)
{
// if (mididevice < 0) return 0;
return 0;
}

View file

@ -58,7 +58,7 @@ int MV_Installed;
static int MV_TotalVolume = MV_MAXTOTALVOLUME;
static int MV_MaxVoices = 1;
static int MV_BufferSize = MV_MIXBUFFERSIZE;
int MV_BufferSize = MV_MIXBUFFERSIZE;
static int MV_BufferLength;
static int MV_NumberOfBuffers = MV_NUMBEROFBUFFERS;
@ -92,8 +92,8 @@ float MV_VolumeSmooth = 1.f;
int MV_Locked;
static char *MV_MusicBuffer;
static void (*MV_MusicCallback)(char *buffer, int length) = NULL;
char *MV_MusicBuffer;
static void (*MV_MusicCallback)(void);
static int MV_MixMusic = FALSE;
static bool MV_Mix(VoiceNode * const voice, int const buffer)
@ -269,10 +269,10 @@ static void MV_ServiceVoc(void)
if (MV_MixMusic)
{
MV_MusicCallback(MV_MusicBuffer, MV_BufferSize);
MV_MusicCallback();
int16_t *source = (int16_t*)MV_MusicBuffer;
int16_t *dest = (int16_t*)MV_MixBuffer[MV_MixPage+MV_NumberOfBuffers];
for (int i = 0; i < MV_BufferSize / 4; i++)
for (int i = 0; i < MV_BufferSize>>2; i++)
{
int sl = *source++;
int sr = *source++;
@ -716,7 +716,7 @@ static int MV_StartPlayback(void)
MV_MixPage = 1;
if (SoundDriver_PCM_BeginPlayback(MV_MixBuffer[0], MV_BufferSize, MV_NumberOfBuffers, MV_ServiceVoc) != MV_Ok)
if (SoundDriver_PCM_BeginPlayback(MV_MixBuffer[MV_NumberOfBuffers], MV_BufferSize, MV_NumberOfBuffers, MV_ServiceVoc) != MV_Ok)
return MV_SetErrorCode(MV_DriverError);
return MV_Ok;
@ -789,7 +789,7 @@ int MV_Init(int soundcard, int MixRate, int Voices, int numchannels, void *initd
MV_SetErrorCode(MV_Ok);
// MV_TotalMemory + 2: FIXME, see valgrind_errors.log
int const totalmem = Voices * sizeof(VoiceNode) + (MV_TOTALBUFFERSIZE << 1) + 2;
int const totalmem = Voices * sizeof(VoiceNode) + (MV_TOTALBUFFERSIZE<<1) + (MV_MIXBUFFERSIZE<<2);
char *ptr = (char *) Xaligned_alloc(16, totalmem);
@ -845,6 +845,8 @@ int MV_Init(int soundcard, int MixRate, int Voices, int numchannels, void *initd
ptr += MV_BufferSize;
}
MV_MusicBuffer = ptr;
// Calculate pan table
MV_CalcPanTable();
@ -894,7 +896,7 @@ int MV_Shutdown(void)
return MV_Ok;
}
void MV_HookMusicRoutine(void(*callback)(char *buffer, int length))
void MV_HookMusicRoutine(void(*callback)(void))
{
MV_Lock();
MV_MusicCallback = callback;
@ -903,12 +905,15 @@ void MV_HookMusicRoutine(void(*callback)(char *buffer, int length))
}
void MV_UnhookMusicRoutine(void)
{
if (MV_MusicCallback)
{
MV_Lock();
MV_MusicCallback = NULL;
MV_MixMusic = FALSE;
MV_Unlock();
}
}
const char *loopStartTags[loopStartTagCount] = { "LOOP_START", "LOOPSTART", "LOOP" };
const char *loopEndTags[loopEndTagCount] = { "LOOP_END", "LOOPEND" };

View file

@ -0,0 +1,158 @@
//-------------------------------------------------------------------------
/*
Copyright (C) 2010-2019 EDuke32 developers and contributors
Copyright (C) 2019 Nuke.YKT
This file is part of NBlood.
NBlood is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License version 2
as published by the Free Software Foundation.
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
//-------------------------------------------------------------------------
// This object is shared by all Build games with MIDI playback!
#include "music.h"
#include "al_midi.h"
#include "compat.h"
#include "drivers.h"
#include "midi.h"
#include "multivoc.h"
#include "sndcards.h"
int MUSIC_ErrorCode = MUSIC_Ok;
static midifuncs MUSIC_MidiFunctions;
#define MUSIC_SetErrorCode(status) MUSIC_ErrorCode = (status);
const char *MUSIC_ErrorString(int ErrorNumber)
{
const char *ErrorString;
switch (ErrorNumber)
{
case MUSIC_Warning:
case MUSIC_Error: ErrorString = MUSIC_ErrorString(MUSIC_ErrorCode); break;
case MUSIC_Ok: ErrorString = "Music ok."; break;
case MUSIC_MidiError: ErrorString = "Error playing MIDI file."; break;
default: ErrorString = "Unknown Music error code."; break;
}
return ErrorString;
}
int MUSIC_Init(int SoundCard)
{
int detected = 0;
if (SoundCard == ASS_AutoDetect)
{
redetect:
detected++;
#if defined _WIN32
SoundCard = ASS_WinMM;
#elif RENDERTYPESDL
SoundCard = ASS_SDL;
#else
SoundCard = ASS_NoSound;
#endif
}
if (SoundCard < 0 || SoundCard >= ASS_NumSoundCards)
{
failed:
MV_Printf("failed!\n");
MUSIC_ErrorCode = MUSIC_MidiError;
return MUSIC_Error;
}
if (!SoundDriver_IsMIDISupported(SoundCard))
{
MV_Printf("Couldn't init %s\n", SoundDriver_GetName(SoundCard));
if (detected < 2)
goto redetect;
goto failed;
}
ASS_MIDISoundDriver = SoundCard;
int status = SoundDriver_MIDI_Init(&MUSIC_MidiFunctions);
if (status != MUSIC_Ok)
{
if (detected < 2)
goto redetect;
goto failed;
}
MV_Printf("successfully initialized %s!\n", SoundDriver_GetName(SoundCard));
MIDI_SetMidiFuncs(&MUSIC_MidiFunctions);
return MUSIC_Ok;
}
int MUSIC_Shutdown(void)
{
MIDI_StopSong();
return MUSIC_Ok;
}
void MUSIC_SetVolume(int volume) { MIDI_SetVolume(min(max(0, volume), 255)); }
int MUSIC_GetVolume(void) { return MIDI_GetVolume(); }
void MUSIC_SetLoopFlag(int loopflag) { MIDI_SetLoopFlag(loopflag); }
void MUSIC_Continue(void) { MIDI_ContinueSong(); }
void MUSIC_Pause(void) { MIDI_PauseSong(); }
int MUSIC_StopSong(void)
{
MIDI_StopSong();
MUSIC_SetErrorCode(MUSIC_Ok);
return MUSIC_Ok;
}
int MUSIC_PlaySong(char *song, int songsize, int loopflag, const char *fn /*= nullptr*/)
{
UNREFERENCED_PARAMETER(songsize);
UNREFERENCED_PARAMETER(fn);
MUSIC_SetErrorCode(MUSIC_Ok)
if (MIDI_PlaySong(song, loopflag) != MIDI_Ok)
{
MUSIC_SetErrorCode(MUSIC_MidiError);
return MUSIC_Warning;
}
return MUSIC_Ok;
}
void MUSIC_Update(void)
{
MIDI_UpdateMusic();
}

View file

@ -1131,7 +1131,7 @@ static MenuEntry_t ME_SAVE_NEW = MAKE_MENUENTRY( s_NewSaveGame, &MF_Minifont, &M
static MenuEntry_t *ME_SAVE;
static MenuEntry_t **MEL_SAVE;
static int32_t soundrate, soundvoices;
static int32_t soundrate, soundvoices, musicdevice;
static MenuOption_t MEO_SOUND = MAKE_MENUOPTION( &MF_Redfont, &MEOS_OffOn, &snd_enabled.Value );
static MenuEntry_t ME_SOUND = MAKE_MENUENTRY( "Sound:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND, Option );
@ -1165,6 +1165,23 @@ static MenuRangeInt32_t MEO_SOUND_NUMVOICES = MAKE_MENURANGE( &soundvoices, &MF_
static MenuEntry_t ME_SOUND_NUMVOICES = MAKE_MENUENTRY( "Voices:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_NUMVOICES, RangeInt32 );
#endif
static char const *MEOSN_SOUND_MUSICDEVICE[] = {
#ifdef _WIN32
"Windows",
#endif
"OPL3",
};
static int32_t MEOSV_SOUND_MUSICDEVICE[] = {
#ifdef _WIN32
ASS_WinMM,
#endif
ASS_OPL3,
};
static MenuOptionSet_t MEOS_SOUND_MUSICDEVICE = MAKE_MENUOPTIONSET( MEOSN_SOUND_MUSICDEVICE, MEOSV_SOUND_MUSICDEVICE, 0x2 );
static MenuOption_t MEO_SOUND_MUSICDEVICE = MAKE_MENUOPTION( &MF_Redfont, &MEOS_SOUND_MUSICDEVICE, &musicdevice );
static MenuEntry_t ME_SOUND_MUSICDEVICE = MAKE_MENUENTRY( "Music device:", &MF_Redfont, &MEF_BigOptionsRt, &MEO_SOUND_MUSICDEVICE, Option );
static MenuEntry_t ME_SOUND_RESTART = MAKE_MENUENTRY( "Apply Changes", &MF_Redfont, &MEF_BigOptions_Apply, &MEO_NULL, Link );
#ifndef EDUKE32_SIMPLE_MENU
@ -1190,6 +1207,7 @@ static MenuEntry_t *MEL_ADVSOUND[] = {
&ME_SOUND_NUMVOICES,
&ME_Space2_Redfont,
#endif
&ME_SOUND_MUSICDEVICE,
&ME_SOUND_RESTART,
};
@ -3219,6 +3237,7 @@ static void Menu_EntryLinkActivate(MenuEntry_t *entry)
{
snd_mixrate = soundrate;
snd_numvoices = soundvoices;
ud.config.MusicDevice = musicdevice;
S_SoundShutdown();
S_MusicShutdown();
@ -4241,6 +4260,7 @@ static void Menu_AboutToStartDisplaying(Menu_t * m)
case MENU_ADVSOUND:
soundrate = snd_mixrate;
soundvoices = snd_numvoices;
musicdevice = ud.config.MusicDevice;
break;
default:

View file

@ -112,7 +112,7 @@ void S_SoundShutdown(void)
void S_MusicStartup(void)
{
initprintf("Initializing music...\n");
initprintf("Initializing music... ");
if (MUSIC_Init(ud.config.MusicDevice) == MUSIC_Ok || MUSIC_Init(0) == MUSIC_Ok || MUSIC_Init(1) == MUSIC_Ok)
{