Ogg Vorbisを楽しむ

Oggとは
Oggの普及率?
WindowsでOggを楽しむには
OggVorbisを作る
OggVorbisデコーダを作る
OggVorbisエンコーダを作る
OggVorbisプレーヤーを作る
メモリ上のOgg Vorbisをデコードする
インターネット上のOgg Vorbisを再生する
関連リンク集

注意!以下、2010年以前のクッソ古い内容。って誰も見てないか・・・

最近流行りのOpusへの置き換えはopusfileを使えば簡単で、ov_???()をop_???()に変えれば良い(はず)

Oggとは

OggとはVorbis(非可逆圧縮)、FLAC(可逆圧縮)、Speex(音声向け)、等の入れ物のこと。
AVIファイルといっても非圧縮からDivXとかVP6とかCINEPAKがあるのと同じ。
まあ一般にOggといったらVorbisを指すけどね。(以下OggVorbisをOggと表記)
ライセンスフリーなので、私のような貧乏人や一部の方に人気ですね。
あとはライブラリの使いやすさです。私でもエンコード、デコードプログラム作れたし。
まあサンプルコード丸写しですが。

Oggの普及率?

5年くらいになるんかな。SDKが出て。
実際のとこ、どれくらい普及してるんだろうか。
デジタルオーディオプレイヤーでもmp3とwmaはほとんど対応だろうけどOggはね。
PCで再生できる環境の人はどれくらいいるんだろうか?
結構音質良いのに。みんなmp3で満足なんだろうか?ただLAMEだと結構音いいからなあ。

WindowsでOggを楽しむには

せっかくだからOggを聴く方法を書いておきます。当方WinXPしかないんで他のOSはわからん。
ライブラリを自分でコンパイルすれば、どのOSでも(pc88+N88BASICでも)Oggを作れるはずだけど。
WindowsMediaPlayerでOggを聴くにはOggDSとかのDirectShowフィルタを入れればいいだけ。
ついでにDirectShowを使ったアプリでも聴けるようになります。(コーデックをインストールするとも言う。)
うちとしては、拙作のMusicPlayerを勧めます。もれなくエンコーダもついてくるしね。

OggVorbisを作る

聞く方法がわかっても肝心のoggがなけりゃどうにもなりませんね。
Oggを作るソフトっていってもそんなに無いけど。低ビットレート(96kbps以下?)ならaoTuVが良いですね。
配布されているエンコーダはコンソールだからバッチ処理もできるし。ただGUIな人は困るかも。
AudioEncoder(現在2.04d:開発停止)はもの凄く便利ですね。
ただ使ってるエンコードライブラリが古いのが難点。
それから問題点がいくつかあります。
0,前提として.netを入れる必要があります。
1,oggエンコードでクオリティを-1.00にすると尻切れをおこします。
2,WMP10を入れるとwmaに変換できなくなります。(回避方法はあるみたいです。)

他にもVectorからダウンロードできると思います。

OggVorbisデコーダを作る

私はデコードさえ出来ればかまわないので、vorbisfileを使います。
デコードに必要な関数はov_open , ov_read , ov_clearの3つだけです。だから私でも再生するだけなら出来ました。
ogg_static.lib, vorbis_static.lib, vorbisfile_static.libをリンクします。
デコードするプログラムです。SDK等に含まれるvorbisfile_example.cを少しいじっています。
使い方はコンパイルしてvdec <oggファイル >wavファイル です。

/*
  vdec.c oggvorsisデコード
  libogg,libvorbisの著作権はXiphophorusにあります。
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include <io.h>
#include <fcntl.h>
#include<windows.h>

//ライブラリのリンク
#pragma comment(lib , "ogg_static")
#pragma comment(lib , "vorbis_static")
#pragma comment(lib , "vorbisfile_static")

//wavファイルのヘッダ
typedef struct{
    char RIFF[4];//"RIFF"
    DWORD bytes;//(ファイルのバイト数)-8
    char WAVE[4];// "WAVE" 
    char fmt[4];// "fmt "
    DWORD siz_wf;//PCMWAVEFORMAT構造体のバイト数=常に16
    //PCMWAVEFORMAT構造
    WORD wFormatTag; 
    WORD nChannels; 
    DWORD nSamplesPerSec;
    DWORD nAvgBytesPerSec; 
    WORD nBlockAlign;
    WORD wBitsPerSample;
    char data[4];//"data"
    DWORD pcmbytes;//波形データのバイト数
} WAVHEADER;

int main(){
  OggVorbis_File vf;
  vorbis_info *vi;
  int ret = 0;
  int current_section;
  char pcmout[4096]; /* take 4k out of the data segment, not the stack */
  WAVHEADER wh;
  DWORD bytes;
  DWORD pos = 0;
  
  _setmode( _fileno( stdin ), _O_BINARY );
  _setmode( _fileno( stdout ), _O_BINARY );

  //ファイルを開く
  if(ov_open(stdin, &vf, NULL, 0) < 0) {
      fprintf(stderr,"Oggではありません\n");
      exit(1);
  }

  //必要な情報を取得
  vi=ov_info(&vf,-1);
  bytes = vi->channels * 2 * ov_pcm_total(&vf,-1);
  
  //wavヘッダを作る
  memcpy(wh.RIFF , "RIFF" , 4);
  wh.bytes = bytes + 44 - 8;
  memcpy(wh.WAVE , "WAVE", 4); 
  memcpy(wh.fmt , "fmt ", 4);
  wh.siz_wf = 16;
  wh.wFormatTag = WAVE_FORMAT_PCM;//=1
  wh.nChannels = vi->channels; 
  wh.nSamplesPerSec = vi->rate;
  wh.wBitsPerSample = 16;
  wh.nBlockAlign = wh.nChannels * wh.wBitsPerSample/8;
  wh.nAvgBytesPerSec = wh.nSamplesPerSec * wh.nBlockAlign; 
  memcpy(wh.data , "data", 4);
  wh.pcmbytes= bytes;
  fwrite(&wh,44,1,stdout);//書き込む
  
  //デコード
 fprintf(stderr,"デコード中...\n");
  do{
    ret = ov_read(&vf,pcmout,sizeof(pcmout),0,2,1,&current_section);
    if (ret > 0) fwrite(pcmout,1,ret,stdout);
    
    //進捗状況表示(無いほうがいいかも)
    pos += ret;
    fprintf(stderr,"%d / %d (%d%%)\r" , pos , bytes , pos/(bytes/100));
  }while(ret != 0);
  
  //後始末
  ov_clear(&vf);
  fprintf(stderr,"\n終わり\n");
  
  return(0);
}

OggVorbisエンコーダを作る

私はエンコードさえ出来ればかまわないので、vorbisencを使います。
中でどのようなことが行われているのかよく分からないので、サンプル丸写しです。
使い方はコマンドラインでvenc wavファイル oggファイルです。
エラーチェックはまったくといっていいほどしていません。

/*
 * venc.c Ogg Vorbis エンコーダ
 * libogg,libvorbisはXiph.orgに著作権があります。
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include "vorbis/vorbisenc.h"
#include <windows.h>

#include <io.h>
#include <fcntl.h>

#pragma comment(lib , "ogg_static")
#pragma comment(lib , "vorbis_static")
#pragma comment(lib , "vorbisenc_static")
#define READ 1024

//wavファイルのヘッダ
typedef struct{
    char RIFF[4];//"RIFF"
    DWORD bytes;//(ファイルのバイト数)-8
    char WAVE[4];// "WAVE" 
    char fmt[4];// "fmt "
    int siz_wf;//PCMWAVEFORMAT構造体のバイト数=常に16
    //PCMWAVEFORMAT構造
    WORD wFormatTag; 
    WORD nChannels; 
    DWORD nSamplesPerSec;
    DWORD nAvgBytesPerSec; 
    WORD nBlockAlign;
    WORD wBitsPerSample;
    char data[4];//"data"
    DWORD pcmbytes;//波形データのバイト数
} WAVHEADER;


signed char readbuffer[READ*4+44]; /* out of the data segment, not the stack */

void usage()
{
    fprintf(stderr , "venc 入力ファイル 出力ファイル");
    exit(0);
}

int main(int argc,char**argv){
  ogg_stream_state os; /* take physical pages, weld into a logical
              stream of packets */
  ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
  ogg_packet       op; /* one raw packet of data for decode */
  
  vorbis_info      vi; /* struct that stores all the static vorbis bitstream
              settings */
  vorbis_comment   vc; /* struct that stores all the user comments */

  vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
  vorbis_block     vb; /* local working space for packet->PCM decode */

  int eos=0,ret;
  int i, founddata;
  int ch , hz;
  int n;
  DWORD pos = 0;
  
  FILE *infile , *outfile ;
  WAVHEADER wh;
  
  if(argc<3) usage();
  
  infile  = fopen(argv[1] , "rb");
  outfile = fopen(argv[2] , "wb");
  if(infile == NULL || outfile == NULL) usage();
  
  fread(&wh , 1 , sizeof(WAVHEADER), infile);
  
  ch = wh.nChannels;
  hz = wh.nSamplesPerSec;
  
  //エンコード情報
  fprintf(stderr , "%s -> %s %dch %dHz\n" , argv[1] , argv[2] , ch , hz);
  
  vorbis_info_init(&vi);

  ret=vorbis_encode_init_vbr(&vi,ch,hz,.5);
  if(ret)exit(1);

  vorbis_comment_init(&vc);
  vorbis_comment_add_tag(&vc,"ENCODER","encoder_example.c");

  vorbis_analysis_init(&vd,&vi);
  vorbis_block_init(&vd,&vb);
  
  srand(time(NULL));
  ogg_stream_init(&os,rand());

  {
    ogg_packet header;
    ogg_packet header_comm;
    ogg_packet header_code;

    vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code);
    ogg_stream_packetin(&os,&header); /* automatically placed in its own
                     page */
    ogg_stream_packetin(&os,&header_comm);
    ogg_stream_packetin(&os,&header_code);

    /* This ensures the actual
     * audio data will start on a new page, as per spec
     */
    while(!eos){
        int result=ogg_stream_flush(&os,&og);
        if(result==0)break;
        fwrite(og.header,1,og.header_len,outfile);
        fwrite(og.body,1,og.body_len,outfile);
    }

  }
  
  fprintf(stderr , "エンコード中...\n");
  while(!eos){
    long i;
    long bytes=fread(readbuffer,1,READ*4,infile); /* stereo hardwired here */
    
    //進捗状況表示
    pos += bytes;
    fprintf(stderr , "%d / %d (%d%%)\r" , pos , wh.pcmbytes , pos/(wh.pcmbytes/100));
    
    if(bytes==0){
      /* end of file.  this can be done implicitly in the mainline,
         but it's easier to see here in non-clever fashion.
         Tell the library we're at end of stream so that it can handle
         the last frame and mark end of stream in the output properly */
      vorbis_analysis_wrote(&vd,0);

    }else{
      /* data to encode */

      /* expose the buffer to submit data */
      float **buffer=vorbis_analysis_buffer(&vd,READ);
      
      /* uninterleave samples */
      for(i=0;i<bytes/(ch*2);i++){
          for(n=0;n<ch;n++){
    buffer[n][i]=((readbuffer[i*(ch*2)+n*2+1]<<8)|
              (0x00ff&(int)readbuffer[i*(ch*2)+n*2]))/32768.f;
          }
      }
    
      /* tell the library how much we actually submitted */
      vorbis_analysis_wrote(&vd,i);
    }

    /* vorbis does some data preanalysis, then divvies up blocks for
       more involved (potentially parallel) processing.  Get a single
       block for encoding now */
    while(vorbis_analysis_blockout(&vd,&vb)==1){

      /* analysis, assume we want to use bitrate management */
      vorbis_analysis(&vb,NULL);
      vorbis_bitrate_addblock(&vb);

      while(vorbis_bitrate_flushpacket(&vd,&op)){
    
    /* weld the packet into the bitstream */
    ogg_stream_packetin(&os,&op);
    
    /* write out pages (if any) */
    while(!eos){
      int result=ogg_stream_pageout(&os,&og);
      if(result==0)break;
      fwrite(og.header,1,og.header_len,outfile);
      fwrite(og.body,1,og.body_len,outfile);
      
      /* this could be set above, but for illustrative purposes, I do
         it here (to show that vorbis does know where the stream ends) */
      
      if(ogg_page_eos(&og))eos=1;
    }
      }
    }
  }

  /* clean up and exit.  vorbis_info_clear() must be called last */
  
  ogg_stream_clear(&os);
  vorbis_block_clear(&vb);
  vorbis_dsp_clear(&vd);
  vorbis_comment_clear(&vc);
  vorbis_info_clear(&vi);
  
  /* ogg_page and ogg_packet structs always point to storage in
     libvorbis.  They're never freed or manipulated directly */
  
  fprintf(stderr,"\n終わり\n");
  return(0);
}

