Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions EXTENSIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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.

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

...

## Extension lifecycle

1. `Extension::Extension()` will be called when a new extension is loaded by `Prototracker::loadExtension<Extension>()` and if it was not already constructed by earlier loads.
2. `Extension::init()` will be called some time after `Prototracker::loadExtension<Extension>()` 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<T>()` (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 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.
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -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)
5 changes: 1 addition & 4 deletions Makefile.chip8
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
# to other optimizations. Fullscreen mode is forced.

OUTPUT=prototracker
SRC=src/*.cpp

$(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


5 changes: 2 additions & 3 deletions Makefile.emscripten
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
# command line.

OUTPUT=prototracker
SRC=src/*.cpp

$(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
3 changes: 1 addition & 2 deletions Makefile.linux
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
OUTPUT=prototracker
SRC=src/*.cpp

$(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
3 changes: 1 addition & 2 deletions Makefile.mingw
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
OUTPUT=prototracker.exe
SRC=src/*.cpp

$(OUTPUT): $(SRC) src/*.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
26 changes: 26 additions & 0 deletions src/BaseExtension/BaseExtension.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "BaseExtension.h"
#include "../UIComponentFactory.h"
#include "WaveView.h"

void BaseExtension::init(Prototracker& prototracker, IPlayer& player, const Song& song)
{
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()); });
}
16 changes: 16 additions & 0 deletions src/BaseExtension/BaseExtension.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "../Extension.h"
#include "Synth.h"

struct IPlayer;

class BaseExtension: public Extension
{
Synth *mSynth;
public:
virtual void init(Prototracker& prototracker, IPlayer& player, const Song& song);
virtual void deinit();
virtual ISynth* registerSynth();
virtual void registerUIComponents(UIComponentFactory& factory, EditorState& editorState);
};
6 changes: 3 additions & 3 deletions src/Oscillator.cpp → src/BaseExtension/Oscillator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
#include <math.h>
#include <cstdio>
#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)
Expand Down
10 changes: 5 additions & 5 deletions src/Oscillator.h → src/BaseExtension/Oscillator.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "IOscillator.h"
#include "../IOscillator.h"

struct WaveStore;

Expand All @@ -11,27 +11,27 @@ 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;
static const int oscillatorOversample = OVERSAMPLE;

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);
Expand Down
File renamed without changes.
File renamed without changes.
22 changes: 11 additions & 11 deletions src/Synth.cpp → src/BaseExtension/Synth.cpp
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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.

*/
}

Expand Down
8 changes: 4 additions & 4 deletions src/Synth.h → src/BaseExtension/Synth.h
Original file line number Diff line number Diff line change
@@ -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;
};
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions src/WaveView.cpp → src/BaseExtension/WaveView.cpp
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 1 addition & 3 deletions src/WaveView.h → src/BaseExtension/WaveView.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include "Editor.h"
#include "../Editor.h"

struct WaveStore;

Expand All @@ -14,5 +14,3 @@ class WaveView: public Editor
virtual void onDraw(Renderer& renderer, const SDL_Rect& area);
virtual bool onEvent(SDL_Event& event);
};


42 changes: 20 additions & 22 deletions src/EditorState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -59,20 +62,12 @@ bool EditorState::unpack(const FileSection& section)
if (version == FileSection::invalidRead)
return false;

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);

Expand Down Expand Up @@ -133,9 +128,6 @@ bool EditorState::unpack(const FileSection& section)
if (!tempAudioDevice)
return false;

macro = macroNr;
octave = octaveNr;
editMode = editModeNr;
followPlayPosition = followNr;
audioDevice = tempAudioDevice;

Expand Down Expand Up @@ -222,8 +214,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 = "";
}
11 changes: 8 additions & 3 deletions src/EditorState.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading