HPC/並列プログラミングポータルでは、HPC(High Performance Computing)プログラミングや並列プログラミングに関する情報を集積・発信しています。

新着トピックス

Parallel Studioを活用したソフトウェアの並列化:AACエンコーダを高速化する

 プログラムの高速化が強く求められている分野の1つに、画像処理や音声処理、動画処理といったマルチメディア処理が挙げられる。これらのプログラムは処理すべきデータ量も多く、また行う処理も非常に複雑である。そのため、PCの処理速度が向上した現在でも「時間がかかる処理」の代表であり、このような処理にこそ並列化による処理速度の高速化が期待されている。本記事ではフリーの音声エンコーダ「FAAC」をインテルの並列プログラミング支援ツール「Parallel Studio」を使って並列化し、その処理速度を高速化する試みを紹介する。

並列処理による高速化が期待されるマルチメディア処理

 現在広く使われている音声符号化方式の1つに「AAC」がある。AACはMPEGによって規格化された音声圧縮方式で、高音質・高圧縮で音声・音楽を圧縮する目的で開発されている。AACは地上デジタル放送の音声圧縮方式として採用されているほか、携帯電話/携帯音楽プレーヤーなどでもサポートされるなど普及が進んでいる

 今回紹介するFAACはフリーソフトウェアのAACエンコーダ/デコーダであり、エンコーダ/デコーダ部を実装したライブラリ「libfaac」と、そのコマンドラインフロントエンド「faac」などから構成されている。WindowsおよびLinux、Mac OS XやFreeBSDなどを含む各種UNIXで利用でき、ライセンスはライブラリ部がLGPL、フロントエンド部分はGPLで提供されている。ソースコードはSourceForge.netで入手可能だ(図1)。

 AACのエンコード処理では、入力データに対してFFTによる直行変換や線形予測、聴覚心理モデルに基づいた量子化、ハフマン符号化といった複雑な処理を行うため、ある程度の処理時間を必要とする。たとえば、約4分のWAVE形式音楽ファイル(ファイルサイズは約40MB)を現在比較的ポピュラーであると思われるPC環境(Core 2 Duo/2.33GHz、メモリ2GB、Windows Vista Business SP2)で、faacの標準設定でエンコードしたところ、処理に必要な時間は約10秒程度だった(出力ファイルのサイズは約4.5MB)。

 AACに限らず、動画や音声といったデータはそのファイルサイズが大きく、またエンコード処理も複雑であるため、並列化によってその処理を高速化する試みが行われている。たとえばAppleの音楽管理ソフト「iTunes」に搭載されているMP3エンコーダなどは並列処理に対応しており、マルチコアCPUを搭載したPCでは高速にエンコード処理を実行できる。また、フリーのMP3エンコーダ「LAME」でも並列化による高速化の試みが行われているようだ。

 しかし、並列処理の実装には越えなければならないハードルも多く、一筋縄ではいかないことが多い。むやみに並列化を行ってもパフォーマンスが向上しない可能性があるほか、複数スレッドから同一のデータにアクセスすることによるデータの競合、メモリリークなどの問題が発生する可能性も増える。このような問題に対し効果的なツールが、インテルがリリースしている開発ツール「インテル Parallel Studio」(以下、Parallel Studio)である。本記事では、Parallel Studioを活用しながらfaacを並列化する試みを行う過程を紹介する。

Parallel Studioによる並列化

 faacはWindowsやLinux、Mac OS XなどさまざまなOS上でコンパイル/実行できるが、今回はParallel Studioを使用するため、Windows環境でVisual Studio 2008を使用して開発を行った。ベースとしたfaacのバージョンは1.28である。配布されているfaacのソースコードにはVisual Studio向けのプロジェクトファイルも付属しており、これを利用することでWindows環境でも簡単にコンパイルが可能だ(図2)。

 今回使用したParallel Studioは、自動並列化/最適化機能を備えたコンパイラやマルチスレッド対応デバッガ、パフォーマンス計測ツール、メモリ/スレッドに関する正当性チェッカなどから構成される開発支援ツールである。詳しくは過去の記事を参照してほしいが、ここでは下記の目的にParallel Studioを使用した。

  • 自動最適化によるパフォーマンス向上
  • 処理に時間のかかっている個所(ホットスポット)の検出
  • マルチスレッド化によって発生する、スレッド間でのデータ競合などの検出
  • メモリリークや不正なメモリの検出

インテル コンパイラーでコンパイルするだけでもパフォーマンスが向上

 Parallel Studioの「インテル Parallel Composer」コンポーネントには、強力な最適化機能で知られるインテル コンパイラーが含まれており、このコンパイラを利用するだけでパフォーマンスの向上が期待できる。Parallel Studioでは、Visual StudioのIDEからボタン1つで自動的にインテル コンパイラーでソフトウェアをコンパイルできるようになり、またGUIによるコンパイルオプション設定が可能だ。

 ソースコードの修正を行う前にまずはインテル コンパイラーでfaacをコンパイルしたところ、これだけでも大幅なパフォーマンス向上が確認できた(表1)。なお、ここでは約4分のWAVE形式音声ファイル(約40MB)を、faacのデフォルト設定でエンコードした際にかかった時間でその処理速度を比較している(表2)。

