PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Hilfe mit Audio



hirnfrei
28.06.2016, 10:42
Mahlzeit!

Ich muss um Hilfe rufen. Google, mein Freund, will da einfach keine passende Antwort finden.

So es geht um folgendes. Ich brauche ein Audiosignal. Das an sich wäre weniger das Problem. Mein Code, um das Signal von einer Soundkarte mit Alsa abzurufen und auch wieder auszugeben sehen so aus:



void audioAufzeichnung(double *input, int max, string name, unsigned int channels, unsigned int actualRate, unsigned short inputBits)
{
int i=0;

double *puffer;

puffer = (double *) malloc(1);

if(InitCapture(name, channels, actualRate, inputBits))
{
while(i < max)
{
snd_pcm_readi(soundKarte, puffer, 1);

input[i] = puffer[0];

i++;
}

free(puffer);

UnInit();
}
}

void playAufzeichnung(double *array, unsigned int inputSize, string name, unsigned int channels, unsigned int actualRate, unsigned short inputBits)
{
uint8_t *puffer;

register snd_pcm_uframes_t count, frames;

Init(name, channels, actualRate, inputBits);

puffer = (uint8_t *) malloc(inputSize);

for(int32_t i=0;i<inputSize;i++) puffer[i] = array[i];

inputSize = inputSize * 8 / inputBits * channels;

count = 0;

do
{
frames = snd_pcm_writei(soundKarte, puffer + count, inputSize - count);

if(frames < 0) frames = snd_pcm_recover(soundKarte, frames, 0);
if(frames < 0)
{
break;
}

count += frames;

} while(count < inputSize);

if(count == inputSize) snd_pcm_drain(soundKarte);

UnInit();
}


Mein Problem ist folgendes. Benutze ich diesen Code und nehme anstatt double int32_t funktioniert es. Nun habe ich ja aber hier im Forum und auch sonst im Netz immer wieder gelesen, man solle doch für Audiodaten double nehmen. Das mag mit dem Einlesen der Daten von der Soundkarte noch funktionieren. Will ich es jedoch wieder ausgeben kommt dann nur ein tiefes Brummen.

Nun würde ich den Schuldigen auf



uint8_t *puffer;

..

puffer = (uint8_t *) malloc(inputSize);

for(int32_t i=0;i<inputSize;i++) puffer[i] = array[i];


schieben. Jedoch waren alle Versuche snd_pcm_writei() mit etwas Anderem wie mit einem char, bzw uint8_t Array zu füttern äusserst Schmerzhaft für die Ohren. Das gibt immer einen brutal hohen Piepton.

Kann mir da jemand helfen?

- - - Aktualisiert - - -

Lol okay, hab gerade gesehen das ich doch nur int16 brauche. Das kriege ich dann wieder hin ^^.

Das mit Double würde mich dann doch interessieren.

Peter(TOO)
28.06.2016, 14:12
Hallo,

Der Fehler liegt bei malloc(1);
Das reserviert 1 Byte, da passt aber kein double rein!
Was genau passiert kann dir jetzt aber keiner sagen, im Prinzip überschreibst du etwas, was hinter dem Speicherbereich von malloc() liegt.
Damit der Heap nicht zu sehr fragmentiert, reserviert malloc() immer ein ganzzahliges vielfaches einer konstanten Anzahl Bytes, der Wert nennt sich Granularity.

Richtig für die Benutzung von malloc ist:

puffer = (double *) malloc( sizeof(double) );

Ob das jetzt zum Aufruf von snd_pcm_readi() passt, ist eine andere frage und steht in der Beschreibung dieser Funktion.

MfG Peter(TOO)

hirnfrei
28.06.2016, 14:38
Kopf -> Tisch

Da kommen die Schwächen von C++ zum Tragen ^^. Also die Schwächen die sich bei mir selbst einschleichen. Als ich noch in Ansi C gearbeitet habe wäre mir das nicht passiert. Da wird man eben durch string usw. doch schon sehr verwöhnt.

Danke schon mal. Ich versuche es mal.

Peter(TOO)
28.06.2016, 16:36
Hallo,

Kopf -> Tisch
Danke schon mal. Ich versuche es mal.
Müsste eigentlich hohl tönen, bei hirnfrei :-)

MfG Peter(TOO)

botty
28.06.2016, 16:36
Das reicht aber evtl. auch nicht. Der Parameter für die Anzahl in snd_pcm_readi() gibt _nicht_ die Anzahl der Bytes an sondern die Anzahl der zu lesenden Sample-Frames. Die Größe dieser Frames hängt davon ab, wieviele Kanäle und welchen Datentyp Du für ein Sample bei der Initialisierung der Soundschnittstelle eingestellt hast.

Ein Kanal mit SND_PCM_FORMAT_S16_LE sind z.B. 2 Bytes pro Frame die Du im Puffer zur Verfügung stellen mußt.
Bei zwei Kanälen sind's vier. Dazu ist zu beachten, wenn Du zwei Kanäle hast, wie die Anordnung der Samples ist. Wenn Du SND_PCM_ACCESS_RW_INTERLEAVED in snd_pcm_hw_params_set_access() angegeben hast, musst Du


snd_pcm_readi(hndl, buf, 4);

aufrufen. _readi() weil interleaved (es gibt noch_readn() für non interleaved) und dann bedeutes das, das wenn du _S16_LE, 2 Kanäle angegeben hast, das der Puffer 2 * 4 * sizeof(int16_t) groß sein muß (Anzahl der Kanäle * Anz.d. Frames * Sample-Größe). Sonst schäpperts.
Die Samples für den ersten Kanal liegen dann in buf[0,2,4,6] und für den zweiten Kanal in buf[1,3,5,7].

hirnfrei
28.06.2016, 16:55
Wie ich merke ist Audio gar nicht so einfach wie man denken sollte ^^.

Zum Glück brauche ich aktuell nur ein Kanal.

Aber mal schauen ob ich das richtig verstanden habe

Anstelle von



snd_pcm_readi(hndl, buf, 4);


könnte ich auch

[code]
snd_pcm_readi(hndl, buf, channel*2);
[code]

verwenden?

Wäre dann bei SND_PCM_FORMAT_S32 das dann * 4?

botty
28.06.2016, 18:14
Der letzte Parameter ist die Anzahl der Frames, nicht die Puffergröße! Diese Anzahl wählt man am besten so groß, das man eine Periode aus ALSA in einem Rutsch auslesen kann (und wie ich feststellen mußte ist meine letzte ALSA Zeit zu lange her, als das ich das im Gedächtnis gehabt hätte).
Schau Dir mal das Listing 4 in diesem Linux Journal Artikel (http://www.linuxjournal.com/node/6735/print) an, ich hoffe dann wird's verständlicher.

hirnfrei
28.06.2016, 19:40
Das sieht sehr gut erklärt aus. Das werde ich mir mal genauer zu Gemüte führen! Vielen Dank!