pschatzmann / arduino-audio-tools

Arduino Audio Tools (a powerful Audio library not only for Arduino)
GNU General Public License v3.0
1.49k stars 233 forks source link

MemoryStream crashing on Pico #1627

Closed madskjeldgaard closed 3 months ago

madskjeldgaard commented 3 months ago

Problem Description

Running a modified version of an example of streaming wav file audio from memory causes the Raspberry Pi Pico board to crash after playing the audio file 5-10 times approximately. The Pico becomes completely unresponsive after that, does not play audio and does not respond to software resets.

Here is the code that makes it crash. For comparison, using something like a SineWave generator instead of the MemoryStream does not crash. Also I tried sample rates of 8000 as well as resolutions of 8-11 for the PWM output.

Device Description

Raspberry Pi Pico.

Sketch

#include "AudioTools.h"
#include "alice.h"
// #include "knghtsng.h"
#include <Arduino.h>

MemoryStream wavfileMemory(alice_wav, alice_wav_len);
// MemoryStream wav(knghtsng_wav, knghtsng_wav_len);

constexpr auto sample_rate = 44100;
constexpr auto channels = 1;
constexpr auto bits_per_sample = 16;
constexpr auto baudrate = 115200;

AudioInfo info(sample_rate, channels, bits_per_sample);
PWMAudioOutput pwmOutput;
EncodedAudioStream out(&pwmOutput, new WAVDecoder()); // Decoder stream
StreamCopy copier(out, wavfileMemory);                          // copy in to out

void setup() {
  Serial.begin(baudrate);
  AudioLogger::instance().begin(Serial, AudioLogger::Info);

  // Setup audio file
  wavfileMemory.begin();

  // setup PWM output
  auto config = pwmOutput.defaultConfig();
  config.copyFrom(info);
  config.resolution =
      8; // must be between 8 and 11 -> drives pwm frequency (8 is default)
  // alternative 1
  // config.start_pin = 3;
  // alternative 2
  int pins[] = {2};
  // Pins pins = {3};
  config.setPins(pins);
  pwmOutput.begin(config);
}

void loop() {
  if (wavfileMemory) {
    copier.copy();
  } else {
    // after we are done we just print some info form the wav file
    auto info = out.audioInfo();
    LOGI("The audio rate from the wav file is %d", info.sample_rate);
    LOGI("The channels from the wav file is %d", info.channels);

    // restart from the beginning
    Serial.println("Restarting...");
    delay(5000);
    out.begin();           // indicate that we process the WAV header
    wavfileMemory.begin(); // reset actual position to 0
    pwmOutput.begin();     // reset counters
  }
}

Other Steps to Reproduce

No response

What is your development environment

PlatformIO on a MacOS.

platformio.ini looks like this:

; The configuration file for PlatformIO
;
; This file is setup with a lot of suggestions for libraries etc.
;
; You can delete them as you wish :)
;
[platformio]
description = My cool project

; This is the default environment that will be used when you run `pio run`
default_envs = raspberrypi-pico

[env]
framework = arduino

; Use C++ version 17
build_unflags = -std=gnu++11

; Support C++ 17 and enable some warnings
build_flags = -std=gnu++17 -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-function -Wno-format-extra-args -Wsign-compare -Wuninitialized -Wunused-parameter -Wunused

; Use clang-tidy when runnning `pio check`
; https://docs.platformio.org/en/stable/advanced/static-code-analysis/tools/clang-tidy.html
check_tool = clangtidy

; Serial monitor speed, make sure this is matched in Serial.begin()
monitor_speed = 115200

# Common global libraries. Uncomment any of these to include them in the build.
lib_deps =
  # USB support
  Adafruit TinyUSB Library

  # MIDI communication – send MIDI messages over USB or physical MIDI ports
  https://github.com/FortySevenEffects/arduino_midi_library

  # OSC communication – senc control messages over the network
  # cnmat/OSC

  # Smooth reading of analog signals, eg potentiometers, analog sensors, etc.
  # dxinteractive/ResponsiveAnalogRead

  # Debounce buttons – removes noise from button presses
  # thomasfredericks/Bounce2

  # SPI
  SPI

  # I2C
  #Wire

  # DNSServer
  # DNSServer

  # Send commands to this board over serial
  # ppedro74/SerialCommands

  # Schedule tasks, timers, etc.
  # arkhipenko/TaskScheduler

  ; Audio Tools
  ; This library has a TON of audio related stuff
  https://github.com/pschatzmann/arduino-audio-tools.git

  ; Helix MP3 decoder for playing MP3 files
  https://github.com/pschatzmann/arduino-libhelix