表1 コンパイラによる処理時間の違い
コンパイラ処理時間
Visual C++10.7秒
インテル コンパイラー8.8秒
表2 今回のテストに使用した条件
構成要素構成
CPUCore 2 Duo E6550(2.33GHz)
メモリ2GB
OSWindows Vista Business SP1
HDDSEAGATE ST3250310AS(250GB)
入力ファイルWAVE形式、約4分(40MB)
faacのコマンドラインオプションなし(デフォルト設定)

 なお、実行ファイルのパフォーマンスは、コンパイラの設定(コンパイルオプション)でも大きく変化する。ここでは、Visual C++およびインテル コンパイラーで最も強力と思われる最適化設定を用いてプログラムをコンパイルしている(図3、4)。

インテル Parallel Amplifierによる「ホットスポット」の特定

 処理の並列化を行うには、まずどの部分の処理を並列化するかを検討しなければならない。処理の並列化を行うためにはコードの修正という手間が必要なだけでなく、並列化によるオーバーヘッドのため逆に処理が遅くなる可能性もある。そのため、プログラム中で時間のかかる処理を特定し、その部分を並列実行するようにするのが定石だ。このような処理に時間のかかる個所は「ホットスポット」などと呼ばれ、「プロファイラ」と呼ばれるツールを利用することで特定できる。Parallel Studioには「インテル Parallel Amplifier」(以下、Parallel Amplifier)というプロファイラが含まれており、今回はこれを利用してプログラム中のホットスポットを見つけることにする。

 Parallel Amplifierを利用するには、Visual StudioのParallel Amplifierツールバーで計測する対象を指定し、ボタンをクリックするだけである(図5)。

 今回は計測対象として「Hotspots」を指定してParallel Amplifierを実行した。なお、Parallel Amplifierを使用する際にはコンパイル時にデバッグ情報を生成しておくように指定しておく。デバッグ情報を生成するには、プロジェクトのプロパティ中「C/C++」-「全般」の「デバッグ情報の形式」で「プログラム データベース(/Zi)」もしくは「エディット コンティニュ用プログラム データベース(/ZI)」を選択したうえで、「リンカ」-「デバッグ」の「デバッグ情報の生成」で「はい(/DEBUG)」を選択する(図6、7)。デバッグ情報がなくてもParallel Amplifierの実行自体は可能だが、その場合実行結果とソースコードが紐付けられないため、有用な情報が得られなくなってしまう。

 faacに対してParallel Amplifierを実行した結果は、図8のようになった。ここから、変換元ファイルからデータを読み出す「wav_read_float32()」関数やFFTを行う「rfft()」関数、AAC量子化関数「AACQuantize()」およびその中で呼ばれる「FixNoise()」関数などがfaacのホットスポットであることが分かる。

 また、Parallel Amplifierの実行結果ではそれぞれの関数がどのように呼び出されているかも確認できる(図9)。これによると今回ホットスポットとなっている関数はすべて「faacEncEncode()」関数、もしくはその関数内で使用している関数から呼び出されており、これらの関数に注目して並列化を行えばよい、ということが分かる。

OpenMPによる並列処理の実装

 Parallel Amplifierの実行結果より、wav_read_float32()関数やrfft()関数、FixNoise()関数、AACQuantize()関数などについて何らかの並列化を行えばよい、という指針が得られた。そこで、これらの関数を呼び出している関数に対して、並列化できる個所がないかを探して行くこととなる。

 ここでソースコードを分析してみると、fft_proc()関数は「PsyBufferUpdate()」関数から呼ばれており、FixNoise()関数は「AACQuantize()」関数、QuantizeBand()はFixNosie()関数内で呼び出されていることが分かった。さらに、PsyBufferUpdate()およびAACQuantize()関数はfaacEncEncode()関数内で呼び出されている(図10)。つまり、faac内では多数のループが呼びされているのだが、faacEncEncode()関数はこれらのループのうちもっとも外側で呼びだされているのである。

 そこで、まずはaacEncEncode()の実行を並列化し、複数のスレッドで同時実行する方針で実装を行うことにする。faacのエンコーダ部はスレッドセーフな実装になっているので、複数のエンコーダを用意し、読み込んだデータを並列にエンコードして読み込んだ順に出力する、という流れである。

 詳細については記事末のソースコードを参照してほしいが、変更を加えた点を簡単にまとめると下記のようになる。

  • エンコーダのインスタンスを複数用意
  • 処理のメインループを複数のスレッドで同時処理
  • 入力データは読み出した順にシーケンスIDを付け、エンコード後はシーケンスIDの順番にデータを出力

 このような並列化を行ったところ、先ほどと同じテスト環境(表2)では実行時間が約50%削減できることが確認できた(表3)。

