Added event wait timeout when idle.

Added show menu button to prevent lockout; this somewhat ruins no menu aesthetic.
Cleaned up 3DPB vs. FT methods in midi.
This commit is contained in:
Muzychenko Andrey 2021-09-29 17:53:49 +03:00
parent ba5a0f3044
commit 03deda2f9d
5 changed files with 138 additions and 151 deletions

View File

@ -5,7 +5,10 @@
#include "pb.h" #include "pb.h"
#include "pinball.h" #include "pinball.h"
Mix_Music* midi::currentMidi;
objlist_class<Mix_Music>* midi::LoadedTracks;
Mix_Music *midi::track1, *midi::track2, *midi::track3, *midi::active_track, *midi::NextTrack;
bool midi::SetNextTrackFlag;
constexpr uint32_t FOURCC(uint8_t a, uint8_t b, uint8_t c, uint8_t d) constexpr uint32_t FOURCC(uint8_t a, uint8_t b, uint8_t c, uint8_t d)
{ {
@ -27,160 +30,127 @@ int ToVariableLen(uint32_t value, uint32_t& dst)
return count; return count;
} }
int midi::play_pb_theme(int flag) int midi::play_pb_theme()
{ {
if (pb::FullTiltMode) // Todo: add support for tracks 2 and 3
{ return play_track(track1);
return play_ft(track1);
}
int result = 0;
music_stop();
if (currentMidi)
result = Mix_PlayMusic(currentMidi, -1);
return result;
} }
int midi::music_stop() int midi::music_stop()
{ {
if (pb::FullTiltMode) if (active_track)
{ {
return stop_ft(); active_track = nullptr;
Mix_HaltMusic();
} }
return Mix_HaltMusic(); return true;
} }
int midi::music_init() int midi::music_init()
{
if (pb::FullTiltMode)
{
return music_init_ft();
}
// File name is in lower case, while game data is in upper case.
std::string fileName = pinball::get_rc_string(156, 0);
std::transform(fileName.begin(), fileName.end(), fileName.begin(), [](unsigned char c) { return std::toupper(c); });
auto midiPath = pinball::make_path_name(fileName);
currentMidi = Mix_LoadMUS(midiPath.c_str());
return currentMidi != nullptr;
}
void midi::music_shutdown()
{
if (pb::FullTiltMode)
{
music_shutdown_ft();
return;
}
Mix_FreeMusic(currentMidi);
}
objlist_class<Mix_Music>* midi::TrackList;
Mix_Music *midi::track1, *midi::track2, *midi::track3, *midi::active_track, *midi::active_track2;
int midi::some_flag1;
int midi::music_init_ft()
{ {
active_track = nullptr; active_track = nullptr;
TrackList = new objlist_class<Mix_Music>(0, 1); LoadedTracks = new objlist_class<Mix_Music>(0, 1);
if (pb::FullTiltMode)
{
track1 = load_track("TABA1.MDS", true);
track2 = load_track("TABA2.MDS", true);
track3 = load_track("TABA3.MDS", true);
}
else
{
// 3DPB has only one music track. PINBALL2.MID is a bitmap font, in the same format as PB_MSGFT.bin
track1 = load_track("PINBALL.MID", false);
}
track1 = load_track("taba1");
track2 = load_track("taba2");
track3 = load_track("taba3");
if (!track2) if (!track2)
track2 = track1; track2 = track1;
if (!track3) if (!track3)
track3 = track1; track3 = track1;
return 1; return track1 != nullptr;
} }
void midi::music_shutdown_ft() void midi::music_shutdown()
{ {
if (active_track) if (active_track)
Mix_HaltMusic(); Mix_HaltMusic();
while (TrackList->GetCount())
while (LoadedTracks->GetCount())
{ {
auto midi = TrackList->Get(0); auto midi = LoadedTracks->Get(0);
Mix_FreeMusic(midi); Mix_FreeMusic(midi);
TrackList->Delete(midi); LoadedTracks->Delete(midi);
} }
active_track = nullptr; active_track = nullptr;
delete TrackList; delete LoadedTracks;
} }
Mix_Music* midi::load_track(std::string fileName) Mix_Music* midi::load_track(std::string fileName, bool isMds)
{ {
Mix_Music* audio;
auto origFile = fileName; auto origFile = fileName;
// File name is in lower case, while game data is in upper case.
std::transform(fileName.begin(), fileName.end(), fileName.begin(), [](unsigned char c) { return std::toupper(c); });
if (pb::FullTiltMode) if (pb::FullTiltMode)
{ {
// FT sounds are in SOUND subfolder // FT sounds are in SOUND subfolder
fileName.insert(0, 1, PathSeparator); fileName.insert(0, 1, PathSeparator);
fileName.insert(0, "SOUND"); fileName.insert(0, "SOUND");
} }
fileName += ".MDS";
auto filePath = pinball::make_path_name(fileName); auto filePath = pinball::make_path_name(fileName);
auto midi = MdsToMidi(filePath); if (isMds)
if (!midi)
return nullptr;
// Dump converted MIDI file
/*origFile += ".midi";
FILE* fileHandle = fopen(origFile.c_str(), "wb");
fwrite(midi->data(), 1, midi->size(), fileHandle);
fclose(fileHandle);*/
auto rw = SDL_RWFromMem(midi->data(), static_cast<int>(midi->size()));
auto audio = Mix_LoadMUS_RW(rw, 1); // This call seems to leak memory no matter what.
delete midi;
if (!audio)
return nullptr;
TrackList->Add(audio);
return audio;
}
int midi::play_ft(Mix_Music* midi)
{
int result;
stop_ft();
if (!midi)
return 0;
if (some_flag1)
{ {
active_track2 = midi; auto midi = MdsToMidi(filePath);
return 0; if (!midi)
} return nullptr;
if (Mix_PlayMusic(midi, -1))
{ // Dump converted MIDI file
active_track = nullptr; /*origFile += ".midi";
result = 0; FILE* fileHandle = fopen(origFile.c_str(), "wb");
fwrite(midi->data(), 1, midi->size(), fileHandle);
fclose(fileHandle);*/
auto rw = SDL_RWFromMem(midi->data(), static_cast<int>(midi->size()));
audio = Mix_LoadMUS_RW(rw, 1); // This call seems to leak memory no matter what.
delete midi;
} }
else else
{ {
active_track = midi; audio = Mix_LoadMUS(filePath.c_str());
result = 1;
} }
return result;
if (!audio)
return nullptr;
LoadedTracks->Add(audio);
return audio;
} }
int midi::stop_ft() bool midi::play_track(Mix_Music* midi)
{ {
int returnCode = 0; music_stop();
if (active_track) if (!midi)
returnCode = Mix_HaltMusic(); return false;
active_track = nullptr;
return returnCode; if (SetNextTrackFlag)
{
NextTrack = midi;
SetNextTrackFlag = false;
return true;
}
if (Mix_PlayMusic(midi, -1))
{
active_track = nullptr;
return false;
}
active_track = midi;
return true;
} }
/// <summary> /// <summary>
/// SDL_mixed does not support MIDS. To support FT music, a conversion to MIDI is required. /// SDL_mixed does not support MIDS. To support FT music, a conversion to MIDI is required.
/// </summary> /// </summary>
@ -194,9 +164,9 @@ std::vector<uint8_t>* midi::MdsToMidi(std::string file)
fseek(fileHandle, 0, SEEK_END); fseek(fileHandle, 0, SEEK_END);
auto fileSize = static_cast<uint32_t>(ftell(fileHandle)); auto fileSize = static_cast<uint32_t>(ftell(fileHandle));
auto filePtr = reinterpret_cast<riff_header*>(memory::allocate(fileSize)); auto fileBuf = reinterpret_cast<riff_header*>(new uint8_t [fileSize]);
fseek(fileHandle, 0, SEEK_SET); fseek(fileHandle, 0, SEEK_SET);
fread(filePtr, 1, fileSize, fileHandle); fread(fileBuf, 1, fileSize, fileHandle);
fclose(fileHandle); fclose(fileHandle);
int returnCode = 0; int returnCode = 0;
@ -208,14 +178,14 @@ std::vector<uint8_t>* midi::MdsToMidi(std::string file)
returnCode = 3; returnCode = 3;
break; break;
} }
if (filePtr->Riff != FOURCC('R', 'I', 'F', 'F') || if (fileBuf->Riff != FOURCC('R', 'I', 'F', 'F') ||
filePtr->Mids != FOURCC('M', 'I', 'D', 'S') || fileBuf->Mids != FOURCC('M', 'I', 'D', 'S') ||
filePtr->Fmt != FOURCC('f', 'm', 't', ' ')) fileBuf->Fmt != FOURCC('f', 'm', 't', ' '))
{ {
returnCode = 3; returnCode = 3;
break; break;
} }
if (filePtr->FileSize > fileSize - 8) if (fileBuf->FileSize > fileSize - 8)
{ {
returnCode = 3; returnCode = 3;
break; break;
@ -225,14 +195,14 @@ std::vector<uint8_t>* midi::MdsToMidi(std::string file)
returnCode = 3; returnCode = 3;
break; break;
} }
if (filePtr->FmtSize < 12 || filePtr->FmtSize > fileSize - 12) if (fileBuf->FmtSize < 12 || fileBuf->FmtSize > fileSize - 12)
{ {
returnCode = 3; returnCode = 3;
break; break;
} }
auto streamIdUsed = filePtr->dwFlags == 0; auto streamIdUsed = fileBuf->dwFlags == 0;
auto dataChunk = reinterpret_cast<riff_data*>(reinterpret_cast<char*>(&filePtr->dwTimeFormat) + filePtr-> auto dataChunk = reinterpret_cast<riff_data*>(reinterpret_cast<char*>(&fileBuf->dwTimeFormat) + fileBuf->
FmtSize); FmtSize);
if (dataChunk->Data != FOURCC('d', 'a', 't', 'a')) if (dataChunk->Data != FOURCC('d', 'a', 't', 'a'))
{ {
@ -274,7 +244,7 @@ std::vector<uint8_t>* midi::MdsToMidi(std::string file)
// MThd chunk // MThd chunk
std::vector<uint8_t>& midiBytes = *new std::vector<uint8_t>(); std::vector<uint8_t>& midiBytes = *new std::vector<uint8_t>();
midiOut = &midiBytes; midiOut = &midiBytes;
midi_header header(SwapByteOrderShort(static_cast<uint16_t>(filePtr->dwTimeFormat))); midi_header header(SwapByteOrderShort(static_cast<uint16_t>(fileBuf->dwTimeFormat)));
auto headerData = reinterpret_cast<const uint8_t*>(&header); auto headerData = reinterpret_cast<const uint8_t*>(&header);
midiBytes.insert(midiBytes.end(), headerData, headerData + sizeof header); midiBytes.insert(midiBytes.end(), headerData, headerData + sizeof header);
@ -339,8 +309,7 @@ std::vector<uint8_t>* midi::MdsToMidi(std::string file)
} }
while (false); while (false);
if (filePtr) delete[] fileBuf;
memory::free(filePtr);
if (returnCode && midiOut) if (returnCode && midiOut)
delete midiOut; delete midiOut;
return midiOut; return midiOut;

