From 1a536ca45652a13f793d8dc61c39082f07807066 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Tue, 11 Dec 2018 21:02:06 +0200 Subject: [PATCH 01/14] Add extension handling --- src/Extension.cpp | 35 +++++++++++++++++ src/Extension.h | 16 ++++++++ src/Prototracker.cpp | 91 ++++++++++++++++++++++++++++++-------------- src/Prototracker.h | 40 ++++++++++--------- 4 files changed, 135 insertions(+), 47 deletions(-) create mode 100644 src/Extension.cpp create mode 100644 src/Extension.h diff --git a/src/Extension.cpp b/src/Extension.cpp new file mode 100644 index 0000000..bef4f05 --- /dev/null +++ b/src/Extension.cpp @@ -0,0 +1,35 @@ +#include "Extension.h" +#include + +Extension::~Extension() +{ + +} + +void Extension::init() +{ + +} + + +void Extension::deinit() +{ + +} + + +void Extension::registerUIComponents(UIComponentFactory& factory) +{ +} + + +void Extension::registerFileSections(Song& song) +{ + +} + + +ISynth* Extension::registerSynth() +{ + return NULL; +} diff --git a/src/Extension.h b/src/Extension.h new file mode 100644 index 0000000..16dfe4b --- /dev/null +++ b/src/Extension.h @@ -0,0 +1,16 @@ +#pragma once + +struct ISynth; +struct Song; +struct UIComponentFactory; + +class Extension +{ +public: + virtual ~Extension(); + virtual void init(); + virtual void deinit(); + virtual void registerUIComponents(UIComponentFactory& factory); + virtual void registerFileSections(Song& song); + virtual ISynth* registerSynth(); +}; diff --git a/src/Prototracker.cpp b/src/Prototracker.cpp index 6332ee8..973b76d 100644 --- a/src/Prototracker.cpp +++ b/src/Prototracker.cpp @@ -16,55 +16,88 @@ #endif Prototracker::Prototracker() - : mReady(false) + : mReady(false) { } Prototracker::~Prototracker() { + for (auto extension : mExtensions) + { + delete extension; + } } bool Prototracker::init() { - mEditorState = new EditorState(); - mSong = new Song(); - mPlayer = new Player(*mSong); - mGamepad = new Gamepad(); - mSynth = new Synth(); - mMixer = new Mixer(*mPlayer, *mSynth); - mRenderer = new Renderer(); + for (auto extension : mExtensions) + { + extension->init(); + } + + mEditorState = new EditorState(); + mSong = new Song(); + mPlayer = new Player(*mSong); + mGamepad = new Gamepad(); + mSynth = NULL; + + for (auto extension : mExtensions) + { + ISynth *synth = extension->registerSynth(); + + if (synth != NULL) + { + mSynth = synth; + } + } - mMainEditor = new MainEditor(*mEditorState, *mPlayer, mPlayer->getPlayerState(), *mSong, *mSynth, *mMixer); + // Use default synth - if (!initRenderer()) { - return false; - } + if (mSynth == NULL) + { + mSynth = new Synth(); + } + + mMixer = new Mixer(*mPlayer, *mSynth); + mRenderer = new Renderer(); + + for (auto extension : mExtensions) + { + extension->registerUIComponents(*mUIComponentFactory); + extension->registerSectionListeners(*mSong); + } + + mMainEditor = new MainEditor(*mEditorState, *mPlayer, mPlayer->getPlayerState(), *mSong, *mSynth, *mMixer); + + if (!initRenderer()) { + return false; + } #ifndef __EMSCRIPTEN__ - initEditor(); + initEditor(); #endif - return true; + return true; } void Prototracker::deinit() { - mMixer->stopThread(); - - delete mMainEditor; - delete mMixer; - delete mPlayer; - delete mSong; - delete mEditorState; - delete mGamepad; - delete mRenderer; + mMixer->stopThread(); + + delete mMainEditor; + delete mMixer; + delete mPlayer; + delete mSong; + delete mEditorState; + delete mGamepad; + delete mRenderer; } bool Prototracker::initRenderer() { - Theme theme; + Theme theme; if (!theme.load("assets/elements")) { @@ -85,13 +118,13 @@ bool Prototracker::initRenderer() } mPreviousTick = SDL_GetTicks(); - return true; + return true; } void Prototracker::initEditor() { - // Emscripten needs an absolute path to filesystem root + // Emscripten needs an absolute path to filesystem root #ifdef __EMSCRIPTEN__ const char *gamepadPath = "/assets/gamecontrollerdb.txt"; const char *songPath = "/assets/dub.song"; @@ -118,7 +151,7 @@ void Prototracker::initEditor() bool Prototracker::handleEvents() { - bool done = false; + bool done = false; SDL_Event event; while (SDL_PollEvent(&event)) @@ -257,11 +290,11 @@ bool Prototracker::handleEvents() #endif } - return !done; + return !done; } std::string Prototracker::getSongBase64() const { - return mMainEditor->getSongBase64(); + return mMainEditor->getSongBase64(); } diff --git a/src/Prototracker.h b/src/Prototracker.h index 4f02430..14e0dd3 100644 --- a/src/Prototracker.h +++ b/src/Prototracker.h @@ -11,29 +11,33 @@ struct Mixer; struct MainEditor; struct Gamepad; struct Renderer; +struct Extension; class Prototracker { - Renderer *mRenderer; - EditorState *mEditorState; - IPlayer *mPlayer; - Song *mSong; - ISynth *mSynth; - Mixer *mMixer; - MainEditor *mMainEditor; - Gamepad *mGamepad; + Renderer *mRenderer; + EditorState *mEditorState; + IPlayer *mPlayer; + Song *mSong; + ISynth *mSynth; + Mixer *mMixer; + MainEditor *mMainEditor; + Gamepad *mGamepad; + UIComponentFactory *mUIComponentFactory - Uint32 mPreviousTick; - bool mReady; + std::vector mExtensions; - void initEditor(); - bool initRenderer(); + Uint32 mPreviousTick; + bool mReady; + + void initEditor(); + bool initRenderer(); public: - Prototracker(); - ~Prototracker(); + Prototracker(); + ~Prototracker(); - bool init(); - void deinit(); - bool handleEvents(); - std::string getSongBase64() const; + bool init(); + void deinit(); + bool handleEvents(); + std::string getSongBase64() const; }; From 03a9e8adc67b6f44f39ef5acae253ccce3adc717 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Wed, 12 Dec 2018 21:22:31 +0200 Subject: [PATCH 02/14] Refactored default functionality into extension --- Makefile.chip8 | 7 ++--- Makefile.emscripten | 7 +++-- Makefile.linux | 5 ++-- Makefile.mingw | 7 +++-- src/Base/BaseExtension.cpp | 26 +++++++++++++++++ src/Base/BaseExtension.h | 14 +++++++++ src/{ => Base}/Oscillator.cpp | 6 ++-- src/{ => Base}/Oscillator.h | 10 +++---- src/{ => Base}/Random.cpp | 0 src/{ => Base}/Random.h | 0 src/{ => Base}/Synth.cpp | 22 +++++++-------- src/{ => Base}/Synth.h | 8 +++--- src/{ => Base}/Wave.cpp | 0 src/{ => Base}/Wave.h | 0 src/{ => Base}/WaveGen.cpp | 0 src/{ => Base}/WaveGen.h | 0 src/{ => Base}/WaveStore.cpp | 0 src/{ => Base}/WaveStore.h | 0 src/{ => Base}/WaveView.cpp | 4 +-- src/{ => Base}/WaveView.h | 4 +-- src/Extension.cpp | 4 +-- src/Extension.h | 37 ++++++++++++++++++++---- src/ISynth.h | 34 +++++++--------------- src/Main.cpp | 2 ++ src/MainEditor.cpp | 19 +++++++++++-- src/MainEditor.h | 3 +- src/Prototracker.cpp | 17 ++++++++--- src/Prototracker.h | 10 ++++++- src/{ISynth.cpp => SynthBase.cpp} | 38 ++++++++++++------------- src/SynthBase.h | 47 +++++++++++++++++++++++++++++++ src/Theme.cpp | 14 +++------ src/Theme.h | 1 + src/UIComponentFactory.cpp | 19 +++++++++++++ src/UIComponentFactory.h | 18 ++++++++++++ 34 files changed, 274 insertions(+), 109 deletions(-) create mode 100644 src/Base/BaseExtension.cpp create mode 100644 src/Base/BaseExtension.h rename src/{ => Base}/Oscillator.cpp (97%) rename src/{ => Base}/Oscillator.h (96%) rename src/{ => Base}/Random.cpp (100%) rename src/{ => Base}/Random.h (100%) rename src/{ => Base}/Synth.cpp (88%) rename src/{ => Base}/Synth.h (70%) rename src/{ => Base}/Wave.cpp (100%) rename src/{ => Base}/Wave.h (100%) rename src/{ => Base}/WaveGen.cpp (100%) rename src/{ => Base}/WaveGen.h (100%) rename src/{ => Base}/WaveStore.cpp (100%) rename src/{ => Base}/WaveStore.h (100%) rename src/{ => Base}/WaveView.cpp (97%) rename src/{ => Base}/WaveView.h (93%) rename src/{ISynth.cpp => SynthBase.cpp} (79%) create mode 100644 src/SynthBase.h create mode 100644 src/UIComponentFactory.cpp create mode 100644 src/UIComponentFactory.h diff --git a/Makefile.chip8 b/Makefile.chip8 index 7944117..e7789b7 100644 --- a/Makefile.chip8 +++ b/Makefile.chip8 @@ -4,9 +4,8 @@ # to other optimizations. Fullscreen mode is forced. OUTPUT=prototracker -SRC=src/*.cpp +SRC=src/*.cpp src/Base/*.cpp +SRC_H=src/*.h src/Base/*.h -$(OUTPUT): $(SRC) src/*.h +$(OUTPUT): $(SRC) $(SRC_H) g++ -Wformat -s -std=c++11 -o $@ $(SRC) -lSDL2_image -lSDL2main `sdl2-config --cflags --libs` -march=armv7-a -mtune=cortex-a7 -ffast-math -Ofast -DSCALE=1 -DOVERSAMPLE=0 -DFULLSCREEN=1 -DSAMPLERATE=22050 - - diff --git a/Makefile.emscripten b/Makefile.emscripten index 8d9ebc6..65711cb 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -5,14 +5,15 @@ # command line. OUTPUT=prototracker -SRC=src/*.cpp +SRC=src/*.cpp src/Base/*.cpp +SRC_H=src/*.h src/Base/*.h -$(OUTPUT).html: $(SRC) src/*.h +$(OUTPUT).html: $(SRC) $(SRC_H) emcc -std=c++11 --preload-file assets -O3 -s NO_EXIT_RUNTIME=0 \ -s EXPORTED_FUNCTIONS='["_main"]' -s SDL2_IMAGE_FORMATS='["png"]' \ -s EXTRA_EXPORTED_RUNTIME_METHODS='["ccall", "FS"]' -DSCALE=2 -DSAMPLERATE=22050 \ -s ALLOW_MEMORY_GROWTH=1 -s USE_SDL=2 -s USE_SDL_IMAGE=2 -s BINARYEN_TRAP_MODE=clamp \ - -s WASM=1 -s MODULARIZE=1 $(wildcard $(SRC)) -o $@ -s ASSERTIONS=1 + -s WASM=1 -s MODULARIZE=0 $(wildcard $(SRC)) -o $@ -s ASSERTIONS=1 clean: rm $(OUTPUT).html $(OUTPUT).js $(OUTPUT).data $(OUTPUT).wasm diff --git a/Makefile.linux b/Makefile.linux index 1195b5b..7e457cf 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,5 +1,6 @@ OUTPUT=prototracker -SRC=src/*.cpp +SRC=src/*.cpp src/Base/*.cpp +SRC_H=src/*.h src/Base/*.h -$(OUTPUT): $(SRC) src/*.h +$(OUTPUT): $(SRC) $(SRC_H) g++ -O3 -Wformat -std=c++11 -o $@ $(SRC) -lSDL2_image -lSDL2main `sdl2-config --cflags --libs` -s -DSCALE=2 diff --git a/Makefile.mingw b/Makefile.mingw index cb53dcb..e150d29 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,5 +1,6 @@ OUTPUT=prototracker.exe -SRC=src/*.cpp +SRC=src/*.cpp src/Base/*.cpp +SRC_H=src/*.h src/Base/*.h -$(OUTPUT): $(SRC) src/*.h - g++ -DSCALE=2 -O3 -Wformat -std=c++11 -o $@ $(SRC) -lmingw32 -lSDL2_image -lSDL2main -lSDL2 -Ic:/mingw/include/SDL2 -Ic:/tdm-gcc-32/include/SDL2 -s -DENABLE_AUDIO_QUEUE=1 +$(OUTPUT): $(SRC) $(SRC_H) + g++ -DSCALE=2 -O3 -Wformat -std=c++11 -o $@ $(SRC) -lmingw32 -lSDL2_image -lSDL2main -lSDL2 -Ic:/mingw/include/SDL2 -Ic:/tdm-gcc-32/include/SDL2 -g -DENABLE_AUDIO_QUEUE=1 diff --git a/src/Base/BaseExtension.cpp b/src/Base/BaseExtension.cpp new file mode 100644 index 0000000..34a0be3 --- /dev/null +++ b/src/Base/BaseExtension.cpp @@ -0,0 +1,26 @@ +#include "BaseExtension.h" +#include "../UIComponentFactory.h" +#include "WaveView.h" + +void BaseExtension::init() +{ + mSynth = new Synth(); +} + + +void BaseExtension::deinit() +{ + delete mSynth; +} + + +ISynth* BaseExtension::registerSynth() +{ + return mSynth; +} + + +void BaseExtension::registerUIComponents(UIComponentFactory& factory, EditorState& editorState) +{ + factory.registerComponent("WaveView", [&](const Theme::Element& element){ return new WaveView(editorState, mSynth->getWaveStore()); }); +} diff --git a/src/Base/BaseExtension.h b/src/Base/BaseExtension.h new file mode 100644 index 0000000..f13e349 --- /dev/null +++ b/src/Base/BaseExtension.h @@ -0,0 +1,14 @@ +#pragma once + +#include "../Extension.h" +#include "Synth.h" + +class BaseExtension: public Extension +{ + Synth *mSynth; +public: + virtual void init(); + virtual void deinit(); + virtual ISynth* registerSynth(); + virtual void registerUIComponents(UIComponentFactory& factory, EditorState& editorState); +}; diff --git a/src/Oscillator.cpp b/src/Base/Oscillator.cpp similarity index 97% rename from src/Oscillator.cpp rename to src/Base/Oscillator.cpp index c8ccef2..a7cc0b4 100644 --- a/src/Oscillator.cpp +++ b/src/Base/Oscillator.cpp @@ -2,11 +2,11 @@ #include #include #include "SDL.h" -#include "Sample.h" +#include "../Sample.h" #include "WaveStore.h" -#include "SequenceRow.h" +#include "../SequenceRow.h" #include "Wave.h" -#include "TrackState.h" +#include "../TrackState.h" Oscillator::Oscillator() : IOscillator(), mWaveStore(NULL), mWave(0), mSpeed(0), mVolume(1.0), mPosition(0) diff --git a/src/Oscillator.h b/src/Base/Oscillator.h similarity index 96% rename from src/Oscillator.h rename to src/Base/Oscillator.h index 615bda1..4d510b1 100644 --- a/src/Oscillator.h +++ b/src/Base/Oscillator.h @@ -1,6 +1,6 @@ #pragma once -#include "IOscillator.h" +#include "../IOscillator.h" struct WaveStore; @@ -11,12 +11,12 @@ struct WaveStore; class Oscillator: public IOscillator { const WaveStore* mWaveStore; - + int mWave, mQueuedWave; int mSpeed; int mPosition; int mVolume; - + public: static const int volumeResolution = 8192; static const int oscillatorLength = 256; @@ -24,14 +24,14 @@ class Oscillator: public IOscillator Oscillator(); virtual ~Oscillator(); - + virtual void triggerNote(); virtual void handleTrackState(ITrackState& trackState); void setWaveStore(const WaveStore& sampleStore); void setPosition(int newPosition); void setWave(int wave); void queueWave(int wave); - + virtual void setFrequency(float frequency); virtual void setVolume(int volume); virtual void update(int numSamples); diff --git a/src/Random.cpp b/src/Base/Random.cpp similarity index 100% rename from src/Random.cpp rename to src/Base/Random.cpp diff --git a/src/Random.h b/src/Base/Random.h similarity index 100% rename from src/Random.h rename to src/Base/Random.h diff --git a/src/Synth.cpp b/src/Base/Synth.cpp similarity index 88% rename from src/Synth.cpp rename to src/Base/Synth.cpp index 2549b7c..30118a6 100644 --- a/src/Synth.cpp +++ b/src/Base/Synth.cpp @@ -1,21 +1,21 @@ #include "Synth.h" #include "Oscillator.h" -#include "SequenceRow.h" -#include "Sample.h" +#include "../SequenceRow.h" +#include "../Sample.h" #include "WaveStore.h" #include "SDL.h" Synth::Synth() - : ISynth() + : SynthBase() { mWaveStore = new WaveStore(); - - /* - + + /* + Initialize the audio tracks. - + */ - + for (int i = 0 ; i < SequenceRow::maxTracks ; ++i) { Oscillator *oscillator = new Oscillator(); @@ -28,12 +28,12 @@ Synth::Synth() Synth::~Synth() { delete mWaveStore; - + /* - + NOTE: ~ISynth() will delete the Oscillator objects we initialized above! No need to cleanup yourself. - + */ } diff --git a/src/Synth.h b/src/Base/Synth.h similarity index 70% rename from src/Synth.h rename to src/Base/Synth.h index b96522a..553a817 100644 --- a/src/Synth.h +++ b/src/Base/Synth.h @@ -1,16 +1,16 @@ #pragma once -#include "ISynth.h" +#include "../SynthBase.h" struct WaveStore; -class Synth: public ISynth +class Synth: public SynthBase { WaveStore* mWaveStore; - + public: Synth(); virtual ~Synth(); - + const WaveStore& getWaveStore() const; }; diff --git a/src/Wave.cpp b/src/Base/Wave.cpp similarity index 100% rename from src/Wave.cpp rename to src/Base/Wave.cpp diff --git a/src/Wave.h b/src/Base/Wave.h similarity index 100% rename from src/Wave.h rename to src/Base/Wave.h diff --git a/src/WaveGen.cpp b/src/Base/WaveGen.cpp similarity index 100% rename from src/WaveGen.cpp rename to src/Base/WaveGen.cpp diff --git a/src/WaveGen.h b/src/Base/WaveGen.h similarity index 100% rename from src/WaveGen.h rename to src/Base/WaveGen.h diff --git a/src/WaveStore.cpp b/src/Base/WaveStore.cpp similarity index 100% rename from src/WaveStore.cpp rename to src/Base/WaveStore.cpp diff --git a/src/WaveStore.h b/src/Base/WaveStore.h similarity index 100% rename from src/WaveStore.h rename to src/Base/WaveStore.h diff --git a/src/WaveView.cpp b/src/Base/WaveView.cpp similarity index 97% rename from src/WaveView.cpp rename to src/Base/WaveView.cpp index 875ef9a..09ea8e0 100644 --- a/src/WaveView.cpp +++ b/src/Base/WaveView.cpp @@ -1,6 +1,6 @@ #include "WaveView.h" -#include "Renderer.h" -#include "Color.h" +#include "../Renderer.h" +#include "../Color.h" #include "SDL.h" #include "Wave.h" #include "WaveStore.h" diff --git a/src/WaveView.h b/src/Base/WaveView.h similarity index 93% rename from src/WaveView.h rename to src/Base/WaveView.h index 9c1371a..36610a7 100644 --- a/src/WaveView.h +++ b/src/Base/WaveView.h @@ -1,6 +1,6 @@ #pragma once -#include "Editor.h" +#include "../Editor.h" struct WaveStore; @@ -14,5 +14,3 @@ class WaveView: public Editor virtual void onDraw(Renderer& renderer, const SDL_Rect& area); virtual bool onEvent(SDL_Event& event); }; - - diff --git a/src/Extension.cpp b/src/Extension.cpp index bef4f05..3384141 100644 --- a/src/Extension.cpp +++ b/src/Extension.cpp @@ -18,12 +18,12 @@ void Extension::deinit() } -void Extension::registerUIComponents(UIComponentFactory& factory) +void Extension::registerUIComponents(UIComponentFactory& factory, EditorState& editorState) { } -void Extension::registerFileSections(Song& song) +void Extension::registerSectionListeners(Song& song) { } diff --git a/src/Extension.h b/src/Extension.h index 16dfe4b..9faa7e0 100644 --- a/src/Extension.h +++ b/src/Extension.h @@ -3,14 +3,39 @@ struct ISynth; struct Song; struct UIComponentFactory; +struct EditorState; class Extension { public: - virtual ~Extension(); - virtual void init(); - virtual void deinit(); - virtual void registerUIComponents(UIComponentFactory& factory); - virtual void registerFileSections(Song& song); - virtual ISynth* registerSynth(); + virtual ~Extension(); + + /** + * Extension should initialise everything in init() + */ + + virtual void init(); + + /** + * Extension should deinitialise everything in deinit() + */ + + virtual void deinit(); + + /** + * Extension will register any new UI components when this is called + */ + + virtual void registerUIComponents(UIComponentFactory& factory, EditorState& editorState); + + /** + * Extension will register any Song FileSection listeners here + */ + + virtual void registerSectionListeners(Song& song); + + /** + * Return either an instance of ISynth to register a synth or NULL for no synth + */ + virtual ISynth* registerSynth(); }; diff --git a/src/ISynth.h b/src/ISynth.h index 8eb31e5..eccaac9 100644 --- a/src/ISynth.h +++ b/src/ISynth.h @@ -3,46 +3,32 @@ struct IOscillator; struct Sample16; -/* - -ISynth handles the IOscillators and some things needed by the GUI -(namely, data for visualization). - -Override ISynth() to initialize your own IOscillators. - -*/ - class ISynth { -protected: - IOscillator **mOscillator; - Sample16 *mPreviousOscillatorOutput, *mTempBuffer; - int mProbePosition; - public: - ISynth(); - virtual ~ISynth(); - virtual void reset(); + virtual ~ISynth() {} + + virtual void reset() = 0; static const int oscillatorProbeLength = 128; - const Sample16* getOscillatorProbe(int oscillator) const; - IOscillator& getOscillator(int i); - int getProbePosition() const; - void setSampleRate(int rate); + virtual const Sample16* getOscillatorProbe(int oscillator) const = 0; + virtual IOscillator& getOscillator(int i) = 0; + virtual int getProbePosition() const = 0; + virtual void setSampleRate(int rate) = 0; // Update the synth state - virtual void update(int numSamples); + virtual void update(int numSamples) = 0; /* Get samples (does not change synth state) - Note: ISynth should overwrite the values in the buffer; + Note: SynthBase should overwrite the values in the buffer; it might contain random values. Fill with zeroes if the output should be silent. This should also update mPreviousOscillatorOutput for the GUI. */ - virtual void render(Sample16 *buffer, int numSamples); + virtual void render(Sample16 *buffer, int numSamples) = 0; }; diff --git a/src/Main.cpp b/src/Main.cpp index a15e4f4..36628d1 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -2,6 +2,7 @@ #include "SDL_image.h" #include "Debug.h" #include "Prototracker.h" +#include "Base/BaseExtension.h" #include #ifdef __EMSCRIPTEN__ @@ -30,6 +31,7 @@ extern "C" int main(int argc, char **argv) atexit(IMG_Quit); Prototracker prototracker; + prototracker.registerExtension(new BaseExtension()); if (!prototracker.init()) { diff --git a/src/MainEditor.cpp b/src/MainEditor.cpp index 34cde0e..45c37fa 100644 --- a/src/MainEditor.cpp +++ b/src/MainEditor.cpp @@ -34,6 +34,8 @@ #include "MessageDisplayer.h" #include "TooltipManager.h" #include "TooltipDisplayer.h" +#include "UIComponentFactory.h" +#include "Debug.h" #include "App.h" #include "SDL.h" #include "Theme.h" @@ -711,7 +713,7 @@ void MainEditor::saveState() } -bool MainEditor::loadElements(const Theme& theme) +bool MainEditor::loadElements(const Theme& theme, const UIComponentFactory& componentFactory) { deleteChildren(); @@ -820,7 +822,20 @@ bool MainEditor::loadElements(const Theme& theme) break; case Theme::Unknown: - break; + { + Editor *component = componentFactory.createComponent(element.name, element); + + if (component != NULL) + { + addChild(component, element.parameters[0], element.parameters[1], element.parameters[2], element.parameters[3]); + } + else + { + showMessageV(MessageError, "Unknown element %s", element.name); + debug("Unknown element %s", element.name); + } + } + break; } } diff --git a/src/MainEditor.h b/src/MainEditor.h index 0bcb2ca..60c2791 100644 --- a/src/MainEditor.h +++ b/src/MainEditor.h @@ -22,6 +22,7 @@ struct TooltipDisplayer; struct AudioDeviceSelector; struct CommandSelector; struct CommandOptionSelector; +struct UIComponentFactory; class MainEditor: public Editor { @@ -115,6 +116,6 @@ class MainEditor: public Editor bool loadState(); void saveState(); - bool loadElements(const Theme& theme); + bool loadElements(const Theme& theme, const UIComponentFactory& componentFactory); }; diff --git a/src/Prototracker.cpp b/src/Prototracker.cpp index 973b76d..eb3edd7 100644 --- a/src/Prototracker.cpp +++ b/src/Prototracker.cpp @@ -2,7 +2,6 @@ #include "Prototracker.h" #include "Mixer.h" #include "Song.h" -#include "Synth.h" #include "Player.h" #include "EditorState.h" #include "Gamepad.h" @@ -10,6 +9,8 @@ #include "MainEditor.h" #include "Emscripten.h" #include "Debug.h" +#include "Extension.h" +#include "UIComponentFactory.h" #ifdef __EMSCRIPTEN__ #include @@ -41,6 +42,7 @@ bool Prototracker::init() mPlayer = new Player(*mSong); mGamepad = new Gamepad(); mSynth = NULL; + mUIComponentFactory = new UIComponentFactory(); for (auto extension : mExtensions) { @@ -49,6 +51,7 @@ bool Prototracker::init() if (synth != NULL) { mSynth = synth; + break; } } @@ -56,7 +59,7 @@ bool Prototracker::init() if (mSynth == NULL) { - mSynth = new Synth(); + debug("No synth set"); } mMixer = new Mixer(*mPlayer, *mSynth); @@ -64,7 +67,7 @@ bool Prototracker::init() for (auto extension : mExtensions) { - extension->registerUIComponents(*mUIComponentFactory); + extension->registerUIComponents(*mUIComponentFactory, *mEditorState); extension->registerSectionListeners(*mSong); } @@ -112,7 +115,7 @@ bool Prototracker::initRenderer() SDL_Rect area = {0, 0, theme.getWidth(), theme.getHeight()}; mMainEditor->setArea(area); - if (!mMainEditor->loadElements(theme)) + if (!mMainEditor->loadElements(theme, *mUIComponentFactory)) { return false; } @@ -298,3 +301,9 @@ std::string Prototracker::getSongBase64() const { return mMainEditor->getSongBase64(); } + + +void Prototracker::registerExtension(Extension *extension) +{ + mExtensions.push_back(extension); +} diff --git a/src/Prototracker.h b/src/Prototracker.h index 14e0dd3..3e6fb06 100644 --- a/src/Prototracker.h +++ b/src/Prototracker.h @@ -2,6 +2,7 @@ #include "SDL.h" #include +#include struct EditorState; struct IPlayer; @@ -12,6 +13,7 @@ struct MainEditor; struct Gamepad; struct Renderer; struct Extension; +struct UIComponentFactory; class Prototracker { Renderer *mRenderer; @@ -22,7 +24,7 @@ class Prototracker { Mixer *mMixer; MainEditor *mMainEditor; Gamepad *mGamepad; - UIComponentFactory *mUIComponentFactory + UIComponentFactory *mUIComponentFactory; std::vector mExtensions; @@ -36,6 +38,12 @@ class Prototracker { Prototracker(); ~Prototracker(); + /** + * Register extension, this class will destruct the passed extension object. + */ + + void registerExtension(Extension *extension); + bool init(); void deinit(); bool handleEvents(); diff --git a/src/ISynth.cpp b/src/SynthBase.cpp similarity index 79% rename from src/ISynth.cpp rename to src/SynthBase.cpp index d81675c..ef648ca 100644 --- a/src/ISynth.cpp +++ b/src/SynthBase.cpp @@ -1,72 +1,72 @@ -#include "ISynth.h" +#include "SynthBase.h" #include "IOscillator.h" #include "SequenceRow.h" #include "Sample.h" #include "SDL.h" #include -const int ISynth::oscillatorProbeLength; +const int SynthBase::oscillatorProbeLength; -ISynth::ISynth() +SynthBase::SynthBase() : mProbePosition(0) { mOscillator = new IOscillator*[SequenceRow::maxTracks]; mPreviousOscillatorOutput = new Sample16[oscillatorProbeLength * SequenceRow::maxTracks]; mTempBuffer = new Sample16[2048]; - + SDL_memset(mPreviousOscillatorOutput, 0, sizeof(Sample16) * oscillatorProbeLength * SequenceRow::maxTracks); } -ISynth::~ISynth() +SynthBase::~SynthBase() { for (int i = 0 ; i < SequenceRow::maxTracks ; ++i) delete mOscillator[i]; - + delete[] mOscillator; delete[] mPreviousOscillatorOutput; } -IOscillator& ISynth::getOscillator(int i) +IOscillator& SynthBase::getOscillator(int i) { return *mOscillator[i]; } -const Sample16* ISynth::getOscillatorProbe(int oscillator) const +const Sample16* SynthBase::getOscillatorProbe(int oscillator) const { return &mPreviousOscillatorOutput[oscillatorProbeLength * oscillator]; } -void ISynth::update(int numSamples) +void SynthBase::update(int numSamples) { for (int i = 0 ; i < SequenceRow::maxTracks ; ++i) mOscillator[i]->update(numSamples); } -void ISynth::render(Sample16 *buffer, int numSamples) +void SynthBase::render(Sample16 *buffer, int numSamples) { SDL_memset(buffer, 0, sizeof(Sample16) * numSamples); - + int probeCount = std::min(numSamples, oscillatorProbeLength); - + for (int i = 0 ; i < SequenceRow::maxTracks ; ++i) { SDL_memset(mTempBuffer, 0, sizeof(Sample16) * numSamples); mOscillator[i]->render(mTempBuffer, numSamples); - + for (int p = 0 ; p < numSamples ; ++p) { buffer[p].left += mTempBuffer[p].left; buffer[p].right += mTempBuffer[p].right; } - + Sample16 *src = mTempBuffer + std::max(0, numSamples - oscillatorProbeLength); Sample16 *dest = mPreviousOscillatorOutput + oscillatorProbeLength * i; - + for (int p = 0 ; p < probeCount ; ++p) { Sample16& sample = dest[(p + mProbePosition) % oscillatorProbeLength]; @@ -75,24 +75,24 @@ void ISynth::render(Sample16 *buffer, int numSamples) src++; } } - + mProbePosition += probeCount; } -int ISynth::getProbePosition() const +int SynthBase::getProbePosition() const { return mProbePosition; } -void ISynth::setSampleRate(int rate) +void SynthBase::setSampleRate(int rate) { for (int i = 0 ; i < SequenceRow::maxTracks ; ++i) mOscillator[i]->setSampleRate(rate); } -void ISynth::reset() +void SynthBase::reset() { } diff --git a/src/SynthBase.h b/src/SynthBase.h new file mode 100644 index 0000000..d23f3eb --- /dev/null +++ b/src/SynthBase.h @@ -0,0 +1,47 @@ +#pragma once + +#include "ISynth.h" + +/* + +SynthBase handles the IOscillators and some things needed by the GUI +(namely, data for visualization). + +Override SynthBase() to initialize your own IOscillators. + +*/ + +class SynthBase: public ISynth +{ +protected: + IOscillator **mOscillator; + Sample16 *mPreviousOscillatorOutput, *mTempBuffer; + int mProbePosition; + +public: + SynthBase(); + virtual ~SynthBase(); + + virtual void reset(); + + static const int oscillatorProbeLength = 128; + + virtual const Sample16* getOscillatorProbe(int oscillator) const; + virtual IOscillator& getOscillator(int i); + virtual int getProbePosition() const; + virtual void setSampleRate(int rate); + + // Update the synth state + virtual void update(int numSamples); + + /* + Get samples (does not change synth state) + Note: SynthBase should overwrite the values in the buffer; + it might contain random values. Fill with zeroes if + the output should be silent. + + This should also update mPreviousOscillatorOutput for + the GUI. + */ + virtual void render(Sample16 *buffer, int numSamples); +}; diff --git a/src/Theme.cpp b/src/Theme.cpp index 24fd359..d45ff1a 100644 --- a/src/Theme.cpp +++ b/src/Theme.cpp @@ -225,6 +225,7 @@ bool Theme::loadDefinition(const std::string& path) { Element element; element.type = Theme::Unknown; + strncpy(element.name, elementName, sizeof(element.name) - 1); for (auto elementDef : elements) { @@ -235,16 +236,9 @@ bool Theme::loadDefinition(const std::string& path) } } - if (element.type != Theme::Unknown) - { - memcpy(element.parameters, parameters, sizeof(element.parameters)); - memcpy(element.strParameters, strParameters, sizeof(element.strParameters)); - mElement.push_back(element); - } - else - { - debug("Unknown element %s on line %d", elementName, lineCounter); - } + memcpy(element.parameters, parameters, sizeof(element.parameters)); + memcpy(element.strParameters, strParameters, sizeof(element.strParameters)); + mElement.push_back(element); } else { diff --git a/src/Theme.h b/src/Theme.h index 73e214f..a0150ba 100644 --- a/src/Theme.h +++ b/src/Theme.h @@ -26,6 +26,7 @@ class Theme struct Element { + char name[32]; ElementType type; int parameters[10]; char strParameters[10][50]; diff --git a/src/UIComponentFactory.cpp b/src/UIComponentFactory.cpp new file mode 100644 index 0000000..f771985 --- /dev/null +++ b/src/UIComponentFactory.cpp @@ -0,0 +1,19 @@ +#include "UIComponentFactory.h" + +void UIComponentFactory::registerComponent(const char *componentName, EditorCreatorFunc creatorFunc) +{ + mComponentPrototypes[componentName] = creatorFunc; +} + + +Editor* UIComponentFactory::createComponent(const char *componentName, const Theme::Element& element) const +{ + auto createComponent = mComponentPrototypes.find(componentName); + + if (createComponent == mComponentPrototypes.end()) + { + return NULL; + } + + return createComponent->second(element); +} diff --git a/src/UIComponentFactory.h b/src/UIComponentFactory.h new file mode 100644 index 0000000..01998b2 --- /dev/null +++ b/src/UIComponentFactory.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include +#include +#include "Theme.h" + +struct Editor; + +typedef std::function EditorCreatorFunc; + +class UIComponentFactory +{ + std::map mComponentPrototypes; +public: + void registerComponent(const char *componentName, EditorCreatorFunc creatorFunc); + Editor* createComponent(const char *componentName, const Theme::Element& element) const; +}; From 03d596d51a1d5fa9ea126c5f56f6dc043271bbcc Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Fri, 14 Dec 2018 17:10:25 +0200 Subject: [PATCH 03/14] Pass data to extension init --- src/Base/BaseExtension.cpp | 2 +- src/Base/BaseExtension.h | 4 +++- src/Extension.cpp | 2 +- src/Extension.h | 3 ++- src/Prototracker.cpp | 13 +++++++------ 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Base/BaseExtension.cpp b/src/Base/BaseExtension.cpp index 34a0be3..5f2a6b7 100644 --- a/src/Base/BaseExtension.cpp +++ b/src/Base/BaseExtension.cpp @@ -2,7 +2,7 @@ #include "../UIComponentFactory.h" #include "WaveView.h" -void BaseExtension::init() +void BaseExtension::init(IPlayer& player, const Song& song) { mSynth = new Synth(); } diff --git a/src/Base/BaseExtension.h b/src/Base/BaseExtension.h index f13e349..7032b72 100644 --- a/src/Base/BaseExtension.h +++ b/src/Base/BaseExtension.h @@ -3,11 +3,13 @@ #include "../Extension.h" #include "Synth.h" +struct IPlayer; + class BaseExtension: public Extension { Synth *mSynth; public: - virtual void init(); + virtual void init(IPlayer& player, const Song& song); virtual void deinit(); virtual ISynth* registerSynth(); virtual void registerUIComponents(UIComponentFactory& factory, EditorState& editorState); diff --git a/src/Extension.cpp b/src/Extension.cpp index 3384141..14f35ef 100644 --- a/src/Extension.cpp +++ b/src/Extension.cpp @@ -6,7 +6,7 @@ Extension::~Extension() } -void Extension::init() +void Extension::init(IPlayer& player, const Song& song) { } diff --git a/src/Extension.h b/src/Extension.h index 9faa7e0..788b862 100644 --- a/src/Extension.h +++ b/src/Extension.h @@ -4,6 +4,7 @@ struct ISynth; struct Song; struct UIComponentFactory; struct EditorState; +struct IPlayer; class Extension { @@ -14,7 +15,7 @@ class Extension * Extension should initialise everything in init() */ - virtual void init(); + virtual void init(IPlayer& player, const Song& song); /** * Extension should deinitialise everything in deinit() diff --git a/src/Prototracker.cpp b/src/Prototracker.cpp index eb3edd7..7f2b6dc 100644 --- a/src/Prototracker.cpp +++ b/src/Prototracker.cpp @@ -32,17 +32,18 @@ Prototracker::~Prototracker() bool Prototracker::init() { + mGamepad = new Gamepad(); + mEditorState = new EditorState(); + mSong = new Song(); + mPlayer = new Player(*mSong); + mUIComponentFactory = new UIComponentFactory(); + for (auto extension : mExtensions) { - extension->init(); + extension->init(*mPlayer, *mSong); } - mEditorState = new EditorState(); - mSong = new Song(); - mPlayer = new Player(*mSong); - mGamepad = new Gamepad(); mSynth = NULL; - mUIComponentFactory = new UIComponentFactory(); for (auto extension : mExtensions) { From b1cf922601f61a1d1abb5c2e67fd88204325510f Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Fri, 14 Dec 2018 21:24:36 +0200 Subject: [PATCH 04/14] Remove debug flag from compiler options --- Makefile.mingw | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.mingw b/Makefile.mingw index e150d29..0bcd0f9 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -3,4 +3,4 @@ SRC=src/*.cpp src/Base/*.cpp SRC_H=src/*.h src/Base/*.h $(OUTPUT): $(SRC) $(SRC_H) - g++ -DSCALE=2 -O3 -Wformat -std=c++11 -o $@ $(SRC) -lmingw32 -lSDL2_image -lSDL2main -lSDL2 -Ic:/mingw/include/SDL2 -Ic:/tdm-gcc-32/include/SDL2 -g -DENABLE_AUDIO_QUEUE=1 + g++ -DSCALE=2 -O3 -Wformat -std=c++11 -o $@ $(SRC) -lmingw32 -lSDL2_image -lSDL2main -lSDL2 -Ic:/mingw/include/SDL2 -Ic:/tdm-gcc-32/include/SDL2 -s -DENABLE_AUDIO_QUEUE=1 From e0cdd1376519e64f11c4c6a9ee8023ff7f8c505c Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Mon, 21 Jan 2019 19:19:22 +0200 Subject: [PATCH 05/14] Renamed BaseExtension directory --- Makefile.chip8 | 4 ++-- Makefile.emscripten | 4 ++-- Makefile.linux | 4 ++-- Makefile.mingw | 4 ++-- src/{Base => BaseExtension}/BaseExtension.cpp | 0 src/{Base => BaseExtension}/BaseExtension.h | 0 src/{Base => BaseExtension}/Oscillator.cpp | 0 src/{Base => BaseExtension}/Oscillator.h | 0 src/{Base => BaseExtension}/Random.cpp | 0 src/{Base => BaseExtension}/Random.h | 0 src/{Base => BaseExtension}/Synth.cpp | 0 src/{Base => BaseExtension}/Synth.h | 0 src/{Base => BaseExtension}/Wave.cpp | 0 src/{Base => BaseExtension}/Wave.h | 0 src/{Base => BaseExtension}/WaveGen.cpp | 0 src/{Base => BaseExtension}/WaveGen.h | 0 src/{Base => BaseExtension}/WaveStore.cpp | 0 src/{Base => BaseExtension}/WaveStore.h | 0 src/{Base => BaseExtension}/WaveView.cpp | 0 src/{Base => BaseExtension}/WaveView.h | 0 src/Main.cpp | 2 +- 21 files changed, 9 insertions(+), 9 deletions(-) rename src/{Base => BaseExtension}/BaseExtension.cpp (100%) rename src/{Base => BaseExtension}/BaseExtension.h (100%) rename src/{Base => BaseExtension}/Oscillator.cpp (100%) rename src/{Base => BaseExtension}/Oscillator.h (100%) rename src/{Base => BaseExtension}/Random.cpp (100%) rename src/{Base => BaseExtension}/Random.h (100%) rename src/{Base => BaseExtension}/Synth.cpp (100%) rename src/{Base => BaseExtension}/Synth.h (100%) rename src/{Base => BaseExtension}/Wave.cpp (100%) rename src/{Base => BaseExtension}/Wave.h (100%) rename src/{Base => BaseExtension}/WaveGen.cpp (100%) rename src/{Base => BaseExtension}/WaveGen.h (100%) rename src/{Base => BaseExtension}/WaveStore.cpp (100%) rename src/{Base => BaseExtension}/WaveStore.h (100%) rename src/{Base => BaseExtension}/WaveView.cpp (100%) rename src/{Base => BaseExtension}/WaveView.h (100%) diff --git a/Makefile.chip8 b/Makefile.chip8 index e7789b7..5d2c766 100644 --- a/Makefile.chip8 +++ b/Makefile.chip8 @@ -4,8 +4,8 @@ # to other optimizations. Fullscreen mode is forced. OUTPUT=prototracker -SRC=src/*.cpp src/Base/*.cpp -SRC_H=src/*.h src/Base/*.h +SRC=src/*.cpp src/BaseExtension/*.cpp +SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT): $(SRC) $(SRC_H) g++ -Wformat -s -std=c++11 -o $@ $(SRC) -lSDL2_image -lSDL2main `sdl2-config --cflags --libs` -march=armv7-a -mtune=cortex-a7 -ffast-math -Ofast -DSCALE=1 -DOVERSAMPLE=0 -DFULLSCREEN=1 -DSAMPLERATE=22050 diff --git a/Makefile.emscripten b/Makefile.emscripten index 65711cb..1fbdab5 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -5,8 +5,8 @@ # command line. OUTPUT=prototracker -SRC=src/*.cpp src/Base/*.cpp -SRC_H=src/*.h src/Base/*.h +SRC=src/*.cpp src/BaseExtension/*.cpp +SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT).html: $(SRC) $(SRC_H) emcc -std=c++11 --preload-file assets -O3 -s NO_EXIT_RUNTIME=0 \ diff --git a/Makefile.linux b/Makefile.linux index 7e457cf..2b1ee3e 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,6 +1,6 @@ OUTPUT=prototracker -SRC=src/*.cpp src/Base/*.cpp -SRC_H=src/*.h src/Base/*.h +SRC=src/*.cpp src/BaseExtension/*.cpp +SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT): $(SRC) $(SRC_H) g++ -O3 -Wformat -std=c++11 -o $@ $(SRC) -lSDL2_image -lSDL2main `sdl2-config --cflags --libs` -s -DSCALE=2 diff --git a/Makefile.mingw b/Makefile.mingw index 0bcd0f9..273294a 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,6 +1,6 @@ OUTPUT=prototracker.exe -SRC=src/*.cpp src/Base/*.cpp -SRC_H=src/*.h src/Base/*.h +SRC=src/*.cpp src/BaseExtension/*.cpp +SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT): $(SRC) $(SRC_H) g++ -DSCALE=2 -O3 -Wformat -std=c++11 -o $@ $(SRC) -lmingw32 -lSDL2_image -lSDL2main -lSDL2 -Ic:/mingw/include/SDL2 -Ic:/tdm-gcc-32/include/SDL2 -s -DENABLE_AUDIO_QUEUE=1 diff --git a/src/Base/BaseExtension.cpp b/src/BaseExtension/BaseExtension.cpp similarity index 100% rename from src/Base/BaseExtension.cpp rename to src/BaseExtension/BaseExtension.cpp diff --git a/src/Base/BaseExtension.h b/src/BaseExtension/BaseExtension.h similarity index 100% rename from src/Base/BaseExtension.h rename to src/BaseExtension/BaseExtension.h diff --git a/src/Base/Oscillator.cpp b/src/BaseExtension/Oscillator.cpp similarity index 100% rename from src/Base/Oscillator.cpp rename to src/BaseExtension/Oscillator.cpp diff --git a/src/Base/Oscillator.h b/src/BaseExtension/Oscillator.h similarity index 100% rename from src/Base/Oscillator.h rename to src/BaseExtension/Oscillator.h diff --git a/src/Base/Random.cpp b/src/BaseExtension/Random.cpp similarity index 100% rename from src/Base/Random.cpp rename to src/BaseExtension/Random.cpp diff --git a/src/Base/Random.h b/src/BaseExtension/Random.h similarity index 100% rename from src/Base/Random.h rename to src/BaseExtension/Random.h diff --git a/src/Base/Synth.cpp b/src/BaseExtension/Synth.cpp similarity index 100% rename from src/Base/Synth.cpp rename to src/BaseExtension/Synth.cpp diff --git a/src/Base/Synth.h b/src/BaseExtension/Synth.h similarity index 100% rename from src/Base/Synth.h rename to src/BaseExtension/Synth.h diff --git a/src/Base/Wave.cpp b/src/BaseExtension/Wave.cpp similarity index 100% rename from src/Base/Wave.cpp rename to src/BaseExtension/Wave.cpp diff --git a/src/Base/Wave.h b/src/BaseExtension/Wave.h similarity index 100% rename from src/Base/Wave.h rename to src/BaseExtension/Wave.h diff --git a/src/Base/WaveGen.cpp b/src/BaseExtension/WaveGen.cpp similarity index 100% rename from src/Base/WaveGen.cpp rename to src/BaseExtension/WaveGen.cpp diff --git a/src/Base/WaveGen.h b/src/BaseExtension/WaveGen.h similarity index 100% rename from src/Base/WaveGen.h rename to src/BaseExtension/WaveGen.h diff --git a/src/Base/WaveStore.cpp b/src/BaseExtension/WaveStore.cpp similarity index 100% rename from src/Base/WaveStore.cpp rename to src/BaseExtension/WaveStore.cpp diff --git a/src/Base/WaveStore.h b/src/BaseExtension/WaveStore.h similarity index 100% rename from src/Base/WaveStore.h rename to src/BaseExtension/WaveStore.h diff --git a/src/Base/WaveView.cpp b/src/BaseExtension/WaveView.cpp similarity index 100% rename from src/Base/WaveView.cpp rename to src/BaseExtension/WaveView.cpp diff --git a/src/Base/WaveView.h b/src/BaseExtension/WaveView.h similarity index 100% rename from src/Base/WaveView.h rename to src/BaseExtension/WaveView.h diff --git a/src/Main.cpp b/src/Main.cpp index 36628d1..c0a4814 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -2,7 +2,7 @@ #include "SDL_image.h" #include "Debug.h" #include "Prototracker.h" -#include "Base/BaseExtension.h" +#include "BaseExtension/BaseExtension.h" #include #ifdef __EMSCRIPTEN__ From b1ed2adbfd5e3716a82586e2eb00d5a50d252796 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Wed, 23 Jan 2019 19:30:42 +0200 Subject: [PATCH 06/14] Added support for extension dependencies --- src/BaseExtension/BaseExtension.cpp | 2 +- src/BaseExtension/BaseExtension.h | 2 +- src/Extension.cpp | 3 ++- src/Extension.h | 4 +++- src/Main.cpp | 2 +- src/Prototracker.cpp | 16 +++++----------- src/Prototracker.h | 28 +++++++++++++++++++++++----- 7 files changed, 36 insertions(+), 21 deletions(-) diff --git a/src/BaseExtension/BaseExtension.cpp b/src/BaseExtension/BaseExtension.cpp index 5f2a6b7..bea73b1 100644 --- a/src/BaseExtension/BaseExtension.cpp +++ b/src/BaseExtension/BaseExtension.cpp @@ -2,7 +2,7 @@ #include "../UIComponentFactory.h" #include "WaveView.h" -void BaseExtension::init(IPlayer& player, const Song& song) +void BaseExtension::init(Prototracker& prototracker, IPlayer& player, const Song& song) { mSynth = new Synth(); } diff --git a/src/BaseExtension/BaseExtension.h b/src/BaseExtension/BaseExtension.h index 7032b72..09fe9a6 100644 --- a/src/BaseExtension/BaseExtension.h +++ b/src/BaseExtension/BaseExtension.h @@ -9,7 +9,7 @@ class BaseExtension: public Extension { Synth *mSynth; public: - virtual void init(IPlayer& player, const Song& song); + virtual void init(Prototracker& prototracker, IPlayer& player, const Song& song); virtual void deinit(); virtual ISynth* registerSynth(); virtual void registerUIComponents(UIComponentFactory& factory, EditorState& editorState); diff --git a/src/Extension.cpp b/src/Extension.cpp index 14f35ef..f5ea4db 100644 --- a/src/Extension.cpp +++ b/src/Extension.cpp @@ -6,7 +6,8 @@ Extension::~Extension() } -void Extension::init(IPlayer& player, const Song& song) + +void Extension::init(Prototracker& prototracker, IPlayer& player, const Song& song) { } diff --git a/src/Extension.h b/src/Extension.h index 788b862..bd1ab90 100644 --- a/src/Extension.h +++ b/src/Extension.h @@ -5,17 +5,19 @@ struct Song; struct UIComponentFactory; struct EditorState; struct IPlayer; +struct Prototracker; class Extension { public: virtual ~Extension(); + /** * Extension should initialise everything in init() */ - virtual void init(IPlayer& player, const Song& song); + virtual void init(Prototracker& prototracker, IPlayer& player, const Song& song); /** * Extension should deinitialise everything in deinit() diff --git a/src/Main.cpp b/src/Main.cpp index c0a4814..27fa89c 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -31,7 +31,7 @@ extern "C" int main(int argc, char **argv) atexit(IMG_Quit); Prototracker prototracker; - prototracker.registerExtension(new BaseExtension()); + prototracker.loadExtension(); if (!prototracker.init()) { diff --git a/src/Prototracker.cpp b/src/Prototracker.cpp index 7f2b6dc..6cdcc2e 100644 --- a/src/Prototracker.cpp +++ b/src/Prototracker.cpp @@ -26,7 +26,7 @@ Prototracker::~Prototracker() { for (auto extension : mExtensions) { - delete extension; + delete extension.second; } } @@ -40,14 +40,14 @@ bool Prototracker::init() for (auto extension : mExtensions) { - extension->init(*mPlayer, *mSong); + extension.second->init(*this, *mPlayer, *mSong); } mSynth = NULL; for (auto extension : mExtensions) { - ISynth *synth = extension->registerSynth(); + ISynth *synth = extension.second->registerSynth(); if (synth != NULL) { @@ -68,8 +68,8 @@ bool Prototracker::init() for (auto extension : mExtensions) { - extension->registerUIComponents(*mUIComponentFactory, *mEditorState); - extension->registerSectionListeners(*mSong); + extension.second->registerUIComponents(*mUIComponentFactory, *mEditorState); + extension.second->registerSectionListeners(*mSong); } mMainEditor = new MainEditor(*mEditorState, *mPlayer, mPlayer->getPlayerState(), *mSong, *mSynth, *mMixer); @@ -302,9 +302,3 @@ std::string Prototracker::getSongBase64() const { return mMainEditor->getSongBase64(); } - - -void Prototracker::registerExtension(Extension *extension) -{ - mExtensions.push_back(extension); -} diff --git a/src/Prototracker.h b/src/Prototracker.h index 3e6fb06..ea34867 100644 --- a/src/Prototracker.h +++ b/src/Prototracker.h @@ -2,7 +2,9 @@ #include "SDL.h" #include -#include +#include +#include +#include "Extension.h" struct EditorState; struct IPlayer; @@ -12,7 +14,6 @@ struct Mixer; struct MainEditor; struct Gamepad; struct Renderer; -struct Extension; struct UIComponentFactory; class Prototracker { @@ -26,7 +27,7 @@ class Prototracker { Gamepad *mGamepad; UIComponentFactory *mUIComponentFactory; - std::vector mExtensions; + std::map mExtensions; Uint32 mPreviousTick; bool mReady; @@ -39,13 +40,30 @@ class Prototracker { ~Prototracker(); /** - * Register extension, this class will destruct the passed extension object. + * Register extension/dependency. */ - void registerExtension(Extension *extension); + template + T& loadExtension(); bool init(); void deinit(); bool handleEvents(); std::string getSongBase64() const; }; + + +template +T& Prototracker::loadExtension() { + auto typeName = typeid(T).name(); + auto foundExtension = mExtensions.find(typeName); + + if (foundExtension != mExtensions.end()) + { + return static_cast(*foundExtension->second); + } + + auto extension = new T(); + mExtensions[typeName] = extension; + return *extension; +} From fcb5da925367067abed2c02440a40a175b895c58 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Wed, 23 Jan 2019 21:00:53 +0200 Subject: [PATCH 07/14] Unify makefiles --- Makefile | 7 ++++++- Makefile.chip8 | 2 -- Makefile.emscripten | 2 -- Makefile.linux | 2 -- Makefile.mingw | 2 -- 5 files changed, 6 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index e0082a7..c739ac2 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,16 @@ PLAT=none PLATS=mingw linux chip8 emscripten +EXTENSIONS=BaseExtension + +export SRC=src/*.cpp $(foreach ext,$(EXTENSIONS),src/$(ext)/*.cpp) +export SRC_H=src/*.h $(foreach ext,$(EXTENSIONS),src/$(ext)/*.h) + all: $(PLAT) $(PLATS) clean: $(MAKE) -f Makefile.$@ - + none: @echo Build with 'make PLATFORM'. The supported platforms are: @echo $(PLATS) diff --git a/Makefile.chip8 b/Makefile.chip8 index 5d2c766..5d44a8f 100644 --- a/Makefile.chip8 +++ b/Makefile.chip8 @@ -4,8 +4,6 @@ # to other optimizations. Fullscreen mode is forced. OUTPUT=prototracker -SRC=src/*.cpp src/BaseExtension/*.cpp -SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT): $(SRC) $(SRC_H) g++ -Wformat -s -std=c++11 -o $@ $(SRC) -lSDL2_image -lSDL2main `sdl2-config --cflags --libs` -march=armv7-a -mtune=cortex-a7 -ffast-math -Ofast -DSCALE=1 -DOVERSAMPLE=0 -DFULLSCREEN=1 -DSAMPLERATE=22050 diff --git a/Makefile.emscripten b/Makefile.emscripten index 1fbdab5..cc4ad50 100644 --- a/Makefile.emscripten +++ b/Makefile.emscripten @@ -5,8 +5,6 @@ # command line. OUTPUT=prototracker -SRC=src/*.cpp src/BaseExtension/*.cpp -SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT).html: $(SRC) $(SRC_H) emcc -std=c++11 --preload-file assets -O3 -s NO_EXIT_RUNTIME=0 \ diff --git a/Makefile.linux b/Makefile.linux index 2b1ee3e..a016242 100644 --- a/Makefile.linux +++ b/Makefile.linux @@ -1,6 +1,4 @@ OUTPUT=prototracker -SRC=src/*.cpp src/BaseExtension/*.cpp -SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT): $(SRC) $(SRC_H) g++ -O3 -Wformat -std=c++11 -o $@ $(SRC) -lSDL2_image -lSDL2main `sdl2-config --cflags --libs` -s -DSCALE=2 diff --git a/Makefile.mingw b/Makefile.mingw index 273294a..01c229d 100644 --- a/Makefile.mingw +++ b/Makefile.mingw @@ -1,6 +1,4 @@ OUTPUT=prototracker.exe -SRC=src/*.cpp src/BaseExtension/*.cpp -SRC_H=src/*.h src/BaseExtension/*.h $(OUTPUT): $(SRC) $(SRC_H) g++ -DSCALE=2 -O3 -Wformat -std=c++11 -o $@ $(SRC) -lmingw32 -lSDL2_image -lSDL2main -lSDL2 -Ic:/mingw/include/SDL2 -Ic:/tdm-gcc-32/include/SDL2 -s -DENABLE_AUDIO_QUEUE=1 From 196631f1bef93d24d23ef867f36510059dab0611 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Wed, 23 Jan 2019 21:37:03 +0200 Subject: [PATCH 08/14] Added readme on extensions --- EXTENSIONS.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 EXTENSIONS.md diff --git a/EXTENSIONS.md b/EXTENSIONS.md new file mode 100644 index 0000000..eb58b51 --- /dev/null +++ b/EXTENSIONS.md @@ -0,0 +1,21 @@ +# Extensions + +Prototracker can be extended (compile-time) with modular extensions. The exensions can provide new UI components ("Editors") and the main synth engine. The idea is that any forked trackers can pick and choose interesting features while avoiding feature bloat. + +The basic Prototracker functionality is in `src/BaseExtension`, you should replace this with your own if you choose to fork Prototracker and name it descriptively. + +## Dependencies + +`YourExtension::init()` is the place to load new dependency extensions with `Prototracker::loadExtension()` (the Prototracker instance is passed as the first argument). This ensures the extension is loaded and not duplicated (in case multiple extensions depend on the same extension) and returns a reference to the extension. It is up to you what you do with the reference, in most cases you would use it to request a property of the dependency to use in your extension. + +The main `Makefile` needs to be updated to enable extensions. I.e. locate the following line and add your extension to it: + + ... + + EXTENSIONS=BaseExtension YourExtension + + ... + +## Problems + +While changes to the main Prototracker fork can be isolated to extensions, it may be necessary to edit e.g. the TrackState class (to add effects) and those changes will not be automatically included when reusing an extension in another fork. The extension mechanism will still be useful for many features. From 7218c9d8436c84d68595b00c09f278b68560ad13 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Thu, 24 Jan 2019 20:17:12 +0200 Subject: [PATCH 09/14] Updated documentation --- EXTENSIONS.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/EXTENSIONS.md b/EXTENSIONS.md index eb58b51..b022602 100644 --- a/EXTENSIONS.md +++ b/EXTENSIONS.md @@ -4,10 +4,6 @@ Prototracker can be extended (compile-time) with modular extensions. The exensio The basic Prototracker functionality is in `src/BaseExtension`, you should replace this with your own if you choose to fork Prototracker and name it descriptively. -## Dependencies - -`YourExtension::init()` is the place to load new dependency extensions with `Prototracker::loadExtension()` (the Prototracker instance is passed as the first argument). This ensures the extension is loaded and not duplicated (in case multiple extensions depend on the same extension) and returns a reference to the extension. It is up to you what you do with the reference, in most cases you would use it to request a property of the dependency to use in your extension. - The main `Makefile` needs to be updated to enable extensions. I.e. locate the following line and add your extension to it: ... @@ -16,6 +12,19 @@ The main `Makefile` needs to be updated to enable extensions. I.e. locate the fo ... +## Extension lifecycle + + 1. `Extension::Extension()` will be called when a new extension is loaded by `Prototracker::loadExtension()` and if it was not already constructed by earlier loads. + 2. `Extension::init()` will be called some time after `Prototracker::loadExtension()` has been called. If you add a dependency in `Extension::init()`, it is ensured that it has been constructed but it is not ensured the init method for the extension has been called. You should only store the reference to the dependency here and use it later in the other lifecycle methods. + 3. `Extension::registerSynth()` will be called if an extension hasn't registered a synth yet - not a problem as only one extension should provide a synth. + 4. `Extension::registerUIComponents()` will be called. + 5. `Extension::registerSectionListeners()` will be called. + 6. `Extension::deinit()` (and `Extension::~Extension()`) will be called when Prototracker is shut down. + +## Dependencies + +`Extension::init()` is the place to load new dependency extensions with `Prototracker::loadExtension()` (the Prototracker instance is passed as the first argument to `Extension::init()`). This makes sure the dependency is loaded and not duplicated (in case multiple extensions depend on the same extension) and returns a reference to the extension. Please take care of the fact the dependency has been loaded but is not necessarily initialized at this point. + ## Problems -While changes to the main Prototracker fork can be isolated to extensions, it may be necessary to edit e.g. the TrackState class (to add effects) and those changes will not be automatically included when reusing an extension in another fork. The extension mechanism will still be useful for many features. +While many changes to the main Prototracker fork can be isolated to extensions, it may be necessary to edit e.g. the TrackState class (to add effects) and those changes will not be automatically included when reusing an extension in another fork. The extension mechanism will still be useful for many features. From 99d2d57ac2919e99f70a75650ab2e68220b42bec Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Fri, 25 Jan 2019 16:51:28 +0200 Subject: [PATCH 10/14] Updated extension doc --- EXTENSIONS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXTENSIONS.md b/EXTENSIONS.md index b022602..538b71c 100644 --- a/EXTENSIONS.md +++ b/EXTENSIONS.md @@ -14,7 +14,7 @@ The main `Makefile` needs to be updated to enable extensions. I.e. locate the fo ## Extension lifecycle - 1. `Extension::Extension()` will be called when a new extension is loaded by `Prototracker::loadExtension()` and if it was not already constructed by earlier loads. + 1. `Extension::Extension()` will be called when a new extension is loaded by `Prototracker::loadExtension()` and if it was not already constructed by earlier loads. 2. `Extension::init()` will be called some time after `Prototracker::loadExtension()` has been called. If you add a dependency in `Extension::init()`, it is ensured that it has been constructed but it is not ensured the init method for the extension has been called. You should only store the reference to the dependency here and use it later in the other lifecycle methods. 3. `Extension::registerSynth()` will be called if an extension hasn't registered a synth yet - not a problem as only one extension should provide a synth. 4. `Extension::registerUIComponents()` will be called. From f1e65f81991324918bdc915e18d31daac1602056 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Fri, 25 Jan 2019 17:21:32 +0200 Subject: [PATCH 11/14] Updated error message if no synth registered --- src/Prototracker.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Prototracker.cpp b/src/Prototracker.cpp index 6cdcc2e..a480e48 100644 --- a/src/Prototracker.cpp +++ b/src/Prototracker.cpp @@ -56,11 +56,9 @@ bool Prototracker::init() } } - // Use default synth - if (mSynth == NULL) { - debug("No synth set"); + debug("No synth registered by any extension"); } mMixer = new Mixer(*mPlayer, *mSynth); From 40176586a7f23098d81290c00d700f34873bb04b Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Sat, 26 Jan 2019 12:19:46 +0200 Subject: [PATCH 12/14] Refactor editor state values definition --- src/EditorState.cpp | 39 +++++++++++++++++++++------------------ src/EditorState.h | 11 ++++++++--- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/EditorState.cpp b/src/EditorState.cpp index d4f07ce..e6eb540 100644 --- a/src/EditorState.cpp +++ b/src/EditorState.cpp @@ -21,9 +21,12 @@ FileSection * EditorState::pack() { FileSection * state = FileSection::createSection("STAT"); state->writeByte(0); - state->writeDword(macro); - state->writeDword(octave); - state->writeDword(editMode); + + #define EDITOR_STATE_VALUE(name) \ + state->writeDword(name); + EDITOR_STATE_VALUES + #undef EDITOR_STATE_VALUE + state->writeDword(followPlayPosition); /*state->writeDword(copyBuffer.getLength()); @@ -59,20 +62,17 @@ bool EditorState::unpack(const FileSection& section) if (version == FileSection::invalidRead) return false; - unsigned int macroNr = section.readDword(offset); + unsigned int macroNr = section.readDword(offset); if (macroNr == FileSection::invalidRead) return false; - unsigned int octaveNr = section.readDword(offset); - - if (octaveNr == FileSection::invalidRead) - return false; - - unsigned int editModeNr = section.readDword(offset); - - if (editModeNr == FileSection::invalidRead) - return false; + #define EDITOR_STATE_VALUE(name) \ + name = section.readDword(offset); \ + if (name == FileSection::invalidRead) \ + return false; \ + EDITOR_STATE_VALUES + #undef EDITOR_STATE_VALUE unsigned int followNr = section.readDword(offset); @@ -133,9 +133,6 @@ bool EditorState::unpack(const FileSection& section) if (!tempAudioDevice) return false; - macro = macroNr; - octave = octaveNr; - editMode = editModeNr; followPlayPosition = followNr; audioDevice = tempAudioDevice; @@ -222,8 +219,14 @@ void EditorState::reset() followPlayPosition = true; - macro = 0; + // Default all editor state values to zero, override below if needed + + #define EDITOR_STATE_VALUE(name) \ + name = 0; + EDITOR_STATE_VALUES + #undef EDITOR_STATE_VALUE + + // Override octave default octave = 4; - editMode = 0; audioDevice = ""; } diff --git a/src/EditorState.h b/src/EditorState.h index 8733cd9..004aa8c 100644 --- a/src/EditorState.h +++ b/src/EditorState.h @@ -21,11 +21,16 @@ struct TrackEditorState bool unpack(const FileSection& section); }; +#define EDITOR_STATE_VALUES \ + EDITOR_STATE_VALUE(macro) \ + EDITOR_STATE_VALUE(octave) \ + EDITOR_STATE_VALUE(editMode) + struct EditorState { - Value macro; - Value octave; - Value editMode; + #define EDITOR_STATE_VALUE(name) Value name; + EDITOR_STATE_VALUES + #undef EDITOR_STATE_VALUE TrackEditorState sequenceEditor; TrackEditorState patternEditor; From 5324fb864ef898a705f1e97dbb71359556377c43 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Sat, 26 Jan 2019 16:33:45 +0200 Subject: [PATCH 13/14] EDITOR_STATE_VAUES macro was not expanded --- src/EditorState.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorState.cpp b/src/EditorState.cpp index e6eb540..eec953f 100644 --- a/src/EditorState.cpp +++ b/src/EditorState.cpp @@ -70,7 +70,7 @@ bool EditorState::unpack(const FileSection& section) #define EDITOR_STATE_VALUE(name) \ name = section.readDword(offset); \ if (name == FileSection::invalidRead) \ - return false; \ + return false; EDITOR_STATE_VALUES #undef EDITOR_STATE_VALUE From 060669b3cebdc217e08ab9faa6d592fd113ff064 Mon Sep 17 00:00:00 2001 From: Tero Lindeman Date: Sat, 26 Jan 2019 20:18:09 +0200 Subject: [PATCH 14/14] Fixed state saving --- src/EditorState.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/EditorState.cpp b/src/EditorState.cpp index eec953f..2872a42 100644 --- a/src/EditorState.cpp +++ b/src/EditorState.cpp @@ -62,11 +62,6 @@ bool EditorState::unpack(const FileSection& section) if (version == FileSection::invalidRead) return false; - unsigned int macroNr = section.readDword(offset); - - if (macroNr == FileSection::invalidRead) - return false; - #define EDITOR_STATE_VALUE(name) \ name = section.readDword(offset); \ if (name == FileSection::invalidRead) \