22
22
*/
23
23
24
24
#pragma once
25
- // #pragma GCC optimize("Ofast")
25
+ // #pragma GCC optimize("Ofast")
26
26
#pragma GCC optimize("O3")
27
27
28
28
#include " AudioTools/AudioCodecs/AudioCodecsBase.h"
@@ -51,9 +51,7 @@ namespace audio_tools {
51
51
*/
52
52
struct DSFMetadata : public AudioInfo {
53
53
DSFMetadata () = default ;
54
- DSFMetadata (int rate) {
55
- sample_rate = rate;
56
- }
54
+ DSFMetadata (int rate) { sample_rate = rate; }
57
55
uint32_t dsd_sample_rate =
58
56
0 ; // /< DSD sample rate (e.g. 2822400 Hz for DSD64)
59
57
uint64_t dsd_data_bytes = 0 ; // /< Size of DSD bitstream data in bytes
@@ -64,6 +62,7 @@ struct DSFMetadata : public AudioInfo {
64
62
DSD_BUFFER_SIZE; // /< Internal buffer size for DSD processing
65
63
float filter_q = 1 .41f ;
66
64
float filter_cutoff = 0 .4f ; // /< Cutoff frequency as fraction of Nyquist
65
+ int output_buffer_size = 1024 ;
67
66
};
68
67
69
68
/* *
@@ -140,20 +139,18 @@ class DSFDecoder : public AudioDecoder {
140
139
AudioDecoder::setAudioInfo (from);
141
140
meta.copyFrom (from);
142
141
// Ensure PCM buffer is allocated based on the new audio info
143
- int frame_size = meta.bits_per_sample / 8 * meta.channels ;
144
- if (meta.bits_per_sample == 24 ) frame_size = 4 * meta.channels ;
145
-
146
- pcmBuffer.resize (frame_size); // Allocate PCM buffer (up to 32-bit stereo)
142
+ int buffer_size = getOutputBufferSize ();
143
+ pcmBuffer.resize (buffer_size);
147
144
channelAccum.resize (meta.channels );
148
145
channelIntegrator.resize (meta.channels );
149
-
146
+
150
147
// Initialize integrator states
151
148
for (int i = 0 ; i < meta.channels ; i++) {
152
149
channelIntegrator[i] = 0 .0f ;
153
150
}
154
-
155
- setupDecimationStep ();
151
+
156
152
setupTargetPCMRate ();
153
+ setupDecimationStep ();
157
154
}
158
155
159
156
/* *
@@ -246,19 +243,30 @@ class DSFDecoder : public AudioDecoder {
246
243
size_t filePos; // /< Current position in DSF file
247
244
248
245
// Processing buffers and state
249
- Vector <uint8_t > pcmBuffer; // /< Buffer for PCM output samples - supports
250
- // /< multi-channel up to 32-bit
246
+ SingleBuffer <uint8_t > pcmBuffer{ 0 } ; // /< Buffer for PCM output samples -
247
+ // /< supports multi-channel up to 32-bit
251
248
Vector<float > channelAccum; // /< Accumulator for each channel during DSD to
252
249
// /< PCM conversion
253
250
Vector<LowPassFilter<float >>
254
251
channelFilters; // /< Anti-aliasing filters for each channel
255
252
RingBuffer<uint8_t > dsdBuffer{0 }; // /< Ring buffer for DSD data
256
253
uint32_t decimationStep; // /< Decimation factor for DSD to PCM conversion
257
- Vector<float > channelIntegrator; // /< Integrator state for each channel (for better DSD conversion)
254
+ Vector<float > channelIntegrator; // /< Integrator state for each channel (for
255
+ // /< better DSD conversion)
258
256
259
257
// Metadata
260
258
DSFMetadata meta; // /< Extracted DSF file metadata
261
259
260
+ // / The buffer size is defined in the metadata: it must be at least 1 frame
261
+ int getOutputBufferSize () {
262
+ int frame_size = meta.bits_per_sample / 8 * meta.channels ;
263
+ if (meta.bits_per_sample == 24 ) frame_size = 4 * meta.channels ;
264
+ int buffer_size = frame_size;
265
+ if (meta.output_buffer_size > buffer_size)
266
+ buffer_size = meta.output_buffer_size ;
267
+ return buffer_size;
268
+ }
269
+
262
270
/* *
263
271
* @brief Process header data until header is complete or data is exhausted
264
272
* @param data Input data buffer
@@ -314,7 +322,7 @@ class DSFDecoder : public AudioDecoder {
314
322
// Buffer as much DSD data as possible
315
323
bytesProcessed += bufferDSDData (data, len, startPos);
316
324
317
- // Convert buffered DSD data to PCM output
325
+ // Convert buffered DSD data to PCM output
318
326
convertDSDToPCM ();
319
327
320
328
return bytesProcessed;
@@ -331,25 +339,24 @@ class DSFDecoder : public AudioDecoder {
331
339
* data is consumed or the buffer becomes full.
332
340
*/
333
341
size_t bufferDSDData (const uint8_t * data, size_t len, size_t startPos) {
334
- size_t bytesBuffered = 0 ;
335
- size_t pos = startPos;
336
-
337
- while (pos < len && !dsdBuffer.isFull ()) {
338
- dsdBuffer.write (data[pos]);
339
- filePos++;
340
- pos++;
341
- bytesBuffered++;
342
+ int write_len = len - startPos;
343
+ if (write_len > dsdBuffer.availableForWrite ()) {
344
+ write_len = dsdBuffer.availableForWrite ();
342
345
}
346
+ dsdBuffer.writeArray (data + startPos, write_len);
347
+ filePos += write_len;
343
348
344
- return bytesBuffered ;
349
+ return write_len ;
345
350
}
346
351
347
352
/* *
348
353
* @brief Convert buffered DSD data to PCM samples and output them
349
354
*
350
- * Performs the core DSD to PCM conversion process using integrator-based approach:
355
+ * Performs the core DSD to PCM conversion process using integrator-based
356
+ * approach:
351
357
* 1. Integrates DSD bits over the decimation period for each channel
352
- * 2. Converts DSD bits to analog values (-1 or +1) with proper delta-sigma handling
358
+ * 2. Converts DSD bits to analog values (-1 or +1) with proper delta-sigma
359
+ * handling
353
360
* 3. Applies low-pass filtering to remove high-frequency noise
354
361
* 4. Converts filtered values to PCM samples
355
362
* 5. Outputs PCM samples for all channels
@@ -380,14 +387,14 @@ class DSFDecoder : public AudioDecoder {
380
387
// Use integrator-based approach for better DSD conversion
381
388
for (int bit = 0 ; bit < 8 ; bit++) {
382
389
int channelBit = (dsdByte >> (7 - bit)) & 1 ; // MSB first in DSF
383
-
390
+
384
391
// Delta-sigma integration: accumulate the difference
385
392
channelIntegrator[ch] += channelBit ? 1 .0f : -1 .0f ;
386
-
393
+
387
394
// Apply decay to prevent DC buildup
388
395
channelIntegrator[ch] *= 0 .9999f ;
389
396
}
390
-
397
+
391
398
// Add integrated value to channel accumulator
392
399
channelAccum[ch] += channelIntegrator[ch];
393
400
samplesProcessed += 8 ;
@@ -401,7 +408,7 @@ class DSFDecoder : public AudioDecoder {
401
408
for (int ch = 0 ; ch < meta.channels ; ch++) {
402
409
if (samplesPerChannel > 0 ) {
403
410
// Normalize by sample count and apply scaling factor
404
- channelAccum[ch] = channelAccum[ch] / (samplesPerChannel * 8 .0f );
411
+ channelAccum[ch] = clip ( channelAccum[ch] / (samplesPerChannel * 8 .0f ) );
405
412
}
406
413
407
414
// Apply low-pass filter to remove high-frequency noise
@@ -410,16 +417,21 @@ class DSFDecoder : public AudioDecoder {
410
417
}
411
418
412
419
// Convert to PCM sample and store in buffer
413
- convertToPCMSample (clip (channelAccum[ch]), ch );
420
+ writePCMSample (clip (channelAccum[ch]));
414
421
}
415
422
416
423
// Output the PCM samples for all channels
417
- size_t frameSize = pcmBuffer.size ();
418
- size_t written =
419
- getOutput ()->write ((uint8_t *)pcmBuffer.data (), frameSize);
420
- if (written != frameSize) {
421
- LOGE (" Failed to write PCM samples: expected %zu bytes, wrote %zu bytes" ,
422
- frameSize, written);
424
+ if (pcmBuffer.isFull ()) {
425
+ size_t frameSize = pcmBuffer.available ();
426
+ size_t written =
427
+ getOutput ()->write ((uint8_t *)pcmBuffer.data (), frameSize);
428
+ if (written != frameSize) {
429
+ LOGE (
430
+ " Failed to write PCM samples: expected %zu bytes, wrote %zu "
431
+ " bytes" ,
432
+ frameSize, written);
433
+ }
434
+ pcmBuffer.reset ();
423
435
}
424
436
}
425
437
}
@@ -443,14 +455,16 @@ class DSFDecoder : public AudioDecoder {
443
455
*
444
456
* Initializes anti-aliasing filters for each audio channel with appropriate
445
457
* cutoff frequency (40% of Nyquist frequency) for the current sample rate.
446
- * This ensures proper anti-aliasing performance during DSD to PCM conversion.
458
+ * This ensures proper anti-aliasing performance during DSD to PCM
459
+ * conversion.
447
460
*/
448
461
void setupTargetPCMRate () {
449
462
TRACEI ();
450
463
451
464
// Initialize filters for the correct number of channels
452
465
if (meta.sample_rate > 0 && meta.channels > 0 ) {
453
- float cutoffFreq = meta.sample_rate * meta.filter_cutoff ; // 40% of Nyquist frequency
466
+ float cutoffFreq =
467
+ meta.sample_rate * meta.filter_cutoff ; // 40% of Nyquist frequency
454
468
channelFilters.resize (meta.channels );
455
469
for (int i = 0 ; i < meta.channels ; i++) {
456
470
channelFilters[i].begin (cutoffFreq, meta.sample_rate , meta.filter_q );
@@ -461,32 +475,35 @@ class DSFDecoder : public AudioDecoder {
461
475
/* *
462
476
* @brief Calculate optimal decimation step for DSD to PCM conversion
463
477
*
464
- * Calculates the decimation factor as the ratio of DSD sample rate to target
465
- * PCM sample rate. Clamps the value between 64 and 512 to ensure reasonable
466
- * processing efficiency and audio quality while maintaining good anti-aliasing
467
- * performance.
478
+ * Calculates the decimation factor as the ratio of DSD sample rate to
479
+ * target PCM sample rate. Clamps the value between 64 and 512 to ensure
480
+ * reasonable processing efficiency and audio quality while maintaining good
481
+ * anti-aliasing performance.
468
482
*/
469
483
void setupDecimationStep () {
470
484
TRACEI ();
471
485
if (meta.sample_rate == 0 || meta.dsd_sample_rate == 0 ) {
472
- LOGE (" Invalid sample rates: DSD=%u, PCM=%u" , (unsigned )meta.dsd_sample_rate , (unsigned )meta.sample_rate );
486
+ LOGE (" Invalid sample rates: DSD=%u, PCM=%u" ,
487
+ (unsigned )meta.dsd_sample_rate , (unsigned )meta.sample_rate );
473
488
return ;
474
489
}
475
-
490
+
476
491
decimationStep = meta.dsd_sample_rate / meta.sample_rate ;
477
492
if (decimationStep < 64 ) {
478
- LOGW (" Decimation step %u too low, setting to 64" , (unsigned )decimationStep);
493
+ LOGW (" Decimation step %u too low, setting to 64" ,
494
+ (unsigned )decimationStep);
479
495
decimationStep = 64 ;
480
496
}
481
497
if (decimationStep > 512 ) {
482
- LOGW (" Decimation step %u too high, setting to 512" , (unsigned )decimationStep);
498
+ LOGW (" Decimation step %u too high, setting to 512" ,
499
+ (unsigned )decimationStep);
483
500
decimationStep = 512 ;
484
501
}
485
-
502
+
486
503
// Ensure decimation step is multiple of 8 for clean byte processing
487
504
decimationStep = (decimationStep / 8 ) * 8 ;
488
505
if (decimationStep < 64 ) decimationStep = 64 ;
489
-
506
+
490
507
LOGI (" Decimation step set to %u for DSD rate %u and target PCM rate %u" ,
491
508
(unsigned )decimationStep, (unsigned )meta.dsd_sample_rate ,
492
509
(unsigned )meta.sample_rate );
@@ -503,7 +520,8 @@ class DSFDecoder : public AudioDecoder {
503
520
*/
504
521
bool hasEnoughData () {
505
522
// DSF uses byte interleaving: each decimation step needs enough bytes
506
- // to cover all channels. Each byte contains 8 DSD samples for one channel.
523
+ // to cover all channels. Each byte contains 8 DSD samples for one
524
+ // channel.
507
525
int bytesPerDecimationStep = (decimationStep / 8 ) * meta.channels ;
508
526
if (bytesPerDecimationStep < meta.channels )
509
527
bytesPerDecimationStep = meta.channels ;
@@ -516,28 +534,28 @@ class DSFDecoder : public AudioDecoder {
516
534
* @param filteredValue The filtered DSD value (range -1.0 to 1.0)
517
535
* @param channel Channel index (0 for left/mono, 1 for right)
518
536
*/
519
- void convertToPCMSample (float filteredValue, int channel ) {
537
+ void writePCMSample (float filteredValue) {
520
538
switch (meta.bits_per_sample ) {
521
539
case 8 : {
522
- int8_t * buffer8 = reinterpret_cast <int8_t *>(pcmBuffer. data () );
523
- buffer8[channel] = static_cast < int8_t >(filteredValue * 127 . 0f );
540
+ int8_t buffer8 = static_cast <int8_t >(filteredValue * 127 . 0f );
541
+ pcmBuffer. write (buffer8 );
524
542
break ;
525
543
}
526
544
case 16 : {
527
- int16_t * buffer16 = reinterpret_cast <int16_t *>(pcmBuffer. data () );
528
- buffer16[channel] = static_cast < int16_t >(filteredValue * 32767 . 0f );
545
+ int16_t buffer16 = static_cast <int16_t >(filteredValue * 32767 . 0f );
546
+ pcmBuffer. writeArray (( uint8_t *)&buffer16, sizeof ( int16_t ) );
529
547
break ;
530
548
}
531
549
case 24 : {
532
- int24_t * buffer24 = reinterpret_cast <int24_t *>(pcmBuffer.data ());
533
- buffer24[channel] =
550
+ int24_t buffer24 =
534
551
static_cast <int24_t >(filteredValue * 8388607 .0f ); // 2^23 - 1
552
+ pcmBuffer.writeArray ((uint8_t *)&buffer24, sizeof (int24_t ));
535
553
break ;
536
554
}
537
555
case 32 : {
538
- int32_t * buffer32 = reinterpret_cast < int32_t *>(pcmBuffer. data ());
539
- buffer32[channel] =
540
- static_cast < int32_t >(filteredValue * 2147483647 . 0f ); // 2^31 - 1
556
+ int32_t buffer32 =
557
+ static_cast < int32_t >(filteredValue * 2147483647 . 0f ); // 2^31 -
558
+ pcmBuffer. writeArray (( uint8_t *)&buffer32, sizeof ( int32_t ));
541
559
break ;
542
560
}
543
561
default :
0 commit comments