www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Hochzahlen?


Autor: Tina (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo bin noch Anfänger in Sachen C und hab mal an euch hier eine Frage.
Also ich benötige um Midi Noten Nummern in die jeweilige Frequenz zu 
berechnen Hochzahlen.
(Vieleicht kann das ja hier jemand als Code Schnipsel gebrauchen :-))

Die Berechnung lautet: 440 * 2^((Midinote - 69) / 12)
(zur Berechnung dient dabei a4 440Hz und Midi-Notennummer 69)
und meine Frage bezieht sich jetzt, wie man dies in C umsetzt, denn das
Zeichen ^ funktioniert leider nicht :-/.

schon mal vielen Dank im Vorraus für eure Hilfe

Tina

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zählschleife, die (Midinote - 69) / 12) durchlaufen wird (geht halt nur 
für ganze Zahlen in der Potenz) und in jedem Durchlauf dem Ergebnis mit 
dem Startwert 440 den Faktor 2 verpasst...

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
#include <math.h>
...
double frequenz;
...
frequenz = 440 * pow(2, (Midinote - 69) / 12.0);
Die Berechnung erfolgt hier (bis auf die Subtraktion) in Fließkomma,
deswegen auch 12.0 statt der 12. pow() ist die Potenzfunktion aus der
C-Standardbibliothek.

Autor: Tina (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Prima danke!!!

aber als #define kann man es dem Compiler nicht direkt klar machen oder?

sowas wie ein Makro z.B.
#define setfrequenz (notenum) (440 * 2^(notenum/12))
(natürlich wenn es so richtig geschrieben wäre ;-) )

tina

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tina wrote:
> Prima danke!!!
>
> aber als #define kann man es dem Compiler nicht direkt klar machen oder?
>

Warum soll man dafür kein #define machen können.

Ein #define bewirkt nichts anderes als eine Textersetzung
im Quelltext.

Wenn dein Programm so aussieht:


#include <math.h>

int main()
{
  double frequenz;
  frequenz = 440 * pow(2, (Midinote - 69) / 12.0);
}

dann kannst du jeden beli belibigen Abschnitt davon heraus-
ziehen und dafür ein Makro machen:

zb. möchte ich für Midinote - 69 die Bezeichnung MIDI_A
haben. MIDI_A hat logischerweise ein Argument, nämlich
die zu bearbeitende Midinote

also
#define MIDI_A(x)    ( x - 69 )

und als Programm schreibe ich dann:

#include <math.h>

#define MIDI_A(x)  ( x - 69 )
int main()
{
  double frequenz;
  frequenz = 440 * pow(2, MIDI_A(Midinote) / 12.0);
}

Das hats nicht wirklich gebracht :-)
Besser wäre es doch, anstelle der ganzen Formel ein
Makro zu machen. Wenn schon, denn schon
Also: ich möchte gerne den Text

   440 * pow( 2, ( Midinote - 69 ) / 12.0 )

durch ein Makro abdecken, wobei dieses Makro einen
Parameter hat:

#include <math.h>

#define TO_FREQ(x) ( 440 * pow( 2, ( x - 69 ) / 12.0 ) )

int main()
{
  double frequenz;
  frequenz = TO_FREQ(Midinote);
}

Also: machen kann man das schon.
Die sehr viel wichtigere Frage ist aber: Will man denn das
so machen?
Es ist OK, wenn man einfache Ausdrücke oder Konstane per
Makro vom Präprozessor ersetzen lässt. Für komplexere Dinge,
wie diese Berechnung ist es besser eine Funktion zu definieren:

#include <math.h>

double toFreq( unsigned char Note )
{
  return  440 * pow( 2, ( x - 69 ) / 12.0 );
}

int main()
{
  double frequenz;
  frequenz = toFreq( Midinote );
}

Ist auf lange Sicht sehr viel sicherer.

Nur als Beispiel:
Wenn ich da oben zb. definiert hätte

#include <math.h>

#define MIDI_A(x)  x - 69
int main()
{
  double frequenz;
  frequenz = 440 * pow(2, MIDI_A(Midinote) / 12.0);
}

dann hätte ich einen schwer zu findenden Fehler eingebaut.
Um den zu sehen, machen wir mal genau das, was auch der
Präprozessor macht: Eine textuelle Ersetzung des Makros
bei seiner Verwendung durch den Inhalt.

Ersetze also mal in

440 * pow(2, MIDI_A(Midinote) / 12.0);

den Text 'MIDI_A(Midinote)'
durch den Ersetzungstext 'x - 69' wobei für x Midinote eingesetzt
wird. Man erhält

440 * pow(2, Midinote - 69 / 12.0 );

Und jetzt überlege mal genau, wie hier die Punkt vor Strich
Rechnung ein anderes Ergebnis liefert als für

440 * pow(2, ( Midinote - 69 ) / 12.0 );

