Я пытаюсь устранить задержку запуска при воспроизведении аудиофайла (очень короткого - менее 2 секунд) через AVAudioPlayer на iPhone.
Сначала код:
NSString *audioFile = [NSString stringWithFormat:@"%@/%@.caf", [[NSBundle mainBundle] resourcePath], @"audiofile"];
NSData *audioData = [NSData dataWithContentsOfMappedFile:audioFile];
NSError *err;
AVAudioPlayer *audioPlayer = [(AVAudioPlayer*)[AVAudioPlayer alloc] initWithData:audioData error:&err];
audioPlayer.delegate = self;
[audioPlayer play];
Я также реализую метод audioPlayerDidFinishPlaying для выпуска AVAudioPlayer, как только я закончу.
В первый раз, когда я воспроизвожу аудио, отставание ощутимо - не менее 2 секунд. Однако после этого звук воспроизводится немедленно. Я подозреваю, что виновником является то, что [NSData dataWithContentsOfMappedFile] долгое время читает со вспышки, но затем быстро переходит к более позднему чтению. Я не уверен, как это проверить.
Это так? Если да, должен ли я предварительно кэшировать объекты NSData и быть агрессивным относительно их очистки в условиях низкой памяти?
Ответ 1
Задержка, похоже, связана с созданием AVAudioPlayer в первый раз. Если я загружаю любой звук, запустите [audioPlayer prepareToPlay], а затем немедленно отпустите его, время загрузки для всего моего другого звука очень близко к незаметному. Так что теперь я делаю это в applicationDidFinishLaunching, и все остальное работает хорошо.
Я не могу найти ничего об этом в документах, но, похоже, это так.
Ответ 2
Вот что я сделал (в отдельном потоке):
[audioplayer start]
[audioplayer stop]
self.audioplayer = audioplayer
[audioplayer prepareToPlay] кажется асинхронным, поэтому вы не можете быть уверены, когда он вернется, если звук действительно готов к воспроизведению.
В моем случае я вызываю начало фактически начать играть - это кажется синхронным. Затем я немедленно прекращаю это. В симуляторе, в любом случае, я не слышу никакого звука из этой активности. Теперь, когда звук "действительно" готов к воспроизведению, я назначаю локальную переменную переменной-члену, поэтому код вне потока имеет к ней доступ.
Я должен сказать, что я нахожу несколько неожиданным, что даже на iOS 4 требуется всего 2 секунды, чтобы загрузить аудиофайл длиной всего 4 секунды....
Ответ 3
Если ваш звук длится менее 30 секунд и находится в линейном формате PCM или IMA4 и упакован как .caf,.wav или .aiff, вы можете использовать системные звуки:
Импортируйте платформу AudioToolbox
В вашем файле .h создайте эту переменную:
SystemSoundID mySound;
В вашем файле .m реализуйте его в методе init:
-(id)init{
if (self) {
//Get path of VICTORY.WAV <-- the sound file in your bundle
NSString* soundPath = [[NSBundle mainBundle] pathForResource:@"VICTORY" ofType:@"WAV"];
//If the file is in the bundle
if (soundPath) {
//Create a file URL with this path
NSURL* soundURL = [NSURL fileURLWithPath:soundPath];
//Register sound file located at that URL as a system sound
OSStatus err = AudioServicesCreateSystemSoundID((CFURLRef)soundURL, &mySound);
if (err != kAudioServicesNoError) {
NSLog(@"Could not load %@, error code: %ld", soundURL, err);
}
}
}
return self;
}
В вашем методе IBAction вы вызываете звук следующим образом:
AudioServicesPlaySystemSound(mySound);
Это работает для меня, играет звук довольно чертовски близко, когда нажата кнопка.
Надеюсь, это поможет вам.
Ответ 4
Я написал простую оболочку для AVAudioPlayer, которая предоставляет метод prepareToPlay, который действительно работает:
https://github.com/nicklockwood/SoundManager
В основном он просто сканирует ваше приложение для звуковых файлов, выбирает один и воспроизводит его на нулевой громкости. Это инициализирует аппаратное обеспечение звука и позволяет сразу воспроизводить следующий звук.
Ответ 5
Я использовал альтернативный подход, который работает для меня. Я видел эту технику, упомянутую в другом месте (я не помню в тот момент, когда это было).
Короче говоря, предварите звуковую систему, загрузив и играя короткий, "пустой" звук, прежде чем делать что-либо еще. Код выглядит так для короткого mp3 файла, который я предварительно загружаю в свой метод контроллера viewDidLoad
моего контроллера вида .1 секунд:
NSError* error = nil;
NSString* soundfilePath = [[NSBundle mainBundle] pathForResource:@"point1sec" ofType:@"mp3"];
NSURL* soundfileURL = [NSURL fileURLWithPath:soundfilePath];
AVAudioPlayer* player = [[[AVAudioPlayer alloc] initWithContentsOfURL:soundfileURL error:&error] autorelease];
[player play];
Вы можете создать свой собственный пустой mp3, если хотите, или выполнить поиск в google в "пустых mp3 файлах", чтобы найти и загрузить тот, который уже был сконструирован кем-то другим.
Ответ 6
Другим обходным решением является создание короткого и тихого звука и воспроизведение его при первом запуске проигрывателя.
- Установите уровень микрофона на 0 в системных настройках.
- Откройте QuickTime.
- Создайте новую аудиозапись (Файл > Новая аудиозапись).
- Запись за 1 или 2 секунды.
- Сохранить/Экспортировать аудиофайл. (он будет иметь расширение .m4a и около 2 килобайт по размеру).
- Добавьте файл в свой проект и воспроизведите его.
Если вы не хотите создавать новый звуковой файл самостоятельно, вы можете скачать короткий и тихий аудио файл здесь: https://www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0
Ответ 7
Здесь представлено простое расширение Swift для AVAudioPlayer, которое использует идею воспроизведения и остановки в 0 томах, представленную в предыдущих ответах. PrepareToPlay(), к сожалению, по крайней мере, для меня не сделал трюк.
extension AVAudioPlayer {
private func initDelaylessPlayback() {
volume = 0
play()
stop()
volume = 1
}
convenience init(contentsOfWithoutDelay : URL) throws {
try self.init(contentsOf: contentsOfWithoutDelay, fileTypeHint: nil)
initDelaylessPlayback()
}
}
Ответ 8
Я не знаю точно, но я подозреваю, что объект NSData ленив и загружает содержимое файла по требованию. Вы можете попробовать "обмануть", вызвав [audioData getBytes:someBuffer length:1];
в какой-то ранний момент, чтобы загрузить его, прежде чем он понадобится.
Ответ 9
Ответ
AVAudioPlayer *audioplayer;
[audioplayer prepareToPlay];