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
8 changes: 8 additions & 0 deletions sources/Adapters/picoTracker/audio/picoTrackerAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions sources/Adapters/picoTracker/audio/picoTrackerAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
Expand Down
74 changes: 49 additions & 25 deletions sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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_);
Expand Down
7 changes: 7 additions & 0 deletions sources/Adapters/picoTracker/audio/picoTrackerAudioDriver.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
10 changes: 7 additions & 3 deletions sources/Application/Views/DeviceView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
1 change: 1 addition & 0 deletions sources/Services/Audio/Audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ class Audio : public T_Factory<Audio> {
virtual int GetSampleRate() { return 44100; };
virtual int GetMixerVolume() { return 100; };
virtual void SetMixerVolume(int volume){};
virtual void SetAudioLevel(int level){};

const char *GetAudioAPI();
const char *GetAudioDevice();
Expand Down
Loading