Ticket #30 (closed enhancement: fixed)

Opened 9 years ago

Last modified 6 years ago

Use existing Album and Track REPLAYGAIN tags from .flac

Reported by: anonymous Owned by: robin
Priority: minor Milestone: Release 0.4.0
Keywords: Cc: Benkard_Lanier@GSB.Stanford.EDU, hakan@gurkensalat.com

Description (last modified by robin) (diff)

May consider this issue in 0.3.0

Change History

comment:1 Changed 9 years ago by anonymous

  • Priority changed from major to minor
  • Type changed from defect to enhancement

Can you elaborate a little?

I'm guessing you mean convert the replaygain values from Vorbis comments to ID3 tags in the mp3 file?

comment:2 Changed 9 years ago by robin

Ack, wasn't logged in when I added the comment!

comment:3 Changed 9 years ago by robin

  • Cc Benkard_Lanier@GSB.Stanford.EDU added
  • Owner set to robin
  • Status changed from new to assigned

Further details from Lanier (Benkard_Lanier@GSB.Stanford.EDU)

More details:

  1. The four vorbis comment fields in the flac file are:
    • REPLAYGAIN_TRACK_GAIN
    • REPLAYGAIN_TRACK_PEAK
    • REPLAYGAIN_ALBUM_GAIN
    • REPLAYGAIN_ALBUM_PEAK
  2. The ID3V2 tags work as follows:
    • Each tag goes into a "TXXX" frame, which is a user defined complex frame with two fields, "description" and "value" (so it is a complex frame).
    • There can be more than one TXXX frame in each file (here we need four, one for each tag).
    • For each of the four tags above, you need to write a TXXX frame with description equal to the tag name above and value equal to the value read from the flac file.

comment:4 Changed 9 years ago by robin

Lanier, are you sure these are the correct ID3v2 tags? What applications will read these?

comment:5 Changed 9 years ago by lanierb@stanford.edu

I just looked into it a bit more and found out a few things. First, the information I gave you above is for ID3v2.3. It seems that implementation is spotty. This is what foobar2000 currently does, but I can't verify that anyone else does the same. For ID3v2.4 it seems that a standard is developing to use the RVA2 frame instead. This is implemented in slimserver, rockbox, media monkey, and a few others. (foobar2000 does not seem to do this yet but probably will eventually -- it uses id3v2.3 right now I think.) So that opens a question as to what to implement. Probably RVA2 would be more compatible.

Also, I found out another thing which is that apparently MP3 encoding can change the replaygain settings a fair amount, so it might be worth rescanning anyway.

comment:6 Changed 9 years ago by pangloss <pangloss@gmail.com>

I'm interested in this as well, but it's not clear to me what the best path is if we're writing id3v2.3 and not id3v2.4 tags.

However, I'm also interested in generating Sound Check values (for iPod use) from flac REPLAYGAIN_ALBUM_GAIN tags (since iTunes generated Sound Check tags are more or less equivalent to REPLAYGAIN_TRACK_GAIN tags). From http://www.hydrogenaudio.org/forums/index.php?showtopic=24620, the conversion is given by the following (in C):

soundcheck = 1000 * pow(10.0, (-0.1 * gain))

However, I'm not sure how to write the tag. Evidently, Sound Check is "just a comment frame with the description iTunNORM".

I tried adding 'REPLAYGAIN_ALBUM_GAIN' => 'COMM', to the MP3frames hash, and

if ( $frame =~ m/^REPLAYGAIN/i ) {
  if ( $framestring =~ /(.*)\s+dB$/ ) {
    my $replaygain = $1;
    $::Options{debug} && msg("replaygain = $replaygain\n");
    my $soundcheck = 1000 * (10 ** (-0.1 * $replaygain));
    $::Options{debug} && msg("Setting soundcheck = $soundcheck\n");
    $framestring = 'iTunNORM: ' . $soundcheck;
  }
}
$mp3->{"ID3v2"}
->add_frame( $method, 'ENG', 'Short text', $framestring );

