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.
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.
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.
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.
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.
Bu, dosyada bazı bilgileri kapar. OpenAL format enumeratorünü Ogg'de kaç kanal olduğu temelinde çıkarsarız.
ö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.
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 .
Biz, dosyada ekstra bilgileri görmek için bunu kullanabiliriz.
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.
Bu, source'nin durumunu kontrol etme işini basitleştirir.
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.
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.
Bu metot, source'de bekleyecek herhangi bir buffer'I kuyruğa alacak.
Bu, bizim hata kontrollerimiz için bazı yazımlardan kurtarır.
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.
Bu, hiç kafa patlatıcı değil.
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.
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.
Fırlatılmış(throw) bir hata stringini yakalayıp (catch) programın neden sonlanmak zorunda olduğu hakkında bazı bilgiler gösteriliyor.
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