유닉스 소켓을 사용하여 전자 렌지 (Electron NodeJS 앱을 통해)에서 소켓에서 수신하는 Python 프로그램으로 오디오를 스트리밍하고 재생을 위해 PyAudio에 오디오를 보냅니다.JS에서 로컬 PyAudio로 이음새없는 오디오를 보내고 보내주세요
전자 응용 getUserMedia()
=> WAV 형식 => NodeJS 소켓 => 유닉스 소켓 => 파이썬 소켓 => PyAudio
나는 그것이 작동해야하지만 각 청크가 시작되거나 종료 될 때 일정한 딸깍 소리가있다. 어디에서 디버깅을 시작해야합니까?
NodeJS 응용 프로그램 (발신자) :
var net = require('net');
const nodeWav = require("node-wav");
var recorder = null;
var volume = null;
var audioInput = null;
var sampleRate = null;
var audioContext = null;
var context = null;
var outputElement = document.getElementById('output');
var outputString;
var bufferSize = 1024;
var mediaSourceIn;
// callback for navigator.mediaDevices.getUserMedia()
function audioReceiver(e) {
// creates Socket
mediaSourceIn = e;
initSocket();
}
var audioSocket;
function initSocket() {
audioSocket = net.connect('/tmp/audio_input', connected)
.catch(function(err) {
console.log("Could not connect...");
console.log(err);
});
}
function connected() {
console.log("CONNECTED TO UNIX SOCKET!");
audioSocket = this;
createRecordingTask();
}
function createRecordingTask() {
// creates the audio context
audioContext = window.AudioContext || window.webkitAudioContext;
context = new audioContext();
// retrieve the current sample rate to be used for WAV packaging
sampleRate = context.sampleRate;
// creates a gain node
volume = context.createGain();
// creates an audio node from the microphone incoming stream
audioInput = context.createMediaStreamSource(mediaSourceIn);
// connect the stream to the gain node
audioInput.connect(volume);
/* From the spec: This value controls how frequently the audioprocess event is
dispatched and how many sample-frames need to be processed each call.
Lower values for buffer size will result in a lower (better) latency.
Higher values will be necessary to avoid audio breakup and glitches */
recorder = context.createScriptProcessor(bufferSize, 2, 2);
recorder.onaudioprocess = function(e){
console.log ('recording');
var left = e.inputBuffer.getChannelData (0);
var right = e.inputBuffer.getChannelData (1);
var bf = createAudioBuffer(
new Float32Array (left),
new Float32Array (right));
upload(bf);
}
// we connect the recorder
volume.connect (recorder);
recorder.connect (context.destination);
}
function mergeBuffers(channelBuffer){
var result = new Float32Array(bufferSize);
result.set(channelBuffer); // make a copy?
return result;
}
function interleave(leftChannel, rightChannel){
var length = leftChannel.length + rightChannel.length;
var result = new Float32Array(length);
var inputIndex = 0;
for (var index = 0; index < length;){
result[index++] = leftChannel[inputIndex];
result[index++] = rightChannel[inputIndex];
inputIndex++;
}
return result;
}
function writeUTFBytes(view, offset, string){
var lng = string.length;
for (var i = 0; i < lng; i++){
view.setUint8(offset + i, string.charCodeAt(i));
}
}
function createAudioBuffer(leftchannel, rightchannel) {
// we flat the left and right channels down
var leftBuffer = mergeBuffers (leftchannel, bufferSize);
var rightBuffer = mergeBuffers (rightchannel, bufferSize);
// we interleave both channels together
var interleaved = interleave (leftBuffer, rightBuffer);
// we create our wav file
var buffer = new ArrayBuffer(44 + interleaved.length * 2);
//var buffer = new ArrayBuffer(interleaved.length * 2);
var view = new DataView(buffer);
// RIFF chunk descriptor
writeUTFBytes(view, 0, 'RIFF');
view.setUint32(4, 44 + interleaved.length * 2, true);
writeUTFBytes(view, 8, 'WAVE');
// FMT sub-chunk
writeUTFBytes(view, 12, 'fmt ');
view.setUint32(16, 16, true);
view.setUint16(20, 1, true);
// stereo (2 channels)
view.setUint16(22, 2, true);
view.setUint32(24, sampleRate, true);
view.setUint32(28, sampleRate * 4, true);
view.setUint16(32, 4, true);
view.setUint16(34, 16, true);
// data sub-chunk
writeUTFBytes(view, 36, 'data');
view.setUint32(40, interleaved.length * 2, true);
// write the PCM samples
var lng = interleaved.length;
//var index = 0;
var index = 44;
var volume = 0.6;
for (var i = 0; i < lng; i++){
view.setInt16(index, interleaved[i] * (0x7FFF * volume), true);
index += 2;
}
// our final binary blob
return Buffer.from(view.buffer);
}
function upload(thatAudio) {
if (audioSocket.writable) {
audioSocket.write(thatAudio);
} else {
console.log("DISCONNECTED!");
}
}
파이썬 프로그램 (수신기) : stream.write()
버퍼 언더런이 발생하는 경우
import socket
import os
import pyaudio
from threading import Thread
sockfile = "/tmp/audio_input"
FORMAT = pyaudio.paInt16
CHUNK = 1024
CHANNELS = 2
RATE = 44100
frames = []
if os.path.exists(sockfile):
os.remove(sockfile)
print("Opening socket...")
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
server.bind(sockfile)
server.listen(5)
conn, addr = server.accept()
print("Creating PyAudio stream...")
p = pyaudio.PyAudio()
stream = p.open(format=FORMAT,
channels = CHANNELS,
rate = RATE,
output = True,
frames_per_buffer = CHUNK,
)
print "Listening..."
singleChunkSizeBytes = 44 + (CHUNK * CHANNELS*2)
print singleChunkSizeBytes, "bytes at a time"
while True:
soundData = conn.recv(singleChunkSizeBytes)
if soundData:
stream.write(soundData, CHUNK)
server.close()
os.remove(sockfile)
예외가 슬로우되지 않았다 ... 송신 측에 아마. 따라서 WAV 포맷이 아마 범인입니다. 정상적인 WAV 파일의 헤더 바이트를보고 내가 보내는 것과 일치하는지 확인하려고합니다. – mmarkman
OK, 이제 보게됩니다 ... WAV 헤더를 포함하는 청크를 받고 있습니다. PyAudio 스트림에 쓰기 전에 처음 44 바이트를 제거해야합니다! 그 외에도,'recv()'는 요청한 것보다 적은 바이트를 반환 할 수도 있습니다. 확실히 확인해야합니다. – Matthias
헤더를 검사하지 않으므로 첫 번째 장소에서 헤더를 보내는 것이 적절하지 않을 수 있습니다. 원시 데이터를 보낼 수도 있습니다. – Matthias