diff --git a/sources/Adapters/picoTracker/audio/picoTrackerAudio.cpp b/sources/Adapters/picoTracker/audio/picoTrackerAudio.cpp index 6f02c75dd..4ce0ac4e4 100644 --- a/sources/Adapters/picoTracker/audio/picoTrackerAudio.cpp +++ b/sources/Adapters/picoTracker/audio/picoTrackerAudio.cpp @@ -49,6 +49,14 @@ void picoTrackerAudio::SetMixerVolume(int v) { } } +void picoTrackerAudio::SetAudioLevel(int level) { + AudioOutDriver *out = (AudioOutDriver *)GetFirstOutput(); + if (out) { + picoTrackerAudioDriver *drv = (picoTrackerAudioDriver *)out->GetDriver(); + drv->SetAudioLevel(level); + } +} + int picoTrackerAudio::GetMixerVolume() { AudioOutDriver *out = (AudioOutDriver *)GetFirstOutput(); if (out) { diff --git a/sources/Adapters/picoTracker/audio/picoTrackerAudio.h b/sources/Adapters/picoTracker/audio/picoTrackerAudio.h index 9f169cd8b..d0d1f4c76 100644 --- a/sources/Adapters/picoTracker/audio/picoTrackerAudio.h +++ b/sources/Adapters/picoTracker/audio/picoTrackerAudio.h @@ -19,6 +19,7 @@ class picoTrackerAudio : public Audio { virtual void Close(); virtual int GetMixerVolume(); virtual void SetMixerVolume(int volume); + virtual void SetAudioLevel(int level); private: AudioSettings hints_; diff --git a/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.cpp b/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.cpp index 507e2e044..b0d94ec5b 100644 --- a/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.cpp +++ b/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.cpp @@ -109,39 +109,24 @@ bool picoTrackerAudioDriver::InitDriver() { volume_ = config->GetValue("VOLUME"); // Audio Level support in PIO code: - // need to modify the PIO instructions 9 and 21 to use the number of "offset" - // aka the OFFSET_COUNT const in the PIO asm code, its value is 3 for default - // "headphones level" bits required and then need to modify the PIO - // instructions 3 and 15 to use aka the BACKFILL_COUNT in the PIO asm code, - // its value is 10 for default "headphones level" the matching number of - // "backfill" number of bits required + // PIO instructions 9 and 21 hold the SET Y immediate for OFFSET_COUNT + // (MSB sign-extension padding - more = quieter), and instructions 3 and 15 + // hold the SET Y immediate for BACKFILL_COUNT (LSB zero padding). + // We load the default program and then patch those four instructions in + // PIO instruction memory via SetAudioLevel, which can also be called at + // runtime to change the level without rebooting. memcpy(modified_audio_i2s_instructions, audio_i2s_program_instructions, 24 * 2); - - // ---- HP High volume - if (audioLevel == 1) { - modified_audio_i2s_instructions[9] = 0xe843; - modified_audio_i2s_instructions[21] = 0xf843; - - modified_audio_i2s_instructions[3] = 0xf84a; - modified_audio_i2s_instructions[15] = 0xe84a; - } - - // ---- Line Level volume - if (audioLevel == 2) { - modified_audio_i2s_instructions[9] = 0xe841; - modified_audio_i2s_instructions[21] = 0xf841; - - modified_audio_i2s_instructions[3] = 0xf84c; - modified_audio_i2s_instructions[15] = 0xe84c; - } - modified_audio_i2s_program.instructions = modified_audio_i2s_instructions; uint offset = pio_add_program(AUDIO_PIO, &modified_audio_i2s_program); + pioOffset_ = offset; audio_i2s_program_init(AUDIO_PIO, AUDIO_SM, offset, AUDIO_SDATA, AUDIO_BCLK); + // Apply the configured audio level by patching the PIO instruction memory. + SetAudioLevel(audioLevel); + // Claim and configure DMA dma_channel_claim(AUDIO_DMA); dma_channel_config dma_config = dma_channel_get_default_config(AUDIO_DMA); @@ -182,6 +167,45 @@ bool picoTrackerAudioDriver::InitDriver() { return true; }; +// SET Y, value with sideset bits - encoded for the I2S PIO program's specific +// sideset values at instruction indices 3, 9, 15, 21. +// See audio_i2s.pio: indices 3 and 21 use side 0b11; indices 9 and 15 use +// side 0b01. Layout: 111(SET) | side(2) | delay(3) | dst=Y(010) | value(5). +static inline uint16_t set_y_side11(uint8_t value) { + return (uint16_t)(0xf840u | (value & 0x1fu)); +} +static inline uint16_t set_y_side01(uint8_t value) { + return (uint16_t)(0xe840u | (value & 0x1fu)); +} + +void picoTrackerAudioDriver::SetAudioLevel(int level) { + uint8_t offsetCount; + uint8_t backfillCount; + + switch (level) { + case 1: // HP high volume + offsetCount = 3u; + backfillCount = 10u; + break; + case 2: // Line level (loudest) - minimum MSB padding for maximum output + offsetCount = 0u; + backfillCount = 13u; + break; + case 0: // Default: matches PIO defaults (OFFSET_COUNT=6, BACKFILL_COUNT=7) + default: + offsetCount = 6u; + backfillCount = 7u; + break; + } + + // Single-word writes to instruction memory; the SM picks up the new + // immediate next time it executes that PC. + AUDIO_PIO->instr_mem[pioOffset_ + 3u] = set_y_side11(backfillCount); + AUDIO_PIO->instr_mem[pioOffset_ + 9u] = set_y_side01(offsetCount); + AUDIO_PIO->instr_mem[pioOffset_ + 15u] = set_y_side01(backfillCount); + AUDIO_PIO->instr_mem[pioOffset_ + 21u] = set_y_side11(offsetCount); +} + void picoTrackerAudioDriver::SetVolume(int v) { volume_ = (v <= 100) ? v : 100; Trace::Debug("Setting volume to %d", volume_); diff --git a/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.h b/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.h index ae4a013f5..27945bef3 100644 --- a/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.h +++ b/sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.h @@ -32,6 +32,12 @@ class picoTrackerAudioDriver : public AudioDriver { void OnChunkDone(); void SetVolume(int v); int GetVolume(); + // Patch the I2S PIO program's SET Y immediates to change the output level + // without requiring a reboot. level matches the LINEOUT config values: + // 0 = default (quietest, sensitive headphones) + // 1 = HP high volume + // 2 = line level + void SetAudioLevel(int level); virtual double GetStreamTime(); static void IRQHandler(); static void BufferNeeded(); @@ -43,5 +49,6 @@ class picoTrackerAudioDriver : public AudioDriver { static const char miniBlank_[MINI_BLANK_SIZE * 2 * sizeof(short)]; int volume_; uint32_t startTime_; + unsigned int pioOffset_; }; #endif diff --git a/sources/Application/Views/DeviceView.cpp b/sources/Application/Views/DeviceView.cpp index f6ebb4f99..0af0bf74b 100644 --- a/sources/Application/Views/DeviceView.cpp +++ b/sources/Application/Views/DeviceView.cpp @@ -195,10 +195,14 @@ void DeviceView::Update(Observable &, I_ObservableData *data) { return; } case FourCC::VarLineOut: { - MessageBox *mb = - MessageBox::Create(*this, "Reboot for new Audio Level!", MBBF_OK); - DoModal(mb); Config *config = Config::GetInstance(); + Variable *lv = config->FindVariable(FourCC::VarLineOut); + if (lv) { + Audio *audio = Audio::GetInstance(); + if (audio) { + audio->SetAudioLevel(lv->GetInt()); + } + } if (!config->Save()) { Trace::Error("DEVICEVIEW", "Failed to save device config after line out change"); diff --git a/sources/Services/Audio/Audio.h b/sources/Services/Audio/Audio.h index 3a74ad172..f91cd2086 100644 --- a/sources/Services/Audio/Audio.h +++ b/sources/Services/Audio/Audio.h @@ -28,6 +28,7 @@ class Audio : public T_Factory