表3 並列化による高速化結果
ソースコードコンパイラ実行時間
オリジナルfaacVisual C++10.7秒
インテル コンパイラー8.8秒
並列化版faacVisual C++5.3秒
インテル コンパイラー4.6秒

インテル Parallel Inspectorによるメモリエラー/スレッドエラーの確認

 以上のように並列化したプログラムは一見正しく動いているように見えるが、内部的に問題が発生している可能性もある。そのような問題を確認するため、「インテル Parallel Inspector」(以下、Parallel Inspector)を使用してエラーチェックを行った。

 Parallel Inspectorでは「Memory errors」および「Threading errors」という2つのテストが用意されている。また、テストの設定画面ではその粒度を指定できるが、今回は「40x-160x」および「80x-320x」を選択した。このスライダを下げるほど厳密なテストが行えるが、その分テストに必要な時間は長くなるため、状況に応じた設定でテストを行いたい。

 さて、これら2つのテストを実行したところ、いくつかのメモリリークと、いくつかのデータ競合が検出された(図11、12)。

 メモリリークについては、並列化に関係のない個所で問題が検出されている。また、スレッドについては並列実行部分にいくつかのデータ競合が確認できた。今回問題が検出されたは処理中の進捗状況メッセージを表示する部分だったため処理結果には影響しなかったのだが、そのために見過ごされていた個所である。

さらなる改善点を検出

 最後に、並列化を行ったfaacに対して再度Parallel Amplifierを実行し、どのようにプログラムの処理時間が変化したかを確認した(図13)。並列化後も各関数の実行時間には大きな変化はないものの、実行時間5.605秒に対して使用したCPU時間は9.849秒と、より効率よくプログラムを実行できていることが分かる。

Parallel Studioはマルチスレッドプログラミングや並列処理の実装に有用

 さて、以上のようにParallel Studioはfaacの並列化を行うにあたり、パフォーマンスの計測や実装指針の決定、バグの検出などに非常に有用であった。Parallel AmplifierやParallel Inspectorは1クリックで実行でき、またコンパイラの切り替えも非常に簡単である。

 今回の実装は並列化の第一歩であり、オプションの設定やMP4コンテナへの対応といった部分を実装しておらず、また音質の劣化などのの問題点も見られるため実用化までにはまだまだ遠いものの、実行時間が半分になるという非常に期待できる結果が得られた。プログラムによって並列化の難度は異なるとは思うが、並列化を行わずとも、インテル コンパイラーによる高速な実行ファイルの生成や、Parallel Inspectorによるメモリリークチェック機能などは有用だろう。Parallel Studioは現在評価版がWebサイトから無償ダウンロードできる。より高速なプログラムを作成したいという開発者の方は、ぜひ試してみてはいかがだろうか。

付録:変更を加えたあとのfaacのソースコード

