/* Copyright (c) MediaArea.net SARL. All Rights Reserved. * * Use of this source code is governed by a BSD-style license that can * be found in the License.html file in the root of the source tree. */ //--------------------------------------------------------------------------- // Informal documentation and tests by spoRv //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Pre-compilation #include "MediaInfo/PreComp.h" #ifdef __BORLANDC__ #pragma hdrstop #endif //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Setup.h" //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #if defined(MEDIAINFO_APTX100_YES) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Audio/File_Aptx100.h" #include "MediaInfo/TimeCode.h" #include using namespace std; //--------------------------------------------------------------------------- namespace MediaInfoLib { //*************************************************************************** // Utils //*************************************************************************** //--------------------------------------------------------------------------- static unsigned BCD_to_Decimal(unsigned Value) { int Tens=Value>>4; int Units=Value&0xF; if (Tens>=10 || Units>=10) return (unsigned)-1; return Tens*10+Units; } //--------------------------------------------------------------------------- static char ascii_to_lower(char v) { if (v >= 'A' && v <= 'Z') v += ('z' - 'Z'); return v; } //*************************************************************************** // Infos //*************************************************************************** //--------------------------------------------------------------------------- enum aptx100_commercial { dts_sv, dts_stereo, dts_70_sv, dts_70, dts_es, dts_6, dts_max }; static const char* Aptx100_Commercial[] = { "DTS Special Venue", "DTS Stereo", "DTS 70 mm Special Venue", "DTS 70 mm", "DTS-ES 35 mm", "DTS 35 mm", }; static_assert(sizeof(Aptx100_Commercial) / sizeof(decltype(*Aptx100_Commercial)) == dts_max, ""); //--------------------------------------------------------------------------- // Usually it is compatible with ISO and IETF BCP 47, but not always, listing exceptions struct language_mapping { const char* From; const char* To; }; static const language_mapping Aptx100_Languages[] = // Must be sorted { { "ARB" , "ar" }, { "CAN" , "yue" }, { "CRO" , "hr" }, { "CZC" , "cs" }, { "ENGL", "en" }, // No idea about the difference with ENG { "FLM" , "nl-BE" }, { "FRN" , "fr" }, { "FRNC", "fr-CA" }, { "GERS", "de-CH" }, { "GRK" , "el" }, { "ITL" , "it" }, { "JAP" , "ja" }, { "PORB", "pt-BR" }, { "SLO" , "sl" }, { "SLV" , "sk" }, { "SPNC", "es" }, { "SPNL", "es-419" }, { "SWD" , "sv" }, { "TRK" , "tr" }, { "VTN" , "vi" }, { "YUG" , "-YU" }, // Which language from https://en.wikipedia.org/wiki/Languages_of_Yugoslavia#Official_languages ? }; static inline bool operator< (const language_mapping& l, const char* r) { return strcmp(l.From, r) < 0; } static inline bool operator< (const char* l, const language_mapping& r) { return strcmp(l, r.From) < 0; } //*************************************************************************** // Buffer - File header //*************************************************************************** //--------------------------------------------------------------------------- void File_Aptx100::FileHeader_Parse() { //Parsing if (File_Size<0x5C) { Reject(); return; } if (Buffer_Size<0x5C) { Element_WaitForMoreData(); return; } for (int i=0; i<60+8+7; i++) if ((Buffer[i] && Buffer[i]<0x20) || Buffer[i]>=0x7F) { Reject(); return; } string Title, Language, Studio; int16u Serial, Zero_4C, ReelNumber, ChannelCount; int8u DiscNumber, Start_HH, Start_MM, Start_SS, Start_FF, End_HH, End_MM, End_SS, End_FF; auto Get_String =[&](size_t MaxSize, string& Value, const char* Name) { auto IsParsed=false; auto Max=Element_Offset+MaxSize; for (;;) { auto Pos=Element_Offset; while (Pos=Max) return; Element_Offset++; while (Element_Offset0x60?(Start_SS-0x60):Start_SS); Start_MM=BCD_to_Decimal(Start_MM>0x60?(Start_MM-0x60):Start_MM); Start_HH=BCD_to_Decimal(Start_HH); End_FF=BCD_to_Decimal(End_FF); End_SS=BCD_to_Decimal(End_SS>0x60?(End_SS-0x60):End_SS); End_MM=BCD_to_Decimal(End_MM>0x60?(End_MM-0x60):End_MM); End_HH=BCD_to_Decimal(End_HH); switch (DiscNumber) { case 0x00: case 0x01: // Disc A or DVD or USB case 0x81: // Disc B break; default: IsNok=true; } switch (ChannelCount) { case 2: case 3: case 4: case 5: case 6: case 8: break; default: IsNok=true; } if (Zero_4C) IsNok=true; if (Start_FF>99 || Start_SS>59 || Start_MM>59 || Start_HH>23) IsNok=true; if (End_FF>99 || End_SS>59 || End_MM>59 || End_HH>23) IsNok=true; auto Start=TimeCode(Start_HH, Start_MM, Start_SS, Start_FF, 99, TimeCode::Timed()); auto End=TimeCode(End_HH, End_MM, End_SS, End_FF, 99, TimeCode::Timed()); auto Start_ms=Start.ToMilliseconds(); auto End_ms=End.ToMilliseconds(); if (!Start.IsSet() || !End.IsSet() || Start_ms>=End_ms) IsNok=true; if (IsNok) { Reject(); return; } End--; int64s Duration_ms=End_ms-Start_ms; //Filling Accept(); Fill(Stream_General, 0, General_Format, "APT-X100"); Fill(Stream_General, 0, General_Movie, Title); Fill(Stream_General, 0, General_Title, Title); if (Studio=="none") Studio.clear(); Fill(Stream_General, 0, General_ProductionStudio, Studio); if (DiscNumber) Fill(Stream_General, 0, General_Part_Position, (DiscNumber>>7)+1); if (ReelNumber) Fill(Stream_General, 0, General_Reel_Position, ReelNumber); if (Serial) Fill(Stream_General, 0, General_CatalogNumber, Serial); Stream_Prepare(Stream_Audio); Fill(Stream_Audio, 0, Audio_Format, "APT-X100"); Fill(Stream_Audio, 0, Audio_BitRate, 44100*4*ChannelCount); // Must be before addition of matrixed channels. 16-bit, 1:4 compression ratio, so 4-bit/channel, before cross overs and matrix string ChannelLayout; string Settings; auto Commercial=dts_max; switch (ChannelCount) { default: Commercial=dts_sv; break; case 2 : ChannelCount+=2; // C S matrixed in L R so +2 ChannelLayout="L C R S"; Settings="4:2:4 Matrix"; Commercial=dts_stereo; break; case 5 : ChannelCount++; // LS RS are crossed over to SW at 80 Hz so +1 ChannelLayout="L LS C RS R SW"; if (Serial>=60000) { ChannelCount++; // BS matrixed in LS RS so +1 ChannelLayout+=" BS"; Settings="ES Matrix"; Commercial=dts_es; } else if ((Serial==1357 && Language=="*ENG") || Serial==11131 || Serial==12030 || (Serial>12074 && Serial<21000)) // There are some exceptions... Hardcoded in the sidecar software update? List based on feedback from this format fans Commercial=dts_70; else Commercial=dts_6; break; case 6 : ChannelLayout="L LC C RC R S"; Commercial=dts_70_sv; break; case 8 : ChannelLayout = "L LS C RS R SW LC RC"; Commercial=dts_70_sv; break; } auto Commercial_String=Aptx100_Commercial[Commercial]; Fill(Stream_General, 0, General_Format_Commercial_IfAny, Commercial_String); Fill(Stream_Audio, 0, Audio_Format_Commercial_IfAny, Commercial_String); Fill(Stream_Audio, 0, Audio_Format_Settings, Settings); Fill(Stream_Audio, 0, Audio_Channel_s_, ChannelCount); Fill(Stream_Audio, 0, Audio_ChannelLayout, ChannelLayout); Fill(Stream_Audio, 0, Audio_BitRate_Mode, "CBR"); Fill(Stream_Audio, 0, Audio_SamplingRate, 44100); Fill(Stream_Audio, 0, Audio_Duration, Duration_ms); Fill(Stream_Audio, 0, Audio_TimeCode_FirstFrame, Start.ToString()); Fill(Stream_Audio, 0, Audio_TimeCode_LastFrame, End.ToString()); if (!Language.empty() && Language[0]=='*') { auto Language_Test=Language.c_str()+1; auto MappedLang=lower_bound(begin(Aptx100_Languages), end(Aptx100_Languages), Language_Test); if (MappedLang!=end(Aptx100_Languages) && !(Language_Test<*MappedLang)) Language=MappedLang->To; else Language.erase(0, 1); } Fill(Stream_Audio, 0, Audio_Language, Language); if (File_Size!=(int64u)-1 && File_Size>Element_Offset) Fill(Stream_Audio, 0, Audio_StreamSize, File_Size-Element_Offset); //No more need data Finish(); } } //NameSpace #endif //MEDIAINFO_APTX100_YES