Lösung des Problems: Soundkarte + Frequenz + Ton Ausgabe



  • Hi,
    nachdem ich mir einen Wolf gesucht habe, wie man ohne ewige WinApi-Zuckelei oder ganz tolle TDX-Komponenten schnell und bequem einen Sinus über die Soundkarte (nicht über den PC-Speaker) ausgeben könnte und nicht fündig wurde, habe ich mich selbst hingesetzt und einige spärliche Quellen zusammengetragen.

    Admins: es tut mir wirklich leid, wenn ich hier so deskreditierend bin, aber das *muss* gesagt werden: ich suchte nach einer Lösung die ohne die bescheuerten TDX-Libraries von BCB-Tools auskommt, weil mich deren Website zum Wahnsinn treibt. Weiterhin ist es so, dass die dann auch noch eigene Kompressionsformate (.PIT-Dateien) verwenden. Diese PIT Dateien kann man nur mit einem ganz tollen Installer (den man sich natürlich auch noch auf der unüberschaubaren Website downloaden muss) installieren. Aber das geht natürlich nur hin und wieder gut, zum Beispiel wenn man alles außer BCB6 auf dem System hat. Der tolle Installer führt nämlich einen Autodetect durch, ob du auch *ja* einen BCB ver 1-5 installiert hast. Ansonsten kommt man an die begehrten Komponenten garnicht erst heran. Solche Überprüfungen sind sowas von absolut überflüssig und wiederspiegeln die Gefälligkeit und die Selbstverherrlichung der Autoren! Bei einem Systemtreiber kann ich sowas ja nachvollziehen, aber bei sowas...ne...ach ich könnt' mich nur aufregen!

    Gut - also zum Code. Er ist mehr oder weniger zusammengeflickt. Sorry, wenn er schlecht kommentiert und ggf schlecht kommentiert ist.

    #include <mmsystem.h>
    #include <dsound.h>
    #include <math.h>
    
    //Frequency in Hz, Duration in msecs
    //Prinzip: erstelle im Memory einen Block mit Werten, die dann letztendlich in den ADC der Soundkarte gelangen. Gebe diesen Block über sndPlaySound aus.
    void sinus_out(int Frequency, int Duration, int Volume)
    {
            tWAVEFORMATEX WaveFormatEx;
            TMemoryStream *MS = new TMemoryStream();
            int i, TempInt, DataCount, RiffCount;
            Byte SoundValue;
            double w;
    
            Byte Mono = 0x01;
            int SampleRate = 11025; // 8000, 11025, 22050, or 44100
            AnsiString RiffId = "RIFF";
            AnsiString WaveId = "WAVE";
            AnsiString FmtId = "fmt "; //<- man beachte das Leerzeichen am Ende: ein Muss!
            AnsiString DataId = "data";
    
                    if (Frequency > (0.6 * SampleRate))
            {
                    Application->MessageBoxA("Zu niedrige Sampling-Rate","Niquist-Error",0);
                    Application->Terminate();
            }
    
            WaveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
            WaveFormatEx.nChannels = Mono;
            WaveFormatEx.nSamplesPerSec = SampleRate;
            WaveFormatEx.wBitsPerSample = 0x08;
            WaveFormatEx.nBlockAlign = (WaveFormatEx.nChannels * WaveFormatEx.wBitsPerSample) / 8;
            WaveFormatEx.nAvgBytesPerSec = WaveFormatEx.nSamplesPerSec * WaveFormatEx.nBlockAlign;
            WaveFormatEx.cbSize = 0;
    
            DataCount = (Duration * SampleRate) / 1000;
            RiffCount = WaveId.Length() + FmtId.Length() + sizeof(int) + sizeof(tWAVEFORMATEX) + DataId.Length() + sizeof(int) + DataCount;
    
            MS->Write(&RiffId[1], 4); // 'RIFF'
            MS->Write(&RiffCount, sizeof(int)); // file data size
            MS->Write(&WaveId[1], WaveId.Length()); // 'WAVE'
            MS->Write(&FmtId[1], FmtId.Length()); // 'fmt '
            TempInt = sizeof(tWAVEFORMATEX);
            MS->Write(&TempInt, sizeof(int)); // TWaveFormat data size
            MS->Write(&WaveFormatEx, sizeof(tWAVEFORMATEX)); // WaveFormatEx record
            MS->Write(&DataId[1], DataId.Length()); // 'data'
            MS->Write(&DataCount, sizeof(int)); // sound data size
    
            //berechne und schreibe das signal
            w = 2 * M_PI * Frequency; // omega
            for (i=0; i<DataCount - 1; i++)
            {
                    SoundValue = 127 + (int) (Volume * sin(i * w / SampleRate)); // wt = w * i / SampleRate
                    MS->Write(&SoundValue, sizeof(Byte));
            }
    
            sndPlaySound((LPSTR)MS->Memory, SND_MEMORY | SND_SYNC);
    
            delete MS;
    }
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
            sinus_out(440,1000,60);
    }
    //---------------------------------------------------------------------------
    

    Ansonsten hab ichhoffentlich ein paar Leuten geholfen. Wenn nicht, war halt alles umsonst 😉 ... gratis isses ja sowieso (entgegen BCB-Tools.com)
    Viele Grüße,
    Simon



  • Das hier ist zwar Delphi, lässt sich aber ganz leicht auf C++ umschreiben, ist nicht alleine von mir, vieles aus dem Internet.

    procedure MakeSound(Frequency, Duration : integer);
    var
      WaveFormatEx : TWaveFormatEx;
      MS           : TMemoryStream;
      i, TempInt,
      DataCount,
      RiffCount    : integer;
      SoundValue   : byte;
      w            : double; // omega ( 2 * pi * f)
    const
      Mono       : Word = $0001;
      SampleRate : integer = 11025; // 8000, 11025, 22050, or 44100
      RiffId     : string = 'RIFF';
      WaveId     : string = 'WAVE';
      FmtId      : string = 'fmt ';
      DataId     : string = 'data';
    begin
      with WaveFormatEx do begin
        wFormatTag := WAVE_FORMAT_PCM;
        nChannels := Mono;
        nSamplesPerSec := SampleRate;
        wBitsPerSample := $0008;
        nAvgBytesPerSec := nSamplesPerSec * nBlockAlign;
        nBlockAlign := (nChannels * wBitsPerSample) div 8;
        cbSize := 0;
      end;
      MS := TMemoryStream.Create;
      with MS do
      begin
        //Länge des Tones bestimmen
        DataCount := (Duration *  SampleRate) div 1000;  
        RiffCount := Length(WaveId)
                     + Length(FmtId) + SizeOf(DWord)
                     + SizeOf(TWaveFormatEx)
                     + Length(DataId) + SizeOf(DWord)
                     + DataCount;
    
        Write(RiffId[1], 4);                        // 'RIFF'
        Write(RiffCount, SizeOf(DWord));            // Dateigröße
        Write(WaveId[1], Length(WaveId));           // 'WAVE'
        Write(FmtId[1], Length(FmtId));             // 'fmt '
        TempInt := SizeOf(TWaveFormatEx);
        Write(TempInt, SizeOf(DWord));              // TWaveFormat größe
        Write(WaveFormatEx, SizeOf(TWaveFormatEx)); // WaveFormatEx record
        Write(DataId[1], Length(DataId));           // 'data'
        Write(DataCount, SizeOf(DWord));            // soundgröße
    
        //tonsignal beschreiben                     // Datenwerte
        w := 2 * Pi * Frequency;  // omega
        for i := 0 to DataCount - 1 do begin
          // wt = w *i / SampleRate
          SoundValue := 127 + trunc(127 * sin(i * w / SampleRate));
          Write(SoundValue, SizeOf(Byte));
        end;
    
        //Sound aus Speicher abspielen
        sndPlaySound(MS.Memory, SND_MEMORY or SND_SYNC);
        MS.Free;
      end;
    end;
    

Anmelden zum Antworten