View File

@ -57,7 +57,7 @@ struct midi_header
{ {
} }
const char MThd[4]{ 'M','T','h','d' }; const char MThd[4]{'M', 'T', 'h', 'd'};
const uint32_t chunklen = SwapByteOrderInt(6); const uint32_t chunklen = SwapByteOrderInt(6);
const int16_t format = SwapByteOrderShort(0); const int16_t format = SwapByteOrderShort(0);
const uint16_t ntracks = SwapByteOrderShort(1); const uint16_t ntracks = SwapByteOrderShort(1);
@ -71,7 +71,7 @@ struct midi_track
{ {
} }
const char MTrk[4]{ 'M','T','r','k' }; const char MTrk[4]{'M', 'T', 'r', 'k'};
uint32_t chunklen; uint32_t chunklen;
}; };
@ -87,20 +87,15 @@ static_assert(sizeof(midi_track) == 8, "Wrong size of midi_track");
class midi class midi
{ {
public: public:
static int play_pb_theme(int flag); static int play_pb_theme();
static int music_stop(); static int music_stop();
static int music_init(); static int music_init();
static void music_shutdown(); static void music_shutdown();
private: private:
static Mix_Music* currentMidi; static objlist_class<Mix_Music>* LoadedTracks;
static Mix_Music *track1, *track2, *track3, *active_track, *NextTrack;
static objlist_class<Mix_Music>* TrackList; static bool SetNextTrackFlag;
static Mix_Music *track1, *track2, *track3, *active_track, *active_track2; static Mix_Music* load_track(std::string fileName, bool isMds);
static int some_flag1; static bool play_track(Mix_Music* midi);
static int music_init_ft();
static void music_shutdown_ft();
static Mix_Music* load_track(std::string fileName);
static int play_ft(Mix_Music* midi);
static int stop_ft();
static std::vector<uint8_t>* MdsToMidi(std::string file); static std::vector<uint8_t>* MdsToMidi(std::string file);
}; };