around line 438, but I don't believe this is adding the frame correctly. Any insight on how the frame should be added assuming the $soundcheck value itself is correct?

comment:7 Changed 9 years ago by pangloss

Found the following information (see http://svn.slimdevices.com/trunk/server/Slim/Utils/SoundCheck.pm?rev=10330&view=markup):

The iTunNORM tag consists of 5 value pairs. These 10 values are encoded as
ASCII Hex values of 8 characters each inside the tag (plus a space as prefix).
 
The tag can be found in MP3, AIFF, AAC and Apple Lossless files.

The relevant information is what is encoded in these 5 value pairs. The first
value of each pair is for the left audio channel, the second value of each
pair is for the right channel.

0/1: Volume adjustment in milliWatt/dBm

2/3: Same as 0/1, but not based on 1/1000 Watt but 1/2500 Watt

4/5: Not sure, but always the same values for songs that only differs in volume - so maybe some statistical values.

6/7: The peak value (maximum sample) as absolute (positive) value; therefore up to 32768 (for songs using 16-Bit samples).

8/9: Not sure, same as for 4/5: same values for songs that only differs in volume.

iTunes is choosing the maximum value of the both first pairs (of the first 4 values) to adjust the whole song.

So I'm not sure how to go about generating the entire Sound Check tag as iTunes would, but if in fact it's only the first two pairs of numbers that matter, it may be doable. Just for reference, here's what a Sound Check tag looks like (omitting the iTunNORM prefix):

00000077 00000057 000001E7 000001BA 00004C61 00004C61 00001BF4 00001929 00004C61 00004C61

which translates to +7.1 dB

comment:8 Changed 8 years ago by pangloss

Here's what I have so far:

sub convertReplayGainToSoundCheck {
	my ($gain, $peak) = @_;
	if ( $gain =~ /(.*)\s+dB$/ ) {
    		$gain = $1;
	}
	my @soundcheck;
	@soundcheck[0,2] = ( gain2sc($gain, 1000), gain2sc($gain, 2500) );
	@soundcheck[1,3] = @soundcheck[0,2];
	$soundcheck[6] = replayGainPeakToSoundCheckPeak($peak);
	$soundcheck[7] = $soundcheck[6];
	# bogus values for now -- however, these don't seem to be used AFAIK
	@soundcheck[4,5,8,9] = ('00024CA8', '00024CA8', '00024CA8', '00024CA8');
	return @soundcheck;
}

sub gain2sc {
	my ($gain, $base) = @_;
	my $result = round(pow(10, -$gain / 10) * $base);

        if ($result > 65534) {
                $result = 65534;
        }

        return decimalToASCIIHex($result);
}

sub round {
	my $number = shift;
	return int($number + .5 * ($number <=> 0));
}

sub decimalToASCIIHex {
	return sprintf("%08X", shift);
}

sub replayGainPeakToSoundCheckPeak {
	my $rgPeak = shift;
	# bogus values for now -- however, these don't seem to be used AFAIK
	return "00007FFF";
}

comment:9 Changed 8 years ago by robin

  • Description modified (diff)
  • Milestone set to Release 0.3.0

comment:10 Changed 8 years ago by robin

  • Milestone changed from Release 0.3.0 to Release 0.4.0

comment:11 Changed 8 years ago by anonymous

  • Cc hakan@gurkensalat.com added

comment:12 Changed 8 years ago by anonymous

  • billable set to 0

Seem to be working just fine.

comment:13 Changed 7 years ago by robin

  • Status changed from assigned to closed
  • Resolution set to fixed

I've bitten the bullet and decided on a way forward with this.

For now, I'm just copying ReplayGain? tags and writing them as TXXX frames in the mp3 files, which is what Foobar2000 does.

It would be possible to also convert ReplayGain? values to SoundCheck? tags, as used by iTUnes, but I have no requirement for that so am not bothering with it for now.

comment:14 Changed 6 years ago by RichieB

I wanted to thank pangloss for the code in comment 8. I used it to create a ReplayGain to SoundCheck converter perl script.

Note: See TracTickets for help on using tickets.