Skip to content

Commit

Permalink
Dev audio rework (#8)
Browse files Browse the repository at this point in the history
* rework of audio handling
* source editor upgrade with dynamic line gutter width for large source files
  • Loading branch information
gulrak authored Dec 8, 2023
1 parent 1138a3e commit 0c61981
Show file tree
Hide file tree
Showing 24 changed files with 218 additions and 98 deletions.
112 changes: 57 additions & 55 deletions src/cadmium.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,11 @@ class Cadmium : public emu::Chip8EmuHostEx
, _screenHeight(MIN_SCREEN_HEIGHT)
{
SetTraceLogCallback(LogHandler);

#ifdef WITH_FLAG_COCOA_GRAPHICS_SWITCHING
#ifdef RESIZABLE_GUI
SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_COCOA_GRAPHICS_SWITCHING);
#else
SetConfigFlags(FLAG_COCOA_GRAPHICS_SWITCHING/*|FLAG_VSYNC_HINT*/);
//SetConfigFlags(FLAG_COCOA_GRAPHICS_SWITCHING/*|FLAG_VSYNC_HINT*/);
#endif
#else
#ifdef RESIZABLE_GUI
Expand Down Expand Up @@ -632,7 +631,12 @@ class Cadmium : public emu::Chip8EmuHostEx
*/
#ifdef PLATFORM_WEB
JsClipboard_AddJsHook();
#else
_volume = _volumeSlider = _cfg.volume;
#endif
if(_volume > 1.0f)
_volume = _volumeSlider = 1.0f;
SetMasterVolume(_volume);
}

~Cadmium() override
Expand Down Expand Up @@ -706,40 +710,15 @@ class Cadmium : public emu::Chip8EmuHostEx
void renderAudio(int16_t *samples, unsigned int frames)
{
std::scoped_lock lock(_audioMutex);
_audioCallbackAvgFrames = _audioCallbackAvgFrames ? (_audioCallbackAvgFrames + frames)/2 : frames;
if(_chipEmu) {
if(_options.behaviorBase == emu::Chip8EmulatorOptions::eMEGACHIP) {
while(frames--) {
*samples++ = ((int16_t)_chipEmu->getNextMCSample() - 128) * 256;
}
return;
}
else {
auto st = _chipEmu->soundTimer();
if(st && _chipEmu->getExecMode() == emu::GenericCpu::eRUNNING) {
auto samplesLeftToPlay = std::min(st * (44100 / 60) / g_frameBoost, (int)frames);
float phase = _chipEmu->getAudioPhase();
if (!_options.optXOChipSound) {
const float step = _chipEmu->getAudioFrequency() / 44100;
for (int i = 0; i < samplesLeftToPlay; ++i, --frames) {
*samples++ = (phase > 0.5f) ? 16384 : -16384;
phase = std::fmod(phase + step, 1.0f);
}
_chipEmu->setAudioPhase(phase);
}
else {
auto len = _audioBuffer.read(samples, frames);
frames -= len;
if (frames > 0) {
//TraceLog(LOG_WARNING, "AudioBuffer underrun: %d frames", frames);
auto step = 4000 * std::pow(2.0f, (float(_chipEmu->getXOPitch()) - 64) / 48.0f) / 128 / 44100;
for (; frames > 0; --frames) {
auto pos = int(std::clamp(phase * 128.0f, 0.0f, 127.0f));
*samples++ = _chipEmu->getXOAudioPattern()[pos >> 3] & (1 << (7 - (pos & 7))) ? 16384 : -16384;
phase = std::fmod(phase + step, 1.0f);
}
_chipEmu->setAudioPhase(phase);
}
}
if(_chipEmu->getExecMode() == emu::GenericCpu::eRUNNING) {
auto len = _audioBuffer.read(samples, frames);
frames -= len;
samples += len;
if(frames) {
_chipEmu->renderAudio(samples, frames, 44100);
frames = 0;
}
}
}
Expand All @@ -748,26 +727,22 @@ class Cadmium : public emu::Chip8EmuHostEx
}
}

void pushAudio(float deltaT = 1.0f/60)
void pushAudio(int frames)
{
static int16_t sampleBuffer[44100];
auto st = _chipEmu->soundTimer();
if(_chipEmu->getExecMode() == emu::IChip8Emulator::eRUNNING && st && _options.optXOChipSound) {
auto samples = int(44100 * deltaT + 0.75f);
if(samples > 44100) samples = 44100;
auto step = 4000 * std::pow(2.0f, (float(_chipEmu->getXOPitch()) - 64) / 48.0f) / 128 / 44100;
float phase = st ? _chipEmu->getAudioPhase() : 0.0f;
auto* dest = sampleBuffer;
for (int i = 0; i < samples; ++i) {
auto pos = int(std::clamp(phase * 128.0f, 0.0f, 127.0f));
*dest++ = _chipEmu->getXOAudioPattern()[pos >> 3] & (1 << (7 - (pos & 7))) ? 16384 : -16384;
phase = std::fmod(phase + step, 1.0f);
}
_audioBuffer.write(sampleBuffer, samples);
_chipEmu->setAudioPhase(phase);
if(_chipEmu->getExecMode() == emu::IChip8Emulator::eRUNNING) {
//if(_audioBuffer.dataAvailable() < _audioCallbackAvgFrames) ++frames;
if(frames > _audioBuffer.spaceAvailable()) frames = _audioBuffer.spaceAvailable();
_chipEmu->renderAudio(sampleBuffer, frames, 44100);
_audioBuffer.write(sampleBuffer, frames);
}
}

void vblank() override
{
pushAudio(44100 / _options.frameRate);
}

uint8_t getKeyPressed() override
{
static uint32_t instruction = 0;
Expand Down Expand Up @@ -1066,7 +1041,6 @@ class Cadmium : public emu::Chip8EmuHostEx
_chipEmu->tick(getInstrPerFrame());
g_soundTimer.store(_chipEmu->soundTimer());
}
pushAudio(deltaT);
}
else {
static int cntx = 0;
Expand All @@ -1079,7 +1053,6 @@ class Cadmium : public emu::Chip8EmuHostEx
else {
excessTime = 0;
}
pushAudio(deltaT);
}

if(_chipEmu->needsScreenUpdate())
Expand Down Expand Up @@ -1214,6 +1187,7 @@ class Cadmium : public emu::Chip8EmuHostEx
static uint32_t* selectedColor = nullptr;
static std::string colorText;
static uint32_t previousColor{};
static std::chrono::steady_clock::time_point volumeClick{};

#ifdef RESIZABLE_GUI
auto screenScale = std::min(std::clamp(int(GetScreenWidth() / _screenWidth), 1, 8), std::clamp(int(GetScreenHeight() / _screenHeight), 1, 8));
Expand Down Expand Up @@ -1443,7 +1417,7 @@ class Cadmium : public emu::Chip8EmuHostEx
}
}
SetTooltip("RESTART");
int buttonsRight = 6;
int buttonsRight = 7;
#ifdef WITH_EDITOR
++buttonsRight;
#endif
Expand Down Expand Up @@ -1479,6 +1453,9 @@ class Cadmium : public emu::Chip8EmuHostEx
if (iconButton(ICON_GEAR, _mainView == eSETTINGS))
_mainView = eSETTINGS;
SetTooltip("SETTINGS");
if (iconButton(ICON_AUDIO, false))
volumeClick = std::chrono::steady_clock::now();
SetTooltip("VOLUME");

