//-----------------------------------------------------------------
// driff.cpp:
//       RIFF 構造データの再生/表示(AVI/WAVE + MP3)
//       AVI: codec の使用(divx,xvid etc. に対応)
//       MP3: <MPEG 1,2,2.5 Layer I,II,III 対応>ACM->PCM
//                  Last Update: <2005/06/04 11:43:31 A.Murakami>
//-----------------------------------------------------------------
#include "driff.h"
//-----------------------------------------------------------------
// グローバル変数
//-----------------------------------------------------------------
DRIFF dr;
CDG_Global dggd;
HDRAWDIB dg_DDib;
//-----------------------------------------------------------------
// 一般関数
//-----------------------------------------------------------------
void TRACE(char *fmt,...)
{
    char szBuff[BUFSIZ];
    va_list ap;
    va_start(ap,fmt);
    vsprintf(szBuff,fmt,ap);
#ifdef _MSC_VER
    OutputDebugString(szBuff);
#else
    fprintf(stderr,szBuff);
#endif
    va_end(ap);
}

// mmr
int err_check(MMRESULT mmr)
{
    switch(mmr) {
    case MMSYSERR_NOERROR: return TRUE;
    case MMSYSERR_INVALPARAM:
        MessageBox(NULL,"Invalid parameters passed to acmStreamOpen",0,0);
        return FALSE;
    case ACMERR_NOTPOSSIBLE:
        MessageBox(NULL,"No ACM filter found capable of decoding MP3",0,0);
        return FALSE;
    default:
        MessageBox(NULL,"Some error opening ACM decoding stream!",0,0);
        return FALSE;
    }
    return TRUE;
}


BOOL CALLBACK acmDrvFind(HACMDRIVERID hadid,DWORD dwInst,DWORD fdwSupport)
{
    if(fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) {
        MMRESULT mmr;
        ACMDRIVERDETAILS details;
        HACMDRIVER driver;
        UINT i;
        details.cbStruct = sizeof(ACMDRIVERDETAILS);
        mmr = acmDriverDetails(hadid,&details,0);
        mmr = acmDriverOpen(&driver,hadid,0);
        for(i=0;i<details.cFormatTags;i++) {
            ACMFORMATTAGDETAILS fmtDetails;
            ZeroMemory(&fmtDetails,sizeof(fmtDetails));
            fmtDetails.cbStruct = sizeof(ACMFORMATTAGDETAILS);
            fmtDetails.dwFormatTagIndex = i;
            mmr = acmFormatTagDetails(driver,&fmtDetails,
                                      ACM_FORMATTAGDETAILSF_INDEX);
            if(fmtDetails.dwFormatTag == WAVE_FORMAT_MPEGLAYER3){
                TRACE("Found an MP3-capable ACM codec: %s\n",
                      details.szLongName);
                dggd.g_mp3Drivers++;
            }
        }
        mmr = acmDriverClose(driver,0);
    }
    return TRUE;
}

void err_avi(int err)
{
    switch(err){
    case AVIERR_OK: return;
    case AVIERR_BUFFERTOOSMALL:
        TRACE("cbBuffer パラメータに、データの1 つのサンプルよりも"
              "小さいバッファサイズが指定されています。\n");
        break;
    case AVIERR_MEMORY:
        TRACE("メモリが不足しているため、読み取り操作を完了"
              "できませんでした。\n");
        break;
    case AVIERR_FILEREAD:
        TRACE("ファイルの読み取り中にディスクエラーが発生しました。\n");
        break;
    }
}

int check_suffix(char* filename,char* ext)
{
    static char buf[256]="";
    int i=strlen(filename);
    buf[0] = '\0';
    while(1){ if(filename[i--] == '.') break; }
    if(i<=0) return FALSE;
    strncpy(buf,filename+i+2,strlen(filename)-i-1);
    if(!strcmp(buf,ext)) return TRUE;
    return FALSE;
}

void set_win(HWND mwh,HWND edwh)
{
    dggd.AppHwnd = mwh;
    dggd.edHwnd  = edwh;
}

void set_info(char* sbuf)
{
    SetWindowText(dggd.edHwnd,sbuf);
}

void cat_info(char* sbuf)
{
    int tlen;
    static char wbuf[BUFSIZ];
    wbuf[0] = '\0';
    tlen = GetWindowText(dggd.edHwnd,wbuf,BUFSIZ);
    strcat(wbuf+tlen,sbuf);
    SetWindowText(dggd.edHwnd,wbuf);
}

char* getfps()
{
    static DWORD last   = 0;
    static DWORD frames = 0;
    static char  buf[256] = "";
    DWORD current = timeGetTime(); // 現時刻
    frames++;
    // 0.5秒毎に更新
    if(500 <= current - last) {
        double  dt = (double)(current - last) / 1000.0f;
        double fps = (double)frames / dt;
        last = current;
        frames = 0;
        sprintf(buf,"%.02f fps",fps);
    }
    return buf;
}

char* getwif()
{
    static char buf[256];
    buf[0] = '\0';
    switch(dggd.dg_type){
    case DRIFF_AVI:
        if(!dggd.avi_audio) break;
        wsprintf(buf,": WAVE %dHz %dbit",dggd.wave_hlz,dggd.wave_bit);
        break;
    default:
        wsprintf(buf,": WAVE %dHz %dbit",dggd.wave_hlz,dggd.wave_bit);
        break;
    }
    return buf;
}

int dg_fileopen(HWND hWnd)
{
    DWORD old_type = dggd.dg_type;
    static char filename[MAX_PATH];
    dggd.dg_type = 0;
    filename[0]  = '\0';
    //--------------------------------------------------
    // format of file open
    //--------------------------------------------------
    OPENFILENAME file;
    memset(&file,0,sizeof(OPENFILENAME));
    file.lStructSize    = sizeof(OPENFILENAME);
    file.hwndOwner      = hWnd;
    file.lpstrFilter    = 
        "AVI ファイル(*.avi)\0*.avi\0"
        "WAVE ファイル(*.wav)\0*.wav\0"
        "MP3 ファイル(*.mp3)\0*.mp3\0"
        "全てのファイル(*.*)\0*.*\0\0";
    file.lpstrFile      = filename;
    file.lpstrFileTitle = "読み込み";
    file.nMaxFile       = sizeof(filename);
    file.Flags          = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
    file.lpstrDefExt    = "avi";
    file.lpstrTitle     = "開く";
    //--------------------------------------------------
    // open dialog
    //--------------------------------------------------
    if(!GetOpenFileName(&file)){
        dggd.dg_type = old_type;
        if(old_type) dg_draw(hWnd);
        return FALSE;
    }
    //--------------------------------------------------
    // open file
    //--------------------------------------------------
    dg_close();
    if(!dr.Open(filename)) {
        dggd.dg_type = old_type;
        if(old_type) dg_draw(hWnd);
        return FALSE;
    }
    //--------------------------------------------------
    // set datas
    //--------------------------------------------------
    dggd.ds       = *dr.AviGetSize();
    dggd.waittime = 1/dr.AviGetFps();
    dggd.dg_type  = dr.GetType();
    strcpy(dggd.dg_fname,filename);
    // title
    static char t_str[256];
    wsprintf(t_str,"%s %s",dggd.dg_fname,getwif());
    SetWindowText(hWnd,t_str);
    //--------------------------------------------------
    // AVI画像/WAVE音声の再生/表示
    //--------------------------------------------------
    dg_draw(hWnd);
    return TRUE;
}

void dg_close()
{
    dggd.dg_type = 0;
    WaitForSingleObject(dggd.dg_hthr,1000); // 描画スレッドが終了するまで待機
    dr.Close();
    InvalidateRect(dggd.AppHwnd,NULL,TRUE);
    PostMessage(dggd.AppHwnd,WM_PAINT,(unsigned int)NULL,(long int)NULL);
}

void dg_isave(HWND hWnd)
{
    if(dggd.dg_type != DRIFF_AVI) return;
    static char filename[MAX_PATH];
    dggd.dg_type = 0;
    filename[0]  = '\0';
    //--------------------------------------------------
    // format of file save
    //--------------------------------------------------
    OPENFILENAME file;
    memset(&file,0,sizeof(OPENFILENAME));
    file.lStructSize    = sizeof(OPENFILENAME);
    file.hwndOwner      = hWnd;
    file.lpstrFilter    = 
        "BMP ファイル(*.bmp)\0*.bmp\0"
        "全てのファイル(*.*)\0*.*\0\0";
    file.lpstrFile      = filename;
    file.lpstrFileTitle = "画像保存";
    file.nMaxFile       = sizeof(filename);
    file.Flags          = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
    file.lpstrDefExt    = "bmp";
    file.lpstrTitle     = "保存";
    // open dialog
    GetOpenFileName(&file);
    // save image
    LPVOID a;
    if(!dr.AviRead2(&a)){
        MessageBox(NULL,"画像読込み失敗",0,0);
        return;
    }
    SaveBMP(filename,(LPBYTE)a);
    // restart playing
    dggd.dg_type = DRIFF_AVI;
    dg_draw(hWnd);
}

