Feb 23

iPhone Audio Programmierung mit Audio Toolbox

Kategorie: Anleitungen / Dokusholeg

In einem älteren Artikel habe ich darüber geschrieben, wie man mit Celestial Audio auf dem iPhone wiedergeben kann. Mit dieser Methode kann man auf sehr einfache Weise fertige Audio Dateien abspielen. Möchte man jedoch selbst erzeugten Sound oder mehrere Audio-Streams gleichzeitig abspielen, wie es z.B. für Spiele notwendig ist, dann kommt man mit Celestial nicht weiter. Für diesen Fall hat Apple das AudioToolbox Framework erschaffen. Dieses Framework hat eine eigene Audio Queue, welche erschaffen wurde um reine PCM Audio Daten zu verarbeiten. Um die Funktionsweise einer Audio Queue verstehen zu können, sollte man sich im Vorfeld schon mit digitaler Audioverarbeitung auseinander gesetzt haben. Man findet zu dem Thema im Internet genug Infos, daher möchte ich nicht näher darauf eingehen. Zu empfehlen wäre noch das Buch von Bruce und Marty Fries mit dem Titel Digital Audio Essentials.

Eine Audio Queue kann man sich am besten wie eine Menschenschlange vorstellen, die Wasser von Ort A nach Ort B mit Eimern befördern möchte, zB. aus einem Fluss um einen Brand zu löschen. Ein Mensch steht am Fluss und füllt die Eimer, welche dann Stück für Stück durch die Schlange gereicht werden, bis sie schließlich am Feuer angekommen sind. Dort werden die Eimer dann in die Flammen geleert und wandern wieder zurück zum Fluss um erneut gefüllt zu werden. Das Wasser im Eimer wäre in unserem Fall ein Teil des Audio Streams, die Eimer repräsentieren die Audio Buffer und das Feuer wäre die Audio-Hardware, welche den Stream abspielt. Die Schlange selbst ist die Audio Queue. Wie groß die Eimer sind, wie viele Eimer in einer Schlange benötigt werden und welche Art der Eimer benutzt werden, das bestimmen wir als Programmierer. Ebenso müssen wir eine Funktion schreiben, welche die Eimer mit den richtigen Daten füllt.

Im folgenden Beispiel werden wir eine Sinus Welle erzeugen und mit Hilfe der Audio Queue wiedergeben.

Bevor wir mit der Erzeugung der Sinuswelle beginnen, müssen wir uns zuerst überlegen mit welcher Samplingrate und Auflösung die Welle wiedergegeben werden soll. Ein seit Jahren bewährter Standard ist 44.1 kHz Samplingrate mit 16 Bit Auflösung und das Ganze auf 2 Kanälen (Stereo). Das bedeutet dass wir 44100 Werte benötigen um eine Sekunde Audio zu beschreiben. Ein Wert hat die Genauigkeit von 16 Bit; es werden also 2 Bytes pro Wert und Kanal benötigt. Das entspricht einem Wert von -32767 bis +32767. Für eine Sekunde Stereo Audio benötigt man 44100 * 2 * 2 = 176400 Bytes Speicherplatz (= 172,26 kByte). Unsere Sinuswelle soll eine Frequenz von 440 Hz haben, was bedeutet sie wiederholt sich 440 mal pro Sekunde. Um eine solche Welle zu erzeugen bedienen wir uns folgender mathematischer Formel:

y = sin(x)

sinewave.png

Diese Funktion liefert allerdings als Ergebnis Werte von -1 bis +1 und sie durchläuft den Nullpunkt das erste Mal bei π (3.14..), das nächste Mal bei 2π, dann 3π und so weiter. Eine komplette Schwingung wird daher bei 2π erreicht. 2π entspricht also einer Schwingung. Um das Ganze in Herz (Schwingungen pro Sekunde) auszudrücken, müssen wir daher den x Wert noch mit 2π multiplizieren:

y = sin(x * 2 * π)

Da wir aber nicht jede Sekunde unsere Welle abtasten, sondern 44100 mal in der Sekunde, muss das Produkt noch durch 44100 geteilt werden:

y = sin((x * 2 * π) / 44100)

Damit die Welle mit 440 Hz (Kammerton A) schwingt, lassen wir den Faktor 440 mit einfließen:

y = sin((x * 2 * π * 440) / 44100)

