Added:
trunk/Utilities/GaplessUtilities.m
Removed:
trunk/Utilities/GaplessUtilities.mm
Modified:
trunk/Encoders/CoreAudioEncoder.m
trunk/Max.xcodeproj/project.pbxproj
trunk/Utilities/GaplessUtilities.h
Log:
Write accurate bitrate information
Modified: trunk/Encoders/CoreAudioEncoder.m
===================================================================
--- trunk/Encoders/CoreAudioEncoder.m 2009-09-18 02:26:08 UTC (rev 1422)
+++ trunk/Encoders/CoreAudioEncoder.m 2009-09-18 04:06:27 UTC (rev 1423)
@@ -1,7 +1,7 @@
/*
* $Id$
*
- * Copyright (C) 2005 - 2007 Stephen F. Booth <m...@sbooth.org>
+ * Copyright (C) 2005 - 2009 Stephen F. Booth <m...@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -232,7 +232,7 @@
++iterations;
}
- // Write gapless info for AAC files
+ // Write gapless info and accurate bitrate for AAC files
if((kAudioFileMPEG4Type == [self fileType] || kAudioFileM4AType == [self fileType]) && kAudioFormatMPEG4AAC == [self formatID]) {
// First close the output files
@@ -240,7 +240,11 @@
NSAssert2(noErr == err, NSLocalizedStringFromTable(@"The call to %@ failed.", @"Exceptions", @""), @"ExtAudioFileDispose", UTCreateStringForOSType(err));
extAudioFile = NULL;
- addMPEG4AACGaplessInformationAtom(filename, [decoder totalFrames]);
+ // Snow Leopard correctly writes the SMPB atom
+ if(floor(NSAppKitVersionNumber) > 949.0 /* Leopard */)
+ addMPEG4AACGaplessInformationAtom(filename, [decoder totalFrames]);
+
+ addMPEG4AACBitrateInformationAtom(filename, bitrate, mode);
}
}
Modified: trunk/Max.xcodeproj/project.pbxproj
===================================================================
--- trunk/Max.xcodeproj/project.pbxproj 2009-09-18 02:26:08 UTC (rev 1422)
+++ trunk/Max.xcodeproj/project.pbxproj 2009-09-18 04:06:27 UTC (rev 1423)
@@ -14,6 +14,7 @@
32A145211046DD100020238F /* LibsndfileEncoderTask.mm in Sources */ = {isa = PBXBuildFile; fileRef = 32A145201046DD100020238F /* LibsndfileEncoderTask.mm */; };
32A14790104742030020238F /* NSString+URLEscapingMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = 32A1478F104742030020238F /* NSString+URLEscapingMethods.m */; };
32A147B0104745BD0020238F /* dsa_pub.pem in Resources */ = {isa = PBXBuildFile; fileRef = 32A147AF104745BD0020238F /* dsa_pub.pem */; };
+ 32C8F0EE10632AB0004AB74F /* GaplessUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C8F0ED10632AB0004AB74F /* GaplessUtilities.m */; };
8C0406530B5C584000648158 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 8C04064E0B5C584000648158 /* InfoPlist.strings */; };
8C05F19C0CC2DAA1006E5746 /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C05F1800CC2DAA1006E5746 /* Growl.framework */; };
8C05F19D0CC2DAA1006E5746 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C05F1810CC2DAA1006E5746 /* Sparkle.framework */; };
@@ -114,7 +115,6 @@
8C53FF930A05CD4100890518 /* ParanoiaRipper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C53FF860A05CD4100890518 /* ParanoiaRipper.m */; };
8C53FF950A05CD4100890518 /* Rip.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C53FF880A05CD4100890518 /* Rip.m */; };
8C53FF970A05CD4100890518 /* Ripper.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C53FF8A0A05CD4100890518 /* Ripper.m */; };
- 8C57F9EF0B10DE1300AA493C /* GaplessUtilities.mm in Sources */ = {isa = PBXBuildFile; fileRef = 8C57F9ED0B10DE1300AA493C /* GaplessUtilities.mm */; };
8C5E60860ACDC6F600D92D9A /* SecondsFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C5E60840ACDC6F500D92D9A /* SecondsFormatter.m */; };
8C5ED74F0ACB581C003E8ECF /* mac.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C5ED73B0ACB581C003E8ECF /* mac.framework */; };
8C5ED7500ACB5821003E8ECF /* mac.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8C5ED73B0ACB581C003E8ECF /* mac.framework */; };
@@ -349,6 +349,7 @@
32A14836104747240020238F /* Italian */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Italian; path = Italian.lproj/RipperPreferences.nib; sourceTree = "<group>"; };
32A14837104747240020238F /* Italian */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Italian; path = Italian.lproj/TaggingPreferences.nib; sourceTree = "<group>"; };
32A1483A104747240020238F /* Italian */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Italian; path = Italian.lproj/WavPackSettingsSheet.nib; sourceTree = "<group>"; };
+ 32C8F0ED10632AB0004AB74F /* GaplessUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GaplessUtilities.m; sourceTree = "<group>"; };
32CA4F630368D1EE00C91783 /* Max_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Max_Prefix.pch; sourceTree = "<group>"; };
32F895B80D8B61AE006B6116 /* French */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = French; path = French.lproj/CueSheetDocument.nib; sourceTree = "<group>"; };
32F895BB0D8B7DDD006B6116 /* Dutch */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Dutch; path = Dutch.lproj/CueSheetDocument.nib; sourceTree = "<group>"; };
@@ -535,7 +536,6 @@
8C53FF8B0A05CD4100890518 /* RipperMethods.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = RipperMethods.h; sourceTree = "<group>"; };
8C5568DC0948A4CF00F45C7E /* AudioToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AudioToolbox.framework; path = /System/Library/Frameworks/AudioToolbox.framework; sourceTree = "<absolute>"; };
8C57F9EC0B10DE1300AA493C /* GaplessUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GaplessUtilities.h; sourceTree = "<group>"; };
- 8C57F9ED0B10DE1300AA493C /* GaplessUtilities.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = GaplessUtilities.mm; sourceTree = "<group>"; };
8C5E60830ACDC6F500D92D9A /* SecondsFormatter.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = SecondsFormatter.h; path = Formatters/SecondsFormatter.h; sourceTree = "<group>"; };
8C5E60840ACDC6F500D92D9A /* SecondsFormatter.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = SecondsFormatter.m; path = Formatters/SecondsFormatter.m; sourceTree = "<group>"; };
8C5ED73B0ACB581C003E8ECF /* mac.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = mac.framework; path = Frameworks/mac/build/Release/mac.framework; sourceTree = "<group>"; };
@@ -1135,6 +1135,7 @@
8C53021E0A05D66A00890518 /* Utilities */ = {
isa = PBXGroup;
children = (
+ 32C8F0ED10632AB0004AB74F /* GaplessUtilities.m */,
8CF0E8C30B0C21570018F871 /* ImageAndTextCell.h */,
8CF0E8C40B0C21570018F871 /* ImageAndTextCell.m */,
8C74F8900A0B307E002260CF /* ServicesProvider.h */,
@@ -1147,7 +1148,6 @@
8C5302220A05D66A00890518 /* UtilityFunctions.h */,
8C5302230A05D66A00890518 /* UtilityFunctions.m */,
8C57F9EC0B10DE1300AA493C /* GaplessUtilities.h */,
- 8C57F9ED0B10DE1300AA493C /* GaplessUtilities.mm */,
);
path = Utilities;
sourceTree = "<group>";
@@ -1736,7 +1736,6 @@
8CD0201A0ADB66E000216B29 /* iTunesPreferencesController.m in Sources */,
8CD0251A0ADC036A00216B29 /* PostProcessingPreferencesController.m in Sources */,
8CF0E8C60B0C21570018F871 /* ImageAndTextCell.m in Sources */,
- 8C57F9EF0B10DE1300AA493C /* GaplessUtilities.mm in Sources */,
8C1370390C42649000D0238C /* FormatsController.m in Sources */,
8C1371260C42F43E00D0238C /* CueSheetTrack.m in Sources */,
8C13724D0C432CF400D0238C /* CueSheetDocumentToolbar.m in Sources */,
@@ -1758,6 +1757,7 @@
32A145131046DB920020238F /* CoreAudioEncoderTask.mm in Sources */,
32A145211046DD100020238F /* LibsndfileEncoderTask.mm in Sources */,
32A14790104742030020238F /* NSString+URLEscapingMethods.m in Sources */,
+ 32C8F0EE10632AB0004AB74F /* GaplessUtilities.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Modified: trunk/Utilities/GaplessUtilities.h
===================================================================
--- trunk/Utilities/GaplessUtilities.h 2009-09-18 02:26:08 UTC (rev 1422)
+++ trunk/Utilities/GaplessUtilities.h 2009-09-18 04:06:27 UTC (rev 1423)
@@ -1,7 +1,7 @@
/*
* $Id$
*
- * Copyright (C) 2005 - 2007 Stephen F. Booth <m...@sbooth.org>
+ * Copyright (C) 2005 - 2009 Stephen F. Booth <m...@sbooth.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -27,6 +27,11 @@
// Add the appropriate iTunSMPB atom for AAC gapless playback in iTunes
void addMPEG4AACGaplessInformationAtom(NSString *filename,
SInt64 totalFrames);
+
+ // Add the appropriate Encoding Params atom for AAC accurate bitrate in iTunes
+ void addMPEG4AACBitrateInformationAtom(NSString *filename,
+ UInt32 bitrate,
+ int bitrateMode);
#ifdef __cplusplus
}
Copied: trunk/Utilities/GaplessUtilities.m (from rev 1411, trunk/Utilities/GaplessUtilities.mm)
===================================================================
--- trunk/Utilities/GaplessUtilities.m (rev 0)
+++ trunk/Utilities/GaplessUtilities.m 2009-09-18 04:06:27 UTC (rev 1423)
@@ -0,0 +1,113 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005 - 2009 Stephen F. Booth <m...@sbooth.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#import "GaplessUtilities.h"
+
+#include <mp4v2/mp4v2.h>
+
+void
+addMPEG4AACGaplessInformationAtom(NSString *filename, SInt64 totalFrames)
+{
+ NSCParameterAssert(nil != filename);
+
+ MP4FileHandle file = MP4Modify([filename fileSystemRepresentation], MP4_DETAILS_ERROR, 0);
+ if(file == MP4_INVALID_FILE_HANDLE)
+ return;
+
+ MP4ItmfItem *smpb = MP4ItmfItemAlloc("----", 1);
+ smpb->mean = strdup("com.apple.iTunes");
+ smpb->name = strdup("iTunSMPB");
+
+ // Construct the encoder delay and padding
+ unsigned delay = 0x840;
+ unsigned padding = (unsigned)ceil((totalFrames + 2112) / 1024.0) * 1024 - (totalFrames + 2112);
+
+ NSString *value = [NSString stringWithFormat:@"00000000 %.8x %.8x %.16qx 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000", delay, padding, totalFrames];
+
+ const char *const utf8 = [value UTF8String];
+
+ MP4ItmfData *data = &smpb->dataList.elements[0];
+ data->typeCode = MP4_ITMF_BT_UTF8;
+ data->valueSize = strlen(utf8);
+ data->value = (uint8_t *)malloc( data->valueSize );
+
+ memcpy(data->value, utf8, data->valueSize);
+
+ // Add to mp4 file
+ MP4ItmfAddItem(file, smpb);
+ MP4ItmfItemFree(smpb);
+
+ MP4Close(file);
+}
+
+void
+addMPEG4AACBitrateInformationAtom(NSString *filename, UInt32 bitrate, int bitrateMode)
+{
+ NSCParameterAssert(nil != filename);
+
+ MP4FileHandle file = MP4Modify([filename fileSystemRepresentation], MP4_DETAILS_ERROR, 0);
+ if(file == MP4_INVALID_FILE_HANDLE)
+ return;
+
+ MP4ItmfItem *smpb = MP4ItmfItemAlloc("----", 1);
+ smpb->mean = strdup("com.apple.iTunes");
+ smpb->name = strdup("Encoding Params");
+
+ // Consruct the block
+ NSMutableData *blockData = [NSMutableData data];
+
+ NSString *atomName = @"vers";
+ const char *utf8 = [atomName UTF8String];
+ [blockData appendBytes:utf8 length:strlen(utf8)];
+ UInt32 num = OSSwapHostToBigInt32(1);
+ [blockData appendBytes:&num length:4];
+
+ // CBR, ABR, VBR, VBR (true)
+ atomName = @"acbf";
+ utf8 = [atomName UTF8String];
+ [blockData appendBytes:utf8 length:strlen(utf8)];
+ num = OSSwapHostToBigInt32(bitrateMode);
+ [blockData appendBytes:&num length:4];
+
+ atomName = @"brat";
+ utf8 = [atomName UTF8String];
+ [blockData appendBytes:utf8 length:strlen(utf8)];
+ num = OSSwapHostToBigInt32(bitrate);
+ [blockData appendBytes:&num length:4];
+
+ atomName = @"vers";
+ utf8 = [atomName UTF8String];
+ [blockData appendBytes:utf8 length:strlen(utf8)];
+ num = OSSwapHostToBigInt32(0x10504);
+ [blockData appendBytes:&num length:4];
+
+ MP4ItmfData *data = &smpb->dataList.elements[0];
+ data->typeCode = MP4_ITMF_BT_UTF8;
+ data->valueSize = [blockData length];
+ data->value = (uint8_t *)malloc( data->valueSize );
+
+ memcpy(data->value, [blockData bytes], data->valueSize);
+
+ // Add to mp4 file
+ MP4ItmfAddItem(file, smpb);
+ MP4ItmfItemFree(smpb);
+
+ MP4Close(file);
+}
Deleted: trunk/Utilities/GaplessUtilities.mm
===================================================================
--- trunk/Utilities/GaplessUtilities.mm 2009-09-18 02:26:08 UTC (rev 1422)
+++ trunk/Utilities/GaplessUtilities.mm 2009-09-18 04:06:27 UTC (rev 1423)
@@ -1,100 +0,0 @@
-/*
- * $Id$
- *
- * Copyright (C) 2005 - 2007 Stephen F. Booth <m...@sbooth.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
-#import "GaplessUtilities.h"
-
-#include <mp4v2/mp4.h>
-#include <mp4v2/mp4util.h>
-#include <mp4v2/mp4array.h>
-#include <mp4v2/mp4track.h>
-#include <mp4v2/mp4file.h>
-
-#undef ASSERT
-#define ASSERT(x)
-
-#include <mp4v2/mp4property.h>
-#include <mp4v2/mp4atom.h>
-#include <mp4v2/atoms.h>
-
-#include <cstdio>
-
-void
-addMPEG4AACGaplessInformationAtom(NSString *filename, SInt64 totalFrames)
-{
- MP4File *file;
- NSString *bundleVersion, *versionString;
- MP4Atom *dashAtom, *meanAtom, *nameAtom, *dataAtom;
- MP4Property *prop;
- MP4BytesProperty *bytesProp;
- const char *value;
-
- file = new MP4File();
-
- file->Modify([filename fileSystemRepresentation]);
-
- // Set this atom so mp4v2 will flesh out the "ilst" atom (otherwise tagging will fail)
- bundleVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"];
- versionString = [NSString stringWithFormat:@"Max %@", bundleVersion];
- file->SetMetadataString("\251too", [versionString UTF8String]);
-
- // Add the incredibly annoying "----" atom, with the appropriate gapless info
- dashAtom = file->AddDescendantAtoms("", "moov.udta.meta.ilst.----");
-
- meanAtom = dashAtom->FindChildAtom("mean");
- prop = meanAtom->GetProperty(2);
-
- if(BytesProperty == prop->GetType()) {
- value = "com.apple.iTunes";
- bytesProp = dynamic_cast<MP4BytesProperty *>(prop);
- bytesProp->SetValue((const uint8_t *)value, strlen(value));
- }
-
- nameAtom = dashAtom->FindChildAtom("name");
- prop = nameAtom->GetProperty(2);
-
- if(BytesProperty == prop->GetType()) {
- value = "iTunSMPB";
- bytesProp = dynamic_cast<MP4BytesProperty *>(prop);
- bytesProp->SetValue((const uint8_t *)value, strlen(value));
- }
-
- dataAtom = dashAtom->FindChildAtom("data");
- prop = dataAtom->GetProperty(3);
-
- if(BytesProperty == prop->GetType()) {
- NSString *newValue;
- unsigned delay;
- unsigned padding;
-
- delay = 0x840;
- padding = (unsigned)ceil((totalFrames + 2112) / 1024.0) * 1024 - (totalFrames + 2112);
-
- newValue = [NSString stringWithFormat:@"00000000 %.8x %.8x %.16qx 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000",
- delay, padding, totalFrames];
- value = [newValue cStringUsingEncoding:NSASCIIStringEncoding];
-
- bytesProp = dynamic_cast<MP4BytesProperty *>(prop);
- bytesProp->SetValue((const uint8_t *)value, strlen(value));
- }
-
- file->Close();
-
- delete file;
-}