Autor Wątek: Strumieniowe odtwarzanie OGG - Jak uaktualniac?  (Przeczytany 6583 razy)

Offline Sir

  • Użytkownik

# Styczeń 10, 2006, 19:32:47
Witam,

uzywam OpenAL i OGG Vorbis. Napisalem sobie klase do obslugi odtwarzania plikow ogg, oczywiscie strumieniowo. Jak wiadomo przy takim odtwarzaniu trzeba uaktualniac bufory przechowujace ostatnio zaladowane kawalki pliku z muzyka. W tym celu stworzylem metode c_MusicSource::Update(); Trzeba ja wywolywac conajmniej co okolo 50ms (zalezy od wielkosci bufora, ale przy buforze 32kb tak jest odpowiednio). Problem polega na tym ze jesli bufor nie zostanie uaktualniony w odpowiednim czasie to odtwarzanie muzyki sie zacina i jest odtwarzana w kolko ostatnia zawartosc bufora.

Jak myslicie, lub jak macie zrobione w waszych projektach? Gdzie bylo by dobrze wywolywac Update()? Jesli wywoluje go po kazdym narysowaniu klatki to gdy przejde na pulpit i zaczne robic cos innego, muzyka momentalnie sie zacina. To samo sie dzieje jak chcem przesunac lub zmniejszyc okno. Wymyslilem ze mozna by to bylo dac do osobnego watku. W tym przypadku niby wszystko jest ok, nic sie nie przycina itd. tylko ze przy ustawieniu priorytetu watku na normalny strasznie zacina sie rysowanie sceny, za to jak ustawie priorytet na nizszy od normalnego to znowu muzyka sie zacina. W funkcji watku mam wpisane:

while( watek_nie_zostanie_przerwany )
Update();

Troche nieporzadane jest to wlasciwie nieskonczone while.

Wymyslilem tez ze mozna by to zrobic na jakis doskonalych timerach ktore sie wykonuja nawet jesli okno jest aktualnie przesuwane i nawet wtedy gdy w watku glownym dzieje sie cos zupelnie innego. Taki osobny watek wykonywany co te kilkadziesiat ms.

Jak to macie rozwiazane w swoich projektach, a jesli nie macie tego u siebie to jak myslicie ze mozna bylo by to rozwiazac? :).

Pozdrawiam Sir, ps. Troche szkoda tej bazy z starego forum :(.

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

nadult

  • Gość
# Styczeń 10, 2006, 19:41:09
Cytuj
while( watek_nie_zostanie_przerwany )
Update();
Może tak:
while( watek_nie_zostanie_przerwany  ) {
    Update();
    Sleep(25);
}

Offline yorp

  • Użytkownik
    • ProfessionGG Project

# Styczeń 10, 2006, 19:49:11
Witam!
Co do twojego problemu z bufforem, jesli nie zdarzysz pobrac nowego, to ustaw stary na pusty lub zapauzuj odtwarzanie do momentu, gdy ten zostanie pobrany.

Offline Sir

  • Użytkownik

# Styczeń 10, 2006, 19:58:11
Może tak:
while( watek_nie_zostanie_przerwany  ) {
    Update();
    Sleep(25);
}

Tylko ze Sleep(); chyba tez zabiera czas procesora? Moge sie mylic :).

Cytuj
Co do twojego problemu z bufforem, jesli nie zdarzysz pobrac nowego, to ustaw stary na pusty lub zapauzuj odtwarzanie do momentu, gdy ten zostanie pobrany.

To nie rozwiazuje problemu moze juz nie calkowitego zacinania sie muzyki, tylko przerywania.

Dodam jeszcze, ze sposob z while jest niedokonca idealny poniewaz gdy wywale wszystko z petli glownej i wstawie tam tylko Update() wykorzystywane jest 100% czasu procesora, a dlaczego winamp zuzywa niecaly 1 do 2-3 max?

nadult

  • Gość
# Styczeń 10, 2006, 20:05:05
Tylko ze Sleep(); chyba tez zabiera czas procesora? Moge sie mylic :).
Sleep usypia wątek więc wogóle nie zabiera czasu procesora (no może kilkadziesiąt cykli :)).

Offline yorp

  • Użytkownik
    • ProfessionGG Project

