14/07/2003
23/05/2010
Yeniden merhaba
programcı arkadaşlar. OpenAL api'e oldukça uzun bir aradan sonra başka bir
dersle döndüm. Ve epey
iri olacağını düşünüyorum
bunun. İlkin, dizide buraya kadar
desteği olanlara
teşekkür
etmek isterim. Diziyi web sitelerinde
ağırlayan
DevMaster.net 'e çok özel
teşekkürlerimi bildirmek istiyorum. Tüm seriyi Visual C++ 6’e portu için TheCell’e,
ve Java JOAL için Athomas Goldberg’e (Java
Bindings for OpenAL). Yine onların
Brezilyaca GameDev 'i Portekizceye çevirdiğinide
duydum.
Yine, bir daha
yazmam için kafi gazı sağlayan bazı örnek
kodlar yollayan Jorge Bernal'a özel
teşekkürlerimi
sunmak
isterim. Bu büyük bir
yardımdı
(İspanyolcadan kodu çevirmek bir
angarya olsa bile :).
OggVorbis’e Giriş
Hiç Ogg'i duydunuz
mu?
Tuhaf ismi dışında daha fazlasıdır. Ses sıkıştırması için
mp3'den bu yana
olan en
önemlisidir (yine genelde müzik için
kullanılır). Umarım bir gün
ses
sıkıştırması için ana standart
olarak mp3'in yerine
geçecek. O, mp3'e kıyasla daha
iyi midir? Cevaplaması biraz zor olan bir
sorudur. Bu, bazı topluluklarda
epey şiddetli bir
tartışmadır. Ses kalitesine karşı okuma için bazen
gerçekten
elverişsiz olabilen sıkıştırma oranının
hakkında çeşitli tartışmalar vardır. Ben şahsen, "Daha
iyisinin" olduğu düşüncesinde değilim. her durumda karşı
görüşlerin tartışmalı olduğunu
düşünüyorum
ve
Bahsetmeye değer
değil.
Ama benim
için Ogg'in telif hakkının
beleş olması (mp3 değil)
gerçeği, tartışmayı
kazandırır. Mp3 lisans ücreti,
hiçbir şekilde ensesi
kalın
geliştiriciler için fahiş değildir, Ama boş zamanında bir
projede minimal kaynaklarla çalışanlar
için,
Bu
parayı coşmak
bir
seçenek değildir. Ogg’nin ücreti hayır
duasıdır.
OggVorbis Streamleme APIlerinizin
tasarlanması
Fazla
uzatmadan
biraz koda bakalım.
#include <string>;
#include <iostream>;
using namespace std;#include <al.h> ;
#include <ogg/ogg.h>;
#include <vorbis/codec.h>;
#include <vorbis/vorbisenc.h>;
#include <vorbis/vorbisfile.h>;#define BUFFER_SIZE (4096 * 8)
#include <iostream>;
using namespace std;#include <
#include <ogg/ogg.h>;
#include <vorbis/codec.h>;
#include <vorbis/vorbisenc.h>;
#include <vorbis/vorbisfile.h>;#define BUFFER_SIZE (4096 * 8)
Bu ders pür C++ kodlarla
yazılı
bu yüzden ilkin C++ 'ın bazı standart header'larını include ederiz. tabii OpenAL include
edilecek (her zaman ki
gibi), Ve
aynı şekilde, 4 yeni header'ı include edeceğiz. Bu yeni header'lar,
OggVorbis'in geliştiricileri tarafından yazılan
kütüphaneler takımı içindir. Toplamda bu 4
tane: 'ogg.dll' (format ve dekoder), 'vorbis.dll' (kodlama düzeni),
'vorbisenc.dll' ( encoding araçları), ve 'vorbisfile.dll' (streamleme ve seeking araçları).
vorbisenci
kullanmayacağız Ama
kullanabilir
dosyalardan biri olduğunu
göstermek için onu ekledim. Bu kütüphaneleri
kullanmak, işin 99% zorluğunu
alacak
(hemen hemen tümü). Gerçekten, onları
kullanmamak için hiçbir sebep yoktur. her şeyi biz yapsaydık
tekerleği tekrar icat ediyor olurduk, Ve ben, biz koderlerin asıl geliştiricilere
kıyasla daha iyisini yazabileceğinden şüpheliyim. Diğer bir artı: Bizim tarafımıza
herhangi bir ekstra iş
düşmeden
format evrimi mesela bu kütüphanelerce güncelleştirilecek. Ama bu kütüphaneleri
kullanmanın en büyük sebebi tutarlılıktır . Eğer bütün Ogg dosyaları dekoder ve encodinglemede bu kütüphaneleri
kullansa O zaman bütün Ogg'ler bütün
Ogg çalıcılarında çalınabilir. bu standart kütüphane setini kullandığımız
taktirde o zaman varolan tüm Ogg dosyalarını
destekleyebileceğimizden
emin
olabiliriz.
Yine
her
güncellemede streamden okumayı istediğimiz büyüklüğü
tanımlayan 'BUFFER_SIZE' makrosunu
yaratırız. çoğunlukla
güncelleştirilmediğinden dolayı daha kaliteli sesi üreten Ve genellikle
herhangi beklenmedik duraksama veya ses
bozukluklarını önleyecek buffer büyüklüğünü
keşfedersin (Birazcık
denemeyle). Tabii buffer'ının ne
kadar
büyükse aynı oranda daha çok
bellek tüketimi
demektir. lüzumsuz Bir stream
yapmak.
4096'nın sahip
olunmalı
minimum buffer boyutu olduğuna
inanıyorum. daha küçüğünü kullanmayı tavsiye
etmem.
Denedim, ve çok cızırtıya sebebiyet
verdi.
Böyle niye streamlemeyle canımızı
sıkıyoruz ki? Niye bir tampona tüm
dosyayı yükleyipte sonrada çalmayalım? Ee, bu
güzel bir
soru.
Hızlı cevap, gerçekte çok
fazla ses verisine sahip olunur
bir oyunda. Asıl Ogg dosya boyutu oldukça küçükse bile (Genellikle 1-3 MB civarında) Sen, bu ses
verisinin
sıkıştırıldığını aklından
çıkarmamalısın. verinin sıkıştırılmış
biçimini çalamazsın
. bir bufferda
kullanılabilmeden önce O encode edilmeli ve OpenAL'in tanıdığı forma döndürülmeli. İşte bu bizim
dosyayı streamleme nedenimizdir.
class ogg_stream
{
public:
void open(string path); // dosyaya handle elde et
void release(); // dosya handleyini serbest bırak
void display(); // Ogg’de bazı bilgileri göster
bool playback(); // Ogg streami çal
bool playing(); // source’nin çalındığını kontrol et
bool update(); // gerekliyse stream’i güncelle
protected:
bool stream(ALuint buffer); // buffer ‘a yeniden yükle
void empty(); // kuyruğu boşalt
void check(); // OpenAL hata durumunu kontrol et
string errorString(int code); // hata kodunu stringe çevir
{
public:
void open(string path); // dosyaya handle elde et
void release(); // dosya handleyini serbest bırak
void display(); // Ogg’de bazı bilgileri göster
bool playback(); // Ogg streami çal
bool playing(); // source’nin çalındığını kontrol et
bool update(); // gerekliyse stream’i güncelle
protected:
bool stream(ALuint buffer); // buffer ‘a yeniden yükle
void empty(); // kuyruğu boşalt
void check(); // OpenAL hata durumunu kontrol et
string errorString(int code); // hata kodunu stringe çevir
Bu, Ogg streamleme apimizin
temeli olacak. public metotlar Ogg'i çalacak birinin
aslında
sahip olmaya ihtiyaç duyduğu her şeydir. Protected metotlar, daha
içsel prosedürlerdir (Hata kontrolu gibi). tüm
fonksiyonlara
girmeyeceğim. Açıklamalarımın
size
onların neyin nesi
olduğu gibi bazı fikirler verdiğine
inanıyorum.
private:
FILE* oggFile; // dosya handle
OggVorbis_File oggStream; // stream handle
vorbis_info* vorbisInfo; // bazı formatlı data
vorbis_comment* vorbisComment; // kullanıcı açıklamaları
ALuint buffers[2]; // ön saf ve arka saf bufferları
ALuint source; // ses source
ALenum format; // internal format
};
FILE* oggFile; // dosya handle
OggVorbis_File oggStream; // stream handle
vorbis_info* vorbisInfo; // bazı formatlı data
vorbis_comment* vorbisComment; // kullanıcı açıklamaları
ALuint buffers[2]; // ön saf ve arka saf bufferları
ALuint source; // ses source
ALenum format; // internal format
};
Dikkat çekmek
istediğim ilk şey, wav dosyalar için streamlemede her zaman
kullandığımız 1'den daha ziyade 2 buffer'ı
adamış olmamızdır. Bu,
önemlidir. Bunu anlamak için,
Ben, OpenGL / DirectX'de çift
tamponlama nasıl çalıştığı hakkında düşünmenizi istiyorum. herhangi görevde geride bir arka
saf buffer çizime
hazırlanırken "Ekranda" olan ise
ön saf buffer vardır. Onlar sonra, değiş
tokuş edilir. Arka
saf buffer, ön
saf
olur ve
terside doğru. tamamen aynı ilke, burada
uygulanır. Çalınan bir buferr ile
çalınmak için bekleyen bir başkası
vardır . buffer çalması bittiğinde sonraki başlar. Sonraki buffer
çalınırken, İlk olan streamden yeni
bir veri parçasıyla tekrar doldurulup çalma bittiğinde
ilk çalıncak
diye
set
edilir.
Yine
de
karışıkmı? daha çok ayrıntıda bunu
sonra açıklayacağım.
'FILE*' e bakıp
C++
kullanırken neden C’e özgül dosya handleyini neden
kullandığımızı düşündüğünü biliyorum. Ee, vorbisfile C++'den ziyade C'nin etrafında tasarlandı Bundan dolayı onların C dosya sistemini
kullanması
normaldir. bu mümkündür, Ve gerçekten oldukça
kolaylık ,
fstream ile
çalışmanın vorbisfile'de
sağlanması. Bu kolaylık olmasına
rağmen
onu
kullanmak o kadar basit
değildir.
void ogg_stream::open(string path)
{
int result;
if(!(oggFile = fopen(path.c_str(), "rb")))
throw string("Could not open Ogg file.");
if((result = ov_open(oggFile, &oggStream, NULL, 0)) < 0)
{
fclose(oggFile);
throw string("Could not open Ogg stream. ") + errorString(result);
}
{
int result;
if(!(oggFile = fopen(path.c_str(), "rb")))
throw string("Could not open Ogg file.");
if((result = ov_open(oggFile, &oggStream, NULL, 0)) < 0)
{
fclose(oggFile);
throw string("Could not open Ogg stream. ") + errorString(result);
}
Demek istediğimi
anladın? Eğer biz, fstreamı kullanmış olsaydık 'ov_open_callbacks' içinde
birkaç
yeni fonksiyon yaratmak zorunda olurduk
. Bu, sizin için daha
faydalı olabilir
Eğer sanal dosya
sistemi desteğine
gereksinirseniz. 'ov_open' Fonksiyonu Ogg stream ile dosya handleyini bağlar. Şimdi bu dosya handleyi stream'in kendinindir ki Kendiniz onla
oynaşmazsınız.
vorbisInfo = ov_info(&oggStream, -1);
vorbisComment = ov_comment(&oggStream, -1);
if(vorbisInfo->channels == 1)
format = AL_FORMAT_MONO16;
else
format = AL_FORMAT_STEREO16;
vorbisComment = ov_comment(&oggStream, -1);
if(vorbisInfo->channels == 1)
format = AL_FORMAT_MONO16;
else
format = AL_FORMAT_STEREO16;
Bu, dosyada bazı
bilgileri kapar. OpenAL format enumeratorünü
Ogg'de kaç kanal olduğu
temelinde
çıkarsarız.
alGenBuffers(2, buffers);
check();
alGenSources(1, &source);
check();
alSource3f(source, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(source, AL_VELOCITY, 0.0, 0.0, 0.0);
alSource3f(source, AL_DIRECTION, 0.0, 0.0, 0.0);
alSourcef (source, AL_ROLLOFF_FACTOR, 0.0 );
alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE );
}
check();
alGenSources(1, &source);
check();
alSource3f(source, AL_POSITION, 0.0, 0.0, 0.0);
alSource3f(source, AL_VELOCITY, 0.0, 0.0, 0.0);
alSource3f(source, AL_DIRECTION, 0.0, 0.0, 0.0);
alSourcef (source, AL_ROLLOFF_FACTOR, 0.0 );
alSourcei (source, AL_SOURCE_RELATIVE, AL_TRUE );
}
önceden çoğunu gördün. ön tanımlı değerlerin
bir demetini set
ederiz,
pozisyon, hız, yön... Ama rolloff faktörü nedir? Bu, azaltmayla
ilgilidir. sonraki bir makalede azaltımı
anlatacağım Bundan dolayı ben,
fazla derine girmeyeceğim, Ama ben, temelde onu
açıklayacağım
. Rolloff faktörü, mesafe
sebebiyle kuvvet zayıflamasını yönetir . onu 0'a set
etmeyle
onu kapatmış oluruz. Listener’ın çok uzakta olmasına rağmen Ogg kaynağı yine de duyalacak
anlamındadır. Aynı mantık source'e uygulanır.
void ogg_stream::release()
{
alSourceStop(source);
empty();
alDeleteSources(1, &source);
check();
alDeleteBuffers(1, buffers);
check();
ov_clear(&oggStream);
}
{
alSourceStop(source);
empty();
alDeleteSources(1, &source);
check();
alDeleteBuffers(1, buffers);
check();
ov_clear(&oggStream);
}
Kullanım sonu
temizlik
bu.
source'İ
durdururuz, hala Kuyrukta olan herhangi
buffer varsa
boşaltırız, Ve nesnelerimiz yok
edilir. 'ov_clear' , dosya streamlemede
tutulan handleyi serbest bırakıp ayrıca bizim için
handlleyi kapatacak .
void ogg_stream::display()
{
cout
<< "version " << vorbisInfo->version << "\n"
<< "channels " << vorbisInfo->channels << "\n"
<< "rate (hz) " << vorbisInfo->rate << "\n"
<< "bitrate upper " << vorbisInfo->bitrate_upper << "\n"
<< "bitrate nominal " << vorbisInfo->bitrate_nominal << "\n"
<< "bitrate lower " << vorbisInfo->bitrate_lower << "\n"
<< "bitrate window " << vorbisInfo->bitrate_window << "\n"
<< "\n"
<< "vendor " << vorbisComment->vendor << "\n";
for(int i = 0; i < vorbisComment->comments; i++)
cout << " " << vorbisComment->user_comments[i] << "\n";
cout << endl;
}
{
cout
<< "version " << vorbisInfo->version << "\n"
<< "channels " << vorbisInfo->channels << "\n"
<< "rate (hz) " << vorbisInfo->rate << "\n"
<< "bitrate upper " << vorbisInfo->bitrate_upper << "\n"
<< "bitrate nominal " << vorbisInfo->bitrate_nominal << "\n"
<< "bitrate lower " << vorbisInfo->bitrate_lower << "\n"
<< "bitrate window " << vorbisInfo->bitrate_window << "\n"
<< "\n"
<< "vendor " << vorbisComment->vendor << "\n";
for(int i = 0; i < vorbisComment->comments; i++)
cout << " " << vorbisComment->user_comments[i] << "\n";
cout << endl;
}
Biz, dosyada ekstra
bilgileri görmek için bunu kullanabiliriz.
bool ogg_stream::playback()
{
if(playing())
return true;
if(!stream(buffers[0]))
return false;
if(!stream(buffers[1]))
return false;
alSourceQueueBuffers(source, 2, buffers);
alSourcePlay(source);
return true;
}
{
if(playing())
return true;
if(!stream(buffers[0]))
return false;
if(!stream(buffers[1]))
return false;
alSourceQueueBuffers(source, 2, buffers);
alSourcePlay(source);
return true;
}
Bu, Ogg'i çalmaya başlatacak. Eğer Ogg hali hazırda çalıyorsa O zaman, tekrar bunu
yapmak için hiçbir sebep yoktur. Biz aynı şekilde,
onların ilk veri setiyle bufferları ilklemeliyiz
. Biz sonra, onlar
kuyrukta bekleriz ve onları çalmak için source söyleriz
. Bu, bizim, 'alSourceQueueBuffers''ı kullandığımız ilk
seferdir. Temel olarak
yaptığı, source çoklu bufferlaşır. Bu bufferlar,
sırayla çalınacak . source kuyruğuyla beraber
bunu daha sonra
açıklayacağım. not geçeceğimiz bir
şey:
streamleme için bir source
kullanırsanız 'alSourcei' kullanarak onu bir buffera asla bağlama, Her zaman 'alSourceQueueBuffers' daim kullan.
bool ogg_stream::playing()
{
ALenum state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
{
ALenum state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
Bu, source'nin
durumunu kontrol etme işini basitleştirir.
bool ogg_stream::update()
{
int processed;
bool active = true;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
while(processed--)
{
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
check();
active = stream(buffer);
alSourceQueueBuffers(source, 1, &buffer);
check();
}
return active;
}
{
int processed;
bool active = true;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
while(processed--)
{
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
check();
active = stream(buffer);
alSourceQueueBuffers(source, 1, &buffer);
check();
}
return active;
}
Kuyruğun, kısaca nasıl
çalıştığı buradadır: bufferların bir
'Listesi' vardır. Bir buffer'ı
kuyruktan aldığın
zaman o
ölür.
bir buffer'ı
kuyruğa
aldığın
zaman o arkaya sıraya konur. Olay bu.
oldukça basit?
Bu, sınıfta önemli
olan 2
metotdan ilkiydi. bu kod parçasında
yaptığımız kontrol Herhangi bir
buffer'ın hali
hazırda
çalınıp
çalınmadığıdır. Öyleyse o zaman
kuyruğun
arkasına onların her birini almaya
başlarız, Biz, stream'den
veriyle bufferları tekrar doldururuz, Ve biz sonra, kuyruğa
onları koymaya zorlarız ki onlar,
çalınabilir. İnşallah
Listener bizim bunu
yaptığımız
hakkında
hiçbir fikre sahip olmayacak. o uzun devamlı müzik
zinciri gibi çalmalı. Eğer stream çalma
biterse 'stream' fonksiyonu aynı şekilde bize
söyler.
Bu flag
fonksiyonun döndüğü zamanı bildirir.
bool ogg_stream::stream(ALuint buffer)
{
char data[BUFFER_SIZE];
int size = 0;
int section;
int result;
while(size < BUFFER_SIZE)
{
result = ov_read(&oggStream, data + size, BUFFER_SIZE - size, 0, 2, 1, & section);
if(result > 0)
size += result;
else
if(result < 0)
throw oggString(result);
else
break;
}
if(size == 0)
return false;
alBufferData(buffer, format, data, size, vorbisInfo->rate);
check();
return false;
}
{
char data[BUFFER_SIZE];
int size = 0;
int section;
int result;
while(size < BUFFER_SIZE)
{
result = ov_read(&oggStream, data + size, BUFFER_SIZE - size, 0, 2, 1, & section);
if(result > 0)
size += result;
else
if(result < 0)
throw oggString(result);
else
break;
}
if(size == 0)
return false;
alBufferData(buffer, format, data, size, vorbisInfo->rate);
check();
return false;
}
Buda sınıfın öteki
önemli metodudur. Bu kısım Ogg bitstreamdan veriyle bufferları
doldurur. Onun biraz
kavraması daha zordur
çünkü tepeden
tırnağa davranışı açıklanabilir değildir. 'ov_read', yaptığını
düşündüğünüz şeyi
yapar; O,
Ogg bitstreamdan veriyi
okur
. Vorbisfile, tüm bitstream decodelemesini yapar, Bundan dolayı biz,
ondan endişe duymak zorunda olmayız. Bu fonksiyon, bizim
'oggStream' yapımızı, decodelenmiş sesin yazılabildiği yer olarak bir data buffer, ve decodelemeyi istediğininz yığının
boyutunu argüman olarak alır. En son 4 argüman senin gerçekten hakkında endişelenmek zorunda
olmadığındır Ama ben, yine de
açıklayacağım. Göreli
olarak:
ilki little endian (0)
(önemli bitin en solda olduğu düzen) veya big endian (1) (önemli bitin en sağda olduğu düzen)’ı belirtir,
ikincisi 8 bit (1) veya 16 bit (2) olarak data boyutu
(bayt cinsli) belirtir, üçüncüsü datanın işaretsiz (0) veya işaretliliğini (1)
belirtir, ve sonuncusu mevcut bitstream sayısını
düzenler.
'ov_read' 'nin dönüş değeri
bazı şeyleri gösterir. Eğer sonuç değeri
pozitifse o zaman, ne kadar verinin okunduğunu ifade eder. Bu, önemlidir
çünkü
'ov_read' talep edilen bütün boyutu okumayabilir (misal dosyanın
sonundadır ve Okumak için
kalan hiçbir şey
yoktur). her halükârda 'ov_read' sonucu karşı 'BUFFER_SIZE' i kullan. Eğer 'ov_read' 'ın sonucu negatif olursa o zaman bitstreamda bir hata olduğunu gösterir. Sonuc değeri bu
durumda bir hata
kodudur. Eğer sonuç sıfıra eşit
olursa o zaman
Çalmak
için dosyada bırakılan hiçbir şey yoktur.
bu kodu karışıklaştıran
döngüleme
esnasıdır. Bu metot, modüler ve
değiştirilebilir olmak için tasarlandı. istediğinize 'BUFFER_SIZE'i değiştirebilirsin
ve
O hala,
çalışacak. Ama 'ov_read' 'da çoklu çağrılarla bufferın tam boyutunu doldurulup Her şeyin uygun
şekilde düzenlendiğinden emin olunmalı. Bu metodun son parçası
Ogg'nin
kullandığı 'ov_read' 'den streamlediğimiz veriyle buffer id
dolduran 'alBufferData' çağrısıdır. daha önce
gösterdiğimiz
'format' ve 'VorbisInfo' datalarını
kullanırız.
void ogg_stream::empty()
{
int queued;
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
while(queued--)
{
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
check();
}
}
{
int queued;
alGetSourcei(source, AL_BUFFERS_QUEUED, &queued);
while(queued--)
{
ALuint buffer;
alSourceUnqueueBuffers(source, 1, &buffer);
check();
}
}
Bu metot, source'de
bekleyecek herhangi bir buffer'I
kuyruğa
alacak.
void ogg_stream::check()
{
int error = alGetError();
if(error != AL_NO_ERROR)
throw string("OpenAL error was raised.");
}
{
int error = alGetError();
if(error != AL_NO_ERROR)
throw string("OpenAL error was raised.");
}
Bu, bizim hata
kontrollerimiz için bazı
yazımlardan kurtarır.
string ogg_stream::errorString(int code)
{
switch(code)
{
case OV_EREAD:
return string("Read from media.");
case OV_ENOTVORBIS:
return string("Not Vorbis data.");
case OV_EVERSION:
return string("Vorbis version mismatch.");
case OV_EBADHEADER:
return string("Invalid Vorbis header.");
case OV_EFAULT:
return string("Internal logic fault (bug or heap/stack corruption.");
default:
return string("Unknown Ogg error.");
}
}
{
switch(code)
{
case OV_EREAD:
return string("Read from media.");
case OV_ENOTVORBIS:
return string("Not Vorbis data.");
case OV_EVERSION:
return string("Vorbis version mismatch.");
case OV_EBADHEADER:
return string("Invalid Vorbis header.");
case OV_EFAULT:
return string("Internal logic fault (bug or heap/stack corruption.");
default:
return string("Unknown Ogg error.");
}
}
Bu, hata
mesajının stringleştirilmesini yapar böylece
anlamlı şekilde
konsoldan veya mesaj kutusundan veya herneydense okunabilir.
Kendi
OggVorbis Player’ınızı yapma
Eğer şimdiye kadar
benleysen o zaman işe
yarar şekilde
bunu
anlamada hayli ciddi
olmalısın. Endişelenme
! neredeyse işi bitiriyoruz. şimdi yapmamız
gerekli
tek şey,
bir Ogg dosyasını çalmak için kullanılacak
yeni
bir
sınıf tasarlamaktır. O, şuana kadara göre
basit bir işlem. en zor bölümü yaptık. bir oyun döngüsünde bunu
kullanacağınızı farz
etmiyorum, Ama döngü tasarladığımda akılda onu
tutacağım.
int main(int argc, char* argv[])
{
ogg_stream ogg;
alutInit(&argc, argv);
{
ogg_stream ogg;
alutInit(&argc, argv);
Bu, hiç kafa patlatıcı
değil.
try
{
if(argc < 2)
throw string("oggplayer *.ogg");
ogg.open(argv[1]);
ogg.display();
{
if(argc < 2)
throw string("oggplayer *.ogg");
ogg.open(argv[1]);
ogg.display();
C++ kullanıyor
olduğumuzdan dolayı aynı şekilde istisna yönetimi için try/catch/throw anahtar kelimelerini
kullanacağız. Muhtemelen bu makalede
kodun başından sonuna stringler fırlattırdığımı fark
ettiniz.
Benim, burada yaptığım
ilk şey, kullanıcının, diziniyle dosyanın bizi sağladığından emin olma
kontrolüdür. Eğer, programa hiçbir
argüman geçilmemiş
ise,
herhangi şey yapmayız, böyle
durumda
basitçe kullanıcıya Ogg uzantısı gösteren küçük bir
mesaj göstereceğiz. Çok bilgilendirici
değil Ama bir konsol
uygulamasında bu standart bir usluptur. Eğer programa bir
argüman geçilmiş olsaydı o zaman, o dosyayı
açmak için onu kullanabiliriz. Aynı
zamanda
Ogg dosyası bilgisini
göstereceğiz.
if(!ogg.playback())
throw string("Ogg refused to play.");
while(ogg.update())
{
if(!ogg.playing())
{
if(!ogg.playback())
throw string("Ogg abruptly stopped.");
else
cout << "Ogg stream was interrupted.\n";
}
}
cout << "Program normal termination.";
cin.get();
}
throw string("Ogg refused to play.");
while(ogg.update())
{
if(!ogg.playing())
{
if(!ogg.playback())
throw string("Ogg abruptly stopped.");
else
cout << "Ogg stream was interrupted.\n";
}
}
cout << "Program normal termination.";
cin.get();
}
her zaman programın ana döngüsünün yazılan en
eğlenceli kısım olduğu düşünürüm. Ogg'i çalarak
başlarız. Bir Ogg, eğer 2 bufferla streamlemede yeterli veri yoksa
Veya basitçe dosyayı okuyamazsa
çalmayı reddedebilir (Diğer bir deyişle
Ogg çokça küçüktür).
Program, 'update' metodu
true
döndürmeyi sürdürdüğü sürece
devamlı olarak döngülenir, Ve başarılı bir
şekilde, okuma
yapılıp
ses çalınabilidiği sürece true
döndürmeyi sürdürecek . döngü içinde, Ogg'nin çalıyor olduğundan emin olacağız. Bu, onun, 'update' gibi
aynı maksada hizmet ettiği düşünülebilir, Ama o hem de, sistemle
yapmak zorunda olan bazı diğer meseleleri çözecek. Ben basit bir test
için aynı anlı birçok diğer program
çalışırken bu programı
çalıştırdım. oggplayerin, nasıl tepki
gösterecek olduğunu görmek için yaptığım
bunda çok
cpu zamanı yenip
bitti.
Mesaj
göstermek
için
kesilmesi
şaşırtabilir. Streamleme ,
harici processlerce
kesilebilir. Bu bir
hataya neden olmaz
ama.
Akılda
bunu tut .
Eğer başka hiçbir şey
sonra olmazsa, program küçük bir mesajla sizi
bilgilendirerek normal olarak sonlanacak.
catch(string error)
{
cout << error;
cin.get();
}
{
cout << error;
cin.get();
}
Fırlatılmış(throw) bir hata stringini
yakalayıp (catch) programın neden
sonlanmak
zorunda
olduğu hakkında bazı
bilgiler gösteriliyor.
ogg.release();
alutExit();
return 0;
}
alutExit();
return 0;
}
main'imizin sonu, yine kafa
patlatıcı değil.
Sorabileceğiniz sorulara
cevaplar
streamleme
için bir tampondan
fazlasını kullanabilir
miyim?
Kısaca, evet.
aynı anda source'de kuyrukta beklenen
birçok buffer olabilir. Bunu yapmak da
aslında, size daha iyi sonuçlar verebilir. Daha önce dediğim gibi ,
Her daim kuyrukta 2 bufferla
olmalı
, cpu meşgul (veya sistem yanıt vermediğinde), stream diğerini decodelemeden önce
Kaynak
aslında çalmayı bitirebilir. kuyrukta 3 veya hatta 4 buffera sahip olmak,
güncelleştirmeyi kaçırma
olasılığıyla ilerde
birazcık daha fazla
alıkoyacak sizi.
ogg.update'i ne kadar sık
çağırmalıyım?
Bu, birkaç şeye bağlı
olarak değişecek. Eğer çabuk bir
cevap istiyorsan, söyleyeyim, her nezaman istersen günceleyebilirsin, ama bu gerçekten gerekli
değildir. Yeter ki
source kuyruğun sonunda
çalmayı bitirmeden önce güncellensin.
Bunu
etkileyecek olan en büyük faktörler, Kuyrukta tahsis edilmiş
buffer boyutu ve
bufferların sayısıdır. Eğer
çalmaya hazır çok veriye
sahipsen daha az güncelleştirme gerekli olacak.
Aynı anda bir Ogg'den fazlasını streamlemek güvenli
midir?
Bu açık
olmalı.
Herhangi
uç test yapmadım ama ben olamayacağını
düşünmüyorum. Siz genellikle zaten
birden çok streamlemeyi yapmak
zorunda
olmayacaksınız. Arka plan müziğini
çalmak için bir tane ve oyun içi ara sıra
olan karakter diyalogu için olacak, tabi ses efektlerinin çoğu streamlemenin can sıkmayacağı
kadar
kısadır. Source'lerinin çoğu, onlara
bağlanan sadece bir buffer'a sahip olur.
İsimler ne
alaka?
"Ogg", Xiph.org'un ses, video ve metadata için konteyner biçimi
ismidir. "Vorbis" , Ogg’deki konteynerlar için tasarlanmış özel bir ses
sıkıştırma düzeninin
adıdır.
Sözcüklerin özel
anlamlarına gelince ... Ee, anlatması daha bir
zor.
Terry Pratchett romanlarıyla
bazı garip bağlar içerdiğini düşünüyorum. Detaylandırılamayacak kadar küçük bir
sayfa burası.
Örnek programı
çalıştırdığımda konsolda
neden
'Oggplayer* .ogg' yazısı
gösteriliyor?
Bir dosya adı belirtmek
zorunluluğu var.
Kolayca
program üzerine bir Ogg dosyası sürükle bırak
yapabilirsiniz ki bu pek güzelde işe
yarar.
Bununla
birlikte örnekle gidecek bir Ogg dosyası
sağlamadım. Kısmen birinin
müziğini kullanmanın yasallığı kısmen
dosya
boyutunu azaltmak için. Eğer biri bir parça
bağışlamayı istiyorsa (tabi ben onu beğenecem :) ) örnek dosyalarına onu
ekleyebilirim. Ama lütfen telif haklı
materyalı
sunulmamış veya hip hop olmasın
:).
Visual C++ 6.0 kaynak ve proje dosyalarını - (TheCell tarafından port edilmiştir)
Bu dersin Delphi’e portunu yükle - (Sascha Willems, ve Peter Hine tarafından port edilmiştir)
Bu dersin Linux’e portunu yükle - (Lee Trager tarafından port edilmiştir)
Dersin Orjinali : http://www.devmaster.net/articles/openal-tutorials/lesson8.php