Beta-testing WinWave

0

Temat umieszczam w C++, bo nie bardzo wiem, do jakie działu to dać, ale biblioteka pod C++...

Jakiś czas temu zainspirowany artykułem o odtwarzaniu dźwięku niskopoziomowego, zainteresowałem się tematem, napisałem sobie prostą klasę do odtwarzania dźwięku buforowanego(koncepcję używania buforów wyjaśnił mi 0x666 za co mu dziękuję, bo ja jej trochę źle używałem). Przez miesiące coś tam się co jakiś czas coś dopisywało, powstała nie taka całkiem skromna klasa.

Jakby ktoś miał czas i chęci, byłbym wdzięczny o przetestowanie wynalazku, dokumentacja jest, gdyby coś się działo niezgodnego z dokumentacją, proszę o powiadomienie mnie...
Ewentualne sugestie także mile widziane(szczególnie na temat streamingu, czy to będzie wygodny sposób dostarczania buforów do urządzenia, jakieś lepsze pomysły niż moje)...

Kolejne rozszerzenia: oczywiście to nie koniec prac nad tym projektem, a mam nadzieję że dopiero początek :> teraz planuję wyposażyć bibliotekę w konwersję ilości kanałów, następnie w programowy poziom natężenia dźwięku(modyfikowanie wartości próbek o podany współczynnik), czy dalej nawet mam w planach generowanie dźwięku w przestrzeni 3D, bo to przecież nic innego jak zmiana natężenia dźwięku na poszczególnych kanałach), chociaż nie mam koncepcji na teki efekty jak ściany(wyciszanie dźwięku gdy pomiędzy źródłem dźwięku a kamerą/postacią jest ściana), bo wprowadzanie każdej ściany do przestrzeni 3D w bibliotece nie jest dobrym pomysłem), ale to już takie przyszłościowe problemy :D

Gdyby ktoś był zaznajomiony z wyżej wspomnianymi rzeczami, czy miał jakieś ciekawe materiały na powyższy temat z góry dziękuje...

EDIT: no nie zapomniałem o najważniejszym :D link do biblioteki:
http://lublin.webd.pl/crayze/winwave32.exe
można sprawdzić programem antywirusowym, już parę miesięcy nie używam antywira, możliwe że jakieś złośliwe kody się kopiuja pomiędzy aplikacjami :>

0

Dobrze by było jakbyś dołączył do projektu wymagane DLL'ki (CRT), ewentualnie zlinkował wszystko ze statycznymi runtime'ami.

0
0x666 napisał(a)

Dobrze by było jakbyś dołączył do projektu wymagane DLL'ki (CRT), ewentualnie zlinkował wszystko ze statycznymi runtime'ami.

wolę wszystko statycznie związać, w temacie nie jestem bo VC++ używam dopiero od kilku dni :> konfiguruje się to w: C/C++->Code generation->Runtime Library i na opcja z /MT ?? z tego co zauważyłem
o ile dobrze rozumiem wystarczy tylko to zmienić? (rzeczywiście DLLka z 30 do 80 KB urosła :>)

można pobrać już poprawioną wersję

0

A EXEki? :>

0
0x666 napisał(a)

A EXEki? :>

a tam nie chce mi się jeszcze raz ich od nowa kompilować, ich kody źródłowe ort!, można sobie skompilować :> Jak coś nowego napiszę jako kolejna wersję DLLki to jeszcze raz pokompiluję przykłady, na razie iznajmy powinny wystarczyć same kody w przykładach ;)

0

Dobra, zrobiłem szybki test i... Access violation reading location 0xffffffff przy wywoływaniu destruktora. Bez znaczenia, czy wave się załadował, czy też nie.

Następna sprawa to to, że zrobiłeś naiwny (czyt. bez zrozumienia struktury plików RIFF) sposób czytania plików wave. Na twoim przykładowym samplu działa, ale na moim już nie ;)

Twój przykład ma taką strukturę:

RIFF
  └ WAVE
        ├ fmt
        └ data