// ビットマップの保存
UINT SaveBMP(char* filename,LPBYTE lpBuf)
{
    HANDLE hdlBmp;
    BITMAPFILEHEADER bfhBmp;
    LPBITMAPINFOHEADER bih = (LPBITMAPINFOHEADER)lpBuf;
    LONG offset = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    LONG cpsize = bih->biClrUsed*sizeof(RGBQUAD);
    UINT bCnt   = bih->biBitCount,bPln = bih->biPlanes;
    DWORD dwdPixelsSize,wSize;
    // 画像サイズ
    dwdPixelsSize = BMPLL(bih)*bih->biHeight;
    // ファイルを開く
    hdlBmp = CreateFile(filename,GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,NULL);
    if(hdlBmp==INVALID_HANDLE_VALUE){
        MessageBox(NULL,"書込みに失敗しました",NULL,MB_OK);
        return FALSE;
    }
    // ヘッダ情報
    bfhBmp.bfType      = 'M'*256+'B';
    bfhBmp.bfSize      = offset + cpsize + dwdPixelsSize;
    bfhBmp.bfReserved1 = 0;
    bfhBmp.bfReserved2 = 0;
    bfhBmp.bfOffBits   = offset + cpsize;
    // 書込み
    dwdPixelsSize += sizeof(BITMAPINFOHEADER) + cpsize;
    WriteFile(hdlBmp,&bfhBmp,sizeof(BITMAPFILEHEADER),&wSize,NULL);
    WriteFile(hdlBmp,lpBuf,sizeof(BYTE)*dwdPixelsSize,&wSize,NULL);
    // 後片付け
    CloseHandle(hdlBmp);
    return TRUE;
}

void set_wavedraw(int dgw)
{
    dggd.dgw_type = dgw;
}

ULONG __stdcall DG_thread(void* p_arg)
{
    HWND hWnd = *(HWND*)p_arg;
    HDC hdc; PAINTSTRUCT ps;
    RECT rdr,wr,wer,pR;
    //DrawDibBegin(dg_DDib,hdc,-1,-1,dibh,-1,-1,0);
    // 連続再生
    while(dggd.dg_type){
        hdc = BeginPaint(dggd.AppHwnd,&ps);
        if(dggd.dg_type == DRIFF_WIN) break;
        SetRect(&pR,0,
                (dggd.ds.y)?dggd.ds.y+5:0,
                dggd.ds.x,(dggd.ds.y)?(dggd.ds.y+100):100);
        GetClientRect(hWnd,&wr);
        GetWindowRect(dggd.edHwnd,&wer);
        pR.right = wr.right-(wer.right-wer.left);
        // 再描画領域
        SetRect(&rdr,0,0,pR.right,wr.bottom);
        // 各種描画
        if(dggd.dg_type == DRIFF_AVI) {
            LPVOID a;
            double iscale =
                (dggd.ds.x>pR.right)? pR.right/(double)dggd.ds.x : 1.0;
            pR.top    = wr.bottom-100;//(int)(ds.y*iscale) + 5;
            pR.bottom = wr.bottom;//(int)(ds.y*iscale) + 100;
            if(pR.top < 0) pR.top = 0;
            if(pR.bottom > wr.bottom) pR.bottom = wr.bottom;
            // 音声出力
            if(dggd.avi_audio){
                int wait = 2;
                if(dr.AviGetFps() < 10) wait = 1;
                if(dr.AviGetFps() > 60) wait = 30;
                if(dr.AviGetCount() % wait == 0){
                    dr.WavePlay(wait*dggd.waittime,TRUE);
                    dr.DrawWave(hdc,&pR);
                    if(dr.WaveEof()) dr.WaveSeek(0);
                }
            }
            // 画像読込み
            if(!dr.AviRead(&a)){
                dr.Seek(0);
                Sleep(1000); // 小休止
                continue;
            }
            // DIB描画用
            LPBITMAPINFOHEADER lpbi = (LPBITMAPINFOHEADER)a;
            int bsz = lpbi->biSize + lpbi->biClrUsed * sizeof(RGBQUAD);
            LPBYTE start = (LPBYTE)a + bsz;
            // DIB描画
            DrawDibDraw(dg_DDib,       // DrawDIB のハンドル
                        hdc,           // デバイスコンテキスト
                        0,0,           // 転送先の先頭座標
                        pR.right,      // 転送先の幅(int)(iscale*ds.x)
                        pR.bottom-105, // 転送先の高さ
                        lpbi,          // BITMAPINFOHEADER構造体
                        (LPVOID)start, // 転送元画像データ
                        0,0,           // 転送元の先頭画素
                        dggd.ds.x,dggd.ds.y, // 転送画素の行と列数
                        DDF_BUFFER
                        //DDF_SAME_HDC
                        //| DDF_SAME_DRAW // 描画用フラグ
                );
            // 表示待ち
            if(dggd.avi_audio==FALSE) Sleep((int)(1000.*dggd.waittime/2));
        } else if(dggd.dg_type == DRIFF_WAVE || dggd.dg_type == DRIFF_MP3) {
            dr.WavePlay(0.1,TRUE); // set sec
            if(dr.WaveEof()) {
                dr.WaveSeek(0);
                Sleep(1000); // 小休止
            }
            dr.DrawWave(hdc,&pR);
        }
        // FPS 表示
        //static char t_str[256];
        //wsprintf(t_str,"%s [%s%s]",dg_fname,getfps(),getwif());
        //SetWindowText(hWnd,t_str);
        // 再描画
        InvalidateRect(hWnd,&rdr,FALSE);
        InvalidateRect(dggd.edHwnd,NULL,FALSE);
        EndPaint(hWnd,&ps);
    }
    //DrawDibClose(dg_DDib);
    return 0;
}

void dg_draw(HWND hWnd)
{
    if(!dggd.dg_type || dggd.dg_type == DRIFF_WIN) return;
    // 描画用スレッドの開始
    DWORD tid;
    dggd.dg_hthr = CreateThread(NULL,0,DG_thread,&hWnd,0,&tid);
    SetThreadPriority(dggd.dg_hthr,THREAD_PRIORITY_ABOVE_NORMAL);
    // 再描画
    InvalidateRect(hWnd,NULL,TRUE);
    PostMessage(hWnd,WM_PAINT,0,0);
}

void dg_wavein(HWND hWnd)
{
    if(dggd.dg_type == DRIFF_WIN) return;
    dg_close();
    if(!dr.WaveInOpen()){
        MessageBox(NULL,"初期化失敗","WAVE IN",MB_OK);
    }
    dggd.dg_type = DRIFF_WIN;
    dggd.ds.x = 0; dggd.ds.y = 0;
    // テキスト描画
    static char t_str[256];
    wsprintf(t_str,"WAVE 音声入力%s",getwif());
    SetWindowText(hWnd,t_str);
    // 再描画
    InvalidateRect(hWnd,NULL,TRUE);
    PostMessage(hWnd,WM_PAINT,0,0);
}

void dg_windraw(HWND hWnd)
{
    if(dggd.dg_type != DRIFF_WIN) return;
    if(!dr.WaveInPlay()) return;
    // 描画領域
    RECT wr,wer,rdr,pR;
    SetRect(&pR,0,0,0,100);
    GetClientRect(hWnd,&wr);
    GetWindowRect(dggd.edHwnd,&wer);
    pR.right = wr.right-(wer.right-wer.left);
    SetRect(&rdr,0,0,pR.right,wr.bottom);
    // 描画
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd,&ps);
    dr.DrawWave(hdc,&pR);
    EndPaint(hWnd,&ps);
    // 再描画
    InvalidateRect(hWnd,&rdr,FALSE);
    PostMessage(dggd.edHwnd,WM_PAINT,0,0);
}
//-----------------------------------------------------------------
//
// Class of DRIFF
//          AVI draw graphic and playing wave(PCM) sound
//
//-----------------------------------------------------------------
DRIFF::DRIFF()
{
    file = NULL;
    type = 0;
    memset(&movie,0,sizeof(movie));
    memset(&wave,0,sizeof(wave));
    wave.buf = NULL;
    wave.fft = NULL;
    WaveOutInit();
    WaveInInit();
    // find an MP3 codec
    acmDriverEnum(acmDrvFind,0,0);
    if(dggd.g_mp3Drivers == 0){
        MessageBox(NULL,"No MP3 decoders found!",0,0);
    }
}

DRIFF::~DRIFF()
{
    Close();
}

