mirror of
https://github.com/ZDoom/gzdoom-gles.git
synced 2024-11-28 15:02:39 +00:00
- made the MIDI to WAVE dumper functional again, including the option of choosing the MIDI synth and sound font with which it should be rendered.
This commit is contained in:
parent
786f1b34be
commit
2202a52840
6 changed files with 177 additions and 94 deletions
|
@ -713,6 +713,45 @@ ADD_STAT(music)
|
|||
return "No song playing";
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Common loader for the dumpers.
|
||||
//
|
||||
//==========================================================================
|
||||
extern MusPlayingInfo mus_playing;
|
||||
|
||||
static MIDISource *GetMIDISource(const char *fn)
|
||||
{
|
||||
FString src = fn;
|
||||
if (src.Compare("*") == 0) src = mus_playing.name;
|
||||
|
||||
auto lump = Wads.CheckNumForName(src, ns_music);
|
||||
if (lump < 0) lump = Wads.CheckNumForFullName(src);
|
||||
if (lump < 0)
|
||||
{
|
||||
Printf("Cannot find MIDI lump %s.\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FWadLump wlump = Wads.OpenLumpNum(lump);
|
||||
uint32_t id[32 / 4];
|
||||
|
||||
if (wlump.Read(id, 32) != 32 || wlump.Seek(-32, SEEK_CUR) != 0)
|
||||
{
|
||||
Printf("Unable to read lump %s\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto type = IdentifyMIDIType(id, 32);
|
||||
auto source = CreateMIDISource(wlump, type);
|
||||
|
||||
if (source == nullptr)
|
||||
{
|
||||
Printf("%s is not MIDI-based.\n", src.GetChars());
|
||||
return nullptr;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
//==========================================================================
|
||||
//
|
||||
// CCMD writeopl
|
||||
|
@ -762,29 +801,42 @@ UNSAFE_CCMD (writeopl)
|
|||
|
||||
UNSAFE_CCMD (writewave)
|
||||
{
|
||||
if (argv.argc() >= 2 && argv.argc() <= 3)
|
||||
if (argv.argc() >= 3 && argv.argc() <= 7)
|
||||
{
|
||||
if (currSong == nullptr)
|
||||
auto source = GetMIDISource(argv[1]);
|
||||
if (source == nullptr) return;
|
||||
|
||||
EMidiDevice dev = MDEV_DEFAULT;
|
||||
|
||||
if (argv.argc() >= 6)
|
||||
{
|
||||
Printf ("No song is currently playing.\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
MusInfo *dumper = currSong->GetWaveDumper(argv[1], argv.argc() == 3 ? atoi(argv[2]) : 0);
|
||||
if (dumper == nullptr)
|
||||
{
|
||||
Printf ("Current song cannot be saved as wave data.\n");
|
||||
}
|
||||
if (!stricmp(argv[5], "WildMidi")) dev = MDEV_WILDMIDI;
|
||||
else if (!stricmp(argv[5], "GUS")) dev = MDEV_GUS;
|
||||
else if (!stricmp(argv[5], "Timidity") || !stricmp(argv[5], "Timidity++")) dev = MDEV_TIMIDITY;
|
||||
else if (!stricmp(argv[5], "FluidSynth")) dev = MDEV_FLUIDSYNTH;
|
||||
else if (!stricmp(argv[5], "OPL")) dev = MDEV_OPL;
|
||||
else
|
||||
{
|
||||
dumper->Play(false, 0); // FIXME: Remember subsong
|
||||
delete dumper;
|
||||
Printf("%s: Unknown MIDI device\n", argv[5]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// We must stop the currently playing music to avoid interference between two synths.
|
||||
auto savedsong = mus_playing;
|
||||
S_StopMusic(true);
|
||||
if (snd_mididevice >= 0) dev = MDEV_FLUIDSYNTH; // The Windows system synth cannot dump a wave.
|
||||
auto streamer = new MIDIStreamer(dev, argv.argc() < 6 ? nullptr : argv[6]);
|
||||
streamer->SetMIDISource(source);
|
||||
streamer->DumpWave(argv[2], argv.argc() <4? 0: (int)strtol(argv[3], nullptr, 10), argv.argc() <5 ? 0 : (int)strtol(argv[4], nullptr, 10));
|
||||
delete streamer;
|
||||
S_ChangeMusic(savedsong.name, savedsong.baseorder, savedsong.loop, true);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf ("Usage: writewave <filename> [sample rate]");
|
||||
Printf ("Usage: writewave <midi> <filename> [subsong] [sample rate] [synth] [soundfont]\n"
|
||||
" - use '*' as song name to dump the currently playing song\n"
|
||||
" - use 0 for subsong and sample rate to play the default\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -796,44 +848,16 @@ UNSAFE_CCMD (writewave)
|
|||
// like older versions did.
|
||||
//
|
||||
//==========================================================================
|
||||
extern MusPlayingInfo mus_playing;
|
||||
|
||||
|
||||
UNSAFE_CCMD (writemidi)
|
||||
UNSAFE_CCMD(writemidi)
|
||||
{
|
||||
if (argv.argc() != 3)
|
||||
{
|
||||
Printf("Usage: writemidi <midisong> <filename> - use '*' as song name to dump the currently playing song");
|
||||
return;
|
||||
}
|
||||
FString src = argv[1];
|
||||
if (src.Compare("*") == 0) src= mus_playing.name;
|
||||
|
||||
auto lump = Wads.CheckNumForName(src, ns_music);
|
||||
if (lump < 0) lump = Wads.CheckNumForFullName(src);
|
||||
if (lump < 0)
|
||||
{
|
||||
Printf("Cannot find MIDI lump %s.\n", src.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
FWadLump wlump = Wads.OpenLumpNum(lump);
|
||||
uint32_t id[32/4];
|
||||
|
||||
if(wlump.Read(id, 32) != 32 || wlump.Seek(-32, SEEK_CUR) != 0)
|
||||
{
|
||||
Printf("Unable to read lump %s\n", src.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
auto type = IdentifyMIDIType(id, 32);
|
||||
auto source = CreateMIDISource(wlump, type);
|
||||
|
||||
if (source == nullptr)
|
||||
{
|
||||
Printf("%s is not MIDI-based.\n", src.GetChars());
|
||||
Printf("Usage: writemidi <midisong> <filename> - use '*' as song name to dump the currently playing song\n");
|
||||
return;
|
||||
}
|
||||
auto source = GetMIDISource(argv[1]);
|
||||
if (source == nullptr) return;
|
||||
|
||||
TArray<uint8_t> midi;
|
||||
bool success;
|
||||
|
|
|
@ -87,8 +87,6 @@ public:
|
|||
|
||||
#ifdef _WIN32
|
||||
MIDIDevice *CreateWinMIDIDevice(int mididevice);
|
||||
#elif defined __APPLE__
|
||||
MIDIDevice *CreateAudioToolboxMIDIDevice();
|
||||
#endif
|
||||
MIDIDevice *CreateTimidityPPMIDIDevice(const char *args);
|
||||
void TimidityPP_Shutdown();
|
||||
|
@ -97,6 +95,7 @@ void TimidityPP_Shutdown();
|
|||
|
||||
class SoftSynthMIDIDevice : public MIDIDevice
|
||||
{
|
||||
friend class MIDIWaveWriter;
|
||||
public:
|
||||
SoftSynthMIDIDevice();
|
||||
~SoftSynthMIDIDevice();
|
||||
|
@ -132,6 +131,11 @@ protected:
|
|||
int OpenStream(int chunks, int flags, MidiCallback, void *userdata);
|
||||
static bool FillStream(SoundStream *stream, void *buff, int len, void *userdata);
|
||||
virtual bool ServiceStream (void *buff, int numbytes);
|
||||
virtual void SetSampleRate(int rate)
|
||||
{
|
||||
if (rate > 11025) { SampleRate = rate; }
|
||||
}
|
||||
int GetSampleRate() const { return SampleRate; }
|
||||
|
||||
virtual void HandleEvent(int status, int parm1, int parm2) = 0;
|
||||
virtual void HandleLongEvent(const uint8_t *data, int len) = 0;
|
||||
|
@ -148,6 +152,7 @@ public:
|
|||
void Close();
|
||||
int GetTechnology() const;
|
||||
FString GetStats();
|
||||
virtual void SetSampleRate(int rate) { } // cannot be changed.
|
||||
|
||||
protected:
|
||||
void CalcTickRate();
|
||||
|
@ -184,6 +189,7 @@ public:
|
|||
void PrecacheInstruments(const uint16_t *instruments, int count);
|
||||
FString GetStats();
|
||||
int GetDeviceType() const override { return MDEV_GUS; }
|
||||
virtual void SetSampleRate(int rate) { if (rate >= 11025 && rate < 65535) SampleRate = rate; }
|
||||
|
||||
protected:
|
||||
Timidity::Renderer *Renderer;
|
||||
|
@ -193,18 +199,35 @@ protected:
|
|||
void ComputeOutput(float *buffer, int len);
|
||||
};
|
||||
|
||||
// Internal TiMidity disk writing version of a MIDI device ------------------
|
||||
// Internal disk writing version of a MIDI device ------------------
|
||||
|
||||
class TimidityWaveWriterMIDIDevice : public TimidityMIDIDevice
|
||||
class MIDIWaveWriter : public SoftSynthMIDIDevice
|
||||
{
|
||||
public:
|
||||
TimidityWaveWriterMIDIDevice(const char *filename, int rate);
|
||||
~TimidityWaveWriterMIDIDevice();
|
||||
MIDIWaveWriter(const char *filename, MIDIDevice *devtouse, int rate);
|
||||
~MIDIWaveWriter();
|
||||
int Resume();
|
||||
int Open(MidiCallback cb, void *userdata)
|
||||
{
|
||||
return playDevice->Open(cb, userdata);
|
||||
}
|
||||
void Stop();
|
||||
void HandleEvent(int status, int parm1, int parm2) { playDevice->HandleEvent(status, parm1, parm2); }
|
||||
void HandleLongEvent(const uint8_t *data, int len) { playDevice->HandleLongEvent(data, len); }
|
||||
void ComputeOutput(float *buffer, int len) { playDevice->ComputeOutput(buffer, len); }
|
||||
int StreamOutSync(MidiHeader *data) { return playDevice->StreamOutSync(data); }
|
||||
int StreamOut(MidiHeader *data) { return playDevice->StreamOut(data); }
|
||||
int GetDeviceType() const override { return playDevice->GetDeviceType(); }
|
||||
bool ServiceStream (void *buff, int numbytes) { return playDevice->ServiceStream(buff, numbytes); }
|
||||
int GetTechnology() const { return playDevice->GetTechnology(); }
|
||||
int SetTempo(int tempo) { return playDevice->SetTempo(tempo); }
|
||||
int SetTimeDiv(int timediv) { return playDevice->SetTimeDiv(timediv); }
|
||||
bool IsOpen() const { return playDevice->IsOpen(); }
|
||||
void CalcTickRate() { playDevice->CalcTickRate(); }
|
||||
|
||||
protected:
|
||||
FileWriter *File;
|
||||
SoftSynthMIDIDevice *playDevice;
|
||||
};
|
||||
|
||||
// WildMidi implementation of a MIDI device ---------------------------------
|
||||
|
@ -219,6 +242,7 @@ public:
|
|||
void PrecacheInstruments(const uint16_t *instruments, int count);
|
||||
FString GetStats();
|
||||
int GetDeviceType() const override { return MDEV_WILDMIDI; }
|
||||
virtual void SetSampleRate(int rate) { if (rate >= 11025 && SampleRate < rate) SampleRate = rate; }
|
||||
|
||||
protected:
|
||||
WildMidi_Renderer *Renderer;
|
||||
|
@ -253,6 +277,7 @@ public:
|
|||
void FluidSettingNum(const char *setting, double value);
|
||||
void FluidSettingStr(const char *setting, const char *value);
|
||||
int GetDeviceType() const override { return MDEV_FLUIDSYNTH; }
|
||||
virtual void SetSampleRate(int rate) { if (rate >= 22050 && rate <= 96000) SampleRate = rate; }
|
||||
|
||||
protected:
|
||||
void HandleEvent(int status, int parm1, int parm2);
|
||||
|
@ -336,11 +361,9 @@ public:
|
|||
? MusInfo::GetDeviceType()
|
||||
: MIDI->GetDeviceType();
|
||||
}
|
||||
|
||||
// Must be redone later when the rest is properly rebuilt
|
||||
//MusInfo *GetOPLDumper(const char *filename);
|
||||
//MusInfo *GetWaveDumper(const char *filename, int rate);
|
||||
|
||||
bool DumpWave(const char *filename, int subsong, int samplerate);
|
||||
|
||||
|
||||
protected:
|
||||
MIDIStreamer(const char *dumpname, EMidiDevice type);
|
||||
|
@ -382,7 +405,6 @@ protected:
|
|||
EMidiDevice DeviceType;
|
||||
bool CallbackIsThreaded;
|
||||
int LoopLimit;
|
||||
FString DumpFilename;
|
||||
FString Args;
|
||||
MIDISource *source;
|
||||
|
||||
|
|
|
@ -245,10 +245,10 @@ FString TimidityMIDIDevice::GetStats()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename, int rate)
|
||||
:TimidityMIDIDevice(nullptr)
|
||||
MIDIWaveWriter::MIDIWaveWriter(const char *filename, MIDIDevice *playdevice, int rate)
|
||||
{
|
||||
File = FileWriter::Open(filename);
|
||||
playDevice = (SoftSynthMIDIDevice*) playdevice;
|
||||
if (File != nullptr)
|
||||
{ // Write wave header
|
||||
uint32_t work[3];
|
||||
|
@ -259,12 +259,16 @@ TimidityWaveWriterMIDIDevice::TimidityWaveWriterMIDIDevice(const char *filename,
|
|||
work[2] = MAKE_ID('W','A','V','E');
|
||||
if (4*3 != File->Write(work, 4 * 3)) goto fail;
|
||||
|
||||
|
||||
playDevice->SetSampleRate(rate);
|
||||
playDevice->CalcTickRate();
|
||||
rate = playDevice->GetSampleRate(); // read back what the device made of it.
|
||||
fmt.ChunkID = MAKE_ID('f','m','t',' ');
|
||||
fmt.ChunkLen = LittleLong(uint32_t(sizeof(fmt) - 8));
|
||||
fmt.FormatTag = LittleShort(0xFFFE); // WAVE_FORMAT_EXTENSIBLE
|
||||
fmt.Channels = LittleShort(2);
|
||||
fmt.SamplesPerSec = LittleLong((int)Renderer->rate);
|
||||
fmt.AvgBytesPerSec = LittleLong((int)Renderer->rate * 8);
|
||||
fmt.SamplesPerSec = LittleLong(rate);
|
||||
fmt.AvgBytesPerSec = LittleLong(rate * 8);
|
||||
fmt.BlockAlign = LittleShort(8);
|
||||
fmt.BitsPerSample = LittleShort(32);
|
||||
fmt.ExtensionSize = LittleShort(2 + 4 + 16);
|
||||
|
@ -301,7 +305,7 @@ fail:
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
TimidityWaveWriterMIDIDevice::~TimidityWaveWriterMIDIDevice()
|
||||
MIDIWaveWriter::~MIDIWaveWriter()
|
||||
{
|
||||
if (File != nullptr)
|
||||
{
|
||||
|
@ -317,7 +321,7 @@ TimidityWaveWriterMIDIDevice::~TimidityWaveWriterMIDIDevice()
|
|||
size = LittleLong(uint32_t(pos - 12 - sizeof(FmtChunk) - 8));
|
||||
if (0 == File->Seek(4 + sizeof(FmtChunk) + 4, SEEK_CUR))
|
||||
{
|
||||
if (1 == File->Write(&size, 4))
|
||||
if (4 == File->Write(&size, 4))
|
||||
{
|
||||
delete File;
|
||||
return;
|
||||
|
@ -336,7 +340,7 @@ TimidityWaveWriterMIDIDevice::~TimidityWaveWriterMIDIDevice()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
int TimidityWaveWriterMIDIDevice::Resume()
|
||||
int MIDIWaveWriter::Resume()
|
||||
{
|
||||
float writebuffer[4096];
|
||||
|
||||
|
@ -357,6 +361,6 @@ int TimidityWaveWriterMIDIDevice::Resume()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void TimidityWaveWriterMIDIDevice::Stop()
|
||||
void MIDIWaveWriter::Stop()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -68,6 +68,10 @@ public:
|
|||
if (instruments != nullptr) delete instruments;
|
||||
instruments = nullptr;
|
||||
}
|
||||
virtual void SetSampleRate(int rate)
|
||||
{
|
||||
if (rate >= 4000 && SampleRate < rate) SampleRate = rate;
|
||||
}
|
||||
|
||||
double test[3] = { 0, 0, 0 };
|
||||
|
||||
|
|
|
@ -82,19 +82,6 @@ MIDIStreamer::MIDIStreamer(EMidiDevice type, const char *args)
|
|||
memset(Buffer, 0, sizeof(Buffer));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer OPL Dumping Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
MIDIStreamer::MIDIStreamer(const char *dumpname, EMidiDevice type)
|
||||
:
|
||||
MIDI(0), DeviceType(type), DumpFilename(dumpname)
|
||||
{
|
||||
memset(Buffer, 0, sizeof(Buffer));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer Destructor
|
||||
|
@ -254,28 +241,18 @@ void MIDIStreamer::Play(bool looping, int subsong)
|
|||
if (source == nullptr) return; // We have nothing to play so abort.
|
||||
|
||||
assert(MIDI == NULL);
|
||||
|
||||
// fixme: The device should be attached by the controlling code to allow more flexibility.
|
||||
// It should also separate the softsynth device from the playback engines.
|
||||
// The approach here pretty much prevents the implementation of a generic WAVE writer because no generic dumper device class can be created.
|
||||
devtype = SelectMIDIDevice(DeviceType);
|
||||
|
||||
/*
|
||||
if (DumpFilename.IsNotEmpty())
|
||||
{
|
||||
if (devtype == MDEV_OPL)
|
||||
if (DeviceType == MDEV_OPLDUMP)
|
||||
{
|
||||
MIDI = new OPLDumperMIDIDevice(DumpFilename);
|
||||
}
|
||||
else if (devtype == MDEV_GUS)
|
||||
{
|
||||
MIDI = new TimidityWaveWriterMIDIDevice(DumpFilename, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MIDI = CreateMIDIDevice(devtype);
|
||||
}
|
||||
|
||||
*/
|
||||
MIDI = CreateMIDIDevice(devtype);
|
||||
|
||||
if (MIDI == NULL || 0 != MIDI->Open(Callback, this))
|
||||
{
|
||||
Printf(PRINT_BOLD, "Could not open MIDI out device\n");
|
||||
|
@ -366,6 +343,58 @@ void MIDIStreamer::StartPlayback()
|
|||
while (BufferNum != 0);
|
||||
}
|
||||
|
||||
bool MIDIStreamer::DumpWave(const char *filename, int subsong, int samplerate)
|
||||
{
|
||||
EMidiDevice devtype;
|
||||
|
||||
m_Status = STATE_Stopped;
|
||||
m_Looping = false;
|
||||
EndQueued = 0;
|
||||
VolumeChanged = false;
|
||||
Restarting = true;
|
||||
InitialPlayback = true;
|
||||
if (source == nullptr) return false; // We have nothing to play so abort.
|
||||
|
||||
assert(MIDI == NULL);
|
||||
devtype = SelectMIDIDevice(DeviceType);
|
||||
MIDI = CreateMIDIDevice(devtype);
|
||||
MIDI = new MIDIWaveWriter(filename, MIDI, samplerate);
|
||||
|
||||
if (MIDI == NULL || 0 != MIDI->Open(Callback, this))
|
||||
{
|
||||
Printf(PRINT_BOLD, "Could not open MIDI out device\n");
|
||||
if (MIDI != NULL)
|
||||
{
|
||||
delete MIDI;
|
||||
MIDI = NULL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
source->SetMIDISubsong(subsong);
|
||||
source->CheckCaps(MIDI->GetTechnology());
|
||||
|
||||
if (MIDI->Preprocess(this, false))
|
||||
{
|
||||
StartPlayback();
|
||||
if (MIDI == NULL)
|
||||
{ // The MIDI file had no content and has been automatically closed.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (0 != MIDI->Resume())
|
||||
{
|
||||
Printf("Starting MIDI playback failed\n");
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// MIDIStreamer :: Pause
|
||||
|
|
|
@ -89,7 +89,7 @@ static bool opt_eq_control = false;
|
|||
static bool op_nrpn_vibrato = true;
|
||||
static bool opt_tva_attack = false;
|
||||
static bool opt_tva_decay = false;
|
||||
static bool ppt_tva_release = false;
|
||||
static bool opt_tva_release = false;
|
||||
static bool opt_insertion_effect = false;
|
||||
static bool opt_delay_control = false;
|
||||
|
||||
|
|
Loading…
Reference in a new issue