[env:raspberrypi-pico]
; The Raspberry Pi Pico
; Link: https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html
; For more information about the platformio support:
; https://arduino-pico.readthedocs.io/en/latest/platformio.html
platform = https://github.com/maxgerhardt/platform-raspberrypi.git
board_build.core = earlephilhower
board = pico
board_build.mcu = rp2040
board_build.f_cpu = 133000000L
upload_protocol = picotool
; in reference to a board = pico config (2MB flash)
; Flash Size: 2MB (Sketch: 1MB, FS:1MB)
; board_build.filesystem_size = 1m
; Flash Size: 2MB (No FS)
; board_build.filesystem_size = 0m
; Flash Size: 2MB (Sketch: 0.5MB, FS:1.5MB)
; board_build.filesystem_size = 1.5m

; Extend build flags from the global section to enable TinyUSB support
build_flags = ${env.build_flags} -DUSE_TINYUSB
lib_deps =
  ${env.lib_deps}

  # USB support
  Adafruit TinyUSB Library

I have checked existing issues, discussions and online documentation

madskjeldgaard commented 3 months ago

And from the serial monitor, this is the last thing posted before the crash:

[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  1024 -> 1024 -> 1024 bytes - in 1 hops
[I] StreamCopy.h : 158 - StreamCopy::copy  766 -> 766 -> 766 bytes - in 1 hops
[I] main.cpp : 48 - The audio rate from the wav file is 44100
[I] main.cpp : 49 - The channels from the wav file is 2
Restarting...
[I] PWMAudioBase.h : 124 - decimation: 1
[I] PWMAudioBase.h : 153 - ->Buffer available: 4004
[I] PWMAudioBase.h : 154 - ->Buffer available for write: 96
[I] PWMAudioBase.h : 155 - ->is_timer_started: true 
[I] AudioEncoded.h : 75 - virtual void audio_tools::EncodedAudioOutput::addNotifyAudioChange(audio_tools::AudioInfoSupport&)
madskjeldgaard commented 3 months ago

Another point of debugging: If I set looping to active on the MemoryStream, it doesn't seem to crash.

pschatzmann commented 3 months ago

My gut feeling is that calling pwmOutput.begin(); again might be the source of your problem. You can set the log level to debug to get more details....

madskjeldgaard commented 3 months ago

Removing that from the loop doesn't help unfortunately. I set logging to Debug and got this before crashing:

.        Connected!
[D] StreamCopy.h : 98 - copy 1024 bytes 
[D] StreamCopy.h : 192 - available: 36606
[D] StreamCopy.h : 384 - write: 1024
[I] AudioEncoded.h : 75 - virtual void audio_tools::EncodedAudioOutput::addNotifyAudioChange(audio_tools::AudioInfoSupport&)
[D] AudioEncoded.h : 190 - EncodedAudioOutput::write: 1024
[D] CodecWAV.h : 323 - virtual size_t audio_tools::WAVDecoder::write(const uint8_t*, size_t)
[I] CodecWAV.h : 56 - WAVHeader::write: 1024 -> 44 -> 44
[I] CodecWAV.h : 63 - WAVHeader::begin: 44
[I] CodecWAV.h : 220 - WAVHeader sound_pos: 44
[I] CodecWAV.h : 221 - WAVHeader channels: 1 
[I] CodecWAV.h : 222 - WAVHeader bits_per_sample: 16
[I] CodecWAV.h : 223 - WAVHeader sample_rate: 8000 
[I] CodecWAV.h : 224 - WAVHeader format: 1
[I] CodecWAV.h : 420 - WAV sample_rate: 8000
[I] CodecWAV.h : 421 - WAV data_length: 2147418104
[I] CodecWAV.h : 422 - WAV is_streamed: 1
[I] CodecWAV.h : 423 - WAV is_valid: true
[I] AudioPWM.h : 35 - virtual void audio_tools::PWMAudioOutput::setAudioInfo(audio_tools::AudioInfo)
[I] AudioTypes.h : 128 -  sample_rate: 8000 / channels: 1 / bits_per_sample: 16
[D] PWMAudioRP2040.h : 52 - virtual void audio_tools::PWMDriverRP2040::end()
[D] AudioPWM.h : 50 - bool audio_tools::PWMAudioOutput::begin(audio_tools::PWMConfig)
[D] AudioOutput.h : 47 - virtual void audio_tools::AudioOutput::setAudioInfo(audio_tools::AudioInfo)
[I] AudioTypes.h : 128 -  sample_rate: 8000 / channels: 1 / bits_per_sample: 16
[D] PWMAudioBase.h : 119 - bool audio_tools::DriverPWMBase::begin(audio_tools::PWMConfig)
[I] PWMAudioBase.h : 124 - decimation: 1
[I] PWMAudioBase.h : 132 - ->Allocating new buffer 4 * 1024 bytes
[I] Buffers.h : 372 - resize: 4096
[I] PWMAudioBase.h : 87 - sample_rate: 8000
[I] PWMAudioBase.h : 88 - channels: 1
[I] PWMAudioBase.h : 89 - bits_per_sample: 16
[I] PWMAudioBase.h : 90 - buffer_size: 1024
[I] PWMAudioBase.h : 91 - buffer_count: 4
[I] PWMAudioBase.h : 92 - pwm_frequency: 30000
[I] PWMAudioBase.h : 93 - resolution: 8
[D] PWMAudioRP2040.h : 81 - virtual void audio_tools::PWMDriverRP2040::setupPWM()
[D] PWMAudioRP2040.h : 104 - pwm_config audio_tools::PWMDriverRP2040::setupPWMConfig()
[I] PWMAudioRP2040.h : 112 - ->wrap_value = 63
[I] PWMAudioRP2040.h : 113 - ->max clock speed = 133000000.000000
[I] PWMAudioRP2040.h : 114 - ->divider = 70.370369
[I] PWMAudioRP2040.h : 115 - ->clock speed = 1890000.000000
[I] PWMAudioRP2040.h : 92 - PWM pin 2
[D] PWMAudioRP2040.h : 125 - void audio_tools::PWMDriverRP2040::setupPWMPin(pwm_config&, audio_tools::PicoChannelOut&) for gpio 2
[I] PWMAudioBase.h : 153 - ->Buffer available: 0
[I] PWMAudioBase.h : 154 - ->Buffer available for write: 4096
[I] PWMAudioBase.h : 155 - ->is_timer_started: false 
[I] CodecWAV.h : 443 - WAVDecoder writing first sound data
[D] PWMAudioBase.h : 172 - adjusted size: 980
[D] Buffers.h : 77 - writeArray 980 -> 980
[D] PWMAudioRP2040.h : 70 - virtual void audio_tools::PWMDriverRP2040::startTimer()
[I] AudioTimerRP2040.h : 37 - timer time: 125 us
[D] PWMAudioBase.h : 172 - [D] Buffers.h : 50 - readArray 2 -> 2
adjusted size: 44
[D] Buffers.h : 50 - readArray 2 -> 2
[D] Buffers.h : 50 - readArray 2 -> 2
[D] Buffers.h : 77 - [D] Buffers.h : 50 - readArray 2 -> 2

[[D] Buffers.h : 50 - readArray 2 -> 2
D] AudioEncoded.h : 202 - EncodedAudioOutput::write: 1024 -> 1024
madskjeldgaard commented 3 months ago

With logging set to Debug it also crashes as soon as I connect the serial monitor now.

madskjeldgaard commented 3 months ago

Ah, I think I found the issue.

In the else part of the loop, the wavefile should have .end() called on it before calling .begin() again.

void loop() {
  if (wavfileMemory) {

    copier.copy();
  } else {

    // after we are done we just print some info form the wav file
    auto info = out.audioInfo();
    LOGI("The audio rate from the wav file is %d", info.sample_rate);
    LOGI("The channels from the wav file is %d", info.channels);

    // restart from the beginning
    Serial.println("Restarting...");
    // out.begin();           // indicate that we process the WAV header

    wavfileMemory.end(); // reset actual position to 0
    wavfileMemory.begin(); // reset actual position to 0
    // pwmOutput.begin();     // reset counters
  }
}
madskjeldgaard commented 3 months ago

It still crashes the Pico when I connect the Serial monitor while Logging is set to Debug, but with Info and Warning it works fine it seems (maybe that's a seperate issue).

pschatzmann commented 3 months ago

My gut feeling is that this might be related to the PWMStream.
Did you try to replace it with an I2SStream ?

In order to avoid some unnecessary changes, set the sample rate to 8000!

pschatzmann commented 3 months ago

I committed a correction that stops an active timer before it is restarted again....

I did not check if I can reproduce your issue, but I was double checking after the correction and it did not crash....

madskjeldgaard commented 3 months ago

Thanks @pschatzmann , I just tested the changes here and so far it seems to work! Thanks so much :)