📄 foo_faac.cpp
字号:
// FAAC encoder for foobar2000 diskwriter// Copyright (C) 2003 Janne Hyv鋜inen//// Changes:// 0.4.2 (2003-12-14): Changed the gapless method again// 0.4.1 (2003-12-13): Added ctts field writing for MP4 mode// 0.4 (2003-12-11): Added support for average bitrate controlling// 0.3.5 (2003-10-17): Changed way gapless encoding is handled (iTunes is buggy...)// 0.3.4 (2003-10-14): Fixed AAC object type selecting// 0.3.3 (2003-10-02): Removed gapless support for raw AAC files, it was hacky and recent libfaad changes broke it// 0.3.2 (2003-09-17): Last fix wasn't perfect and very small input chunks wouldn't have been encoded// 0.3.1 (2003-09-14): Fixed possible memory access problems// 0.3 (2003-08-17): Even more corrections to MP4 writing, now encoder delay is taken into account and first MP4 sample is given length 0// writes 'TOOL' metadata tag with libfaac version string// 0.2.9 (2003-08-16): Fixes in MP4 writing// 0.2.8 (2003-08-16): Added silence padding at the end, new libfaac doesn't do it itself// 0.2.7 (2003-08-16): MP4 fixes, now MP4 header stores correct length// 0.2.6 (2003-08-16): MP4 writing uses correct length for last frame// 0.2.5 (2003-08-15): "libfaac flushing" added for 0.2.2 removed, foo_mp4 was cutting the end away incorrectly// 0.2.4 (2003-08-09): Added direct M4A writing and one new mode to bandwidth list// uses LFE mode with 6 channels// 0.2.3 (2003-08-08): Doesn't write tech info to APEv2 tags anymore// stores original file length now also for AAC// no longer limited to 32bit storage for length// changes to config// 0.2.2 (2003-08-07): Flushes libfaac now properly to get gapless playback// fixed bandwidth selecting issues in config// 0.2.1 (2003-08-07): Fixed MP4 writing// 0.2 (2003-08-07): Added MP4 creation and tagging, reorganized config// reports libfaac version in component info string// 0.1 (2003-08-06): First public version#include <mp4.h>#include "../SDK/foobar2000.h"#include "resource.h"#include <commctrl.h>#include <faac.h>#include <version.h>#define FOO_FAAC_VERSION "0.4.2"#define FF_AAC 0#define FF_MP4 1#define FF_M4A 2#define FF_DEFAULT_OBJECTTYPE LOW#define FF_DEFAULT_MIDSIDE 1#define FF_DEFAULT_TNS 0#define FF_DEFAULT_QUANTQUAL 100#define FF_DEFAULT_AVGBRATE 64#define FF_DEFAULT_CUTOFF -1#define FF_DEFAULT_MP4CONTAINER FF_MP4#define FF_DEFAULT_USE_QQ 1#define FF_DEFAULT_USE_AB 0static cfg_int cfg_objecttype ( "objecttype", FF_DEFAULT_OBJECTTYPE );static cfg_int cfg_midside ( "midside", FF_DEFAULT_MIDSIDE );static cfg_int cfg_tns ( "tns", FF_DEFAULT_TNS );static cfg_int cfg_quantqual ( "quantqual", FF_DEFAULT_QUANTQUAL );static cfg_int cfg_avgbrate ( "avgbrate", FF_DEFAULT_AVGBRATE );static cfg_int cfg_cutoff ( "cutoff", FF_DEFAULT_CUTOFF );static cfg_int cfg_mp4container ( "mp4container", FF_DEFAULT_MP4CONTAINER );static cfg_int cfg_use_qq ( "use_qq", FF_DEFAULT_USE_QQ );static cfg_int cfg_use_ab ( "use_ab", FF_DEFAULT_USE_AB );DECLARE_COMPONENT_VERSION ( "FAAC encoder", FOO_FAAC_VERSION, "Uses libfaac version " FAAC_VERSION );class diskwriter_faac : public diskwriter {private: // mp4 MP4FileHandle MP4hFile; MP4TrackId MP4track; // faac faacEncHandle hEncoder; faacEncConfigurationPtr myFormat; unsigned int objectType; unsigned int useMidSide; unsigned int useTns; int cutOff; int bitRate; unsigned long quantqual; int use_qq, use_ab; int create_mp4; reader *m_reader; mem_block_t<unsigned char> bitbuf; mem_block_t<float> floatbuf; unsigned long samplesInput, maxBytesOutput; int *chanmap; unsigned int bufferedSamples; unsigned int frameSize; string8 path; file_info_i_full info; unsigned int srate, nch, bps; __int64 total_samples, encoded_samples, delay_samples; bool encode_error;public: diskwriter_faac() { objectType = cfg_objecttype; useMidSide = cfg_midside; useTns = cfg_tns; cutOff = cfg_cutoff; bitRate = cfg_avgbrate * 1000; quantqual = cfg_quantqual; use_qq = cfg_use_qq; use_ab = cfg_use_ab; hEncoder = 0; myFormat = 0; MP4hFile = 0; MP4track = 0; create_mp4 = cfg_mp4container; m_reader = 0; } ~diskwriter_faac() { if ( m_reader ) m_reader->reader_release(); } virtual const char *get_name() { return "AAC"; } virtual const char *get_extension() { switch ( create_mp4 ) { case FF_MP4: default: return "mp4"; case FF_M4A: return "m4a"; case FF_AAC: return "aac"; } } virtual int open ( const char *filename, metadb_handle *src_file ) { if ( m_reader ) return 0; encode_error = false; path = filename; if ( src_file ) src_file->handle_query ( &info ); else info.reset(); console::info ( "AAC encoding with FAAC version " FAAC_VERSION ); console::info ( string_printf ("Source file: %s", (const char *)info.get_file_path()) ); console::info ( string_printf ("Destination file: %s", (const char *)path) ); if ( path.is_empty() ) { console::error ( "No destination name" ); return 0; } m_reader = file::g_open ( path, reader::MODE_WRITE_NEW ); if ( !m_reader ) { console::error ( "Can't write to destination" ); return 0; } return 1; } virtual int process_samples ( const audio_chunk *src ) { if ( encode_error ) return 0; if ( !hEncoder ) { encode_error = true; nch = src->get_channels(); srate = src->get_srate(); // get faac version hEncoder = faacEncOpen ( 44100, 2, &samplesInput, &maxBytesOutput ); myFormat = faacEncGetCurrentConfiguration ( hEncoder ); if ( myFormat->version == FAAC_CFG_VERSION ) { //console::info ( string_printf ("libfaac version %s", (const char *)myFormat->name) ); faacEncClose ( hEncoder ); } else { console::error ( "Wrong libfaac version" ); faacEncClose ( hEncoder ); hEncoder = 0; return 0; } // open the encoder library hEncoder = faacEncOpen ( srate, nch, &samplesInput, &maxBytesOutput ); bufferedSamples = 0; frameSize = samplesInput / nch; total_samples = 0; encoded_samples = 0; delay_samples = frameSize; bitbuf.check_size ( maxBytesOutput ); floatbuf.check_size ( samplesInput ); chanmap = mkChanMap ( nch, 3/*chanC*/, 4/*chanLF*/ ); if ( cutOff <= 0 ) { if ( cutOff < 0 ) { cutOff = 0; } else { cutOff = srate / 2; } } if ( (unsigned)cutOff > (srate / 2) ) cutOff = srate / 2; // put the options in the configuration struct myFormat = faacEncGetCurrentConfiguration ( hEncoder ); myFormat->aacObjectType = objectType; myFormat->mpegVersion = (create_mp4 || objectType == LTP) ? MPEG4 : MPEG2; myFormat->useLfe = (nch == 6) ? 1 : 0; myFormat->useTns = useTns; myFormat->allowMidside = useMidSide; if ( use_ab ) myFormat->bitRate = bitRate; myFormat->bandWidth = cutOff; if ( use_qq ) myFormat->quantqual = quantqual; myFormat->outputFormat = create_mp4 ? 0 : 1; myFormat->inputFormat = FAAC_INPUT_FLOAT; if ( !faacEncSetConfiguration (hEncoder, myFormat) ) { console::error ( "Unsupported output format" ); return 0; } // initialize MP4 creation if ( create_mp4 ) { MP4hFile = MP4CreateCb ( 0, 0, 0, open_cb, close_cb, read_cb, write_cb, setpos_cb, getpos_cb, filesize_cb, (void *)m_reader ); if ( MP4hFile == MP4_INVALID_FILE_HANDLE ) { console::error ( "MP4Create() failed" ); return 0; } MP4SetTimeScale ( MP4hFile, 90000 ); MP4track = MP4AddAudioTrack ( MP4hFile, srate, MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE ); MP4SetAudioProfileLevel ( MP4hFile, 0x0F ); unsigned char *ASC = 0; unsigned long ASCLength = 0; faacEncGetDecoderSpecificInfo(hEncoder, &ASC, &ASCLength); MP4SetTrackESConfiguration ( MP4hFile, MP4track, (u_int8_t *)ASC, ASCLength ); } cutOff = myFormat->bandWidth; quantqual = myFormat->quantqual; bitRate = myFormat->bitRate; if ( bitRate > 0 ) { console::info ( string_printf ("Using quantizer quality %i and average bitrate of %i kbps per channel", quantqual, bitRate/1000) ); } else { console::info ( string_printf ("Using quantizer quality %i and no average bitrate control", quantqual, bitRate) ); } encode_error = false; } if ( srate != src->get_srate() || nch != src->get_channels() ) return 0; { unsigned int samples = src->get_sample_count() * nch; const audio_sample *s = src->get_data(); do { unsigned int num = (samples+bufferedSamples < samplesInput) ? samples+bufferedSamples : samplesInput; if ( num == 0 ) break; float *d = (float *)floatbuf.get_ptr() + bufferedSamples; for ( unsigned int i = bufferedSamples; i < num; i++ ) { *d++ = (float)((*s++) * 32768.); bufferedSamples++; samples--; } if ( bufferedSamples == samplesInput ) { if ( nch >= 3 && chanmap ) { chan_remap ( (int *)floatbuf.get_ptr(), nch, frameSize, chanmap ); } // call the actual encoding routine int bytesWritten = faacEncEncode ( hEncoder, (int32_t *)floatbuf.get_ptr(), samplesInput, bitbuf.get_ptr(), maxBytesOutput ); bufferedSamples = 0; if ( bytesWritten < 0 ) { console::error ( "faacEncEncode() failed" ); return 0; } if ( bytesWritten > 0 ) { MP4Duration dur = frameSize; MP4Duration ofs = 0; if ( delay_samples > 0 ) { dur = 0; ofs = delay_samples; delay_samples -= frameSize; } if ( create_mp4 ) { MP4WriteSample ( MP4hFile, MP4track, (const unsigned __int8 *)bitbuf.get_ptr(), bytesWritten, frameSize, ofs ); } else { m_reader->write ( bitbuf.get_ptr(), bytesWritten ); } encoded_samples += dur; } } } while ( bufferedSamples == 0 ); } total_samples += src->get_sample_count(); return 1; } virtual void flush() { if ( hEncoder ) { if ( nch >= 3 && chanmap ) { chan_remap ( (int *)floatbuf.get_ptr(), nch, bufferedSamples/nch, chanmap ); } __int64 samples_left = total_samples - encoded_samples; while ( samples_left > 0 ) { if ( !bufferedSamples ) { bufferedSamples = samplesInput; memset ( floatbuf.get_ptr(), 0, samplesInput * sizeof(float) ); } int bytesWritten = faacEncEncode ( hEncoder, (int32_t *)floatbuf.get_ptr(), bufferedSamples, bitbuf.get_ptr(), maxBytesOutput ); bufferedSamples = 0; if ( bytesWritten < 0 ) { console::error ( "faacEncEncode() failed" ); break; } else if ( bytesWritten > 0 ) { MP4Duration dur = samples_left > frameSize ? frameSize : samples_left; if ( create_mp4 ) { MP4WriteSample ( MP4hFile, MP4track, (const unsigned __int8 *)bitbuf.get_ptr(), bytesWritten, dur ); } else { m_reader->write ( bitbuf.get_ptr(), bytesWritten ); } samples_left -= frameSize; } } faacEncClose ( hEncoder ); hEncoder = 0; } if ( m_reader ) { bool success = !encode_error && (m_reader->get_length() > 0); if ( success ) { write_tag(); console::info ( "Encoding finished successfully" ); } if ( create_mp4 ) { MP4Close ( MP4hFile ); MP4hFile = 0; } m_reader->reader_release(); m_reader = 0; if ( !success ) { console::info ( "Encoding failed" ); file::g_remove ( path ); } } } virtual const char *get_config_page_name() { return "FAAC encoder"; }private: int write_tag() { info.info_remove_all(); if ( !create_mp4 ) { return tag_writer::g_run ( m_reader, &info, "ape" ); } else { MP4SetMetadataTool ( MP4hFile, "libfaac version " FAAC_VERSION ); for ( int i = 0; i < info.meta_get_count(); i++ ) { char *pName = (char *)info.meta_enum_name ( i ); const char *val = info.meta_enum_value ( i ); if ( !val || (val && !(*val)) ) continue;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -