Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Sumowanie sygnalu audio (wave form.) w C++

tomaszo 08 Jun 2005 13:22 3674 25
  • #1
    tomaszo
    VIP Meritorious for electroda.pl
    Witam!

    Dzialania w zakresie projektu (SKYPE pod linuxa) w ramach pracy na uniwerku wymagaja nieco innych kwestii, niz przedstawione na poczatku - no prof. nie wie co nadzoruje dokldnie...

    Potrzebuje Waszego wsparcia jednym slowem...

    Nie mialem tutaj byc programista, bo juz od dawna nie poruszam sie w srodowisku C++, zreszta w ogole nie mam czasu...


    Kwestia, poki co, lezy w prostej implementacji - mamy sumowac dwa sygnaly audio - na poczatku 8 bitowa kwantyzacja, potem przejdziemy do 16 bitow.

    Przepraszam, jesli sie powtarzam, nie doszedlem do ladu z wyszukiwarka...
    Bede bardzo wdzieczny za wszelkie wskazowki - mysle, ze dla niektorych z Was to buleczka z maselkiem na 2 minuty...
  • #2
    MirekCz
    Level 35  
    Sumowanie dwoch sygnalow audio to po prostu ich dodawanie. Uwazaj tylko czy sygnal wyjsciowy nie wychodzi poza dozwolone wartosci i ewentualnie go przytnij.
  • #3
    tomaszo
    VIP Meritorious for electroda.pl
    Wlasnie mi programik nie dziala... To znaczy napewno poziom nie jest za wysoki... Mam problem, zeby to odpowiednio zsumowac chyba...
  • Helpful post
    #5
    MirekCz
    Level 35  
    robisz to mniejwiecej tak:
    Code:


    signed char tab1[256];

    signed char tab2[256];

    int temp;

    for (i=0;i<255;i++)
    {
        temp=(int)tab1[i]+(int)tab2[i];
        if (temp>127) temp=127;
        if (temp<-128) temp=-128;
        tab1[i]=(signed char)temp;
    }


    kod ten dziala nastepujaco:
    1.temp - zmienna 32 bitowa ze znakiem - do ktorej zapisujemy sume dwoch sygnalow audio (oba traktowane jako liczby 8 bitowe ze znakiem).. podpowiadamy kompilatorowi, zeby traktowal te wartosci jako 32bitowe przy dodawaniu

    2.nastepnie sprawdzamy czy temp wychodzi poza zakres zmiennej 8bitowej ze znakiem (ten zakres to od -128 do 127) i ewentualnie ja przycinamy do tego zakresu jezeli wychodzi

    3.na koncu zapisujemy zsumowany sygnal do tab1....

    Tak samo mozesz robic z sygnalami 16bitowymi, tylko zmien typ danych tab1 i tab2 oraz zakres wartosci na od -32768 do 32767
  • #6
    tomaszo
    VIP Meritorious for electroda.pl
    Jak pisalem - wyszedlem z obiegu, proste jest wszystko, jak się to umie...

    Wielkie Dzieki MirekCz!

    Jesli ktos jeszcze ma jakies propozycje to bede zobowiazany...


    --------------------------

    Zeby to dzialalo, trzeba uciac header wave - i po prostu dodac zawartosc dwoch odrebnych plikow...

    Wstepnie programik wyglada tak - szefu stwierdzil, ze powinien dzialac...

    Problem z dobraniem wartosci HDRSIZE - nie wiem dlaczego, dziala tylko przy bardzo duzych wartosciach... Nie dziala to jak trzeba, ale juz przynajmniej nie wychodyzi po zsumowaniu totalny przester i krach... Poki co, nie sumuje tych dwoch waveow!

    #include <stdio.h>
    #include <iostream>

    #define BLOCKSIZE 256
    #define HDRSIZE 99999999
    //es muss GROSS sein! aber warum es ist nur WAVE1 hoerbar?????
    int main()
    {
    FILE* f1;
    FILE* f2;
    FILE* f3;

    f1 = fopen("WAVE1","r");
    f2 = fopen("WAVE2","r");
    f3 = fopen("outwave","w");

    // 8 Bit sampling

    char data1[BLOCKSIZE];
    char data2[BLOCKSIZE];
    char outputdata[BLOCKSIZE];
    int i;

    // 16 Bit sampling


    char Hdr[HDRSIZE];

    fread(&Hdr,sizeof(char),HDRSIZE,f1);
    fwrite(&Hdr,sizeof(char),HDRSIZE,f3);
    fread(&Hdr,sizeof(char),HDRSIZE,f2);

    while ((fread(data1,sizeof(char),BLOCKSIZE,f1)==BLOCKSIZE) &&
    (fread(data2,sizeof(char),BLOCKSIZE,f2)==BLOCKSIZE)) {

    int dat;
    for(i=0; i<BLOCKSIZE; i++) {

    dat = (data1[i] + data2[i]); //256; das ist groesste zahl
    moeglich!!!(256)-nicht mehr

    if (dat > 127)
    dat = 127;

    if (dat < -128)
    dat = -128;

    outputdata[i] = (char) dat;
    }

    fwrite(outputdata, sizeof(char), BLOCKSIZE,f3);
    }

    fclose(f1);
    fclose(f2);
    fclose(f3);

    }


    W teorii - dla 8bit 11,25kHz HDRSIZE jest na poziomie 48... Jednak to nie dziala....
  • #7
    Sam Sung
    Level 32  
    Może trzeba otworzyć pliki w trybie binarnym (rb / wb) ?
    Co to znaczy, że nie działa tak jak trzeba? Piszesz, że działa przy dużych HDRSIZE, a potem, że jednak nie działa...
    A może te pliki mają inny format od założonego? Np. jakąś kompresję ADPCM czy coś?
    Wypadałoby wczytać nagłówek, sprawdzić format dźwięku i rzeczywisty rozmiar nagłówka.
    Może program czasami się nie uruchamia przez to, że wymaga 100 MB RAMu?
    Bez debuggera chyba się nie obejdzie...
  • Helpful post
    #8
    MirekCz
    Level 35  
    no naprawde wyszedles z obiegu :)

    1.Wiesz co to debuger? uzyj go, napewno WIELE wyjasni.. przelec krok po kroku twoj program to bedziesz wiedzial gdzie sie kaszani

    2.data1 i data2 sa typu char (wiec wartosci 0..255) a ty je przycinasz potem do -128..127 (jak dla signed char)... tu chyba akurat rybka jest, ale napewno smiesznie to wyglada

    3.Jak kolega mowil, pliki musisz otworzyc binarnie

    4.Co to ma byc:
    fwrite(&Hdr,sizeof(char),HDRSIZE,f3); ??
    Nie rozumiem po co to... otworz plik do zapisu ze skasowaniem starych danych i tyle

    tutaj masz opis fopen: http://www.cplusplus.com/ref/cstdio/fopen.html

    wiec uzyc musisz dla f3 "wb+"

    5.Jak pisalem, daj podpowiedz kompilatorowi, ze dodawanie ma byc zrobione w 32bitach a nie 8... wiec zamiast

    dat = (data1[i] + data2[i]);

    uzyj

    dat=(int)data1[i]+(int)data2[i];

    6.to HDRSIZE jest zupelnie nie na miejscu... wlasnie przeczytalem komentarz "//es muss GROSS sein! aber warum es ist nur WAVE1 hoerbar????? " a odpowiedz jest prosta... ty otwierasz plik f1 , czytasz z niego wszystko co sie da , zapisujesz do f3, otwierasz plik f2... potem wchodzisz do petli gdzie ma byc dodawanie a oba pliki sa juz cale przeczytane... wiec zadnego dodawania nie ma... to co jest w pliku f3 jest zywcem skopiowane z pliku f1


    Wywal to cale HDRSIZE, wprowadz poprawki ktore podalem powyzej , przerzuc to przez debuger i sprawdz co sie dzieje w programie.

    Tutaj masz prosty opis .wav headera... poszukaj po google kompletnego http://www.neurotraces.com/scilab/scilab2/node24.html
  • Helpful post
    #9
    Sam Sung
    Level 32  
    MirekCz wrote:
    2.data1 i data2 sa typu char (wiec wartosci 0..255) a ty je przycinasz potem do -128..127 (jak dla signed char)... tu chyba akurat rybka jest, ale napewno smiesznie to wyglada

    Każdy typ całkowitoliczbowy w C jest domyślnie signed, więc char to to samo co signed char - no chyba że w opcjach projektu ktoś sobie zmienił :)
    MirekCz wrote:

    4.Co to ma byc:
    fwrite(&Hdr,sizeof(char),HDRSIZE,f3); ??
    Nie rozumiem po co to... otworz plik do zapisu ze skasowaniem starych danych i tyle
    wiec uzyc musisz dla f3 "wb+"

    Tam chyba jednak chodzi o stworzenie nowego pliku z sumą dwóch danych strumieni. Więc kopiowanie nagłówka jest jak najbardziej w porządku (do celów testowych), ale nie całego pliku 1 :)
    MirekCz wrote:

    Tutaj masz prosty opis .wav headera... poszukaj po google kompletnego http://www.neurotraces.com/scilab/scilab2/node24.html

    Struktury nagłówka wave są opisane w helpie do Microsoft Platform SDK - poszukaj funkcji waveOut* i znajdzie się cały rozdział.
  • Helpful post
    #10
    Paweł Es.
    VIP Meritorious for electroda.pl
    Jeżeli te operacje mają na celu np. monofonizację sygnału to robi to się wg następujacego wzoru:

    $$M=\frac{X+Y}{2}$$ (****)

    Dodajemy w zmiennej 16 bitowej ze znakiem a wynik po podzieleniu przez 2 zapisujemy w 8 bitowej ze znakiem.

    Nie podzielenie wyniku przez dwa powoduje wyprowadzenie wyniku poza zakres 8 bitowy.

    Obcinanie typu if x>127 then x=127 nie ma sensu bo wprowadza zniekształcenia do przebiegu wynikowego.

    Dzielenie przeprowadzamy (przez przesunięcie w prawo) i po dodawaniu by nie tracić rozdzielczoości:

    $$\frac{X}{2}+\frac{Y}{2}$$ w zakresie liczb całkowitych zwykle nie daje tego samego wyniku co wzór ****
  • #11
    tomaszo
    VIP Meritorious for electroda.pl
    Dziekuje wszystkim...i prosze o jeszcze ;) Od dwoch lat nie mialem z c++ do czynienia - nie bylo potrzeby (oprocz paru drobiazgow, ktore dzisiejsze dziecko napisze w przedszkolu...).

    Program napisal "szefu" - inzynierek prowadzacy w praktyce projekt, profesorek nie ma z tym w ogole nic wspolnego...- i twierdzi, ze NIE MA BLEDOW - stad pytam, bo sie mecze juz dluzszy czas z tym, co wychodzi po zsumowaniu...

    W ogole to ma na celu pozniejsze modyfikacje tego naszego programu (a'la skype) - jak na razie mozna rozmawiac we dwoch (tez nie zawsze dziala) - a w planie jest mozliwosc konferencji do 10 osob...
  • #12
    Bielsky
    Level 21  
    tomaszo wrote:

    Program napisal "szefu" - inzynierek prowadzacy w praktyce projekt, profesorek nie ma z tym w ogole nic wspolnego...- i twierdzi, ze NIE MA BLEDOW - stad pytam, bo się mecze juz dluzszy czas z tym, co wychodzi po zsumowaniu...


    W zasadzie moża być bez błędów, ale poprawnie będzie sumował tylko plik w jednym formacie. Nagłówek w pliku wav pojawia sięnie tylko na początku, może mieć dodatkowe bity; poza tym w środku pliku pojawiają się nagłówki bloków.

    tomaszo wrote:

    W ogole to ma na celu pozniejsze modyfikacje tego naszego programu (a'la skype) - jak na razie mozna rozmawiac we dwoch (tez nie zawsze dziala) - a w planie jest mozliwosc konferencji do 10 osob...

    Tu raczej nie wchodzi w rachubę sumowanie sygnałów. Z tego, co wiem o systemach konferencyjnych - aby unikąć hałasu - w praktyce wpuszcza się do linii tylko najsilniejszy sygnał.

    Spróbuję się tym pobawić, od jakiegoś czasu i tak miałem zamiar rozgryźć strukturę wawe'a, na razie niczego nie obiecuję.
    Mam jedną sugestię. Skoro system, któy tworzysz ma byś systemem konferencyjnym - raczej nie będziesz pobierał dźwięków w fromie pliku a będzie trzeba przechwycić strumień danych opisujących dźwięk. Myślę, że większym problemem będzie odczytanie ciągu próbek ze strumienia, niż ich sumowanie; dość dobrze opisał je Paweł ES.
  • #13
    fantom
    Level 31  
    Wedlug mnie program jest niekompletny dlatego, ze nie ma nigdzie sprawdzenia czy wszystkie operacje wykonaly sie prawidlowo (wylapanie ewentualnych bledow).Poza tym mam watpliwosci co do alokacji takiej ilosci pamieci na potrzeby aplikacji zwazywszy ze jest to pamiec alokowana na stosie.Moze warto przeniesc to do zmiennych globalnych.
  • #14
    MirekCz
    Level 35  
    Pawel:
    Tak, twoj kod zapewni, ze sygnal nie wyjdzie poza dozwolone wartosci ... ale co bedzie, jak bedziesz laczyl 10 sygnalow i z tego tylko 1 bedzie mial jakies dzwieki a 9 pozostalych bedzie mialo wartosc 0?

    Odpowiedz jest prosta - sygnal bedzie BARDZO cichy... a nie o to tutaj chodzi


    Tomaszo:jak nie umiecie tego zrobic, to moze siegnijcie po profesjonalne narzedzie?
    www.fmod.com
    Jezeli ten projekt ma byc czyms wiecej niz tylko "pokazowka" na uniwersytecie to napewno nie bedziecie tego zalowali
  • #15
    tomaszo
    VIP Meritorious for electroda.pl
    Bielsky - no byloby wspaniale...

    MirekCz - badzo chetnie, ale ja tylko operuje tutaj trzonkiem chwytnym recznej glebogryzarki...

    Powiem Wam szczerze - zostalem podciagniety pod ten projekt jako pomoc studencka - przeszedlem najpierw dwie rozmowy kwalifikacyjne u dwoch roznych profesorkow - z czego pierwsza trwala prawie 90 minut...(oczywiscie dotyczyly innej nieco tematyki). Jednym slowem mam robote w ramach "stypendium opiekunczego"...

    NAJSMIESZNIEJSZE w tym wszystkim jest to, ze CALY CZAS mowilem, ze NIE JESTEM OD PROGRAMOWANIA - a mi mowiono, ze projekt dotyczy urzadzenia nadawczo-odbiorczego (takie walkie-talkie) z mozliwoscia aktywnego filtrowania sygnalu... CALY CZAS MYSLALEM, ze bede uzywal WaveLaba i jemu podobnych, znanych mi dosc dobrze aplikacji - a tutaj sie okazalo, ze nie dosc, ze to TOTALNIE co innego - to jeszcze maja wszystko pod linuksem...

    Czyli teraz - dostaje kawaleczek do zrobienia i musze cos zrobic, obojetnie czy to w ogole jest potrzebne, czy nie dla calosci projektu - ... Na poczatku sie niezle zezloscilem - prfesorek nadzorujacy nie ma pojecia co robi w ogole...
    Pojecie ma (choc nie do konca) taki inzynierek, ktory w sumie rozdaje ludziom zadania... Najlepsze, ze chyba sam przy tym siedzi, bo z kilkuosobowej grupy podobnych do mnie kazdy robi co innego - i nie zwiazanego z tym calym..ehh...

    Tak to wyglada - a teraz mi mowi szefu, ze to jest potrzebne, bo ten programik bedzie wykorzystany pozniej do miksowania kilku sygnalow razem - ... Z uwagi na taka organizacje sie mecze, a bez pomocy nie dam rady - a efekty musza byc jakies... Dlatego jestem niezmiernie wdzieczny za wszelka pomoc...
  • Helpful post
    #16
    fantom
    Level 31  
    Cos znalazlem na temat pliku WAV i wyglada ze odpowiedni segment probek w pliku trzeba znalezc na podstawie ID zapisanego w formacie Stringa.Trzeba przeleciec strumien pod katem znalezienia stringa "data", nastepnie odczytac ilosc probek ktore sa nastepnymi 4 bajtami po ID i odczytac odpowiednie dane.Tu sa linki ktore ja przejrzalem:
    http://www.borg.com/~jglatt/tech/wave.htm
    http://www.sonicspot.com/guide/wavefiles.html
    A tak mniej wiecej mogla by wygladac implementacja:
    Code:

    #include <string.h>
    #include <stdio.h>
    #include <stdlib.h>

    char bufor[5];
    unsigned int size;
    char* samples;
    FILE* fd;

    int main()
    {
       ...
       bufor[4]=0;
       fd = fopen("nazwa_pliku","rb");
       fread(bufor,sizeof(char),4,fd);
       while(strcmp(bufor,"data"))
       {
          bufor[0]=bufor[1];
          bufor[1]=bufor[2];
          bufor[2]=bufor[3];
          fread(&bufor[3],sizeof(char),1,fd);
       }
       fread(&size,sizeof(int),1,fd);
       samples = (char*)malloc(sizeof(char)*size);
       fread(samples,sizeof(char),size,fd);
       ...
    }
    // samples[0]  do samples[size-1] - zawiera probki dzwieku
    // odrebna kwestia to sama konstrukcja ramek

    Cos w ten desen.Mam nadzieje ze sie nie pomylilem.Trzeba znalezc w pliku wszystkie segmenty danych no i oczywiscie rowniez ich naglowki.Kolega wyzej mial racje naglowki z danymi przeplataja sie wzajemnie i trzeba to zrobic troche bardziej inteligentnie niz tylko odczytac caly plik i posumowac wszystkie bajty.Aby zrealizowac twoj algorytm potrzebna bedzie drobna modyfikacja powyzszego kodu.
  • #17
    tomaszo
    VIP Meritorious for electroda.pl
    Code:


    #include <stdio.h>
    #include <iostream>

    #define BLOCKSIZE 256
    #define HDRSIZE 44 //i teraz go przekonaj, ze to po chuj potrzebne


    int rmHeader(FILE* fd, FILE* out = 0)
    {
       int counter(4);
       int size;
       char bufor[4];
       fread(bufor,sizeof(char),4,fd);
       if (out)
         fwrite(bufor,sizeof(char),4,out);
       while(strncmp(bufor,"data",4))
       {
          counter++;
          bufor[0]=bufor[1];
          bufor[1]=bufor[2];
          bufor[2]=bufor[3];
          fread(&bufor[3],sizeof(char),1,fd);
          if (out)
           fwrite(&bufor[3],sizeof(char),1,out);
       }
       fread(&size,sizeof(int),1,fd);
       if (out)
         fwrite(&size,sizeof(int),1,out);
       std::cout<<"HdrSize: "<<counter<<"\n";
       return(size);
    }


    int main()
    {
      FILE* f1;
      FILE* f2;
      FILE* f3;

      f1 = fopen("WAVE1","rb");
      f2 = fopen("WAVE2","rb");
      f3 = fopen("outwave","wb+");
     
      // 16 Bit sampling
     

      signed short data1[BLOCKSIZE];
      signed short data2[BLOCKSIZE];
      signed short outputdata[BLOCKSIZE];
      int i;
     
      char Hdr[HDRSIZE];

      int size = rmHeader(f1);
      rmHeader(f2,f3);

      std::cout << "file size: "<<size<<std::endl;

      while ((fread(data1,sizeof(short),BLOCKSIZE,f1)==BLOCKSIZE) &&
             (fread(data2,sizeof(short),BLOCKSIZE,f2)==BLOCKSIZE)) { 

        int dat;
        for(i=0; i<BLOCKSIZE; i++) {
         
         dat=(int)data1[i]+(int)data2[i]; 
           
        if (dat > 32767)  //fuer 16 bit jetzt 32767 und - 32768
            dat = 32767;  //fuer 8 bit sind 127 und -128
               
        if (dat < -32768)
            dat = -32768;

        outputdata[i] = (signed short) dat;
        }

        fwrite(outputdata, sizeof(short), BLOCKSIZE,f3);
      }

      fclose(f1);
      fclose(f2);
      fclose(f3);

    }





    NO dalimy rade :)

    Fantom - no pomoglo...WIELKIE DZIEKI.

    Wszystkim jestem niesamowicie dzwieczny! Nie myslcie sobie, ze to juz koniec :)

    Moze to jeszcze nie dziala jak trzeba - to znaczy moze mozna prosciej, ale poki co - miksuje i to bez klpotow pliki skwantyzowane 16 bit.
  • #18
    fantom
    Level 31  
    Ja moze jeszcze dodam ze gdyby to mialo byc napisane naprawde w C++ to powinno sie wykorzystac obiekt klasy fstream do odczytania strumienia danych z pliku a takze zrobic to modularnie w klasach bo w zasadzie to wyglada jak napisane w C (poza uzyciem obiektow cin i cout) no ale to jest temat do roztrzasania jesli juz wszystko bedzie dzialac.
  • #19
    tomaszo
    VIP Meritorious for electroda.pl
    Racja... Ale tutaj nie chodzilo o czystosc jezyka - mialo tylko dzialac... Dziala - nie jest to optymalny program do tego typu zadan naturalnie...

    Bardzo jestem wdzieczny za pomoc i sugestie!
    Jeszcze sie pojawie, jesli wyszukiwarka nie pomoze...
  • #20
    Paweł Es.
    VIP Meritorious for electroda.pl
    Czy zmienna int ma rozmiar 16 bitów ? Bo jeżeli tak to te ograniczenia 32767 nie zadziałają i wyjdą cuda przy przepełnieniu.

    Ja bym jednak podzielił sumę przez dwa zamiast ograniczać wynik (obcięcie daje zniekształcenia dźwięku przy przesterowaniu).
  • #21
    Bielsky
    Level 21  
    Zależy od kompilatora. (ale nie wiem, czy standard nie zaczął czegoś określać) GCC typ int ma czterobitowy. Zawsze jego rozmiar można sprawdzić za pomocą sizeof.
  • #22
    tomaszo
    VIP Meritorious for electroda.pl
    Jednak przy wiekszej ilosci miksowanych sygnalow dzielenie przez ich ilosc doprowadzi do zbyt duzego wyciszenia...
  • #23
    Bielsky
    Level 21  
    Ale przesterowywanie sygnału będzie prowadzić do kompletnej jego nieczytelności. Będzie trzeba pomyśleć nad zasadami arbitrażu i wpuszczaniem tylko najsilniejszych sygnałów z dodaniem priorytetów dla poszczególnych rozmówców.
  • Helpful post
    #25
    Paweł Es.
    VIP Meritorious for electroda.pl
    Zawsze można użyć mnożników określających proporcje sygnałów

    int y,a,b,c;

    Y=(X1*a+X2*(255-a)) div c

    a - 0-255
    c=256

    dla a-128 mamy oba sygnały po równo.

    regulacja jest w liniowa od 0 do 255/256.

    Oczywiście można zrobić układ decyzyjny dający sygnał wyjściowy, który po scałkowaniu steruje współczynnikiem a i dobiera dynamicznie proporcje obu sygnałów.
  • #26
    tomaszo
    VIP Meritorious for electroda.pl
    To bardzo ciekawy pomysl :) Przemyslimy, jak juz ten mixer bedzie w uzyciu dla programu glownego.