예제 #1
0
int AnalyzeSamplesInterleaved(char * samples, long num_samples, int
                              num_channels){

    double leftbuf[MAXSAMPLES], rightbuf[MAXSAMPLES];
    long i;
    long totSamples = num_samples;
    long nSamples   = num_samples;
    signed short int * samp = (signed short int *)samples;
    int result = GAIN_ANALYSIS_ERROR;

/* NOTES:
 * leftbuf and rightbuf are arrays of doubles
 * samp is a short (16-bit) integer
 * inf is the input file
 * totSamples is the total number of samples remaining in the input file
 * MAXSAMPLES is the maximum number of samples to send to AnalyzeSamples at
 * once
 */

    while (totSamples > 0) {

        if (totSamples > MAXSAMPLES)
            nSamples = MAXSAMPLES;
        else
            nSamples = totSamples;

        if (num_channels == 2) {
            for (i = 0; i < nSamples; i++) {
                leftbuf[i] = *samp++; /* default conversion from short to double */
                rightbuf[i] = *samp++;
            }
            result = AnalyzeSamples(leftbuf, rightbuf, nSamples, 2);
            if (result != GAIN_ANALYSIS_OK) return result;
        } // end stereo
        else { /* Just one channel (mono) */
            for (i = 0; i < nSamples; i++) {
                leftbuf[i] = *samp++;
            }
            result = AnalyzeSamples(leftbuf, NULL, nSamples, 1);
            if (result != GAIN_ANALYSIS_OK) return result;
        } // end just mono

        totSamples -= nSamples;
    } // end while

    return result;

} /* AnalyzeSamplesInterleaved */
예제 #2
0
파일: replaygain.c 프로젝트: Erikhht/TCPMP
FLAC__bool grabbag__replaygain_analyze(const FLAC__int32 * const input[], FLAC__bool is_stereo, unsigned bps, unsigned samples)
{
	/* using a small buffer improves data locality; we'd like it to fit easily in the dcache */
	static Float_t lbuffer[2048], rbuffer[2048];
	static const unsigned nbuffer = sizeof(lbuffer) / sizeof(lbuffer[0]);
	FLAC__int32 block_peak = 0, s;
	unsigned i, j;

	FLAC__ASSERT(bps >= 4 && bps <= FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE);
	FLAC__ASSERT(FLAC__MIN_BITS_PER_SAMPLE == 4);
	/*
	 * We use abs() on a FLAC__int32 which is undefined for the most negative value.
	 * If the reference codec ever handles 32bps we will have to write a special
	 * case here.
	 */
	FLAC__ASSERT(FLAC__REFERENCE_CODEC_MAX_BITS_PER_SAMPLE < 32);

	if(bps == 16) {
		if(is_stereo) {
			j = 0;
			while(samples > 0) {
				const unsigned n = local_min(samples, nbuffer);
				for(i = 0; i < n; i++, j++) {
					s = input[0][j];
					lbuffer[i] = (Float_t)s;
					s = abs(s);
					block_peak = local_max(block_peak, s);

					s = input[1][j];
					rbuffer[i] = (Float_t)s;
					s = abs(s);
					block_peak = local_max(block_peak, s);
				}
				samples -= n;
				if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK)
					return false;
			}
		}
		else {
			j = 0;
			while(samples > 0) {
				const unsigned n = local_min(samples, nbuffer);
				for(i = 0; i < n; i++, j++) {
					s = input[0][j];
					lbuffer[i] = (Float_t)s;
					s = abs(s);
					block_peak = local_max(block_peak, s);
				}
				samples -= n;
				if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK)
					return false;
			}
		}
	}
	else { /* bps must be < 32 according to above assertion */
		const double scale = (
			(bps > 16)?
				(double)1. / (double)(1u << (bps - 16)) :
				(double)(1u << (16 - bps))
		);

		if(is_stereo) {
			j = 0;
			while(samples > 0) {
				const unsigned n = local_min(samples, nbuffer);
				for(i = 0; i < n; i++, j++) {
					s = input[0][j];
					lbuffer[i] = (Float_t)(scale * (double)s);
					s = abs(s);
					block_peak = local_max(block_peak, s);

					s = input[1][j];
					rbuffer[i] = (Float_t)(scale * (double)s);
					s = abs(s);
					block_peak = local_max(block_peak, s);
				}
				samples -= n;
				if(AnalyzeSamples(lbuffer, rbuffer, n, 2) != GAIN_ANALYSIS_OK)
					return false;
			}
		}
		else {
			j = 0;
			while(samples > 0) {
				const unsigned n = local_min(samples, nbuffer);
				for(i = 0; i < n; i++, j++) {
					s = input[0][j];
					lbuffer[i] = (Float_t)(scale * (double)s);
					s = abs(s);
					block_peak = local_max(block_peak, s);
				}
				samples -= n;
				if(AnalyzeSamples(lbuffer, 0, n, 1) != GAIN_ANALYSIS_OK)
					return false;
			}
		}
	}

	{
		const double peak_scale = (double)(1u << (bps - 1));
		double peak = (double)block_peak / peak_scale;
		if(peak > title_peak_)
			title_peak_ = peak;
		if(peak > album_peak_)
			album_peak_ = peak;
	}

	return true;
}
예제 #3
0
bool SFB::Audio::ReplayGainAnalyzer::AnalyzeURL(CFURLRef url, CFErrorRef *error)
{
	if(nullptr == url)
		return false;

	auto decoder = Decoder::CreateDecoderForURL(url, error);
	if(!decoder || !decoder->Open(error))
		return false;
	
	AudioStreamBasicDescription inputFormat = decoder->GetFormat();

	// Higher sampling rates aren't natively supported but are handled via resampling
	int32_t decoderSampleRate = (int32_t)inputFormat.mSampleRate;

	bool validSampleRate = EvenMultipleSampleRateIsSupported(decoderSampleRate);
	if(!validSampleRate) {
		if(error) {
			SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” does not contain audio at a supported sample rate."), "");
			SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Only sample rates of 8.0 KHz, 11.025 KHz, 12.0 KHz, 16.0 KHz, 22.05 KHz, 24.0 KHz, 32.0 KHz, 44.1 KHz, 48 KHz and multiples are supported."), "");
			SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "");

			*error = CreateErrorForURL(ReplayGainAnalyzer::ErrorDomain, ReplayGainAnalyzer::FileFormatNotSupportedError, description, url, failureReason, recoverySuggestion);
		}

		return false;
	}

	Float64 replayGainSampleRate = GetBestReplayGainSampleRateForSampleRate(decoderSampleRate);

	if(!(1 == inputFormat.mChannelsPerFrame || 2 == inputFormat.mChannelsPerFrame)) {
		if(error) {
			SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” does not contain mono or stereo audio."), "");
			SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Only mono or stereo files supported"), "");
			SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "");

			*error = CreateErrorForURL(ReplayGainAnalyzer::ErrorDomain, ReplayGainAnalyzer::FileFormatNotSupportedError, description, url, failureReason, recoverySuggestion);
		}

		return false;
	}

	AudioStreamBasicDescription outputFormat = {
		.mFormatID				= kAudioFormatLinearPCM,
		.mFormatFlags			= kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved,
		.mReserved				= 0,
		.mSampleRate			= replayGainSampleRate,
		.mChannelsPerFrame		= inputFormat.mChannelsPerFrame,
		.mBitsPerChannel		= 32,
		.mBytesPerPacket		= 4,
		.mBytesPerFrame			= 4,
		.mFramesPerPacket		= 1
	};
	
	if(!SetSampleRate((int32_t)outputFormat.mSampleRate)) {
		if(error) {
			SFB::CFString description = CFCopyLocalizedString(CFSTR("The file “%@” does not contain audio at a supported sample rate."), "");
			SFB::CFString failureReason = CFCopyLocalizedString(CFSTR("Only sample rates of 8.0 KHz, 11.025 KHz, 12.0 KHz, 16.0 KHz, 22.05 KHz, 24.0 KHz, 32.0 KHz, 44.1 KHz, 48 KHz and multiples are supported."), "");
			SFB::CFString recoverySuggestion = CFCopyLocalizedString(CFSTR("The file's extension may not match the file's type."), "");

			*error = CreateErrorForURL(ReplayGainAnalyzer::ErrorDomain, ReplayGainAnalyzer::FileFormatNotSupportedError, description, url, failureReason, recoverySuggestion);
		}

		return false;
	}

	// Converter takes ownership of decoder
	Converter converter(std::move(decoder), outputFormat);
	if(!converter.Open(error))
		return false;
	
	const UInt32 bufferSizeFrames = 512;
	BufferList outputBuffer(outputFormat, bufferSizeFrames);

	bool isStereo = (2 == outputFormat.mChannelsPerFrame);

	for(;;) {
		UInt32 frameCount = converter.ConvertAudio(outputBuffer, bufferSizeFrames);
		if(0 == frameCount)
			break;

		// Find the peak sample magnitude
		float lpeak, rpeak;
		vDSP_maxmgv((const float *)outputBuffer->mBuffers[0].mData, 1, &lpeak, frameCount);
		if(isStereo) {
			vDSP_maxmgv((const float *)outputBuffer->mBuffers[1].mData, 1, &rpeak, frameCount);
			priv->trackPeak = std::max(priv->trackPeak, std::max(lpeak, rpeak));
		}
		else
			priv->trackPeak = std::max(priv->trackPeak, lpeak);

		// The replay gain analyzer expects 16-bit sample size passed as floats
		const float scale = 1u << 15;
		vDSP_vsmul((const float *)outputBuffer->mBuffers[0].mData, 1, &scale, (float *)outputBuffer->mBuffers[0].mData, 1, frameCount);
		if(isStereo) {
			vDSP_vsmul((const float *)outputBuffer->mBuffers[1].mData, 1, &scale, (float *)outputBuffer->mBuffers[1].mData, 1, frameCount);
			AnalyzeSamples((const float *)outputBuffer->mBuffers[0].mData, (const float *)outputBuffer->mBuffers[1].mData, frameCount, true);
		}
		else
			AnalyzeSamples((const float *)outputBuffer->mBuffers[0].mData, nullptr, frameCount, false);
	}

	priv->albumPeak = std::max(priv->albumPeak, priv->trackPeak);

	return true;
}

bool SFB::Audio::ReplayGainAnalyzer::GetTrackGain(float& trackGain)
{
	if(!analyzeResult(priv->A, sizeof(priv->A) / sizeof(*(priv->A)), trackGain))
		return false;

	for(uint32_t i = 0; i < sizeof(priv->A) / sizeof(*(priv->A)); ++i) {
		priv->B[i] += priv->A[i];
		priv->A[i]  = 0;
	}

	priv->Zero();

	priv->totsamp	= 0;
	priv->lsum		= priv->rsum = 0.;

	return true;
}

bool SFB::Audio::ReplayGainAnalyzer::GetTrackPeak(float& trackPeak)
{
	trackPeak = priv->trackPeak;
	priv->trackPeak = 0.;
	return true;
}
예제 #4
0
static void
CalcReplayGain ( const char* filename, gain_info_t* G )
{
    FILE*    fp;
    float    buffl [NO];
    float    buffr [NO];
    Int16_t  buff  [NO] [2];
    size_t   i;
    size_t   len;
    size_t   lastlen = 0;
    unsigned int max = 0;
    float    level;
    float    mult;

    if ((fp = pipeopen ( "mppdec --silent --scale 0.5 --gain 0 --raw - - < #", filename)) == NULL) {
        stderr_printf ( "Can't decode '%s'\n", filename );
        exit (9);
    }

    memset ( buff, 0, sizeof(buff) );
    G->Silence = 0;

    lastlen = len = fread (buff, 4, NO, fp);
    for ( i = 0; i < len; i++ ) {
        buffl [i] = 2. * buff [i] [0];
        buffr [i] = 2. * buff [i] [1];
        if ( abs(buff[i][0]) > max ) max = abs(buff[i][0]);
        if ( abs(buff[i][1]) > max ) max = abs(buff[i][1]);
    }
    AnalyzeSamples ( buffl, buffr, len, 2 );

    level = 0.;
    mult  = 1.;
    for ( i = 0; i < len; i++ ) {
        level += mult * (buff [i] [0] * buff [i] [0] + buff [i] [1] * buff [i] [1]);
        mult  *= 0.95;
    }
    level = 2*sqrt(level * 0.05);
    if ( level > LEVEL_THR )
        G->Silence |= 2;

    sh ( NULL, level );

    while (( len = fread (buff, 4, NO, fp) ) > 0 ) {
        lastlen = len;
        for ( i = 0; i < len; i++ ) {
            buffl [i] = 2. * buff [i] [0];
            buffr [i] = 2. * buff [i] [1];
            if ( abs(buff[i][0]) > max ) max = abs(buff[i][0]);
            if ( abs(buff[i][1]) > max ) max = abs(buff[i][1]);
        }
        AnalyzeSamples ( buffl, buffr, len, 2 );
    }

    level = 0.;
    mult  = 1.;
    for ( i = 1; i <= NO; i++ ) {
        int  idx = (lastlen + NO - i) % NO;
        level += mult * (buff [idx] [0] * buff [idx] [0] + buff [idx] [1] * buff [idx] [1]);
        mult  *= 0.95;
    }
    level = 2*sqrt(level * 0.05);
    if ( level > LEVEL_THR )
        G->Silence |= 1;

    sh(filename,level);

    PCLOSE (fp);
#if 0
    GetTitleDynamics ();
#endif
    G -> FileName  = filename;
    G -> TitleGain = GetTitleGain ();
    G -> TitlePeak = 2 * max + 1;
    G -> AlbumGain = GetAlbumGain ();
    G -> AlbumPeak = G->AlbumPeak < G->TitlePeak  ?  G->TitlePeak  :  G->AlbumPeak;
    return;
}