Das Ergebnis bewegt sich allerdings immer noch zwischen -1 und +1. Wenn wir diese Werte der Audio Queue übergeben, werden wir nichts hören, da die Audio Queue Wete von -32767 bis + 32767 (16 Bit) erwartet. Daher müssen wir das Ergebnis noch mit 32767 multiplizieren:

y = sin((x * 2 * π * 440) / 44100) * 32767;

In C sieht das dann so aus:

#include <stdio.h>
#include <math.h>

int main (int argc, const char * argv[]) {

	short sampleValue; // -32767 bis +32767
	float floatVal;

	int t;

	for (t=0; t<=44100; t++) {
		floatVal = sin(((float)t * 2.0 * M_PI * 440.0) / 44100.0);
		sampleValue = (int)(floatVal * 32767.0);
		printf("Sample für %d = %d\n", t, sampleValue);
	}

 return 0;
}

Nachdem wir nun wissen wie man eine Sinuswelle erzeugt, wenden wir uns wieder der Audio Queue zu, da wir unsere erzeugte Welle ja auch abspielen möchten. Das Audio Toolbox Framework wurde in reinem C geschrieben. Aus diesem Grunde werde ich der Verständlichkeit halber auch die Beispiele in diesem Artikel in C aufführen. Eine Mischung mit Objective C würde das Ganze nur verkomplizieren. Um die Sinuswelle abzuspielen sind folgende Schritte notwendig:

  1. Erzeugen einer AQCallbackStruct Struktur
  2. Initialisieren der Struktur mit Eigenschaften, wie Samplingrate, Format, etc …
  3. Erzeugen der Audio Queue und Übergabe der Struktur.
  4. Erzeugen der Sound Buffer (Eimer), welche der Queue hinzugefügt werden.
  5. Bereitstellen einer Funktion, welche die leeren Sound Buffer füllt (Mann am Fluss der die Eimer füllt)

1. Struktur erzeugen

Damit alle Daten zusammen bleiben kapseln wir diese in einer Struktur die wie folgt aussieht:

typedef struct AQCallbackStruct {
	AudioQueueRef					queue;
	UInt32						frameCount;
	AudioQueueBufferRef			mBuffers[NUM_BUFFERS];
	AudioStreamBasicDescription		mDataFormat;
} AQCallbackStruct;

Die einzelnen Variablen der Struktur haben folgende Bedeutung:

  • AudioQueueRef queue

    Ein Zeiger auf das Audio Queue Objekt, welches vom Programm erzeugt wurde

  • UInt32 frameCount

    Anzahl Samples pro Buffer (Eimer)

  • AudioQueueBufferRef mBuffers[NUM_BUFFERS]

    Array um alle benutzen Sound Buffer zu speichern. Die Größe des Arrays (Anzahl der Audio Buffer) muss beim iPhone mindestens 3 sein.

  • AudioStreamBasicDescription mDataFormat

    Informationen über das Audioformat.

2. Struktur initialisieren


AQCallbackStruct in;
in.mDataFormat.mSampleRate = 44100.0;
in.mDataFormat.mFormatID = kAudioFormatLinearPCM;
in.mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
in.mDataFormat.mBytesPerPacket = 4;
in.mDataFormat.mFramesPerPacket = 1;
in.mDataFormat.mBytesPerFrame = 4;
in.mDataFormat.mChannelsPerFrame = 2;
in.mDataFormat.mBitsPerChannel = 16;
in.frameCount = 1024;

3. Erzeugen der Queue


UInt32 err = AudioQueueNewOutput(&in.mDataFormat, AQBufferCallback, &in, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &in.queue);
if(err) fprintf(stderr, "AudioQueueNewOutput err %d\n", err);

4. Erzeugen der Buffer und Starten der Queue

// Ermittlen der Buffergröße
UInt32 bufferBytes = in.frameCount * in.mDataFormat.mBytesPerFrame;

// alloc 3 buffers.
for (i=0; i< NUM_BUFFERS; i++) {
	err = AudioQueueAllocateBuffer(in.queue, bufferBytes, &in.mBuffers[i]);
	if(err) fprintf(stderr, "AudioQueueAllocateBuffer [%d] err %d\n",i, err);
	// Erster initialer Aufruf der Callback Funktion
	AQBufferCallback (&in, in.queue, in.mBuffers[i]);
}	