i to jest podstawowa struktura pliku wav, ale nie jedyna. Format RIFF dopuszcza możliwość wystąpienia dodatkowych bloków, niekoniecznie predefiniowanych, pomiędzy blokiem 'fmt ' i 'data' (być może nawet fmt nie musi być pierwszym blokiem). Mój sampel miał taką strukturę:

RIFF
  └ WAVE
        ├ fmt
        ├ PAD (4kB)
        └ data

...i efekt wiadomo jaki ;)

class __declspec(dllexport) CWinWave
{
public:
    [...]

    //do not call this
    DWORD ThreadProc();

    [...]
};

Cóż stało na przeszkodzie zrobić tą metodę prywatną?

oczywiście to nie koniec prac nad tym projektem, a mam nadzieję że dopiero początek :> teraz planuję wyposażyć bibliotekę w konwersję ilości kanałów, następnie w programowy poziom natężenia dźwięku(modyfikowanie wartości próbek o podany współczynnik) [...]

To zacznij od rozdzielenia "engina" audio od strumieni plikowych/pamięciowych/itp. Wykorzystaj możliwości jakie daje polimorfizm, wtedy twoja biblioteka nie będzie się ograniczała tylko do odtwarzania plików wav.

0
0x666 napisał(a)

Dobra, zrobiłem szybki test i... Access violation reading location 0xffffffff przy wywoływaniu destruktora. Bez znaczenia, czy wave się załadował, czy też nie.

hmmm... u mnie coś takiego się nie robiło, czyżby dodanie, runtime do DLL mogło to powodować? hmmm ale co ma sie runtime do kody klasy... Sprawdzę to w najbliższym czasie ale raczej nie dziś...

0x666 napisał(a)

Następna sprawa to to, że zrobiłeś naiwny (czyt. bez zrozumienia struktury plików RIFF) sposób czytania plików wave. Na twoim przykładowym samplu działa, ale na moim już nie ;)

Twój przykład ma taką strukturę:

RIFF
  └ WAVE
        ├ fmt
        └ data

i to jest podstawowa struktura pliku wav, ale nie jedyna. Format RIFF dopuszcza możliwość wystąpienia dodatkowych bloków, niekoniecznie predefiniowanych, pomiędzy blokiem 'fmt ' i 'data' (być może nawet fmt nie musi być pierwszym blokiem). Mój sampel miał taką strukturę:

RIFF
  └ WAVE
        ├ fmt
        ├ PAD (4kB)
        └ data

...i efekt wiadomo jaki ;)

ta wiem, funkcja ta stara jeszcze z czasów początków pisania tej klasy, prawie nic tam nie zmieniałem oprócz czytania z memory dla zasobów, poprawi się ;) (WINDOWS/Media(w PCM) działają i wygenerowane przez WavePad pliki tez mi działały), ale wiem zmienię
albo już na zapas pytanie żebym nie musiał szukać, czy pomiędzy blokami mogą występować występować puste przestrzenie, powiedzmy wypełnione 0, bo to utrudni, będę musiał szukać następnego znanego identyfikatora po bajcie, a tak to można założyć, że po zakończeniu wielkości bloki, od razu powinien być identyfikator i jego wielkość i dla nieznanego przeskoczył bym sobie o odczytaną wielkość bez szukania, a i spodziewać się możliwości wystąpienia bloku fmt po data? czy fmt musi być pierwsze?

0x666 napisał(a)
class __declspec(dllexport) CWinWave
{
public:
    [...]

    //do not call this
    DWORD ThreadProc();

    [...]
};

Cóż stało na przeszkodzie zrobić tą metodę prywatną?

jest to klasowy odpowiednik ThreadProc, w CreateThread parametrem musi być statyczna funkcja, a zrobiłem to tak, że tej statycznej funkcji przekazuje this klasy (wywołującej nowy wątek) jako parametr i ona wywołuje przez ten parametr metodę ThreadProc, jakby nie patrzeć jest ona wywoływana z zewnątrz klasy przez system, więc musi być publiczna, bo się kompilator będzie burzył... lepszego rozwiązania nie znam