static Vector2 versionSize = MeasureTextEx(GuiGetFont(), "v" CADMIUM_VERSION, 8, 0);
DrawTextEx(GuiGetFont(), "v" CADMIUM_VERSION, {spacePos.x + (spaceWidth - versionSize.x) / 2, spacePos.y + 6}, 8, 0, WHITE);
Expand Down Expand Up @@ -1551,11 +1528,11 @@ class Cadmium : public emu::Chip8EmuHostEx
SetRowHeight(20);
if(!_chipEmu->isGenericEmulation() || _options.behaviorBase == emu::Chip8EmulatorOptions::eCHIP8TE)
GuiDisable();
Spinner("Instructions per frame", &_options.instructionsPerFrame, 0, 500000);
Spinner("Instructions per frame", &_options.instructionsPerFrame, 0, 1000000);
Spinner("Frame rate", &_options.frameRate, 10, 120);
if(!_chipEmu->isGenericEmulation() || _options.behaviorBase == emu::Chip8EmulatorOptions::eCHIP8TE)
GuiEnable();
if (!_options.instructionsPerFrame) {
if (true/*!_options.instructionsPerFrame*/) {
static int _fb1{1};
GuiDisable();
Spinner("Frame boost", &_fb1, 1, 100000);
Expand Down Expand Up @@ -1882,6 +1859,27 @@ class Cadmium : public emu::Chip8EmuHostEx
EndColumns();
EndWindowBox();
}
if(IsKeyDown(KEY_ESCAPE))
volumeClick = std::chrono::steady_clock::time_point{};
if(volumeClick != std::chrono::steady_clock::time_point{}) {
if (std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - volumeClick).count() < 2) {
Rectangle bounds{430.0, 21.0f, 80.0f, 14.0f};
DrawRectangleRec({bounds.x-56, bounds.y-2, bounds.width+58, bounds.height+4}, {0,0,0,128});
GuiSliderBar(bounds, "Volume: ", "", &_volumeSlider, 0.0001f, 1.0f);
if (_volumeSlider != _volume)
SetMasterVolume(_volumeSlider);
if (CheckCollisionPointRec(GetMousePosition(), bounds)) {
volumeClick = std::chrono::steady_clock::now();
}
}
else {
if (_volumeSlider != _volume) {
_volume = _volumeSlider;
_cfg.volume = _volume;
saveConfig();
}
}
}
EndGui();
}
if(_chipEmu->getExecMode() != ExecMode::ePAUSED) {
Expand Down Expand Up @@ -2291,12 +2289,15 @@ class Cadmium : public emu::Chip8EmuHostEx
std::string _screenShotSha1sum;
RenderTexture _keyboardOverlay{};
CircularBuffer<int16_t,1> _audioBuffer;
int64_t _audioGaps{};
bool _shouldClose{false};
bool _showKeyMap{false};
int _screenWidth{};
int _screenHeight{};
RenderTexture _renderTexture{};
AudioStream _audioStream{};
float _volumeSlider{0.5f};
float _volume{0.5f};
SMA<60,uint64_t> _ipfAverage;
SMA<120,uint32_t> _frameTimeAverage_us;
SMA<120,int> _frameDelta;
Expand All @@ -2311,6 +2312,7 @@ class Cadmium : public emu::Chip8EmuHostEx
int _frameBoost{1};
int _memoryOffset{-1};
int _instructionOffset{-1};
std::atomic_int _audioCallbackAvgFrames{};

//std::string _romName;
//std::vector<uint8_t> _romImage;
Expand Down
2 changes: 1 addition & 1 deletion src/chip8emuhostex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ bool Chip8EmuHostEx::loadRom(const char* filename, bool andRun)
_editor.setText("");
_editor.setFilename("");
#endif
auto fileData = loadFile(filename);
auto fileData = loadFile(filename, Librarian::MAX_ROM_SIZE);
return loadBinary(filename, fileData.data(), fileData.size(), andRun);
//memory[0x1FF] = 3;
}
Expand Down
1 change: 1 addition & 0 deletions src/chip8emuhostex.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class Chip8HeadlessHostEx : public Chip8EmuHostEx
bool isKeyDown(uint8_t key) override { return false; }
const std::array<bool,16>& getKeyStates() const override { static const std::array<bool,16> keys{}; return keys; }
void updateScreen() override {}
void vblank() override {}
void updatePalette(const std::array<uint8_t,16>& palette) override {}
void updatePalette(const std::vector<uint32_t>& palette, size_t offset) override {}
};
Expand Down
2 changes: 2 additions & 0 deletions src/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

