Calliope Sound-Recorder    

           
Elektronik-Labor   Projekte   Microbit 




Video: https://youtu.be/CHs2OTMw69Y

Dieser Sound-Recorder mit einer Aufnahmezeit von einer Sekunde wurde mit Mbed entwickelt. Die Sound-Daten werden in einem Array mit 2500 Bytes gespeichert. Die Abtastrate wurde auf unter 2 kHz eingestellt, damit die Aufnahmedauer ausreicht. Und beim Abspielen kann man auch etwas länger warten und so einen tieferen Ton abspielen.




#include "mbed.h"

DigitalOut col0(P0_4, 0);
DigitalOut myled(p13);
//DigitalOut sl(p28, 0);
DigitalOut inl(p29, 0);
DigitalOut in2(p30, 1);
DigitalIn tasteA (p17);
DigitalIn tasteB (p16);

AnalogIn u2(p3); //P3 = MIC
PwmOut pwm1(p28);


int main() {
// float d[1000];
uint8_t d[2500];
uint16_t n=0;
pwm1.period_us(50);
while(1){
if (tasteA.read()==0){
myled = 1;
for (n=0; n<2500;n++){
d[n]= int(u2.read()*255);
wait_us(500);
}
myled = 0;
}
if (tasteB.read()==0){
for (n=0; n<2500;n++){
//printf("%d\r\n",n);
pwm1.write(float(d[n])/255);
wait_us(500);
// printf("%f\r\n",255*d[n]);
}
}
}
}


Hier gibt es drei fertig übersetzte Hex-Dateien für unterschiedliche Abspielklänge, hoch, normal und tief. Man kann das jeweils gewünschte Programm direkt in den Calliope kopieren.  Download: Recorder.zip




Probleme mit neuer Software

Das Beispiel habe ich auch in meinem Buch: Calliope und Micro:bit in der Praxis verwendet, dann aber längere Zeit nicht mehr getestet. Jetzt kam dazu eine Rückmeldung von Thorsten Kimmeskamp:

Im o. g. Buch ist mit dem Sound Recorder ein spannendes Projekt enthalten, das ich gerne zum Laufen bringen würde. Das zur Verfügung gestellte Hex-File scheint jedoch zumindest mit der aktuellen Calliope-Version nicht zu funktionieren. Ebenso lässt sich der Quellcode in der Mbed-IDE in dieser Form nicht (mehr) kompilieren. Zumindest nicht alleine mit mbed.h. Mit dem microbit-DAL dazwischen schon, aber dann wird vermutlich die Pin-Belegung nicht stimmen, jedenfalls ließ sich über die serielle Schnittstelle „mitschneiden“, dass das Array d auch nach einer Aufnahme mit 0en gefüllt ist. Haben Sie einen Rat, was ich vielleicht falsch mache? Über eine Antwort würde ich mich sehr freuen!

(B.K: Meine erster Gedanke: Das ist ja grausam. Ich dachte, das Hex wird immer laufen, während die Compiler sich ändern können. Auch dass die Calliope-Firmware sich stark ändern könnte, hatte ich nicht erwartet. Mit den alten Hex-Files an der alten Platine funktioniert es noch. Die PWM-Frequenz wurde mit 20 kHz gemessen.)


Die Lösung des Problems kam ebenfalls von Thorsten Kimmeskamp (thorsten.kimmeskamp@uni-due.de)

Juhu, mit einem 8 Ohm-Kleinlautsprecher von Conrad funktioniert jetzt tatsächlich alles einwandfrei und sogar mit ziemlich annehmbarer Soundqualität :-)

Der Code müsste mit allen neuen Calliopes funktionieren. Anbei die aktuellste Version. Der Teil mit der Ausgabe auf die serielle Schnittstelle ist auskommentiert; hält ja nur auf, wenn man ihn nicht braucht. Aus diesen Rohdaten eine .WAV-Datei zu basteln, ist dann etwas „Handarbeit“, aber mit einem Programm wie Audacity kriegt man es gut hin.

#include "mbed.h"

DigitalOut col0(P0_4, 0);
DigitalOut myled(p13);

DigitalOut nSleep(p28, 0); // P28 = Motortreiber enable
PwmOut in1(p29); // P29 = Motortreiber Eingang 1
DigitalOut in2(p30, 1); // P30 = Motortreiber Eingang 2

AnalogIn mic(p3); // P3 = MIC

DigitalIn tasteA(p17);
DigitalIn tasteB(p16);

DigitalOut pin1(p1, 0); // Port als Widerstand benutzen

int main() {
uint16_t len = 4000; // Anzahl Samples, Speicher reicht fuer max. 5000
uint16_t interval = 500; // Abtastintervall in us => Abtastrate in Hz = 1/(interval*1.000.000)
// (=> Aufnahmedauer in s = len*interval)
uint8_t buffer[len]; // Aufnahmepuffer
uint16_t n = 0;
in1.period_us(50); // Periodendauer der PWM
while(1) {
if (tasteA.read() == 0) { // Aufnahme
myled = 1;
for (n = 0; n < len; n++) {
buffer[n] = int(mic.read() * 255);
wait_us(interval);
}
myled = 0;

/* optional: Waveform auf serieller Schnittstelle ausgeben
for (n = 0; n < len; n++){
printf("%c", buffer[n]);
}
printf("\n");
*/
}
if (tasteB.read() == 0) { // Wiedergabe
nSleep.write(1);
for (n = 0; n < len; n++) {
in1.write(float(255-buffer[n]) / 255);
wait_us(interval);
}
nSleep.write(0);
}
}
}