0x666 napisał(a)

oczywiście to nie koniec prac nad tym projektem, a mam nadzieję że dopiero początek :> teraz planuję wyposażyć bibliotekę w konwersję ilości kanałów, następnie w programowy poziom natężenia dźwięku(modyfikowanie wartości próbek o podany współczynnik) [...]

To zacznij od rozdzielenia "engina" audio od strumieni plikowych/pamięciowych/itp. Wykorzystaj możliwości jakie daje polimorfizm, wtedy twoja biblioteka nie będzie się ograniczała tylko do odtwarzania plików wav.
</quote>
nawet chciałem tak zrobić, z każdym kolejnym dziedziczeniem kolejne możliwości miały być dostępne, wyszło mi że koło 10 klas, czyli możliwość streamingu(klasa najwyżej w hierarchii) miała być dziedziczona po 9 klasach :D uznałem jednak że to z lekka przesada, ale jakiś podział musi być rzeczywiście, bo w miarę rozbudowy coraz więcej tych metod w jednej klasie, robi się ciasno ;)
W sumie teraz oddzielę sam "engine" od reszty, bo znacznie zmieni się jego budowa, jak zrobię konwersje kanałów i głębi próbek + modyfikacja natężenia, będzie oddzielny bufor wejściowy i wyjściowy, zapowiada się całkiem inne działanie engina...

W sumie zadowolony jestem bo chociaż jedna osoba się tematem zainteresowała...

EDIT: jeszcze coś, w sumie nie sprawdzałem, a mogłem, czy formatami skompresowanymi(tymi uwzględnionymi w wave, np. ADPCM) zajmie się karta muzyczna, tzn. jeśli wyślę skompresowany bufor bezpośrednio wczytany z pliku, i w WAVEFORMATEX uwzględnię, że to nie PCM, to czydekompresją zajmie się karta/system po wysłaniu tego przez waveOurWrite? cz sam powinienem zająć się dekompresją, bo jeżeli nie, to nie ma powodu ograniczać się tylko do PCM(dopóki w grę nie wchodzi oczywiście zmienianie natężeń itp., bo raczej nie skompresowanych danych nie będę modyfikował)

0

czyżby dodanie, runtime do DLL mogło to powodować?

Wątpie.

czy pomiędzy blokami mogą występować występować puste przestrzenie, powiedzmy wypełnione 0 [...]

Nie, nie mogą.

a i spodziewać się możliwości wystąpienia bloku fmt po data?

Format dopuszcza taką możliwość, chociaż nie spotkałem się z takim plikiem, gdzie blok data byłby przed fmt.

czy fmt musi być pierwsze?

j/w.

jakby nie patrzeć jest ona wywoływana z zewnątrz klasy przez system, więc musi być publiczna, bo się kompilator będzie burzył... lepszego rozwiązania nie znam

A o zaprzyjaźnianiu (friend) funkcji z klasą słyszał? :> Albo o metodach statycznych?

nawet chciałem tak zrobić, z każdym kolejnym dziedziczeniem kolejne możliwości miały być dostępne, wyszło mi że koło 10 klas, czyli możliwość streamingu(klasa najwyżej w hierarchii) miała być dziedziczona po 9 klasach

Hmm, nie mówiłem o rozszerzaniu właściwości klasy, ale o polimorfizmie. Czyli tworzysz interface wejściowego strumienia audio. Po nim dziedziczą klasy, które implementują obsługę poszczególnych formatów plików. Klasa CWinWave zamiast uchwytów do zasobów lub nazw plików przyjmuje wskaźnik na taki strumień poprzez wskaźnik na interface (CWinWave nie musi wiedzieć skąd pochodzą dane). Takie podejście poszerzy znacznie uniwersalność biblioteki i ułatwi jej dalszą rozbudowę.