OggVorbisプレーヤーを作る

実際に音が出ないと面白くないので、プレーヤーを作ってみました。windows専用です。 使い方は引数にogg vorbisのファイルを指定するだけです。 あとは勝手に再生します。途中でやめるときは[Ctrl]+[C]ですね。 デコード後のサイズが4096*100*10000バイトを超える場合、どうなるか分かりません。

/*
 * ovp.c Ogg Vorbis プレイヤー
 * libogg,libvorbisはXiph.orgに著作権があります。
 */
#include <windows.h>
#include <mmsystem.h>
#include <stdlib.h>
#include <math.h>
#include "vorbis/vorbisfile.h"

#pragma comment(lib,"winmm.lib")
#pragma comment(lib,"ogg_static.lib")
#pragma comment(lib,"vorbis_static.lib")
#pragma comment(lib,"vorbisfile_static.lib")

#define conv 4096
#define bufsiz (conv*100)
#define MAXBUF 10000

HWAVEOUT hwo;
char *buf[MAXBUF];
WAVEHDR *wh[MAXBUF];
OggVorbis_File vf;
int play, played,cleared, flag;

/* コールバック関数 */
void CALLBACK call( HWAVEOUT hwo, UINT msg,DWORD inst, DWORD p1, DWORD p2 ){
    if (msg == WOM_DONE) played++;
}

/* デコード準備 */
int hajime(const char *filename)
{
    FILE *fp;
    int ret;
    vorbis_info *vi;
    WAVEFORMATEX wfe;
    
    fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("ファイル[%s]開けません\n", filename);
        return FALSE;
    }
    ret = ov_open(fp, &vf, NULL, 0);
    if (ret < 0 ) {
        printf("ov_open関数失敗\n");
        return FALSE;
    }
    vi = ov_info(&vf,-1);
    printf("再生ファイル=%s (%dHz %dch)\n再生時間=%d(秒) 平均ビットレート=%d(bps)\n",
        filename, vi->rate, vi->channels, (int)ov_time_total(&vf, -1), vi->bitrate_nominal);
    wfe.wFormatTag=WAVE_FORMAT_PCM;
    wfe.nChannels=vi->channels;
    wfe.wBitsPerSample=16;
    wfe.nBlockAlign=wfe.nChannels * wfe.wBitsPerSample/8;
    wfe.nSamplesPerSec=vi->rate;
    wfe.nAvgBytesPerSec=wfe.nSamplesPerSec * wfe.nBlockAlign;
    waveOutOpen(&hwo,WAVE_MAPPER,&wfe,(DWORD)call,0, WAVE_ALLOWSYNC | CALLBACK_FUNCTION);/* デバイスオープン */
    dec();/* デコード開始 */
    return TRUE;
}

