Open ullibak opened 1 year ago
After digging through the files and some research, I found a way to make the webradio example run on a Pico W. The http connection seems to be stable, but the sound quality needs just some improvement. Feels like almost being there...
This is what I did:
Modified the preprocessor directives in AudioFileSourceHTTPStream.h;
#if defined(ESP32) || defined(ESP8266)
#pragma once
#include <Arduino.h>
#ifdef ESP32
#include <HTTPClient.h>
#else
#include <ESP8266HTTPClient.h>
#endif
#include "AudioFileSource.h"
#endif // moved this endif from last line to here
#if defined(ARDUINO_ARCH_RP2040) // added directives for RP2040
#pragma once
#include <Arduino.h>
#include <HTTPClient.h>
#include "AudioFileSource.h"
#endif
Modified the preprocessor directives in AudioFileSourceHTTPStream.cpp;
#if defined(ESP32) || defined(ESP8266) || defined(ARDUINO_ARCH_RP2040) //added RP2040
Here is the code I used:
/* Example Arduino code for Raspberry Pico W
Streams mp3 from http, buffers, decodes and outputs through i2s on a 1334A DAC board from Adafruit:
https://learn.adafruit.com/adafruit-i2s-stereo-decoder-uda1334a
Pin connections:
UDA1334A: Pico W:
BCLK = GPIO26
WSEL = GPIO27
DIN = GPIO28
*/
#include <Arduino.h>
//#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#include <WiFi.h>
#include <HTTPClient.h>
#include "AudioFileSourceHTTPStream.h"
#include "AudioFileSourceBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2S.h"
AudioFileSourceHTTPStream *file = NULL;
AudioFileSourceBuffer *buff = NULL;
AudioGenerator *decoder = NULL;
AudioOutputI2S *out = NULL;
const char* ssid = "your SSID";
const char* password = "your password";
const char* url = "http://airspectrum.cdnstream1.com:8000/1261_192";
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
// Note that the string may be in PROGMEM, so copy it to RAM for printf
char s1[64];
strncpy_P(s1, string, sizeof(s1));
s1[sizeof(s1)-1]=0;
Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, s1);
Serial.flush();
}
const int preallocateBufferSize = 16*1024;
const int preallocateCodecSize = 85332; // AAC+SBR codec max mem needed
void *preallocateBuffer = NULL;
void *preallocateCodec = NULL;
void setup()
{
Serial.begin(115200);
delay(3000);
Serial.printf_P(PSTR("Connecting to WiFi\n"));
WiFi.disconnect();
delay(1000);
WiFi.softAPdisconnect(true);
delay(1000);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
// Try forever
while (WiFi.status() != WL_CONNECTED) {
Serial.printf_P(PSTR("...Connecting to WiFi\n"));
delay(1000);
}
Serial.printf_P(PSTR("Connected\n"));
Serial.print("RSSI: ");
Serial.println(WiFi.RSSI());
Serial.printf_P(PSTR("Local IP:"));
Serial.println(WiFi.localIP());
// First, preallocate all the memory needed for the buffering and codecs, never to be freed
preallocateBuffer = malloc(preallocateBufferSize);
preallocateCodec = malloc(preallocateCodecSize);
if (!preallocateBuffer || !preallocateCodec) {
Serial.printf_P(PSTR("FATAL ERROR: Unable to preallocate %d bytes for app\n"), preallocateBufferSize+preallocateCodecSize);
while (1) delay(1000); // Infinite halt
}
file = new AudioFileSourceHTTPStream(url);
buff = new AudioFileSourceBuffer(file, preallocateBuffer, preallocateBufferSize);
Serial.printf_P(PSTR("created buffer\n"));
buff->RegisterStatusCB(StatusCallback, NULL);
out = new AudioOutputI2S();
//out->SetPinout(int bclkPin, int wclkPin, int doutPin), default is bclkPin = 26, wclkPin = 27, doutPin = 28
//out->SetPinout(10, 11, 9);
//out->SetGain(0.02); //some DAC have very high output
decoder = new AudioGeneratorMP3(preallocateCodec, preallocateCodecSize);
//decoder = new AudioGeneratorMP3a();
Serial.printf_P(PSTR("created decoder\n"));
decoder->RegisterStatusCB(StatusCallback, NULL);
Serial.printf_P("Decoder start...\n");
decoder->begin(buff, out);
}
uint32_t getTotalHeap(void) {
extern char __StackLimit, __bss_end__;
return &__StackLimit - &__bss_end__;
}
uint32_t getFreeHeap(void) {
struct mallinfo m = mallinfo();
return getTotalHeap() - m.uordblks;
}
void loop()
{
static int lastms = 0;
if (millis()-lastms > 10000) {
lastms = millis();
Serial.printf_P(PSTR("Running for %d seconds...Free mem=%d \n"), lastms/1000, getFreeHeap());
}
decoder->loop();
}
With the modifications described above, the code compiled. (Although, I ran into some difficulties because of a conflicting library, see https://github.com/earlephilhower/arduino-pico/issues/1127 It compiled after removing the "SdFat_-_Adafruit_Fork" library.)
The output of the speakers is a terrible noise, but the Pico W seems to establish a stable connection to the radio station via http.
Adding a larger buffer for i2s in AudioOutputI2S.cpp helped a lot:
#elif defined(ARDUINO_ARCH_RP2040)
AudioOutputI2S::AudioOutputI2S(long sampleRate, pin_size_t sck, pin_size_t data) {
i2sOn = false;
mono = false;
bps = 16;
channels = 2;
hertz = sampleRate;
bclkPin = sck;
doutPin = data;
SetGain(1.0);
i2s.setBuffers(5,2048); // <--- added this line
}
#endif
Although the sound quality dramatically improved by adding this buffer, it ist still not good. The "underlying" sound is good, but there are cracks at a rate of approx 10 Hz. I tried to play a short mp3 file at a sample rate of 44100 Hz from memory, but also got crackling sound.
Is there not enough processing power in the RP2040 to decode a mp3 (stream)? I tried to overclock the processor up to 240 MHz, but there was no improvement. So I think the processing power is not the problem. Played with buffers, but no improvement...
Now I'm stuck! Any ideas on how to proceed from here woud be greatly appreciated. Is there a way to further analyze where the problem could be?
Many Thx!
Maybe you experienced this bug in arduino-pico which caused clicking/cracks: https://github.com/earlephilhower/arduino-pico/pull/1500 https://github.com/earlephilhower/arduino-pico/discussions/1491
It's fixed in the latest release.
Yes, this solves the problem! Thanks a lot, @LinusHeu !
Installing version 3.2.2 and applying the modifications described above for AudioFileSourceHTTPStream.h and AudioFileSourceHTTPStream.cpp as well as increasing the buffer in AudioOutputI2S.cpp led to a clean sound without clicking/cracks.
Not sure though, if i2s.setBuffers(5,2048)
is really the ideal setting, but it works well for this http stream...
Thanks a lot @ullibak! I was able to make the required fixes to stream internet radio with my pimoroni pico audio pack (a different rp2040 i2s board)
the i2s.setBuffers
call really made the difference! cheers
I have the same project than @gregsadetsky : build a webradio with a Pimoroni Pico Audio board.
I followed @ullibak ullibak steps to modify AudioFileSourceHTTPStream.h
, AudioFileSourceHTTPStream.cpp
and AudioOutputI2S.cpp
The sound is still not perfect with missing part. increase CPU to 250MHz helped a bit. I also compile with maximum optimisation -O3
Any other idea?
@PicoGhost if it can help, all of the code for my internet radio client is here -- https://github.com/gregsadetsky/wfmu-rp2040-pimoroni-radio
@PicoGhost: When I worked on the webradio, I once experienced clicks and other noises or stuttering and I was absolutely unable to correlate them to sofware settings such as buffers etc. After hours of altering the code, compiler settings and processor speed without any success, I discovered that I had a hardware problem. I used Pimoroni PicoAudio as DAC and found that the contacts of the PicoAudio were bent in such a way that they made bad contact to the pins of the Pico. The contacts are gold plated, but the spring force is weak so that they plastically deform if the Pico is unplugged and re-plugged multiple times. I had to bend the contacts back towards the middle of the hole using a tiny watchmaker's screwdriver and that solved the problem.
Another problem that occured later turned out to be a power supply problem. I used a cheap USB power supply but this obviously was good enough to charge a mobile phone but not good enough to power a Pico and a DAC.
Hi, I am trying to run the webradio.ino example on a Pico W. The structure of the .ino file is
So this means that if a board with a RP2040 is used, the compiler does essentially nothing.
If I comment out these preprocessor directives and choose the Pico W as board in die Arduino IDE, I get a series of error messages, the first of them is
Compilation error: 'AudioFileSourceICYStream' does not name a type; did you mean 'AudioFileSourceBuffer'?
Has anyone managed to get this (or other) examples to run on a Pico W?
Thx!