czy formatami skompresowanymi(tymi uwzględnionymi w wave, np. ADPCM) zajmie się karta muzyczna, tzn. jeśli wyślę skompresowany bufor bezpośrednio wczytany z pliku, i w WAVEFORMATEX uwzględnię, że to nie PCM, to czy dekompresją zajmie się karta/system po wysłaniu tego przez waveOurWrite?

Raczej nie. Zainteresuj się ACM (audio compression manager), jest częścią API systemowego.

0
0x666 napisał(a)

A o zaprzyjaźnianiu (friend) funkcji z klasą słyszał? :> Albo o funkcjach statycznych?

Słyszeć coś tam słyszał, ale nie używał :> więc już za specjalnie nie pamięta o tym :>

0x666 napisał(a)

Hmm, nie mówiłem o rozszerzaniu właściwości klasy, ale o polimorfizmie. Czyli tworzysz interface wejściowego strumienia audio. Po nim dziedziczą klasy, które implementują obsługę poszczególnych formatów plików. Klasa CWinWave zamiast uchwytów do zasobów lub nazw plików przyjmuje wskaźnik na taki strumień poprzez wskaźnik na interface (CWinWave nie musi wiedzieć skąd pochodzą dane). Takie podejście poszerzy znacznie uniwersalność biblioteki i ułatwi jej dalszą rozbudowę.

Aha takie "dziedziczenie przez wskaźnik", dobrze wiedzieć, że to też zwie się dziedziczenie :> nie pomyślałem o takim nowoczesnym podejściu w stylu C++ :>
niestety myślę bardziej w kategoriach C i nie mogę jakoś się przerzucić na myślenie takimi interfejsami C++, choć czas najwyższy

czyli tak CWinAudio(skoro nie tylko wave to trzeba zmienić nazwę), będzie przyjmować wskaźnik na CWinAudioInput, od razu nasunęła mi się myśl, że można zrobić kolejne interfejsy CWinAudioMessages(sposób obsługi komunikatów zwrotnych), z regualcją wyjścia WAVE dam sobie spokój skoro będzie programowy regulator natężenia... Dodatkowo trzeba dać możliwość zmiany interfejsów, np CWinAudioInput, żeby można było bez potrzeby tworzenia nowego obiektu odtwarzać wave i po zmianie wskaźnika na inny interfejs inny rodzaj wejścia
Rzeczywiście to najlepsze rozwiązanie, może nie tak proste jak tamto, ale dużo uniwersalniejsze

A co do regulacji dźwięku, czy wystarczy pomnożenie próbek o współczynnik? I w sumie nie będzie to wydajne, to jeśli dobrze rozumuje, próbka jest jako liczba całkowita(w PCM), będzie trzeba to przerzucić na float, i pomnożyć o współczynnik float natężenia dźwięku, po czym znowu zaokrąglić do wartości całkowitej, i tak dla każdej próbki, trochę tych konwersji będzie

EDIT: a i trzeba najpierw przejechać po wszystkich próbkach, żeby znaleźć próbkę o najwyższej/najniższej wartości, bo na jej podstawie trzeba będzie wyznaczyć o ile możemy maksymalnie podnieść poziom natężenia, bo przecież nie mogę przekroczyć maksymalnej wartości próbki przy mnożeniu

0

Aha takie "dziedziczenie przez wskaźnik", dobrze wiedzieć, że to też zwie się dziedziczenie

:| Może bardziej obrazowo:

Masz interface:

class IAudioInputStream
{
public:
	virtual DWORD __stdcall Read(void*,DWORD)=0;
	//... tu inne istotne metody
}

i klasy, które implementują metody interface'u dla poszczególnych typów plików:

class CWAVInputStream:public IAudioInputStream
{
public:
	DWORD __stdcall Read(void*,DWORD);
	[...]
};

class CAIFFInputStream:public IAudioInputStream
{
public:
	DWORD __stdcall Read(void*,DWORD);
	[...]
};

class CSNDInputStream:public IAudioInputStream
{
public:
	DWORD __stdcall Read(void*,DWORD);
	[...]
};

Twoja klasa dostaje wskaźnik na strumień IAudioInputStream

