Обратные вызовы PortAudio и изменение переменной в другом месте

Я использую API обратного вызова PortAudio для разработки библиотеки обратной связи обработки сигналов.

Я хотел бы добавить ветку, которая зависит от флага внутри обратного вызова, например

int pa_callback(const void *inputuffer,
                 void *outputBuffer,
                 unsigned long frameCount,
                 const PaStreamCallbackTimeInfo *timeInfo,
                 PaStreamCallbackFlags statusFlags,
                 void *userData)
{
  if (do_something_flag) {
    do_something(inputBuffer, outputBuffer, frameCount);
  } else {
    do_something_else(inputBuffer, outputBuffer, frameCount);
  }

  return paContinue;
}

Где do_something_flag устанавливается в другом месте моей программы через равные промежутки времени.

В документации обратного вызова PortAudio указано:

Прежде чем мы начнем, важно понять, что обратный вызов — это деликатное место. Это связано с тем, что некоторые системы выполняют обратный вызов в специальном потоке или обработчике прерывания, и он редко обрабатывается так же, как остальная часть вашего кода. Для большинства современных систем вы не сможете вызвать сбои, выполнив запрещенные вызовы в обратном вызове, но если вы хотите, чтобы ваш код воспроизводил звук без сбоев, вам нужно убедиться, что вы избегаете вызовов функций, которые могут занимать неограниченное время. количество времени на выполнение. Что именно это зависит от вашей платформы, но почти наверняка включает следующее: выделение/освобождение памяти, ввод-вывод (включая файловый ввод-вывод, а также консольный ввод-вывод, например printf()), переключение контекста (например, exec () или yield()), операции с мьютексами или что-то еще, что может зависеть от ОС. Если вы считаете, что короткие критические разделы безопасны, прочтите об инверсии приоритетов.

Меня не волнует атомарность do_something_flag. То есть мне все равно, сколько циклов потребуется, чтобы получить правильное значение (в пределах разумного). Согласно документации, похоже, я не могу использовать мьютексы для установки/чтения этой переменной.

1) Какие у меня есть варианты?

2) Если я сделаю его глобальным и установлю его в другой части моей программы (в другом потоке), что произойдет в худшем случае? Опять же, я имею в виду повреждение данных вплоть до сбоя программы и т. д.

Есть ли правильный способ сделать это?


person justynnuff    schedule 13.07.2016    source источник


Ответы (1)


Я не совсем уверен, что вы именно пытаетесь сделать, но я предполагаю, что это то, о чем спрашивает ваш заголовок - «Изменение переменной в другом месте».

Возьмем такой пример: у вас есть переменная frequency, которая меняется со временем. Как вы получаете доступ к этому? Ну, у вас есть общий указатель в обратном вызове с именем userData. Это может указывать на что угодно - структуру данных, массив и т. д. Я действительно не помню, как часто вызывается функция обратного вызова (это довольно часто... я бы не беспокоился о скорости), но userData позволяет вам иметь переменные который можно изменить в вашем основном потоке, в то время как указатель в аудиопотоке позволяет вам получить к нему доступ непосредственно в памяти... Мои знания о безопасности потоков не самые лучшие, и извините, если это не лучшее объяснение, но я могу по крайней мере показать вам, как это сделать с помощью кода (ниже).

Вот как я обычно это делаю, но вам не нужно делать это самостоятельно; Я установил структуру в верхней части моего файла следующим образом:

typedef struct {
    float freq;
    float vol;
}paData;

Очевидно, вы инициализируете это где-то в своем коде (возможно, в вызове функции main) и открываете аудиопоток как таковой (data имеет тип paData):

/* Open audio stream */
err = Pa_OpenStream(&(*stream),
        &inputParameters,
        &outputParameters,
        SAMPLE_RATE, bufSize, paNoFlag, 
        paCallback, &data);

После его открытия вы можете получить обратный вызов следующим образом:

static int pa_callback(const void *inputBffer,
             void *outputBuffer,
             unsigned long frameCount,
             const PaStreamCallbackTimeInfo *timeInfo,
             PaStreamCallbackFlags statusFlags,
             void *userData)
{
    // cast data so we can use it
    paData *data = (paData *)userData;

    // what's our frequency?
    printf("%f\n", data->freq);

    /* Do something with your code here */

    return paContinue;
}

Надеюсь, это поможет.

person yun    schedule 15.07.2016
comment
Да, это прекрасно. Я так увлекся потокобезопасностью, атомарностью и глобальными переменными, что не увидел простого ответа: PA дает вам эту механику. - person justynnuff; 16.07.2016
comment
Да, не беспокойтесь, самостоятельно изучить новый API сложно... К счастью, когда я изучал PortAudio, у меня был отличный учитель, так что это намного облегчило мне задачу. Удачи вам в том, что вы делаете! - person yun; 16.07.2016
comment
Привет, @yun.cloud, недавно я начал собирать и обрабатывать речь с помощью portaudio, и при его реализации у меня возникли некоторые проблемы. Я разместил вопрос здесь, в stackoverflow, здесь (stackoverflow.com/questions/44645466/). Любая помощь приветствуется. - person Ashish K; 22.06.2017