Erzeugen von DTMF Tönen



  • CrazyPlaya schrieb:

    Ah danke schön....warum *63?

    sin_freq sehe ich das richtig wäre z.B. die freq für die z.B. 1 im low Bereich also 697?

    damit 128-2*63 bis 128+2*63, also 2 bis 254
    mit 64 wärs nicht mehr gepaßt haben tun getan (0 bis 256).



  • CrazyPlaya schrieb:

    Ah danke schön....warum *63?

    kannst auch 63.75 nehmen. 🙂
    Damit landest Du zwischen 0.5 und 255.5, was dann durch das Abwärtsrunden von static_cast<unsigned char> den vollen 8bit-Bereich 0...255 ausnutzt.

    CrazyPlaya schrieb:

    sin_freq sehe ich das richtig wäre z.B. die freq für die z.B. 1 im low Bereich also 697?

    Ja, wobei sampling_freq dann 8000 wäre.

    Es hat mich etwas in den Fingern gejuckt: Habe mal einen schnellen Sinusgenerator geschrieben, der mit 2.2 FLOPS pro Sample auskommt und "stabil" läuft:

    Edit: neue Version

    sine.hpp

    #ifndef SINE_GENERATOR_HPP_INCLUDED
    #define SINE_GENERATOR_HPP_INCLUDED
    
    #include <cmath>
    #include <complex>
    
    namespace sin_gen_details { // "action" functors ...
    
    struct replace
    {
    	template<typename Iter>
    	void operator()(Iter & it, double sample) const
    	{
    		*it = sample;
    		++it;
    	}
    };
    
    template<typename BinOp>
    struct binop_wrapper
    {
    	BinOp binop;
    
    	binop_wrapper(BinOp const& bo) : binop(bo) {}
    
    	template<typename Iter>
    	void operator()(Iter & it, double sample) const
    	{
    		*it = binop(*it, sample);
    		++it;
    	}
    };
    
    } // namespace sine_gen_details
    
    class sine_generator
    {
    	std::complex<double> phase;
    	std::complex<double> rotation;
    
    	template<typename Iter, typename Action>
    	void internal_generate(int count, Iter begin, Action x);
    
    public:
    	sine_generator() : phase(1),rotation(1) {}
    
    	void set_frequency(double rad)
    	{ rotation = std::polar(double(1),rad); }
    
    	void reset_phase(double rad)
    	{ phase = std::polar(double(1),rad); }
    
    	template<typename OutputIter>
    	void generate(int count, OutputIter begin)
    	{
    		internal_generate(count,begin,sin_gen_details::replace());
    	}
    
    	template<typename ForwardIter, typename BinOp>
    	void generate(int count, ForwardIter begin, BinOp binop)
    	{
    		internal_generate(count,begin,
    			sin_gen_details::binop_wrapper<BinOp>(binop) );
    	}
    };
    
    template<typename Iter, typename Action>
    void sine_generator::internal_generate(int count, Iter begin, Action action)
    {
    	std::complex<double> rot_forx = pow(rotation,12);
    	phase /= abs(phase); // prevent drifting off due to rounding errors
    	// use linear recurrence for generating the samples
    	double const fy = 2 * real(rotation);
    	double const fx = 2 * real(rot_forx);
    	double y0 = imag(phase);
    	double x0 = real(phase);
    	double y1 = imag(phase*conj(rotation));
    	double x1 = real(phase*conj(rot_forx));
    	double tt; // temporary variable
    	while (count >= 12) {
    		// 4*3 = 12 samples in a row ...
    		for (int k=0; k<4; ++k) {
    			// output       | compute next sample
    			action(begin,y0); tt = fy*y0-y1;
    			action(begin,tt); y1 = fy*tt-y0;
    			action(begin,y1); y0 = fy*y1-tt;
    		}
    		// update x as well ...
    		tt = fx*x0-x1; x1 = x0; x0 = tt;
    		count -= 12;
    	}
    	phase = std::complex<double>(x0,y0);
    	while (count-- > 0) {
    		action(begin,imag(phase));
    		phase *= rotation;
    	}
    }
    
    #endif // SINE_GENERATOR_HPP_INCLUDED
    

    test.cpp

    #include <iostream>
    #include <vector>
    #include <functional>
    
    #include "sine.hpp"
    
    using std::vector;
    using std::cout;
    using std::plus;
    
    int main()
    {
    	const int buffsize = 160;
    	vector<double> buffer(buffsize);
    
    	sine_generator sg;
    
    	sg.set_frequency(440 * 2*3.141592653589793 / 8000);
    	sg.reset_phase(0);
    	// overwrite buffer with sine wave ...
    	sg.generate(buffsize,buffer.begin());
    
    	sg.set_frequency(880 * 2*3.141592653589793 / 8000);
    	sg.reset_phase(0);
    	// mix buffer with sine wave ...
    	sg.generate(buffsize,buffer.begin(),plus<double>());
    
    	for (int i=0; i<buffsize; ++i) {
    		cout << buffer.at(i) << '\n';
    	}
    }
    

    Gruß,
    SP



  • Wow...darf ich annehmen das du dich öfters mit sowas befasst?



  • CrazyPlaya schrieb:

    Wow...darf ich annehmen das du dich öfters mit sowas befasst?

    Das ist schon 'ne Weile her, aber ich habe mal Software-Synthesizer programmiert und bin recht fit, was digitale Signalverarbeitung angeht.



  • 😃 Gut zu wissen, ich fang gerade damit an:)

    Also mit DSV...und meine erste Ausgabe ist gleich aus nem Audiostream zu DTMF Töne herauszuerkennen aber um meinen Filter zu testen muss ich diese erstmal erzeugen.



  • @Sebastian Pizer
    Hab da mal ein paar Fragen zu dem Code.

    Die Methode rotate_inplace, wozu ist das gut?

    Kannst du die Methode generate etwas näher erklären was genau du machst?
    Wie gesagt, ich fang gerad erst an mit DSV und hab noch nicht so ganz den Überblick.



  • CrazyPlaya schrieb:

    Hab da mal ein paar Fragen zu dem Code.
    Die Methode rotate_inplace, wozu ist das gut?

    Rotiert einen Punkt (x;y) "in-place" (also auf der Stelle, verändert direkt x und y) über gegebenen Sinus (s) und Cosinus (c).

    | c -s |   | x |      | x |
    |      | * |   |  --> |   |
    | s  c |   | y |      | y |
    

    CrazyPlaya schrieb:

    Kannst du die Methode generate etwas näher erklären was genau du machst?

    Das ist eine Kombination aus der Rekurrenzformel und dem Rotieren eines Punktes in 2D. Im Zustand merke ich mir den letzten Punkt über x0/y0 und die y-Koordinate des Punktes davor, y1. c und s speichert Cosinus und Sinus des Drehwinkels, mit dem die Frequenz geregelt wird.

    generate berechnet 12 Samples (die y-Werte) über die Rekurrenzgleichung und wendet die Rekurrenzgleichung auch einmal an, für die 12fache Frequenz, um den letzten x-wert zu berechnen. Der x-Wert ist praktisch, wenn man die Frequenz ändern will, oder die Amplitude normalisieren will. Da die x-Werte aber sonst nicht benötigt werden, muss ich nicht so viele davon berechnen. Im Beispiel von oben tue ich das im Regelfall nur einmal pro 12 Samples -- den Schluss mal ignoriert.

    Gruß,
    SP



  • Ja...hm... 😃

    Ich muss noch ne Menge lernen in dem Bereich, ich merk das schon 😉

    Aber besten Dank...ich werd mal versuchen das alles nachzuvollziehen

    Gruß
    Karsten



  • CrazyPlaya schrieb:

    Ja...hm... 😃
    Ich muss noch ne Menge lernen in dem Bereich, ich merk das schon 😉
    Aber besten Dank...ich werd mal versuchen das alles nachzuvollziehen

    Ich habe die Version auf die Nutzung von std::complex umgestellt. Das ist nun etwas eleganter und wahrscheinlich auch leichter nachvollziehbar, wenn man komplexe Zahlen kennt. Als angehender DSVler muss man sich früher oder später sowieso mit komplexen Zahlen auseinandersetzen. 🙂

    Grup,
    SP



  • Nicht Schlecht Herr Specht.
    Zu den komplexen Zahlen muss ich mich erst noch vorarbeiten...da wird noch eine Weile ins Land gehen bis ich da angelangt bin.

    Mir graut jetzt schon davor 😃 .


Anmelden zum Antworten