class CWinAudio
{
public:
	BOOL Play(IAudioInputStream*);
	[...]
}

A co do regulacji dźwięku, czy wystarczy pomnożenie próbek o współczynnik?

Tak.

I w sumie nie będzie to wydajne

Dzisiaj praktycznie wszystkie DAWy/edytory/syntezatory pracują na samplach float.

to jeśli dobrze rozumuje, próbka jest jako liczba całkowita(w PCM), będzie trzeba to przerzucić na float, i pomnożyć o współczynnik float natężenia dźwięku, po czym znowu zaokrąglić do wartości całkowitej, i tak dla każdej próbki

Musisz też zadbać o to, żeby nie przekroczyć zakresu sampla, bo zamiast przesterowania będziesz miał nieprzyjemny trzask.

trochę tych konwersji będzie

Nie tak dużo, wystarczy 8, 16, 24, 32 (ale nie float) bity.

a i trzeba najpierw przejechać po wszystkich próbkach, żeby znaleźć próbkę o najwyższej/najniższej wartości, bo na jej podstawie trzeba będzie wyznaczyć o ile możemy maksymalnie podnieść poziom natężenia

To co ty chcesz zrobić, normalizacje, czy regulacje głośności?

0
0x666 napisał(a)

to jeśli dobrze rozumuje, próbka jest jako liczba całkowita(w PCM), będzie trzeba to przerzucić na float, i pomnożyć o współczynnik float natężenia dźwięku, po czym znowu zaokrąglić do wartości całkowitej, i tak dla każdej próbki

Musisz też zadbać o to, żeby nie przekroczyć zakresu sampla, bo zamiast przesterowania będziesz miał nieprzyjemny trzask.

Jak temu zapobiegać, jakie się metody stosuje?

0
out=(fabs(in+1.0)-fabs(in-1.0))*0.5;

Dla sampli float z zakresu [-1,1].

0

a konwersja wsteczna kanałów np. ze stereo do mono, czytałem, że lewą i prawą próbkę się łączy średnią arytmetyczną, nie lepiej zawsze brać wartość większą(wartość bezwzględną wartości wiekszej)

0

Nie lepiej. Zrób sobie doświadczenie, zsumuj dwie sinusoidy przesunięte względem siebie o PI radianów i zobacz co wyjdzie z twojej metody, a co ze zwykłego sumowania.

0
0x666 napisał(a)

Nie lepiej. Zrób sobie doświadczenie, zsumuj dwie sinusoidy przesunięte względem siebie o PI radianów i zobacz co wyjdzie z twojej metody, a co ze zwykłego sumowania.

rzeczywiście :>

0

nowy engine jako tako sklecony,

-konwersja głębi działa
-konwersja kanałów działa(z tym, że na razie tylko mono)

Co do głębi jest ok, we wszystkie strony(chyba)...
Co do konwersji kanałów to zastanawiam się czy brać pod uwagę, że na wejściu może być więcej niż stereo, spotykałeś się z plikami zapisanymi z kanałami większymi niż stereo? Ciekawe czy popularne programy będą obsługiwać niestandardowe pliki powiedzmy z 4 kanałami, chyba dla testu spreparuję sobie wav jako 4 kanały(oczywiście fmt w formacie WAVE_FORMAT_EXTENSIBLE)...

No i teraz kwestia regulacji natężenia dźwięku, mnożenie wartości próbek o współczynnik nie działają, to nie tak działa :/ im większy/mniejszy współczynnik od 1.0f tym większe zniekształcenie dźwięku...

0

Co do konwersji kanałów to zastanawiam się czy brać pod uwagę, że na wejściu może być więcej niż stereo, spotykałeś się z plikami zapisanymi z kanałami większymi niż stereo?

Yyyyy, chyba tak. Bodajże były to sample z jakiegoś drumkit'a (chyba 16 kanałów). Ale jeżeli chodzi o typowe wavy, to nie - muzyki nie zapisuje się w większej liczbie kanałów.

No i teraz kwestia regulacji natężenia dźwięku, mnożenie wartości próbek o współczynnik nie działają, to nie tak działa :/ im większy/mniejszy współczynnik od 1.0f tym większe zniekształcenie dźwięku...

Pokaż kod.

0
0x666 napisał(a)

Pokaż kod.

Tylko nie krzycz jak będzie źle ;) od razu mówię, że nie wiem czy dobrze przeprowadzam konwersję, może stosuje się całkiem inną metodę, to co napisałem wymyśliłem sam...

Masz cały projekt, jakbyś chciał po testować to format wyjściowy zmieniaj w konstruktorze CWinAudio
http://lublin.webd.pl/crayze/winaudiomake.rar

0
DWORD CWinAudio::TransformInputToOutput(DWORD CopySize,char* OutBuffer)
{
	[...]

	if(free=true) delete[] TempBuffer; //<--- BŁĄD!!!
	return ResultBufferSize;
}

Tu masz błąd:

int InINTSmp;

[...]

memcpy(&InINTSmp,lpIn,InSampleBytes);

[...]

memcpy(lpTemp,&TmpINTSmp,OutSampleBytes);

short(-1) po czymś takim będzie się równało int(65535), a nie int(-1).

Next one:

TmpINTSmp=(int)ceil(FOut*SampleScope(OutSampleBytes));
memcpy(lpTemp,&TmpINTSmp,OutSampleBytes);

[...]

float CWinAudio::SampleScope(DWORD SampleBits)
{
	switch(SampleBits) //<--- Bits?!
	{
	case 1: return 128.0f;   //<--- dla 1.0f da to char(-128)
	case 2: return 32768.0f; //<--- dla 1.0f da to short(-32768)
	case 3: return 8388608.0f; //<--- dla 1.0f da to int24(-8388608)
	case 4: return 2147483648.0f; //<--- dla 1.0f da to int(-2147483648)
	}
	return 0.0f;
}

W LoadFromFile w ogóle nie sprawdzasz poprawności odczytu z pliku. Nigdzie nie zwalniasz uchwytu BufferingThread.

Na razie tyle...

0

czyli jak proponujesz odczytywać i konwertować wartości całkowite do ich wartości ułamkowej, w sumie nie pomyślałem, jeśli odczytam do wartości próbki mniejszej niż int do int przez memcpy, to szlak trafi znak, chyba, żeby ręcznie czuwać nad znakiem:

if(próbka==2 bajty) if(próbka >= 32tys z kawałkiem) zmień znak na przeciwny w incie(nie znam funkcji w C++ do zmiany znaku na przeciwny, albo modyfikacja ostatniego bitu będzie szybsza) i zmniejsz o 32tys z kanałkiem

(od razu sobie napiszę żeby nie zapomnieć, żeby zerować int'a przed każdym memcpy bo memcpy nie nadpisze mi ostatnich bajtów, tam gdzie będzie znak, i się zdziwię jak potem wszystkie wartości będą ujemne xd,)
Taka konwersja wartości, jest w tym jakaś logika :>

czy może jednak lepiej dla każdej konwersji napisać oddzielną funkcję?

0

chyba, żeby ręcznie czuwać nad znakiem:

if(próbka==2 bajty) if(próbka >= 32tys z kawałkiem) zmień znak na przeciwny w incie(nie znam funkcji w C++ do zmiany znaku na przeciwny, albo modyfikacja ostatniego bitu będzie szybsza) i zmniejsz o 32tys z kanałkiem [...]

Kombinujesz jak koń pod górę ;-)

InINTSmp = *((unsigned char*)lpIn) - 128; //<--- 8bit
InINTSmp = *((int16*)lpIn);    //<--- 16bit
InINTSmp = *((int32*)lpIn)<<8; //<--- 24bit z konwersją na 32bit

Zasadniczo sample 8 i 16 bitowe możesz sprowadzić do sampli 32 bitowej, wtedy będziesz mógł się pozbyć metody SampleScope.