/* デコード */
int dec()
{
    int siz, read, current_section;
    
    play = flag = 0;
    do {
        buf[play] = malloc(bufsiz+conv);
        wh[play] = calloc(sizeof(WAVEHDR), 1);
        siz = current_section = 0;
        while (siz < bufsiz) {
            read=ov_read(&vf,&buf[play][siz],conv,0,2,1,&current_section);
            if (read != 0) {
                siz += read;
            } else {
                flag = 1; /* 終了のフラグ立てる */
                break;
            }
        }
        wh[play]->lpData = buf[play];
        wh[play]->dwBufferLength = siz;
        waveOutPrepareHeader (hwo,wh[play],sizeof(WAVEHDR));
        waveOutWrite(hwo,wh[play],sizeof(WAVEHDR));/* 再生 */
        while (fabs(play - played) > 1) Sleep(100);/* 待つ */
        for (; cleared < played; cleared++) {/* バッファがあいていれば削除しておく */
            waveOutUnprepareHeader (hwo,wh[cleared],sizeof(WAVEHDR));
            free(buf[cleared]);
            free(wh[cleared]);
        }
        play++;
    } while (flag == 0 && play < MAXBUF);
    while (play != played) Sleep(100);/* 再生終了まで待つ。*/
    owari();/* 終わり*/
    printf("終わり\n");
    return TRUE;
}

/* 後始末する */
int owari()
{
    for (;cleared < played ; cleared++) {
        waveOutUnprepareHeader(hwo,wh[cleared],sizeof(WAVEHDR));
        free(buf[cleared]);
        free(wh[cleared]);
    }
    waveOutReset(hwo);
    waveOutClose(hwo);
    ov_clear(&vf);
    return TRUE;
}

/* main関数 ここから。*/
main(int argc, char **argv)
{
    if (argc < 2) {
        puts("引数にogg vorbisファイルを入れましょう");
        return 0;
    }
    hajime(argv[1]);
}

メモリ上のOgg Vorbisをデコードする

vorbisfileを使うと、簡単にメモリ上に展開されたOgg Vorbisを再生できるそうです。 私には大変難解でしたが、とりあえず出来てうれしかったので、書いておきます。 ov_openは、コールバック関数にfread,fseek, ftell,fclseを入れて、ov_open_callbacksを呼んでいるだけです。 だから、同じようにメモリ上のデータに対する関数を作ればメモリ上のデータをデコードできるわけです。 様々方法がありますが、私はFILEのように構造体(OVMEM)を使うことにしました。

使い方はDOS窓で ovmd oggファイル です。勝手にデコードしwavファイルを作ります。

/*
    ovmd.c oggvorsisデコード(メモリ版)
    libogg,libvorbisの著作権はXiphophorusにあります。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include <windows.h>

//ライブラリのリンク
#pragma comment(lib , "ogg_static")
#pragma comment(lib , "vorbis_static")
#pragma comment(lib , "vorbisfile_static")

//wavファイルのヘッダ
typedef struct{
    char RIFF[4];//"RIFF"
    DWORD bytes;//(ファイルのバイト数)-8
    char WAVE[4];// "WAVE" 
    char fmt[4];//"fmt "
    DWORD siz_wf;//PCMWAVEFORMAT構造体のバイト数=常に16
    //PCMWAVEFORMAT構造
    WORD wFormatTag; 
    WORD nChannels; 
    DWORD nSamplesPerSec;
    DWORD nAvgBytesPerSec; 
    WORD nBlockAlign;
    WORD wBitsPerSample;
    char data[4];//"data"
    DWORD pcmbytes;//波形データのバイト数
} WAVHEADER;

typedef struct {
    char *buf;//メモリ上のogg vorbis
    int siz;//サイズ
    int pos;//現在位置
} OVMEM;

//ファイル名(fnam)をmodeで開き OVMEM構造体にいれ、それをポインタで返す。
OVMEM *mopen(const char *fnam, char *mode)
{
    FILE *fp;
    int rsiz;
    OVMEM *pom;
    
    fp = fopen(fnam, mode);
    if(fp == NULL) {
        puts("mopen : fopenエラー");
        return NULL;
    }
    pom = malloc(sizeof(OVMEM));
    pom->pos = 0;
    fseek(fp, 0, SEEK_END);
    pom->siz = ftell(fp);
    pom->buf = malloc(pom->siz);
    rewind(fp);
    
    rsiz =fread(pom->buf, 1, pom->siz, fp);
    if (rsiz != pom->siz) {
        puts("mopen : freadエラー 指定量を読み込めん");
        return NULL;
    }
    fclose(fp);
    return pom;
}

/* コールバック関数 以下4つ */
size_t mread(char *ptr, size_t size, size_t nmemb, OVMEM *pom)
{
    if (pom == NULL) return -1;
    if ((pom->pos >= pom->siz) || (pom->pos == -1)) return 0;
    if (pom->pos + size*nmemb >= pom->siz) {
        int ret;
        memcpy(ptr, &pom->buf[pom->pos], pom->siz - pom->pos);
        ret = (pom->siz - pom->pos)/size;
        pom->pos = pom->siz;
        return ret;
    }
    memcpy(ptr, &pom->buf[pom->pos], nmemb * size);
    pom->pos += (nmemb * size);
    return nmemb;
}

int mseek(OVMEM *pom, ogg_int64_t offset, int whence)
{
    int newpos;
    
    if (pom == NULL || pom->pos < 0) return -1;
    if (offset < 0) {
        pom->pos = -1;
        return -1;
    }
    switch (whence) {
        case SEEK_SET:
            newpos = offset;
            break;
        case SEEK_CUR:
            newpos = pom->pos + offset;
            break;
        case SEEK_END:
            newpos = pom->siz + offset;
            break;
        default:
            return -1;
    }
    if (newpos < 0) return -1;
    pom->pos = newpos;
    
    return 0;
}

long mtell(OVMEM *pom)
{
    if (pom == NULL) return -1;
    return pom->pos;
}

int mclose(OVMEM *pom)
{
    if (pom == NULL) return -1;
    free(pom->buf);
    free(pom);
    return 0;
}

// mainです。ここからスタート
main(int argc, char **argv)
{
    OggVorbis_File vf;
    vorbis_info *vi;
    ov_callbacks oc; 
    FILE *of;
    OVMEM *pom;
    WAVHEADER wh;
    DWORD bytes;
    DWORD pos = 0;
    int ret = 0;
    int current_section;
    char *buf;
    char pcmout[4096]; //take 4k out of the data segment, not the stack
    char outfile[MAX_PATH];//出力ファイル名
    
    if (argc < 2) {
        puts("引数無いよ。 ovmd 入力oggファイル (出力wavファイル)");
        return ;
    }
    
    //コールバック関数を指定
    oc.read_func = mread;
    oc.seek_func = mseek;
    oc.close_func = mclose;
    oc.tell_func = mtell;
    
    pom = mopen(argv[1], "rb");//argv[1]のファイルをメモリに展開
    
    ret = ov_open_callbacks(pom, &vf, NULL, 0, oc);
    switch (ret) {
        case OV_EREAD:{printf("A read from media returned an error.\n");exit(1);} 
        case OV_ENOTVORBIS:{printf("Bitstream is not Vorbis data. \n");exit(1);}
        case OV_EVERSION :{printf("Vorbis version mismatch. \n");exit(1);}
        case OV_EBADHEADER:{printf("Invalid Vorbis bitstream header. \n");exit(1);}
        case OV_EFAULT:{printf("Internal logic fault; indicates a bug or heap/stack corruption. \n");exit(1);}
    }
    
    //出力wavファイルを開く
    if (argc > 2) {
        if (!strcmp(&argv[2][strlen(argv[2])-4] , ".wav")) {
            //お尻が.wavかな?
            strcpy(outfile, argv[2]);
        } else {
            sprintf(outfile, "%s.wav", argv[2]);
        }
    } else {
        sprintf(outfile, "%s.wav", argv[1]);
    }
    
    if ((of = fopen(outfile ,"wb")) == NULL) {
        puts("出力ファイル開けん。エラーですよ");
        return ;
    }
    vi=ov_info(&vf,-1);
    bytes = vi->channels * 2 * ov_pcm_total(&vf, -1);
    
    //wavヘッダを作る
    memcpy(wh.RIFF , "RIFF" , 4);
    wh.bytes = bytes + 44 - 8;
    memcpy(wh.WAVE , "WAVE", 4); 
    memcpy(wh.fmt , "fmt ", 4);
    wh.siz_wf = 16;
    wh.wFormatTag = WAVE_FORMAT_PCM;//=1
    wh.nChannels = vi->channels; 
    wh.nSamplesPerSec = vi->rate;
    wh.wBitsPerSample = 16;
    wh.nBlockAlign = wh.nChannels * wh.wBitsPerSample/8;
    wh.nAvgBytesPerSec = wh.nSamplesPerSec * wh.nBlockAlign; 
    memcpy(wh.data , "data", 4);
    wh.pcmbytes= bytes;
    
    //ヘッダを書き込む
    fwrite(&wh,44,1,of);
 
    //デコード
    printf("デコード中...\n");
    do{
        ret = ov_read(&vf,pcmout,4096,0,2,1,&current_section);
        if (ret > 0) fwrite(pcmout,1,ret,of);
        pos += ret; printf("%d / %d\r", pos, bytes);
    }while(ret != 0);
    
    fclose(of);
    
    //後始末
    ov_clear(&vf);
    printf("\n終わり\n");
    
    return(0);
}