int DRIFF::Open(char *filename)
{
    // ファイルを開く
    file = fopen(filename,"rb");
    if(file == NULL) return FALSE;
    // ファイル情報
    int ret;
    char buf[256];
    sprintf(buf,"FILE: %s\r\n",filename);
    set_info(buf);
    if(check_suffix(filename,"avi") || check_suffix(filename,"wav")){
        // RIFF チャンクの読み込み
        fread(&head[0],1,sizeof(DRIFF_HEAD),file);
        if(head[0].ckID == mmioFOURCC('R','I','F','F')) {
            fread(&type,1,sizeof(DWORD),file);
            if(type == DRIFF_WAVE)     ret=WaveOpen(filename); // WAVE
            else if(type == DRIFF_AVI) ret=AviOpen(filename);  // AVI
        } else ret = FALSE;
        if(ret==FALSE) sprintf(buf,"RIFF:形式が異なります.");
    } else if(check_suffix(filename,"mp3")){ // [RIFF] 以外
        ret = MP3Open(filename);
        if(ret==FALSE) sprintf(buf,"MP3:形式が異なります.");
    } else {
        ret = FALSE; sprintf(buf,"未対応");
    }
    if(ret==FALSE) {
        type = 0; set_info(""); Close();
        //MessageBox(NULL,buf,"入力ファイル",MB_OK);
        return FALSE;
    }
    return TRUE;
}

int DRIFF::Close()
{
    if(file != NULL)    { fclose(file); file = NULL; }
    if(movie.init_flag) { AviClose(); movie.init_flag = FALSE; }
    if(wave.init_flag)  { WaveClose(); wave.init_flag = FALSE; }
    if(wave.in_flag)    { WaveInClose(); wave.in_flag = FALSE; }
    type = 0;
    memset(&movie,0,sizeof(movie));
    memset(&wave,0,sizeof(wave));
    return TRUE;
}

int DRIFF::Seek(double sec)
{
    AviSeek(sec);
    WaveSeek(sec);
    return TRUE;
}

int DRIFF::GetType()
{
    return type;
}
//-----------------------------------------------------------------
//
// AVI 画像関数
//
//-----------------------------------------------------------------
int DRIFF::AviOpen(char *filename)
{
    WAVEFORMATEX cpf;
    //--------------------------------------------------
    // VFW 関数(Video for Windows)でAVIファイルを開く
    //--------------------------------------------------
    AVIFileInit();
    if(AVIFileOpen(&movie.file,filename,OF_READ|OF_SHARE_DENY_NONE,NULL)){
        MessageBox(NULL,"AVIファイルエラー","入力ファイル",MB_OK);
        return FALSE;
    }
    // VIDEO & AUDIO ストリームを開く
    AVIFileGetStream(movie.file,&movie.ps_movie,streamtypeVIDEO,0);
    AVIFileGetStream(movie.file,&movie.ps_audio,streamtypeAUDIO,0);
    AVIFileInfo(movie.file,&movie.info,sizeof(AVIFILEINFO));
    // VIDEO ストリームを開く(2)
    if(movie.ps_movie) {
        cat_info("**VIDEO\r\n");
        // read format
        AVIStreamFormatSize(movie.ps_movie,0,&movie.FmtLeng);
        if(movie.lpSrcFmt) GlobalFree(movie.lpSrcFmt);
        if(dggd.dibh) GlobalFree(dggd.dibh);
        movie.lpSrcFmt = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,movie.FmtLeng);
        dggd.dibh = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,movie.FmtLeng);
        AVIStreamReadFormat(movie.ps_movie,0,movie.lpSrcFmt,&movie.FmtLeng);
        memcpy(dggd.dibh,movie.lpSrcFmt,movie.FmtLeng);
        // frame open
        movie.movie_frame = AVIStreamGetFrameOpen(movie.ps_movie,NULL);
        if(movie.movie_frame == NULL){
            if(!(movie.codec=codecOpen())){
                MessageBox(NULL,"AVI未対応","入力ファイル",MB_OK);
                AviClose();
                return FALSE;
            }
        }
        static char buf[256];
        sprintf(buf,"  size: %dx%d[%dbit]\r\n",movie.info.dwWidth,
                movie.info.dwHeight,movie.lpSrcFmt->biBitCount);
        cat_info(buf);
        sprintf(buf,"  FPS: %.1lf\r\n",AviGetFps());
        cat_info(buf);
    } else { AviClose(); return FALSE; }
    // AUDIO ストリームを開く(2)
    if(movie.ps_audio) {
        LONG len;
        AVIStreamReadFormat(movie.ps_audio,0,NULL,&len);
        wave.format_src = (WAVEFORMATEX *)malloc(len);
        AVIStreamReadFormat(movie.ps_audio,0,wave.format_src,&len);
        //--------------------------------------------------
        // wave format compression setup.
        //--------------------------------------------------
        AVICOMPRESSOPTIONS opt;
        WAVEFORMATEX* f = wave.format_src;
        cpf = *wave.format_src;
        if(f->wFormatTag != WAVE_FORMAT_PCM){
            f->wFormatTag      = WAVE_FORMAT_PCM;
            f->wBitsPerSample  = f->wBitsPerSample<12?8:16;
            f->nBlockAlign     = f->nChannels*f->wBitsPerSample/8;
            f->nAvgBytesPerSec = f->nSamplesPerSec*f->nBlockAlign;
            f->cbSize          = 0;
        }
        memset(&opt,0,sizeof(opt));
        opt.fccType  = streamtypeAUDIO;
        opt.lpFormat = f;
        opt.cbFormat = sizeof(WAVEFORMATEX)-sizeof(WORD);
        //--------------------------------------------------
        // compress start
        //--------------------------------------------------
        if(AVIMakeCompressedStream(
            &movie.ps_autmp,movie.ps_audio,&opt,NULL) == AVIERR_OK){
            if(!WaveInit(*f)){
                MessageBox(NULL,"AVI音声初期化エラー","AVI",MB_OK);
                AviClose();
                return FALSE;
            }
            LONG hr,size=1024,rb,rc;
            BYTE buffer[1024];
            hr = AVIStreamRead(movie.ps_autmp,0,1,buffer,size,&rb,&rc);
            if(!hr && rb){
                dggd.avi_audio = TRUE;
                dggd.wave_hlz  = f->nSamplesPerSec;
                dggd.wave_bit  = f->wBitsPerSample;
                dggd.wave_chn  = f->nChannels;
                wave.current_samples = 0;
            } else {
                err_avi(hr);
                dggd.avi_audio = FALSE;
                movie.ps_audio = NULL;
                movie.ps_autmp = NULL;
            }
        } else {
            dggd.avi_audio = FALSE;
            movie.ps_audio = NULL;
            movie.ps_autmp = NULL;
        }
    } else dggd.avi_audio = FALSE;
    type = DRIFF_AVI;
    movie.init_flag = TRUE;
    if(dggd.avi_audio) WaveInfo("**WAVE AUDIO",cpf);
    Seek(0);
    return TRUE;
}

int DRIFF::codecOpen()
{
    //--------------------------------------------------
    // get format size
    //--------------------------------------------------
    AVIStreamFormatSize(movie.ps_movie,0,&movie.FmtLeng);
    if(movie.lpDecFmt) GlobalFree(movie.lpDecFmt);
    if(dggd.dibh)      GlobalFree(dggd.dibh);
    movie.lpDecFmt = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,movie.FmtLeng);
    dggd.dibh      = (LPBITMAPINFOHEADER)GlobalAlloc(GPTR,movie.FmtLeng);
    //--------------------------------------------------
    // read format
    //--------------------------------------------------
    LPBITMAPINFOHEADER s,d;
    s = movie.lpSrcFmt; d = movie.lpDecFmt;
    // set source format
    AVIStreamInfo(movie.ps_movie,&movie.si,sizeof(AVISTREAMINFO));
    // set decode format
    memcpy(dggd.dibh,s,movie.FmtLeng);
    memcpy(d,s,movie.FmtLeng);
    d->biBitCount    = 24;
    d->biCompression = BI_RGB;
    d->biSize        = movie.FmtLeng;
    d->biSizeImage   = BMPLL(d)*d->biHeight;
    // alloc buffer
    movie.lLength    = BMPLL(s)*s->biHeight;
    if(movie.lpSrc) GlobalFree(movie.lpSrc);
    if(movie.lpDec) GlobalFree(movie.lpDec);
    movie.lpSrc      = (LPBYTE)GlobalAlloc(GPTR,movie.lLength);
    movie.lpDec      = (LPBYTE)GlobalAlloc(GPTR,d->biSizeImage+movie.FmtLeng);
    if(movie.lpSrc == NULL || movie.lpDec == NULL) return FALSE;
    memcpy(movie.lpDec,d,movie.FmtLeng);
    // suggested buffer size
    if(movie.si.dwSuggestedBufferSize)
        if(movie.si.dwSuggestedBufferSize < (unsigned)movie.lLength)
            movie.lLength = (LONG)movie.si.dwSuggestedBufferSize;
    //--------------------------------------------------
    // open the decompressor
    //--------------------------------------------------
    ICINFO icinfo;
    static char buf[256];
    movie.hicd = ICDecompressOpen(ICTYPE_VIDEO,movie.si.fccHandler,s,d);
    ICGetInfo(movie.hicd,&icinfo,sizeof(icinfo));
    if(!movie.hicd){ // cannot find codec
        wsprintf(buf,"%S[%S]: cannot decode image.",
                 icinfo.szName,icinfo.szDescription);
        MessageBox(NULL,buf,"codec open",0);
        return FALSE;
    }
    wsprintf(buf,"  codec info: %S\r\n  (%S)\r\n",
             icinfo.szName,icinfo.szDescription);
    cat_info(buf);
    //--------------------------------------------------
    // get palette(if used)
    //--------------------------------------------------
    //ICDecompressGetPalette(hic,s,d);
    //--------------------------------------------------
    // start decompressing
    //--------------------------------------------------
    if(ICDecompressBegin(movie.hicd,movie.lpSrcFmt,movie.lpDecFmt))
        return FALSE;
    return TRUE;
}

int DRIFF::AviClose()
{
    if(movie.ps_autmp) {
        AVIStreamRelease(movie.ps_autmp); movie.ps_autmp = NULL;
    }
    if(movie.ps_audio) {
        AVIStreamRelease(movie.ps_audio); movie.ps_audio = NULL;
    }
    if(movie.ps_movie) {
        AVIStreamRelease(movie.ps_movie); movie.ps_movie = NULL;
    }
    if(movie.file) {
        AVIFileRelease(movie.file); movie.file = NULL;
    }
    if(movie.movie_frame) {
        AVIStreamGetFrameClose(movie.movie_frame); movie.movie_frame = NULL;
    }
    if(movie.codec){
        if(movie.lpSrc) { GlobalFree(movie.lpSrc); movie.lpSrc = NULL; }
        if(movie.lpDec) { GlobalFree(movie.lpDec); movie.lpDec = NULL; }
        if(movie.lpSrcFmt) {
            GlobalFree(movie.lpSrcFmt); movie.lpSrcFmt = NULL;
        }
        if(movie.lpDecFmt) {
            GlobalFree(movie.lpDecFmt); movie.lpDecFmt = NULL;
        }
        ICDecompressEnd(movie.hicd);
        ICClose(movie.hicd);
        movie.codec = FALSE;
    }
    AVIFileExit();
    return TRUE;
}

int DRIFF::AviSeek(double sec)
{
    if(type != DRIFF_AVI) return FALSE;
    double fps = AviGetFps();
    if(sec == 0) movie.current_samples = -1;
    else movie.current_samples = (int)(fps * sec); 
    movie.eof = FALSE;
    return TRUE;
}

int DRIFF::AviRead(void **data)
{
    if(type != DRIFF_AVI || movie.eof) return FALSE;
    int ret;
    if(movie.current_samples < 0){ // 初期表示用
        ret = AviGetFrame(0,data);
        movie.current_samples = 0;
    } else {
        ret = AviGetFrame(movie.current_samples,data);
        double fps=AviGetFps();
        // 表示速度の調節
        if(fps>100)     movie.current_samples+=5;
        else if(fps>60) movie.current_samples+=3;
        else            movie.current_samples+=1;
    }
    if(ret==FALSE) movie.eof = TRUE;
    return ret;
}

int DRIFF::AviRead2(void **data)
{
    if(type != DRIFF_AVI || movie.eof) return FALSE;
    if(movie.current_samples < 0){ // 初期表示用
        return AviGetFrame(0,data);
    } else {
        return AviGetFrame(movie.current_samples,data);
    }
}

POINT* DRIFF::AviGetSize()
{
    static POINT dsize;
    if(type == DRIFF_AVI) {
        dsize.x = movie.info.dwWidth; dsize.y = movie.info.dwHeight; 
    } else {
        dsize.x = 0; dsize.y = 0;
    }
    return &dsize;
}

int DRIFF::AviGetCount()
{
    return movie.current_samples;
}

int DRIFF::AviGetFrame(int frame,void **data)
{
    if(type != DRIFF_AVI) return FALSE;
    if(movie.codec==FALSE){
        *data = AVIStreamGetFrame(movie.movie_frame,frame);
    } else {
        if(!AVIStreamRead(movie.ps_movie,frame,1,movie.lpSrc,movie.lLength,
                          NULL,NULL)){
            LPBYTE dst = movie.lpDec + movie.lpDecFmt->biSize;
            ICDecompress(movie.hicd,0,movie.lpSrcFmt,movie.lpSrc,
                         movie.lpDecFmt,dst);
            *data = movie.lpDec;
        } else return FALSE;
    }
    if(*data == NULL) return FALSE;
    return TRUE;
}

double DRIFF::AviGetFps()
{
    if(type != DRIFF_AVI) return 1;
    return (double)movie.info.dwRate / movie.info.dwScale;
}

int DRIFF::AviEof()
{
    return movie.eof;
}

//-----------------------------------------------------------------
//
// WAVE 関数
//
//-----------------------------------------------------------------
void DRIFF::WaveInfo(char* title,WAVEFORMATEX f)
{
    static char stmp[BUFSIZ],sbuf[BUFSIZ];
    sprintf(sbuf,"%s\r\n",title);
    sprintf(stmp,"  wFormatTag: %d\r\n",f.wFormatTag);
    strcat(sbuf,stmp);
    sprintf(stmp,"  nChannels: %d\r\n",f.nChannels);
    strcat(sbuf,stmp);
    sprintf(stmp,"  nSamplesPerSec: %d\r\n",f.nSamplesPerSec);
    strcat(sbuf,stmp);
    sprintf(stmp,"  nAvgBytesPerSec: %d\r\n",f.nAvgBytesPerSec);
    strcat(sbuf,stmp);
    sprintf(stmp,"  nBlockAlign: %d\r\n",f.nBlockAlign);
    strcat(sbuf,stmp);
    sprintf(stmp,"  wBitsPerSample: %d\r\n",f.wBitsPerSample);
    strcat(sbuf,stmp);
    sprintf(stmp,"  cbSize: %d\r\n",f.cbSize);
    strcat(sbuf,stmp);
    cat_info(sbuf);
}

int DRIFF::WaveOpen(char* filename)
{
    int i=0;
    acm.flag = FALSE;
    while(i<(int)(sizeof(head)/sizeof(head[0]))) {
        i++;
        fread(&head[i],1,sizeof(DRIFF_HEAD),file);
        if(feof(file)) { Close(); return FALSE; }
        // read header.
        if(head[i].ckID == mmioFOURCC('f','m','t',' ')){ // [fmt ]
            wave.format_src = (WAVEFORMATEX *)malloc(head[i].ckSize);
            fread(wave.format_src,1,head[i].ckSize,file);
            wave.format_src->wFormatTag = WAVE_FORMAT_PCM;
            if(!WaveInit(*wave.format_src)) {
                type = 0;
                MessageBox(NULL,"WAVE format 未対応","WAVE FILE",MB_OK);
                return FALSE;
            }
            dggd.wave_hlz = wave.format_src->nSamplesPerSec;
            dggd.wave_bit = wave.format_src->wBitsPerSample;
            dggd.wave_chn = wave.format_src->nChannels;
        } else if(head[i].ckID == mmioFOURCC('d','a','t','a')) { // [data]
            wave.start_pos = ftell(file);
            type = DRIFF_WAVE;
            WaveInfo("**WAVE FILE",*wave.format_src);
            return TRUE;
        } else {
            fseek(file,head[i].ckSize,SEEK_CUR);
        }
        if(head[i].ckSize % 2 != 0) {
            fseek(file,1,SEEK_CUR);
        }
    }
    type = 0;
    MessageBox(NULL,"WAVE未対応","WAVE FILE",MB_OK);
    return FALSE;
}

int DRIFF::WaveClose()
{
    if(wave.format_src) { delete wave.format_src; wave.format_src = NULL; }
    if(acm.format_src)  { delete acm.format_src; acm.format_src = NULL; }
    if(acm.flag) { AcmClose(); acm.flag = FALSE; }
    WaveOutClose();
    return TRUE;
}

int DRIFF::WaveInit(WAVEFORMATEX f)
{
    if(wave.init_flag) WaveClose();
    if(f.wFormatTag != WAVE_FORMAT_PCM) {
        if(!AcmOpen(f)) return FALSE;
    } else wave.format = f;
    if(type != DRIFF_MP3)
        wave.block_size = WaveBlockSize(f);
    wave.buf = new char[wave.block_size];
    wave.fft = new float[wave.block_size];
    if(!WaveOutOpen(&f,wave.block_size,2)) {
        return FALSE;
    }
    wave.init_flag = TRUE;
    return TRUE;
}

int DRIFF::WaveBlockSize(WAVEFORMATEX f)
{
    int size = (f.nChannels*f.nSamplesPerSec*f.wBitsPerSample/8/
                f.nBlockAlign) * f.nBlockAlign;
    if(size == 0) // Default Buffer(4KB)
        size = (1024 / f.nBlockAlign) * f.nBlockAlign;
    return size;
}

int DRIFF::WaveSeek(double sec)
{
    if(!wave.init_flag) return FALSE;
    if(acm.flag) { // ACM のseek は、先頭以外サポート外
        if(sec!=0) return FALSE;
        else {
            AcmBufferClear();
            fseek(file,0,SEEK_SET);
        }
    } else { // RIFF data
        if(type == DRIFF_WAVE) {
            fseek(file,WavePos(sec)+wave.start_pos,SEEK_SET);
        } else { // AVI AUDIO
            wave.current_samples = WavePos(sec) / wave.format.nBlockAlign;
        }
    }
    wave.eof = FALSE;
    return TRUE;
}

int DRIFF::WavePos(double sec)
{
    if(!wave.init_flag) return 0;
    int samples = (int)(wave.block_size*sec)/wave.format.nBlockAlign;
    return samples*wave.format.nBlockAlign;
}

int DRIFF::WavePlay(void *buffer,int size,int wait=FALSE)
{
    if(!wave.init_flag || size > wave.block_size) return FALSE;
    WaveOutPlay(buffer,size,wait);
    return TRUE;
}

int DRIFF::WavePlay(double sec,int wait=FALSE)
{
    if(!wave.init_flag) return FALSE;
    int size = WavePos(sec);
    if(wave.eof || size > wave.block_size) return FALSE;
    size = WaveRead(wave.buf,size);
    WavePlay(wave.buf,size,wait);
    return TRUE;
}

int DRIFF::WaveEof()
{
    if(!wave.init_flag) return TRUE;
    return wave.eof;
}

int DRIFF::WaveRead(void *buf,int size)
{
    void *buffer = buf; // バッファの内容をコピー
    if(!wave.init_flag) return 0;
    if(!acm.flag) return WaveRead2(buf,size);
    return AcmRead(buf,size);
}

int DRIFF::WaveRead2(void *buffer,int size)
{
    if(!wave.init_flag || wave.eof) return 0;
    LONG read_byte=0;
    if(type == DRIFF_WAVE || type == DRIFF_MP3) {
        read_byte = fread(buffer,1,size,file);
        wave.dgw_size = read_byte;
    } else if(type == DRIFF_AVI) {
        LONG hr,read_count;
        hr = AVIStreamRead(movie.ps_autmp,    // ストリームへのポインタ
                           wave.current_samples,  // 読込開始サンプル番号
                           size / wave.format.nBlockAlign, // サンプル数
                           buffer,                // データ格納用
                           size,                  // 読取バイト数
                           &read_byte,            // 読み込んだバイト数
                           &read_count            // 読み込んだサンプル数。
            );
        if(hr){ // read err.
            err_avi(hr);
            TRACE("wave: current_sample = %d\n",wave.current_samples);
            TRACE("wave: nBlockAlign = %d\n",wave.format.nBlockAlign);
            TRACE("wave: size = %d\n",size);
            wave.current_samples += size / wave.format.nBlockAlign;
            return 0;
        }
        wave.dgw_size = read_byte;
        wave.current_samples += read_count;
    }
    if(read_byte != size) wave.eof = TRUE;
    return read_byte;
}

//-----------------------------------------------------------------
// WaveOut
//-----------------------------------------------------------------
int DRIFF::WaveOutInit()
{
    waveio.FHWaveOut   = NULL;
    waveio.out_count   = 0;
    waveio.out_current = 0;
    return TRUE;
}

