Skip to content

Commit

Permalink
Merge pull request #25 from TheSlowGrowth/feature/TSG/saveAndRecall
Browse files Browse the repository at this point in the history
Feature/tsg/save and recall
  • Loading branch information
TheSlowGrowth authored Nov 4, 2021
2 parents e4a0b93 + c806c48 commit 64002e8
Show file tree
Hide file tree
Showing 10 changed files with 745 additions and 16 deletions.
23 changes: 23 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,29 @@
"windows": {
"MIMode": "gdb",
}
},
{
"name": "Standalone Plugin",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/plugin/build",
"environment": [],
"externalConsole": false,
"logging": {
"engineLogging": false
},
"preLaunchTask": "build plugin",

"osx": {
"MIMode": "lldb",
"program": "${workspaceFolder}/plugin/build/TapeLooperPlugin_artefacts/Debug/Standalone/Tape Looper Plugin.app/Contents/MacOS/Tape Looper Plugin",
},
"windows": {
"MIMode": "gdb",
"program": "${workspaceFolder}/plugin/build/TapeLooperPlugin_artefacts/Debug/Standalone/Tape Looper Plugin.exe",
}
}
]
}
4 changes: 3 additions & 1 deletion dsp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ set(HEADER_FILES
src/dsp/Recorder.h
src/dsp/TapeLooper.h
src/dsp/TapeProcessor.h
src/util/Memory.h
)

add_subdirectory(lib/sprout)
Expand All @@ -19,4 +20,5 @@ add_library(${TARGET} INTERFACE)
target_include_directories(${TARGET} INTERFACE
src/
${CMAKE_CURRENT_SOURCE_DIR}/lib/sprout/
)
)
set_property(TARGET tapeLooperDsp PROPERTY CXX_STANDARD 17)
2 changes: 2 additions & 0 deletions dsp/src/dsp/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class Player
return isPlaying_;
}

size_t getLoopLengthInSamples() const { return playbackLength_; }

void process(float paramSpeed,
float speedModulationAmt,
Direction direction,
Expand Down
25 changes: 25 additions & 0 deletions dsp/src/dsp/Recorder.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "DspHelpers.h"
#include "AudioBuffer.h"
#include "../util/Memory.h"

template <size_t samplerate, size_t numChannels>
class Recorder
Expand Down Expand Up @@ -89,6 +90,30 @@ class Recorder
}
}

size_t getSaveAndRecallStorageSize() const
{
return sizeof(uint32_t); // recordingLengthInSamples
}

template <typename StorageType>
bool save(WritableMemory<StorageType>& mem) const
{
const auto recordingLengthInSamples = uint32_t(currentLength_);
return mem.writeItems(recordingLengthInSamples);
}

template <typename StorageType>
bool restore(ReadableMemory<StorageType>& mem)
{
reset();
uint32_t recordingLengthInSamples = 0;
if (!mem.readItems(recordingLengthInSamples))
return false;

currentLength_ = std::min(size_t(recordingLengthInSamples), buffer_.size_);
return true;
}

size_t getCurrentRecordingLength() const { return currentLength_; }
bool isRecording() const { return isRecording_ || isFadingOut_; }

Expand Down
74 changes: 70 additions & 4 deletions dsp/src/dsp/TapeLooper.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,19 @@
#include "TapeProcessor.h"
#include "WowAndFlutter.h"

template <size_t numChannels>
#include "../util/Memory.h"

template <size_t numChannels_>
struct LooperStoragePtr
{
float* data[numChannels];
float* data[numChannels_];
size_t numSamples;
static constexpr size_t numChannels = numChannels_;

size_t getTotalSizeInBytes() const
{
return numChannels * numSamples * sizeof(float);
}
};

template <size_t size, size_t numChannels>
Expand Down Expand Up @@ -44,6 +52,8 @@ class TapeLooper
public:
using ProcessorType = TapeProcessor<sampleRate, numChannels>;
using SpeedModulatorType = WowAndFlutterOscillator<float, sampleRate>;
using PlayerType = Player<ProcessorType, SpeedModulatorType, sampleRate, numChannels>;
using RecorderType = Recorder<sampleRate, numChannels>;

TapeLooper(LooperStoragePtr<numChannels> storageToUse) :
storage_(storageToUse),
Expand Down Expand Up @@ -110,10 +120,66 @@ class TapeLooper
switchState(LooperState::playing);
}

size_t getSaveAndRecallStorageSize() const
{
return recorder_.getSaveAndRecallStorageSize()
+ sizeof(uint8_t) // isPlaying?
+ storage_.getTotalSizeInBytes();
}

/**
* Saves the state of the looper
*/
template <typename StorageType>
bool save(WritableMemory<StorageType>& mem) const
{
if (!recorder_.save(mem))
return false;

// write sample data
for (size_t ch = 0; ch < numChannels; ch++)
{
if (!mem.writeRaw(storage_.data[ch], sizeof(float) * storage_.numSamples))
return false;
}

const uint8_t isPlaying = state_ == LooperState::playing ? 1 : 0;

return mem.writeItems(isPlaying);
}

/**
* Stopps playback or recording and recalls the state of the looper, including
* - the length of the current recording
* - if the looper is currently playing
* - the sample data stored
*/
template <typename StorageType>
bool restore(ReadableMemory<StorageType>& mem)
{
if (!recorder_.restore(mem))
return false;

// read sample data
for (size_t ch = 0; ch < numChannels; ch++)
{
if (!mem.readRaw(storage_.data[ch], sizeof(float) * storage_.numSamples))
return false;
}

uint8_t isPlaying = 0;
mem.readItems(isPlaying);

switchState(isPlaying == 1 ? LooperState::playing : LooperState::stopped);
return true;
}