インターネット上のOgg Vorbisを再生する

いわゆるストリーミング再生とは違うかもしれませんが、インターネット上にあるOgg Vorbisを再生するプログラムです。

/*
    ovst.c インターネット上のoggvorsisを再生する。
    libogg,libvorbisの著作権はXiphophorusにあります。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>
#include<windows.h>
#include<wininet.h>
#include <mmsystem.h>

//ライブラリのリンク
#pragma comment(lib , "ogg_static")
#pragma comment(lib , "vorbis_static")
#pragma comment(lib , "vorbisfile_static")
#pragma comment(lib , "wininet")
#pragma comment(lib , "winmm")

#define conv 4096
#define bufsiz (conv*100)
#define MAXBUF 10000

typedef struct {
    HINTERNET hINET;
    HINTERNET hURL;
    DWORD read;
} INET;

HWAVEOUT hwo;
char *buf[MAXBUF];
WAVEHDR *wh[MAXBUF];
OggVorbis_File vf;
int play, played,cleared, flag;

//URLを入れる
INET *iopen(const char *url)
{
    HINTERNET hInternet;
    HINTERNET hUrl;
    INET *inet;
    
    inet = malloc(sizeof(INET));
    
    /* WININET初期化 */
    hInternet = InternetOpen("Ogg Vorbis Streaming Test",INTERNET_OPEN_TYPE_PRECONFIG,    NULL,NULL,0);
    if(hInternet == NULL) return NULL;
    /* URLのオープン */
    hUrl = InternetOpenUrl(hInternet,url,NULL,0,0,0);
    if(hUrl == NULL) return NULL;
    
    inet->hINET = hInternet;
    inet->hURL  = hUrl;
    inet->read  = 0;
    return inet;

}

/* コールバック関数 以下4つ */
size_t iread(char *ptr, size_t size, size_t nmemb, INET *inet)
{
    int readsize, ret;
    
    if (inet == NULL) return -1;
    ret = InternetReadFile(inet->hURL, ptr, size*nmemb ,&readsize);
    if (ret == FALSE) {
        puts("InternetReadFileエラー");
        exit(0);
    }
    if (readsize == 0) return 0;
    inet->read += readsize;
    return (readsize / size);
}

int iseek(INET *inet, ogg_int64_t offset, int whence)
{
    return -1;//シーク不可能
}

long itell(INET *inet)
{
    if (inet == NULL) return -1;
    return inet->read;
}

int iclose(INET *inet)
{
    if (inet == NULL) return -1;
    InternetCloseHandle(inet->hURL);
    InternetCloseHandle(inet->hINET);
    free(inet);
    return 0;
}

/* コールバック関数 */
void CALLBACK call( HWAVEOUT hwo, UINT msg,DWORD inst, DWORD p1, DWORD p2 ){
    if (msg == WOM_DONE) played++;
}

