Adium

Changeset 23017

Show
Ignore:
Timestamp:
04/03/2008 09:47:43 AM (8 months ago)
Author:
evands
Message:

The G5 has a rather odd bug in its interaction with CoreAudio which leads us to get repeated spurious notifications that kAudioHardwarePropertyDefaultSystemOutputDevice changed. We can't make use of the notification on that platform; doing so means that playing sounds continuously stutter as we pause and resume them while trying to update the output device (and also eat CPU in the process).

Once the requested debug output from a G5 is given, I'll be filing this with Apple.

Fixes repeated/stuttering event sounds on G5s, which have been a problem since the switch to QTMovie for sound playing. Fixes #8828

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/Source/AdiumSound.m

    r22732 r23017  
    2121#import <AIUtilities/AISleepNotification.h> 
    2222#import <QTKit/QTKit.h> 
     23#import <CoreServices/CoreServices.h> 
    2324 
    2425#define SOUND_DEFAULT_PREFS                             @"SoundPrefs" 
     
    3132- (void)_uncacheLeastRecentlyUsedSound; 
    3233- (QTAudioContextRef)createAudioContextWithSystemOutputDevice; 
     34- (void)configureAudioContextForMovie:(QTMovie *)movie; 
    3335- (NSArray *)allSounds; 
    3436@end 
     
    6870                                                                                                   object:nil]; 
    6971                 
    70                 //Sign up for notification when the user changes the system output device in the Sound pane of System Preferences. 
    71                 OSStatus err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultSystemOutputDevice, systemOutputDeviceDidChange, /*refcon*/ self); 
    72                 if (err != noErr) 
    73                         NSLog(@"%s: Couldn't sign up for system-output-device-changed notification, because AudioHardwareAddPropertyListener returned %i. Adium will not know when the default system audio device changes.", __PRETTY_FUNCTION__, err); 
     72                /* Sign up for notification when the user changes the system output device in the Sound pane of System Preferences. 
     73                 * 
     74                 * However, we avoid doing this on G5 machines. G5s spew a continuous stream of 
     75                 * kAudioHardwarePropertyDefaultSystemOutputDevice notifications without the device actually changing; 
     76                 * rather than stutter our audio and eat CPU continuously, we just won't try to update. 
     77                 */ 
     78                SInt32 gestaltReturnValue; 
     79                OSErr gestaltErr; 
     80                gestaltErr = Gestalt(gestaltNativeCPUfamily, &gestaltReturnValue); 
     81                if ((gestaltErr != noErr) || ((gestaltReturnValue != gestaltCPU970) && (gestaltReturnValue != gestaltCPU970FX))) { 
     82                        //We couldn't determine the CPU type, or we're not on a G5. 
     83                        OSStatus err = AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultSystemOutputDevice, systemOutputDeviceDidChange, /*refcon*/ self); 
     84                        if (err != noErr) 
     85                                NSLog(@"%s: Couldn't sign up for system-output-device-changed notification, because AudioHardwareAddPropertyListener returned %i. Adium will not know when the default system audio device changes.", __PRETTY_FUNCTION__, err);                         
     86                } else { 
     87                        //We won't be updating automatically, so reconfigure before a sound is played again 
     88                        reconfigureAudioContextBeforeEachPlay = YES; 
     89                } 
    7490        } 
    7591 
     
    184200                        [movie setVolume:customVolume]; 
    185201 
    186                         //Create an audio context for the system output audio device. 
    187                         //We'd reuse one context for all the movies, but that doesn't work; movies can't share a context, apparently. (You get paramErr when you try to give the second movie a context already in use by the first.) 
    188                         QTAudioContextRef newAudioContext = [self createAudioContextWithSystemOutputDevice]; 
    189  
    190                         if (newAudioContext) { 
    191                                 OSStatus err = SetMovieAudioContext([movie quickTimeMovie], newAudioContext); 
    192                                 if (err != noErr) 
    193                                         NSLog(@"%s: Could not set audio context of movie %@ to %p: SetMovieAudioContext returned error %i. Sounds may be routed to the default audio device instead of the system alert audio device.", __PRETTY_FUNCTION__, movie, newAudioContext, err); 
    194                                  
    195                                 //We created it, so we must release it. 
    196                                 QTAudioContextRelease(newAudioContext); 
    197                         } else { 
    198                                 NSLog(@"cachedPlaySound: Could not set audio context because -[AdiumSound createAudioContextWithSystemOutputDevice] returned NULL"); 
    199                         }                        
     202                        [self configureAudioContextForMovie:movie]; 
    200203                } 
    201204 
     
    204207                [soundCacheArray removeObject:inPath]; 
    205208                [soundCacheArray insertObject:inPath atIndex:0]; 
     209                 
     210                if (reconfigureAudioContextBeforeEachPlay) { 
     211                        [movie stop]; 
     212                        [self configureAudioContextForMovie:movie]; 
     213                } 
    206214    } 
    207215 
     
    236244} 
    237245 
    238 - (QTAudioContextRef) createAudioContextWithSystemOutputDevice 
     246- (QTAudioContextRef)createAudioContextWithSystemOutputDevice 
    239247{ 
    240248        QTAudioContextRef newAudioContext = NULL; 
     
    271279} 
    272280 
     281- (void)configureAudioContextForMovie:(QTMovie *)movie 
     282{ 
     283        //QTMovie gets confused if we're playing when we do this, so pause momentarily. 
     284        float savedRate = [movie rate]; 
     285        [movie setRate:0.0]; 
     286         
     287        //Exchange the audio context for a new one with the new device. 
     288        QTAudioContextRef newAudioContext = [self createAudioContextWithSystemOutputDevice]; 
     289         
     290        if (newAudioContext) { 
     291                OSStatus err = SetMovieAudioContext([movie quickTimeMovie], newAudioContext); 
     292                if (err != noErr) { 
     293                        NSLog(@"%s: Could not set audio context of movie %@ to %p: SetMovieAudioContext returned error %i. Sounds may be routed to the default audio device instead of the system alert audio device.", __PRETTY_FUNCTION__, movie, newAudioContext, err); 
     294                } 
     295                 
     296                //We created it, so we must release it. 
     297                QTAudioContextRelease(newAudioContext); 
     298        } else { 
     299                NSLog(@"%s: Could not set audio context because -[AdiumSound createAudioContextWithSystemOutputDevice] returned NULL", __PRETTY_FUNCTION__); 
     300        } 
     301         
     302        //Resume playback, now on the new device. 
     303        [movie setRate:savedRate]; 
     304} 
     305 
    273306- (NSArray *)allSounds 
    274307{ 
     
    315348 
    316349        while ((movie = [soundsEnum nextObject])) { 
    317                 //QTMovie gets confused if we're playing when we do this, so pause momentarily. 
    318                 float savedRate = [movie rate]; 
    319                 [movie setRate:0.0]; 
    320  
    321                 //Exchange the audio context for a new one with the new device. 
    322                 QTAudioContextRef newAudioContext = [self createAudioContextWithSystemOutputDevice]; 
    323  
    324                 if (newAudioContext) { 
    325                         OSStatus err = SetMovieAudioContext([movie quickTimeMovie], newAudioContext); 
    326                         if (err != noErr) { 
    327                                 NSLog(@"%s: Could not set audio context of movie %@ to %p: SetMovieAudioContext returned error %i. Sounds may be routed to the default audio device instead of the system alert audio device.", __PRETTY_FUNCTION__, movie, newAudioContext, err); 
    328                         } 
    329  
    330                         //We created it, so we must release it. 
    331                         QTAudioContextRelease(newAudioContext); 
    332                 } else { 
    333                         NSLog(@"systemOutputDeviceDidChange: Could not set audio context because -[AdiumSound createAudioContextWithSystemOutputDevice] returned NULL"); 
    334                 } 
    335  
    336                 //Resume playback, now on the new device. 
    337                 [movie setRate:savedRate]; 
     350                [self configureAudioContextForMovie:movie]; 
    338351        } 
    339352}