Search code examples
phpiosaudiowavpcm

iOS, WAV audio not playing


I am trying to get an in app feature working of selecting wav file ids, which downloads & plays using NSURLRequest/Connect & AVAudioPlayer.

I keep running into OSStatus error 1954115647 (The Operation couldn't be completed. (File type not supported apparently)).

The file details are PCM S16 LE - Mono - 8000Hz - 16 bit/sample
To investigate further i tried to read the audio direct from the stream where it is hosted using iPhone's safari. Unfortunately it seems the browser is also unable to play the file (works fine in MAC safari as well as other browsers).

The PHP for hosting the stream is as follows:

    $filename = "audio.wav";

    header('Content-type: audio/x-wav');
    header('Content-length: ' . strlen($file));
    header('Content-disposition: inline; filename="' . $filename);
    header('X-Pad: avoid browser bug');
    header('Cache-Control: no-cache');
    echo $file;

I'm surprised this is an issue as to the common use of the WAV file as this. Do i look for an alternative format & convert/deconvert to make this work or is there more likely to be a mistake somewhere else down the line?

I read here that iPhone didn't support certain encodings, however this was published in 2007.


Solution

  • Overall solution (request to hosted file):

    @interface audioPlayer()<AVAudioPlayerDelegate, AVAudioPlayerDataSource>{
        @property AVAudioPlayer *player;
    }
    
    ...
    -(void) playFile{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.responseSerializer = [AFHTTPResponseSerializer serializer];
    [manager setCredential:[NSURLCredential credentialWithUser:@"USER" password:@"PASSWORD" persistence:NSURLCredentialPersistenceNone]];
    [manager GET:@"http://example.com/audio.wav" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"Download Complete!");
        NSData *data = responseObject;
    
        NSString *filePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"audio.wav"]; 
    
        NSError *error;
        [data writeToFile:filePath options:NSDataWritingAtomic error:&error];
        NSLog(@"file error - %@", error); //should be (null)
    
        NSURL *path = [NSURL fileURLWithPath:filePath];
    
        /*
        CFURLRef sysSound;
        SystemSoundID soundID;
        sysSound = (__bridge CFURLRef)path;
    
        AudioServicesCreateSystemSoundID(sysSound, &soundID);
        AudioServicesPlaySystemSound(soundID);
        */
    
        NSError *playError;
        player = [[AVAudioPlayer alloc] initWithContentsOfURL:path fileTypeHint:@"wav" error:&playError];
        NSLog(@"play error - %@", playError);
    
        [player setDelegate:self];
        [player setVolume:1.0];
    
        BOOL prepareCheck = [player prepareToPlay];
        NSLog(@"prepare - %hhd", prepareCheck);
        BOOL playCheck = [player play];
        NSLog(@"play - %hhd", playCheck);
    
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];
    }
    

    Caution: this does not appear to work on iOS simulator

    You'll notice the CFURLRef & SystemSoundID lines commented out. This was originally how i managed to play the audio file, however the system sound functions are limited (less than 30 secs, PCM type audio only).

    This is fine if you're audio is within those restrictions, but i required around a minutes audio. AVAudioPlayer solved this. See code for the easy implementation, but please be aware that intimation of the AVAudioPlayer local to the function will NOT work. The player needs to be global to the implementation file (see the @property under @interface)