Ihr Code hatte einen Eingang konstant auf 0 gelassen, den anderen konstant auf 1, dazu dann die PWM auf dem Enable-Eingang. Da könnte man meinen, dann bleibt auch ein Ausgang konstant null und der andere bildet eben die PWM ab. Leider kommt das am Ausgang der Brücke aber so nicht an. Wenn man beide *Ausgänge* auf 0 haben will, muss man beide *Eingänge* auf 1 (!) schalten („Bremsen“) und nicht auf 0. Also habe ich das ganze praktisch „invertiert“: D. h. einen Eingang dauerhaft auf 1, den anderen je nach Sampledaten zw. 0 und 1 wechselnd.

Die entscheidende dadurch nötige Zeile war: „in1.write(float(255-buffer[n]) / 255);“ also eben nicht den eigentlichen Samplingwert, sondern die Differenz zu den maximal möglichen 255. Denn 1,1 auf den Eingängen heißt ja 0,0 also „Stille“ auf den Ausgängen. Also schreibt man quasi ein „Negativ“ der gesampelten Daten auf den Eingang und bekommt das „Postitiv“ am Ausgang des Motortreibers. 

Hier die beiden Hex-Files, einmal zur Wiedergabe am Motortreiber-Ausgang wenn Taste B gedrückt wird, einmal zum Schreiben der Rohdaten auf die serielle Schnittstelle, wenn B gedrückt wird; Aufnahme wie gehabt mit Taste A. Ich habe 2kHz Sampling-Frequenz und 2 Sek. Aufnahmedauer konfiguriert. Mit der derzeitigen Hardware und Software (Bibliotheken) funktionierts: Recorder2.zip


Verbessertes Timing von Thorsten Kimmeskamp (thorsten.kimmeskamp@uni-due.de)

Noch etwas sehr cooles zum Wochenende! Ich hatte noch einen Fehler gefunden, der von Anfang an im Programm war: Bei der Aufnahme verbraucht die Ausführung der Zeile buffer[n] = int(mic.read() * 255); ja auch Zeit, bei der Wiedergabe die Ausführung der Zeile in1.write(float(255-buffer[n]) / 255);.

Man darf also nicht einfach z. B. bei 2kHz Abtastrate die 500us warten, sondern muss davon die Ausführungszeit abziehen! Das sind (gemessen) bei der Aufnahme 101us bei der Wiedergabe 72us. Bei 2kHz war es mir nicht aufgefallen, aber bei 8kHz war das Intervall bei der Aufnahme fast doppelt so lang (125+101)!

Wenn man das aber macht, dann hat die Wiedergabe erstens die richtige Tonhöhe (mittels Tongenerator und Spektralanalyse in Audacity geprüft) und auch auf dem Piezo des Calliope kann man tatsächlich erkennen, was man eingesprochen hat.

Ich habe in den angehängten Files die Abtastrate auf 4kHz erhöht (und die PWM), was die Qualität nochmal verbessert (angehängte .wav bzw. .mp3-Datei) und die Aufnahme auf 7500 Samples (bei Ausgabe auf serieller Schnittstelle sind nur 6000 möglich) erhöht. Anbei auch der neue Quelltext. Jetzt stimmt tatsächlich alles und weiter kann man es, so glaube ich auch nicht mehr „pushen“.

 Download:    Recorder3.zip

#include "mbed.h"

DigitalOut col0(P0_4, 0);
DigitalOut myled(p13);

DigitalOut nSleep(p28, 0); // P28 = Motortreiber enable
PwmOut in1(p29); // P29 = Motortreiber Eingang 1
DigitalOut in2(p30, 1); // P30 = Motortreiber Eingang 2

AnalogIn mic(p3); // P3 = MIC

DigitalIn tasteA(p17);
DigitalIn tasteB(p16);

DigitalOut pin1(p1, 0); // Port als Widerstand benutzen

int main() {
uint16_t len = 6000; // Anzahl Samples, Speicher reicht ohne Waveform-Ausgabe fuer max. 7500
uint16_t interval = 250; // Abtastintervall in us => Abtastrate in Hz = 1/(interval*1.000.000)
// (=> Aufnahmedauer in s = len*interval)
uint8_t buffer[len]; // Aufnahmepuffer
uint16_t n = 0;
in1.period_us(20); // Periodendauer der PWM
while(1) {
if (tasteA.read() == 0) { // Aufnahme
myled = 1;
for (n = 0; n < len; n++) {
buffer[n] = int(mic.read() * 255);
wait_us(interval-101); // gemessene Laufzeit der vorigen Zeile: 101 us
}
myled = 0;

/* optional: Waveform auf serieller Schnittstelle ausgeben
for (n = 0; n < len; n++){
printf("%c", buffer[n]);
}
printf("\n");
*/
}
if (tasteB.read() == 0) { // Wiedergabe

nSleep.write(1);
for (n = 0; n < len; n++) {
in1.write(float(255-buffer[n]) / 255);
wait_us(interval-72); // gemessene Laufzeit der vorigen Zeile: 72 us
}
nSleep.write(0);

}
}
}




Elektronik-Labor   Projekte   Microbit