feat(pocketdungeon): intro GOAL+CONTROLS left icons right + 2x2 tutorial levels
Main / Build (BookPlayer) (push) Has been cancelled
Main / Build (Brainfuck) (push) Has been cancelled
Main / Build (Breakout) (push) Has been cancelled
Main / Build (Calculator) (push) Has been cancelled
Main / Build (Diceware) (push) Has been cancelled
Main / Build (EpubReader) (push) Has been cancelled
Main / Build (GPIO) (push) Has been cancelled
Main / Build (GraphicsDemo) (push) Has been cancelled
Main / Build (HelloWorld) (push) Has been cancelled
Main / Build (M5UnitTest) (push) Has been cancelled
Main / Build (Magic8Ball) (push) Has been cancelled
Main / Build (MediaKeys) (push) Has been cancelled
Main / Build (MystifyDemo) (push) Has been cancelled
Main / Build (SerialConsole) (push) Has been cancelled
Main / Build (Snake) (push) Has been cancelled
Main / Build (TamaTac) (push) Has been cancelled
Main / Build (TodoList) (push) Has been cancelled
Main / Build (TwoEleven) (push) Has been cancelled
Main / Bundle (push) Has been cancelled

- Intro redesign QVGA: left scrollable column 2 cards GOAL+CONTROLS only,
  right 44px rail small icons Play 38 accent + Exit 30 muted (fixes cramped)
- Tutorial by play: floor0 TUTORIAL_ROWS=2 player+stairs, floor1 bat+gold+stairs,
  real game from floor2 infinite with displayFloor mapping
- Model: renderRows, isTutorial(), tutorialId(), clearAllTiles(), generateTutorialFloor
- Renderer: respects renderRows HIDDEN, board height 2 rows centered, TUT messages
- Game: reset tutorials=true, skip moveMonsters in tutorial, bests only real floors
- Keep audio-stream migration: SDK 0.8.0-dev IDF5.5, CMake GLOB SfxEngine, SfxEngine audio-stream
- No unexported LVGL: no max_width/scrollbar_mode -> 0 missing symbols