リスト1 並列化版faacのmain()関数
int main(int argc, char *argv[])
{
    int frames, currentFrame;
    faacEncHandle hEncoder[_THREADS];
    pcmfile_t *infile = NULL;

    unsigned long samplesInput, maxBytesOutput, totalBytesWritten=0;

    faacEncConfigurationPtr myFormat;
    unsigned int mpegVersion = MPEG2;
    unsigned int objectType = LOW;
    unsigned int useMidSide = 1;
    static unsigned int useTns = DEFAULT_TNS;
    enum container_format container = NO_CONTAINER;
    int optimizeFlag = 0;
    enum stream_format stream = ADTS_STREAM;
    int cutOff = -1;
    int bitRate = 0;
    unsigned long quantqual = 0;
    int chanC = 3;
    int chanLF = 4;

    char *audioFileName = NULL;
    char *aacFileName = NULL;
    char *aacFileExt = NULL;
    int aacFileNameGiven = 0;

    float *pcmbuf;
    int *chanmap = NULL;

    unsigned char *bitbuf;
    int samplesRead = 0;
    const char *dieMessage = NULL;

    int rawChans = 0; // disabled by default
    int rawBits = 16;
    int rawRate = 44100;
    int rawEndian = 1;

    int shortctl = SHORTCTL_NORMAL;

    FILE *outfile = NULL;

    int seq_read = 0;
    int seq_write = 0;
    int nLp;

#ifdef HAVE_LIBMP4V2
    MP4FileHandle MP4hFile = MP4_INVALID_FILE_HANDLE;
    MP4TrackId MP4track = 0;
    unsigned int ntracks = 0, trackno = 0;
    unsigned int ndiscs = 0, discno = 0;
    u_int8_t compilation = 0;
    const char *artist = NULL, *title = NULL, *album = NULL, *year = NULL,
      *genre = NULL, *comment = NULL, *writer = NULL;
    u_int8_t *art = NULL;
    u_int64_t artSize = 0;
    u_int64_t total_samples = 0;
    u_int64_t encoded_samples = 0;
    unsigned int delay_samples;
    unsigned int frameSize;
#endif
    char *faac_id_string;
    char *faac_copyright_string;

#ifndef _WIN32
    // install signal handler
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
#endif

    // get faac version
    if (faacEncGetVersion(faac_id_string, faac_copyright_string) == FAAC_CFG_VERSION)
    {
        fprintf(stderr, "Freeware Advanced Audio Coder\nFAAC %s\n\n", faac_id_string);
    }
    else
    {
        fprintf(stderr, __FILE__ "(%d): wrong libfaac version\n", __LINE__);
        return 1;
    }

    /* begin process command line */
    progName = argv[0];
    while (1) {
        static struct option long_options[] = {
            { "help", 0, 0, 'h'},
            { "long-help", 0, 0, 'H'},
            { "raw", 0, 0, 'r'},
            { "no-midside", 0, 0, NO_MIDSIDE_FLAG},
            { "cutoff", 1, 0, 'c'},
            { "quality", 1, 0, 'q'},
            { "pcmraw", 0, 0, 'P'},
            { "pcmsamplerate", 1, 0, 'R'},
            { "pcmsamplebits", 1, 0, 'B'},
            { "pcmchannels", 1, 0, 'C'},
            { "shortctl", 1, 0, SHORTCTL_FLAG},
            { "tns", 0, 0, TNS_FLAG},
            { "no-tns", 0, 0, NO_TNS_FLAG},
            { "mpeg-version", 1, 0, MPEGVERS_FLAG},
            { "obj-type", 1, 0, OBJTYPE_FLAG},
            { "license", 0, 0, 'L'},
#ifdef HAVE_LIBMP4V2
            { "createmp4", 0, 0, 'w'},
            { "optimize", 0, 0, 's'},
            { "artist", 1, 0, ARTIST_FLAG},
            { "title", 1, 0, TITLE_FLAG},
            { "album", 1, 0, ALBUM_FLAG},
            { "track", 1, 0, TRACK_FLAG},
            { "disc", 1, 0, DISC_FLAG},
            { "genre", 1, 0, GENRE_FLAG},
            { "year", 1, 0, YEAR_FLAG},
            { "cover-art", 1, 0, COVER_ART_FLAG},
            { "comment", 1, 0, COMMENT_FLAG},
        { "writer", 1, 0, WRITER_FLAG},
        { "compilation", 0, 0, COMPILATION_FLAG},
#endif
        { "pcmswapbytes", 0, 0, 'X'},
            { 0, 0, 0, 0}
        };
        int c = -1;
        int option_index = 0;

        c = getopt_long(argc, argv, "Hhb:m:o:rnc:q:PR:B:C:I:X"
#ifdef HAVE_LIBMP4V2
                        "ws"
#endif
            ,long_options, option_index);

        if (c == -1)
            break;

        if (!c)
        {
          dieMessage = usage;
          break;
        }

        switch (c) {
    case 'o':
        {
            int l = strlen(optarg);
        aacFileName = malloc(l+1);
        memcpy(aacFileName, optarg, l);
        aacFileName[l] = '\0';
        aacFileNameGiven = 1;
        }
        break;
        case 'r': {
            stream = RAW_STREAM;
            break;
        }
        case NO_MIDSIDE_FLAG: {
            useMidSide = 0;
            break;
        }
        case 'c': {
            unsigned int i;
            if (sscanf(optarg, "%u", i)  0) {
                cutOff = i;
            }
            break;
        }
        case 'b': {
            unsigned int i;
            if (sscanf(optarg, "%u", i)  0)
            {
                bitRate = 1000 * i;
            }
            break;
        }
        case 'q':
        {
            unsigned int i;
            if (sscanf(optarg, "%u", i)  0)
            {
                if (i  0  i  1000)
                    quantqual = i;
            }
            break;
        }
        case 'I':
            sscanf(optarg, "%d,%d", chanC, chanLF);
            break;
        case 'P':
            rawChans = 2; // enable raw input
            break;
        case 'R':
        {
            unsigned int i;
            if (sscanf(optarg, "%u", i)  0)
            {
                rawRate = i;
                rawChans = (rawChans  0) ? rawChans : 2;
            }
            break;
        }
        case 'B':
        {
            unsigned int i;
            if (sscanf(optarg, "%u", i)  0)
            {
                if (i  32)
                    i = 32;
                if (i  8)
                    i = 8;
                rawBits = i;
                rawChans = (rawChans  0) ? rawChans : 2;
            }
            break;
        }
        case 'C':
        {
            unsigned int i;
            if (sscanf(optarg, "%u", i)  0)
                rawChans = i;
            break;
        }
#ifdef HAVE_LIBMP4V2
        case 'w':
        container = MP4_CONTAINER;
            break;
        case 's':
        optimizeFlag = 1;
            break;
    case ARTIST_FLAG:
        artist = optarg;
        break;
    case WRITER_FLAG:
        writer = optarg;
        break;
    case TITLE_FLAG:
        title = optarg;
        break;
    case ALBUM_FLAG:
        album = optarg;
        break;
    case TRACK_FLAG:
        sscanf(optarg, "%d/%d", trackno, ntracks);
        break;
    case DISC_FLAG:
        sscanf(optarg, "%d/%d", discno, ndiscs);
        break;
    case COMPILATION_FLAG:
        compilation = 0x1;
        break;
    case GENRE_FLAG:
        genre = optarg;
        break;
    case YEAR_FLAG:
        year = optarg;
        break;
    case COMMENT_FLAG:
        comment = optarg;
        break;
    case COVER_ART_FLAG: {
        FILE *artFile = fopen(optarg, "rb");

        if(artFile) {
            u_int64_t r;

            fseek(artFile, 0, SEEK_END);
        artSize = ftell(artFile);

        art = malloc(artSize);

            fseek(artFile, 0, SEEK_SET);
        clearerr(artFile);

        r = fread(art, artSize, 1, artFile);

        if (r != 1) {
            dieMessage = "Error reading cover art file!\n";
            free(art);
            art = NULL;
        } else if (artSize  12 || !check_image_header(art)) {
            /* the above expression checks the image signature */
            dieMessage = "Unsupported cover image file format!\n";
            free(art);
            art = NULL;
        }

        fclose(artFile);
        } else {
            dieMessage = "Error opening cover art file!\n";
        }

        break;
    }
#endif
        case SHORTCTL_FLAG:
            shortctl = atoi(optarg);
            break;
        case TNS_FLAG:
            useTns = 1;
            break;
        case NO_TNS_FLAG:
            useTns = 0;
            break;
    case MPEGVERS_FLAG:
            mpegVersion = atoi(optarg);
            switch(mpegVersion)
            {
            case 2:
                mpegVersion = MPEG2;
                break;
            case 4:
                mpegVersion = MPEG4;
                break;
            default:
            dieMessage = "Unrecognised MPEG version!\n";
            }
            break;
#if 0
    case OBJTYPE_FLAG:
        if (!strcasecmp(optarg, "LC"))
                objectType = LOW;
        else if (!strcasecmp(optarg, "Main"))
            objectType = MAIN;
        else if (!strcasecmp(optarg, "LTP")) {
            mpegVersion = MPEG4;
        objectType = LTP;
        } else
            dieMessage = "Unrecognised object type!\n";
        break;
#endif
        case 'L':
        fprintf(stderr, faac_copyright_string);
        dieMessage = license;
        break;
    case 'X':
      rawEndian = 0;
      break;
    case 'H':
      dieMessage = long_help;
      break;
    case 'h':
          dieMessage = short_help;
      break;
    case '?':
        default:
      dieMessage = usage;
          break;
        }
    }

    /* check that we have at least one non-option arguments */
    if (!dieMessage  (argc - optind)  1  aacFileNameGiven)
        dieMessage = "Cannot encode several input files to one output file.\n";

    if (argc - optind  1 || dieMessage)
    {
        fprintf(stderr, dieMessage ? dieMessage : usage,
           progName, progName, progName, progName);
        return 1;
    }

    while (argc - optind  0) {

    /* get the input file name */
    audioFileName = argv[optind++];

    /* generate the output file name, if necessary */
    if (!aacFileNameGiven) {
        char *t = strrchr(audioFileName, '.');
    int l = t ? strlen(audioFileName) - strlen(t) : strlen(audioFileName);

#ifdef HAVE_LIBMP4V2
    aacFileExt = container == MP4_CONTAINER ? ".m4a" : ".aac";
#else
    aacFileExt = ".aac";
#endif

    aacFileName = malloc(l+1+4);
    memcpy(aacFileName, audioFileName, l);
    memcpy(aacFileName + l, aacFileExt, 4);
    aacFileName[l+4] = '\0';
    } else {
        aacFileExt = strrchr(aacFileName, '.');

        if (aacFileExt  (!strcmp(".m4a", aacFileExt) || !strcmp(".m4b", aacFileExt) || !strcmp(".mp4", aacFileExt)))
#ifndef HAVE_LIBMP4V2
        fprintf(stderr, "WARNING: MP4 support unavailable!\n");
#else
        container = MP4_CONTAINER;
#endif
    }

    /* open the audio input file */
    if (rawChans  0) // use raw input
    {
        infile = wav_open_read(audioFileName, 1);
    if (infile)
    {
        infile-bigendian = rawEndian;
        infile-channels = rawChans;
        infile-samplebytes = rawBits / 8;
        infile-samplerate = rawRate;
        infile-samples /= (infile-channels * infile-samplebytes);
    }
    }
    else // header input
        infile = wav_open_read(audioFileName, 0);

    if (infile == NULL)
    {
        fprintf(stderr, "Couldn't open input file %s\n", audioFileName);
    return 1;
    }


    /* open the encoder library */
    for(nLp = 0; nLp  _THREADS; nLp++) {
    hEncoder[nLp] = faacEncOpen(infile-samplerate, infile-channels,
        samplesInput, maxBytesOutput);
    }

#ifdef HAVE_LIBMP4V2
    if (container != MP4_CONTAINER  (ntracks || trackno || artist ||
                       title ||  album || year || art ||
                       genre || comment || discno || ndiscs ||
                       writer || compilation))
    {
        fprintf(stderr, "Metadata requires MP4 output!\n");
    return 1;
    }

    if (container == MP4_CONTAINER)
    {
        mpegVersion = MPEG4;
    stream = RAW_STREAM;
    }

    frameSize = samplesInput/infile-channels;
    delay_samples = frameSize; // encoder delay 1024 samples
#endif
/*
    pcmbuf = (float *)malloc(samplesInput*sizeof(float));
    bitbuf = (unsigned char*)malloc(maxBytesOutput*sizeof(unsigned char));
    chanmap = mkChanMap(infile-channels, chanC, chanLF);
    if (chanmap)
    {
        fprintf(stderr, "Remapping input channels: Center=%d, LFE=%d\n",
            chanC, chanLF);
    }
*/

    if (cutOff = 0)
    {
        if (cutOff  0) // default
            cutOff = 0;
        else // disabled
            cutOff = infile-samplerate / 2;
    }
    if (cutOff  (infile-samplerate / 2))
        cutOff = infile-samplerate / 2;

    /* put the options in the configuration struct */
    myFormat = faacEncGetCurrentConfiguration(hEncoder[0]);
    myFormat-aacObjectType = objectType;
    myFormat-mpegVersion = mpegVersion;
    myFormat-useTns = useTns;
    switch (shortctl)
    {
    case SHORTCTL_NOSHORT:
      fprintf(stderr, "disabling short blocks\n");
      myFormat-shortctl = shortctl;
      break;
    case SHORTCTL_NOLONG:
      fprintf(stderr, "disabling long blocks\n");
      myFormat-shortctl = shortctl;
      break;
    }
    if (infile-channels = 6)
        myFormat-useLfe = 1;
    myFormat-allowMidside = useMidSide;
    if (bitRate)
        myFormat-bitRate = bitRate / infile-channels;
    myFormat-bandWidth = cutOff;
    if (quantqual  0)
        myFormat-quantqual = quantqual;
    myFormat-outputFormat = stream;
    myFormat-inputFormat = FAAC_INPUT_FLOAT;
    if (!faacEncSetConfiguration(hEncoder[0], myFormat)) {
        fprintf(stderr, "Unsupported output format!\n");
    #ifdef HAVE_LIBMP4V2
        if (container == MP4_CONTAINER) MP4Close(MP4hFile);
#endif
        return 1;
    }
    for(nLp = 1; nLp  _THREADS; nLp++) {
    faacEncSetConfiguration(hEncoder[nLp], myFormat);
    }


#ifdef HAVE_LIBMP4V2
    /* initialize MP4 creation */
    if (container == MP4_CONTAINER) {
        unsigned char *ASC = 0;
        unsigned long ASCLength = 0;
    char *version_string;

#ifdef MP4_CREATE_EXTENSIBLE_FORMAT
    /* hack to compile against libmp4v2 = 1.0RC3
     * why is there no version identifier in mp4.h? */
        MP4hFile = MP4Create(aacFileName, MP4_DETAILS_ERROR, 0);
#else
    MP4hFile = MP4Create(aacFileName, MP4_DETAILS_ERROR, 0, 0);
#endif
        if (!MP4_IS_VALID_FILE_HANDLE(MP4hFile)) {
            fprintf(stderr, "Couldn't create output file %s\n", aacFileName);
            return 1;
        }

        MP4SetTimeScale(MP4hFile, 90000);
        MP4track = MP4AddAudioTrack(MP4hFile, infile-samplerate, MP4_INVALID_DURATION, MP4_MPEG4_AUDIO_TYPE);
        MP4SetAudioProfileLevel(MP4hFile, 0x0F);
        faacEncGetDecoderSpecificInfo(hEncoder[0], ASC, ASCLength);
        MP4SetTrackESConfiguration(MP4hFile, MP4track, ASC, ASCLength);
    free(ASC);

    /* set metadata */
    version_string = malloc(strlen(faac_id_string) + 6);
    strcpy(version_string, "FAAC ");
    strcpy(version_string + 5, faac_id_string);
    MP4SetMetadataTool(MP4hFile, version_string);
    free(version_string);

    if (artist) MP4SetMetadataArtist(MP4hFile, artist);
    if (writer) MP4SetMetadataWriter(MP4hFile, writer);
    if (title) MP4SetMetadataName(MP4hFile, title);
    if (album) MP4SetMetadataAlbum(MP4hFile, album);
    if (trackno  0) MP4SetMetadataTrack(MP4hFile, trackno, ntracks);
    if (discno  0) MP4SetMetadataDisk(MP4hFile, discno, ndiscs);
    if (compilation) MP4SetMetadataCompilation(MP4hFile, compilation);
    if (year) MP4SetMetadataYear(MP4hFile, year);
    if (genre) MP4SetMetadataGenre(MP4hFile, genre);
    if (comment) MP4SetMetadataComment(MP4hFile, comment);
        if (artSize) {
        MP4SetMetadataCoverArt(MP4hFile, art, artSize);
        free(art);
    }
    }
    else
    {
#endif
        /* open the aac output file */
        if (!strcmp(aacFileName, "-"))
        {
            outfile = stdout;
        }
        else
        {
            outfile = fopen(aacFileName, "wb");
        }
        if (!outfile)
        {
            fprintf(stderr, "Couldn't create output file %s\n", aacFileName);
            return 1;
        }
#ifdef HAVE_LIBMP4V2
    }
#endif

    cutOff = myFormat-bandWidth;
    quantqual = myFormat-quantqual;
    bitRate = myFormat-bitRate;
    if (bitRate)
      fprintf(stderr, "Average bitrate: %d kbps\n",
          (bitRate + 500)/1000*infile-channels);
    fprintf(stderr, "Quantization quality: %ld\n", quantqual);
    fprintf(stderr, "Bandwidth: %d Hz\n", cutOff);
    fprintf(stderr, "Object type: ");
    switch(objectType)
    {
    case LOW:
        fprintf(stderr, "Low Complexity");
        break;
    case MAIN:
        fprintf(stderr, "Main");
        break;
    case LTP:
        fprintf(stderr, "LTP");
        break;
    }
    fprintf(stderr, "(MPEG-%d)", (mpegVersion == MPEG4) ? 4 : 2);
    if (myFormat-useTns)
        fprintf(stderr, " + TNS");
    if (myFormat-allowMidside)
        fprintf(stderr, " + M/S");
    fprintf(stderr, "\n");

    fprintf(stderr, "Container format: ");
    switch(container)
    {
    case NO_CONTAINER:
      switch(stream)
    {
    case RAW_STREAM:
      fprintf(stderr, "Headerless AAC (RAW)\n");
      break;
    case ADTS_STREAM:
      fprintf(stderr, "Transport Stream (ADTS)\n");
      break;
    }
        break;
#ifdef HAVE_LIBMP4V2
    case MP4_CONTAINER:
        fprintf(stderr, "MPEG-4 File Format (MP4)\n");
        break;
#endif
    }

    if (outfile
#ifdef HAVE_LIBMP4V2
        || MP4hFile != MP4_INVALID_FILE_HANDLE
#endif
       )
    {
        int showcnt = 0;
#ifdef _WIN32
        long begin = GetTickCount();
#endif
        if (infile-samples)
            frames = ((infile-samples + 1023) / 1024) + 1;
        else
            frames = 0;
        currentFrame = 0;

        fprintf(stderr, "Encoding %s to %s\n", audioFileName, aacFileName);
        if (frames != 0)
            fprintf(stderr, "   frame          | bitrate | elapsed/estim | "
            "play/CPU | ETA\n");
        else
            fprintf(stderr, " frame | elapsed | play/CPU\n");

        /* encoding loop */

/* ループここから */

    omp_set_num_threads(_THREADS); /* 2スレッド使用 */

/* begin OpenMP */
#pragma omp parallel private(samplesRead, pcmbuf, bitbuf, chanmap) firstprivate(samplesInput)
{
    int _THREAD_ID;
    int seq = 0;
    _THREAD_ID = omp_get_thread_num();
    pcmbuf = (float *)malloc(samplesInput*sizeof(float));
    bitbuf = (unsigned char*)malloc(maxBytesOutput*sizeof(unsigned char));
    chanmap = mkChanMap(infile-channels, chanC, chanLF);
    if (chanmap)
    {
        fprintf(stderr, "Remapping input channels: Center=%d, LFE=%d\n",
            chanC, chanLF);
    }
#ifdef _WIN32
    for (;;)
#else
        while (running)
#endif
        {
            int bytesWritten;

#pragma omp critical (sample_read)
{
            samplesRead = wav_read_float32(infile, pcmbuf, samplesInput, chanmap);
        seq = seq_read;
        seq_read++;
#ifdef HAVE_LIBMP4V2
            total_samples += samplesRead / infile-channels;
#endif
}
            if(samplesRead == 0)
        break;

            /* call the actual encoding routine */
            bytesWritten = faacEncEncode(hEncoder[_THREAD_ID],
                (int32_t *)pcmbuf,
                samplesRead,
                bitbuf,
                maxBytesOutput);

            if (bytesWritten)
            {
#pragma omp critical (inc_frames)
{
        currentFrame++;
                showcnt--;
        totalBytesWritten += bytesWritten;
}
        }

            if ((showcnt = 0) || !bytesWritten)
            {
                double timeused;
#ifdef __unix__
                struct rusage usage;
#endif
#ifdef _WIN32
                char percent[MAX_PATH + 20];
                timeused = (GetTickCount() - begin) * 1e-3;
#else
#ifdef __unix__
                if (getrusage(RUSAGE_SELF, usage) == 0) {
                    timeused = (double)usage.ru_utime.tv_sec +
                        (double)usage.ru_utime.tv_usec * 1e-6;
                }
                else
                    timeused = 0;
#else
                timeused = (double)clock() * (1.0 / CLOCKS_PER_SEC);
#endif
#endif
                if (currentFrame  (timeused  0.1))
                {
#pragma omp critical (inccnt)
{
                    showcnt += 50;
}
                    if (frames != 0)
                        fprintf(stderr,
                            "\r%5d/%-5d (%3d%%)|  %5.1f  | %6.1f/%-6.1f | %7.2fx | %.1f ",
                            currentFrame, frames, currentFrame*100/frames,
                ((double)totalBytesWritten * 8.0 / 1000.0) /
                ((double)infile-samples / infile-samplerate * currentFrame / frames),
                            timeused,
                            timeused * frames / currentFrame,
                            (1024.0 * currentFrame / infile-samplerate) / timeused,
                            timeused  * (frames - currentFrame) / currentFrame);
                    else
                        fprintf(stderr,
                            "\r %5d |  %6.1f | %7.2fx ",
                            currentFrame,
                            timeused,
                            (1024.0 * currentFrame / infile-samplerate) / timeused);

                    fflush(stderr);
#ifdef _WIN32
                    if (frames != 0)
                    {
                        sprintf(percent, "%.2f%% encoding %s",
                            100.0 * currentFrame / frames, audioFileName);
                        SetConsoleTitle(percent);
                    }
#endif
                }
            }

            /* all done, bail out */
            if (!samplesRead  !bytesWritten)
                break ;

            if (bytesWritten  0)
            {
                fprintf(stderr, "faacEncEncode() failed\n");
                break ;
            }

            if (bytesWritten  0)
            {
#ifdef HAVE_LIBMP4V2
                u_int64_t samples_left = total_samples - encoded_samples + delay_samples;
                MP4Duration dur = samples_left  frameSize ? frameSize : samples_left;
                MP4Duration ofs = encoded_samples  0 ? 0 : delay_samples;

                if (container == MP4_CONTAINER)
                {
                    /* write bitstream to mp4 file */
                    MP4WriteSample(MP4hFile, MP4track, bitbuf, bytesWritten, dur, ofs, 1);
                }
                else
                {
#endif
                    /* write bitstream to aac file */
            while(seq  seq_write) {
#pragma omp flush(seq_write)
                Sleep(0);
            }
#pragma omp critical (write_aac)
{
                    fwrite(bitbuf, 1, bytesWritten, outfile);
            seq_write++;
 }
#ifdef HAVE_LIBMP4V2
                }
#pragma omp critical (addsample)
{
                encoded_samples += dur;
}
#endif
            }
        else
        {
#pragma omp critical (write_aac)
{
            seq_write++;
 }
        }
        }
/* ループここまで */
    if (pcmbuf) free(pcmbuf);
    if (bitbuf) free(bitbuf);
} /* #pragma omp parallel */

#ifdef HAVE_LIBMP4V2
        /* clean up */
        if (container == MP4_CONTAINER)
        {
            MP4Close(MP4hFile);
            if (optimizeFlag == 1)
            {
                fprintf(stderr, "\n\nMP4 format optimization... ");
                MP4Optimize(aacFileName, NULL, 0);
                fprintf(stderr, "Done!");
            }
        } else
#endif
            fclose(outfile);

        fprintf(stderr, "\n\n");
    }

for(nLp = 0; nLp  _THREADS; nLp++) {
    faacEncClose(hEncoder[nLp]);
 }

    wav_close(infile);

/*
    if (pcmbuf) free(pcmbuf);
    if (bitbuf) free(bitbuf);
*/
    if (aacFileNameGiven) free(aacFileName);

    }

    return 0;
}