【问题标题】:Android NDK Audio CallbackAndroid NDK 音频回调
【发布时间】:2016-12-26 00:47:21
【问题描述】:

开发实时合成的 Android 应用。我正在使用 NDK 生成波形等,并使用 Java 来完成所有 UI。我有以下内容:

private Thread audioThread; 

@Override
protected void onCreate(Bundle savedInstanceState) {
     // UI Initializations here
     // Audio Thread creation:
      if (audioThread == null) {
        audioThread = new Thread() {
            public void run() {
                setPriority(Thread.MAX_PRIORITY);
                JNIWrapper.runProcess();
            }
        };
        audioThread.start();
    }
}

在我的 C++ 文件中:

void Java_com_rfoo_runProcess() {
    OPENSL_STREAM *p = android_OpenAudioDevice(SAMPLE_RATE, 0, 2, FRAME_SIZE);
    double outBuffer[FRAME_SIZE];

    while (true) {
         // Audio Processing code happens HERE
         // Write to output buffer here
         android_AudioOut(p, outBuffer, FRAME_SIZE);
    }
    android_CloseAudioDevice(p);
}

如果我没有在我的runProcess 代码中做大量的信号处理工作,现在这一切都会变得很时髦。因为有很多工作要做,所以当我尝试更改信号处理代码的参数(例如频率、ADSR 包络、滤波器截止频率等)时,我的 UI 延迟非常高,并且会导致点击。

有哪些方法可以减少这种延迟?在 iOS 和 PortAudio 中,当时间间隔/缓冲区被填满时,会定期调用音频回调。我尝试搜索存在于 Android 中的类似音频回调,但找不到。我应该编写自己的计时器来调用我的处理代码吗?

谢谢!

【问题讨论】:

  • 您是否尝试过使用 openSLES 进行音频播放?它可能是您可以获得的尽可能低的延迟,并且还使用回调机制来排队下一个缓冲区。
  • @WLGfx 这就是我正在做的事情
  • 实际上,经过仔细检查,我设置了我的 openSLES 驱动程序错误。得重做

标签: android audio callback android-ndk latency


【解决方案1】:

是的,所以我完全错误地设置了回调……事实上,我什至根本没有设置回调。

为了解决这个问题,我按照网上的一些提示,创建了一个处理回调:

// Define the callback:
typedef void (*opensl_callback) (void *context, int buffer_frames, 
                                 int output_channels, short *output_buffer);
// Declare callback:
static opensl_callback myAudioCallback;
// Define custom callback:
static void audioCallback(void *context, int buffer_frames, 
                          int output_channels, short *output_buffer) {
    // Get my object's data
    AudioData *data = (AudioData *)context;
    // Process here! Then: 
    output_buffer[i] = final_sample;
} 

我如何声明/初始化 OpenSL 流:

jboolean Java_com_rfoo_AudioProcessor_runProcess(JNIEnv *, jobject, 
                                                 int srate, int numFrames) {
    myAudioCallback = audioCallback;
    OPENSL_Stream *p = opensl_openDevice(srate, 2, numFrames, myAudioCallback, audioData);
    // Check if successful initialization
    if (!p) return JNI_FALSE;
    // Start our process:
    opensl_startProcess(p);
    return JNI_TRUE;
}

基本上opensl_openDevice()opensl_startProcess() 做了什么:

OPENSL_STREAM *opensl_openDevice(int sampleRate, int outChans, int numFrames, opensl_callback cb, void *data) {
    if (!cb) {
       return NULL;
    }
    if (outChans == 0) {
       return NULL;
    }

    SLuint32 srmillihz = convertSampleRate(sampleRate);
    if (srmillihz < 0) {
      return NULL;
    }

    OPENSL_STREAM *p = (OPENSL_STREAM *) calloc(1, sizeof(OPENSL_STREAM));
    if (!p) {
      return NULL;
    }

    p->callback = cb;
    p->data = data;
    p->isRunning = 0;

    p->outputChannels = outChans;
    p->sampleRate = sampleRate;

    p->thresholdMillis = 750.0 * numFrames / sampleRate;

    p->outputBuffer = NULL;
    p->dummyBuffer = NULL;

    p->numFrames = numFrames;
    p->outputBufferFrames = OUTPUT_BUFFERS * numFrames;

    if (openSLCreateEngine(p) != SL_RESULT_SUCCESS) {
       opensl_close(p);
       return NULL;
    }

    if (outChans) {
       int outBufSize = p->outputBufferFrames * outChans;
       if (!(openSLPlayOpen(p, srmillihz) == SL_RESULT_SUCCESS &&
        (p->outputBuffer = (short *) calloc(outBufSize, sizeof(short))))) {
       opensl_close(p);
       return NULL;
     }
  }

  LOGI("OpenSL_Stream", "Created OPENSL_STREAM(%d, %d, %d, %d)",
       sampleRate, inChans, outChans, callbackBufferFrames);
  LOGI("OpenSL_Stream", "numBuffers: %d", OUTPUT_BUFFERS);
  return p;
}

开始流代码:

int opensl_startProcess(OPENSL_STREAM *p) {
    if (p->isRunning) {
      return 0;  // Already running.
    }

    p->outputIndex = 0;
    p->readIndex = -1;

    p->outputTime.tv_sec = 0;
    p->outputTime.tv_nsec = 0;
    p->outputIntervals = 0;
    p->previousOutputIndex = 0;
    p->outputOffset = 0;

    p->lowestMargin = p->inputBufferFrames;

    if (p->playerPlay) {
      LOGI("OpenSL_Stream", "Starting player queue.");
      int i;
      for (i = 0; i < OUTPUT_BUFFERS; ++i) {
        playerCallback(p->playerBufferQueue, p);
      }
      if ((*p->playerPlay)->SetPlayState(p->playerPlay,
           SL_PLAYSTATE_PLAYING) != SL_RESULT_SUCCESS) {
        opensl_pause(p);
        return -1;
      }
   }
   p->isRunning = 1;
   return 0;
}

还有我的音频播放器回调:

static void playerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
  OPENSL_STREAM *p = (OPENSL_STREAM *) context;

  short *currentOutputBuffer = p->outputBuffer +
      (p->outputIndex % p->numFrames) * p->outputChannels;

  memset(currentOutputBuffer, 0, p->callbackBufferFrames * p->outputChannels * sizeof(short));

  p->callback(p->context, p->sampleRate, p->callbackBufferFrames,
        p->inputChannels, p->dummyBuffer,
        p->outputChannels, currentOutputBuffer);
  }
  (*bq)->Enqueue(bq, currentOutputBuffer, p->callbackBufferFrames * p->outputChannels * sizeof(short));
  p->outputIndex = nextIndex(p->outputIndex, p->callbackBufferFrames);
}

当我整理完后,我将链接我的 github opensl_stream 代码示例,以便像我这样的其他菜鸟可以轻松找到可用的示例。干杯! :)

【讨论】:

    猜你喜欢
    • 2011-10-16
    • 2015-07-30
    • 1970-01-01
    • 2013-02-21
    • 1970-01-01
    • 2013-04-23
    • 2014-08-08
    • 2012-12-23
    • 1970-01-01
    相关资源
    最近更新 更多