shithub: aacenc

Download patch

ref: 3cb3f492ded0659b8b7ecbbd1ec3de9159ef6d7a
parent: 395d360b40ff96e116c4f1ca4992e57906978572
author: menno <menno>
date: Sun Aug 17 15:55:04 EDT 2003

foo_faac plugin

diff: cannot open b/plugins/foo_faac//null: file does not exist: 'b/plugins/foo_faac//null'
--- /dev/null
+++ b/plugins/foo_faac/foo_faac.cpp
@@ -1,0 +1,728 @@
+// FAAC encoder for foobar2000 diskwriter
+// Copyright (C) 2003 Janne Hyv�rinen
+//
+// Changes:
+//  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.3"
+
+#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_BITRATE      0
+#define FF_DEFAULT_QUANTQUAL    100
+#define FF_DEFAULT_CUTOFF       -1
+#define FF_DEFAULT_MP4CONTAINER FF_MP4
+
+static 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_bitrate ( "bitrate", FF_DEFAULT_BITRATE );
+static cfg_int cfg_quantqual ( "quantqual", FF_DEFAULT_QUANTQUAL );
+static cfg_int cfg_cutoff ( "cutoff", FF_DEFAULT_CUTOFF );
+static cfg_int cfg_mp4container ( "mp4container", FF_DEFAULT_MP4CONTAINER );
+
+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 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;
+    unsigned __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_bitrate;
+        quantqual = cfg_quantqual;
+        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 ( path.is_empty() ) return 0;
+
+        m_reader = file::g_open ( path, reader::MODE_WRITE_NEW );
+        if ( !m_reader ) {
+            console::error ( string_printf ("Can't write to '%s'", (const char *)path) );
+            return 0;
+        }
+
+        if ( src_file ) src_file->handle_query ( &info ); else info.reset();
+        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 ( bitRate ) myFormat->bitRate = bitRate;
+            myFormat->bandWidth = cutOff;
+            if ( quantqual > 0 ) 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;
+
+            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();
+
+            while ( samples + bufferedSamples >= samplesInput ) {
+                float *d = (float *)floatbuf.get_ptr() + bufferedSamples;
+
+                for ( unsigned int i = bufferedSamples; i < samplesInput; i++ ) {
+                    *d++ = (float)((*s++) * 32768.);
+                }
+
+                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 );
+
+                if ( bytesWritten < 0 ) {
+                    console::error ( "faacEncEncode() failed" );
+                    bufferedSamples = 0;
+                    return 0;
+                }
+
+                if ( bytesWritten > 0 ) {
+                    MP4Duration dur = frameSize;
+
+                    if ( delay_samples > 0 ) {
+                        dur = 0;
+                        delay_samples -= frameSize;
+                    }
+
+                    if ( create_mp4 ) {
+                        MP4WriteSample ( MP4hFile, MP4track, (const unsigned __int8 *)bitbuf.get_ptr(), bytesWritten, dur );
+                    } else {
+                        m_reader->write ( bitbuf.get_ptr(), bytesWritten );
+                    }
+
+                    encoded_samples += dur;
+                }
+
+                samples -= (samplesInput - bufferedSamples);
+
+                bufferedSamples = 0;
+            }
+
+            if ( samples > 0 ) {
+                float *d = (float *)floatbuf.get_ptr() + bufferedSamples;
+
+                for ( unsigned int i = 0; i < samples; i++ ) {
+                    *d++ = (float)((*s++) * 32768.);
+                }
+
+                bufferedSamples += samples;
+            }
+        }
+
+        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 );
+            }
+
+            while ( encoded_samples < total_samples ) {
+                if ( !bufferedSamples ) { // libfaac doesn't output all samples unless fed with more data
+                    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 = (MP4Duration) (((total_samples - encoded_samples) < frameSize) ? (total_samples - encoded_samples) : frameSize);
+
+                    if ( create_mp4 ) {
+                        MP4WriteSample ( MP4hFile, MP4track, (const unsigned __int8 *)bitbuf.get_ptr(), bytesWritten, dur );
+                    } else {
+                        m_reader->write ( bitbuf.get_ptr(), bytesWritten );
+                    }
+
+                    encoded_samples += dur;
+                }
+            };
+
+            faacEncClose ( hEncoder );
+            hEncoder = 0;
+        }
+
+        if ( m_reader ) {
+            bool success = !encode_error && (m_reader->get_length() > 0);
+
+            if ( success ) write_tag();
+
+            if ( create_mp4 ) {
+                MP4Close ( MP4hFile );
+                MP4hFile = 0;
+            }
+
+            m_reader->reader_release();
+            m_reader = 0;
+
+            if ( !success ) 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 ) {
+            if ( total_samples > 0 ) {
+                char temp[64];
+                _ui64toa ( total_samples, temp, 10 );
+                info.meta_add ( "samples", temp );
+            }
+
+            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 ) continue;
+
+                if ( !stricmp (pName, "TITLE") ) {
+                    MP4SetMetadataName ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "ARTIST") ) {
+                    MP4SetMetadataArtist ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "WRITER") ) {
+                    MP4SetMetadataWriter ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "ALBUM") ) {
+                    MP4SetMetadataAlbum ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "YEAR") || !stricmp (pName, "DATE") ) {
+                    MP4SetMetadataYear ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "COMMENT") ) {
+                    MP4SetMetadataComment ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "GENRE") ) {
+                    MP4SetMetadataGenre ( MP4hFile, val );
+                }
+                else if ( !stricmp (pName, "TRACKNUMBER") ) {
+                    unsigned __int16 trkn = 0, tot = 0;
+                    sscanf ( val, "%d", &trkn );
+                    MP4SetMetadataTrack ( MP4hFile, trkn, tot );
+                }
+                else if ( !stricmp (pName, "DISKNUMBER") || !stricmp (pName, "DISC") ) {
+                    unsigned __int16 disk = 0, tot = 0;
+                    sscanf ( val, "%d", &disk );
+                    MP4SetMetadataDisk ( MP4hFile, disk, tot );
+                }
+                else if ( !stricmp (pName, "COMPILATION") ) {
+                    unsigned __int8 cpil = 0;
+                    sscanf ( val, "%d", &cpil );
+                    MP4SetMetadataCompilation ( MP4hFile, cpil );
+                }
+                else if ( !stricmp (pName, "TEMPO") ) {
+                    unsigned __int16 tempo = 0;
+                    sscanf ( val, "%d", &tempo );
+                    MP4SetMetadataTempo ( MP4hFile, tempo );
+                } else {
+                    MP4SetMetadataFreeForm ( MP4hFile, pName, (unsigned __int8*)val, strlen(val) );
+                }
+            }
+
+            return 1;
+        }
+    }
+
+    int *mkChanMap ( int channels, int center, int lf )
+    {
+        if ( !center && !lf ) return 0;
+        if ( channels < 3 ) return 0;
+
+        if ( lf > 0 ) {
+            lf--;
+        } else {
+            lf = channels - 1; // default AAC position
+        }
+
+        if ( center > 0 ) {
+            center--;
+        } else {
+            center = 0; // default AAC position
+        }
+
+        int *map = (int *)calloc ( channels, sizeof(map[0]) );
+
+        int outpos = 0;
+        if ( (center >= 0) && (center < channels) ) map[outpos++] = center;
+
+        int inpos = 0;
+        for ( ; outpos < (channels - 1); inpos++ ) {
+            if ( inpos == center ) continue;
+            if ( inpos == lf ) continue;
+
+            map[outpos++] = inpos;
+        }
+
+        if ( outpos < channels ) {
+            if ( (lf >= 0) && (lf < channels) ) {
+                map[outpos] = lf;
+            } else {
+                map[outpos] = inpos;
+            }
+        }
+
+        return map;
+    }
+
+    void chan_remap ( int *buf, unsigned int channels, unsigned int blocks, int *map )
+    {
+        int *tmp = (int *)alloca ( channels * sizeof(int) );
+
+        for ( unsigned int i = 0; i < blocks; i++ ) {
+            memcpy ( tmp, buf + i * channels, sizeof(int) * channels );
+
+            for ( unsigned int chn = 0; chn < channels; chn++ ) {
+                buf[i * channels + chn] = tmp[map[chn]];
+            }
+        }
+    }
+
+    // MP4 I/O callbacks
+    static unsigned __int32 open_cb ( const char *pName, const char *mode, void *userData ) { return 1; }
+
+    static void close_cb ( void *userData ) { return; }
+
+    static unsigned __int32 read_cb ( void *pBuffer, unsigned int nBytesToRead, void *userData )
+    {
+        reader *r = (reader *)userData;
+        return r->read ( pBuffer, nBytesToRead );
+    }
+
+    static unsigned __int32 write_cb ( void *pBuffer, unsigned int nBytesToWrite, void *userData )
+    {
+        reader *r = (reader *)userData;
+        return r->write ( pBuffer, nBytesToWrite );
+    }
+
+    static __int64 getpos_cb ( void *userData )
+    {
+        reader *r = (reader *)userData;
+        return r->get_position();
+    }
+
+    static __int32 setpos_cb ( unsigned __int32 pos, void *userData )
+    {
+        reader *r = (reader *)userData;
+        return !r->seek ( pos );
+    }
+
+    static __int64 filesize_cb ( void *userData )
+    {
+        reader *r = (reader *)userData;
+        return r->get_length();
+    }
+};
+
+// -------------------------------------
+
+typedef struct {
+    int     mp4;
+    char    name[4];
+} format_list_t;
+
+static format_list_t format_list[] = {
+    { FF_MP4, "MP4" },
+    { FF_M4A, "M4A" },
+    { FF_AAC, "AAC" },
+};
+
+typedef struct {
+    int     profile;
+    char    name[6];
+} profile_list_t;
+
+static profile_list_t profile_list[] = {
+    { LOW,  "LC"   },
+    { MAIN, "Main" },
+    { LTP,  "LTP"  },
+};
+
+typedef struct {
+    int     cutoff;
+    char    name[10];
+} cutoff_list_t;
+
+static cutoff_list_t cutoff_list[] = {
+    {     -1, "Automatic" },
+    {      0, "Full"      },
+    {  20000, "20000"     },
+    {  19000, "19000"     },
+    {  18000, "18000"     },
+    {  17000, "17000"     },
+    {  16000, "16000"     },
+    {  15000, "15000"     },
+    {  14000, "14000"     },
+};
+
+class config_faac : public config {
+    static void update ( HWND wnd )
+    {
+        int i;
+        HWND wnd_format = GetDlgItem ( wnd, IDC_FORMAT );
+        HWND wnd_profile = GetDlgItem ( wnd, IDC_PROFILE );
+        HWND wnd_cutoff = GetDlgItem ( wnd, IDC_CUTOFF );
+
+        for ( i = 0; i < sizeof(format_list)/sizeof(*format_list); i++ ) {
+            if ( (cfg_mp4container == format_list[i].mp4) ) {
+                uSendMessage ( wnd_format, CB_SETCURSEL, i, 0 );
+                break;
+            }
+        }
+
+        for ( i = 0; i < sizeof(profile_list)/sizeof(*profile_list); i++ ) {
+            if ( cfg_objecttype == profile_list[i].profile ) {
+                uSendMessage ( wnd_profile, CB_SETCURSEL, i, 0 );
+                break;
+            }
+        }
+
+        bool cutoff_found = false;
+
+        for ( i = 0; i < sizeof(cutoff_list)/sizeof(*cutoff_list); i++ ) {
+            if ( cfg_cutoff == cutoff_list[i].cutoff ) {
+                uSendMessage ( wnd_cutoff, CB_SETCURSEL, i, 0 );
+                cutoff_found = true;
+                break;
+            }
+        }
+
+        if ( !cutoff_found ) uSetDlgItemText ( wnd, IDC_CUTOFF, string_printf ("%i", (int)cfg_cutoff) );
+
+        uSendDlgItemMessage ( wnd, IDC_QUANTQUAL_SLIDER, TBM_SETPOS, 1, cfg_quantqual );
+        uSetDlgItemText ( wnd, IDC_QUANTQUAL_EDIT, string_printf ("%i", (int)cfg_quantqual) );
+
+        CheckDlgButton ( wnd, IDC_MIDSIDE, cfg_midside );
+        CheckDlgButton ( wnd, IDC_TNS, cfg_tns );
+    }
+
+    static BOOL CALLBACK ConfigProc ( HWND wnd, UINT msg, WPARAM wp, LPARAM lp )
+    {
+        HWND wnd_format = GetDlgItem ( wnd, IDC_FORMAT );
+        HWND wnd_profile = GetDlgItem ( wnd, IDC_PROFILE );
+        HWND wnd_cutoff = GetDlgItem ( wnd, IDC_CUTOFF );
+
+        switch ( msg ) {
+        case WM_INITDIALOG:
+            {
+                int i;
+
+                for ( i = 0; i < sizeof(format_list)/sizeof(*format_list); i++ ) {
+                    uSendMessageText ( wnd_format, CB_ADDSTRING, 0, format_list[i].name );
+                }
+
+                for ( i = 0; i < sizeof(profile_list)/sizeof(*profile_list); i++ ) {
+                    uSendMessageText ( wnd_profile, CB_ADDSTRING, 0, profile_list[i].name );
+                }
+
+                for ( i = 0; i < sizeof(cutoff_list)/sizeof(*cutoff_list); i++ ) {
+                    uSendMessageText ( wnd_cutoff, CB_ADDSTRING, 0, cutoff_list[i].name );
+                }
+
+                uSendDlgItemMessage ( wnd, IDC_QUANTQUAL_SLIDER, TBM_SETRANGE, 0, MAKELONG(10, 500) );
+
+                update ( wnd );
+            }
+            return 1;
+
+        case WM_COMMAND:
+            switch ( wp ) {
+            case IDC_FORMAT | (CBN_SELCHANGE<<16):
+                {
+                    int t = (int)uSendMessage ( wnd_format, CB_GETCURSEL, 0, 0 );
+                    if ( t >= 0 ) cfg_mp4container = format_list[t].mp4;
+                }
+                return 0;
+
+            case IDC_PROFILE | (CBN_SELCHANGE<<16):
+                {
+                    int t = (int)uSendMessage ( wnd_profile, CB_GETCURSEL, 0, 0 );
+                    if ( t >= 0 ) cfg_objecttype = profile_list[t].profile;
+                }
+                return 0;
+
+            case IDC_QUANTQUAL_EDIT | (EN_KILLFOCUS<<16):
+                {
+                    cfg_quantqual = GetDlgItemInt ( wnd, IDC_QUANTQUAL_EDIT, 0, 0 );
+                    if ( cfg_quantqual < 10 || cfg_quantqual > 500 ) {
+                        if ( cfg_quantqual < 10 ) {
+                            cfg_quantqual = 10;
+                        } else {
+                            cfg_quantqual = 500;
+                        }
+                        uSetDlgItemText ( wnd, IDC_QUANTQUAL_EDIT, string_printf ("%i", (int)cfg_quantqual) );
+                    }
+                    uSendDlgItemMessage ( wnd, IDC_QUANTQUAL_SLIDER, TBM_SETPOS, 1, cfg_quantqual );
+                }
+                return 0;
+
+            case IDC_CUTOFF | (CBN_SELCHANGE<<16):
+                {
+                    int t = (int)uSendMessage ( wnd_cutoff, CB_GETCURSEL, 0, 0 );
+                    if ( t >= 0 ) cfg_cutoff = cutoff_list[t].cutoff;
+                }
+
+            case IDC_CUTOFF | (CBN_KILLFOCUS<<16):
+                {
+                    int t = (int)uSendMessage ( wnd_cutoff, CB_GETCURSEL, 0, 0 );
+                    if ( t < 0 ) {
+                        cfg_cutoff = GetDlgItemInt ( wnd, IDC_CUTOFF, 0, 0 );
+                        if ( cfg_cutoff > 0 && cfg_cutoff < 100 ) {
+                            cfg_cutoff = 100;
+                            uSetDlgItemText ( wnd, IDC_CUTOFF, string_printf ("%i", (int)cfg_cutoff) );
+                        }
+                        else if ( cfg_cutoff <= 0 ) {
+                            uSendMessage ( wnd_cutoff, CB_SETCURSEL, 0, 0 );
+                            cfg_cutoff = cutoff_list[0].cutoff;
+                        }
+                    }
+                }
+                return 0;
+
+            case IDC_MIDSIDE:
+                {
+                    cfg_midside = !cfg_midside;
+                }
+                return 0;
+
+            case IDC_TNS:
+                {
+                    cfg_tns = !cfg_tns;
+                }
+                return 0;
+
+            case IDC_DEFAULTS:
+                {
+                    cfg_objecttype = FF_DEFAULT_OBJECTTYPE;
+                    cfg_midside = FF_DEFAULT_MIDSIDE;
+                    cfg_tns = FF_DEFAULT_TNS;
+                    cfg_bitrate = FF_DEFAULT_BITRATE;
+                    cfg_quantqual = FF_DEFAULT_QUANTQUAL;
+                    cfg_cutoff = FF_DEFAULT_CUTOFF;
+                    update ( wnd );
+                }
+                return 0;
+            }
+            break;
+
+        case WM_HSCROLL:
+            {
+                cfg_quantqual = uSendDlgItemMessage ( wnd, IDC_QUANTQUAL_SLIDER, TBM_GETPOS, 0, 0 );
+                uSetDlgItemText ( wnd, IDC_QUANTQUAL_EDIT, string_printf ("%i", (int)cfg_quantqual) );
+            }
+            return 0;
+        }
+
+        return 0;
+    }
+
+    virtual HWND create ( HWND parent ) {
+        return uCreateDialog ( IDD_CONFIG, parent, ConfigProc );
+    }
+
+    virtual const char *get_name() { return "FAAC encoder"; }
+
+    virtual const char *get_parent_name() { return "Diskwriter"; }
+};
+
+static service_factory_t<diskwriter, diskwriter_faac> foo_faac;
+static service_factory_single_t<config, config_faac> foo_faac_cfg;
--- /dev/null
+++ b/plugins/foo_faac/foo_faac.dsp
@@ -1,0 +1,121 @@
+# Microsoft Developer Studio Project File - Name="foo_faac" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=foo_faac - Win32 Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "foo_faac.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "foo_faac.mak" CFG="foo_faac - Win32 Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "foo_faac - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "foo_faac - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=xicl6.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF  "$(CFG)" == "foo_faac - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "foo_faac_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O1 /I "../../../faac/include" /I "../../../faac/libfaac" /I "..\..\..\faad2\common\mp4v2" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=xilink6.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /dll /machine:I386
+# Begin Special Build Tool
+SOURCE="$(InputPath)"
+PostBuild_Desc=UPX
+PostBuild_Cmds=upx --best --crp-ms=999999 Release\foo_faac.dll
+# End Special Build Tool
+
+!ELSEIF  "$(CFG)" == "foo_faac - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "foo_faac_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../../faac/include" /I "../../../faac/libfaac" /I "..\..\..\faad2\common\mp4v2" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=xilink6.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+
+!ENDIF 
+
+# Begin Target
+
+# Name "foo_faac - Win32 Release"
+# Name "foo_faac - Win32 Debug"
+# Begin Group "faac"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\faac\include\faac.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\faac\libfaac\version.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\foo_faac.cpp
+# ADD CPP /I "../SDK" /I "../../mpc/include" /I "../../ogg/include"
+# End Source File
+# Begin Source File
+
+SOURCE=.\foo_faac.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# End Target
+# End Project
--- /dev/null
+++ b/plugins/foo_faac/foo_faac.dsw
@@ -1,0 +1,125 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "foo_faac"=.\foo_faac.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name foobar2000_SDK
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pfc
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name foobar2000_component_client
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name libfaac
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name utf8api
+    End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name libmp4v2_cb
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "foobar2000_SDK"=..\SDK\foobar2000_SDK.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name utf8api
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Project: "foobar2000_component_client"=..\foobar2000_component_client\foobar2000_component_client.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "libfaac"=..\..\..\faac\libfaac\libfaac.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "libmp4v2_cb"=..\..\..\faad2\common\mp4v2\libmp4v2_cb.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "pfc"=..\..\pfc\pfc.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Project: "utf8api"=..\utf8api\utf8api.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+    Begin Project Dependency
+    Project_Dep_Name pfc
+    End Project Dependency
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
--- /dev/null
+++ b/plugins/foo_faac/foo_faac.rc
@@ -1,0 +1,114 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_CONFIG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 293
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 183
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CONFIG DIALOG DISCARDABLE  0, 0, 300, 190
+STYLE WS_CHILD
+FONT 8, "Tahoma"
+BEGIN
+    LTEXT           "Output format:",IDC_STATIC,13,16,54,8
+    COMBOBOX        IDC_FORMAT,68,14,53,50,CBS_DROPDOWNLIST | WS_VSCROLL | 
+                    WS_TABSTOP
+    LTEXT           "AAC profile:",IDC_STATIC,21,60,42,8
+    COMBOBOX        IDC_PROFILE,81,58,60,50,CBS_DROPDOWNLIST | CBS_SORT | 
+                    WS_VSCROLL | WS_TABSTOP
+    LTEXT           "Quantizer quality:",IDC_STATIC,21,44,60,8
+    EDITTEXT        IDC_QUANTQUAL_EDIT,220,42,32,12,ES_CENTER | 
+                    ES_AUTOHSCROLL | ES_NUMBER
+    LTEXT           "Bandwidth (Hz):",IDC_STATIC,21,76,56,8
+    CONTROL         "Use mid/side coding",IDC_MIDSIDE,"Button",
+                    BS_AUTOCHECKBOX | WS_TABSTOP,21,91,79,10
+    CONTROL         "Use TNS",IDC_TNS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
+                    21,104,43,10
+    COMBOBOX        IDC_CUTOFF,81,74,60,50,CBS_DROPDOWN | WS_VSCROLL | 
+                    WS_TABSTOP
+    CONTROL         "Slider2",IDC_QUANTQUAL_SLIDER,"msctls_trackbar32",
+                    WS_TABSTOP,84,42,132,11
+    GROUPBOX        "Encoder settings",IDC_STATIC,14,31,246,90
+    PUSHBUTTON      "Defaults",IDC_DEFAULTS,213,100,40,14
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
--- /dev/null
+++ b/plugins/foo_faac/resource.h
@@ -1,0 +1,26 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by foo_faac.rc
+//
+#define IDD_CONFIG                      116
+#define IDC_COMPRESSION                 1000
+#define IDC_FORMAT                      1002
+#define IDC_BPS                         1003
+#define IDC_PROFILE                     1004
+#define IDC_QUANTQUAL_EDIT              1006
+#define IDC_CUTOFF                      1007
+#define IDC_MIDSIDE                     1008
+#define IDC_TNS                         1009
+#define IDC_QUANTQUAL_SLIDER            1014
+#define IDC_DEFAULTS                    1015
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        105
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1016
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif