-
-
Save mirichi/77968e571d01082814a9 to your computer and use it in GitHub Desktop.
OggのファイルアクセスをIOオブジェクト経由に変更
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "ruby.h" | |
#include "ruby/encoding.h" | |
#include "vorbis/vorbisfile.h" | |
// RubyのSoundOggクラス | |
static VALUE cSoundOgg; | |
// Rubyの例外オブジェクト | |
static VALUE eSoundOggError; | |
// RubyのSoundOggオブジェクトが持つC構造体 | |
struct SoundOgg { | |
OggVorbis_File ovf; | |
vorbis_info *info; | |
VALUE vio; | |
}; | |
// プロトタイプ宣言 | |
static void SoundOgg_mark(void *s); | |
static void SoundOgg_free(void *s); | |
static size_t SoundOgg_memsize(const void *s); | |
// TypedData用の型データ | |
const rb_data_type_t SoundOgg_data_type = { | |
"SoundOgg", | |
{ | |
SoundOgg_mark, // マーク関数 | |
SoundOgg_free, // 解放関数 | |
SoundOgg_memsize, // サイズ関数 | |
}, | |
NULL, NULL | |
}; | |
// GCのマークで呼ばれるマーク関数 | |
static void SoundOgg_mark(void *s) | |
{ | |
struct SoundOgg *so = (struct SoundOgg *)s; | |
// マーク | |
rb_gc_mark(so->vio); | |
} | |
// GCで回収されたときに呼ばれる解放関数 | |
static void SoundOgg_free(void *s) | |
{ | |
struct SoundOgg *so = (struct SoundOgg *)s; | |
ov_clear(&so->ovf); | |
so->info = NULL; | |
} | |
// ObjectSpaceからのサイズの問い合わせに応答する | |
static size_t SoundOgg_memsize(const void *s) | |
{ | |
// ざっくり | |
return sizeof(struct SoundOgg); | |
} | |
// SoundOgg.newするとまずこれが呼ばれ、次にinitializeが呼ばれる | |
static VALUE SoundOgg_allocate(VALUE klass) | |
{ | |
VALUE obj; | |
struct SoundOgg *so; | |
// RubyのTypedData型オブジェクトを生成する | |
obj = TypedData_Make_Struct(klass, struct SoundOgg, &SoundOgg_data_type, so); | |
so->info = NULL; | |
// 生成したSoundOggオブジェクトを返す | |
return obj; | |
} | |
// コールバック関数SoundOgg_read | |
size_t SoundOgg_read(void *buf, size_t blocksize, size_t readsize, void *f) | |
{ | |
VALUE obj = (VALUE)f; | |
VALUE vstr; | |
vstr = rb_funcall(obj, rb_intern_const("read"), 1, UINT2NUM(blocksize * readsize)); | |
Check_Type(vstr, T_STRING); | |
memcpy(buf, RSTRING_PTR(vstr), RSTRING_LEN(vstr)); | |
return RSTRING_LEN(vstr); | |
} | |
// コールバック関数SoundOgg_seek | |
int SoundOgg_seek(void *f, ogg_int64_t offset, int whence) | |
{ | |
VALUE obj = (VALUE)f; | |
int result; | |
result = NUM2INT(rb_funcall(obj, rb_intern_const("seek"), 2, LL2NUM(offset), INT2NUM(whence))); | |
return result; | |
} | |
// コールバック関数SoundOgg_close | |
int SoundOgg_close(void *f) | |
{ | |
VALUE obj = (VALUE)f; | |
int result; | |
result = NUM2INT(rb_funcall(obj, rb_intern_const("close"), 0)); | |
return result; | |
} | |
// コールバック関数SoundOgg_tell | |
long SoundOgg_tell(void *f) | |
{ | |
VALUE obj = (VALUE)f; | |
long result; | |
result = NUM2LONG(rb_funcall(obj, rb_intern_const("tell"), 0)); | |
return result; | |
} | |
// SoundOgg#initialize | |
static VALUE SoundOgg_initialize(VALUE self, VALUE vio) | |
{ | |
struct SoundOgg *so = (struct SoundOgg *)RTYPEDDATA_DATA(self); | |
ov_callbacks callbacks = { | |
(size_t (*)(void *, size_t, size_t, void *)) SoundOgg_read, | |
(int (*)(void *, ogg_int64_t, int)) SoundOgg_seek, | |
(int (*)(void *)) NULL, | |
(long (*)(void *)) SoundOgg_tell | |
}; | |
if (ov_open_callbacks((void *)vio, &so->ovf, NULL, 0, callbacks) != 0) rb_raise(eSoundOggError, "open error"); | |
// Oggファイルの音声フォーマット情報 | |
so->info = ov_info(&so->ovf, -1); | |
so->vio = vio; | |
return self; | |
} | |
// SoundOgg#rate | |
static VALUE SoundOgg_im_rate(VALUE self) | |
{ | |
struct SoundOgg *so = (struct SoundOgg *)RTYPEDDATA_DATA(self); | |
if (!so->info) rb_raise(eSoundOggError, "disposed object"); | |
return INT2NUM(so->info->rate); | |
} | |
// SoundOgg#channels | |
static VALUE SoundOgg_im_channels(VALUE self) | |
{ | |
struct SoundOgg *so = (struct SoundOgg *)RTYPEDDATA_DATA(self); | |
if (!so->info) rb_raise(eSoundOggError, "disposed object"); | |
return INT2NUM(so->info->channels); | |
} | |
// SoundOgg#read | |
static VALUE SoundOgg_im_read(VALUE self, VALUE vsize) | |
{ | |
struct SoundOgg *so = (struct SoundOgg *)RTYPEDDATA_DATA(self); | |
char buf[4096]; | |
int readsize; | |
if (!so->info) rb_raise(eSoundOggError, "disposed object"); | |
readsize = ov_read(&so->ovf, buf, NUM2INT(vsize) > 4096 ? 4096 : NUM2INT(vsize), 0, 2, 1, NULL); | |
if (readsize < 0) rb_raise(eSoundOggError, "file read error"); | |
return rb_str_new(buf, readsize); | |
} | |
void Init_soundogg(void) | |
{ | |
// 例外定義 | |
eSoundOggError = rb_define_class( "SoundOggError", rb_eRuntimeError ); | |
// SoundOggクラス生成 | |
cSoundOgg = rb_define_class("SoundOgg", rb_cObject); | |
rb_define_private_method(cSoundOgg, "initialize", SoundOgg_initialize, 1); | |
rb_define_method(cSoundOgg, "rate", SoundOgg_im_rate, 0); | |
rb_define_method(cSoundOgg, "channels", SoundOgg_im_channels, 0); | |
rb_define_method(cSoundOgg, "read", SoundOgg_im_read, 1); | |
// SoundOggオブジェクトを生成した時にinitializeの前に呼ばれるメモリ割り当て関数登録 | |
rb_define_alloc_func(cSoundOgg, SoundOgg_allocate); | |
} | |
// int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); // シーク |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment