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
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...
1 | #include <math.h> |
2 | ...
|
3 | double frequenz; |
4 | ...
|
5 | 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.
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
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.
Ü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
1 | //
|
2 | // Berechnet 2 hoch x
|
3 | //
|
4 | int myPow2( int x ) |
5 | {
|
6 | int i; |
7 | int Result = 1; |
8 | |
9 | for( i = 0; i < x; ++i ) |
10 | Result = 2 * Result; |
11 | |
12 | return Result; |
13 | }
|
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.
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.
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
Ich glaube das eigentliche Rechnen wird auch nicht viel kleiner. Tabelle ist für die wenigen Daten sicher die schnellste Lösung.
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
Vorschlag zum Kompromiss:
1 | #include <stdint.h> |
2 | |
3 | double midifreq(uint8_t note) { |
4 | static double factors[12] = { |
5 | 8.17579891564, /* midifreq(0) */ |
6 | 8.66195721803, /* midifreq(0) * 2 ** (1/12) */ |
7 | 9.17702399742, /* midifreq(0) * 2 ** (2/12) */ |
8 | 9.72271824132, |
9 | 10.3008611535, |
10 | 10.9133822323, |
11 | 11.5623257097, |
12 | 12.2498573744, |
13 | 12.9782717994, |
14 | 13.7500000000, |
15 | 14.5676175474, |
16 | 15.4338531643 /* midifreq(0) * 2 ** (11/12) */ |
17 | };
|
18 | |
19 | return factors[note % 12] * (1 << (note / 12)); |
20 | }
|
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.
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.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.