FormatConverterStream causing dropouts (under-runs?) during channel conversion #2135
-
I'm using FormatConverterStream to convert URLstreams with different sampling rates and channel counts, so they can eventually be sent to a mixer for further processing. I've observed the following:
I see no changes in the log when the sound starts breaking up, but the pace of the messages slows down to match the gaps in the audio:
(also, if I specify a larger buffer size in the StreamCopy constructor, I see no effect in the copy sizes decribed in the logs, but maybe I'm misunderstanding that?) Test sketch that shows the problem with a mono -> stereo conversion after a few seconds:
|
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 4 replies
-
Please share the log: double check that i2s gets informormed about the 2 channels. |
Beta Was this translation helpful? Give feedback.
-
I'm bumping this after doing some more troubleshooting. If I pass manually-configured AudioInfo objects to the How can I use FormatConverterStream to dynamically change channel-counts after .begin()? Reminder of the symptoms: mono URLstreams play at double speed for a few seconds and then start stuttering (probably since they are emptying 2x faster than filling). |
Beta Was this translation helpful? Give feedback.
-
I can see the issue in the log: when helix reports mono, I2S is switched to mono, but it should stay as stereo. [I] AudioTypes.h : 124 - MP3DecoderHelix sample_rate: 44100 / channels: 1 / bits_per_sample: 16 I committed a correction: double check if this makes a difference and provide the updated log. ps. Please note that when you call conv.begin(multi.audioInfo(), out_info), multi.audioInfo() is still stereo because the decoding has not started yet. |
Beta Was this translation helpful? Give feedback.
-
I committed some corrections to the AudioTools
and LibHelix library
The following sketch is working now for me. I printing the AudioInfo for the full chain. Please note that it takes at least a copy() step for the decoder to inform about a change: #include "AudioTools.h"
#include "AudioTools/AudioCodecs/CodecMP3Helix.h"
#include "AudioTools/AudioLibs/AudioBoardStream.h"
#define audioKit 1 // 0 for Zach's Xaio+DAC, 1 for Phil's AudioKit
const char *ssid = "tbd"; // Change this to your WiFi SSID
const char *password = "tbd"; // Change this to your WiFi password
#if (audioKit == 1)
AudioBoardStream i2s(AudioKitEs8388V1);
#else
I2SStream i2s;
#endif
AudioInfo stereo_info(44100, 2, 16); // destination format for I2S (all streams will be converted to this)
AudioInfo mono_info(44100, 1, 16); // specs for typical mono stream
URLStream url;
VolumeStream vol(i2s);
FormatConverterStream conv(vol);
MultiDecoder multi(url);
MP3DecoderHelix mp3;
EncodedAudioStream dec(&conv, &multi); // Decoding stream
StreamCopy copier(dec, url); // copy url to decoder
static bool lastSwitch = 0;
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println("******************************************************");
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
//AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Info);
// setup i2s
auto config = i2s.defaultConfig(TX_MODE);
config.copyFrom(stereo_info);
if (audioKit == 0) {
// Custom I2S pins for my Xaio board
config.pin_ws = 9; // (DAC "LRCK")
config.pin_bck = 7; // (DAC "BCK")
config.pin_data = 8; // (DAC "DIN")
}
i2s.begin(config);
// setup VolumeStream
auto vcfg = vol.defaultConfig();
vcfg.allow_boost = true; // activate amplification using linear control
vol.begin(vcfg);
if (audioKit == 0) {
vol.setVolume(0.03); // For my setup where amp gain is fixed and very loud!
}
// setup audio converter: any of these variants will work
//conv.begin(mono_info, stereo_info);
//conv.begin(stereo_info, stereo_info);
conv.begin(stereo_info);
// setup decoder
multi.addDecoder(mp3, "audio/mpeg");
dec.begin();
Serial.println("Starting MONO Stream (after conv.begin)");
url.begin("https://s2.yesstreaming.net:17161/stream"); // MONO 44k STREAM
}
void loop() {
copier.copy();
static uint32_t lastSerialOutputTime;
static uint32_t lastURLTime;
char debugInfo[128];
if (millis() - lastSerialOutputTime > 1000) {
if (lastSwitch == 1) {
Serial.println("(MONO Stream...)");
} else {
Serial.println("(STEREO Stream...)");
}
snprintf(debugInfo, 96, "MP3DecoderHelix Samplerate: %d, Depth: %d, Channels: %d", mp3.audioInfo().sample_rate, mp3.audioInfo().bits_per_sample, mp3.audioInfo().channels);
Serial.println(debugInfo);
snprintf(debugInfo, 96, "MultiDecoder Samplerate: %d, Depth: %d, Channels: %d", multi.audioInfo().sample_rate, multi.audioInfo().bits_per_sample, multi.audioInfo().channels);
Serial.println(debugInfo);
snprintf(debugInfo, 96, "EncodedAudioStream Samplerate: %d, Depth: %d, Channels: %d", dec.audioInfo().sample_rate, dec.audioInfo().bits_per_sample, dec.audioInfo().channels);
Serial.println(debugInfo);
snprintf(debugInfo, 96, "FormatConverterStream In Samplerate: %d, Depth: %d, Channels: %d", conv.audioInfo().sample_rate, conv.audioInfo().bits_per_sample, conv.audioInfo().channels);
Serial.println(debugInfo);
snprintf(debugInfo, 96, "FormatConverterStream Out Samplerate: %d, Depth: %d, Channels: %d", conv.audioInfoOut().sample_rate, conv.audioInfoOut().bits_per_sample, conv.audioInfoOut().channels);
Serial.println(debugInfo);
snprintf(debugInfo, 96, "I2SStream Samplerate: %d, Depth: %d, Channels: %d", i2s.audioInfo().sample_rate, i2s.audioInfo().bits_per_sample, i2s.audioInfo().channels);
Serial.println(debugInfo);
Serial.println("");
lastSerialOutputTime = millis();
}
if (millis() - lastURLTime > 10000) {
if (lastSwitch == 0) {
Serial.println("Starting MONO Stream");
url.end();
url.begin("https://s2.yesstreaming.net:17161/stream"); // MONO 44k STREAM
lastSwitch = 1;
} else {
Serial.println("Starting STEREO Stream");
url.end();
url.begin("http://s1.voscast.com:8652/stream"); // STEREO 44k STREAM
lastSwitch = 0;
}
lastURLTime = millis();
}
} Please also note that the source AudioInfo of the conv.begin(); call does not matter because that will be dynamically refresheshd from the decoder. Only the target matters, so any of this will work:
|
Beta Was this translation helpful? Give feedback.
I can see the issue in the log: when helix reports mono, I2S is switched to mono, but it should stay as stereo.
[I] AudioTypes.h : 124 - MP3DecoderHelix sample_rate: 44100 / channels: 1 / bits_per_sample: 16
[I] ResampleStream.h : 145 - setStepSize: 1.000000
[I] ResampleStream.h : 129 - -> ResampleStream:
[I] AudioTypes.h : 124 - in: sample_rate: 44100 / channels: 1 / bits_per_sample: 16
[I] AudioTypes.h : 124 - out: sample_rate: 44100 / channels: 1 / bits_per_sample: 16
[I] I2SStream.h : 96 - virtual void audio_tools::I2SStream::setAudioInfo(audio_tools::AudioInfo)
[I] AudioTypes.h : 124 - in: sample_rate: 44100 / channels: 1 / bits_per_sample: 16
[I] AudioTypes.h : 124 - out: sample_rat…