System.Media.SoundPlayer

NET2.0ではSoundPlayerクラスが用意され、簡単にサウンドが使えるようになっています。試しにoggを演奏させるプログラムを書いてみました。

#include <windows.h>
#include <Mmreg.h>
#include <stdio.h>
#include <math.h>
#include <vorbis/codec.h>
#include <vorbis/vorbisfile.h>

#using <System.dll>

using namespace System;
using namespace System::IO;
using namespace System::Media;

#pragma pack (push, 1)
typedef struct {
    short   wFormatTag;
    unsigned short wChannels;
    unsigned long dwSamplesPerSec;
    unsigned long dwAvgBytesVerSec;
    unsigned short wBlockAlign;
    unsigned short wBitsPerSample;
} FormatChunk;

typedef struct {
    char cRIFF[4];
    int iSizeRIFF;
    char cType[4];
    char cFmt[4];
    long chunkSize;
    FormatChunk chunk;
    char cData[4];
    int iSizeData;
} WAVEFILEHEADER;
#pragma pack (pop)

// ogg -> wave
bool convert_to_wave (OggVorbis_File& vf, vorbis_info* vi, 
        int word, array<unsigned char>^ bytes) {
    pin_ptr<unsigned char> p = &bytes [0];
    char* output = reinterpret_cast<char*>(p);

    int cur;
    int nWrite = 0;
    int nRead = 0;
    while (true) {
        nWrite = ov_read (&vf, output + sizeof (WAVEFILEHEADER) +  nRead, 
                bytes->Length - nRead, 0, word, 1, &cur);
        if (nWrite == 0) 
            break;
        else if (nWrite < 0) 
            return false;
        else 
            nRead += nWrite;
    }

    WAVEFILEHEADER wh;
    memcpy (wh.cRIFF, "RIFF", 4);
    wh.iSizeRIFF = sizeof (WAVEFILEHEADER) + nRead - 8;
    memcpy (wh.cType, "WAVE", 4);
    memcpy (wh.cFmt, "fmt ", 4);

    wh.chunkSize = sizeof (FormatChunk);
    wh.chunk.wFormatTag = WAVE_FORMAT_PCM;
    wh.chunk.wChannels = vi->channels;
    wh.chunk.dwSamplesPerSec = vi->rate;
    wh.chunk.dwAvgBytesVerSec= vi->rate * vi->channels * word;
    wh.chunk.wBlockAlign = vi->channels * word;
    wh.chunk.wBitsPerSample = word * 8;
    memcpy (wh.cData, "data", 4);
    wh.iSizeData = nRead;

    memcpy (output, &wh, sizeof (wh));

    return true;
}

void main (int argc, char* argv[]) {

    if (argc < 1) {
        printf ("usage: playogg.exe file(.ogg)\n");
        return;
    }

    FILE* fp;
    errno_t err;

    err = fopen_s (&fp, argv [1], "rb");
    if (err != 0) {
        printf ("fopen_s failed.\n");
        return;
    }

    OggVorbis_File vf;
    if (ov_open (fp, &vf, NULL, 0) < 0) {
        fclose (fp);
        fprintf (stderr, "ov_open failed.\n");
        return;
    }

    vorbis_info* vi = ov_info (&vf, -1);
    if (vi == NULL) {
        ov_clear (&vf);
        fprintf (stderr, "ov_info failed.\n");
        return;
    }

    int word = 2;
    double sec = ov_time_total (&vf, -1);

    printf ("%s - %02d:%02d\n", argv[1], (int)sec / 60, (int)sec % 60);

    int size = (LONG)ceil(vi->channels * vi->rate * sec * word);
    array<unsigned char>^ bytes = 
        gcnew array<unsigned char>(size + sizeof (WAVEFILEHEADER));
    bool ok = convert_to_wave (vf, vi, word, bytes);
    ov_clear (&vf);
    if (!ok) return;

    SoundPlayer^ player = gcnew SoundPlayer (gcnew MemoryStream (bytes));
    player->PlayLooping ();

    // Hit any key
    Console::Read ();
}

/* playogg 02.ogg
02.ogg - 05:15
 */

一度メモリに展開しているのが無駄なのでストリーミングにしようかと思いましたが時間切れ。(^^;