View File

@ -188,7 +188,7 @@ void options::toggle(Menu1 uIDCheckItem)
if (!newValue) if (!newValue)
midi::music_stop(); midi::music_stop();
else else
midi::play_pb_theme(0); midi::play_pb_theme();
return; return;
case Menu1::Show_Menu: case Menu1::Show_Menu:
Options.ShowMenu = Options.ShowMenu == 0; Options.ShowMenu = Options.ShowMenu == 0;

View File

@ -202,7 +202,7 @@ void pb::replay_level(int demoMode)
demo_mode = demoMode; demo_mode = demoMode;
mode_change(1); mode_change(1);
if (options::Options.Music) if (options::Options.Music)
midi::play_pb_theme(0); midi::play_pb_theme();
MainTable->Message(1014, static_cast<float>(options::Options.Players)); MainTable->Message(1014, static_cast<float>(options::Options.Players));
} }
@ -350,7 +350,7 @@ void pb::pause_continue()
pinball::InfoTextBox->Display(text, textTime); pinball::InfoTextBox->Display(text, textTime);
} }
if (options::Options.Music && !winmain::single_step) if (options::Options.Music && !winmain::single_step)
midi::play_pb_theme(0); midi::play_pb_theme();
Sound::Activate(); Sound::Activate();
} }
} }