Installed 192.168.68.134 verified screenshot 6.1K airy left+right
This commit is contained in:
Adolfo
2026-07-19 23:05:43 -04:00
parent b2e9046438
commit 2a45c98fb3
9 changed files with 457 additions and 130 deletions
+53 -36
View File
@@ -9,7 +9,8 @@
#include "SfxEngine.h"
#include "SfxDefinitions.h"
#include <tactility/drivers/i2s_controller.h>
#include <tactility/device.h>
#include <tactility/drivers/audio_stream.h>
#include <cmath>
#include <cstring>
#include "esp_log.h"
@@ -424,7 +425,7 @@ void SfxEngine::audioTaskFunc(void* param) {
size_t written;
QueueMsg msg;
ESP_LOGI(TAG, "Audio task started");
ESP_LOGI(TAG, "Audio task started (audio-stream)");
while (self->running_) {
// Process queued messages (non-blocking)
@@ -463,19 +464,26 @@ void SfxEngine::audioTaskFunc(void* param) {
// Fill audio buffer (member buffer to avoid stack pressure)
self->fillStereoBuffer(self->audioBuffer_, BUFFER_SAMPLES);
// Write to I2S
error_t error = i2s_controller_write(self->i2sDevice_, self->audioBuffer_,
sizeof(self->audioBuffer_), &written, pdMS_TO_TICKS(100));
// Write via audio-stream (resampled to native codec rate, e.g. 44100)
if (self->streamHandle_ == nullptr) {
vTaskDelay(pdMS_TO_TICKS(10));
continue;
}
error_t error = audio_stream_write(self->streamHandle_, self->audioBuffer_,
sizeof(self->audioBuffer_), &written, pdMS_TO_TICKS(200));
if (error != ERROR_NONE) {
ESP_LOGE(TAG, "I2S write error");
ESP_LOGE(TAG, "audio_stream_write error %d", error);
self->running_ = false;
break;
}
}
// Flush silence
memset(self->audioBuffer_, 0, sizeof(self->audioBuffer_));
i2s_controller_write(self->i2sDevice_, self->audioBuffer_, sizeof(self->audioBuffer_), &written, pdMS_TO_TICKS(50));
// Flush silence to avoid pop
if (self->streamHandle_ != nullptr) {
memset(self->audioBuffer_, 0, sizeof(self->audioBuffer_));
size_t dummy = 0;
audio_stream_write(self->streamHandle_, self->audioBuffer_, sizeof(self->audioBuffer_), &dummy, pdMS_TO_TICKS(100));
}
ESP_LOGI(TAG, "Audio task exiting");
@@ -494,33 +502,38 @@ void SfxEngine::audioTaskFunc(void* param) {
bool SfxEngine::start() {
if (running_) return true;
// Find I2S device
i2sDevice_ = nullptr;
device_for_each_of_type(&I2S_CONTROLLER_TYPE, &i2sDevice_, [](Device* device, void* context) {
if (!device_is_ready(device)) return true;
Device** devicePtr = static_cast<Device**>(context);
*devicePtr = device;
return false;
});
// Find audio-stream device (new audio provision)
streamDevice_ = nullptr;
streamHandle_ = nullptr;
if (i2sDevice_ == nullptr) {
ESP_LOGW(TAG, "No I2S device found");
streamDevice_ = device_find_by_name("audio-stream");
if (streamDevice_ == nullptr) {
// Fallback: find first device of AUDIO_STREAM_TYPE
device_for_each_of_type(&AUDIO_STREAM_TYPE, &streamDevice_, [](Device* device, void* context) {
if (!device_is_ready(device)) return true;
Device** devPtr = static_cast<Device**>(context);
*devPtr = device;
return false;
});
}
if (streamDevice_ == nullptr) {
ESP_LOGW(TAG, "No audio-stream device found - audio provision missing");
return false;
}
// Configure I2S
I2sConfig config = {
.communication_format = I2S_FORMAT_STAND_I2S,
// Open output stream with resampling (app wants 16k stereo, codec native is 44100)
struct AudioStreamConfig cfg = {
.sample_rate = SAMPLE_RATE,
.bits_per_sample = 16,
.channel_left = 0,
.channel_right = 0
.channels = 2
};
error_t error = i2s_controller_set_config(i2sDevice_, &config);
error_t error = audio_stream_open_output(streamDevice_, &cfg, &streamHandle_);
if (error != ERROR_NONE) {
ESP_LOGE(TAG, "Failed to configure I2S: %s", error_to_string(error));
i2sDevice_ = nullptr;
ESP_LOGE(TAG, "Failed to open audio-stream: %s (%d)", error_to_string(error), error);
streamDevice_ = nullptr;
streamHandle_ = nullptr;
return false;
}
@@ -528,12 +541,13 @@ bool SfxEngine::start() {
msgQueue_ = xQueueCreate(8, sizeof(QueueMsg));
if (msgQueue_ == nullptr) {
ESP_LOGE(TAG, "Failed to create message queue");
i2s_controller_reset(i2sDevice_);
i2sDevice_ = nullptr;
audio_stream_close(streamHandle_);
streamHandle_ = nullptr;
streamDevice_ = nullptr;
return false;
}
// Start audio task
// Start audio task (needs slightly larger stack for audio_stream write path)
running_ = true;
BaseType_t result = xTaskCreate(audioTaskFunc, "sfxeng", 4096, this, 5, &task_);
if (result != pdPASS) {
@@ -541,12 +555,13 @@ bool SfxEngine::start() {
running_ = false;
vQueueDelete(msgQueue_);
msgQueue_ = nullptr;
i2s_controller_reset(i2sDevice_);
i2sDevice_ = nullptr;
audio_stream_close(streamHandle_);
streamHandle_ = nullptr;
streamDevice_ = nullptr;
return false;
}
ESP_LOGI(TAG, "SfxEngine started (voices=%d, sampleRate=%d)", NUM_VOICES, SAMPLE_RATE);
ESP_LOGI(TAG, "SfxEngine started via audio-stream (voices=%d, sampleRate=%d -> resampled to native)", NUM_VOICES, SAMPLE_RATE);
return true;
}
@@ -575,11 +590,13 @@ void SfxEngine::stop() {
msgQueue_ = nullptr;
}
if (i2sDevice_ != nullptr) {
i2s_controller_reset(i2sDevice_);
i2sDevice_ = nullptr;
if (streamHandle_ != nullptr) {
audio_stream_close(streamHandle_);
streamHandle_ = nullptr;
}
streamDevice_ = nullptr;
ESP_LOGI(TAG, "SfxEngine stopped");
}