// Einstellung der Lautstärke
err = AudioQueueSetParameter(in.queue, kAudioQueueParam_Volume, 1.0);
if(err) fprintf(stderr, "AudioQueueSetParameter err %d\n", err);

// Starten der Queue
err = AudioQueueStart(in.queue, NULL);
if(err) fprintf(stderr, "AudioQueueStart err %d\n", err);

// Nichts tun und das im Loop ;-)
while(1) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false);

// Speicher der Queue wieder freigeben.
err = AudioQueueDispose(in.queue, true);

5. Erzeugen der Callback Funktion zum Füllen der Buffer

static void AQBufferCallback(void *in,	AudioQueueRef inQ, AudioQueueBufferRef outQB)
{
	int i;
	UInt32 err;

	// Zuweisung der Struktur und des Zeigers für den Output uffer
	AQCallbackStruct *inData = (AQCallbackStruct *)in;
	short *coreAudioBuffer = (short*) outQB->mAudioData;

	// so lange frameCount > 0 ist ist, haben wir auch Daten zum verarbeiten
	if (inData->frameCount > 0) {
		// Zuweisen der Größe des Buffers
		outQB->mAudioDataByteSize = 4*inData->frameCount; // zwei shorts pro sample * 2 Kanäle

		// Für jedes Sample pro Kanal
		for(i=0; i<inData->frameCount*2; i=i+2) {
			// Berechnen der Sinuskurve
			floatVal = sin(((float)sampleNr * 2.0 * M_PI * 440.0) / 44100.0); // Kammerton A
			sampleValue = (int)(floatVal * 32767.0);
			coreAudioBuffer[i] = sampleValue; // Linker Kanal
			coreAudioBuffer[i+1] = sampleValue; // Rechter Kanal
			sampleNr++;
		}
		// Buffer entleeren (Wasser des Eimers ins Feuer kippen)
		AudioQueueEnqueueBuffer(inQ, outQB, 0, NULL);
	} else {
		err = AudioQueueStop(inData->queue, false);
	}
}

Das komplette Beispiel sieht dann so aus:

#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <AudioToolbox/AudioQueue.h> 

#define NUM_BUFFERS 3

typedef struct AQCallbackStruct {
	AudioQueueRef					queue;
	UInt32							frameCount;
	AudioQueueBufferRef				mBuffers[NUM_BUFFERS];
	AudioStreamBasicDescription		mDataFormat;
} AQCallbackStruct;

int sampleNr = 0; // Nummer des Samples, welches gerade im Buffer verarbeitet wird

static void AQBufferCallback(void *in,	AudioQueueRef inQ, AudioQueueBufferRef outQB) {

	int i;
	UInt32 err;
	float floatVal;
	short sampleValue; // -32767 bis +32767

	// Zuweisung der Struktur und des Zeigers für den Output uffer
	AQCallbackStruct *inData = (AQCallbackStruct *)in;
	short *coreAudioBuffer = (short*) outQB->mAudioData;

	// so lange frameCount > 0 ist ist, haben wir auch Daten zum verarbeiten
	if (inData->frameCount > 0) {
		// Zuweisen der Größe des Buffers
		outQB->mAudioDataByteSize = 4*inData->frameCount; // zwei shorts pro sample * 2 Kanäle

		// Für jedes Sample pro Kanal
		for(i=0; i<inData->frameCount*2; i=i+2) {
			// Berechnen der Sinuskurve
			floatVal = sin(((float)sampleNr * 2.0 * M_PI * 440.0) / 44100.0); // Kammerton A
			sampleValue = (int)(floatVal * 32767.0);
			coreAudioBuffer[i] = sampleValue; // Linker Kanal
			coreAudioBuffer[i+1] = sampleValue; // Rechter Kanal
			sampleNr++;
		}
		// Buffer entleeren (Wasser des Eimers ins Feuer kippen)
		AudioQueueEnqueueBuffer(inQ, outQB, 0, NULL);
	} else {
		err = AudioQueueStop(inData->queue, false);
	}
}