void to_json(nlohmann::json& j, const CadmiumConfiguration& cc) {
j = nlohmann::json{
{"volume", cc.volume},
{"workingDirectory", cc.workingDirectory},
{"databaseDirectory", cc.databaseDirectory},
{"emuOptions", cc.emuOptions},
Expand All @@ -40,6 +41,7 @@ void to_json(nlohmann::json& j, const CadmiumConfiguration& cc) {
void from_json(const nlohmann::json& j, CadmiumConfiguration& cc) {
j.at("workingDirectory").get_to(cc.workingDirectory);
cc.databaseDirectory = j.value("databaseDirectory", "");
cc.volume = j.value("volume", 0.5f);
try {
j.at("emuOptions").get_to(cc.emuOptions);
}
Expand Down
1 change: 1 addition & 0 deletions src/configuration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@

struct CadmiumConfiguration
{
float volume;
std::string workingDirectory;
std::string databaseDirectory;
emu::Chip8EmulatorOptions emuOptions;
Expand Down
22 changes: 14 additions & 8 deletions src/editor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ void Editor::update()
setFocus();
if(_mouseDownInText || IsMouseButtonPressed(0)) {
auto clickPos = GetMousePosition();
auto cx = (clickPos.x - _textArea.x - 6 * COLUMN_WIDTH) / COLUMN_WIDTH;
auto cx = (clickPos.x - _textArea.x - _lineNumberWidth) / COLUMN_WIDTH;
auto cy = (clickPos.y - _textArea.y - 4) / LINE_SIZE;
if (cx > 0 && cy + _tosLine >= 0) {
_cursorX = cx + _losCol;
Expand Down Expand Up @@ -423,11 +423,14 @@ void Editor::update()
}
}
}
_lineNumberCols = _lines.empty() ? 5 : std::max(3,int(std::log10(_lines.size())) + 3);
_lineNumberWidth = _lineNumberCols * COLUMN_WIDTH;
}

void Editor::recompile()
{
try {
auto start = std::chrono::steady_clock::now();
_compiler.reset();
if(_text.empty() || _text.back() != '\n') {
auto text = _text + '\n';
Expand All @@ -436,6 +439,8 @@ void Editor::recompile()
else {
_compiler.compile(_filename, _text.data(), _text.data() + _text.size() + 1);
}
auto time_ms = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - start).count();
TraceLog(LOG_INFO, "Recompilation took %dms", (int)time_ms);
}
catch(std::exception& ex)
{}
Expand All @@ -457,17 +462,18 @@ void Editor::draw(Font& font, Rectangle rect)
_textArea = {_totalArea.x, _totalArea.y + _toolArea.height, _totalArea.width, _totalArea.height - _toolArea.height - _messageArea.height};
float ypos = _textArea.y - 4;
_visibleLines = uint32_t((_textArea.height - 6) / LINE_SIZE);
_visibleCols = uint32_t(_textArea.width - 6*COLUMN_WIDTH - 6) / COLUMN_WIDTH;
_visibleCols = uint32_t(_textArea.width - _lineNumberWidth - 6) / COLUMN_WIDTH;
_scrollPos = {-(float)_losCol * COLUMN_WIDTH, -(float)_tosLine * LINE_SIZE};
gui::SetStyle(DEFAULT, BORDER_WIDTH, 0);
gui::BeginScrollPanel(_textArea.height, {0,0,std::max(_textArea.width, (float)(_longestLineSize+8) * COLUMN_WIDTH), (float)std::max((uint32_t)_textArea.height, uint32_t(_lines.size()+1)*LINE_SIZE)}, &_scrollPos);
gui::SetStyle(DEFAULT, BORDER_WIDTH, 1);
//gui::Space(rect.height -50);
DrawRectangle(5*COLUMN_WIDTH, _textArea.y, 1, _textArea.height, GetColor(0x2f7486ff));
DrawRectangle(_lineNumberWidth - COLUMN_WIDTH/2, _textArea.y, 1, _textArea.height, GetColor(0x2f7486ff));
std::string lineNumberFormat = fmt::format("%{}d", _lineNumberCols - 1);
while(lineNumber < int(_lines.size()) && ypos < _textArea.y + _textArea.height) {
if(lineNumber >= 0) {
DrawTextEx(font, TextFormat("%4d", lineNumber + 1), {_textArea.x, ypos}, 8, 0, LIGHTGRAY);
drawTextLine(font, lineStart(lineNumber), lineEnd(lineNumber), {_textArea.x + 6 * COLUMN_WIDTH, ypos}, _textArea.width - 6 * COLUMN_WIDTH, _losCol);
DrawTextEx(font, TextFormat(lineNumberFormat.c_str(), lineNumber + 1), {_textArea.x, ypos}, 8, 0, LIGHTGRAY);
drawTextLine(font, lineStart(lineNumber), lineEnd(lineNumber), {_textArea.x + _lineNumberWidth, ypos}, _textArea.width - _lineNumberWidth, _losCol);
}
++lineNumber;
ypos += LINE_SIZE;
Expand All @@ -478,8 +484,8 @@ void Editor::draw(Font& font, Rectangle rect)
if(hasFocus() && _blinkTimer >= BLINK_RATE/2) {
auto cx = (_cursorX - _losCol) * COLUMN_WIDTH;
auto cy = (_cursorY - _tosLine) * LINE_SIZE + LINE_SIZE - 4;
if(cx >= 0 && cx < _textArea.width - 6*COLUMN_WIDTH - 3 && cy >= 0 && cy + 8 < _textArea.height)
DrawRectangle(_textArea.x + 6*COLUMN_WIDTH + cx, _textArea.y + cy - 2, 2, LINE_SIZE, WHITE);
if(cx >= 0 && cx < _textArea.width - _lineNumberWidth - 3 && cy >= 0 && cy + 8 < _textArea.height)
DrawRectangle(_textArea.x + _lineNumberWidth + cx, _textArea.y + cy - 2, 2, LINE_SIZE, WHITE);
}

auto handle = verticalScrollHandle();
Expand Down Expand Up @@ -918,4 +924,4 @@ void Editor::updateFindResults()
}
if(_findCurrentResult > _findResults)
_findCurrentResult = _findResults;
}
}
2 changes: 2 additions & 0 deletions src/editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ class Editor
int _cursorY{0};
uint32_t _visibleLines{0};
uint32_t _visibleCols{0};
int _lineNumberWidth{6*COLUMN_WIDTH};
uint32_t _lineNumberCols{6};
uint32_t _selectionStart{0};
uint32_t _selectionEnd{0};
uint32_t _editId{0};
Expand Down
Loading

0 comments on commit 0c61981

Please sign in to comment.