|
3 | 3 | audio.cpp |
4 | 4 |
|
5 | 5 | Created on: Oct 28.2018 */char audioI2SVers[] ="\ |
6 | | - Version 3.3.1 "; |
7 | | -/* Updated on: Jun 08.2025 |
| 6 | + Version 3.3.1a "; |
| 7 | +/* Updated on: Jun 12.2025 |
8 | 8 |
|
9 | 9 | Author: Wolle (schreibfaul1) |
10 | 10 | Audio library for ESP32, ESP32-S3 or ESP32-P4 |
@@ -232,12 +232,13 @@ esp_err_t Audio::I2Sstart() { |
232 | 232 | esp_err_t Audio::I2Sstop() { |
233 | 233 | memset(m_outBuff.get(), 0, m_outbuffSize * sizeof(int16_t)); // Clear OutputBuffer |
234 | 234 | memset(m_samplesBuff48K.get(), 0, m_samplesBuff48KSize * sizeof(int16_t)); // Clear samplesBuff48K |
| 235 | + std::fill(std::begin(m_inputHistory), std::end(m_inputHistory), 0); // Clear history in samplesBuff48K |
235 | 236 | return i2s_channel_disable(m_i2s_tx_handle); |
236 | 237 | } |
237 | 238 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
238 | 239 | void Audio::zeroI2Sbuff(){ |
239 | 240 | uint8_t buff[2] = {0, 0}; // From IDF V5 there is no longer the zero_dma_buff() function. |
240 | | - size_t bytes_loaded = 0; // As a replacement, we write a small amount of zeros in the buffer and thus reset the entire buffer. |
| 241 | + size_t bytes_loaded = 0; // As a replacement, we write a small amount of zeros in the buffer and thus reset the entire buffer. |
241 | 242 | i2s_channel_preload_data(m_i2s_tx_handle, buff, 2, &bytes_loaded); |
242 | 243 | } |
243 | 244 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
@@ -321,7 +322,7 @@ void Audio::setDefaults() { |
321 | 322 | m_M4A_sampleRate = 0; |
322 | 323 | m_sumBytesDecoded = 0; |
323 | 324 | m_vuLeft = m_vuRight = 0; // #835 |
324 | | - |
| 325 | + std::fill(std::begin(m_inputHistory), std::end(m_inputHistory), 0); |
325 | 326 | if(m_f_reset_m3u8Codec){m_m3u8Codec = CODEC_AAC;} // reset to default |
326 | 327 | m_f_reset_m3u8Codec = true; |
327 | 328 |
|
@@ -2353,34 +2354,83 @@ bool Audio::pauseResume() { |
2353 | 2354 | return retVal; |
2354 | 2355 | } |
2355 | 2356 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 2357 | +size_t Audio::resampleTo48kStereo(const int16_t* input, size_t inputSamples) { |
| 2358 | + float ratio = static_cast<float>(m_sampleRate) / 48000.0f; |
| 2359 | + float cursor = m_resampleCursor; |
| 2360 | + |
| 2361 | + // Anzahl Input-Samples + 3 History-Samples (vorherige 3 Stereo-Frames) |
| 2362 | + size_t extendedSamples = inputSamples + 3; |
| 2363 | + |
| 2364 | + // Temporärer Buffer: History + aktueller Input |
| 2365 | + std::vector<int16_t> extendedInput(extendedSamples * 2); |
| 2366 | + |
| 2367 | + // Historie an den Anfang kopieren (6 Werte = 3 Stereo-Samples) |
| 2368 | + memcpy(&extendedInput[0], m_inputHistory, 6 * sizeof(int16_t)); |
| 2369 | + |
| 2370 | + // Aktuelles Input danach einfügen |
| 2371 | + memcpy(&extendedInput[6], input, inputSamples * 2 * sizeof(int16_t)); |
| 2372 | + |
| 2373 | + size_t outputIndex = 0; |
| 2374 | + |
| 2375 | + auto clipToInt16 = [](float value) -> int16_t { |
| 2376 | + if (value > 32767.0f){log_e("overflow +"); return 32767;} |
| 2377 | + if (value < -32768.0f) {log_e("overflow -");return -32768;} |
| 2378 | + return static_cast<int16_t>(value); |
| 2379 | + }; |
| 2380 | + |
| 2381 | + for (size_t inIdx = 1; inIdx < extendedSamples - 2; ++inIdx) { |
| 2382 | + int32_t xm1_l = clipToInt16(extendedInput[(inIdx - 1) * 2]); |
| 2383 | + int32_t x0_l = clipToInt16(extendedInput[(inIdx + 0) * 2]); |
| 2384 | + int32_t x1_l = clipToInt16(extendedInput[(inIdx + 1) * 2]); |
| 2385 | + int32_t x2_l = clipToInt16(extendedInput[(inIdx + 2) * 2]); |
2356 | 2386 |
|
2357 | | -size_t Audio::resampleTo48kStereo(const int16_t* input, size_t inputFrames) { |
| 2387 | + int32_t xm1_r = clipToInt16(extendedInput[(inIdx - 1) * 2 + 1]); |
| 2388 | + int32_t x0_r = clipToInt16(extendedInput[(inIdx + 0) * 2 + 1]); |
| 2389 | + int32_t x1_r = clipToInt16(extendedInput[(inIdx + 1) * 2 + 1]); |
| 2390 | + int32_t x2_r = clipToInt16(extendedInput[(inIdx + 2) * 2 + 1]); |
2358 | 2391 |
|
2359 | | - float exactOutputFrames = inputFrames * m_resampleRatio;; |
2360 | | - size_t outputFrames = static_cast<size_t>(std::floor(exactOutputFrames + m_resampleError)); |
2361 | | - m_resampleError += exactOutputFrames - outputFrames; |
| 2392 | + while (cursor < 1.0f) { |
| 2393 | + float t = cursor; |
2362 | 2394 |
|
2363 | | - std::vector<int16_t> output(outputFrames * 2); |
| 2395 | + // Catmull-Rom spline, construct a cubic curve |
| 2396 | + auto catmullRom = [](float t, float xm1, float x0, float x1, float x2) { |
| 2397 | + return 0.5f * ( |
| 2398 | + (2.0f * x0) + |
| 2399 | + (-xm1 + x1) * t + |
| 2400 | + (2.0f * xm1 - 5.0f * x0 + 4.0f * x1 - x2) * t * t + |
| 2401 | + (-xm1 + 3.0f * x0 - 3.0f * x1 + x2) * t * t * t |
| 2402 | + ); |
| 2403 | + }; |
2364 | 2404 |
|
2365 | | - for (size_t i = 0; i < outputFrames; ++i) { |
2366 | | - float inFramePos = i / m_resampleRatio; |
2367 | | - size_t idx = static_cast<size_t>(inFramePos); |
2368 | | - float frac = inFramePos - idx; |
2369 | 2405 |
|
2370 | | - size_t i1 = idx * 2; |
2371 | | - size_t i2 = (idx + 1 < inputFrames) ? (idx + 1) * 2 : i1; |
2372 | 2406 |
|
2373 | | - int16_t left1 = input[i1]; |
2374 | | - int16_t right1 = input[i1 + 1]; |
2375 | | - int16_t left2 = input[i2]; |
2376 | | - int16_t right2 = input[i2 + 1]; |
| 2407 | + int16_t outLeft = static_cast<int16_t>( |
| 2408 | + catmullRom(t, xm1_l, x0_l, x1_l, x2_l) |
| 2409 | + ); |
| 2410 | + int16_t outRight = static_cast<int16_t>( |
| 2411 | + catmullRom(t, xm1_r, x0_r, x1_r, x2_r) |
| 2412 | + ); |
2377 | 2413 |
|
2378 | | - m_samplesBuff48K[i * 2] = static_cast<int16_t>(left1 * (1.0f - frac) + left2 * frac); |
2379 | | - m_samplesBuff48K[i * 2 + 1] = static_cast<int16_t>(right1 * (1.0f - frac) + right2 * frac); |
| 2414 | + m_samplesBuff48K[outputIndex * 2] = clipToInt16(outLeft); |
| 2415 | + m_samplesBuff48K[outputIndex * 2 + 1] = clipToInt16(outRight); |
| 2416 | + |
| 2417 | + ++outputIndex; |
| 2418 | + cursor += ratio; |
| 2419 | + } |
| 2420 | + |
| 2421 | + cursor -= 1.0f; |
2380 | 2422 | } |
2381 | 2423 |
|
2382 | | - return outputFrames; |
| 2424 | + // Letzte 3 Stereo-Samples als neue Historie sichern |
| 2425 | + for (int i = 0; i < 3; ++i) { |
| 2426 | + size_t idx = inputSamples - 3 + i; |
| 2427 | + m_inputHistory[i * 2] = input[idx * 2]; |
| 2428 | + m_inputHistory[i * 2 + 1] = input[idx * 2 + 1]; |
| 2429 | + } |
| 2430 | + m_resampleCursor = cursor; |
| 2431 | + return outputIndex; |
2383 | 2432 | } |
| 2433 | + |
2384 | 2434 | //------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
2385 | 2435 | void IRAM_ATTR Audio::playChunk() { |
2386 | 2436 |
|
@@ -2448,7 +2498,7 @@ void IRAM_ATTR Audio::playChunk() { |
2448 | 2498 |
|
2449 | 2499 | i2swrite: |
2450 | 2500 |
|
2451 | | - err = i2s_channel_write(m_i2s_tx_handle, (int16_t*)m_samplesBuff48K.get() + count, samples48K * sampleSize, &i2s_bytesConsumed, 10); |
| 2501 | + err = i2s_channel_write(m_i2s_tx_handle, (int16_t*)m_samplesBuff48K.get() + count, samples48K * sampleSize, &i2s_bytesConsumed, 50); |
2452 | 2502 | if( ! (err == ESP_OK || err == ESP_ERR_TIMEOUT)) goto exit; |
2453 | 2503 | samples48K -= i2s_bytesConsumed / sampleSize; |
2454 | 2504 | count += i2s_bytesConsumed / 2; |
|
0 commit comments