📄 wav_handler.cpp
字号:
// =================================================================================================// ADOBE SYSTEMS INCORPORATED// Copyright 2002-2007 Adobe Systems Incorporated// All Rights Reserved//// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms// of the Adobe license agreement accompanying it.// =================================================================================================#if WIN_ENV #pragma warning ( disable : 4996 ) // '...' was declared deprecated#endif#include "WAV_Handler.hpp"#include "RIFF_Support.hpp"#include "Reconcile_Impl.hpp"#include "XMP_Const.h"using namespace std;#define kXMPUserDataType MakeFourCC ( '_', 'P', 'M', 'X' ) /* Yes, backwards! */#define formtypeWAVE MakeFourCC('W', 'A', 'V', 'E')// -------------------------------------------------------------------------------------------------// Premiere Pro specific info for reconciliation// ! MakeFourCC warning: The MakeFourCC macro creates a 32 bit int with the letters "reversed". This// ! is a leftover of the original Win-only code. It happens to work OK on little endian machines,// ! when stored in memory the letters are in the expected order. To be safe, always use the XMP// ! endian control macros when storing to memory or loading from memory.// FourCC codes for the RIFF chunks#define wavWaveTitleChunk MakeFourCC('D','I','S','P')#define wavInfoCreateDateChunk MakeFourCC('I','C','R','D')#define wavInfoArtistChunk MakeFourCC('I','A','R','T')#define wavInfoAlbumChunk MakeFourCC('I','N','A','M')#define wavInfoGenreChunk MakeFourCC('I','G','N','R')#define wavInfoCommentChunk MakeFourCC('I','C','M','T')#define wavInfoEngineerChunk MakeFourCC('I','E','N','G')#define wavInfoCopyrightChunk MakeFourCC('I','C','O','P')#define wavInfoSoftwareChunk MakeFourCC('I','S','F','T')#define wavInfoTag MakeFourCC('I','N','F','O')#define wavWaveTag MakeFourCC('W','A','V','E')// DC#define kTitle "title"#define kCopyright "rights"// XMP#define kCreateDate "CreateDate"// DM#define kArtist "artist"#define kAlbum "album"#define kGenre "genre"#define kLogComment "logComment"#define kEngineer "engineer"#define kSoftware "CreatorTool"// -------------------------------------------------------------------------------------------------// Legacy digest info// ------------------//// The original WAV handler code didn't keep a legacy digest, it imported the legacy on every open.// Because local encoding is used for the legacy, this can cause loss in the XMP. (The use of local// encoding itself is an issue, the AVI handler is using UTF-8.)//// The legacy digest for WAV is a list of chunk IDs and a 128-bit MD5 digest, formatted like:// DISP,IART,ICMT,ICOP,ICRD,IENG,IGNR,INAM,ISFT;012345678ABCDEF012345678ABCDEF//// The list of IDs are the recognized legacy chunks, in alphabetical order. This the full list that// could be recognized, not restricted to those actually present in the specific file. When opening// a file the new software's list is used to create the comparison digest, not the list from the// file. So that changes to the recognized list will trigger an import.//// The MD5 digest is computed from the full legacy chunk, including the ID and length portions, not// including any pad byte for odd data length. The length must be in file (little endian) order,// not native machine order. The legacy chunks are added to the digest in list (alphabetical by ID)// order.//// Legacy can be imported in 3 circumstances://// 1. If the file does not yet have XMP, all of the legacy is imported.//// 2. If the file has XMP and a digest, and the recomputed digest differs from the saved digest, the// legacy is imported. The digest comparison is the full string, including the list of IDs. A// check is made for each legacy item:// 2a. If the legacy item is missing or has an empty value, the corresponding XMP is deleted.// 2b. If the corresponding XMP is missing, the legacy value is imported.// 2c. If the new legacy value differs from a local encoding of the XMP value, the legacy value// is imported.//// 3. If the file has XMP but no digest, legacy is imported for items that have no corresponding XMP.// Any existing XMP is left alone. This is protection for tools that might be XMP aware but do// not provide a digest or any legacy reconciliation.#define TAG_MAX_SIZE 5024// =================================================================================================static inline int GetStringRiffSize ( const std::string & str ){ int l = strlen ( const_cast<char *> (str.data()) ); if ( l & 1 ) ++l; return l;}// =================================================================================================/// \file WAV_Handler.cpp/// \brief File format handler for WAV.////// This header ...///// =================================================================================================// =================================================================================================// WAV_MetaHandlerCTor// ===================XMPFileHandler * WAV_MetaHandlerCTor ( XMPFiles * parent ){ return new WAV_MetaHandler ( parent );} // WAV_MetaHandlerCTor// =================================================================================================// WAV_CheckFormat// ===============//// A WAVE file must begin with "RIFF", a 4 byte little endian length, then "WAVE". The length should// be fileSize-8, but we don't bother checking this here.bool WAV_CheckFormat ( XMP_FileFormat format, XMP_StringPtr filePath, LFA_FileRef fileRef, XMPFiles * parent ){ IgnoreParam(format); IgnoreParam(parent); XMP_Assert ( format == kXMP_WAVFile ); if ( fileRef == 0 ) return false; enum { kBufferSize = 12 }; XMP_Uns8 buffer [kBufferSize]; LFA_Seek ( fileRef, 0, SEEK_SET ); LFA_Read ( fileRef, buffer, kBufferSize ); // "RIFF" is 52 49 46 46, "WAVE" is 57 41 56 45 if ( (! CheckBytes ( &buffer[0], "\x52\x49\x46\x46", 4 )) || (! CheckBytes ( &buffer[8], "\x57\x41\x56\x45", 4 )) ) return false; return true; } // WAV_CheckFormat// =================================================================================================// WAV_MetaHandler::WAV_MetaHandler// ================================WAV_MetaHandler::WAV_MetaHandler ( XMPFiles * _parent ){ this->parent = _parent; this->handlerFlags = kWAV_HandlerFlags; this->stdCharForm = kXMP_Char8Bit; } // WAV_MetaHandler::WAV_MetaHandler// =================================================================================================// WAV_MetaHandler::~WAV_MetaHandler// =================================WAV_MetaHandler::~WAV_MetaHandler(){ // Nothing to do.} // WAV_MetaHandler::~WAV_MetaHandler// =================================================================================================// WAV_MetaHandler::UpdateFile// ===========================void WAV_MetaHandler::UpdateFile ( bool doSafeUpdate ){ if ( ! this->needsUpdate ) return; if ( doSafeUpdate ) XMP_Throw ( "WAV_MetaHandler::UpdateFile: Safe update not supported", kXMPErr_Unavailable ); bool fReconciliate = ! (this->parent->openFlags & kXMPFiles_OpenOnlyXMP); bool ok; std::string strTitle, strArtist, strComment, strCopyright, strCreateDate, strEngineer, strGenre, strAlbum, strSoftware; if ( fReconciliate ) { // Get the legacy item values, create the new digest, and add the digest to the XMP. The // legacy chunks in alphabetical ID order are: // DISP - title // IART - artist // ICMT - log comment // ICOP - copyright // ICRD - create date // IENG - engineer // IGNR - genre // INAM - album // ISFT - software MD5_CTX md5Ctx; std::string digestStr; XMP_Uns8 md5Val[16]; static char* hexDigits = "0123456789ABCDEF"; MD5Init ( &md5Ctx ); // Prepare the legacy values and compute the new digest. PrepareLegacyExport ( kXMP_NS_DC, kTitle, wavWaveTitleChunk, &strTitle, &digestStr, &md5Ctx, true /* LangAlt */ ); PrepareLegacyExport ( kXMP_NS_DM, kArtist, wavInfoArtistChunk, &strArtist, &digestStr, &md5Ctx ); PrepareLegacyExport ( kXMP_NS_DM, kLogComment, wavInfoCommentChunk, &strComment, &digestStr, &md5Ctx ); PrepareLegacyExport ( kXMP_NS_DC, kCopyright, wavInfoCopyrightChunk, &strCopyright, &digestStr, &md5Ctx, true /* LangAlt */ ); PrepareLegacyExport ( kXMP_NS_XMP, kCreateDate, wavInfoCreateDateChunk, &strCreateDate, &digestStr, &md5Ctx ); PrepareLegacyExport ( kXMP_NS_DM, kEngineer, wavInfoEngineerChunk, &strEngineer, &digestStr, &md5Ctx ); PrepareLegacyExport ( kXMP_NS_DM, kGenre, wavInfoGenreChunk, &strGenre, &digestStr, &md5Ctx ); PrepareLegacyExport ( kXMP_NS_DM, kAlbum, wavInfoAlbumChunk, &strAlbum, &digestStr, &md5Ctx ); PrepareLegacyExport ( kXMP_NS_XMP, kSoftware, wavInfoSoftwareChunk, &strSoftware, &digestStr, &md5Ctx ); // Finish the digest and add it to the XMP. MD5Final ( md5Val, &md5Ctx ); digestStr[digestStr.size()-1] = ';'; for ( size_t i = 0; i < 16; ++i ) { XMP_Uns8 byte = md5Val[i]; digestStr += hexDigits [byte >> 4]; digestStr += hexDigits [byte & 0xF]; } XMP_StringLen oldLen = this->xmpPacket.size(); this->xmpObj.SetProperty ( kXMP_NS_WAV, "NativeDigest", digestStr.c_str() ); try { this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_ExactPacketLength, oldLen ); } catch ( ... ) { this->xmpObj.SerializeToBuffer ( &this->xmpPacket, kXMP_UseCompactFormat ); } } XMP_StringPtr packetStr = this->xmpPacket.c_str(); XMP_StringLen packetLen = this->xmpPacket.size(); if ( packetLen == 0 ) return; // Make sure we're writing an even number of bytes as required by the RIFF specification if ( (this->xmpPacket.size() & 1) == 1 ) this->xmpPacket.push_back (' '); XMP_Assert ( (this->xmpPacket.size() & 1) == 0 ); packetStr = this->xmpPacket.c_str(); // ! Make sure they are current. packetLen = this->xmpPacket.size(); LFA_FileRef fileRef ( this->parent->fileRef ); if ( fileRef == 0 ) return; RIFF_Support::RiffState riffState; long numTags = RIFF_Support::OpenRIFF(fileRef, riffState); if ( numTags == 0 ) return; ok = RIFF_Support::PutChunk ( fileRef, riffState, formtypeWAVE, kXMPUserDataType, (char*)packetStr, packetLen ); if ( ! ok ) return; // If needed, reconciliate the XMP data back into the native metadata. if ( fReconciliate ) { PutChunk ( fileRef, riffState, wavWaveTag, wavWaveTitleChunk, strTitle.c_str(), strTitle.size() ); // Pad the old tags RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoCreateDateChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoArtistChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoAlbumChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoGenreChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoCommentChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoEngineerChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoCopyrightChunk, 0 ); RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, 0, wavInfoSoftwareChunk, 0 ); // Get the old INFO list std::string strOldInfo; unsigned long lOldSize = 0; bool ok = RIFF_Support::GetRIFFChunk ( fileRef, riffState, FOURCC_LIST, wavWaveTag, wavInfoTag, 0, &lOldSize ); if ( ok ) { // We have to get rid of the "INFO" first. strOldInfo.reserve ( lOldSize ); strOldInfo.assign ( lOldSize, ' ' ); RIFF_Support::GetRIFFChunk ( fileRef, riffState, FOURCC_LIST, wavWaveTag, wavInfoTag, (char*)strOldInfo.c_str(), &lOldSize ); // lOldSize -= 4; } // TODO: Cleaning up the OldInfo from the padding // Pad the old INFO list RIFF_Support::MarkChunkAsPadding ( fileRef, riffState, wavWaveTag, FOURCC_LIST, wavInfoTag ); // Calculating the new INFO list size XMP_Int32 dwListSize = 4 + 8 * 8 + GetStringRiffSize ( strCreateDate ) + GetStringRiffSize ( strArtist ) + GetStringRiffSize ( strAlbum ) + GetStringRiffSize ( strGenre ) + GetStringRiffSize ( strComment ) + GetStringRiffSize ( strEngineer ) + GetStringRiffSize ( strCopyright ) + GetStringRiffSize ( strSoftware ) + lOldSize; // list id (4 bytes) + 8 tags hdrs (8 each) ok = MakeChunk ( fileRef, riffState, formtypeWAVE, dwListSize + 8 ); if ( ! ok ) return; // If there's an error making a chunk, bail // Building the INFO list header RIFF_Support::ltag listtag; listtag.id = MakeUns32LE ( FOURCC_LIST ); listtag.len = MakeUns32LE ( dwListSize ); listtag.subid = MakeUns32LE ( wavInfoTag ); LFA_Write ( fileRef, &listtag, 12 );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -