使用acm对Ogg进行Riff / Wave编码

时间:2011-09-30 11:02:03

标签: c++ audio wav ogg microsoft-acm

我的任务是记录wave文件,将其转换为ogg并将其打包到riff容器中。前两部分已完成,但我对第三部分有疑问。我找到了一个可以解决我的问题的源代码,但它无法正常工作。

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <memory.h>
#include <stdlib.h>
#include <mmreg.h>
#include <msacm.h>
#include <assert.h>
#include <math.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>

#define INPUT       "record.wav"
#define OUTPUT  "output_ogg.wav"


/* The following taken from the vorbis.acm sources */
/* Defines modes for encoding. Set this in 'fmt '  */

#define WAVE_FORMAT_VORBIS1     ((WORD)'O'+((WORD)'g'<<8))  // 0x674f("Og") ... Original stream compatible
#define WAVE_FORMAT_VORBIS2     ((WORD)'P'+((WORD)'g'<<8))  // 0x6750("Pg") ... Have independent header
#define WAVE_FORMAT_VORBIS3     ((WORD)'Q'+((WORD)'g'<<8))  // 0x6751("Qg") ... Have no codebook header
#define WAVE_FORMAT_VORBIS1P    ((WORD)'o'+((WORD)'g'<<8))  // 0x676f("og") ... Original stream compatible
#define WAVE_FORMAT_VORBIS2P    ((WORD)'p'+((WORD)'g'<<8))  // 0x6770("pg") ... Have independent header
#define WAVE_FORMAT_VORBIS3P    ((WORD)'q'+((WORD)'g'<<8))  // 0x6771("qg") ... Have no codebook header


/* The 'fact' chunk required for compressed WAV files */
struct FACT {
unsigned long   dwID;
unsigned long   dwSize;
unsigned long   dwSamples;
};

int main()
{   
/* Open source file */
HMMIO hSrcWaveFile=mmioOpen(INPUT,NULL,MMIO_READ);
assert(hSrcWaveFile);

MMCKINFO SrcWaveFile;
mmioDescend(hSrcWaveFile,&SrcWaveFile,NULL,0);

assert(SrcWaveFile.ckid==mmioStringToFOURCC("RIFF",MMIO_TOUPPER));
assert(SrcWaveFile.fccType==mmioStringToFOURCC("WAVE",MMIO_TOUPPER));

MMCKINFO SrcWaveFmt;

/* Go to RIFF-WAVE*/
mmioDescend(hSrcWaveFile,&SrcWaveFmt,&SrcWaveFile,0);

assert(SrcWaveFmt.ckid==mmioStringToFOURCC("fmt ",0));

int SrcHeaderSize=SrcWaveFmt.cksize;

if(SrcHeaderSize<sizeof(WAVEFORMATEX))
    SrcHeaderSize=sizeof(WAVEFORMATEX);

WAVEFORMATEX *SrcHeader=(WAVEFORMATEX *)new char[SrcHeaderSize];
ZeroMemory(SrcHeader,SrcHeaderSize);

/* Read fmt */
mmioRead(hSrcWaveFile,(char*)SrcHeader,SrcWaveFmt.cksize);

/* Leave the chunk */
mmioAscend(hSrcWaveFile,&SrcWaveFmt,0);

MMCKINFO SrcWaveData;
while(1){
    MMRESULT Result=mmioDescend(hSrcWaveFile,&SrcWaveData,&SrcWaveFile,0);
    assert(Result==0);
    if(SrcWaveData.ckid==mmioStringToFOURCC("data",0))
        break;
    Result=mmioAscend(hSrcWaveFile,&SrcWaveData, 0);
    assert(Result==0);
}

/* Destination header */
WAVEFORMATEX *DstHeader=(WAVEFORMATEX *)malloc(1024);
ZeroMemory(DstHeader,1024);

printf ("Going ACM!\n");

/* Suggest a format for us                             */
/* Try to coose the nmber 3 mode (whatever that is)    */

DstHeader->wFormatTag       =       WAVE_FORMAT_VORBIS3;
DstHeader->nChannels        =       2;
DstHeader->wBitsPerSample   =       16;
DstHeader->nSamplesPerSec   =       44100;

printf ("->acmFormatSuggest()\n");  
if (acmFormatSuggest(NULL,SrcHeader,DstHeader,1024,ACM_FORMATSUGGESTF_WFORMATTAG))
    printf ("ERROR: acmFormatSuggest()\n");

/* We shoudl have the DstHeader filled with data byt the ACM now */

/* Open destination */
HMMIO hDstWaveFile;

/* open the destination file */
hDstWaveFile=mmioOpen(OUTPUT,NULL,MMIO_CREATE|MMIO_WRITE);
assert(hDstWaveFile);
printf ("->mmioOpen() output.wav\n");

/* Create chunks */
MMCKINFO DstWaveFile;
DstWaveFile.fccType=mmioStringToFOURCC("WAVE",MMIO_TOUPPER);
mmioCreateChunk(hDstWaveFile,&DstWaveFile,MMIO_CREATERIFF);
printf ("->mmioCreateChunk() WAVE\n");

/* Create format chunk */
MMCKINFO DstWaveFmt;
DstWaveFmt.ckid=mmioStringToFOURCC("fmt ",0);

/* Create chunk write data and Ascend out of it */
mmioCreateChunk(hDstWaveFile,&DstWaveFmt,0);
printf ("->mmioCreateChunk() fmt\n");
mmioWrite(hDstWaveFile,(char*)DstHeader,sizeof(WAVEFORMATEX)+DstHeader->cbSize);
printf ("->mmioWrite() fmt header\n");
mmioAscend(hDstWaveFile,&DstWaveFmt,0);
printf ("->mmioAscend()\n");


/* fact chunk                                               */
/* this is only my idea of what it should look like         */
/* i found that some WAV files had more data than i write   */
/* but that seems enough for most of apps i tested          */

FACT DstFactChunk;
MMCKINFO FactChunk;

DstFactChunk.dwID       =   mmioStringToFOURCC ("fact", 0);
DstFactChunk.dwSamples  =   SrcWaveData.cksize / 4; 
DstFactChunk.dwSize     =   sizeof (DstFactChunk) - sizeof (DstFactChunk.dwID) - sizeof (DstFactChunk.dwSize);
FactChunk.ckid = mmioStringToFOURCC ("fact", 0);
FactChunk.cksize = DstFactChunk.dwSize;

/* Calculate the time */
float TIME;
if (SrcHeader->nSamplesPerSec == 44100)
    TIME = DstFactChunk.dwSamples / 44100.f;

mmioWrite (hDstWaveFile, (char *)&DstFactChunk, sizeof (DstFactChunk));

/* This ascend produced an error when i added this whole code               */
/* to my Dialog based MFC full feature super duper app                      */
/* Don't know why really but i think that Write already moves the pointer   */
/* past the chun sok this is unnecessery                                    */
/* mmioAscend (hDstWaveFile, &FactChunk, 0);                                */


/* Create Data chunk */
MMCKINFO DstWaveData;
DstWaveData.ckid=mmioStringToFOURCC("data",0);

mmioCreateChunk(hDstWaveFile,&DstWaveData,0);
mmioAscend (hDstWaveFile, &DstWaveData, 0);

printf ("->mmioCreateChunk() data\n");

/* Print the data we have gathered so far */
printf ("------------Source-----------\n");
printf ("Format: \t\t%X\n", SrcHeader->wFormatTag);
printf ("Channels: \t\t%d\n", SrcHeader->nChannels);
printf ("Samples/Sec: \t\t%d\n", SrcHeader->nSamplesPerSec);
printf ("AverageBytes/Sec: \t%d\n", SrcHeader->nAvgBytesPerSec);
printf ("Bits/Sample: \t\t%d\n", SrcHeader->wBitsPerSample);
printf ("BlockAlign: \t\t%d\n", SrcHeader->nBlockAlign);
printf ("DataSize: \t\t%d\n", SrcWaveData.cksize);
printf ("Time: \t\t\t%.3f\n", TIME);
printf ("Samples: \t\t%d\n", DstFactChunk.dwSamples);
printf ("Extra: \t\t\t%d\n", SrcHeader->cbSize);
printf ("------------------------------\n");

printf ("\n------------Destination------\n");
printf ("Format: \t\t%X\n", DstHeader->wFormatTag);
printf ("Channels: \t\t%d\n", DstHeader->nChannels);
printf ("Samples/Sec: \t\t%d\n", DstHeader->nSamplesPerSec);
printf ("AverageBytes/Sec: \t%d\n", DstHeader->nAvgBytesPerSec);
printf ("Bits/Sample: \t\t%d\n", DstHeader->wBitsPerSample);
printf ("BlockAlign: \t\t%d\n", DstHeader->nBlockAlign);
printf ("Extra: \t\t\t%d\n", DstHeader->cbSize);
printf ("------------------------------\n");


DWORD maxFormatSize = 0;
MMRESULT ACMres;

/* Get the max possbile size from the system this really no necessery */
/* but i was experimenting a bit and so I left it here                */

ACMres = acmMetrics( NULL, ACM_METRIC_MAX_SIZE_FORMAT, &maxFormatSize );

if (ACMres != MMSYSERR_NOERROR){
    printf ("ERROR: acmMetrics()\n");
}

/* Open ACM stream */
HACMSTREAM acm = NULL;
MMRESULT Result=acmStreamOpen(&acm,NULL,SrcHeader,DstHeader,NULL,0,0,0);
printf ("->acmStreamOpen()\n");

if (Result != MMSYSERR_NOERROR){
    printf ("ERROR: acmStreamOpen()\n");
    exit (-1);
}


/* This is where the problem's begin, first the buffers  */
/* Size of the dest/src is based on the size of src/dest */
DWORD DefaultWriteSize;
DWORD DefaultReadSize = SrcHeader->nBlockAlign * 1024;

/* If we know the dest */
/* Result=acmStreamSize(acm, DefaultWriteSize, &DefaultReadSize, ACM_STREAMSIZEF_DESTINATION); */


printf ("->acmStreamSize()\n");

/* If we know the source, well stay with the source PCM is less problematic */

Result = acmStreamSize (acm, DefaultReadSize, &DefaultWriteSize, ACM_STREAMSIZEF_SOURCE);

printf ("->acmStreamSize() gave us buffer size [%d]\n", DefaultWriteSize);

if (Result != MMSYSERR_NOERROR){
    printf ("ERROR: acmStreamSize()\n");
    exit (-1);
}

/* Allocate memory */
ACMSTREAMHEADER stream;
ZeroMemory(&stream,sizeof(stream));
stream.cbStruct=sizeof(stream);
stream.pbSrc=(BYTE*)GlobalAlloc(GMEM_FIXED,DefaultReadSize);
stream.cbSrcLength=DefaultReadSize;
stream.pbDst=(BYTE*)GlobalAlloc(GMEM_FIXED,DefaultWriteSize);
stream.cbDstLength=DefaultWriteSize;

/* Prepare header */
printf ("->acmStreamPrepareHeader()\n");
Result=acmStreamPrepareHeader(acm, &stream,0);
if (Result != MMSYSERR_NOERROR){
    printf ("ERROR: acmStreamPrepareHeader()\n");
    exit (-1);
}


/* The main encoding loop                                                       */
/* I'm pretty sure that before the actual reading of samples from the source    */
/* I should feed the ACM some junk so that it would write the necessery headers */
/* that Ogg Vorbis requires                                                     */
/* but i dont know how much of that 'junk' i would have to write there          */
/* i don't know if it can be junk                                               */
/* well i'm pretty clueless here                                                */

for(int RemainSize=SrcWaveData.cksize;RemainSize>0;){
    // ????
    int ReadSize=DefaultReadSize;

    if(ReadSize>RemainSize)
        ReadSize=RemainSize;

    RemainSize-=ReadSize;


    ReadSize=mmioRead(hSrcWaveFile,(char*)stream.pbSrc,ReadSize);
    if (ReadSize == -1){
        printf ("Can't read\n");
        break;
    }

    stream.cbSrcLength=ReadSize;

    /* Convert */
    Result=acmStreamConvert(acm,&stream,0);
    if (Result)
        printf ("ERROR: acmStreamConvert()\n");

    int WriteSize=stream.cbDstLengthUsed;

    /* Wrtie data */
    Result=mmioWrite(hDstWaveFile,(char*)stream.pbDst,WriteSize);

    if (Result == -1){
        printf ("Can't Write");
        break;
    }

    /* Uncomment this if you want to see the buffer sizes */
    /* printf ("READ[%d] :: WRITE[%d]\n", stream.cbSrcLengthUsed, stream.cbDstLengthUsed); */

    }


/* Cleaup on Isle 5 !!! */

    GlobalFree(stream.pbSrc);
    GlobalFree(stream.pbDst);
    acmStreamUnprepareHeader(acm,&stream,0);

    acmStreamClose(acm,0);

    mmioAscend(hSrcWaveFile,&SrcWaveData,0);
    mmioAscend(hSrcWaveFile,&SrcWaveFile,0);

    mmioAscend(hDstWaveFile,&DstWaveData,0);
    mmioAscend(hDstWaveFile,&DstWaveFile,0);

    free(DstHeader);

    mmioClose(hSrcWaveFile,0);
    mmioClose(hDstWaveFile,0);

    delete[] SrcHeader;

    return 0;
}

当我尝试执行此程序时,它向我显示acmFormatSuggest()和acmStreamOpen()失败。请帮我找错。

1 个答案:

答案 0 :(得分:2)

如果安装了支持这种格式的ACM音频编解码器,那么<​​p> acmFormatSuggest(顺便说一句,错误代码是什么?)可能会成功。默认情况下没有可用的,你实际安装了一个吗?