diff --git a/CHANGELOG b/CHANGELOG index 9a7b7e80..60ea1c8c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,6 @@ 1.6.0-bacon2 + ProjectView Render item + When in project view, it's now possible to initiate rendering Interpolate values in tables and phrases R + B on single-column selection will fill values from lowest to highest 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 52c6fd3c..ff297ffa 100644 --- a/docs/wiki/What-is-LittlePiggyTracker.md +++ b/docs/wiki/What-is-LittlePiggyTracker.md @@ -591,20 +591,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 abd6c722..04577122 100644 --- a/sources/Application/Mixer/MixerService.cpp +++ b/sources/Application/Mixer/MixerService.cpp @@ -1,52 +1,34 @@ #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), - 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), isRendering_(false) { + 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;iInsert(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 +57,23 @@ bool MixerService::Init() { return (result); }; +void MixerService::initRendering(MixerServiceRenderMode mode) { + switch(mode) { + case MSRM_PLAYBACK: + break; + case MSRM_STEREO: + out_->SetFileRenderer("project:mixdown.wav"); + break; + case MSRM_STEMS: + 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()); @@ -98,21 +82,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 ; } ; @@ -181,20 +163,22 @@ 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: + initRendering(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 8a7f6a5f..55de2f4a 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 @@ -51,6 +49,8 @@ class MixerService: void SetPregain(int); void SetSoftclip(int, int); void SetMasterVolume(int); + void SetRenderMode(int); + bool IsRendering(); int GetPlayedBufferPercentage() ; virtual void Execute(FourCC id,float value) ; @@ -63,11 +63,12 @@ class MixerService: protected: void toggleRendering(bool enable) ; private: - AudioOut *out_ ; - MixBus master_ ; - MixBus bus_[MAX_BUS_COUNT] ; - MixerServiceMode mode_ ; - SDL_mutex *sync_ ; - + void initRendering(MixerServiceRenderMode); + AudioOut *out_; + MixBus master_; + MixBus bus_[MAX_BUS_COUNT]; + MixerServiceRenderMode mode_; + SDL_mutex *sync_; + bool isRendering_; } ; #endif diff --git a/sources/Application/Model/Project.cpp b/sources/Application/Model/Project.cpp index 784a21fb..56bcead3 100644 --- a/sources/Application/Model/Project.cpp +++ b/sources/Application/Model/Project.cpp @@ -41,6 +41,9 @@ tempoNudge_(0) new Variable("scale", VAR_SCALE, scaleNames, scaleCount, 0); this->Insert(scale); scale->SetInt(0); + Variable *renderMode = + new Variable("renderMode", VAR_RENDER, renderModes, MAX_RENDER_MODE, 0); + this->Insert(renderMode); // Reload the midi device list @@ -110,6 +113,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 5b8f5c1a..09ee605b 100644 --- a/sources/Application/Model/Project.h +++ b/sources/Application/Model/Project.h @@ -17,6 +17,7 @@ #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 "6" @@ -43,9 +44,10 @@ 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; // I_Observer virtual void Update(Observable &o, I_ObservableData *d); @@ -56,7 +58,8 @@ class Project: public Persistent,public VariableContainer,I_Observer { void LoadFirstGen(const char *root); protected: - void buildMidiDeviceList() ; + void buildMidiDeviceList(); + private: InstrumentBank *instrumentBank_ ; char **midiDeviceList_ ; @@ -64,6 +67,6 @@ class Project: public Persistent,public VariableContainer,I_Observer { int tempoNudge_ ; unsigned long lastTap_[MAX_TAP] ; unsigned int tempoTapCount_; -} ; +}; #endif 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 2afe9516..386346a8 100644 --- a/sources/Application/Views/ProjectView.cpp +++ b/sources/Application/Views/ProjectView.cpp @@ -1,4 +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" @@ -78,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() ; } } ; @@ -95,8 +99,8 @@ static void PurgeCallback(View &v,ModalView &dialog) { ProjectView::ProjectView(GUIWindow &w,ViewData *data):FieldView(w,data) { - lastClock_=0 ; - lastTick_=0 ; + lastClock_ = 0; + lastTick_ = 0; project_=data->project_ ; @@ -180,6 +184,13 @@ 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); + field = new UIIntVarField(position, *v, "Render: %s", 0, + project_->MAX_RENDER_MODE - 1, 1, 2); + T_SimpleList::Insert(field); + position._y += 2; a1 = new UIActionField("Exit", ACTION_QUIT, position); a1->AddObserver(*this); @@ -192,23 +203,34 @@ ProjectView::~ProjectView() { void ProjectView::ProcessButtonMask(unsigned short mask,bool pressed) { - if (!pressed) return ; + if (!pressed) + return; - 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) ; - } - } else { - if (mask&EPBM_START) { - Player *player=Player::GetInstance() ; + NotifyObservers(&ve); + } + } else { + if (mask&EPBM_START) { + 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_) ; } - } ; + }; } ; void ProjectView::DrawView() { @@ -227,8 +249,22 @@ void ProjectView::DrawView() { SetColor(CD_NORMAL); DrawString(pos._x,pos._y,projectString,props) ; - FieldView::Redraw() ; - drawMap() ; + FieldView::Redraw(); + drawMap(); + + 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(); } ; void ProjectView::Update(Observable &,I_ObservableData *data) { @@ -240,11 +276,11 @@ 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 - UIField *focus=GetFocus() ; - if (fourcc!=ACTION_TEMPO_CHANGED) { + UIField *focus = GetFocus(); + if (fourcc!= ACTION_TEMPO_CHANGED) { focus->ClearFocus() ; focus->Draw(w_) ; w_.Flush() ; @@ -264,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 1f9474cf..352a28e4 100644 --- a/sources/Application/Views/ProjectView.h +++ b/sources/Application/Views/ProjectView.h @@ -27,10 +27,10 @@ class ProjectView: public FieldView,public I_Observer { protected: private: - Project *project_ ; -// 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 e184195a..39c8a59d 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" @@ -485,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); }; @@ -511,6 +521,7 @@ void SongView::onStop() { from = r.Left(); to = r.Right(); } + player->OnSongStartButton(from, to, true, false); }; 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_ ;