I'm trying to make an internet radio station and I would like to frequently change songs and overlay sound freely. I'd like to rate-limit the audio so the feed can be altered before its sent out. I'd also like to serve continuous content if that is possible
So far, we have a close to successful attempt with websockets, but the quality is a bit messy
Here's the code:
server.js
const express = require('express');
const app = express()
const http = require('http')
const server = http.createServer(app)
const { Server } = require("socket.io")
const io = new Server(server)
const fs = require('fs')
const SRC_PATH = 'src.wav'
const PACKET_SIZE = 6400
let PACKET = 0
function getpacket(socket){
const file_descriptor = fs.openSync(SRC_PATH, 'r', null)
const read_offset = PACKET * PACKET_SIZE
const buffer = Buffer.alloc(PACKET_SIZE)
const buffer_write_offset = 0
const num_bytes_to_read = PACKET_SIZE
const num_bytes_read = fs.readSync(file_descriptor, buffer, buffer_write_offset, num_bytes_to_read, read_offset)
fs.closeSync(file_descriptor)
console.log(`Sending packet ${PACKET}`)
socket.emit("data", buffer)
PACKET++
}
app.use('/', express.static('.'))
io.on('connection', (socket) => {
console.log("connected...")
socket.on("get", ()=>{getpacket(socket)})
})
server.listen(3000, () => {
console.log('listening on *:3000');
})
index.html
<!DOCTYPE html>
<html>
<head>
<title>Testing</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
</head>
<body>
<div onclick="listen()">Click to Listen</div>
<script>
const socketio = io()
const SAMPLE_RATE = 32000 // samples/second
async function listen(){
// Set up the new audio context
const audioContext = new AudioContext()
socketio.once("data", (arrayBuff)=>{
const buffer = new Uint8Array(arrayBuff)
addTobuffer(buffer, audioContext)
})
requestData()
}
function requestData(){
socketio.emit("get")
}
async function addTobuffer(data, audioContext){
// Set up the new audio source
const audioSource = await audioContext.createBufferSource()
// create audio buffer from data,
const audioBuffer = await createAudioBuffer(audioContext,data)
// Asign the data buffer to the audioSource
audioSource.buffer = audioBuffer
// Connect the audio source to the audio context
audioSource.connect(audioContext.destination)
audioSource.start(0)
// wait until just before the end and then get more data
const packetLength = (data.length/SAMPLE_RATE)*1000-10
await new Promise(resolve=>setTimeout(resolve,packetLength))
socketio.once("data", (arrayBuff)=>{
const buffer = new Uint8Array(arrayBuff)
addTobuffer(buffer, audioContext)
})
requestData()
}
async function createAudioBuffer(audioContext,data){
/* uint8 pcm to float */
const number_of_channels = 1
const number_of_bytes = data.length
const audioBuffer = audioContext.createBuffer(number_of_channels, number_of_bytes, SAMPLE_RATE)
const nowBuffering = audioBuffer.getChannelData(0)
for (let index=0; index<number_of_bytes;index++){
const thirtytwofloat = new Float32Array(1)
thirtytwofloat[0] = (data[index]-(255/2))/255
nowBuffering[index] = thirtytwofloat[0]
}
return audioBuffer
}
</script>
</body>
</html>
And to generate the strangely formatted PCM WAV:
ffmpeg -i src.mp3 -ar 32000 -ac 1 -acodec pcm_u8 src.wav
Is there a way to get cleaner audio output? Thank you in advance for your help!