Stolpersteine in der iPhone-Entwicklung 1: Speaker und Mikrofon
30. November 2009 von Sven Hamer [permalink]Dieser erste Beitrag zum Thema “Stolpersteine in der iPhone-Entwicklung” befasst sich mit dem Problem “Mikrofon und gleichzeitiger Sound aus dem Speaker”. Hierbei soll der interne Speaker beim iPhone anstelle der normalen “Hörermuschel” als Ausgabegerät und gleichzeitig das eingebaute Mikrofon verwendet werden.
Sound aus dem Speaker
Um dies zu erreichen ist es notwendig den Audioweg umzurouten. Dies geschieht bei der Initialisierung des Apps und wird folgendermaßen erreicht:
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; AudioSessionSetProperty( kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride), &audioRouteOverride );
Beim iPod touch ist dies nicht notwendig, da dieser lediglich den Speaker besitzt und nicht manuell umgeschaltet werden braucht, allerdings schadet diese Angabe auch nichts. Die Ausgabe erfolgt nun über den Speaker, doch ein simples An- und Abstöpseln der Kopfhörer zerstört die ganze Arbeit wieder, da diese Änderung der „Audiohardware“ intern automatisch zum erneuten umrouten des Audiowegs führt. Außerdem ist es ebensowenig angebracht den Speaker zu verwenden, wenn der Anwender die Kopfhörer (oder ähnliches) eingestöpselt hat. Es muss also kontrolliert werden, ob im aktuellen Kontext auf die Speaker gewechselt werden soll oder nicht. Somit muss das App auch auf die Änderung horchen und bei Bedarf die gewünschte Konfiguration wiederherstellen. Hierzu definieren wir zunächst eine Listener-Methode, die immer dann aufgerufen wird, sobald sich der Audioweg ändert (siehe auch Apple iPhone AudioSession Programming Guide):
void audioRouteChangeListener (
void *inUserData,
AudioSessionPropertyID inPropertyID,
Uint32 inPropertyValueSize,
const void *inPropertyValue )
{
CFDictionaryRef routeChangeDictionary = (CFDictionaryRef)inPropertyValue;
CFNumberRef routeChangeReasonRef = (CFNumberRef)CFDictionaryGetValue(
routeChangeDictionary,
CFSTR(kAudioSession_AudioRouteChangeKey_Reason)
);
SInt32 routeChangeReason;
CFNumberGetValue(
routeChangeReasonRef,
kCFNumberSInt32Type,
&routeChangeReason
);
// Warum wurde die Route geändert?
switch(routeChangeReason) {
case kAudioSessionRouteChangeReason_OldDeviceUnavailable:
// Kopfhörer rausgezogen?
maybeSwitchToSpeaker();
break;
}
}
Damit die Methode letztendlich auch aufgerufen wird, muss sie in der Audiosession registriert werden:
void installAudioRouteChangeHook() {
AudioSessionPropertyID routeChangeID =
kAudioSessionProperty_AudioRouteChange;
AudioSessionAddPropertyListener(
routeChangeID,
audioRouteChangeListener,
nil
);
}
Unsere Listener-Methode reagiert somit auf das Ereigniss das eintritt, sobald ein Ausgabegerät (Kopfhörer, Headset, externe Hardware, usw.) nicht mehr verfügbar ist. An dieser Stelle soll nun eine Methode ausgeführt werden, welche den gewünschten Zustand (Ausgabe über Speaker) wiederherstellt. Da wir diese Methode an einer weiteren Stelle ebenfalls einsetzen wollen, beinhaltet sie eine Abfrage über die aktuelle „Abspielsituation“ (basierend auf Beitrag im iPhoneDevSDK-Forum):
void maybeSwitchToSpeaker() {
CFStringRef route;
UInt32 propertySize = sizeof(CFStringRef);
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionGetProperty(
kAudioSessionProperty_AudioRoute,
&propertySize,
&route
);
if((route != NULL) && (CFStringGetLength(route) != 0)){
NSString* routeStr = (NSString*)route;
NSRange receiverRange = [routeStr rangeOfString : @"Receiver"];
if (receiverRange.location != NSNotFound) {
// Aktueller Audioweg führt zur "Hörermuschel", ändere zu Speaker:
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(
kAudioSessionProperty_OverrideAudioRoute,
sizeof(audioRouteOverride),
&audioRouteOverride
);
}
}
}
Diese Methode mit dem etwas seltsamen Namen sorgt neben dem automatischen Wechsel zum Speaker (Aufruf in aus dem Listener heraus) auch für ein eventuelles Wechseln zum selben beim Starten des Apps, indem wir sie ebenfalls beim Initialisieren aufrufen.
Gleichzeitige Verwendung des Mikrofons
Um nun noch zusätzlich das Mikrofon (internes beim iPhone, externes beim iPod touch) verwenden zu können ist es notwendig in eine andere Audio-Category zu wechseln. Hierzu definieren wir uns eine Methode, die wir bei der Initialisierung anstelle der „maybeSwitchToSpeaker“ aufrufen:
void initPlayAndRecord() {
UInt32 audioSessionCategory = kAudioSessionCategory_PlayAndRecord;
AudioSessionSetProperty(
kAudioSessionProperty_AudioCategory,
sizeof(audioSessionCategory),
&audioSessionCategory
);
maybeSwitchToSpeaker();
}
Die Initialisierung erfolgt nun durch Aufruf von „initPlayAndRecord“ gefolgt von eventuellen OpenAL Initialisierungen und letztendlich „installAudioRouteChangeHook“:
- (void) initSound {
initPlayAndRecord();
// Initialisierung von beispielsweise OpenAL Device/Context
// ...
installAudioRouteChangeHook();
}
Tags: iPhone, Mikrofon, Speaker
Kategorie: Softwareentwicklung, iPhone