const LooperStoragePtr<numChannels> getSampleStoragePtr() const { return storage_; }

private:
static constexpr float maxWowAndFlutterAmt_ = 0.0125f;
const LooperStoragePtr<numChannels> storage_;
LooperState state_;
Player<ProcessorType, SpeedModulatorType, sampleRate, numChannels> player_;
Recorder<sampleRate, numChannels> recorder_;
PlayerType player_;
RecorderType recorder_;
};
192 changes: 192 additions & 0 deletions dsp/src/util/Memory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
#pragma once

#include <memory>

/**
* Represents a fixed size chunk of memory to which data can be written.
* Access to the memory is provided by the MemoryProviderType template argument,
* allowing to implement various storage backends, e.g. raw memory, file access, etc.
* @tparam MemoryProviderType A storage provider backend. Must implement the following
* public member functions:
* // returns the maximum number of bytes that can be written
* size_t getAvailableSize() const;
* // writes some data to the memory and advances the write
* // position so that multiple consecutive calls append
* // the data continuously.
* void write(const void* srcData, size_t sizeToWrite);
*/
template <typename MemoryProviderType>
class WritableMemory
{
public:
/**
* @param rawMem Instance of the storage provider backend,
* which is captured by reference (so it must stay alive
* until this WritableMemory is deconstructed).
*/
WritableMemory(MemoryProviderType& rawMem) :
rawMem_(rawMem),
size_(rawMem.getAvailableSize())
{
}

~WritableMemory() {}

/**
* Returns the number of bytes that can still be written
*/
size_t getRemainingSize() const { return size_; }

/**
* Attempts to write to the memory.
* If enough bytes are available, the data is written and true is returned.
* Otherwise, false is returned and the data is not written.
*/
bool writeRaw(const void* src, size_t sizeToWrite)
{
if (size_ < sizeToWrite)
return false;

rawMem_.write(src, sizeToWrite);
size_ -= sizeToWrite;
return true;
}

/**
* Attempts to write a value to the memory.
* If enough bytes are available, the data is written and true is returned.
* Otherwise, false is returned and the data is not written.
*/
template <typename T>
bool writeItem(const T& value)
{
return writeRaw((const void*) &value, sizeof(T));
}

/**
* Attempts to write multiple values to the memory.
* If enough bytes are available to store all values, they're written and true is returned.
* Otherwise, false is returned and none of the values is written.
*/
template <typename... T>
bool writeItems(T const&... values)
{
const auto totalSize = (size_t(0) + ... + sizeof(T));
if (size_ < totalSize)
return false;

return (writeItem(values) && ...);
}

private:
WritableMemory(const WritableMemory&) = delete;
WritableMemory& operator=(const WritableMemory&) = delete;
MemoryProviderType& rawMem_;
size_t size_;
};

/**
* Represents a fixed size chunk of memory from which data can be read.
* Access to the memory is provided by the MemoryProviderType template argument,
* allowing to implement various storage backends, e.g. raw memory, file access, etc.
* @tparam MemoryProviderType A storage provider backend. Must implement the following
* public member functions:
* // returns the maximum number of bytes that can be read
* size_t getAvailableSize() const;
* // reads some data from the memory and advances the read
* // position
* void read(void* dest, size_t sizeToRead);
*/
template <typename MemoryProviderType>
class ReadableMemory
{
public:
/**
* @param rawMem Instance of the storage provider backend,
* which is captured by reference (so it must stay alive
* until this ReadableMemory is deconstructed).
*/
ReadableMemory(MemoryProviderType& rawMem) :
rawMem_(rawMem),
size_(rawMem.getAvailableSize())
{
}
~ReadableMemory() {}

/**
* Returns the number of bytes that can still be read
*/
size_t getRemainingSize() const { return size_; }

/**
* Attempts to read from the memory.
* If enough bytes are available, the data is read and true is returned.
* Otherwise, false is returned and `dest` remains unchanged.
*/
bool readRaw(void* dest, size_t sizeToRead)
{
if (size_ < sizeToRead)
return false;

rawMem_.read(dest, sizeToRead);
size_ -= sizeToRead;
return true;
}

/**
* Attempts to read a value from the memory.
* If enough bytes are available, the data is read and true is returned.
* Otherwise, false is returned and the value is unchanged.
*/
template <typename T>
bool readItem(T& value)
{
return readRaw((void*) &value, sizeof(T));
}

/**
* Attempts to read multiple values from the memory.
* If enough bytes are available to read all values, they're read and true is returned.
* Otherwise, false is returned and all values are unchanged.
*/
template <typename... T>
bool readItems(T&... values)
{
const auto totalSize = (size_t(0) + ... + sizeof(T));
if (size_ < totalSize)
return false;

return (readItem(values) && ...);
}

private:
ReadableMemory(const ReadableMemory&) = delete;
ReadableMemory& operator=(const ReadableMemory&) = delete;
MemoryProviderType& rawMem_;
size_t size_;
};

/**
* A memory provider that allocates a fixed size buffer
* on the Stack. For use with ReadableMemory and WritableMemory
*/
template <size_t size>
struct StackMemoryProvider
{
std::array<uint8_t, size> arr;
uint8_t* ptr = arr.data();

constexpr size_t getAvailableSize() const { return size; }

void write(const void* srcData, size_t sizeToWrite)
{
memcpy(ptr, srcData, sizeToWrite);
ptr += sizeToWrite;
}

void read(void* destData, size_t sizeToRead)
{
memcpy(destData, ptr, sizeToRead);
ptr += sizeToRead;
}
};
Loading

0 comments on commit 64002e8

Please sign in to comment.