diff --git a/index.js b/index.js
index 62c7b4e..3082dd6 100644
--- a/index.js
+++ b/index.js
@@ -17,7 +17,8 @@ var bodyParser = require('body-parser');
var baseRouter = express.Router();
var fsw = require('fs').promises;
var fs = require('fs');
-
+var PulseAudio = require('pulseaudio2');
+var pulse = new PulseAudio();
//// Server Paths Main ////
app.engine('html', require('ejs').renderFile);
@@ -130,6 +131,34 @@ io.on('connection', async function (socket) {
socket.on('createfolder', createFolder);
});
+//// PCM Audio Wrapper ////
+aio = socketIO(http, {path: SUBFOLDER + 'audio/socket.io'});
+aio.on('connection', async function (socket) {
+ var record;
+ let id = socket.id;
+
+ async function open() {
+ if (record) record.end();
+ record = pulse.createRecordStream({
+ channels: 2,
+ rate: 48000,
+ format: 'S16LE',
+ });
+ record.on('connection', function(){
+ record.on('data', function(chunk) {
+ aio.sockets.to(id).emit('audio', chunk);
+ });
+ });
+ }
+ async function close() {
+ if (record) record.end();
+ }
+
+
+ // Incoming socket requests
+ socket.on('open', open);
+ socket.on('close', close);
+});
// Spin up application on 6900
app.use(SUBFOLDER, baseRouter);
diff --git a/package.json b/package.json
index cac04fe..cb9b067 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,12 @@
{
"name": "kclient",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Kclient is a wrapper for KasmVNC to add functionality to a containerized environment",
"main": "index.js",
"dependencies": {
"ejs": "^3.1.8",
"express": "^4.18.2",
+ "pulseaudio2": "^0.5.5",
"socket.io": "^4.6.0"
},
"devDependencies": {},
diff --git a/public/css/kclient.css b/public/css/kclient.css
index d564244..be69ee3 100644
--- a/public/css/kclient.css
+++ b/public/css/kclient.css
@@ -69,6 +69,10 @@
filter: invert(100%) sepia(0%) saturate(0%) hue-rotate(82deg) brightness(105%) contrast(105%);
}
+.icons-selected {
+ background: rgba(0, 0, 0, 0.3);
+}
+
.icons:hover {
background: rgba(0, 0, 0, 0.3);
}
diff --git a/public/css/speaker.svg b/public/css/speaker.svg
new file mode 100644
index 0000000..c355f43
--- /dev/null
+++ b/public/css/speaker.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/public/index.html b/public/index.html
index 81120e4..127cbd8 100644
--- a/public/index.html
+++ b/public/index.html
@@ -9,13 +9,16 @@
+
+
diff --git a/public/js/kclient.js b/public/js/kclient.js
index 330ecd3..b027f32 100644
--- a/public/js/kclient.js
+++ b/public/js/kclient.js
@@ -58,3 +58,36 @@ function fullscreen() {
}
}
}
+
+// Websocket comms for audio
+var host = window.location.hostname;
+var port = window.location.port;
+var protocol = window.location.protocol;
+var path = window.location.pathname;
+var socket = io(protocol + '//' + host + ':' + port, { path: path + 'audio/socket.io'});
+var player = {};
+
+function audio() {
+ if (('audioCtx' in player) && (player.audioCtx)) {
+ player.destroy();
+ socket.emit('close', '');
+ $('#audioButton').removeClass("icons-selected");
+ return;
+ }
+ socket.emit('open', '');
+ player = new PCMPlayer({
+ encoding: '16bitInt',
+ channels: 2,
+ sampleRate: 48000,
+ flushingTime: 10
+ });
+ $('#audioButton').addClass("icons-selected");
+}
+
+function processAudio(data) {
+ let buf = new Uint16Array(data);
+ player.feed(buf);
+ player.volume(1) ;
+}
+
+socket.on('audio', processAudio);
diff --git a/public/js/pcm-player.min.js b/public/js/pcm-player.min.js
new file mode 100644
index 0000000..42c3362
--- /dev/null
+++ b/public/js/pcm-player.min.js
@@ -0,0 +1 @@
+function PCMPlayer(t){this.init(t)}PCMPlayer.prototype.init=function(t){this.option=Object.assign({},{encoding:"16bitInt",channels:1,sampleRate:8e3,flushingTime:1e3},t),this.samples=new Float32Array,this.flush=this.flush.bind(this),this.interval=setInterval(this.flush,this.option.flushingTime),this.maxValue=this.getMaxValue(),this.typedArray=this.getTypedArray(),this.createContext()},PCMPlayer.prototype.getMaxValue=function(){var t={"8bitInt":128,"16bitInt":32768,"32bitInt":2147483648,"32bitFloat":1};return t[this.option.encoding]?t[this.option.encoding]:t["16bitInt"]},PCMPlayer.prototype.getTypedArray=function(){var t={"8bitInt":Int8Array,"16bitInt":Int16Array,"32bitInt":Int32Array,"32bitFloat":Float32Array};return t[this.option.encoding]?t[this.option.encoding]:t["16bitInt"]},PCMPlayer.prototype.createContext=function(){this.audioCtx=new(window.AudioContext||window.webkitAudioContext),this.gainNode=this.audioCtx.createGain(),this.gainNode.gain.value=1,this.gainNode.connect(this.audioCtx.destination),this.startTime=this.audioCtx.currentTime},PCMPlayer.prototype.isTypedArray=function(t){return t.byteLength&&t.buffer&&t.buffer.constructor==ArrayBuffer},PCMPlayer.prototype.feed=function(t){if(this.isTypedArray(t)){t=this.getFormatedValue(t);var e=new Float32Array(this.samples.length+t.length);e.set(this.samples,0),e.set(t,this.samples.length),this.samples=e}},PCMPlayer.prototype.getFormatedValue=function(t){t=new this.typedArray(t.buffer);var e,i=new Float32Array(t.length);for(e=0;e