Durch Betrachten von

#define MIDI_A(x)  x - 69

  frequenz = 440 * pow(2, MIDI_A(Midinote) / 12.0);

ist diese Falle aber nicht unmittelbar zu sehen.

Makros sind Textersetzungen! Sie kümmern sich nicht
um C Regeln!
Für Konstante oder einfache Ausdrücke mag das OK sein.
Für alles andere nimmt man aber besser Funktionen.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Übrigens:
Für dein Beispiel nimmst du besser die Variante
mit den Schleifen anstelle der pow Funktion.

Du kannst dir auch schön eine Funktion schreiben
//
// Berechnet 2 hoch x
//
int myPow2( int x )
{
  int i;
  int Result = 1;

  for( i = 0; i < x; ++i )
    Result = 2 * Result;

  return Result;
}

  

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wollte gerade auch ne Antwort posten, haber aber gerade noch
rechtzeitig die Superallesumfassendehundertzwanzigprozentantwort von
Karl Heinz gesehen. Der gibt es nun wirklich absolut nichts mehr
hinzuzufügen :D (wie übrigens bei den meisten seiner Antworten)

Huch da ist ja schon der nächste Beitrag von Karl Heinz. Diesem hätte
ich aber doch noch etwas hinzuzufügen:

@Karl Heinz:

Tina will Zweierpotenzen mit gebrochenen Exponenten berechnen, da
bringt deine myPow2-Funktion nicht so arg viel.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
yalu wrote:
> Tina will Zweierpotenzen mit gebrochenen Exponenten berechnen, da
> bringt deine myPow2-Funktion nicht so arg viel.

Mea culpa.
Ich hatte nur noch die Midinote als Ganzzahl im Hinterkopf.
Die Division durch 12 hab ich glatt verschlafen.

Also: Wie yalu richtig sagt: Mit Schleifen wird das nichts.

Autor: Tina (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke nochmal!

Nein geht leider nicht mit ganzahligen Werten :-( und ich frage mich 
jetzt, ob ein Avr mit 16Mhz in einer Zeit von maximal 96µs (z.B. 3* 32µs 
minimale Zeit, zwischen zwei NoteOn Befehlen), das alleine berechnen 
kann, oder aber es nicht doch besser wäre, die Frequenzen in eine 
Tabelle abzulegen. Leider würde mich dies bei 128 Noten dann min. 256 
bytes bei int und 512 bytes bei float kosten .. muss ich mal abwägen :-/

Tina


Autor: Thomas S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaube das eigentliche Rechnen wird auch nicht viel kleiner. Tabelle 
ist für die wenigen Daten sicher die schnellste Lösung.

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So hat man das früher angenähert, ein Top-Octave-Synthesizer für 
Elektronische Orgeln von SGS 1981. Die 12 Frequenzteiler liefern 
angenähert Frequenzen im Abstand der 12.Wurzel von 2

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vorschlag zum Kompromiss:
#include <stdint.h>

double midifreq(uint8_t note) {
  static double factors[12] = {
    8.17579891564, /* midifreq(0)               */
    8.66195721803, /* midifreq(0) * 2 ** (1/12) */
    9.17702399742, /* midifreq(0) * 2 ** (2/12) */
    9.72271824132,
    10.3008611535,
    10.9133822323,
    11.5623257097,
    12.2498573744,
    12.9782717994,
    13.7500000000,
    14.5676175474,
    15.4338531643  /* midifreq(0) * 2 ** (11/12) */
  };

  return factors[note % 12] * (1 << (note / 12));
}
Diese Lösung ist wesentlich schneller als die pow-Lösung und benötigt 
eine deutlich kleinere Tabelle als die reine Tabellenlösung. Sie ist gut 
für einen Wertebereich für note von 0..255. Wird mehr gebraucht, kann 
der Typ von note uint8_t in uint16_t geändert werden. Dann benötigen die 
beiden Integer-Divisionen etwas länger.

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zur Ergänzung, der Hersteller empfiehlt eine Taktfrequenz vn 2,00024 MHz 
oder für die schnelleren Typen ohne -A 4,00048 MHz, damit liegt die 
höchste Frequenz auf der Note "C".

Für diese Methode würde das Progamm also eine Halbton-Tabelle von 12 
16-Bit Zahlen enthalten, zweckmäßig die 16-Bit-Timerwerte für die 
unterste Oktave. Für höhere Oktaven werden die Bits nach rechts 
geschoben.
Die Midinotennummer wird durch 12 geteilt, das ganzzahlige Ergebnis ist 
die Oktave, der Teilerrest der Halbton. Mit den 9 Bit-Zahlen aus dem 
Datenblatt bleiben rechts noch 7 Null-Bits zum Schieben, also 8 Oktaven 
ohne Verlust, ab der 9. Oktave wird die Frequenz ungenauer.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.