Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Created February 27, 2022 00:32
Show Gist options
  • Save MCJack123/86472ceab601ddfc4b4cee96c0f97490 to your computer and use it in GitHub Desktop.
Save MCJack123/86472ceab601ddfc4b4cee96c0f97490 to your computer and use it in GitHub Desktop.
Microphone to WebSocket loopback server (requires Poco and PortAudio)
/*
* Microphone to WebSocket loopback server
* Requires Poco and PortAudio
* Compile with g++ -o micserver micserver.cpp -lPocoFoundation -lPocoNet -lPocoUtil -lportaudio
*
* MIT License
*
* Copyright (c) 2022 JackMacWindows
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <Poco/Net/NetException.h>
#include <Poco/Net/HTTPRequest.h>
#include <Poco/Net/HTTPRequestHandler.h>
#include <Poco/Net/HTTPResponse.h>
#include <Poco/Net/HTTPServer.h>
#include <Poco/Net/HTTPServerRequest.h>
#include <Poco/Net/HTTPServerResponse.h>
#include <Poco/Net/WebSocket.h>
#include <portaudio.h>
#include <csignal>
#include <iostream>
#include <mutex>
#include <condition_variable>
using namespace Poco::Net;
static bool running = true;
static uint8_t buf[32768];
std::mutex lock;
std::condition_variable notify;
class WebSocketServer: public HTTPRequestHandler {
public:
void handleRequest(HTTPServerRequest &request, HTTPServerResponse &response) override {
try {
WebSocket ws(request, response);
while (running) {
std::unique_lock<std::mutex> l(lock);
notify.wait(l);
ws.sendFrame(buf, 32768, WebSocket::FRAME_BINARY);
}
try {ws.shutdown();} catch (...) {}
} catch (Poco::Exception &e) {
std::cerr << "WebSocket exception: " << e.displayText() << "\n";
} catch (std::exception &e) {
std::cerr << "WebSocket exception: " << e.what() << "\n";
}
}
class Factory: public HTTPRequestHandlerFactory {
public:
HTTPRequestHandler* createRequestHandler(const HTTPServerRequest&) override {
return new WebSocketServer;
}
};
};
static int recordCallback(const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) {
memcpy(buf, inputBuffer, framesPerBuffer);
notify.notify_all();
return paContinue;
}
static void sighandler(int sig) {
running = false;
}
int main(int argc, const char * argv[]) {
if (argc < 2) {
std::cerr << "Usage: " << argv[0] << " <port> [device name]\n";
return 1;
}
int port = std::stoi(argv[1]);
std::string device;
if (argc > 2) device = argv[2];
std::cout << "Initializing...\n";
PaError err = Pa_Initialize();
if (err) {
std::cerr << "Error while initializing: " << Pa_GetErrorText(err) << "\n";
return err;
}
PaStream * stream;
PaStreamParameters inputParameters;
if (!device.empty()) {
inputParameters.device = paNoDevice;
for (PaDeviceIndex i = 0; i < Pa_GetDeviceCount(); i++) {
const PaDeviceInfo * info = Pa_GetDeviceInfo(i);
if (info->name == device && info->maxInputChannels > 0) {
inputParameters.device = i;
break;
}
}
} else inputParameters.device = Pa_GetDefaultInputDevice();
if (inputParameters.device == paNoDevice) {
std::cerr << "Error: No input device found\n";
Pa_Terminate();
return 2;
}
inputParameters.channelCount = 1;
inputParameters.sampleFormat = paUInt8;
inputParameters.suggestedLatency = Pa_GetDeviceInfo(inputParameters.device)->defaultLowInputLatency;
inputParameters.hostApiSpecificStreamInfo = NULL;
std::cout << "Opening...\n";
err = Pa_OpenStream(&stream, &inputParameters, NULL, 48000, 32768, paNoFlag, recordCallback, NULL);
if (err) {
std::cerr << "Error while opening stream: " << Pa_GetErrorText(err) << "\n";
Pa_Terminate();
return err;
}
std::cout << "Starting stream...\n";
err = Pa_StartStream(stream);
if (err) {
std::cerr << "Error while starting stream: " << Pa_GetErrorText(err) << "\n";
Pa_Terminate();
return err;
}
std::cout << "Starting server...\n";
HTTPServer srv(new WebSocketServer::Factory, port);
srv.start();
signal(SIGINT, sighandler);
std::cout << "Now listening on port " << port << " with device '" << Pa_GetDeviceInfo(inputParameters.device)->name << "'\n";
while (running && Pa_IsStreamActive(stream) == 1) Pa_Sleep(1000);
srv.stopAll();
Pa_StopStream(stream);
Pa_CloseStream(stream);
Pa_Terminate();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment