Wav-datei aus Messdaten erstellen



  • hallo Leute,

    beim googlen bin ich auf diesen wertvollen Thread gestossen.
    Ich nutzte bis jetzt immer die Beep(f,d) Funktion. Das geht jetzt leider nicht
    mehr... Nun muss ich den Umweg über die Soundkarte gehen und im Code .wav Dateien
    erstellen.
    Mir ist jetzt noch nicht ganz klar wie die Daten (samples) aussehen müssen für z.B. einen Ton mit 400Hz der 50ms ertönt.

    Für jede Hilfe wär ich sehr dankbar!



  • Portabel ist der Code jedenfalls nicht.
    - struct-Padding
    - sizeof(int, short, ...)
    - endianness



  • Hab dazu immer libsndfile verwendet, gibts gratis hier zum Download:

    http://www.mega-nerd.com/libsndfile/

    Brauchst nur dazulinken und dann die entsprechenden Funktionen aufrufen.



  • Danke schonmal für die Antwort!

    Ich werd mir libsndfile mal ansehn. Aber lieber wär mir eine
    schlanke Klasse mit einer Methode die Frequenz und Dauer als
    Parameter bekommt und das .wav file schreibt.
    Daran arbeite ich gerade...
    Aber ich steh noch zu wenig über dem .wav Format um zu wissen
    wie genau meine samples für eben das Beispiel 440Hz und 50ms
    ausschauen müssen.

    Greets



  • saerdna schrieb:

    Danke schonmal für die Antwort!

    Ich werd mir libsndfile mal ansehn. Aber lieber wär mir eine
    schlanke Klasse mit einer Methode die Frequenz und Dauer als
    Parameter bekommt und das .wav file schreibt.
    Daran arbeite ich gerade...
    Aber ich steh noch zu wenig über dem .wav Format um zu wissen
    wie genau meine samples für eben das Beispiel 440Hz und 50ms
    ausschauen müssen.

    Greets

    Geht es dir nur darum, einmalig wav Dateien mit verschiedenen Frequenzen zu erstellen? Und diese dann zur Compilezeit einbinden?
    Oder willst du zur Laufzzeit dynamisch neue wav Dateien erstellen?

    In ersterem Fall hab ich mal ein Excel Makro erstellt zum Erstellen von wav Dateien. Damit könntest du dir schnell mal ein paar wav Dateien erzeugen.
    http://imageshack.us/photo/my-images/204/44511203.png/

    Im zweiten Fall kannst du die Werte für einen Frequency Sweep (also f1 ändert sich linear zu f2 in so und so viel Sekunden, bzw. f1=f2 in deinem Fall) auf folgende Weise erstellen. Diese Werte kannst du z.B. in einem Array speichern und dann z.B. mit libsndfile in eine WAV Datei abspeichern.
    Evtl. ist auch ein direkter Weg möglich, also PCM Werte->irgendeine Funktion->Soundausgabe. Da ich das aber noch nie gebraucht habe, ist mir jetzt leider keine Möglichkeit geläufig.

    ~Achtung, VBA Code, kann Augenschmerzen bei C++ Programmierern hervorrufen! Ist zum Erzeugen der PCM Werte aus meinem Excel Makro.~

    n = 0
        While n < numberOfSamples And n < 65000
            val = Round(amplitude * Sin(PI * (2 * frequencyStart + n * (frequencyEnd - frequencyStart) / numberOfSamples) / sampleRate * n), 7)
           ...hier kann nun val weiterverarbeitet werden, z.B. Wert in eine Liste einfügen...
        Wend
    

    Die obere Schleife mit frequencyEnd=frequencyStart=440 und sampleRate=9600 und numberOfSamples=sampleRate*dauer=sampleRate*0,05 sollte dir die richtigen PCM Werte erstellen. Und dieses PCM Array kannst du nun an eine libsndfile Funktion übergeben, diese speichert diese dann als wav ab.



  • Zweiteres, ich will zur Laufzeit dynamisch neue wav Dateien erzeugen, bzw. die bestehende Datei überschreiben.

    Zur Info:
    Ich bekomme mehrmals pro Sekunde einen sich ändernen Qualitätsparameter. Je nach der Qualität 50 = brauchbar bis 100 = optimal soll sich die Tonhöhe des Beeptones
    ändern. Mit der Beep(f,d) Funktion war das ein Kinderspiel.

    Jetzt möchte ich dafür winmm verwenden. Und davon die PlaySound Methode und dieser eben das wav file übergeben. Ist ein wenig mit der Kirche ums Haus aber ich habe bis jetzt noch keine schönere Lösung gefunden.

    Auf libsndfile möcht ich verzichten und stattdessen die wav "per Hand" erstellen.
    Den Header (siehe vorher) kann ich ja, denke ich fast 1:1 übernehmen. Nur bei den Daten (samples) tu ich mir noch ein wenig hart...

    Und ja VBA Code erzeugt wirklich ein wenig Augenschmerzen 😉



  • Wo ist das Problem an den Samples?
    Du kannst den VBA Code sogar noch vereinfachen!

    Ungetesteter C-ähnlicher Pseudocode (die dynamische Array Erstellung programmier ich jetzt sicher nicht aus;-)) für 1 Channel, SampleRate 9600Hz, Dauer 1s.

    long sampleRate=9600,duration=1,numberOfSamples=duration*sampleRate;
    long amplitude=10000;//lautstärke
    long frequency=440;//Frequenz
    signed short data[numberOfSamples];//hab jetzt signed short im Gegensatz zu double im VBA Code lt. Header(siehe 1. Seite im Thread)
    for(long i=0;i<numberOfSamples;i++)
    {
    data[i]=(signed short)amplitude * Sin(2 * PI * frequency / sampleRate * i)
    }
    

    In data hast du jetzt dein "Geräusch" drinnen, das speicherst du dann nach dem Header direkt dahinter in der Datei ab.
    Deine frequency Variable kannst du auf die gleiche Art berechnen wie du es bei Beep(...) getan hast.
    Sollte jetzt echt nicht mehr schwierig sein das ganze zu implementieren!



  • Jetzt klappts, dankeschön und sorry dass ich ein etwas schwieriger Patient war 😉

    Die .wav Datei wird jetzt nach meinen Wünschen erstellt. Ich kann sie auch mit z.B. Windows Media Player wiedergeben. Nur die PlaySound Methode macht jetzt noch Zicken. Ich hab schon in anderen Foren gelesen dass die Funktion bei "falschen" Headern versagt und nur einen kurzen Fehlton anstatt des .wav files ausgibt. Das ist jetzt auch bei mir der Fall.

    Sound::wav_header Sound::createWavHeader( int Channels, int SampleRate, int BitRate, int Samples )
    	{ 
    		const int FileSize = 44 + BitRate/8 * Channels * Samples; 
    		wav_header Header; 
    		Header.RIFF = 'FFIR'; 
    		Header.RiffSize = FileSize - 8; 
    		Header.WAVE = 'EVAW'; 
    		Header.FMT = ' tmf'; 
    		Header.FmtSize = 16; 
    		Header.Format = 1; 
    		Header.Channels = Channels; 
    		Header.SamplesPerSecond = SampleRate; 
    		Header.BitsPerSample = 16; 
    		Header.BytesPerSecond = Header.SamplesPerSecond * Header.BitsPerSample/8 * Header.Channels; 
    		Header.BlockAlign = Channels*(BitRate/8); 
    		Header.DATA = 'atad'; 
    		Header.DataSize = FileSize - 40; 
    		return Header; 
    	}
    

    Die Methode wird mit 1 für Channels, 9600 für SampleRate, 16 für BitRate und 9600 für Samples aufgerufen. Keine Ahnung was hier der PlaySound Methode nicht passen könnte. Wenn ich bei winmm bleiben will muss ich die Lösung noch finden...

    Danke nochmals!



  • Ich möchte dich ja nicht nerven, aber mit libsndfile hab ich das Abspeichern in einer recht kompakten Funktion erledigt, und hab mir keine Gedanken über einen falschen Header machen müssen! Ich kann dir diese Lib nur ans Herz legen, man muss das Rad ja nicht jedes Mal neu erfinden.

    bool WavFileIO::writeWav(string filename)
    {
        filename=OUTSUBDIR+filename;
        if (!sound->getData())
            return false;
    
        SndfileHandle outfile(filename.c_str(), SFM_WRITE, SF_FORMAT_WAV | SF_FORMAT_PCM_16, sound->getChannels(), sound->getSampleRate());
        outfile.write(sound->getData(), sound->getSize());
    
        return true;
    }
    

    In Zeile 7+8 rufe ich die Lib Funktionen auf.
    wobei sound eine Klasse von mir ist die alle Infos zu dem zu erzeugenden Geräusch enthält, siehe Member Vars:

    double* data;
        long size, sampleRate, channels;
    


  • Ich habs mir mal ein wenig angeschaut. Aber die Zeile 7 versteh ich nicht ganz...
    Was ist SndfileHandle? Ein SNDFILE * oder ein Datentyp? Was ist outfile (eine Funktion? Ein FILE * handle? Für beide bekomm ich "undeclared identifier" wenn ich die Zeile so verwende...



  • saerdna schrieb:

    Ich bekomme mehrmals pro Sekunde einen sich ändernen Qualitätsparameter. Je nach der Qualität 50 = brauchbar bis 100 = optimal soll sich die Tonhöhe des Beeptones
    ändern. Mit der Beep(f,d) Funktion war das ein Kinderspiel.

    Das klingt mir eigentlich so als möchtest du einfach nur einen endlosen, generierten Sound abspielen und nicht wirklich irgendwas in .wav Dateien speichern!? Warum kannst du diesen Sound nicht einfach in einen Buffer packen und von dort streamen und fertig, wofür genau der Umweg über .wav Dateien!?



  • Ich weiss, der Umweg über die .wav Datei ist nicht unbedingt notwendig, aber ich wollte es auch so ausprobieren. Nebenbei versuche ich aber auch mit midiOutShortMsg mein gewünschtes Resultat zu erzeugen. Klappt aber bis jetzt noch nicht...



  • Was genau willst du da mit MIDI!?

    http://msdn.microsoft.com/en-us/library/dd371455.aspx 😉

    Oder wenns auf älteren Windows-Versionen laufen soll:
    http://msdn.microsoft.com/en-us/library/dd743832.aspx



  • saerdna schrieb:

    Ich habs mir mal ein wenig angeschaut. Aber die Zeile 7 versteh ich nicht ganz...
    Was ist SndfileHandle? Ein SNDFILE * oder ein Datentyp? Was ist outfile (eine Funktion? Ein FILE * handle? Für beide bekomm ich "undeclared identifier" wenn ich die Zeile so verwende...

    Du musst dafür natürlich die libsndfile installieren, also sowohl Header als auch Bibliothek selber mit deinem Projekt verknüpfen!

    SndfileHandle ist eine Klasse und outfile (...) der Variablenname mit dem Konstruktoraufruf.
    Entspricht also bezogen auf primitive Datentypen etwa int i=0



  • Wieder ein neues API 😉
    Mir wurde midiOut ans Herz gelegt und ich möchte das jetzt auch erstmal damit lösen - genauer gesagt mit midiOutShortMsg. Ich habe keine Ahnung warum es im Moment noch nicht funktioniert. Ich denke schon dass ich alles richtig mache:

    [/cpp]
    unsigned long result;
    HMIDIOUT outHandle;

    result = midiOutOpen(&outHandle, MIDI_MAPPER, 0, 0, CALLBACK_NULL);

    if (result == MMSYSERR_NOERROR)
    {
    result = midiOutShortMsg(outHandle, 0x00404090);
    Sleep(1000);
    midiOutClose(outHandle);
    }
    [cpp]


Anmelden zum Antworten