From c97c30cea1214e81cc365eb221a29972f6a3639d Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Mon, 3 Nov 2025 18:02:36 +0000 Subject: [PATCH 1/9] ProjectView render item Add view item Pass render mode to MixerService on ProjectView::Update --- sources/Application/Mixer/MixerService.cpp | 47 +++++++++++++--------- sources/Application/Mixer/MixerService.h | 13 +++--- sources/Application/Model/Project.cpp | 8 ++++ sources/Application/Model/Project.h | 12 +++--- sources/Application/Views/ProjectView.cpp | 10 ++++- 5 files changed, 58 insertions(+), 32 deletions(-) diff --git a/sources/Application/Mixer/MixerService.cpp b/sources/Application/Mixer/MixerService.cpp index abd6c722..6e9fc14d 100644 --- a/sources/Application/Mixer/MixerService.cpp +++ b/sources/Application/Mixer/MixerService.cpp @@ -1,11 +1,12 @@ #include "MixerService.h" +#include "Application/Audio/DummyAudioOut.h" +#include "Application/Model/Config.h" +#include "Application/Model/Mixer.h" +#include "Application/Model/Project.h" #include "Services/Audio/Audio.h" #include "Services/Audio/AudioDriver.h" #include "Services/Midi/MidiService.h" #include "System/Console/Trace.h" -#include "Application/Model/Config.h" -#include "Application/Audio/DummyAudioOut.h" -#include "Application/Model/Mixer.h" MixerService::MixerService(): out_(0), @@ -60,23 +61,8 @@ bool MixerService::Init() { out_->Insert(master_); } - switch(mode_) { - case MSM_AUDIO: - break ; - case MSM_FILERT: - case MSM_FILE: - out_->SetFileRenderer("project:mixdown.wav"); - break; - case MSM_FILESPLITRT: - case MSM_FILESPLIT: - for (int i=0;iAddObserver(*MidiService::GetInstance()); + initRendering(mode_); + out_->AddObserver(*MidiService::GetInstance()); } sync_=SDL_CreateMutex(); @@ -90,6 +76,25 @@ bool MixerService::Init() { return (result); }; +void MixerService::initRendering(MixerServiceMode mode) { + switch(mode) { + case MSM_AUDIO: + break; + case MSM_FILERT: + case MSM_FILE: + out_->SetFileRenderer("project:mixdown.wav"); + break; + case MSM_FILESPLITRT: + case MSM_FILESPLIT: + for (int i = 0; i < SONG_CHANNEL_COUNT; i++) { + char buffer[1024]; + sprintf(buffer, "project:channel%d.wav", i); + bus_[i].SetFileRenderer(buffer); + } + break; + } +} + void MixerService::Close() { if (out_) { out_->RemoveObserver(*MidiService::GetInstance()); @@ -121,6 +126,8 @@ void MixerService::Close() { sync_=0 ; } ; +void MixerService::SetRenderMode(int mode) { mode_ = MixerServiceMode(mode); } + bool MixerService::Start() { MidiService::GetInstance()->Start() ; if (out_) { diff --git a/sources/Application/Mixer/MixerService.h b/sources/Application/Mixer/MixerService.h index 8a7f6a5f..8e7c31e3 100644 --- a/sources/Application/Mixer/MixerService.h +++ b/sources/Application/Mixer/MixerService.h @@ -51,6 +51,7 @@ class MixerService: void SetPregain(int); void SetSoftclip(int, int); void SetMasterVolume(int); + void SetRenderMode(int); int GetPlayedBufferPercentage() ; virtual void Execute(FourCC id,float value) ; @@ -63,11 +64,11 @@ class MixerService: protected: void toggleRendering(bool enable) ; private: - AudioOut *out_ ; - MixBus master_ ; - MixBus bus_[MAX_BUS_COUNT] ; - MixerServiceMode mode_ ; - SDL_mutex *sync_ ; - + void initRendering(MixerServiceMode); + AudioOut *out_; + MixBus master_; + MixBus bus_[MAX_BUS_COUNT]; + MixerServiceMode mode_; + SDL_mutex *sync_; } ; #endif diff --git a/sources/Application/Model/Project.cpp b/sources/Application/Model/Project.cpp index 784a21fb..9dead77e 100644 --- a/sources/Application/Model/Project.cpp +++ b/sources/Application/Model/Project.cpp @@ -41,6 +41,8 @@ tempoNudge_(0) new Variable("scale", VAR_SCALE, scaleNames, scaleCount, 0); this->Insert(scale); scale->SetInt(0); + Variable *render = new Variable("render", VAR_RENDER, MAX_RENDER_MODE, 0); + this->Insert(render); // Reload the midi device list @@ -110,6 +112,12 @@ int Project::GetPregain() { return v->GetInt(); } +int Project::GetRenderMode() { + Variable *v = FindVariable(VAR_RENDER); + NAssert(v); + return v->GetInt(); +} + void Project::NudgeTempo(int value) { if((GetTempo() + tempoNudge_) > 0) tempoNudge_ += value; diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index f4bb4410..fa1aeb59 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -17,10 +17,11 @@ #define VAR_SOFTCLIP_GAIN MAKE_FOURCC('S', 'F', 'G', 'N') #define VAR_PREGAIN MAKE_FOURCC('P', 'R', 'G', 'N') #define VAR_SCALE MAKE_FOURCC('S', 'C', 'A', 'L') +#define VAR_RENDER MAKE_FOURCC('R', 'N', 'D', 'R') #define PROJECT_NUMBER "1" -#define PROJECT_RELEASE "5" -#define BUILD_COUNT "0-bacon3" +#define PROJECT_RELEASE "6" +#define BUILD_COUNT "0-bacon1" #define MAX_TAP 3 @@ -43,7 +44,7 @@ class Project: public Persistent,public VariableContainer,I_Observer { int GetSoftclip(); int GetSoftclipGain(); int GetPregain(); - + int GetRenderMode(); void Trigger(); // I_Observer @@ -54,9 +55,10 @@ class Project: public Persistent,public VariableContainer,I_Observer { virtual void RestoreContent(TiXmlElement *element); void LoadFirstGen(const char *root) ; + const unsigned int MAX_RENDER_MODE = 2; -protected: - void buildMidiDeviceList() ; + protected: + void buildMidiDeviceList() ; private: InstrumentBank *instrumentBank_ ; char **midiDeviceList_ ; diff --git a/sources/Application/Views/ProjectView.cpp b/sources/Application/Views/ProjectView.cpp index 2afe9516..154729fe 100644 --- a/sources/Application/Views/ProjectView.cpp +++ b/sources/Application/Views/ProjectView.cpp @@ -1,4 +1,5 @@ #include "ProjectView.h" +#include "Application/Mixer/MixerService.h" #include "Application/Model/Scale.h" #include "Application/Persistency/PersistencyService.h" #include "Application/Views/ModalDialogs/MessageBox.h" @@ -180,6 +181,12 @@ ProjectView::ProjectView(GUIWindow &w,ViewData *data):FieldView(w,data) { MidiService::GetInstance()->Size(), 1, 1); T_SimpleList::Insert(field); + v = project_->FindVariable(VAR_RENDER); + NAssert(v); + position._y += 2; + field = new UIIntVarField(position, *v, "Render: %s", 0, project_->MAX_RENDER_MODE, 1, 2); + T_SimpleList::Insert(field); + position._y += 2; a1 = new UIActionField("Exit", ACTION_QUIT, position); a1->AddObserver(*this); @@ -243,7 +250,8 @@ void ProjectView::Update(Observable &,I_ObservableData *data) { int fourcc=(unsigned int)data ; #endif - UIField *focus=GetFocus() ; + MixerService::GetInstance()->SetRenderMode(project_->GetRenderMode()); + UIField *focus=GetFocus() ; if (fourcc!=ACTION_TEMPO_CHANGED) { focus->ClearFocus() ; focus->Draw(w_) ; From 8340f63f2771e28b5d38887c5d2bb8c75d946599 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Fri, 7 Nov 2025 22:21:24 +0000 Subject: [PATCH 2/9] Add notifications Notify users when entering and exiting recording Remove old config-based recording and references in documentation. [bug] Inconsistency when moving from projectview to songview with recording armed Notification will show wrong message [bug] Not disabling recording when exiting causes audio and timing to flip out Track will play back silently at many times the speed. --- docs/LittlePiggyTrackerConf.md | 13 --- docs/wiki/What-is-LittlePiggyTracker.md | 17 +-- docs/wiki/config_xml.md | 10 -- docs/wiki/ubuntu_install.md | 1 - sources/Application/Mixer/MixerService.cpp | 115 ++++++++------------- sources/Application/Mixer/MixerService.h | 16 ++- sources/Application/Model/Project.cpp | 5 +- sources/Application/Model/Project.h | 2 +- sources/Application/Model/ProjectDatas.h | 8 +- sources/Application/Views/ProjectView.cpp | 40 +++++-- sources/Application/Views/ProjectView.h | 3 +- sources/Application/Views/SongView.cpp | 12 +++ sources/Application/Views/SongView.h | 4 +- 13 files changed, 110 insertions(+), 136 deletions(-) diff --git a/docs/LittlePiggyTrackerConf.md b/docs/LittlePiggyTrackerConf.md index 6f6acfd1..cb6bd4aa 100644 --- a/docs/LittlePiggyTrackerConf.md +++ b/docs/LittlePiggyTrackerConf.md @@ -13,7 +13,6 @@ 06. [Key and Button Mapping](#key-and-button-mapping) 07. [Auto Repeat](#auto-repeat) 08. [Path](#path) -09. [Rendering](#rendering) 10. [Volume](#volume) 11. [Audio Configuration](#audio-configuration) 12. [MIDI Configuration](#midi-configuration) @@ -246,18 +245,6 @@ You can tweak two different path: ``` -## Rendering - -Additionally to playing the song, LittleGPTracker can be used to render the audio to file. To control file rendering, the variable `RENDER` can be set to either `FILE`,`FILESPLIT`,`FILERT`,`FILESPLITRT`. Note that there's a small issue with the speed when using `FILE`/`FILESPLIT` so the xxRT seem like the best choice at the moment -The xxRT options render in real time -The xSPLITx options render separate files for the channels (stems) - -```xml - - - -``` - ## Volume For \[**GP2X**/**Dingoo**\] only diff --git a/docs/wiki/What-is-LittlePiggyTracker.md b/docs/wiki/What-is-LittlePiggyTracker.md index 13641f55..b32412c2 100644 --- a/docs/wiki/What-is-LittlePiggyTracker.md +++ b/docs/wiki/What-is-LittlePiggyTracker.md @@ -581,20 +581,11 @@ RTRG 0101: does not do anything because after looping one tick, you move forward # Rendering Some people exploit the analog gap between their device's headphone output and whatever they are recording with. Alternately, you can start piggy in rendering mode so it will output 16bit, 44100Hz .WAV files. -Please note that RENDER mode is not intended to be functional on the GP2X Builds. -The following values can set for RENDER in the config.xml: +The following values can set for RENDER in the project view -- Standard mode: audio is played; no render. -- FILE: File rendering: Full speed (no audio) rendering of the stereo mixdown. -- FILESPLIT: File split rendering: Full speed (no audio) rendering of each channel separately. -- FILERT: Real Time file rendering: Renders the mixdown to file WHILE playing audio. This allow to render live mode tweaks directly. -- FILESPLITRT: Real Time file split: same except all channels are rendered separately. - -Here is an example of the proper XML syntax: (See [The config.xml setup guide](../LittlePiggyTrackerConf.md)) - -```xml - -``` +- Off: audio is played; no render. +- Stereo: Real Time file rendering: Renders the mixdown to file WHILE playing audio. This allow to render live mode tweaks directly. +- Stems: Real Time file split: same except all channels are rendered separately. Remember, any of the config.xml parameters can be specified to lgpt on the command line in this fashion: diff --git a/docs/wiki/config_xml.md b/docs/wiki/config_xml.md index 3687abba..b910ed38 100644 --- a/docs/wiki/config_xml.md +++ b/docs/wiki/config_xml.md @@ -199,16 +199,6 @@ You can tweak two different path: ``` -## Rendering - -Additionally to playing the song, LittleGPTracker can be used to render the audio to file. To control file rendering, the variable RENDER can be set to either FILE,FILESPLIT,FILERT,FILESPLITRT. Note that there's a small issue with the speed when using FILE/FILESPLIT so the xxRT seem like the best choice at the moment - -```xml - - - -``` - ## Volume This setting is for GP2X and Dingoo only. It is used to set the volume of the hardware at startup. In decimal (base 10). diff --git a/docs/wiki/ubuntu_install.md b/docs/wiki/ubuntu_install.md index fef0874f..c78ac58c 100644 --- a/docs/wiki/ubuntu_install.md +++ b/docs/wiki/ubuntu_install.md @@ -58,4 +58,3 @@ Please remember: ##### Use Piggy as your Midi Sequencer -##### Script piggy to run in render mode from a simple xterm command diff --git a/sources/Application/Mixer/MixerService.cpp b/sources/Application/Mixer/MixerService.cpp index 6e9fc14d..39faea70 100644 --- a/sources/Application/Mixer/MixerService.cpp +++ b/sources/Application/Mixer/MixerService.cpp @@ -8,46 +8,25 @@ #include "Services/Midi/MidiService.h" #include "System/Console/Trace.h" -MixerService::MixerService(): - out_(0), - sync_(0) -{ - mode_=MSM_AUDIO ; - const char *render=Config::GetInstance()->GetValue("RENDER") ; - if (render) { - if (!strcmp(render,"FILERT")) { - mode_=MSM_FILERT ; - } ; - if (!strcmp(render,"FILE")) { - mode_=MSM_FILE ; - } ; - if (!strcmp(render,"FILESPLIT")) { - mode_=MSM_FILESPLIT ; - } ; - if (!strcmp(render,"FILESPLITRT")) { - mode_=MSM_FILESPLITRT ; - } ; - } ; -} ; +MixerService::MixerService() : out_(0), sync_(0) { mode_ = MSRM_PLAYBACK; }; -MixerService::~MixerService() { -} ; +MixerService::~MixerService(){}; /* * initializes the mixer service, config changes depending if we're in sequencer or render mode */ bool MixerService::Init() { - // create the output depending on rendering mode - out_ = 0; + // create the output depending on rendering mode + out_ = 0; switch (mode_) { - case MSM_FILE: - case MSM_FILESPLIT: - out_ = new DummyAudioOut(); - break; - default: - Audio *audio = Audio::GetInstance(); - out_ = audio->GetFirst(); - break; + case MSRM_STEREO: + case MSRM_STEMS: + out_ = new DummyAudioOut(); + break; + default: + Audio *audio = Audio::GetInstance(); + out_ = audio->GetFirst(); + break; } for (int i=0;iSetFileRenderer("project:mixdown.wav"); break; - case MSM_FILESPLITRT: - case MSM_FILESPLIT: + case MSRM_STEMS: for (int i = 0; i < SONG_CHANNEL_COUNT; i++) { char buffer[1024]; sprintf(buffer, "project:channel%d.wav", i); @@ -103,21 +80,13 @@ void MixerService::Close() { master_.Empty() ; switch(mode_) { - case MSM_FILE: - case MSM_FILESPLIT: - SAFE_DELETE(out_) ; - break; - default: - break ; - } - switch(mode_) { - case MSM_FILESPLITRT: - case MSM_FILESPLIT: - break; - default: - break ; - } - } + case MSRM_STEMS: + case MSRM_STEREO: + break; + default: + break; + } + } for (int i=0;iStart() ; - if (out_) { - out_->AddObserver(*this) ; - out_->Start() ; + MidiService::GetInstance()->Start(); + if (out_) { + out_->AddObserver(*this); + out_->Start(); } return true ; } ; @@ -188,20 +159,18 @@ int MixerService::GetPlayedBufferPercentage() { } void MixerService::toggleRendering(bool enable) { - switch(mode_) { - case MSM_AUDIO: - break ; - case MSM_FILERT: - case MSM_FILE: - out_->EnableRendering(enable) ; - break ; - case MSM_FILESPLITRT: - case MSM_FILESPLIT: - for (int i=0;iEnableRendering(enable); + break; + case MSRM_STEMS: + for (int i = 0; i < SONG_CHANNEL_COUNT; i++) { + bus_[i].EnableRendering(enable); + }; + break; + } } void MixerService::OnPlayerStart() { diff --git a/sources/Application/Mixer/MixerService.h b/sources/Application/Mixer/MixerService.h index 8e7c31e3..ff3d0e08 100644 --- a/sources/Application/Mixer/MixerService.h +++ b/sources/Application/Mixer/MixerService.h @@ -13,13 +13,11 @@ #include "Services/Audio/AudioOut.h" #include "MixBus.h" -enum MixerServiceMode { - MSM_AUDIO, - MSM_FILE, - MSM_FILESPLIT, - MSM_FILERT, - MSM_FILESPLITRT -} ; +enum MixerServiceRenderMode { + MSRM_PLAYBACK, + MSRM_STEREO, + MSRM_STEMS, +}; #define MAX_BUS_COUNT 10 @@ -64,11 +62,11 @@ class MixerService: protected: void toggleRendering(bool enable) ; private: - void initRendering(MixerServiceMode); + void initRendering(MixerServiceRenderMode); AudioOut *out_; MixBus master_; MixBus bus_[MAX_BUS_COUNT]; - MixerServiceMode mode_; + MixerServiceRenderMode mode_; SDL_mutex *sync_; } ; #endif diff --git a/sources/Application/Model/Project.cpp b/sources/Application/Model/Project.cpp index 9dead77e..56bcead3 100644 --- a/sources/Application/Model/Project.cpp +++ b/sources/Application/Model/Project.cpp @@ -41,8 +41,9 @@ tempoNudge_(0) new Variable("scale", VAR_SCALE, scaleNames, scaleCount, 0); this->Insert(scale); scale->SetInt(0); - Variable *render = new Variable("render", VAR_RENDER, MAX_RENDER_MODE, 0); - this->Insert(render); + Variable *renderMode = + new Variable("renderMode", VAR_RENDER, renderModes, MAX_RENDER_MODE, 0); + this->Insert(renderMode); // Reload the midi device list diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index fa1aeb59..a3b3ce3d 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -55,7 +55,7 @@ class Project: public Persistent,public VariableContainer,I_Observer { virtual void RestoreContent(TiXmlElement *element); void LoadFirstGen(const char *root) ; - const unsigned int MAX_RENDER_MODE = 2; + const unsigned int MAX_RENDER_MODE = 3; protected: void buildMidiDeviceList() ; diff --git a/sources/Application/Model/ProjectDatas.h b/sources/Application/Model/ProjectDatas.h index e4eaff0f..0f70f880 100644 --- a/sources/Application/Model/ProjectDatas.h +++ b/sources/Application/Model/ProjectDatas.h @@ -1,2 +1,6 @@ -char *softclipStates[] = {"Bypass", "Subtle", "Medium", "Heavy", "Insane"}; -char *softclipGainStates[] = {"[unity]", "[boost]"}; +#ifndef _PROJECTDATAS_H_ +#define _PROJECTDATAS_H_ +static const char *softclipStates[] = {"Bypass", "Subtle", "Medium", "Heavy", "Insane"}; +static const char *softclipGainStates[] = {"[unity]", "[boost]"}; +static const char *renderModes[] = {"Off", "Stereo", "Stems"}; +#endif \ No newline at end of file diff --git a/sources/Application/Views/ProjectView.cpp b/sources/Application/Views/ProjectView.cpp index 154729fe..47bc7fb9 100644 --- a/sources/Application/Views/ProjectView.cpp +++ b/sources/Application/Views/ProjectView.cpp @@ -1,5 +1,6 @@ #include "ProjectView.h" #include "Application/Mixer/MixerService.h" +#include "Application/Model/ProjectDatas.h" #include "Application/Model/Scale.h" #include "Application/Persistency/PersistencyService.h" #include "Application/Views/ModalDialogs/MessageBox.h" @@ -96,7 +97,8 @@ static void PurgeCallback(View &v,ModalView &dialog) { ProjectView::ProjectView(GUIWindow &w,ViewData *data):FieldView(w,data) { - lastClock_=0 ; + lastRenderMode_ = 0; + lastClock_=0 ; lastTick_=0 ; project_=data->project_ ; @@ -184,7 +186,8 @@ ProjectView::ProjectView(GUIWindow &w,ViewData *data):FieldView(w,data) { v = project_->FindVariable(VAR_RENDER); NAssert(v); position._y += 2; - field = new UIIntVarField(position, *v, "Render: %s", 0, project_->MAX_RENDER_MODE, 1, 2); + field = new UIIntVarField(position, *v, "Render: %s", 0, + project_->MAX_RENDER_MODE - 1, 1, 2); T_SimpleList::Insert(field); position._y += 2; @@ -199,7 +202,9 @@ ProjectView::~ProjectView() { void ProjectView::ProcessButtonMask(unsigned short mask,bool pressed) { - if (!pressed) return ; + if (!pressed) + return; + View::SetNotification(""); //Clear to not write junk to notification FieldView::ProcessButtonMask(mask) ; @@ -210,12 +215,21 @@ void ProjectView::ProcessButtonMask(unsigned short mask,bool pressed) { SetChanged(); NotifyObservers(&ve) ; } - } else { - if (mask&EPBM_START) { + } else { + if (mask&EPBM_START) { + if (project_->GetRenderMode() > 0) { + if (!lastRenderMode_) { + lastRenderMode_ = true; + View::SetNotification("Rendering start!"); + } else if(lastRenderMode_) { + lastRenderMode_ = false; + View::SetNotification("Rendering done!"); + } + } Player *player=Player::GetInstance() ; player->OnStartButton(PM_SONG,viewData_->songX_,false,viewData_->songX_) ; } - } ; + }; } ; void ProjectView::DrawView() { @@ -234,8 +248,15 @@ void ProjectView::DrawView() { SetColor(CD_NORMAL); DrawString(pos._x,pos._y,projectString,props) ; - FieldView::Redraw() ; - drawMap() ; + FieldView::Redraw(); + drawMap() ; + if(lastRenderMode_ != project_->GetRenderMode()) { + lastRenderMode_ = project_->GetRenderMode(); + MixerService::GetInstance()->SetRenderMode(lastRenderMode_); + if (lastRenderMode_ > 0) View::SetNotification("Rendering armed, press start"); + else View::SetNotification("Rendering disabled"); + } + View::EnableNotification(); } ; void ProjectView::Update(Observable &,I_ObservableData *data) { @@ -247,10 +268,9 @@ void ProjectView::Update(Observable &,I_ObservableData *data) { # ifdef _64BIT int fourcc=*((int*)data); #else - int fourcc=(unsigned int)data ; + int fourcc = (unsigned int)data; #endif - MixerService::GetInstance()->SetRenderMode(project_->GetRenderMode()); UIField *focus=GetFocus() ; if (fourcc!=ACTION_TEMPO_CHANGED) { focus->ClearFocus() ; diff --git a/sources/Application/Views/ProjectView.h b/sources/Application/Views/ProjectView.h index 1f9474cf..54048054 100644 --- a/sources/Application/Views/ProjectView.h +++ b/sources/Application/Views/ProjectView.h @@ -28,7 +28,8 @@ class ProjectView: public FieldView,public I_Observer { protected: private: Project *project_ ; -// Debug + int lastRenderMode_; + // Debug unsigned long lastTick_ ; unsigned long lastClock_ ; UIField *tempoField_ ; diff --git a/sources/Application/Views/SongView.cpp b/sources/Application/Views/SongView.cpp index e184195a..a410769a 100644 --- a/sources/Application/Views/SongView.cpp +++ b/sources/Application/Views/SongView.cpp @@ -1,5 +1,7 @@ #include "SongView.h" #include "Application/Commands/ApplicationCommandDispatcher.h" +#include "Application/Mixer/MixerService.h" +#include "Application/Model/ProjectDatas.h" #include "Application/Player/Player.h" #include "Application/Utils/char.h" #include "System/Console/Trace.h" @@ -20,6 +22,7 @@ SongView::SongView(GUIWindow &w, ViewData *viewData, const char *song) updatingChain_ = false; lastChain_ = 0; songname_ = song; + project_ = viewData->project_; for (int i = 0; i < 8; i++) { this->lastPlayedPosition_[i] = 0; @@ -477,6 +480,15 @@ void SongView::switchSoloMode() { }; void SongView::onStart() { + if (project_->GetRenderMode() > 0) { + if (!lastRenderMode_) { + lastRenderMode_ = true; + View::SetNotification("Rendering start!"); + } else if(lastRenderMode_) { + lastRenderMode_ = false; + View::SetNotification("Rendering done!"); + } + } Player *player = Player::GetInstance(); unsigned char from = viewData_->songX_; unsigned char to = from; diff --git a/sources/Application/Views/SongView.h b/sources/Application/Views/SongView.h index 90512cd1..5895e022 100644 --- a/sources/Application/Views/SongView.h +++ b/sources/Application/Views/SongView.h @@ -81,7 +81,9 @@ class SongView : public View { bool needClear_; bool canDeepClone_; void nudgeTempo(int direction); - uint8_t jumpLength_; // When jumping columns with B + bool lastRenderMode_ = false; + Project *project_; +uint8_t jumpLength_; // When jumping columns with B }; #endif From 2ccdeb73d34c1f770ab291969cd2f4148e7acb23 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Wed, 12 Nov 2025 19:15:03 +0000 Subject: [PATCH 3/9] Fixed the bugs, all is well. Puts the mode in ViewData Respects play mode --- sources/Application/Views/ProjectView.cpp | 65 +++++++++++++---------- sources/Application/Views/ProjectView.h | 11 ++-- sources/Application/Views/SongView.cpp | 19 ++++--- sources/Application/Views/SongView.h | 4 +- sources/Application/Views/ViewData.cpp | 2 + sources/Application/Views/ViewData.h | 6 +++ 6 files changed, 60 insertions(+), 47 deletions(-) diff --git a/sources/Application/Views/ProjectView.cpp b/sources/Application/Views/ProjectView.cpp index 47bc7fb9..d4e5372e 100644 --- a/sources/Application/Views/ProjectView.cpp +++ b/sources/Application/Views/ProjectView.cpp @@ -80,13 +80,15 @@ static void SaveAsProjectCallback(View &v,ModalView &dialog) { } static void LoadCallback(View &v,ModalView &dialog) { - if (dialog.GetReturnCode()==MBL_YES) { + MixerService::GetInstance()->SetRenderMode(0); + if (dialog.GetReturnCode()==MBL_YES) { ((ProjectView &)v).OnLoadProject() ; } } ; static void QuitCallback(View &v,ModalView &dialog) { - if (dialog.GetReturnCode()==MBL_YES) { + MixerService::GetInstance()->SetRenderMode(0); + if (dialog.GetReturnCode()==MBL_YES) { ((ProjectView &)v).OnQuit() ; } } ; @@ -97,9 +99,8 @@ static void PurgeCallback(View &v,ModalView &dialog) { ProjectView::ProjectView(GUIWindow &w,ViewData *data):FieldView(w,data) { - lastRenderMode_ = 0; - lastClock_=0 ; - lastTick_=0 ; + lastClock_ = 0; + lastTick_ = 0; project_=data->project_ ; @@ -204,29 +205,29 @@ void ProjectView::ProcessButtonMask(unsigned short mask,bool pressed) { if (!pressed) return; - View::SetNotification(""); //Clear to not write junk to notification - FieldView::ProcessButtonMask(mask) ; + FieldView::ProcessButtonMask(mask); - if (mask&EPBM_R) { - if (mask&EPBM_DOWN) { + if (mask & EPBM_R) { + if (mask&EPBM_DOWN) { ViewType vt=VT_SONG; ViewEvent ve(VET_SWITCH_VIEW,&vt) ; SetChanged(); - NotifyObservers(&ve) ; - } + NotifyObservers(&ve); + } } else { if (mask&EPBM_START) { - if (project_->GetRenderMode() > 0) { - if (!lastRenderMode_) { - lastRenderMode_ = true; - View::SetNotification("Rendering start!"); - } else if(lastRenderMode_) { - lastRenderMode_ = false; - View::SetNotification("Rendering done!"); - } - } - Player *player=Player::GetInstance() ; + Player *player = Player::GetInstance(); + + int renderMode = viewData_->renderMode_; + if (renderMode > 0 && !player->IsRunning()) { + viewData_->isRendering_ = true; + View::SetNotification("Rendering started!"); + } else if (viewData_->isRendering_ && player->IsRunning()) { + viewData_->isRendering_ = false; + View::SetNotification("Rendering done!"); + } + player->OnStartButton(PM_SONG,viewData_->songX_,false,viewData_->songX_) ; } }; @@ -249,13 +250,20 @@ void ProjectView::DrawView() { DrawString(pos._x,pos._y,projectString,props) ; FieldView::Redraw(); - drawMap() ; - if(lastRenderMode_ != project_->GetRenderMode()) { - lastRenderMode_ = project_->GetRenderMode(); - MixerService::GetInstance()->SetRenderMode(lastRenderMode_); - if (lastRenderMode_ > 0) View::SetNotification("Rendering armed, press start"); - else View::SetNotification("Rendering disabled"); - } + drawMap(); + + // Sync render mode from project to viewData and show notification + int currentMode = project_->GetRenderMode(); + if (viewData_->renderMode_ != currentMode) { + // Mode changed + if (currentMode > 0 && viewData_->renderMode_ == 0) { + // Changed from off to on + View::SetNotification("Rendering on, press start"); + } + viewData_->renderMode_ = currentMode; + MixerService::GetInstance()->SetRenderMode(currentMode); + } + View::EnableNotification(); } ; @@ -292,6 +300,7 @@ void ProjectView::Update(Observable &,I_ObservableData *data) { break ; } case ACTION_SAVE: { + MixerService::GetInstance()->SetRenderMode(0); PersistencyService *service = PersistencyService::GetInstance(); service->Save(); break; diff --git a/sources/Application/Views/ProjectView.h b/sources/Application/Views/ProjectView.h index 54048054..352a28e4 100644 --- a/sources/Application/Views/ProjectView.h +++ b/sources/Application/Views/ProjectView.h @@ -27,11 +27,10 @@ class ProjectView: public FieldView,public I_Observer { protected: private: - Project *project_ ; - int lastRenderMode_; - // Debug - unsigned long lastTick_ ; - unsigned long lastClock_ ; - UIField *tempoField_ ; + Project *project_; + // Debug + unsigned long lastTick_; + unsigned long lastClock_; + UIField *tempoField_; } ; #endif diff --git a/sources/Application/Views/SongView.cpp b/sources/Application/Views/SongView.cpp index a410769a..39c8a59d 100644 --- a/sources/Application/Views/SongView.cpp +++ b/sources/Application/Views/SongView.cpp @@ -22,7 +22,6 @@ SongView::SongView(GUIWindow &w, ViewData *viewData, const char *song) updatingChain_ = false; lastChain_ = 0; songname_ = song; - project_ = viewData->project_; for (int i = 0; i < 8; i++) { this->lastPlayedPosition_[i] = 0; @@ -480,15 +479,6 @@ void SongView::switchSoloMode() { }; void SongView::onStart() { - if (project_->GetRenderMode() > 0) { - if (!lastRenderMode_) { - lastRenderMode_ = true; - View::SetNotification("Rendering start!"); - } else if(lastRenderMode_) { - lastRenderMode_ = false; - View::SetNotification("Rendering done!"); - } - } Player *player = Player::GetInstance(); unsigned char from = viewData_->songX_; unsigned char to = from; @@ -497,6 +487,14 @@ void SongView::onStart() { from = r.Left(); to = r.Right(); } + int renderMode = viewData_->renderMode_; + if (renderMode > 0 && !player->IsRunning()) { + viewData_->isRendering_ = true; + View::SetNotification("Rendering started!"); + } else if (viewData_->isRendering_ && player->IsRunning()) { + viewData_->isRendering_ = false; + View::SetNotification("Rendering done!"); + } player->OnSongStartButton(from, to, false, false); }; @@ -523,6 +521,7 @@ void SongView::onStop() { from = r.Left(); to = r.Right(); } + player->OnSongStartButton(from, to, true, false); }; diff --git a/sources/Application/Views/SongView.h b/sources/Application/Views/SongView.h index 5895e022..90512cd1 100644 --- a/sources/Application/Views/SongView.h +++ b/sources/Application/Views/SongView.h @@ -81,9 +81,7 @@ class SongView : public View { bool needClear_; bool canDeepClone_; void nudgeTempo(int direction); - bool lastRenderMode_ = false; - Project *project_; -uint8_t jumpLength_; // When jumping columns with B + uint8_t jumpLength_; // When jumping columns with B }; #endif diff --git a/sources/Application/Views/ViewData.cpp b/sources/Application/Views/ViewData.cpp index 11376496..f4dca9ff 100644 --- a/sources/Application/Views/ViewData.cpp +++ b/sources/Application/Views/ViewData.cpp @@ -17,6 +17,8 @@ ViewData::ViewData(Project *project) { currentGroove_=0 ; mixerCol_=0 ; mixerRow_=0 ; + renderMode_ = 0; + isRendering_ = false; } ; ViewData::~ViewData() { diff --git a/sources/Application/Views/ViewData.h b/sources/Application/Views/ViewData.h index 08da6f19..26be7b9d 100644 --- a/sources/Application/Views/ViewData.h +++ b/sources/Application/Views/ViewData.h @@ -74,6 +74,12 @@ class ViewData { int mixerCol_ ; // int mixerRow_ ; + + // Render Settings + + int renderMode_ ; // Current render mode (0=audio, 1=mixdown, 2=split) + bool isRendering_ ; // True when actively rendering (playing with mode > 0) + // Player Settings PlayMode playMode_ ; From cb86cd0b4d3cac3d99d6df8c156f2c2cb6bad4f3 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Thu, 13 Nov 2025 18:40:06 +0000 Subject: [PATCH 4/9] Update changelog --- CHANGELOG | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 2be5d1f0..512ae52c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +1.6.0-bacon1 + ProjectView Render project added + When in project view, it's now possible to initiate rendering + 1.5.0-bacon3 Contributions: purelygrey From 5f75f0d2f7805013257b022296f2e6ff54606496 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Tue, 18 Nov 2025 09:50:11 +0000 Subject: [PATCH 5/9] The bugs were, in fact, not all fixed Actually render a project when initiating. viewData_->renderMode_ is neat for keeping common recording state reference between Project/Song views. [OK] It's possible to change output mode while rendering but nothing happens. [OK] Rendering continues to work as intended. [OK] No problems found when changing mode while rendering then starting with a new mode --- sources/Application/Mixer/MixerService.cpp | 6 ++++++ sources/Application/Mixer/MixerService.h | 2 ++ sources/Application/Views/ProjectView.cpp | 22 +++++++++++----------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/sources/Application/Mixer/MixerService.cpp b/sources/Application/Mixer/MixerService.cpp index 39faea70..a3edde83 100644 --- a/sources/Application/Mixer/MixerService.cpp +++ b/sources/Application/Mixer/MixerService.cpp @@ -99,6 +99,8 @@ void MixerService::SetRenderMode(int mode) { mode_ = MixerServiceRenderMode(mode); } +bool MixerService::IsRendering() { return isRendering_; } + bool MixerService::Start() { MidiService::GetInstance()->Start(); if (out_) { @@ -159,13 +161,17 @@ int MixerService::GetPlayedBufferPercentage() { } void MixerService::toggleRendering(bool enable) { + isRendering_ = enable; switch (mode_) { case MSRM_PLAYBACK: + initRendering(MSRM_PLAYBACK); break; case MSRM_STEREO: + initRendering(MSRM_STEREO); out_->EnableRendering(enable); break; case MSRM_STEMS: + initRendering(MSRM_STEMS); for (int i = 0; i < SONG_CHANNEL_COUNT; i++) { bus_[i].EnableRendering(enable); }; diff --git a/sources/Application/Mixer/MixerService.h b/sources/Application/Mixer/MixerService.h index ff3d0e08..29ada47c 100644 --- a/sources/Application/Mixer/MixerService.h +++ b/sources/Application/Mixer/MixerService.h @@ -50,6 +50,7 @@ class MixerService: void SetSoftclip(int, int); void SetMasterVolume(int); void SetRenderMode(int); + bool IsRendering(); int GetPlayedBufferPercentage() ; virtual void Execute(FourCC id,float value) ; @@ -68,5 +69,6 @@ class MixerService: MixBus bus_[MAX_BUS_COUNT]; MixerServiceRenderMode mode_; SDL_mutex *sync_; + bool isRendering_ = false; } ; #endif diff --git a/sources/Application/Views/ProjectView.cpp b/sources/Application/Views/ProjectView.cpp index d4e5372e..db28a37d 100644 --- a/sources/Application/Views/ProjectView.cpp +++ b/sources/Application/Views/ProjectView.cpp @@ -184,9 +184,9 @@ ProjectView::ProjectView(GUIWindow &w,ViewData *data):FieldView(w,data) { MidiService::GetInstance()->Size(), 1, 1); T_SimpleList::Insert(field); + position._y += 2; v = project_->FindVariable(VAR_RENDER); NAssert(v); - position._y += 2; field = new UIIntVarField(position, *v, "Render: %s", 0, project_->MAX_RENDER_MODE - 1, 1, 2); T_SimpleList::Insert(field); @@ -252,16 +252,16 @@ void ProjectView::DrawView() { FieldView::Redraw(); drawMap(); - // Sync render mode from project to viewData and show notification - int currentMode = project_->GetRenderMode(); - if (viewData_->renderMode_ != currentMode) { - // Mode changed - if (currentMode > 0 && viewData_->renderMode_ == 0) { - // Changed from off to on - View::SetNotification("Rendering on, press start"); - } - viewData_->renderMode_ = currentMode; - MixerService::GetInstance()->SetRenderMode(currentMode); + int currentMode = project_->GetRenderMode(); + if ((viewData_->renderMode_ != currentMode) && !MixerService::GetInstance()->IsRendering()) { + // Mode changed + if (currentMode > 0 && viewData_->renderMode_ == 0) { + View::SetNotification("Rendering on, press start"); + } else if (currentMode == 0 && viewData_->renderMode_ > 0) { + View::SetNotification("Rendering off"); + } + viewData_->renderMode_ = currentMode; + MixerService::GetInstance()->SetRenderMode(currentMode); } View::EnableNotification(); From 286f86eb9a5f4a034c5c4caaff66924116c66407 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:13:26 +0000 Subject: [PATCH 6/9] clang-format --- sources/Application/Model/Project.h | 2 +- sources/Application/Views/ProjectView.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index a3b3ce3d..5f84e773 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -55,7 +55,7 @@ class Project: public Persistent,public VariableContainer,I_Observer { virtual void RestoreContent(TiXmlElement *element); void LoadFirstGen(const char *root) ; - const unsigned int MAX_RENDER_MODE = 3; + static const unsigned int MAX_RENDER_MODE = 3; protected: void buildMidiDeviceList() ; diff --git a/sources/Application/Views/ProjectView.cpp b/sources/Application/Views/ProjectView.cpp index db28a37d..386346a8 100644 --- a/sources/Application/Views/ProjectView.cpp +++ b/sources/Application/Views/ProjectView.cpp @@ -279,8 +279,8 @@ void ProjectView::Update(Observable &,I_ObservableData *data) { int fourcc = (unsigned int)data; #endif - UIField *focus=GetFocus() ; - if (fourcc!=ACTION_TEMPO_CHANGED) { + UIField *focus = GetFocus(); + if (fourcc!= ACTION_TEMPO_CHANGED) { focus->ClearFocus() ; focus->Draw(w_) ; w_.Flush() ; From cc0d0453c84a29736314c520ed6339251654d258 Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Tue, 25 Nov 2025 18:39:41 +0000 Subject: [PATCH 7/9] Fix in-class initializer Initialize Mixerservice :: isRendering in constructor --- sources/Application/Mixer/MixerService.cpp | 4 +++- sources/Application/Mixer/MixerService.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/sources/Application/Mixer/MixerService.cpp b/sources/Application/Mixer/MixerService.cpp index a3edde83..04577122 100644 --- a/sources/Application/Mixer/MixerService.cpp +++ b/sources/Application/Mixer/MixerService.cpp @@ -8,7 +8,9 @@ #include "Services/Midi/MidiService.h" #include "System/Console/Trace.h" -MixerService::MixerService() : out_(0), sync_(0) { mode_ = MSRM_PLAYBACK; }; +MixerService::MixerService() : out_(0), sync_(0), isRendering_(false) { + mode_ = MSRM_PLAYBACK; +}; MixerService::~MixerService(){}; diff --git a/sources/Application/Mixer/MixerService.h b/sources/Application/Mixer/MixerService.h index 29ada47c..55de2f4a 100644 --- a/sources/Application/Mixer/MixerService.h +++ b/sources/Application/Mixer/MixerService.h @@ -69,6 +69,6 @@ class MixerService: MixBus bus_[MAX_BUS_COUNT]; MixerServiceRenderMode mode_; SDL_mutex *sync_; - bool isRendering_ = false; + bool isRendering_; } ; #endif From 7cec118826abfa607595b9b193f746ad01586d7c Mon Sep 17 00:00:00 2001 From: djdiskmachine <110535302+djdiskmachine@users.noreply.github.com> Date: Sat, 27 Dec 2025 15:39:45 +0100 Subject: [PATCH 8/9] Put back GetRenderMode in header file Lost in uplift from master --- sources/Application/Model/Project.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index 2d3ad7b9..6de28a8a 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -44,7 +44,7 @@ class Project: public Persistent,public VariableContainer,I_Observer { int GetSoftclip(); int GetSoftclipGain(); int GetPregain(); - + int GetRenderMode(); void Trigger(); static const unsigned int MAX_RENDER_MODE = 3; From d2adf2518b4c128a74a38cce3cb45e708b8ce6cb Mon Sep 17 00:00:00 2001 From: djdiskmachine Date: Sat, 27 Dec 2025 15:42:24 +0100 Subject: [PATCH 9/9] clang-format --- sources/Application/Model/Project.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sources/Application/Model/Project.h b/sources/Application/Model/Project.h index 6de28a8a..09ee605b 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -57,8 +57,9 @@ class Project: public Persistent,public VariableContainer,I_Observer { void LoadFirstGen(const char *root); - protected: - void buildMidiDeviceList() ; +protected: + void buildMidiDeviceList(); + private: InstrumentBank *instrumentBank_ ; char **midiDeviceList_ ; @@ -66,6 +67,6 @@ class Project: public Persistent,public VariableContainer,I_Observer { int tempoNudge_ ; unsigned long lastTap_[MAX_TAP] ; unsigned int tempoTapCount_; -} ; +}; #endif