int main (int argc, const char * argv[]) {
	int i;

	AQCallbackStruct in;
	in.mDataFormat.mSampleRate = 44100.0;
	in.mDataFormat.mFormatID = kAudioFormatLinearPCM;
	in.mDataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
	in.mDataFormat.mBytesPerPacket = 4;
	in.mDataFormat.mFramesPerPacket = 1;
	in.mDataFormat.mBytesPerFrame = 4;
	in.mDataFormat.mChannelsPerFrame = 2;
	in.mDataFormat.mBitsPerChannel = 16;
	in.frameCount = 1024;

	UInt32 err = AudioQueueNewOutput(&in.mDataFormat, AQBufferCallback, &in, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &in.queue);
	if(err) fprintf(stderr, "AudioQueueNewOutput err %d\n", err);

	// Ermittlen der Buffergröße
	UInt32 bufferBytes = in.frameCount * in.mDataFormat.mBytesPerFrame;

	// alloc 3 buffers.
	for (i=0; i< NUM_BUFFERS; i++) {
		err = AudioQueueAllocateBuffer(in.queue, bufferBytes, &in.mBuffers[i]);
		if(err) fprintf(stderr, "AudioQueueAllocateBuffer [%d] err %d\n",i, err);
		// Erster initialer Aufruf der Callback Funktion
		AQBufferCallback (&in, in.queue, in.mBuffers[i]);
	}	

	// Einstellung der Lautstärke
	err = AudioQueueSetParameter(in.queue, kAudioQueueParam_Volume, 1.0);
	if(err) fprintf(stderr, "AudioQueueSetParameter err %d\n", err);

	// Starten der Queue
	err = AudioQueueStart(in.queue, NULL);
	if(err) fprintf(stderr, "AudioQueueStart err %d\n", err);

	// Nichts tun und das im Loop ;-)
	while(1) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false);

	// Speicher der Queue wieder freigeben.
	err = AudioQueueDispose(in.queue, true);

 return 0;
}

Das Beispiel sichert man am Besten in einer Datei Namens sinus.c und kompiliert die Datei dann wie folgt:

arm-apple-darwin-gcc -o sinus sinus.c -framework AudioToolbox -framework CoreAudio -framework CoreFoundation

Das compilierte Programm kopiert man dann aufs iPhone und startet es per ssh. Da in Mac OS X 10.5 auch das Audio Toolbox Framework existiert kann man das auch auf dem normalen Desktop mit dem normalen gcc kompilieren:

gcc -o sinus sinus.c -framework AudioToolbox -framework CoreAudio -framework CoreFoundation

Auf diese Art und Weise kann man auch mehrere Sounds gleichzeitig abspielen. Man muss dazu nur die einzelnen Samples der abzuspielenden Sounds zusammen zählen und durch die Anzahl der Sounds Teilen. Wenn man die Callback Funktion wie folgt ändert, wird noch eine 2. Sinuswelle erzeugt:


....

	// Berechnen der Sinuskurve
	floatVal = sin(((float)sampleNr * 2.0 * M_PI * 440.0) / 44100.0); // Kammerton A
	floatVal2 = sin(((float)sampleNr * 2.0 * M_PI * 587.330) / 44100.0) // D
	sampleValue = (int)(((floatVal+floatVal2)/2.0) * 32767.0);

....

