Anonymous avatar Anonymous committed b276258

Patch from T. Million for 24-bit mad output.

Comments (0)

Files changed (2)

Plugins/MAD/MADDecoder.h

 
 #define INPUT_BUFFER_SIZE 5*8192
 
+struct audio_dither {
+	mad_fixed_t error[3];
+	mad_fixed_t random;
+};
+
+struct audio_stats {
+	unsigned long clipped_samples;
+	mad_fixed_t peak_clipping;
+	mad_fixed_t peak_sample;
+};
+
 @interface MADDecoder : NSObject <CogDecoder>
 {
 	struct mad_stream _stream;
 	struct mad_frame _frame;
 	struct mad_synth _synth;
 
+	struct audio_dither channel_dither[2];
+	struct audio_stats	stats;
+
 	unsigned char _inputBuffer[INPUT_BUFFER_SIZE+MAD_BUFFER_GUARD];
 	unsigned char *_outputBuffer;
 	int _outputFrames;

Plugins/MAD/MADDecoder.m

 	mad_frame_finish (&frame);
 	mad_stream_finish (&stream);
 	
-	bitsPerSample = 16;
+	bitsPerSample = 24;
 	
 	bytesPerFrame = (bitsPerSample/8) * channels;
 	
 	return [self scanFile];
 }
 
+/*
+ * NAME:	prng()
+ * DESCRIPTION:	32-bit pseudo-random number generator
+ */
+static inline
+unsigned long prng(unsigned long state)
+{
+	return (state * 0x0019660dL + 0x3c6ef35fL) & 0xffffffffL;
+}
+
+
+// Clipping and rounding code from madplay(audio.c):
+/*
+ * madplay - MPEG audio decoder and player
+ * Copyright (C) 2000-2004 Robert Leslie
+ */
+static inline signed long audio_linear_dither(unsigned int bits, mad_fixed_t sample,
+											  struct audio_dither *dither,
+											  struct audio_stats *stats)
+{
+	unsigned int scalebits;
+	mad_fixed_t output, mask, random;
+	
+	enum {
+		MIN = -MAD_F_ONE,
+		MAX =  MAD_F_ONE - 1
+	};
+	
+	/* noise shape */
+	sample += dither->error[0] - dither->error[1] + dither->error[2];
+	
+	dither->error[2] = dither->error[1];
+	dither->error[1] = dither->error[0] / 2;
+	
+	/* bias */
+	output = sample + (1L << (MAD_F_FRACBITS + 1 - bits - 1));
+	
+	scalebits = MAD_F_FRACBITS + 1 - bits;
+	mask = (1L << scalebits) - 1;
+	
+	/* dither */
+	random  = prng(dither->random);
+	output += (random & mask) - (dither->random & mask);
+	
+	dither->random = random;
+	
+	/* clip */
+	if (output >= stats->peak_sample) {
+		if (output > MAX) {
+			++stats->clipped_samples;
+			if (output - MAX > stats->peak_clipping)
+				stats->peak_clipping = output - MAX;
+			
+			output = MAX;
+			
+			if (sample > MAX)
+				sample = MAX;
+		}
+		stats->peak_sample = output;
+	}
+	else if (output < -stats->peak_sample) {
+		if (output < MIN) {
+			++stats->clipped_samples;
+			if (MIN - output > stats->peak_clipping)
+				stats->peak_clipping = MIN - output;
+			
+			output = MIN;
+			
+			if (sample < MIN)
+				sample = MIN;
+		}
+		stats->peak_sample = -output;
+	}
+	
+	/* quantize */
+	output &= ~mask;
+	
+	/* error feedback */
+	dither->error[0] = sample - output;
+	
+	/* scale */
+	return output >> scalebits;
+}
 
 // Clipping and rounding code from madplay(audio.c):
 /*
 	
 	_outputBuffer = (unsigned char *) malloc (_outputFrames * bytesPerFrame * sizeof (char));
 	
-	unsigned int i, j; 
-	unsigned int stride = channels * 2; 
-	for (j = 0; j < channels; j++) 
-	{ 
-		/* output sample(s) in 16-bit signed little-endian PCM */  
-		mad_fixed_t const *channel = _synth.pcm.samples[j]; 
-		unsigned char *outputPtr = _outputBuffer + (j * 2); 
-		
-		for (i = startingSample; i < sampleCount; i++) 
-		{  
-			signed short sample = audio_linear_round(bitsPerSample, channel[i]); 
+	int ch;
+	int i;
+	int stride = bitsPerSample/8;
+	unsigned char *outputPtr = _outputBuffer;
+	
+	// samples [0 ... n]
+	for(i = startingSample; i < sampleCount; i++) 
+	{		
+		// channels [0 .. n] in this case LRLRLRLR
+		for (ch = 0; ch < channels; ch++) 
+		{
+			signed long sample = audio_linear_dither(bitsPerSample, 
+													 _synth.pcm.samples[ch][i], 
+													 &channel_dither[ch], 
+													 &stats);
 			
-			outputPtr[0] = sample>>8;  
-			outputPtr[1] = sample & 0xff;  
+			if(bitsPerSample == 24)
+			{
+				outputPtr[0] = sample >> 16;
+				outputPtr[1] = sample >> 8;
+				outputPtr[2] = sample >> 0;				
+			}
+			else 
+			{
+				outputPtr[0] = sample >> 8;  
+				outputPtr[1] = sample & 0xff;  
+			}
 			outputPtr += stride; 
-		} 
-	} 
+		}
+	}
 	
 	// Output to a file
 	// FILE *f = fopen("data.raw", "a");
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.