/* デコード準備 */
int hajime(const char *filename)
{
    int ret;
    vorbis_info *vi;
    WAVEFORMATEX wfe;
    ov_callbacks oc; 
    INET *inet;
    
    //コールバック関数を指定
    oc.read_func = iread;
    oc.seek_func = iseek;
    oc.close_func = iclose;
    oc.tell_func = itell;
    
    inet = iopen(filename);
    ret = ov_open_callbacks(inet, &vf, NULL, 0, oc);
    switch (ret) {
        case OV_EREAD:{printf("A read from media returned an error.\n");exit(1);} 
        case OV_ENOTVORBIS:{printf("Bitstream is not Vorbis data. \n");exit(1);}
        case OV_EVERSION :{printf("Vorbis version mismatch. \n");exit(1);}
        case OV_EBADHEADER:{printf("Invalid Vorbis bitstream header. \n");exit(1);}
        case OV_EFAULT:{printf("Internal logic fault; indicates a bug or heap/stack corruption. \n");exit(1);}
    }
    vi=ov_info(&vf,-1);
    printf("再生ファイル=%s (%dHz %dch)\n平均ビットレート=%d(bps)\n",
        filename, vi->rate, vi->channels, vi->bitrate_nominal);
    wfe.wFormatTag=WAVE_FORMAT_PCM;
    wfe.nChannels=vi->channels;
    wfe.wBitsPerSample=16;
    wfe.nBlockAlign=wfe.nChannels * wfe.wBitsPerSample/8;
    wfe.nSamplesPerSec=vi->rate;
    wfe.nAvgBytesPerSec=wfe.nSamplesPerSec * wfe.nBlockAlign;
    waveOutOpen(&hwo,WAVE_MAPPER,&wfe,(DWORD)call,0, WAVE_ALLOWSYNC | CALLBACK_FUNCTION);/* デバイスオープン */
    dec();/* デコード開始 */
    return TRUE;
}

/* デコード */
int dec()
{
    int siz, read, current_section;
    
    play = flag = 0;
    do {
        buf[play] = malloc(bufsiz+conv);
        wh[play] = calloc(sizeof(WAVEHDR), 1);
        siz = current_section = 0;
        while (siz < bufsiz) {
            read=ov_read(&vf,&buf[play][siz],conv,0,2,1,&current_section);
            if (read != 0) {
                siz += read;
            } else {
                flag = 1; /* 終了のフラグ立てる */
                break;
            }
        }
        wh[play]->lpData = buf[play];
        wh[play]->dwBufferLength = siz;
        waveOutPrepareHeader (hwo,wh[play],sizeof(WAVEHDR));
        waveOutWrite(hwo,wh[play],sizeof(WAVEHDR));/* 再生 */
        
        /* バッファがあいていれば削除しておく */
        for (; cleared < played; cleared++) {
            waveOutUnprepareHeader (hwo,wh[cleared],sizeof(WAVEHDR));
            free(buf[cleared]);
            free(wh[cleared]);
        }
        play++;
    } while (flag == 0 && play < MAXBUF);
    while (play != played) Sleep(100);/* 再生終了まで待つ。*/
    owari();
    puts("終わり");
    return TRUE;
}

/* 後始末する */
int owari()
{
    for (;cleared < played ; cleared++) {
        waveOutUnprepareHeader(hwo,wh[cleared],sizeof(WAVEHDR));
        free(buf[cleared]);
        free(wh[cleared]);
    }
    waveOutReset(hwo);
    waveOutClose(hwo);
    ov_clear(&vf);
    return TRUE;
}

/* main関数 ここから。*/
main(int argc, char **argv)
{
    if (argc < 2) {
        puts("引数にOgg VorbisのURLをいれる。");
        return 0;
    }
    hajime(argv[1]);
}

関連リンク

Ogg Vorbis関連リンク

Xiph.org←oggとかvorbisの親玉。(英語)
aoTuV←最高音質のOgg Vorbisを生成可能。
vectorのOGGOGOダウンロードページ