Extension Ruby-skriptikieleen

cyndis 10.11.04 14:35

Yksinkertainen 3 FMODin funktiota Rubylle wrappaava extension-esimerkki.

 Tekstiversio  Arvo: 4 (6 ääntä)  Äänestä: +  -
// Extension-kirjaston tekeminen Ruby-skriptikielelle
// Tässä esimerkissä wrapataan FMOD-C-kirjaston
// funktiot FSOUND_Stream_Close, FSOUND_Stream_Open ja
// FSOUND_Stream_Play. Huomaa, että näitä funktioita
// voidaan käyttää ainoastaan stream-äänien kanssa,
// kuten wav, mp3 ja ogg.

// Koodin pitäisi toimia kaikilla alustoilla, joilla Ruby ja FMOD toimivat.

// Linkit:
// Ruby : http://www.ruby-lang.org/
// FMOD : http://www.fmod.org/
// FMOD-referenssi : http://www.fmod.org/docs/
// Pickaxe Bookin extension-osa : http://www.rubycentral.com/book/ext_ruby.html

#include <ruby.h>
#include <fmod.h>
#include <fmod_errors.h>

VALUE fmod_moduuli;
VALUE fmod_luokka;
VALUE fmod_virhe;

static VALUE rubyfmod_lopeta(VALUE self) {
        FSOUND_Close();
        return Qnil; // Älä palauta mitään.
}

// FMOD::Musiikki-luokan destruktori.
static void rubyfmod_vapauta_musiikki(void *ptr) {
        FSOUND_Stream_Close(ptr);
}

// FMOD::Musiikki-luokan konstruktori.
static VALUE rubyfmod_uusi(VALUE self,VALUE tiedostonimi) {
        // Tarkista että parametri on String-luokan olio.
        // Aiheuttaa exceptionin, mikäli ei ole.
        Check_Type(tiedostonimi,T_STRING);

        FSOUND_STREAM *stream;
       
        // Makro STR2CSTR() muuttaa String-olion C-merkkitaulukoksi.
        stream = FSOUND_Stream_Open(STR2CSTR(tiedostonimi),0,0,0);
       
        if (stream == NULL) { // lataus epäonnistui
                // Nosta virhe
                rb_raise(fmod_virhe,FMOD_ErrorString(FSOUND_GetError()));
                return Qfalse;
        } else {
                // rb_iv_set asettaa tässä self-luokan (FMOD::Musiikki)
                // instanssimuuttujan @stream arvoksi wrapatun FSOUND_STREAMin.
                // Data_Wrap_Structia pitää käyttää, koska rubyn muuttujiin
                // ei voi asettaa C-tietotyyppejä.
                // Data_Wrap_Struct ottaa seuraavat parametrit:
                // - class (tässä arvo self) on luokka, joka palautetaan,
                //   kun ruby-koodi kysyy @streamin tyyppiä.
                // - mark (tässä arvo 0) on garbage collectorin mark-käsittelijän
                //   C-funktion nimi. Tässä 0 koska sitä ei käytetä.
                // - free (tässä arvo rubyfmod_vapauta_musiikki) on garbage collectorin
                //   free-käsittelijän C-funktion nimi, jota kutsutaan
                //   kun objekti poistetaan.
                // - ptr (tässä arvo stream) on se C-muuttuja, joka halutaan wrapata.
                rb_iv_set(self,"@stream",Data_Wrap_Struct(
                        fmod_luokka,0,rubyfmod_vapauta_musiikki,stream)
                );
                return Qtrue;
        }
}

static VALUE rubyfmod_soita(VALUE self) {
        FSOUND_STREAM *stream;
        // Otetaan C-muuttuja Data_Wrap_Structatusta instanssimuuttujasta.
        // Data_Get_Structin parametrit ovat:
        // - Ruby-arvo josta muuttuja otetaan (rb_iv_get palauttaa rb_iv_setillä
        //   asetetun muuttujan)
        // - Palautettava C-tietotyyppi, tässä FSOUND_STREAM ja
        // - Pointteri muuttujaan johon muuttuja asetetaan.
        Data_Get_Struct(rb_iv_get(self,"@stream"),FSOUND_STREAM,stream);

        // Soita vapailla kanavilla (FSOUND_Free)
        if (FSOUND_Stream_Play(FSOUND_FREE,stream) > 0) {
                // OK
                return Qtrue;
        } else {
                return Qfalse;
        }
}

// Ruby-extensioneissa on aina funktio nimeltä
// Init_<extension-nimi>, jota kutsutaan extensionia
// ladattaessa.
// Extensionin tiedostonimen täytyy myös olla <extension-nimi>.{so/dll}
void Init_Musiikki() {
        FSOUND_Init(44100,32,0);
       
        fmod_moduuli = rb_define_module("FMOD");
       
        // Tee luokka "Musiikki" fmod_luokka-muuttujaan ja
        // periytä luokka rubyn luokasta Object.
        // Luokka tehdään äsken määritellyn FMOD-moduulin
        // alle, joten ruby-koodissa se on FMOD::Musiikki.
        fmod_luokka = rb_define_class_under(
                fmod_moduuli,"Musiikki",rb_cObject
        );
       
        fmod_virhe = rb_define_class_under(fmod_moduuli,"Virhe",rb_eStandardError);

        // Funktiomääritykset, parametrit ovat:
        // Luokka johon funktio lisätään,
        // Funktion nimi (initialize on rubysta käytettynä nimellä new),
        // C-funktio jota kutsutaan ja
        // montako parametria metodi ottaa.
        rb_define_method(fmod_luokka,"initialize",rubyfmod_uusi,1);
        rb_define_method(fmod_luokka,"Soita",rubyfmod_soita,0);
       
        // Wrappaa lopetusfunktio rubyn puolelle.
        rb_define_singleton_method(fmod_moduuli,"lopeta",rubyfmod_lopeta,0);
       
        rb_eval_string("Kernel::at_exit {FMOD.lopeta}");
}
 

empty 19:44 11.11.04 
En testannut, tosin näyttäis ihan pätevältä esimerkiltä miten tehdään Rubyyn extensioneita.