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

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top