mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-29 08:18:12 +00:00
fix: implement nRF52 RTTTL player using direct NRF_PWM2 control for working volume
tone() dynamically computes seq_refresh so the DMA repeats 50% duty for ~13-30ms per note before re-reading its buffer — volume cannot take effect until that delay passes, producing a staircase artifact. Replace tone()/NonBlockingRtttl with a custom RTTTL parser that drives NRF_PWM2 directly using REFRESH=0. DMA now re-reads _duty_buf every PWM period so: - Duty is set before SEQSTART fires → no initial full-volume burst - applyVolume() writes to _duty_buf and takes effect within one period (<2.3ms at A4) - setVolume() during playback adjusts loudness immediately and smoothly Non-nRF52 platforms continue to use NonBlockingRtttl + analogWrite. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,43 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <NonBlockingRtttl.h>
|
||||
|
||||
/* class abstracts underlying RTTTL library
|
||||
|
||||
Just a simple imlementation to start. At the moment use same
|
||||
melody for message and discovery
|
||||
Suggest enum type for different sounds
|
||||
- on message
|
||||
- on discovery
|
||||
|
||||
TODO
|
||||
- make message ring tone configurable
|
||||
|
||||
*/
|
||||
#if !defined(NRF52_PLATFORM)
|
||||
#include <NonBlockingRtttl.h>
|
||||
#endif
|
||||
|
||||
class genericBuzzer
|
||||
{
|
||||
public:
|
||||
void begin(); // set up buzzer port
|
||||
void begin();
|
||||
void play(const char *melody);
|
||||
void playForced(const char *melody); // play regardless of quiet state
|
||||
void loop(); // loop driven-nonblocking
|
||||
void startup(); // play startup sound
|
||||
void shutdown(); // play shutdown sound
|
||||
bool isPlaying(); // returns true if a sound is still playing else false
|
||||
void quiet(bool buzzer_state); // enables or disables the buzzer
|
||||
bool isQuiet(); // get buzzer state on/off
|
||||
void setVolume(uint8_t level); // 0=min..4=max
|
||||
void playForced(const char *melody);
|
||||
void loop();
|
||||
void startup();
|
||||
void shutdown();
|
||||
bool isPlaying();
|
||||
void quiet(bool buzzer_state);
|
||||
bool isQuiet();
|
||||
void setVolume(uint8_t level);
|
||||
uint8_t getVolume() const { return _volume_level; }
|
||||
void stop(); // stop any playing melody
|
||||
void stop();
|
||||
|
||||
private:
|
||||
uint8_t _volume_level = 4;
|
||||
void applyVolume();
|
||||
// gemini's picks:
|
||||
const char *startup_song = "Startup:d=4,o=5,b=160:16c6,16e6,8g6";
|
||||
const char *startup_song = "Startup:d=4,o=5,b=160:16c6,16e6,8g6";
|
||||
const char *shutdown_song = "Shutdown:d=4,o=5,b=100:8g5,16e5,16c5";
|
||||
|
||||
bool _is_quiet = true;
|
||||
|
||||
#if defined(NRF52_PLATFORM)
|
||||
// Own RTTTL player — bypasses tone() to allow volume control from note start.
|
||||
// tone() pre-computes seq_refresh so the DMA repeats 50% duty for ~30ms before
|
||||
// re-reading its buffer; we cannot beat that timing. By owning NRF_PWM2 directly
|
||||
// and setting REFRESH=0, DMA re-reads _duty_buf every period so duty takes effect
|
||||
// immediately at SEQSTART.
|
||||
volatile uint16_t _duty_buf = 0; // DMA source — must stay in RAM
|
||||
const char* _rtttl_pos = nullptr;
|
||||
bool _rtttl_done = true;
|
||||
bool _pwm_on = false;
|
||||
unsigned long _note_end_ms = 0;
|
||||
uint8_t _def_dur = 4;
|
||||
uint8_t _def_oct = 5;
|
||||
uint16_t _def_bpm = 120;
|
||||
|
||||
void _nrfBegin(const char* melody);
|
||||
void _nrfAdvance();
|
||||
void _nrfStartPwm(uint16_t freq);
|
||||
void _nrfStopPwm();
|
||||
uint8_t _dutyPct() const;
|
||||
static uint16_t _noteFreq(char letter, bool sharp, uint8_t octave);
|
||||
static bool _parseNext(const char*& pos, uint8_t def_dur, uint8_t def_oct,
|
||||
uint16_t bpm, uint16_t& freq_hz, uint32_t& dur_ms);
|
||||
static void _parseHeader(const char* melody, uint8_t& def_dur, uint8_t& def_oct,
|
||||
uint16_t& bpm, const char*& notes_start);
|
||||
#endif
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user