Актуализирайте потребителския интерфейс, докато възпроизвеждате аудио

Моето приложение основно възпроизвежда ноти една след друга и искам да покажа коя нота се възпроизвежда всеки път. Всяка нота е квадрат в потребителския интерфейс и имам 1-пикселов изглед, който се движи отляво надясно към нотата, която се изпълнява.

Проблемът е, че не съм сигурен как да правя тези две неща едновременно. Не мога да изпратя съобщение от моята функция за изобразяване на мостри, защото това може да се забави и да причини проблеми при възпроизвеждането на аудио. Някой има ли предложения как да актуализирам потребителския си интерфейс, за да отразява това, което се играе?

Моят код за възпроизвеждане на музика е базиран на този пример и аз мисля, че методът RenderTone се изпълнява в различна нишка. Искам да премествам своя 1-пикселов изглед с 1 пиксел всеки път, когато се възпроизвеждат 1000 семпли (само пример, може да е и по-пр-малко), но не знам как да изпратя съобщение до потребителския интерфейс и да му изпратя актуализации за това как изиграни са много проби.

Така че, докато се изпълнява вариант на кода по-долу, трябва по някакъв начин да актуализирам потребителския си интерфейс.

OSStatus RenderTone(
    void *inRefCon, 
    AudioUnitRenderActionFlags *ioActionFlags, 
    const AudioTimeStamp *inTimeStamp, 
    UInt32 inBusNumber, 
    UInt32 inNumberFrames, 
    AudioBufferList *ioData)

{
    // Fixed amplitude is good enough for our purposes
    const double amplitude = 0.25;

    // Get the tone parameters out of the view controller
    ToneGeneratorViewController *viewController =
        (ToneGeneratorViewController *)inRefCon;
    double theta = viewController->theta;
    double theta_increment =
        2.0 * M_PI * viewController->frequency / viewController->sampleRate;

    // This is a mono tone generator so we only need the first buffer
    const int channel = 0;
    Float32 *buffer = (Float32 *)ioData->mBuffers[channel].mData;

    // Generate the samples
    for (UInt32 frame = 0; frame < inNumberFrames; frame++) 
    {
        buffer[frame] = sin(theta) * amplitude;

        theta += theta_increment;
        if (theta > 2.0 * M_PI)
        {
            theta -= 2.0 * M_PI;
        }
    }

    // Store the updated theta back in the view controller
    viewController->theta = theta;

    return noErr;
}

person Community    schedule 03.08.2012    source източник


Отговори (2)


Бих предложил, както в различни случаи в този код, да използвате прост блок обратно към основната нишка:

dispatch_async(dispatch_get_main_queue(), ^{ someViewController makeSomeChange:withParam; } );

Можете да ги изпращате рядко. Можете да стартирате кода си RenderTone в опашката с highPriority, така че mainQueue да се изпълнява само когато нямате съществена работа.

person David H    schedule 03.08.2012

На устройства с iOS потребителският интерфейс може да се актуализира само по отношение на честотата на опресняване на дисплея от 60 Hz. Така че запитването при честотата на опресняване на дисплея е разумна алтернатива и не рискува да се направи нещо неподходящо, което нарушава ограниченията в реално време в нишката за обратно извикване на аудио модула (като всяко съобщение на Obj C, което потенциално включва управление на паметта.)

Можете да поискате продължителността на буфера на аудио сесията да бъде около 1/60 от секундата или по-малко. След това настройте обратното извикване на буфера на аудио единицата или увеличете някакъв брояч на позиция на аудио проба (направете го 1 подравнена дълга int променлива, така че това записване да е атомарно).

След това, в цикъла на изпълнение на потребителския интерфейс, анкетирайте този брояч на позицията на звука с някаква скорост, която е 60 пъти в секунда (синхронизиране на кадри) или по-малко, в зависимост от изискваната отзивчивост на потребителския интерфейс. Изчислете музикалната позиция от позицията на аудио пробата и актуализирайте потребителския интерфейс, ако музикалната позиция показва, че се изпълнява нова нота или предстои да бъде възпроизведена.

person hotpaw2    schedule 11.08.2012