Медленный запуск AVAudioPlayer при первом воспроизведении звука

StackOverflow https://stackoverflow.com/questions/900461

Вопрос

Я пытаюсь устранить задержку запуска при воспроизведении (очень короткого - менее 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 и быть агрессивным в отношении их очистки в условиях нехватки памяти?

Это было полезно?

Решение

Задержка, по-видимому, связана с созданием экземпляра AVAudioPlayer в первый раз.Если я загружаю какой-либо звук, запускаю [AudioPlayer prepareToPlay], а затем немедленно выпускаю его, время загрузки всех других моих аудиозаписей очень близко к незаметному.Так что теперь я делаю это в applicationDidFinishLaunching, и все остальное работает хорошо.

Я не могу найти ничего по этому поводу в документах, но, похоже, это действительно так.

Другие советы

Вот что я сделал (в отдельном потоке):

[audioplayer start]
[audioplayer stop]
self.audioplayer = audioplayer

[audioplayer prepareToPlay], похоже, является асинхронным методом, поэтому вы не можете быть уверены, когда он вернется, действительно ли аудио готово к воспроизведению.

В моем случае я вызываю start, чтобы действительно начать воспроизведение - похоже, это происходит синхронно.Тогда я немедленно прекращаю это.В любом случае, в симуляторе я не слышу никакого звука, исходящего от этого действия.Теперь, когда звук "действительно" готов к воспроизведению, я присваиваю локальную переменную переменной-члену, чтобы код вне потока имел к ней доступ.

Должен сказать, я нахожу несколько удивительным, что даже на iOS 4 загрузка аудиофайла длиной всего 4 секунды занимает около 2 секунд....

Если ваш звук длится менее 30 секунд и выполнен в линейном формате PCM или IMA4 и упакован в формате .caf, .wav или .aiff, вы можете использовать системные звуки:

Импортируйте фреймворк AudioToolbox

В вашем файле .h создайте эту переменную:

SystemSoundID mySound;

В вашем файле .m реализуйте это в вашем методе инициализации:

-(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);

У меня это работает, звук воспроизводится чертовски близко к моменту нажатия кнопки.Надеюсь, это поможет вам.

Я выбрал альтернативный подход, который работает для меня.Я видел эту технику, упомянутую в другом месте (хотя на данный момент я не помню, где это было).

Короче говоря, выполните предварительную настройку звуковой системы, загрузив и воспроизведя короткий, "пустой" звук, прежде чем делать что-либо еще.Код выглядит следующим образом для короткого mp3-файла, который я предварительно загружаю в свой view controllerviewDidLoad метод длительностью 0,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", чтобы найти и загрузить тот, который уже создан кем-то другим.

Я написал простую оболочку для AVAudioPlayer, которая предоставляет метод prepareToPlay, который действительно работает:

https://github.com/nicklockwood/SoundManager

По сути, он просто сканирует ваше приложение на наличие звуковых файлов, выбирает один и воспроизводит его с нулевой громкостью.Это инициализирует аудиооборудование и позволяет мгновенно воспроизвести следующий звук.

Другим обходным путем является создание короткого и бесшумного аудио и воспроизведение его при первом запуске проигрывателя.

  1. Установите уровень микрофона равным 0 в системных настройках.
  2. Откройте QuickTime.
  3. Создайте новую аудиозапись (Файл > Новая аудиозапись).
  4. Записывайте в течение 1 или 2 секунд.
  5. Сохраните / экспортируйте аудиофайл.(он будет иметь расширение .m4a и размер около 2 кб).
  6. Добавьте файл в свой проект и воспроизведите его.

Если вы не хотите создавать новый звуковой файл самостоятельно, вы можете загрузить короткий и тихий аудиофайл здесь : https://www.dropbox.com/s/3u45x9v72ic70tk/silent.m4a?dl=0

Вот простое расширение 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()
    }
}

Я не знаю наверняка, но я подозреваю, что объект NSData ленив и загружает содержимое файла по требованию.Вы можете попробовать "обмануть", позвонив [audioData getBytes:someBuffer length:1]; на каком-то раннем этапе заставить его загрузить этот файл до того, как он понадобится.

Ответ таков

AVAudioPlayer *audioplayer;
[audioplayer prepareToPlay];
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top