# Styczeń 10, 2006, 20:06:52
Wydaje mi sie, ze to zalezy od jakosci np. mp3 - znajac jej kbps mozesz spokojnie obliczyc co ile musisz robic Update, wiec podobnie pewnie jest z ogg...
//edit: poza tym zawsze mozesz wyznaczyc sobie jakis czas, co jaki bedziesz sprawdzal ile zostalo jeszcze bufora i np. jak zostalo mniej niz 10% to robisz, wtedy update
« Ostatnia zmiana: Styczeń 10, 2006, 20:09:41 wysłana przez Yunior^Pro »

Offline tommyz

  • Użytkownik

# Styczeń 10, 2006, 20:10:09
Zrob to na drugim watku - masz problem z glowy.
Ja mam oggi na drugim watku, ustawione dwa eventy - jeden na srodku a drugi na koncu, nastepnie jest petla ktora WaitujeForMultipleObjects :) i jesli choc jeden z eventow jest ustawiony to bada pozycje kursora i uaktualnia ta polowke bufora w ktorej nie ma kursora. Trzeba badac oba eventy gdyz jest na niektorych systema burak powodujacy odpalanie nie tych zdarzen co trzeba(np u mnie na trzech kompach, w sieci wiecej ludzi ma takie problemy).

Jesli tak zrobisz to masz super odtwarzarke oggow w miare niewrazliwa na zaklocenia.

Co do Sleepa - trzeba uwazac bo moze spac dluzej niz to wynika z podanej wartosci, warto czasami uzywac Sleep(0)

Offline Krzysiek K.

  • Redaktor
    • DevKK.net

# Styczeń 10, 2006, 20:18:38
Cytuj
W tym celu stworzylem metode c_MusicSource::Update(); Trzeba ja wywolywac conajmniej co okolo 50ms (zalezy od wielkosci bufora, ale przy buforze 32kb tak jest odpowiednio).
Wczytywanie muzyki po 32kB jakoś do mnie nie przemawia. Proponowałbym zwiększyć bufor, bo myślę, że czasem dysk może się nie wyrobić (bo np. coś innego czytało z dysku). Tak na zdrowy rozsądek wydaje mi się, że dobrze by było trzymać muzykę w pamięci na jakieś 5 sekund do przodu i w momencie, gdy zostaną do końca jakieś 2-3s próbować doczytać więcej (używając osobnego wątku, żeby przez przypadek nie zablokować renderowania). Po prostu odwoływanie się do dysku co 50ms wydaje mi się wysoce nieefektywne.

Cytuj
Jak myslicie, lub jak macie zrobione w waszych projektach? Gdzie bylo by dobrze wywolywac Update()?
Myślę, że to jest kwestia wybróbowania, a efekty zależne od samej konstrukcji silnika. Jeżeli na przykład silnik robi coś wypasionego, a przy okazji nie korzysta z occlusion query, to można wstawić Update() po D3DDevice::End(), a D3DDevice::Present() (lub po prostu przed swapem w OpenGL), bo najprawdopodobniej wtedy karta graficzna ma jeszcze sporo zakolejkowanej roboty i CPU się może trochę nudzić.

Offline Sir

  • Użytkownik

# Styczeń 10, 2006, 20:32:34
Cytuj
Zrob to na drugim watku - masz problem z glowy.

Moglbys pokazac fragmenty kodu odpowiedzialne za to co opisales z swojego projektu? Tylko czesci odpowiedzialne za obsluge watkow i eventow, albo dkladniej opisac jak to powinno byc skonstruowane.

Cytuj
Wczytywanie muzyki po 32kB jakoś do mnie nie przemawia.

Ustawialem tez wiecej i dzieje sie to samo, bez roznicy jak duzy jest bufor.

Offline tommyz

  • Użytkownik

# Styczeń 10, 2006, 20:57:56
tutaj masz pisane z glowy wiec moglem sie robnac

1. tworzysz watek, wczytujesz w nim ogga, setupujesz bufor
2. setupujesz eventy

LPDIRECTSOUNDNOTIFY8 lpDsNotify;
DSBPOSITIONNOTIFY PositionNotifies[2];

if(FAILED(buf->QueryInterface(IID_IDirectSoundNotify8, &lpDsNotify)))
{// niedobrze}

notifyMiddle = CreateEvent( NULL, false, false, NULL );
notifyEnd = CreateEvent( NULL, false, false, NULL );

PositionNotifies[0].dwOffset = bufSize/2; // w polowie bufora
PositionNotifies[0].hEventNotify = m_notifyMiddle;
PositionNotifies[1].dwOffset = bufSize; // koniec/poczatek bufora
PositionNotifies[1].hEventNotify = notifyEnd;
lpDsNotify->SetNotificationPositions(2, PositionNotifies);
lpDsNotify->Release();

3. W petli: jesli WaitForMultipleObkects( eventy ) zwroci ze ktores jest sygnalizowane to zczytujesz pozycje kursora:

buf->GetCurrentPosition( &plCur, &wrCur );

WFMObjects mozesz dac zeby czekalo na eventa albo nieczekalo - u mnie nie czeka bo jeszcze mam troche stuffu do zorbienia w tym watku. Jka ci nie przeszkadza ze bedzie 'zawisal' na ulamki sekund to mozesz dac czekanie.

4. jesli pozycja kursora > polowa dlugosci bufora wtedy wypelniasz pierwsza czesc bufora, a jesli < wtedy druga

mam nadzieje ze w miare jasno to opisalem

Offline Sir

  • Użytkownik

# Styczeń 11, 2006, 00:04:25
I wszystko jasne, doczytalem o zdarzeniach i juz prawie wiem jak to zrobic :). Jedynym problemem pozostaje to, ze w OpenAL nie ma (albo poprostu nie moge sie doszukac) czegos takiego co wywoluje dane zdarzenia np. w polowie odtwarzania bufora.

Nie wiem czy mialo by sens sprawdzanie w glownym watku, w sumie jak glowny watek "zapomni" sprawdzic czy jest juz polowa to efekt bedzie taki sam jak wywolywanie Update() w glownym watku.

Jakies sugestie?? :)

Offline tommyz

  • Użytkownik

# Styczeń 11, 2006, 00:22:10
Jesli w OpenAL nie ma to moze da sie jakos dobrac do DirectSounda pod spodem?

W glownym watku zdarzenia beda mialy podobny efekt do zwyklego update bo jak zapchcha sie cos to watek sie i tak zwiesi. Nie wiem specjlanie co poradzic jesli koneicznie chcesz to zrobic bez drugiego watku.

Offline Demon

  • Użytkownik

# Styczeń 11, 2006, 17:24:13
Ja to robiłem tak :

bool done = false;

DWORD WINAPI SoundThread(void *args)
{
SoundManager *sm = (SoundManager *)args;
DWORD start;
while (!done)
{
start = GetTickCount();
sm->UpdateAll();
Sleep(200 - (GetTickCount() - start));
};
done = false;
ExitThread(0);
};

...
...
int main(int argc, char *argv[])
{
SoundManager sm(GetConsoleWindow());
size_t identifier = sm.AddOgg("STR0571.ogg");
size_t identifier1 = sm.AddOgg("Sad But True.ogg");
size_t identifier2 = sm.AddOgg("Track 2.ogg");
HANDLE hThread;
CreateThread(NULL,0,SoundThread,&sm,0,reinterpret_cast<DWORD *>(&hThread));
SetThreadPriority(hThread,THREAD_PRIORITY_HIGHEST);
sm.Play(identifier, DSBCAPS_GLOBALFOCUS);
sm.Play(identifier1, DSBCAPS_GLOBALFOCUS);
sm.Play(identifier2, DSBCAPS_GLOBALFOCUS);
cout << "End ?" << endl;
system("PAUSE");
done = true;
while (done);

return 0;
}
« Ostatnia zmiana: Styczeń 11, 2006, 17:50:33 wysłana przez Demon »

Offline tommyz

  • Użytkownik

# Styczeń 11, 2006, 18:58:17
Tak tez mozna, jest prosciej. Dzieki eventom wiesz dokladnie kiedy masz updatowac i ile, nie musisz strzelac. Teroretycznie Sleep moze zaspac i wtedy bedzie przerwa albo zapetlenie a jak bedziesz wywolywal z mniejszymi interwalami to niepotrzebnie robisz nepotrzebne iteracje.

Offline Sir

  • Użytkownik

# Styczeń 11, 2006, 19:15:26
Chyba jednak bede musial skorzystac z bardziej zawodnej metody, nie mam pojecia jak dobrac sie do automatycznego wywolywania zdarzen przez OpenAL, no chyba ze czegos takiego nie obsluguje.