View File

@ -243,28 +243,19 @@ int winmain::WinMain(LPCSTR lpCmdLine)
if (UpdateToFrameCounter >= UpdateToFrameRatio) if (UpdateToFrameCounter >= UpdateToFrameRatio)
{ {
UpdateToFrameCounter -= UpdateToFrameRatio; ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
// Option might be changed in RenderUi, unpairing NewFrame from EndFrame. RenderUi();
auto showMenu = options::Options.ShowMenu;
if (showMenu)
{
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
RenderUi();
}
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
render::PresentVScreen(); render::PresentVScreen();
if (showMenu) ImGui::Render();
{ ImGuiSDL::Render(ImGui::GetDrawData());
ImGui::Render();
ImGuiSDL::Render(ImGui::GetDrawData());
}
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);
frameCounter++; frameCounter++;
UpdateToFrameCounter -= UpdateToFrameRatio;
} }
auto sdlError = SDL_GetError(); auto sdlError = SDL_GetError();
@ -315,6 +306,30 @@ int winmain::WinMain(LPCSTR lpCmdLine)
void winmain::RenderUi() void winmain::RenderUi()
{ {
// A minimal window with a button to prevent menu lockout.
if (!options::Options.ShowMenu)
{
ImGui::SetNextWindowPos(ImVec2{});
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2{10, 0});
if (ImGui::Begin("main", nullptr,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBackground |
ImGuiWindowFlags_AlwaysAutoResize |
ImGuiWindowFlags_NoMove))
{
ImGui::PushID(1);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4{});
if (ImGui::Button("Menu"))
{
options::toggle(Menu1::Show_Menu);
}
ImGui::PopStyleColor(1);
ImGui::PopID();
ImGui::End();
}
ImGui::PopStyleVar();
return;
}
// No demo window in release to save space // No demo window in release to save space
#ifndef NDEBUG #ifndef NDEBUG
if (ShowImGuiDemo) if (ShowImGuiDemo)
@ -675,7 +690,7 @@ int winmain::event_handler(const SDL_Event* event)
activated = 1; activated = 1;
Sound::Activate(); Sound::Activate();
if (options::Options.Music && !single_step) if (options::Options.Music && !single_step)
midi::play_pb_theme(0); midi::play_pb_theme();
no_time_loss = 1; no_time_loss = 1;
has_focus = 1; has_focus = 1;
break; break;
@ -704,9 +719,11 @@ int winmain::event_handler(const SDL_Event* event)
int winmain::ProcessWindowMessages() int winmain::ProcessWindowMessages()
{ {
static auto idleWait = 0;
SDL_Event event; SDL_Event event;
if (has_focus && !single_step) if (has_focus && !single_step)
{ {
idleWait = static_cast<int>(TargetFrameTime.count());
while (SDL_PollEvent(&event)) while (SDL_PollEvent(&event))
{ {
if (!event_handler(&event)) if (!event_handler(&event))
@ -716,8 +733,14 @@ int winmain::ProcessWindowMessages()
return 1; return 1;
} }
SDL_WaitEvent(&event); // Progressively wait longer when transitioning to idle
return event_handler(&event); idleWait = std::min(idleWait + static_cast<int>(TargetFrameTime.count()), 500);
if (SDL_WaitEventTimeout(&event, idleWait))
{
idleWait = static_cast<int>(TargetFrameTime.count());
return event_handler(&event);
}
return 1;
} }
void winmain::memalloc_failure() void winmain::memalloc_failure()