Captura de áudio personalizada
Use uma fonte de áudio externa em vez do microfone do dispositivo
Descreve como implementar a captura de áudio personalizada com o SDK do cliente AOQ, incluindo a adição de streams de áudio externos, envio de dados PCM e gerenciamento do ciclo de vida do stream.
Visão geral
O módulo de áudio integrado do SDK do cliente AOQ cobre necessidades básicas de áudio, mas em alguns cenários o módulo de captura integrado pode não ser suficiente. A captura de áudio personalizada é útil quando você precisa:
- Contornar conflitos de dispositivos de captura de áudio.
- Alimente dados de áudio de um sistema de captura personalizado ou arquivo de áudio no SDK para transmissão.
- Publique áudio gerado por AI TTS por meio do SDK.
O AOQ Client SDK oferece suporte flexível à captura personalizada, permitindo que você gerencie seus próprios dispositivos e fontes de áudio com base no seu caso de uso. Os dados de fluxo de áudio externo são misturados com áudio capturado internamente antes de serem publicados.
Código de exemplo
Em breve.
Pré-requisitos
- Uma instância do mecanismo foi criada chamando
createEngine. - Uma conexão com o servidor foi estabelecida (o
onConnectionStatusChangeretorno de chamada foi relatadoAoqConnectionStatusConnected).
Implementação
1. Iniciar ou parar a captura de áudio
Comece a captura de áudio primeiro. Os dados de fluxo de áudio externo são misturados com dados de captura interna antes de serem publicados. Se você não precisar de captura de microfone interno, defina isExternal=true para desativar o dispositivo de captura interno.
// Option 1: Internal capture — external audio stream data is mixed with microphone data
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = false; // Use internal microphone capture
config.isVoipMode = false;
engine.startAudioCapture(config);
// Option 2: No internal capture — only external audio stream data is published
AoqClientEngine.AoqAudioCaptureConfig config = new AoqClientEngine.AoqAudioCaptureConfig();
config.isExternal = true; // Do not open the microphone; external audio stream provides the data
engine.startAudioCapture(config);
2. Após conectar, adicione o fluxo de áudio externo
Uma vez que onConnectionStatusChange relatórios de retorno de chamada AoqConnectionStatusConnected, ligue addAudioExternalStream para adicionar o fluxo de áudio externo. Atribuir um único streamId que você usará para enviar dados e gerenciar o fluxo.
Se você precisar de processamento 3A (cancelamento de eco, supressão de ruído, controle automático de ganho), defina o enable3A parâmetro em AoqAudioExternalStreamConfig.
// Add the stream after confirming connection in the onConnectionStatusChange callback
@Override
public void onConnectionStatusChange(AoqClientEngine.AoqConnectionStatus status) {
if (status == AoqClientEngine.AoqConnectionStatus.AoqConnectionStatusConnected) {
addExternalAudioStream();
}
}
private void addExternalAudioStream() {
AoqClientEngine.AoqAudioExternalStreamConfig config = new AoqClientEngine.AoqAudioExternalStreamConfig();
config.sampleRate = 48000; // Sample rate — must match the actual audio data
config.channels = 1; // Channel count
config.publishVolume = 100; // Publishing volume [0-100]
config.playoutVolume = 0; // Local playback volume [0-100]; 0 = no local playback
config.maxBufferDuration = 1000; // Maximum buffer duration (milliseconds)
config.enable3A = true; // Whether to apply 3A processing to input PCM
String streamId = "external_audio_1";
int ret = engine.addAudioExternalStream(streamId, config);
if (ret == 0) {
mExternalStreamId = streamId;
}
}
Parâmetros:
| Parâmetro | Tipo | Padrão | Descrição |
|---|---|---|---|
| trackType | AoqTrackType | AoqTrackTypeÁudio | Tipo de faixa de áudio |
| tipo de codec | AoqEncoderType | AoqEncoderTypeAudioPCM | Formato de fluxo de áudio |
| canais | interno | 1 | Contagem de canais |
| taxa de amostra | interno | 48.000 | Taxa de amostragem (Hz) |
| volume de reprodução | interno | 100 | Volume de reprodução [0-100] |
| publicarVolume | interno | 100 | Volume de publicação [0-100] |
| maxBufferDuration | interno | 1000 | Duração máxima do buffer (milissegundos) |
| habilitar3A | booleano | falso | Se deve aplicar o processamento 3A ao PCM de entrada |
3. Capture ou obtenha dados PCM
Implemente sua própria captura de áudio ou fonte de dados para seu caso de uso e, em seguida, alimente os dados no SDK. As fontes de dados comuns incluem:
- Captura de microfone: Use o Android AudioRecord para capturar dados PCM.
- Leitura de arquivo: analisa dados PCM de um arquivo de áudio PCM ou WAV local.
- AI TTS: Obtenha dados PCM de um mecanismo de síntese de fala.
- Fluxo de rede: decodifica dados PCM de um fluxo de áudio de rede.
Os dados de áudio devem estar no formato PCM. Grave a taxa de amostragem, contagem de canais e outros parâmetros para construir o AoqAudioFrameData objeto.
4. Envie dados de áudio para o SDK por meio do ID do stream
Ligar pushAudioExternalStreamData para alimentar os dados PCM capturados no SDK.
- Para captura de hardware: use 10 ms por quadro e chame push sempre que houver dados disponíveis.
- Para entrada baseada em arquivo: use 40 ms por quadro e durma 30 ms entre os push.
- Manter um
runningbandeira. Saia do loop push quando o mecanismo for encerrado ou o ID do fluxo for removido.
// Member variable: flag to control the push loop
private volatile boolean mPushRunning = false;
// Push a single audio frame
private void pushAudioData(byte[] audioData, int bytesRead) {
if (engine == null || mExternalStreamId == null || bytesRead <= 0) {
return;
}
int channels = 1;
int bytesPerSample = 2; // 16-bit PCM
int sampleRate = 48000;
// Build the audio frame data object
AoqClientEngine.AoqAudioFrameData frameData = new AoqClientEngine.AoqAudioFrameData();
frameData.dataPtr = audioData;
frameData.dataSize = bytesRead;
frameData.numOfSamples = bytesRead / (channels * bytesPerSample);
frameData.bytesPerSample = bytesPerSample;
frameData.numOfChannels = channels;
frameData.samplesPerSec = sampleRate;
// Push data and handle buffer-full errors
int ret;
final int WAIT_MS = 30;
do {
// Check whether the running flag and stream ID are still valid
if (!mPushRunning || mExternalStreamId == null) {
break;
}
ret = engine.pushAudioExternalStreamData(mExternalStreamId, frameData);
if (ret == 110) { // AoqErrorCodeAudioExternalBufferFull
try {
Thread.sleep(WAIT_MS);
} catch (InterruptedException e) {
break;
}
} else {
break;
}
} while (true);
}
Notas importantes:
- Comece a enviar dados somente depois que a conexão for estabelecida e o fluxo de áudio externo tiver sido adicionado.
- Definir
AoqAudioFrameData'snumOfSamplespara corresponder ao comprimento real dos dados. pushAudioExternalStreamDatapode falhar se o buffer interno estiver cheio (código de erro 110). Tente novamente após um pequeno atraso.- Para captura em tempo real, use frames de 10 ms e chame push sempre que houver dados disponíveis. Lidar com o código de erro 110.
- Para entrada baseada em arquivo, use quadros de 40 ms e chame push a cada 30 ms. Lidar com o código de erro 110.
- Antes que o motor desligue (
destroy) ou um ID de stream for removido, definamPushRunning = falsepara interromper o loop push e evitar o acesso aos recursos liberados.
5. Remova o fluxo de áudio externo
Quando a captura personalizada não for mais necessária, interrompa o push loop primeiro e depois chame removeAudioExternalStream para remover o fluxo de áudio externo.
// Stop pushing first
stopPushAudio();
// Then remove the external audio stream
engine.removeAudioExternalStream(mExternalStreamId);
mExternalStreamId = null;