Phonegap mixando arquivos de áudio
-
21-12-2019 - |
Pergunta
Estou construindo um aplicativo de karaokê usando Phonegap para Ios.
Tenho arquivos de áudio na pasta www/assets que posso reproduzir usando a função media.play()
Isso permite ao usuário ouvir a faixa de apoio.Enquanto a mídia está sendo reproduzida, outra instância de mídia está gravando.
Assim que a gravação terminar, preciso colocar o arquivo de gravação de voz sobre a trilha de apoio e não tenho ideia de como fazer isso.
Uma abordagem que pensei que poderia funcionar é usar a API WEb Audio - tenho o seguinte código que tirei de Rochas HTML5 O que carrega os dois arquivos em um AudioContext e me permite reproduzir os dois simultaneamente.No entanto, o que eu gostaria de fazer é gravar os dois buffers em um único arquivo .wav.Existe alguma maneira de combinar source1 e source2 em um único novo arquivo?
var context;
var bufferLoader;
function init() {
// Fix up prefixing
window.AudioContext = window.AudioContext || window.webkitAudioContext;
context = new AudioContext();
bufferLoader = new BufferLoader(
context,
[
'backingTrack.wav',
'voice.wav',
],
finishedLoading
);
bufferLoader.load();
}
function finishedLoading(bufferList) {
// Create two sources and play them both together.
var source1 = context.createBufferSource();
var source2 = context.createBufferSource();
source1.buffer = bufferList[0];
source2.buffer = bufferList[1];
source1.connect(context.destination);
source2.connect(context.destination);
source1.start(0);
source2.start(0);
}
function BufferLoader(context, urlList, callback) {
this.context = context;
this.urlList = urlList;
this.onload = callback;
this.bufferList = new Array();
this.loadCount = 0;
}
BufferLoader.prototype.loadBuffer = function(url, index) {
// Load buffer asynchronously
var request = new XMLHttpRequest();
request.open("GET", url, true);
request.responseType = "arraybuffer";
var loader = this;
request.onload = function() {
// Asynchronously decode the audio file data in request.response
loader.context.decodeAudioData(
request.response,
function(buffer) {
if (!buffer) {
alert('error decoding file data: ' + url);
return;
}
loader.bufferList[index] = buffer;
if (++loader.loadCount == loader.urlList.length)
loader.onload(loader.bufferList);
},
function(error) {
console.error('decodeAudioData error', error);
}
);
}
request.onerror = function() {
alert('BufferLoader: XHR error');
}
request.send();
}
BufferLoader.prototype.load = function() {
for (var i = 0; i < this.urlList.length; ++i)
this.loadBuffer(this.urlList[i], i);
}
Pode haver algo nesta solução Como faço para converter uma série de dados de áudio em um arquivo wav? Pelo que entendi, eles estão intercalando os dois buffers e codificando-os como .wav, mas não consigo descobrir onde eles estão gravando-os em um arquivo (salvando o novo arquivo wav). Alguma idéia?
A resposta abaixo - realmente não ajuda, pois estou usando Web Audio Api (javascript) e não IOS
Solução
A solução foi usar o offlineAudioContext
As etapas foram:1.Carregue os dois arquivos como buffers usando o BufferLoader 2.Crie um offlineAudiocontext 3.Conecte os dois buffers ao OffllineAudiocontext 4.Inicie os dois buffers 5.Use a função offline Startrendering 6.Defina a função offline.oncomplete para obter um controle sobre o renderedBuffer.
Aqui está o código:
offline = new webkitOfflineAudioContext(2, voice.buffer.length, 44100);
vocalSource = offline.createBufferSource();
vocalSource.buffer = bufferList[0];
vocalSource.connect(offline.destination);
backing = offline.createBufferSource();
backing.buffer = bufferList[1];
backing.connect(offline.destination);
vocalSource.start(0);
backing.start(0);
offline.oncomplete = function(ev){
alert(bufferList);
playBackMix(ev);
console.log(ev.renderedBuffer);
sendWaveToPost(ev);
}
offline.startRendering();
Outras dicas
Eu sugeriria misturar o PCM diretamente.Se você inicializar um buffer que se sobrepõe aos intervalos de tempo de ambas as trilhas, a fórmula será aditiva:
misturar(a,b) = a+b - a*b/65535.
Esta fórmula depende de inteiros não assinados de 16 bits.Aqui está um exemplo:
SInt16 *bufferA, SInt16 *bufferB;
NSInteger bufferLength;
SInt16 *outputBuffer;
for ( NSInteger i=0; i<bufferLength; i++ ) {
if ( bufferA[i] < 0 && bufferB[i] < 0 ) {
// If both samples are negative, mixed signal must have an amplitude between
// the lesser of A and B, and the minimum permissible negative amplitude
outputBuffer[i] = (bufferA[i] + bufferB[i]) - ((bufferA[i] * bufferB[i])/INT16_MIN);
} else if ( bufferA[i] > 0 && bufferB[i] > 0 ) {
// If both samples are positive, mixed signal must have an amplitude between the greater of
// A and B, and the maximum permissible positive amplitude
outputBuffer[i] = (bufferA[i] + bufferB[i]) - ((bufferA[i] * bufferB[i])/INT16_MAX);
} else {
// If samples are on opposite sides of the 0-crossing, mixed signal should reflect
// that samples cancel each other out somewhat
outputBuffer[i] = bufferA[i] + bufferB[i];
}
}
Esta pode ser uma maneira muito eficaz de lidar com áudio assinado de 16 bits. Vá aqui para a fonte.