30 Kommentare zu “iPhone Audio Programmierung mit Audio Toolbox”

  1. Michael sagt:

    Hi cooles Bsp.
    Wie würde ich ein PCM File, dass lokal vorhanden ist abspielen?

  2. holeg sagt:

    Du legst Dir einen Speicherberich an, der so groß ist wie das PCM File (zB mit malloc() ) In diesen Speicherberich lädst Du dann Dein PCM File. Dann erweiterst Du die AQCallbackStruct um einen Wert mit dem Typ void* (Zeiger auf beliebigen Speicher) Anstelle den Sample-Wert zu berechnen setzt Du die Variable sampleNr einfach ein 2 Bytes im Speicherbereich weiter und liest den Wert als short aus. Diesen Wert weist Du dann coreAudioBuffer zu. Wenn sampleNr die Buffergröße/2 erreicht hat, dann führst Du AudioQueueDispose aus.

  3. Michael sagt:

    Du meinst soetwas in der Art?
    in Funktion main:

    FILE *pFile;
    long lSize;
    short *buffer;
    pFile=fopen(”/var/root/Files/sine440.pcm”,”r”);
    if(pFile==NULL)
    exit (1);
    fseek (pFile,0,SEEK_END);
    lSize=ftell(pFile);
    buffer=(short*)malloc(lSize);
    if(buffer==NULL)
    exit (2);
    fread(buffer,1,lSize,pFile);
    in.pDatatoPlay=buffer;

    und in AQBufferCallback:

    for(i=0; iframeCount*2; i=i+2) {
    // Berechnen der Sinuskurve
    //floatVal = sin(((float)sampleNr * 2.0 * M_PI * 440.0) / 44100.0); // Kammerton A
    sampleValue = (short*)inData->pDatatoPlay;
    coreAudioBuffer[i] = sampleValue; // Linker Kanal
    coreAudioBuffer[i+1] = sampleValue; // Rechter Kanal
    sampleNr=sampleNr+2;
    }

    und wo soll die abfrage nach der buffergröße/2 hin?

  4. holeg sagt:

    in AQBufferCallback:

    if (sampleNr >= bufferLen/2) {
    AudioQueueDispose(inData->queue, true);
    }

  5. Michael sagt:

    ok thx, funktioniert leider nur trotzdem nicht.
    Ich höre nur ein periodisches Knacksen! ;/

    was muss ich in der AQBufferCallback alles ändern?
    Was stimmt an dem Code nicht?

    AQCallbackStruct *inData=(AQCallbackStruct *)in;
    short *coreAudioBuffer=(short*) outQB->mAudioData;
    if(sampleNr<=(lSize/2)){//lSize = Globale Var. entspricht PCM-File-Größe
    for(i=0;iframCount*2;i=i+2){
    coreAudioBuffer[i]=inData->DatatoPlay+sampleNr;
    coreAudioBuffer[i+1]=inData->DatatoPlay+sampleNr+1;
    sampleNr=sampleNr+2; //2 Byte sprung???
    }
    AudioQueueEnqueueBuffer(inQ,OutQB,0,NULL);
    }else{
    err=AudioQueueDispose(inData->queue, true);
    }
    }
    //DatatoPlay= mit malloc allozierter speicher der das PCM sample beinhaltet, in AQCallbackStruct ein short Member

    Muss ich in main noch was ändern, oder woram liegt es, dass ich nur hin und wieder ein knacksen höre?
    Danke für deine Hilfe

  6. Michael sagt:

    habe eine zeile vergessen:
    zw. if und for fehlt folgende Zeile:
    outQB->mAudioDataByteSize=4*inData->frameCount;

  7. holeg sagt:

    Da sind mehrere Fehler:

    1. for(i=0;iframCount*2;i=i+2){ => Wahrscheinlich Tippfehler

    2. inData->DatatoPlay+sampleNr; => Hier greifst Du auf auf eine Adresse zu und nicht den Inhalt. *(inData->DatatoPlay+sampleNr) wäre besser ;-)

    3. sampleNr=sampleNr+2 => Nur wenn das PCM ein Stereo Signal ist ansonsten nur + 1

    Hab mal ohne zu testen (hab gerade nicht die Möglichkeit dazu) die Callback Funktion so geschrieben wie es funktionieren sollte:

    void AQBufferCallback(void *in, AudioQueueRef inQ, AudioQueueBufferRef outQB) {
    	int i;
    	UInt32 err;
    
    	// Zuweisung der Struktur und des Zeigers für den Output buffer
    	AQCallbackStruct *inData = (AQCallbackStruct *)in;
    	short *coreAudioBuffer = (short*) outQB->mAudioData;
    
     short sample;
    
     int sampleLen = (lSize/2);
    
     if (sampleNr >= sampleLen) {
     AudioQueueDispose(inData->queue, true);
     return;
     }
    	if (inData->frameCount > 0) {
     outQB->mAudioDataByteSize = 4 * inData->frameCount;
     for(i=0; i<inData->frameCount*2; i+=2) {
     if (sampleNr > sampleLen || sampleNr < 0) {
     sample = 0;
     } else {
     sample = inData->DatatoPlay[sampleNr];
     }
    
     coreAudioBuffer[i] = sample;
     coreAudioBuffer[i+1] = sample; 
    
     sampleNr++;
     }
     AudioQueueEnqueueBuffer(inQ, outQB, 0, NULL);
     }
    }
    

    Um das ganze rund zu machen würde ich sampleNr und lSize auch als Variable der Struktur zufügen.

  8. holeg sagt:

    Wichtig ist auch dass das raw pcm File im Windows Format (little-endian) und Mono abgespeichert wird! Für Stereo müsste man obigen Code modifizieren.

  9. Michael sagt:

    Hi Holeg!
    Thx für deine Hilfe, hab deinen Code heute getestet, aber er funktioniert leider nicht. Meine PCM Demo ist eine Stereodatei, aber das sollte ja an der Grundfunktionalität deines Codes nichts verändern, oder? auf jeden Fall passiert folgendes wenn ich das Programm starte: es läuft ca. 20 sek. ohne einen ton abzuspielen und schließt sich wieder.

    Meine nächste Frage ist diese: In der main() Funktion, muss ich sonst nichts ändern? nachdem ich das File eingelesen habe, geht es mit deinem Programmcode weiter? Ich lese zwischen der Definition des Fileformats und dem UInt32 err = AudioQueueNewOutput(…); -Aufruf, das PCM file ein.
    Brauche ich dann eigentlich noch 3 Buffer? Wenn ich doch das gesamte File in einen buffer einlese ist doch folgende for Schleife unnötig?!
    for (i=0; i< NUM_BUFFERS; i++) {
    err = AudioQueueAllocateBuffer(in.queue, bufferBytes, &in.mBuffers[i]);
    if(err) fprintf(stderr, “AudioQueueAllocateBuffer [%d] err %ld\n”,i, err);
    // Erster initialer Aufruf der Callback Funktion
    AQBufferCallback (&in, in.queue, in.mBuffers[i]);
    }
    Könnte ich die for Schleife auch weglassen und nur die Aufrufe:
    err = AudioQueueAllocateBuffer(in.queue, bufferBytes, &in.mBuffers[i]);
    if(err) fprintf(stderr, “AudioQueueAllocateBuffer [%d] err %ld\n”,i, err);
    // Erster initialer Aufruf der Callback Funktion
    AQBufferCallback (&in, in.queue, in.mBuffers[i]);
    machen? klar würde ich auch das #define von NUM_BUFFERS auf “1″ setzen…
    Leider denke ich, dass dies mir jetzt nicht weiterhilft, dass das File ausgegeben wird…:(

    LG und danke nochmal

  10. holeg sagt:

    Die 3 Buffer brauchst Du unbedingt! Das ist die minimale Anzahl von Buffern die das iPhone benötigt. Unter Leopard auf dem Mac reicht ein Buffer. Wenn ich ein wenig mehr Zeit habe, werde ich Dir ein Beispiel posten.

  11. Michael sagt:

    alles klar, ich habe jetzt folgendes problem: ich habe auf meinem MAC ein Beispiel zum laufen gebracht, dass mir mein PCm File abspielt. Lasse ich jetzt den fast identen Code am IPod laufen, so bekommen ich in der Console folgenden Fehler:
    udioQueueAllocateBuffer [1] err -50
    AQ_Play(490) malloc: *** error for object 0×103430: double free
    AQ_Play(490) malloc: *** set a breakpoint in malloc_error_break to debug
    AQ_Play(490) malloc: *** error for object 0×1033e0: double free
    AQ_Play(490) malloc: *** set a breakpoint in malloc_error_break to debug
    AudioQueueAllocateBuffer [2] err -50

    Er hat offensichtlich Probleme mit dem malloc.
    Ich habe auch schon das Beispiel mit einem statischen Speicher ausprobiert. Wieder: am MAC läuft es, am IPod nicht!
    ;/
    Hier bekomme ich dann in der Console den folgenden Fehler: zsh: segmentation fault /App…

    Irgendwelche Vorschläge?

  12. Michael sagt:

    Ich habe die Fehler beseitigen können, aber nun spielt er nachdem er alle 3 Buffer befüllt hat, nur ca. 500ms das PCM file ab und dannach passiert nichts mehr…
    Also ich höre nachher nichts mehr.

    Liegt das an der while 1 schleife oder besser gesagt mit diesem CFRunLoopInMode (kCFRunLoopDefalutMode,…)?
    Muss ich nicht hier irgendwie vermitteln, dass er die Buffer weiterbefüllen muss?

  13. holeg sagt:

    Also die while Schleife ist dazu da, dass das Programm nicht gleich beendet wird. Bei einem UIKit Programm auf dem iPhone brauchst Du das natürlich nicht, da UIKit Programme ihren eigenen Run-Loop haben. Wenn die Audio Queue durch AudioQueueStart() gestartet wurde, dann läuft sie in einem eigenen Thread, welcher die Callbackfunktion automatisch aufruft, sobald ein Buffer geleert wurde.

  14. Michael sagt:

    Jetzt stürtzt das Programm nur noch ab, nachdem ich die While 1 Schleife rausgenommen habe und das UIKit hinzugefügt habe…
    <<Bei einem UIKit Programm auf dem iPhone brauchst Du das natürlich nicht, da UIKit Programme ihren eigenen Run-Loop haben….
    Heißt das, dass ich mein Programm komplett umschreiben (auf OBjC) muss und der normale C Code, der ja fast ident mit deinem ist, nicht auf dem IPod funktionieren wird?

  15. holeg sagt:

    Der C-Code funktioniert super auf dem iPhone. Siehe mein neues Programm KARAJAN. Da habe ich einen Objective-C Wrapper drumrum gebaut. Inspiriert wurde ich von Pocket-Guitar.

    Hast Du mal das UIKit Programm von der Konsole gestartet und Dir ein paar Debug Ausgaben anzeigen lassen, damit Du sehen kannst in welcher Zeile es abstürzt? Hast Du auch das UIKit Framework in Deinem Makefile hinzugefügt?

  16. Michael sagt:

    Hast Du mal das UIKit Programm von der Konsole gestartet und Dir ein paar Debug Ausgaben anzeigen lassen, damit Du sehen kannst in welcher Zeile es abstürzt? Hast Du auch das UIKit Framework in Deinem Makefile hinzugefügt?
    Ja habe ich. Nachddem er die Queue gestartet hat, schließt er sie mit AudioQueueDispose wieder. –> dh dieses Threadhandling funktioniert nicht, da er sofort nach dem starten im Code weitermacht und es beendet. Auch wenn ich nur die while 1 schleife lasse, ohne dem CFRunLoopRunInMode(), passiert nichts, er bleibt halt in der Endlosschleife…
    mein Problem an der Sache ist, dass ich nicht verstehe, wie ich den Code ändern soll, dass er ein Threadhandling macht. Ich verstehe auch nicht, warum dein oben geposteter Code, wo man sich selbst einen Sinus generiert funktioniert? Da ist doch nichts anders? oder?

  17. Michael sagt:

    lasse ich den code so wie du es in deinem BSP hast, so sehe ich auf der Konsole dass er schon die samples richtig auf den corAudioBuffer schreibt. Aber wie gesagt, ich höre nur einmal kurz etwas, nachdem er die ersten 3 Buffer befüllt hat, danach, rennt das PRG normal weiter und er macht auch das was er machen soll–> die buffer werde immer wieder aufs neue befüllt, bis zum ende des Files, aber hören u ich nichts mehr…

  18. holeg sagt:

    Das AudioQueueDispose brauchst Du bei einem UIKit Programm eigentlich nicht. Im Prinzip erzeugt man bei einem UIKit Programm gleich zu Anfang die AudioQueue und gibt in der Callback-Funktion lauter Nullen als Wert aus (so sieht man es zumindest bei den ganzen Apps die mit AudioQueue arbeiten, zB MobileBeat, PocketGuitar …). Wenn man dann Sound abspielen möchte, erst dann füllt man in der Callback-Funktion die Queue mit richtigen Werten. D.h. die Queue läuft die ganze Zeit während Deine App auch läuft. Wenn Du die App beendest werden eh alle Threads Deiner App gekillt, so dass AudioQueueDispose daher nicht gebraucht wird.

  19. Michael sagt:

    ok, das hab ich gecheckt, aber gibt es echt keine möglichkeit, dass ich vorerst ohne dem UIKit das ganze zum Laufen bringe? Einfach nur App starten und er soll stur das File abspielen.

    Das Speicher freigeben passiert sowieso “per Hand” im Code.
    Ich meine es wird doch nicht vom UIKit abhängen, ob er nach den ersten 3 Buffern einmal kurz einen Ton abspielt und anschließend nur noch die Werte an den coreAudioBuffer ohne Effekt schreibt…

  20. holeg sagt:

    Natürlich gibt es eine Möglichkeit. Wie Du schon sagst ist bei dem Sinusbeispiel nichts anders, ausser dass die Werte nicht berechnet werden, sondern von einem Speicherbereich kommen. Schätze mal da liegt irgendwo der Fehler. Am Besten Du postest hier mal den kompletten Code, dann schau ich mal drüber. Damit der Code hier aber korrekt angezeigt wird, musst Du HTML Sonderzeichen wie <, >, & und " encodieren und am besten ein <code> Tag aussenrum schreiben! Oder Du zippst das Ganze, legst es auf irgend einen Server und postest hier die URL.

  21. Michael sagt:

    Hallo! Also das File ist dieser Link:
    http://www.zippyshare.com/v/23735608/file.html
    und das Projekt ist dieses hier:
    http://www.zippyshare.com/v/8113809/file.html

    Vielen Dank fürs drüberschaun…
    LG

  22. Michael sagt:

    Nochetwas:
    Das PCM File ist ein Stereo File mit den angegebenen Aprametern wie im Code!

  23. holeg sagt:

    Du wirst lachen, wenn ich in der Callback Funktion folgende fprints rausnehme geht alles:

    printf("sample_left: %d\n",sample_l);
    printf("sample_right: %d\n",sample_r);

    Das iPhone schafft es nämlich nicht 44100 fprints in einer Sekunde auszuführen ;-)

  24. Michael sagt:

    omg!
    das gibt es ja nicht!
    lol, da kann ich echt nur lachen…
    ^^
    thx und respekt!
    gg
    ich hoffe, dass ich nun zügiger vorankomme!

    :)
    Schönes Wochenende, und Danke nochmal!

  25. holeg sagt:

    Ein kleiner Tipp noch: Probier mal das STK Toolkit. Für Audio Processing eine echte Hilfe. Den vom STK generierten Stream kannst Du direkt in die AudioQueue einspeissen.

    Viel Spass beim experimentieren.

  26. modusNY sagt:

    Ich habe noch ein paar Probleme. Vielleicht kann mir noch jemand helfen. Wenn ich die Anweisungen

    // Nichts tun und das im Loop ;-)
    while(1) CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.25, false);

    // Speicher der Queue wieder freigeben.
    err = AudioQueueDispose(in.queue, true);

    herausnehme und sämtliche lokalen Variablen als Attribute einer Klasse definiere, wird nur ca. 500 Millisekunden Audio abgespielt. Noch zur Info. Ich habe eine ganz einfache iPhone-Applikation mit dem SDK erstellt und den Code eingetragen.

    Danke!

  27. stefan sagt:

    wenn ich in oben angeführtem programm die frequenz während der laufzeit auch nur geringfügig ändere kommt es zu einem hässlichen knacksen.

    hat jemand eine idee warum?

  28. ben sagt:

    Hallo!

    Ich bin, was Mac- und iPhone-Programmierung angeht ein Beginner und habe nun mit einigen Mühen eine iPhone-Applikation erstellt, die bei einem touchesBegan-Event auf einer UIView einen Ton abspielt. Das funktioniert auch dank deiner Anleitung. Nur, daß der Sinus-Ton klingt, als ob der Lautsprecher kaputt wäre (also eher wie ein Sägezahn), die Tonhöhe stimmt aber. Das ist aber das kleinere Problem.

    Das größere Problem ist für mich:
    Wie stelle ich es an, daß der Ton bei einem touchesEnded-Event wieder aufhört?
    Nur, wenn ich aus das while (1) weglasse, wird ein touchesEnded-Event getriggert, vorausgesetzt, ich drücke so lange, wie ein Durchlauf von 1024 Frames benötigt. Ansonsten stürzt das Programm ab.
    Wahrscheinlich kann touchesEnded erst aufgerufen werden, wenn touchesBegan vollständig abgearbeitet ist (was bei while(true) eine Weile dauern kann…), weil das im selben Thread abläuft. Aber wie kann ich das ändern und erreichen, daß das touchesEnded-Event die Callback-Prozedur abwürgt?

    Für hilfreiche Hinweise wäre ich sehr dankbar!

  29. sagt:

    Hey Holeg!

    Super, genau, was ich gesucht habe - wusste gar nicht, dass Du schreibst!

    Gruß

  30. Animeshader sagt:

    Der Samplecode Zusammen mit FFMpeg kann mp3/ogg abspielen,ruckelt nur ein bissen am mac unter leopard.

Dein Kommentar