Skip to content

Conversation

@nyanpasu64
Copy link
Collaborator

@nyanpasu64 nyanpasu64 commented Jul 26, 2025

This pull request aims to fix random crashes the first time you export a WAV file from FamiTracker.

bool CSoundGen::m_bStoppingRender failed to be initialized. If it happened to be nonzero, (audio) CSoundGen::OnIdle() would count down m_iDelayedEnd to 0 and then call StopRendering() on every frame, which would fail because IsRendering() is false. (This error was silent on release builds before #353, but now dumps a log and blocks the audio thread with a dialog.)

The crash happens when the user initiates a render on the GUI thread (setting m_pWaveFile and m_bRequestRenderStart). While the audio thread is looping, OnIdle() → StopRendering() could succeed and erase m_pWaveFile before (audio) CSoundGen::DispatchGuiMessage() processes WM_USER_START_RENDER from the GUI. Attempting to render without m_pWaveFile would crash.

Fix this by initializing CSoundGen::m_bStoppingRender, as well as other flags that the code doesn't access immediately but I don't want to leave uninitialized. I didn't initialize everything because I don't understand the existing code.

Crash log:

(audio)
(m_bStoppingRender) { CSoundGen::StopRendering
    ASSERT(m_bRendering) failed
} !IsRendering(), CSoundGen::StopRendering
repeats...

(m_bStoppingRender) { CSoundGen::StopRendering
    ASSERT(m_bRendering) failed
    ...

(gui)
{ CSoundGen::RenderToFile
    m_pWaveFile = std::make_unique<CWaveFile>();
    m_bRequestRenderStart = true;
    CSoundGen::GuiPostMessage(WM_USER_START_RENDER)
} CSoundGen::RenderToFile

(audio)
    m_pWaveFile.reset();
} CSoundGen::StopRendering
(WM_USER_START_RENDER:)
{ CSoundGen::OnStartRender
    {} Lock()
    m_bRendering = true;
} CSoundGen::OnStartRender
{ CSoundGen::FillBuffer
    if (m_bRendering) {
        ASSERT(m_pWaveFile) failed
        (crash)

This pull request improves upon and supercedes #272.


Also update the logging infrastructure to "Don't hold mutex during WAV export error dialog".

  • Do we keep the WAV logging infrastructure indefinitely, or remove it once people stop hitting crashes in the wild?

Changes in this PR:

m_bStoppingRender was never initialized. If it happened to be nonzero,
(audio) OnIdle() would count down m_iDelayedEnd to 0 and then call
StopRendering() on every frame, which would fail because IsRendering()
is false.

When you initiated a render on the GUI thread (setting m_pWaveFile and
m_bRequestRenderStart), (audio) OnIdle() -> StopRendering() could
succeed and erase m_pWaveFile before (audio)
CSoundGen::DispatchGuiMessage() processes WM_USER_START_RENDER from the
GUI. Attempting to render without m_pWaveFile would crash.

Fix this by initializing m_bStoppingRender, as well as other flags that
the code doesn't access immediately but I don't want to leave
uninitialized. I didn't initialize everything because I don't
understand the existing code.
This prevents deadlocks while one thread's Log::dump() is holding the
logger lock, and changes how dump() affects thread interleaving
(releasing the mutex with the dialog open can prevent starvation).
@Gumball2415
Copy link
Collaborator

Do we keep the WAV logging infrastructure indefinitely, or remove it once people stop hitting crashes in the wild?

i think a crash logger in general would definitely help in pinpointing some blind spots regarding the concurrency and state issues of FT, so i'd keep it for now

@Gumball2415
Copy link
Collaborator

lgtm, will be merging soon

@Gumball2415 Gumball2415 merged commit 071f3da into main Jul 30, 2025
9 checks passed
@nyanpasu64 nyanpasu64 deleted the fix-wav-export-crash-2 branch July 30, 2025 14:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Program crashes frequently when exporting .wav files

3 participants