int DRIFF::WaveOutOpen(WAVEFORMATEX *format,int bufsize,int count=2)
{
    MMRESULT mmr;
    if(waveio.FHWaveOut != NULL) WaveOutClose();
    // 境界バイトが1秒に合わないもの
    if(bufsize % format->nBlockAlign != 0) return FALSE;
    // PCM フォーマット以外
    if(format->wFormatTag != WAVE_FORMAT_PCM) return FALSE;
    // wave 出力の開始
    mmr = waveOutOpen(&waveio.FHWaveOut,WAVE_MAPPER,format,0,0,CALLBACK_NULL);
    if(mmr) return FALSE;
    waveio.out_count = count;
    waveio.FOWaveHdr = new WAVEHDR*[count];
    // set wave volume
    waveOutGetVolume(waveio.FHWaveOut,&waveio.volume);
    waveOutSetVolume(waveio.FHWaveOut,MAKELONG(0x3000,0x3000));
    // prepare header
    for(int i=0;i<count;i++) {
        waveio.FOWaveHdr[i] = (WAVEHDR*)HeapAlloc(
            GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
        waveio.FOWaveHdr[i]->lpData =
            (char*)HeapAlloc(GetProcessHeap(),0,bufsize);
        waveio.FOWaveHdr[i]->dwBufferLength = 0;
        mmr = waveOutPrepareHeader(waveio.FHWaveOut,waveio.FOWaveHdr[i],
                                   sizeof(WAVEHDR));
        if(mmr) return FALSE;
        // WHDR_DONE を付けるため、ダミーデータの出力
        mmr = waveOutWrite(waveio.FHWaveOut,waveio.FOWaveHdr[i],
                           sizeof(WAVEHDR));
        if(mmr) return FALSE;
    }
    return TRUE;
}

int DRIFF::WaveOutPlay(void *buffer,int size,int wait=FALSE)
{
    if(waveio.FHWaveOut == NULL) return FALSE;
    // カレントバッファの出力待ち
    while(!(waveio.FOWaveHdr[waveio.out_current]->dwFlags & WHDR_DONE)) {
        if(wait) Sleep(1);//Sleep(1);
        else break;
    }
    // ヘッダの準備
    memcpy(waveio.FOWaveHdr[waveio.out_current]->lpData,buffer,size);
    waveio.FOWaveHdr[waveio.out_current]->dwBufferLength = size;
    // 再生
    if(waveOutWrite(waveio.FHWaveOut,waveio.FOWaveHdr[waveio.out_current],
                    sizeof(WAVEHDR)))
        return FALSE;
    waveio.out_current = (waveio.out_current + 1) % waveio.out_count;
    return TRUE;
}

int DRIFF::WaveOutClose()
{
    if(waveio.FHWaveOut == NULL) return FALSE;
    // set volume
    waveOutSetVolume(waveio.FHWaveOut,waveio.volume);
    // clean up
    waveOutReset(waveio.FHWaveOut);
    for(int i=0;i<waveio.out_count;i++) {
        waveOutUnprepareHeader(waveio.FHWaveOut,
                               waveio.FOWaveHdr[i],sizeof(WAVEHDR));
        HeapFree(GetProcessHeap(),0,waveio.FOWaveHdr[i]->lpData);
        HeapFree(GetProcessHeap(),0,waveio.FOWaveHdr[i]);
    }
    delete []waveio.FOWaveHdr;
    waveOutClose(waveio.FHWaveOut);
    waveio.FHWaveOut = NULL;
    if(wave.buf){ delete []wave.buf; wave.buf = NULL; }
    if(wave.fft){ delete []wave.fft; wave.fft = NULL; }
    return TRUE;
}

//-----------------------------------------------------------------
// WaveIn
//-----------------------------------------------------------------
int DRIFF::WaveInInit()
{
    wave.in_flag      = FALSE;
    waveio.FHWaveIn   = NULL;
    waveio.in_count   = 0;
    waveio.in_current = 0;
    return TRUE;
}

int DRIFF::WaveInOpen()
{
    ULONG FBlockSize,SRATE = 44100;
    MMRESULT mmr;
    //--------------------------------------------------
    // WAVE データの設定
    //--------------------------------------------------
    WAVEFORMATEX FWaveFmt;
    FWaveFmt.wFormatTag      = WAVE_FORMAT_PCM;
    FWaveFmt.nChannels       = 1;
    FWaveFmt.nSamplesPerSec  = SRATE;
    FWaveFmt.wBitsPerSample  = 16;
    FWaveFmt.nBlockAlign     = 1*16/8;
    FWaveFmt.nAvgBytesPerSec = SRATE*2;
    FWaveFmt.cbSize          = 0;
    FBlockSize               = 1024*2;
    wave.dgw_size            = FBlockSize;
    waveio.in_count          = 2;
    waveio.in_current        = 0;
    dggd.wave_hlz            = SRATE;
    dggd.wave_bit            = 16;
    //--------------------------------------------------
    // WAVE 入力開始
    //--------------------------------------------------
    mmr = waveInOpen(&waveio.FHWaveIn,WAVE_MAPPER,&FWaveFmt,(DWORD)dggd.AppHwnd,
                     0,CALLBACK_WINDOW);
    if(mmr) return FALSE;
    //--------------------------------------------------
    // ヘッダ初期化
    //--------------------------------------------------
    waveio.FIWaveHdr = new WAVEHDR*[waveio.in_count];
    for(int i=0;i<waveio.in_count;i++){
        waveio.FIWaveHdr[i] = (WAVEHDR*)HeapAlloc(
            GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(WAVEHDR));
        waveio.FIWaveHdr[i]->lpData = (char*)HeapAlloc(
            GetProcessHeap(),0,FBlockSize);
        waveio.FIWaveHdr[i]->dwBufferLength  = FBlockSize;
        waveio.FIWaveHdr[i]->dwLoops         = 1000;
        waveio.FIWaveHdr[i]->dwBytesRecorded = 0;
        waveio.FIWaveHdr[i]->dwUser          = 0;
        waveio.FIWaveHdr[i]->dwFlags         = 0;
        waveio.FIWaveHdr[i]->lpNext          = NULL;
        waveio.FIWaveHdr[i]->reserved        = 0;
        // 入力設定
        mmr = waveInPrepareHeader(waveio.FHWaveIn,waveio.FIWaveHdr[i],
                                  sizeof(WAVEHDR));
        if(mmr){ WaveInClose(); return FALSE; }
        mmr = waveInAddBuffer(waveio.FHWaveIn,waveio.FIWaveHdr[i],
                              sizeof(WAVEHDR));
        if(mmr){ WaveInClose(); return FALSE; }
    }
    waveInStart(waveio.FHWaveIn);
    //--------------------------------------------------
    // 描画用
    //--------------------------------------------------
    if(wave.buf) delete []wave.buf;
    if(wave.fft) delete []wave.fft;
    wave.buf = new char[FBlockSize];
    wave.fft = new float[FBlockSize];
    if(wave.buf == NULL || wave.fft == NULL){
        WaveInClose(); return FALSE;
    }
    type = DRIFF_WIN;
    set_info("**WAVE IN");
    WaveInfo("",FWaveFmt);
    wave.in_flag = TRUE;
    return TRUE;
}

int DRIFF::WaveInPlay()
{
    if(wave.in_flag == FALSE) return FALSE;
    memcpy(wave.buf,waveio.FIWaveHdr[waveio.in_current]->lpData,wave.dgw_size);
    waveInPrepareHeader(waveio.FHWaveIn,waveio.FIWaveHdr[waveio.in_current],
                        sizeof(WAVEHDR));
    waveInAddBuffer(waveio.FHWaveIn,waveio.FIWaveHdr[waveio.in_current],
                    sizeof(WAVEHDR));
    waveio.in_current = (waveio.in_current + 1) % waveio.in_count;
    return TRUE;
}

int DRIFF::WaveInClose()
{
    if(waveio.FHWaveIn == NULL) return FALSE;
    waveInStop(waveio.FHWaveIn);
    waveInReset(waveio.FHWaveIn);
    for(int i=0;i<waveio.in_count;i++){
        waveInUnprepareHeader(waveio.FHWaveIn,waveio.FIWaveHdr[i],
                              sizeof(WAVEHDR));
        HeapFree(GetProcessHeap(),0,waveio.FIWaveHdr[i]->lpData);
    }
    delete []waveio.FIWaveHdr;
    waveInClose(waveio.FHWaveIn);
    waveio.FHWaveIn = NULL;
    if(wave.buf) { delete []wave.buf; wave.buf = NULL; }
    if(wave.fft) { delete []wave.fft; wave.fft = NULL; }
    wave.in_flag = FALSE;
    return TRUE;
}

//-----------------------------------------------------------------
// ACM sound
//-----------------------------------------------------------------
int DRIFF::AcmOpen(WAVEFORMATEX f)
{
    MMRESULT mmr;
    memset(&wave.format,0,sizeof(WAVEFORMATEX));
    wave.format.wFormatTag = WAVE_FORMAT_PCM;
    mmr = acmFormatSuggest(NULL,wave.format_src,&wave.format,
                           sizeof(WAVEFORMATEX),ACM_FORMATSUGGESTF_WFORMATTAG);
    if(mmr) return FALSE;
    mmr = acmStreamOpen(&acm.acm,NULL,wave.format_src,&wave.format,
                        NULL,0,0,ACM_STREAMOPENF_NONREALTIME);
    if(mmr) return FALSE;
    acm.read_size = WaveBlockSize(*wave.format_src);
    mmr = acmStreamSize(acm.acm,acm.read_size,&acm.buf_size,
                        ACM_STREAMSIZEF_SOURCE);
    if(mmr) return FALSE;
    acm.flag     = TRUE;
    acm.buf      = new char[acm.buf_size];
    acm.buf_read = new char[acm.read_size];
    AcmBufferClear();
    return true;
}

int DRIFF::AcmBufferClear()
{
    acm.buf_p      = acm.buf;
    acm.buf_left   = 0;
    acm.buf_read_p = acm.buf_read;
    return TRUE;
}

int DRIFF::AcmRead(void *buf,int size)
{
    char *buffer=(char*)buf;
    int s,s2=size;
    while(size) {
        // コンバート処理
        if(acm.buf_left == 0) {
            // 残りバッファ0 + eof
            if(wave.eof) { return s2 - size; }
            int left = acm.buf_read_p - acm.buf_read;
            s = WaveRead2(acm.buf_read_p,acm.read_size-left);
            AcmConvert(acm.buf_read,s+left,acm.buf,wave.block_size);
        }
        s = min(size,acm.buf_left);
        memcpy(buffer,acm.buf_p,s);
        size -= s; buffer += s;
        acm.buf_p += s; acm.buf_left -= s;
    }
    wave.dgw_size = s2-size;
    return s2 - size;
}

int DRIFF::AcmConvert(void *src,int src_size,void *dest,int dest_size)
{
    memset(&acm.ach,0,sizeof(acm.ach));
    acm.ach.cbStruct    = sizeof(acm.ach);
    acm.ach.pbSrc       = (unsigned char*)src;
    acm.ach.cbSrcLength = src_size;
    acm.ach.pbDst       = (unsigned char*)dest;
    acm.ach.cbDstLength = dest_size;
    acmStreamPrepareHeader(acm.acm,&acm.ach,0);
    acmStreamConvert(acm.acm,&acm.ach,ACM_STREAMCONVERTF_BLOCKALIGN);
    acmStreamUnprepareHeader(acm.acm,&acm.ach,0);
    if((int)acm.ach.cbSrcLengthUsed == src_size) { // 変換完全終了
        acm.buf_read_p = acm.buf_read;
    } else { // 変換していないデータが残っている
        LONG len = src_size - acm.ach.cbSrcLengthUsed;
        acm.buf_read_p = &acm.buf_read[len];
        memmove(acm.buf_read,&acm.buf_read[acm.ach.cbSrcLengthUsed],len);
    }
    acm.buf_left = acm.ach.cbDstLengthUsed;
    acm.buf_p    = acm.buf;
    return TRUE;
}

int DRIFF::AcmClose()
{
    if(acm.buf) { delete []acm.buf; acm.buf = NULL; }
    if(acm.buf_read) { delete []acm.buf_read; acm.buf_read = NULL; }
    acmStreamClose(acm.acm,0);
    acm.flag = FALSE;
    return TRUE;
}
//-----------------------------------------------------------------
// MP3 出力関数
//-----------------------------------------------------------------
int DRIFF::MP3Open(char* filename)
{
    MMRESULT mmr;
    DWORD maxFormatSize = sizeof(MPEGLAYER3WAVEFORMAT);
    mmr = acmMetrics(NULL,ACM_METRIC_MAX_SIZE_FORMAT,&maxFormatSize);
    if(maxFormatSize == 0) return FALSE;
    //--------------------------------------------------
    // define MP3 input format
    //--------------------------------------------------
    acm.format_src = (LPMPEGLAYER3WAVEFORMAT)malloc(maxFormatSize);
    memset(acm.format_src,0,maxFormatSize);
    MPEGLAYER3WAVEFORMAT* fmp3 = acm.format_src;
    if(!MP3Info(filename,fmp3)) return FALSE;
    //--------------------------------------------------
    // define desired output format
    //--------------------------------------------------
    wave.format_src = (LPWAVEFORMATEX)malloc(maxFormatSize);
    WAVEFORMATEX* fwav = wave.format_src;
    memset(fwav,0,maxFormatSize);
    memcpy(fwav,fmp3,maxFormatSize);
    fwav->wFormatTag      = WAVE_FORMAT_PCM;
    fwav->wBitsPerSample  = fmp3->wfx.wBitsPerSample<12?8:16;
    fwav->nBlockAlign     = fmp3->wfx.nChannels*fwav->wBitsPerSample/8;
    fwav->nAvgBytesPerSec = fmp3->wfx.nSamplesPerSec * fwav->nBlockAlign;
    fwav->cbSize          = 0;
    acm.read_size         = fmp3->nBlockSize;
    //--------------------------------------------------
    // decoder check
    //--------------------------------------------------
    mmr = acmFormatSuggest(NULL,(WAVEFORMATEX*)fmp3,
                           fwav,sizeof(WAVEFORMATEX),
                           ACM_FORMATSUGGESTF_WFORMATTAG);
    if(!err_check(mmr)) return FALSE;
    //--------------------------------------------------
    // ACM出力開始
    //--------------------------------------------------
    acm.acm = NULL;
    mmr = acmStreamOpen(&acm.acm,        // open an ACM conversion stream
                        NULL,            // querying all ACM drivers
                        (LPWAVEFORMATEX)fmp3, // converting from MP3
                        fwav,            // to WAV
                        NULL,            // with no filter
                        0,               // or async callbacks
                        0,               // (and no data for the callback)
                        0                // and no flags
        );
    if(!err_check(mmr)) return FALSE;
    //--------------------------------------------------
    // バッファサイズ
    //--------------------------------------------------
    mmr = acmStreamSize(acm.acm,acm.read_size,&acm.buf_size,
                        ACM_STREAMSIZEF_SOURCE);
    if(!err_check(mmr)) return FALSE;
    wave.block_size = acm.buf_size;
    //--------------------------------------------------
    // WAVE 音源初期化
    //--------------------------------------------------
    if(!WaveInit(*fwav)) {
        MessageBox(NULL,"WAVE未対応","MP3",MB_OK);
        return FALSE;
    }
    //--------------------------------------------------
    // 表示設定
    //--------------------------------------------------
    type     = DRIFF_MP3;
    acm.flag = TRUE;
    dggd.wave_hlz = fwav->nSamplesPerSec;
    dggd.wave_bit = fwav->wBitsPerSample;
    dggd.wave_chn = fwav->nChannels;
    //--------------------------------------------------
    // 読取バッファ
    //--------------------------------------------------
    acm.buf_read = new char[acm.read_size];
    acm.buf      = new char[acm.buf_size];
    AcmBufferClear();
    return TRUE;
}

int DRIFF::MP3Info(char* filename,MPEGLAYER3WAVEFORMAT* mp3fmt)
{
    static char sbuf[BUFSIZ],stmp[BUFSIZ];
    fpos_t pFile;
    int curch;               // for reading MPEG Audio Frame Header
    int FSize,pHead;         // Filesize and header position
    int impegVer,iLayer;     // MPEG ver and layer
    int mp3Freq;             // Frequency
    int padBit;              // Padding bit
    int iFramesize,n_Frames; // Frame size and number
    int n_Channel;           // Channels
    long int FreqTab[4][4]={
        11025,12000,8000,0,  // MPEG 2.5
        0,0,0,0,             // Reserved
        22050,24000,16000,0, // MPEG II
        44100,48000,32000,0, // MPEG I
    };
    static char channelmode[4][20] = { // Channel
        "Stereo","Joint Stereo","Dual Channel","Single Channel"
    };
    int BitRate;             // bitrate
    int BRateTab1[3][16] = { // MPEG I  - Layer III,II,I
        0, 32, 40, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,0,
        0, 32, 48, 56, 64, 80, 96,112,128,160,192,224,256,320,384,0,
        0, 32, 64, 96,128,160,192,224,256,288,320,352,384,416,448,0,
    };
    int BRateTab2[3][16] = { // MPEG II - Layer III, II ,I
        0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0,
        0,  8, 16, 24, 32, 40, 48, 56, 64, 80, 96,112,128,144,160,0,
        0, 32, 48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0,
    };
    int SmpsPerFrame[4][3] = { // Samples Per Frame
        576,1152,384,  // MPEG 2.5
        0,0,0,         // Reserved
        576,1152,384,  // MPEG 2
        1152,1152,384, // MPEG 1
    };
    //--------------------------------------------------
    char idtag[5];
    int ch_flag=0,fpos=0;
    static int id3tag;
    //--------------------------------------------------
    // set initial mp3 header values
    //--------------------------------------------------
    mp3fmt->wfx.cbSize          = MPEGLAYER3_WFX_EXTRA_BYTES;
    mp3fmt->wfx.wFormatTag      = WAVE_FORMAT_MPEGLAYER3;
    mp3fmt->wfx.nChannels       = 2;
    mp3fmt->wfx.nAvgBytesPerSec = 128 * (1000/8);    // 64,96,112,128,160kbps
    mp3fmt->wfx.wBitsPerSample  = 0;                 // MUST BE ZERO
    mp3fmt->wfx.nBlockAlign     = 1;                 // MUST BE ONE
    mp3fmt->wfx.nSamplesPerSec  = 44100;             // 44.1kHz
    mp3fmt->fdwFlags            = MPEGLAYER3_FLAG_PADDING_OFF;
    mp3fmt->nBlockSize          = 144 * 128 * 1000 / 44100 + 0;
    mp3fmt->nFramesPerBlock     = 1;                 // MUST BE ONE
    mp3fmt->nCodecDelay         = 0x571;             // voodoo value #2
    mp3fmt->wID                 = MPEGLAYER3_ID_MPEG;
    //--------------------------------------------------
    // Get file size
    //--------------------------------------------------
    if(file==NULL) {
        MessageBox(NULL,"file == NULL",0,0);
        return FALSE;
    }
    fseek(file,0,SEEK_END); FSize = ftell(file); fseek(file,0,SEEK_SET);
    //--------------------------------------------------
    // Version and Layer check MPEG 1/2, Layer I,II,III
    //--------------------------------------------------
    // check ID3 tag
    memset(idtag,0,sizeof(idtag));
    fread(idtag,1,3,file);
    if(!strcmp(idtag,"ID3")) id3tag = 1;
    else {
        id3tag = 0;
        fseek(file,0,SEEK_SET);
    }
    TRACE("id3tag = %d\n",id3tag);
    // seek first frame
    while(!ch_flag) {
        curch = fgetc(file);
        if(curch == EOF) break;
        if(curch == 0xff) {
            curch = fgetc(file);
            fgetpos(file,(fpos_t*)&fpos);
            fpos-=2;
            if(((curch&0xe0)>>5) == 7) {
                impegVer = ((curch&0x18)>>3);
                iLayer   = ((curch&0x03)>>1);
                if(impegVer == NOT_MPEG || iLayer == NOT_LAYER) ;
                else {
                    ch_flag = 1;
                    TRACE("id3tag = %d, fpos = %d\n",id3tag,fpos);
                    if(id3tag == 1 && fpos < 0xff) ch_flag = 0;
                }
            }
        }
    }
    if(ch_flag == 0){
        MessageBox(NULL,"cannot find first frame.",0,0);
        return FALSE;
    }
    //--------------------------------------------------
    // position of first frame header
    //--------------------------------------------------
    sprintf(sbuf,"** MP3\r\n");
    fgetpos(file,&pFile);
    pFile -= 2; pHead = (int)pFile;
    sprintf(stmp,"First frame found at byte : %d [byte]\r\n\r\n",pHead);
    TRACE("First frame found at byte : %d [byte]\r\n\r\n",pHead);
    strcat(sbuf,stmp);
    //--------------------------------------------------
    // Info. of 2nd byte
    //--------------------------------------------------
    switch(impegVer){
    case  MPEG1: sprintf(stmp,"[ MPEG-1");   break;
    case  MPEG2: sprintf(stmp,"[ MPEG-2");   break;
    case MPEG25: sprintf(stmp,"[ MPEG-2.5"); break;
    }
    strcat(sbuf,stmp);
    switch(iLayer){
    case MPEG_LAYER1: sprintf(stmp," Layer I ]\r\n");   break;
    case MPEG_LAYER2: sprintf(stmp," Layer II ]\r\n");  break;
    case MPEG_LAYER3: sprintf(stmp," Layer III ]\r\n"); break;
    default:          sprintf(stmp," ]\r\n");           break;
    }
    strcat(sbuf,stmp);
    // CRC
    if(tolower(((curch%16)%4)%2)==1)
        sprintf(stmp,"CRCs : No\r\n");
    else
        sprintf(stmp,"CRCs : Yes\r\n");
    strcat(sbuf,stmp);
    //--------------------------------------------------
    // Info. of 3rd byte [bitrate, frequency etc.]
    //--------------------------------------------------
    curch = fgetc(file);
    // bitrate info
     switch(impegVer){
    case MPEG1:  // MPEG I
        BitRate = BRateTab1[iLayer-1][tolower(curch/16)]; break;
    case MPEG2:  // MPEG II
        BitRate = BRateTab2[iLayer-1][tolower(curch/16)]; break;
    case MPEG25: // MPEG 2.5
        BitRate = BRateTab2[iLayer-1][tolower(curch/16)]; break;
    }
    sprintf(stmp,"Bitrate(kbps) : %d\r\n",BitRate);
    strcat(sbuf,stmp);
    // frequency info
    mp3Freq = FreqTab[impegVer][tolower((curch%16)/4)];
    sprintf(stmp,"Frequency(Hz) : %d\r\n",mp3Freq);
    strcat(sbuf,stmp);
    // padding bit
    padBit = tolower(((curch%16)%4)/2);
    sprintf(stmp,"Padding : ");
    strcat(sbuf,stmp);
    if(padBit==0) sprintf(stmp,"No\r\n");
    else          sprintf(stmp,"Yes\r\n");
    strcat(sbuf,stmp);
    // private bit
    sprintf(stmp,"Private : ");
    strcat(sbuf,stmp);
    if(tolower(((curch%16)%4)%2)==0) sprintf(stmp,"No\r\n");
    else                             sprintf(stmp,"Yes\r\n");
    strcat(sbuf,stmp);
    //--------------------------------------------------
    // Info. of 4th byte [channel, copyright etc.]
    //--------------------------------------------------
    curch = fgetc(file);
    //channel mode
    n_Channel = (tolower((curch/16)/4)==3)?1:2;
    sprintf(stmp,"Channel : %s\r\n",channelmode[tolower((curch/16)/4)]);
    strcat(sbuf,stmp);
    // copyright bit
    sprintf(stmp,"Copyrighted : ");
    strcat(sbuf,stmp);
    if(tolower(((curch%16)/4)/2)==1) sprintf(stmp,"Yes\r\n");
    else                             sprintf(stmp,"No\r\n");
    strcat(sbuf,stmp);
    // original bit
    sprintf(stmp,"Original : ");
    strcat(sbuf,stmp);
    if(tolower(((curch%16)/4)%2)==1) sprintf(stmp,"Yes\r\n");
    else                             sprintf(stmp,"No\r\n");
    strcat(sbuf,stmp);
    // emphasis
    sprintf(stmp,"Emphasis : ");
    strcat(sbuf,stmp);
    switch(tolower((curch%16)%4)){
    case 0: sprintf(stmp,"None\r\n");       break;
    case 1: sprintf(stmp,"50/15 ms\r\n");   break;
    case 3: sprintf(stmp,"CCITT J.17\r\n"); break;
    }
    strcat(sbuf,stmp);
    //--------------------------------------------------
    // Calculating Frame size and Length
    //--------------------------------------------------
    int smps   = SmpsPerFrame[impegVer][iLayer-1];
    iFramesize = (smps/8*BitRate*1000) / mp3Freq + padBit;
    n_Frames   = (FSize-pHead)/iFramesize;
    sprintf(stmp,"Framesize : %d\r\n",iFramesize);
    strcat(sbuf,stmp);
    sprintf(stmp,"Frame Num : %d\r\n",n_Frames);
    strcat(sbuf,stmp);
    // song length...
    double temp,msec;
    int Allsec,min,sec;
    temp   = (double)(n_Frames * smps) / (double)mp3Freq;
    Allsec = (n_Frames * smps) / mp3Freq;
    min    = Allsec/60;
    sec    = Allsec%60;
    msec   = (temp - Allsec)*1000;
    if(msec > 500) sec += 1;
    sprintf(stmp,"Length : (%d:%d)\r\n",min,sec);
    strcat(sbuf,stmp);
    // set file pointer
    fseek(file,pHead,SEEK_SET);
    //--------------------------------------------------
    // set format values
    //--------------------------------------------------
    mp3fmt->wfx.nChannels       = n_Channel;
    mp3fmt->wfx.nAvgBytesPerSec = BitRate;
    mp3fmt->wfx.nSamplesPerSec  = mp3Freq;
    mp3fmt->nBlockSize          = iFramesize;
    if(padBit) mp3fmt->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON;
    else       mp3fmt->fdwFlags = MPEGLAYER3_FLAG_PADDING_OFF;

    // display mp3 information
    cat_info(sbuf);
    
    //MessageBox(NULL,sbuf,"MP3情報",0);
    return TRUE;
}

//-----------------------------------------------------------------
// WAVE 波形表示
//-----------------------------------------------------------------
BOOL DRIFF::DrawWave(HDC hDC,RECT *pR)
{
    SaveDC(hDC);
    HPEN *bgp,*wvp;
    int i,wx,wy,dwx,dwy;
    wx = pR->right - pR->left;
    wy = pR->bottom - pR->top;
    if(dggd.dgw_type == DGW_NORMAL) { // normal wave
        dwx = wave.dgw_size/(dggd.wave_bit/8);
        if(dggd.wave_bit == 8) dwy = 1<<9;
        else                   dwy = 1<<17;
    } else {// for FFT draw
        int fftn = FFT();
        //Hz 表示に[max:15000Hz=>15000/(wave_hlz/2)*(4096/2)]
        dwx = (1024/2)*15000/(dggd.wave_hlz/2)/4;//dwx = FFT()/4;
        if(dggd.wave_bit == 8) dwy = 50;
        else                   dwy = 10000;
    }
    //--------------------------------------------------
    // 背景色
    //--------------------------------------------------
    bgp = (HPEN*)CreatePen(PS_SOLID,1,RGB(0,0,0));
    SelectObject(hDC,bgp);
    for(i=0;i<wx;i++){
        MoveToEx(hDC,pR->left+i,pR->top,NULL);
        LineTo(hDC,pR->left+i,pR->bottom);
    }
    //--------------------------------------------------
    // 描画範囲
    //--------------------------------------------------
    SetMapMode(hDC,MM_ANISOTROPIC); // 論理座標の設定
    // 論理座標のサイズ
    SetWindowExtEx(hDC,dwx,dwy,NULL);
    SetViewportExtEx(hDC,wx,-wy,NULL);
    // 原点設定
    if(dggd.dgw_type == DGW_NORMAL) { // normal wave
        SetViewportOrgEx(hDC,0,pR->top+wy/2,NULL);
        MoveToEx(hDC,0,0,NULL);
    } else {
        SetViewportOrgEx(hDC,0,pR->top+wy-1,NULL);
    }
    //--------------------------------------------------
    // 描画開始
    //--------------------------------------------------
    wvp = (HPEN*)CreatePen(PS_SOLID,1,RGB(255,230,50));
    SelectObject(hDC,wvp);
    int dstep = dwx/wx;
    int istep = (dggd.wave_bit/8);
    double mean = 0.0;
    if(dstep < 2) dstep = 2;
    if(dggd.dgw_type == DGW_NORMAL){ // draw sound wave
        for(i=0;i<dwx;i+=istep) {
            if(i%dstep == 0){
                mean /= (double)dstep;
                if(mean < dwy) LineTo(hDC,i,(int)mean);
                else           LineTo(hDC,i,dwy);
                mean = 0.0;
            } else {
                if(dggd.wave_bit==8) mean += wave.buf[i];
                else mean += (short)(wave.buf[i]+(wave.buf[i+1]<<8));
            }
        }
    } else { // draw FFT
        for(i=0;i<dwx;i++) {
            if(i%dstep == 0){
                mean /= (double)dstep;
                MoveToEx(hDC,i,0,NULL);
                if(mean < dwy) LineTo(hDC,i,(int)mean);
                else           LineTo(hDC,i,dwy);
            } else mean += wave.fft[i];
        }
    }
    RestoreDC(hDC,-1);
    return TRUE;
}

//-----------------------------------------------------------------
// 音声波形のFFT
//-----------------------------------------------------------------
int DRIFF::FFT()
{
    if(wave.dgw_size <= 0) return 0;
    int i,j,k,k1,num,nhalf,phi,phi0;
    int m,N,NN,istep;
    double s,sc,c,a0,b0,tmp;
    static int rot[1024];
    static complex f[1024]; // real part にデータ
    // 標本化数の計算[最大1024]
    for(i=0;i<=10;i++){
        N = (int)pow(2,i); m = i;
        if(N >= wave.dgw_size) break;
    }
    if(m<=10){ if(dggd.wave_bit==16){ m-=1; N = N/2; } }
    else { m = 10; N = 1024; }
    NN = (dggd.wave_bit==8)?N:N*2;
    istep = (dggd.wave_bit==8)?1:2;
    // 音声データのコピー
    for(i=0;i<NN;i+=istep){
        if(dggd.wave_bit==8){
            f[i].re = wave.buf[i];
            f[i].im = 0.0;
        } else {
            f[i/2].re = (short)(wave.buf[i]+(wave.buf[i+1]<<8));
            f[i/2].im = 0.0;
        }
    }
    // 計算実行
    nhalf=N/2; num=N/2; sc=FFT_TWOPI/(double)N;
    while(num>=1){
        for(j=0;j<N;j+=(2*num)){
            phi=rot[j]/2; phi0=phi+nhalf;
            c=cos(sc*(double)phi); s=sin(sc*(double)(phi));
            for(k=j;k<j+num;k++){
                k1 = k+num;
                a0 = f[k1].re*c-f[k1].im*s;
                b0 = f[k1].re*s+f[k1].im*c;
                f[k1].re = f[k].re-a0; f[k1].im = f[k].im-b0;
                f[k].re  = f[k].re+a0; f[k].im  = f[k].im+b0;
                rot[k]   = phi;        rot[k1]  = phi0;
            }
        }
        num/=2;
    }
    for(i=0;i<N-1;i++){
        if((j=rot[i])>i){
            if(i<N && j<N){
                tmp=f[i].re; f[i].re=f[j].re; f[j].re=tmp;
                tmp=f[i].im; f[i].im=f[j].im; f[j].im=tmp;
            }
        }
    }
    // パワースペクトルの計算
    for(i=0;i<N;i++){
        wave.fft[i] = (float)(PW(f[i].re,f[i].im)/(N/2));
    }
    return 1024/2;
}