0
for(DWORD i=0;i<BasicBufferSize;i++)//wszystkie próbki
    {
      //save input sample to 32bit sample
      switch(InSampleBytes)
      {
        case 1: InSmp=(*lpIn-128)<<24; break;//8bit to 32bit
        case 2: InSmp=*((short*)lpIn)<<16; break;//16bit to 32bit
        case 3: InSmp=*((int*)lpIn)<<8; break;//24bit to 32bit
        case 4: InSmp=*((int*)lpIn); break;//32 bit to 32bit
      }
      //save 32bit sample to output sample
      switch(OutSampleBytes)
      {
        case 1://32bit to 8bit
        {
          unsigned char sample8bit=*(((char*)(&InSmp))+3)+128;
          memcpy(lpTemp,&sample8bit,1);
          break;
        }
        case 2: memcpy(lpTemp,((char*)(&InSmp))+2,2); break;//32bit to 16bit
        case 3: memcpy(lpTemp,((char*)(&InSmp))+1,3); break;//32bit to 24bit
        case 4: memcpy(lpTemp,(char*)(&InSmp),4); break;//32bit to 32bit
      }
      //przeskok na następną próbkę
      lpIn+=InSampleBytes;
      lpTemp+=OutSampleBytes;
    }

Konwersje przesunięciami bitowymi to całkiem dobry pomysł :>
Skoro konwersja głębi już działa, teraz trzeba by zastanowić się nad regulacją głośności, modyfikacje głośności już chyba powinno się robić na floatach od <-1,1>, więc trzeba by te próbki o wartościach całkowitych zmieniać na float'a <-1,1>, trochę już kombinowałem jak koń pod górę z marnym jak do tej pory skutkiem...

0
unsigned char sample8bit=*(((char*)(&InSmp))+3)+128;
memcpy(lpTemp,&sample8bit,1);

No bez żartów :/

lpTemp = (InSmp>>24) + 128;
0

Oj tam, kwestia zapisu, zresztą 24bitowej wartości nie przypiszesz w ten sposób...

Co z tą konwersją, jak uzyskuje się procent ułamkowy z próbki? Trochę kombinowałem jako ja, jak koń pod górę, ale pewnie jest jakiś prosty sposób :>

0

Oj tam, kwestia zapisu [...]

Kwestia czytelności i wydajności...

zresztą 24bitowej wartości nie przypiszesz w ten sposób...

Przypisze, tylko bufor musiałby być wyrównany do 4 bajtów ;)

Co z tą konwersją, jak uzyskuje się procent ułamkowy z próbki?

Nie wiem, o co chodzi...

0
0x666 napisał(a)

Co z tą konwersją, jak uzyskuje się procent ułamkowy z próbki?

Nie wiem, o co chodzi...

Chodzi o regulację dźwięku, muszę zwiększyć/zmniejszyć amplitudę o wartość, a do tego trzeba uzyskać wartość próbki w wartości ułamkowej <-1,1>, float=próbka/JejSampleScope, o to chodziło, tylko tamto nie miało sensu, próbowałem z przesunięciami we floacie rzutowanym na DWORD, float ceplusowy ma w 9 bitach zapisuje wartość całkowitą, a 23bitach ułamkową? Jakby tak przepchnąć wartość próbki o te 8bitów(bo bez znaku) i rzutować na float, tak, żeby wartość próbki stała się wartością mantysy floata, znowu kombinuje...

0

znowu kombinuje...

No :>

const double mul32bit=2147483647.0;
const double div32bit=1.0/mul32bit;

float fSmp= float(InSmp) * div32bit; //<--- 32bit -> float

fSmp=(fabs(fSmp+1.0) - fabs(fSmp-1.0)) * 0.5; //<--- ograniczanie (konieczne!)
InSmp = fSmp* mul32bit; //<--- float -> 32bit

PS. żeby nie było, to co pokazuje to tylko podstawy i nie są to rozwiązania wydajne.

0

Jest mały problemik konstrukcyjny:

     //...
     if(CopySize)
      {
        HANDLE hMutex=OpenMutex(NULL,true,"BufferingMutex");//locking release buffer
        OutSize=audio->TransformInputToOutput(CopySize,((WAVEHDR*)msgs.lParam)->lpData);//konwersja audio z input do output
        ((WAVEHDR*)msgs.lParam)->dwBufferLength=OutSize;
        waveOutWrite(audio->OutHandle,(WAVEHDR*)msgs.lParam,sizeof(WAVEHDR));
        ReleaseMutex(hMutex);
      }
      else
      {
        if(audio->BuffersUsed==1)//ostatni bufor został odegrany
        { 
          audio->Stop();//  <----------- eeee?
          return 0;
        }
        else audio->BuffersUsed--;
      }
    }
    else if(msgs.message==MM_WOM_CLOSE) break;
  }
  return 0;
}

void CWinAudio::Stop()
{
  if(OutHandle==NULL) return;
  LockBuffer=true;
  WaitForSingleObject(BufferingMutex,INFINITE);//if buffering is executing
  waveOutReset(OutHandle);
  for(DWORD i=0;i<NumberBuffers;i++) waveOutUnprepareHeader(OutHandle,WaveHeader[i],sizeof(WAVEHDR));
  waveOutClose(OutHandle);
  for(DWORD i=0;i<NumberBuffers;i++) delete WaveHeader[i];
  delete[] WaveHeader;
  for(DWORD i=0;i<NumberBuffers;i++) delete OutBuffers[i];
  delete[] OutBuffers;
  delete[] InputBuffer;
  OutHandle=NULL;
  ReleaseMutex(BufferingMutex);
  CloseHandle(BufferingMutex);
  CloseHandle(BufferingHandle);//????? <-------
}

Problemik całkiem z innej beczki niż poprzednio...
Tyczy sie zamykania handle'a wątku przez CloseHandle(), otóż wszystko byłoby fajno, gdyby Stop() zawsze było wywoływane przez główny wątek użytkownika, ale gdy odtwarzanie się zakończy Stop jest wywoływane z poziomu wątku buforującego(co widać w kodzie), a w tym wypadku sam siebie zamknąć nie może(chyba), więc CloseHandle wątku nie zwolni, bo się nie zakończył, skoro Stop jest wywoływane w tym wątku, błędne koło....
Trzeba albo wywołać metodę Stop po zakończeniu odtwarzania, z poziomu wątku głównego użytkownika, tak aby w momencie wykonywania Stop wątek buforujący mógł się zakończyć, nawet bym sobie zrobił kolejnego mutex'a który by poczekał aż wątek buforujący się zakończy, albo go ExitThread(ale chyba jednak wypada poczekać), albo nie wiem, coś pokombinować, np. zwalniać przez CloseHandle dopiero w następnym wywołaniu Play lub dekonstruktorze...

0

coś pokombinować, np. zwalniać przez CloseHandle dopiero w następnym wywołaniu Play lub dekonstruktorze...

Destruktorze ;) Jak bym zrobił tak, że wątek tworzony byłby konstruktorze, a niszczony w destruktorze - nikt nie powiedział, że w Play musisz go tworzyć a w Stop niszczyć.

PS. do synchronizacji międzywątkowej używaj sekcji krytycznych. No i w ogóle zastanów się nad synchronizacją, bo jej nie ma, a być powinna.

0
0x666 napisał(a)

PS. do synchronizacji międzywątkowej używaj sekcji krytycznych. No i w ogóle zastanów się nad synchronizacją, bo jej nie ma, a być powinna.

a mutexy będą złe?
znaczy się co do synchronizacji, chcę aby zabezpieczyć możliwość zwolnienia bufora(wywołanie Stop) wejściowego, w trakcie wypełniania go, bo byłem już świadkiem takiej sytuacji, zatrzymałem odtwarzanie zwalniając jednocześnie bufor, a wątek buforujący zapewne go wypełniał bo wiadomo co się pojawiło :>
bo co tu więcej synchronizować, już raczej wątki w innych miejscach nie wchodzą sobie w drodze...

1 użytkowników online, w tym zalogowanych: 0, gości: 1