Compare commits

..

No commits in common. "sapi4" and "master" have entirely different histories.

136 changed files with 612 additions and 56856 deletions

View file

@ -1,44 +0,0 @@
BasedOnStyle: Google
# force T* or T&
DerivePointerAlignment: false
PointerAlignment: Left
TabWidth: 4
IndentWidth: 4
UseTab: Always
IndentPPDirectives: BeforeHash
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: true
BinPackArguments: true
BinPackParameters: true
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: false
ColumnLimit: 150
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ContinuationIndentWidth: 0
# turning this on causes major issues with initializer lists
Cpp11BracedListStyle: false
SpaceBeforeCpp11BracedList: true
FixNamespaceComments: true
NamespaceIndentation: All
ReflowComments: true
SortIncludes: CaseInsensitive
SortUsingDeclarations: true
SpacesInSquareBrackets: false
SpaceBeforeParens: Never
SpacesBeforeTrailingComments: 1

View file

@ -1,11 +0,0 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
indent_style = tab
indent_size = 4
# specifically for YAML
[{yml, yaml}]
indent_style = space

370
.gitignore vendored
View file

@ -1,11 +1,363 @@
# ccls/clangd
.cache/
**/bin/
**/obj/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# on your own machine, please.
/speech2/build
/speech2/build-debug
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# cmake tools is viciously unaware of subdirectories
/build
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd

201
.gitmodules vendored
View file

@ -1,201 +0,0 @@
[submodule "speech2/third_party/boost/algorithm"]
path = speech2/third_party/boost/algorithm
url = https://github.com/boostorg/algorithm.git
[submodule "speech2/third_party/boost/align"]
path = speech2/third_party/boost/align
url = https://github.com/boostorg/align.git
[submodule "speech2/third_party/boost/array"]
path = speech2/third_party/boost/array
url = https://github.com/boostorg/array.git
[submodule "speech2/third_party/boost/assert"]
path = speech2/third_party/boost/assert
url = https://github.com/boostorg/assert.git
[submodule "speech2/third_party/boost/atomic"]
path = speech2/third_party/boost/atomic
url = https://github.com/boostorg/atomic.git
[submodule "speech2/third_party/boost/bind"]
path = speech2/third_party/boost/bind
url = https://github.com/boostorg/bind.git
[submodule "speech2/third_party/boost/charconv"]
path = speech2/third_party/boost/charconv
url = https://github.com/boostorg/charconv.git
[submodule "speech2/third_party/boost/chrono"]
path = speech2/third_party/boost/chrono
url = https://github.com/boostorg/chrono.git
[submodule "speech2/third_party/boost/circular_buffer"]
path = speech2/third_party/boost/circular_buffer
url = https://github.com/boostorg/circular_buffer.git
[submodule "speech2/third_party/boost/concept_check"]
path = speech2/third_party/boost/concept_check
url = https://github.com/boostorg/concept_check.git
[submodule "speech2/third_party/boost/config"]
path = speech2/third_party/boost/config
url = https://github.com/boostorg/config.git
[submodule "speech2/third_party/boost/container"]
path = speech2/third_party/boost/container
url = https://github.com/boostorg/container.git
[submodule "speech2/third_party/boost/container_hash"]
path = speech2/third_party/boost/container_hash
url = https://github.com/boostorg/container_hash.git
[submodule "speech2/third_party/boost/context"]
path = speech2/third_party/boost/context
url = https://github.com/boostorg/context.git
[submodule "speech2/third_party/boost/conversion"]
path = speech2/third_party/boost/conversion
url = https://github.com/boostorg/conversion.git
[submodule "speech2/third_party/boost/core"]
path = speech2/third_party/boost/core
url = https://github.com/boostorg/core.git
[submodule "speech2/third_party/boost/coroutine"]
path = speech2/third_party/boost/coroutine
url = https://github.com/boostorg/coroutine.git
[submodule "speech2/third_party/boost/date_time"]
path = speech2/third_party/boost/date_time
url = https://github.com/boostorg/date_time.git
[submodule "speech2/third_party/boost/describe"]
path = speech2/third_party/boost/describe
url = https://github.com/boostorg/describe.git
[submodule "speech2/third_party/boost/detail"]
path = speech2/third_party/boost/detail
url = https://github.com/boostorg/detail.git
[submodule "speech2/third_party/boost/endian"]
path = speech2/third_party/boost/endian
url = https://github.com/boostorg/endian.git
[submodule "speech2/third_party/boost/exception"]
path = speech2/third_party/boost/exception
url = https://github.com/boostorg/exception.git
[submodule "speech2/third_party/boost/filesystem"]
path = speech2/third_party/boost/filesystem
url = https://github.com/boostorg/filesystem.git
[submodule "speech2/third_party/boost/function"]
path = speech2/third_party/boost/function
url = https://github.com/boostorg/function.git
[submodule "speech2/third_party/boost/functional"]
path = speech2/third_party/boost/functional
url = https://github.com/boostorg/functional.git
[submodule "speech2/third_party/boost/function_types"]
path = speech2/third_party/boost/function_types
url = https://github.com/boostorg/function_types.git
[submodule "speech2/third_party/boost/fusion"]
path = speech2/third_party/boost/fusion
url = https://github.com/boostorg/fusion.git
[submodule "speech2/third_party/boost/integer"]
path = speech2/third_party/boost/integer
url = https://github.com/boostorg/integer.git
[submodule "speech2/third_party/boost/intrusive"]
path = speech2/third_party/boost/intrusive
url = https://github.com/boostorg/intrusive.git
[submodule "speech2/third_party/boost/io"]
path = speech2/third_party/boost/io
url = https://github.com/boostorg/io.git
[submodule "speech2/third_party/boost/iterator"]
path = speech2/third_party/boost/iterator
url = https://github.com/boostorg/iterator.git
[submodule "speech2/third_party/boost/json"]
path = speech2/third_party/boost/json
url = https://github.com/boostorg/json.git
[submodule "speech2/third_party/boost/leaf"]
path = speech2/third_party/boost/leaf
url = https://github.com/boostorg/leaf.git
[submodule "speech2/third_party/boost/lexical_cast"]
path = speech2/third_party/boost/lexical_cast
url = https://github.com/boostorg/lexical_cast.git
[submodule "speech2/third_party/boost/lockfree"]
path = speech2/third_party/boost/lockfree
url = https://github.com/boostorg/lockfree.git
[submodule "speech2/third_party/boost/logic"]
path = speech2/third_party/boost/logic
url = https://github.com/boostorg/logic.git
[submodule "speech2/third_party/boost/move"]
path = speech2/third_party/boost/move
url = https://github.com/boostorg/move.git
[submodule "speech2/third_party/boost/mp11"]
path = speech2/third_party/boost/mp11
url = https://github.com/boostorg/mp11.git
[submodule "speech2/third_party/boost/mpl"]
path = speech2/third_party/boost/mpl
url = https://github.com/boostorg/mpl.git
[submodule "speech2/third_party/boost/numeric_conversion"]
path = speech2/third_party/boost/numeric_conversion
url = https://github.com/boostorg/numeric_conversion.git
[submodule "speech2/third_party/boost/optional"]
path = speech2/third_party/boost/optional
url = https://github.com/boostorg/optional.git
[submodule "speech2/third_party/boost/parameter"]
path = speech2/third_party/boost/parameter
url = https://github.com/boostorg/parameter.git
[submodule "speech2/third_party/boost/pool"]
path = speech2/third_party/boost/pool
url = https://github.com/boostorg/pool.git
[submodule "speech2/third_party/boost/predef"]
path = speech2/third_party/boost/predef
url = https://github.com/boostorg/predef.git
[submodule "speech2/third_party/boost/preprocessor"]
path = speech2/third_party/boost/preprocessor
url = https://github.com/boostorg/preprocessor.git
[submodule "speech2/third_party/boost/range"]
path = speech2/third_party/boost/range
url = https://github.com/boostorg/range.git
[submodule "speech2/third_party/boost/ratio"]
path = speech2/third_party/boost/ratio
url = https://github.com/boostorg/ratio.git
[submodule "speech2/third_party/boost/rational"]
path = speech2/third_party/boost/rational
url = https://github.com/boostorg/rational.git
[submodule "speech2/third_party/boost/regex"]
path = speech2/third_party/boost/regex
url = https://github.com/boostorg/regex.git
[submodule "speech2/third_party/boost/scope"]
path = speech2/third_party/boost/scope
url = https://github.com/boostorg/scope.git
[submodule "speech2/third_party/boost/smart_ptr"]
path = speech2/third_party/boost/smart_ptr
url = https://github.com/boostorg/smart_ptr.git
[submodule "speech2/third_party/boost/static_assert"]
path = speech2/third_party/boost/static_assert
url = https://github.com/boostorg/static_assert.git
[submodule "speech2/third_party/boost/static_string"]
path = speech2/third_party/boost/static_string
url = https://github.com/boostorg/static_string.git
[submodule "speech2/third_party/boost/system"]
path = speech2/third_party/boost/system
url = https://github.com/boostorg/system.git
[submodule "speech2/third_party/boost/throw_exception"]
path = speech2/third_party/boost/throw_exception
url = https://github.com/boostorg/throw_exception.git
[submodule "speech2/third_party/boost/tokenizer"]
path = speech2/third_party/boost/tokenizer
url = https://github.com/boostorg/tokenizer.git
[submodule "speech2/third_party/boost/tuple"]
path = speech2/third_party/boost/tuple
url = https://github.com/boostorg/tuple.git
[submodule "speech2/third_party/boost/type_index"]
path = speech2/third_party/boost/type_index
url = https://github.com/boostorg/type_index.git
[submodule "speech2/third_party/boost/typeof"]
path = speech2/third_party/boost/typeof
url = https://github.com/boostorg/typeof.git
[submodule "speech2/third_party/boost/type_traits"]
path = speech2/third_party/boost/type_traits
url = https://github.com/boostorg/type_traits.git
[submodule "speech2/third_party/boost/unordered"]
path = speech2/third_party/boost/unordered
url = https://github.com/boostorg/unordered.git
[submodule "speech2/third_party/boost/url"]
path = speech2/third_party/boost/url
url = https://github.com/boostorg/url.git
[submodule "speech2/third_party/boost/utility"]
path = speech2/third_party/boost/utility
url = https://github.com/boostorg/utility.git
[submodule "speech2/third_party/boost/variant2"]
path = speech2/third_party/boost/variant2
url = https://github.com/boostorg/variant2.git
[submodule "speech2/third_party/boost/winapi"]
path = speech2/third_party/boost/winapi
url = https://github.com/boostorg/winapi.git
[submodule "speech2/third_party/boost/asio"]
path = speech2/third_party/boost/asio
url = https://github.com/boostorg/asio.git
[submodule "speech2/third_party/boost/beast"]
path = speech2/third_party/boost/beast
url = https://github.com/boostorg/beast.git

10
.vscode/settings.json vendored
View file

@ -1,10 +0,0 @@
{
"cmake.sourceDirectory": "${workspaceFolder}/speech2",
"cmake.configureArgs": [
"--toolchain cmake/clangcl-winxp.cmake"
],
"cmake.configureEnvironment": {
"VCDIR": "${env:HOME}/vs2022"
},
"cmake.ignoreCMakeListsMissing": true,
}

View file

@ -1,11 +0,0 @@
build:
cd speech2; cmake --toolchain cmake/clangcl-winxp.cmake -GNinja -Bbuild; cd ..
cd speech2; cd build; ninja; cd ..; cd ..;
clean:
rm -rf SAPIServer/bin SAPIServer/obj
make -C speech2 clean
format:
cd SAPIServer; for f in *.cs; do clang-format -i $f; done; cd ..;

View file

@ -2,17 +2,6 @@
Simple HTTP frontend API for Microsoft Speech API
## Building
Requirements
- .NET SDK
- VS2022 lib pack (TODO: link)
- LLVM toolchain
You'll also need to chattr +F (or mount the whole thing with `ciopfs` and rename the headers to lowercase, if not on ext4 or you don't want to tune2fs) the windows sdk header directories so the build works.
`just` should do the trick.
## Running
```

View file

@ -3,67 +3,72 @@ using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace SAPIServer {
namespace SAPIServer
{
/// <summary>
/// Simple routing HTTP server for .NET Framework
/// </summary>
class HTTPServer {
class HTTPServer
{
private bool listening = false;
private HttpListener listener;
private Dictionary<string, Func<HttpListenerContext, byte[]>> GETListeners;
private Dictionary<string, Func<HttpListenerContext, byte[]>> POSTListeners;
public HTTPServer() {
public HTTPServer()
{
listener = new HttpListener();
this.GETListeners = new Dictionary<string, Func<HttpListenerContext, byte[]>>();
this.POSTListeners = new Dictionary<string, Func<HttpListenerContext, byte[]>>();
}
public void Get(string uri, Func<HttpListenerContext, byte[]> handler) {
if(GETListeners.ContainsKey(uri))
throw new InvalidOperationException("A handler by that URI already exists.");
if(!uri.StartsWith("/"))
throw new ArgumentException("URIs must start with /");
public void Get(string uri, Func<HttpListenerContext, byte[]> handler)
{
if (GETListeners.ContainsKey(uri)) throw new InvalidOperationException("A handler by that URI already exists.");
if (!uri.StartsWith("/")) throw new ArgumentException("URIs must start with /");
this.GETListeners.Add(uri, handler);
}
public void Post(string uri, Func<HttpListenerContext, byte[]> handler) {
if(POSTListeners.ContainsKey(uri))
throw new InvalidOperationException("A handler by that URI already exists.");
if(!uri.StartsWith("/"))
throw new ArgumentException("URIs must start with /");
public void Post(string uri, Func<HttpListenerContext, byte[]> handler)
{
if (POSTListeners.ContainsKey(uri)) throw new InvalidOperationException("A handler by that URI already exists.");
if (!uri.StartsWith("/")) throw new ArgumentException("URIs must start with /");
this.POSTListeners.Add(uri, handler);
}
public void Listen(ushort port) {
if(listening)
throw new InvalidOperationException("This HTTPServer is already listening.");
public void Listen(ushort port)
{
if (listening) throw new InvalidOperationException("This HTTPServer is already listening.");
listening = true;
listener.Prefixes.Add(string.Format("http://*:{0}/", port));
listener.Start();
while(listener.IsListening) {
while (listener.IsListening)
{
var context = listener.GetContext();
ThreadPool.QueueUserWorkItem(
_ => {
ThreadPool.QueueUserWorkItem(_ =>
{
var uri = context.Request.RawUrl.Split('?')[0];
var ip = context.Request.RemoteEndPoint.Address;
try {
try
{
Dictionary<string, Func<HttpListenerContext, byte[]>> handlerDict;
// TODO: Make query params parsable
byte[] response;
switch(context.Request.HttpMethod) {
case "GET": {
switch (context.Request.HttpMethod)
{
case "GET":
{
handlerDict = GETListeners;
break;
}
case "POST": {
case "POST":
{
handlerDict = POSTListeners;
break;
}
default: {
default:
{
response = Encoding.UTF8.GetBytes($"Method not allowed: {context.Request.HttpMethod}");
context.Response.StatusCode = 405;
context.Response.ContentType = "text/plain";
@ -73,31 +78,35 @@ namespace SAPIServer {
return;
}
}
if(!handlerDict.TryGetValue(uri, out var handler)) {
if (!handlerDict.TryGetValue(uri, out var handler))
{
response = Encoding.UTF8.GetBytes($"No route defined for {uri}");
context.Response.StatusCode = 404;
context.Response.ContentType = "text/plain";
} else {
try {
}
else
{
try
{
response = handler(context);
} catch(Exception e) {
} catch (Exception e)
{
response = Encoding.UTF8.GetBytes("Internal Server Error");
context.Response.StatusCode = 500;
context.Response.ContentType = "text/plain";
Console.Error.WriteLine(
$"[{DateTime.Now:u}] {ip} - {context.Request.HttpMethod} {context.Request.RawUrl} - Handler Failed: {e.Message}");
Console.Error.WriteLine($"[{DateTime.Now:u}] {ip} - {context.Request.HttpMethod} {context.Request.RawUrl} - Handler Failed: {e.Message}");
}
}
context.Response.ContentLength64 = response.Length;
context.Response.OutputStream.Write(response, 0, response.Length);
context.Response.OutputStream.Close();
Console.WriteLine(
$"[{DateTime.Now:u}] {ip} - {context.Request.HttpMethod} {context.Request.RawUrl} - {context.Response.StatusCode}");
} catch(Exception e) {
Console.Error.WriteLine(
$"[{DateTime.Now:u}] {ip} - {context.Request.HttpMethod} {context.Request.RawUrl} - Exception: {e.Message}");
Console.WriteLine($"[{DateTime.Now:u}] {ip} - {context.Request.HttpMethod} {context.Request.RawUrl} - {context.Response.StatusCode}");
} catch (Exception e)
{
Console.Error.WriteLine($"[{DateTime.Now:u}] {ip} - {context.Request.HttpMethod} {context.Request.RawUrl} - Exception: {e.Message}");
}
});
}
}

View file

@ -6,51 +6,57 @@ using System.Speech.Synthesis;
using System.Text;
using Newtonsoft.Json;
namespace SAPIServer {
class SpeechServer {
private Dictionary<string, SpeechAPI> apis = new();
private HTTPServer httpServer = new();
public SpeechServer() {
// Test out creating a speech2 api object
apis["sapi4"] = new SpeechAPI(EngineType.ET_SAPI4);
#if true
foreach(var voice in apis["sapi4"].GetVoices()) {
Console.WriteLine($"ggg {voice.name}");
namespace SAPIServer
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1 || !ushort.TryParse(args[0], out var port))
{
Console.Error.WriteLine("Usage: SAPIServer.exe <port>");
Environment.Exit(1);
return;
}
#endif
httpServer.Get("/api/voices", ctx => {
/*
using(var synth = new SpeechSynthesizer()) {
var http = new HTTPServer();
http.Get("/api/voices", ctx =>
{
using (var synth = new SpeechSynthesizer())
{
ctx.Response.ContentType = "application/json";
return Encoding.UTF8.GetBytes(
JsonConvert.SerializeObject(new VoicesResponse { voices = synth.GetInstalledVoices().Select(v => v.VoiceInfo.Name).ToArray() }));
}*/
return new byte[]{0};
return Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new VoicesResponse
{
voices = synth.GetInstalledVoices().Select(v => v.VoiceInfo.Name).ToArray()
}));
}
});
httpServer.Post("/api/synthesize", ctx => {
http.Post("/api/synthesize", ctx =>
{
SynthesizePayload body;
try {
try
{
string bodyraw;
using(var reader = new StreamReader(ctx.Request.InputStream)) {
using (var reader = new StreamReader(ctx.Request.InputStream))
{
bodyraw = reader.ReadToEnd();
}
body = JsonConvert.DeserializeObject<SynthesizePayload>(bodyraw);
} catch(Exception) {
}
catch (Exception e)
{
ctx.Response.StatusCode = 400;
return Encoding.UTF8.GetBytes("Bad payload");
}
if(body == null || body.text == null || body.voice == null) {
if (body == null || body.text == null || body.voice == null)
{
ctx.Response.StatusCode = 400;
return Encoding.UTF8.GetBytes("Bad payload");
}
using(var ms = new MemoryStream()) {
using(var synth = new SpeechSynthesizer()) {
if(!synth.GetInstalledVoices().Any(v => v.VoiceInfo.Name == body.voice)) {
using (var ms = new MemoryStream())
using (var synth = new SpeechSynthesizer())
{
if (!synth.GetInstalledVoices().Any(v => v.VoiceInfo.Name == body.voice))
{
ctx.Response.StatusCode = 400;
return Encoding.UTF8.GetBytes("Voice not found");
}
@ -60,26 +66,9 @@ namespace SAPIServer {
ctx.Response.ContentType = "audio/wav";
return ms.ToArray();
}
}
});
}
public void Start(ushort port) {
Console.WriteLine($"[{ DateTime.Now:u}] Starting HTTP server on port {port}");
httpServer.Listen(port);
}
}
class Program {
static void Main(string[] args) {
if(args.Length < 1 || !ushort.TryParse(args[0], out var port)) {
Console.Error.WriteLine("Usage: SAPIServer.exe <port>");
Environment.Exit(1);
return;
}
var server = new SpeechServer();
server.Start(port);
http.Listen(port);
}
}
}

View file

@ -1,23 +1,58 @@
<Project Sdk="Microsoft.NET.Sdk">
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<!-- Older VS generated one for us; we're just using that one.
Unless we wanna use the generated one?? -->
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{BF824074-4C4E-4DE1-8DCA-F022682B00E1}</ProjectGuid>
<OutputType>Exe</OutputType>
<TargetFrameworks>net40</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<RootNamespace>SAPIServer</RootNamespace>
<AssemblyName>SAPIServer</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup>
<RuntimeIdentifier>windows-x86</RuntimeIdentifier>
<!-- N.B: This is only to gain support for some compiler-supported nicities, like new(). -->
<LangVersion>9.0</LangVersion>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<!--<PackageReference Include="Microsoft.Bcl.Async" Version="1.0.168" />-->
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net40\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Speech" />
@ -27,5 +62,23 @@
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="HTTPServer.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SynthesizePayload.cs" />
<Compile Include="VoicesResponse.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.manifest" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -1,85 +0,0 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Security;
namespace SAPIServer {
// Sync with C++ code.
public enum EngineType : int { ET_SAPI4, ET_SAPI5, ET_DECTALK }
public class VoiceDef {
public Guid guid;
public string name;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal struct VoiceDefInternal {
Guid guid;
[MarshalAs(UnmanagedType.LPStr)]
string name;
public VoiceDef Voicify() {
Console.WriteLine($"FUCK ${name}");
//var str = Marshal.PtrToStringAnsi(name);
VoiceDef def = new();
def.guid = guid;
def.name = name;
return def;
}
}
// Speech2 DLL API. Sync with c++ code.
internal class SpeechDLL {
private const string Speech2DLL = "speech2.dll";
[DllImport(Speech2DLL, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr speech2_create_api(EngineType type);
[DllImport(Speech2DLL, CallingConvention = CallingConvention.StdCall)]
public static extern void speech2_destroy_api(IntPtr pAPI);
[DllImport(Speech2DLL, CallingConvention = CallingConvention.StdCall)]
public static extern int speech2_api_get_voiceinfo_count(IntPtr pAPI);
[DllImport(Speech2DLL, CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr speech2_api_get_voiceinfo_index(IntPtr pAPI, int index);
}
// A speech API. This is generic for all speech2 supported speech APIs, so
// we can use the same code for everything. Cool, huh?
public class SpeechAPI : IDisposable {
private IntPtr handle = IntPtr.Zero;
public SpeechAPI(EngineType type) {
handle = SpeechDLL.speech2_create_api(type);
if(handle == IntPtr.Zero)
throw new InvalidOperationException("Failed to create speech API");
}
public List<VoiceDef> GetVoices() {
Console.WriteLine("SpeechAPI.GetVoices()");
var count = SpeechDLL.speech2_api_get_voiceinfo_count(handle);
Console.WriteLine($"count {count}");
var list = new List<VoiceDef>();
for(var i = 0; i < count; ++i) {
var ptr = SpeechDLL.speech2_api_get_voiceinfo_index(handle, i);
var obj = (VoiceDefInternal)Marshal.PtrToStructure(ptr, typeof(VoiceDefInternal));
list.Add(obj.Voicify());
}
return list;
}
void IDisposable.Dispose() {
if(handle != IntPtr.Zero)
SpeechDLL.speech2_destroy_api(handle);
}
}
}

View file

@ -1,5 +1,7 @@
namespace SAPIServer {
class SynthesizePayload {
namespace SAPIServer
{
class SynthesizePayload
{
public string voice { get; set; }
public string text { get; set; }
}

View file

@ -1,5 +1,7 @@
namespace SAPIServer {
class VoicesResponse {
namespace SAPIServer
{
class VoicesResponse
{
public string[] voices { get; set; }
}
}

View file

@ -1,20 +0,0 @@
cmake_minimum_required(VERSION 3.15)
project(speech2)
xp_init()
enable_language(ASM)
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(Policies)
include(ProjectFuncs)
add_subdirectory(third_party/boost)
add_subdirectory(src)

View file

@ -1,3 +0,0 @@
# speech2
speech dll thing

View file

@ -1,22 +0,0 @@
# CMake policy configuration
# Macro to enable new CMake policy.
# Makes this file a *LOT* shorter.
macro (_new_cmake_policy policy)
if(POLICY ${policy})
#message(STATUS "Enabling new policy ${policy}")
cmake_policy(SET ${policy} NEW)
endif()
endmacro()
_new_cmake_policy(CMP0026) # CMake 3.0: Disallow use of the LOCATION property for build targets.
_new_cmake_policy(CMP0042) # CMake 3.0+ (2.8.12): MacOS "@rpath" in target's install name
_new_cmake_policy(CMP0046) # warn about non-existent dependencies
_new_cmake_policy(CMP0048) # CMake 3.0+: project() command now maintains VERSION
_new_cmake_policy(CMP0054) # CMake 3.1: Only interpret if() arguments as variables or keywords when unquoted.
_new_cmake_policy(CMP0056) # try_compile() linker flags
_new_cmake_policy(CMP0066) # CMake 3.7: try_compile(): use per-config flags, like CMAKE_CXX_FLAGS_RELEASE
_new_cmake_policy(CMP0067) # CMake 3.8: try_compile(): honor language standard variables (like C++11)
_new_cmake_policy(CMP0068) # CMake 3.9+: `RPATH` settings on macOS do not affect `install_name`.
_new_cmake_policy(CMP0075) # CMake 3.12+: Include file check macros honor `CMAKE_REQUIRED_LIBRARIES`
_new_cmake_policy(CMP0077) # CMake 3.13+: option() honors normal variables.

View file

@ -1,14 +0,0 @@
function(speech2_target target)
target_compile_definitions(${target} PRIVATE "$<$<CONFIG:DEBUG>:SPEECH2_DEBUG>")
target_compile_features(${target} PRIVATE cxx_std_20)
target_include_directories(${target} PRIVATE ${PROJECT_SOURCE_DIR}/src ${PROJECT_SOURCE_DIR}/third_party ${CMAKE_CURRENT_BINARY_DIR})
# use the static multithreaded C library
set_property(TARGET ${target} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# TODO option for this.
target_link_options(${target} PRIVATE
-Wl,/safeseh:no
-Xlinker /subsystem:console,${CMAKE_SYSTEM_VERSION}
)
endfunction()

View file

@ -1,78 +0,0 @@
# Windows XP
set(CMAKE_SYSTEM_NAME Windows)
set(CMAKE_SYSTEM_VERSION 5.1)
set(CMAKE_SYSTEM_PROCESSOR x86)
set(TARGET_VERSION_MAJOR 5)
set(TARGET_VERSION_MINOR)
set(_MSVC_TRIPLET "i686-pc-windows-msvc")
# CXXRTDIR = $(VCDIR)/crt
# UCRTDIR = $(VCDIR)/ucrt
# PSDKDIR = $(VCDIR)/winsdk
if("$ENV{VCDIR}" STREQUAL "")
message(FATAL_ERROR "Please set VCDIR in your environment to an appropiate path.")
endif()
set(_CRTDIR "$ENV{VCDIR}/crt")
#-isystem ${_CRTDIR}/include
set(_UCRTDIR "$ENV{VCDIR}/ucrt")
set(_PSDKDIR "$ENV{VCDIR}/winsdk")
set(_CLANG_BASEFLAGS "-target ${_MSVC_TRIPLET} -fms-extensions -fms-compatibility -fms-compatibility-version=19 -isystem $ENV{VCDIR}/libcxx/include/c++ -isystem ${_UCRTDIR}/include -isystem $ENV{VCDIR}/libcxx/include -isystem ${_PSDKDIR}/include/shared -isystem ${_PSDKDIR}/include/um ")
set(_CLANG_ARCHFLAGS "-march=pentium4 -D_WIN32_WINNT=0x0501")
set(CMAKE_C_COMPILER "clang" CACHE FILEPATH "")
set(CMAKE_CXX_COMPILER "clang++" CACHE FILEPATH "")
set(CMAKE_LINKER "lld-link" CACHE FILEPATH "")
set(CMAKE_ASM_FLAGS_INIT "${_CLANG_BASEFLAGS} ${_CLANG_ARCHFLAGS}")
set(CMAKE_C_FLAGS_INIT "${_CLANG_BASEFLAGS} ${_CLANG_ARCHFLAGS}")
set(CMAKE_C_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_INIT} -fomit-frame-pointer")
set(CMAKE_CXX_FLAGS_INIT "${CMAKE_C_FLAGS_INIT}")
set(CMAKE_CXX_FLAGS_RELEASE_INIT "${CMAKE_C_FLAGS_RELEASE_INIT} ${CMAKE_CXX_FLAGS_INIT}")
# Set base linker library paths
foreach(type EXE MODULE SHARED)
# -Xlinker /nodefaultlib:libcpmt -Xlinker /nodefaultlib:libcpmtd
set(CMAKE_${type}_LINKER_FLAGS "-Xlinker /libpath:${_CRTDIR}/lib/x86 -Xlinker /libpath:${_UCRTDIR}/lib -Xlinker /libpath:${_PSDKDIR}/lib")
endforeach()
# Remove fluff libraries; projects should specify them
set(CMAKE_C_STANDARD_LIBRARIES "" CACHE STRING "" FORCE)
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
set(CMAKE_CXX_STANDARD_LIBRARIES "-Xlinker /libpath:$ENV{VCDIR}/libcxx/lib -Xlinker libcpmtd.lib -Xlinker libc++d.lib -Xlinker winpthreadsd.lib" CACHE STRING "" FORCE)
else()
set(CMAKE_CXX_STANDARD_LIBRARIES "-Xlinker /libpath:$ENV{VCDIR}/libcxx/lib -Xlinker libcpmt.lib -Xlinker libc++.lib -Xlinker winpthreads.lib" CACHE STRING "" FORCE)
endif()
# Run this once after you call project() to replace broken
macro(xp_init)
foreach(lang C CXX)
# have to patch the link commands to replace /MANIFEST:EMBED with /MANIFEST:NO
# because ....
set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
"<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> -o <TARGET> ${CMAKE_GNULD_IMAGE_VERSION} -Xlinker /MANIFEST:EMBED -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:${CMAKE_SYSTEM_VERSION} <OBJECTS> <LINK_LIBRARIES> <MANIFESTS>" CACHE STRING "" FORCE)
set(CMAKE_${lang}_CREATE_SHARED_MODULE ${CMAKE_${lang}_CREATE_SHARED_LIBRARY} CACHE STRING "" FORCE)
set(CMAKE_${lang}_LINK_EXECUTABLE
"<CMAKE_${lang}_COMPILER> -nostartfiles -nostdlib <FLAGS> <CMAKE_${lang}_LINK_FLAGS> <LINK_FLAGS> <OBJECTS> -o <TARGET> -Xlinker /MANIFEST:NO -Xlinker /implib:<TARGET_IMPLIB> -Xlinker /pdb:<TARGET_PDB> -Xlinker /version:${CMAKE_SYSTEM_VERSION} ${CMAKE_GNULD_IMAGE_VERSION} <LINK_LIBRARIES> <MANIFESTS>" CACHE STRING "" FORCE)
# also patch these so we can set the subsytem version
set(CMAKE_${lang}_CREATE_WIN32_EXE "-Xlinker /subsystem:windows,${CMAKE_SYSTEM_VERSION}" CACHE STRING "" FORCE)
set(CMAKE_${lang}_CREATE_CONSOLE_EXE "-Xlinker /subsystem:console,${CMAKE_SYSTEM_VERSION}" CACHE STRING "" FORCE)
endforeach()
endmacro()
# Use the static multithreaded C library
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
# dummy
set(CMAKE_ASM_MASM_COMPILE_OBJECT "${CMAKE_ASM_COMPILE_OBJECT}")
xp_init()

View file

@ -1,39 +0,0 @@
add_subdirectory(base)
add_subdirectory(impl)
add_subdirectory(sapi4)
add_executable(sapiserver
main.cpp
winxp_compat_fwd.S
winxp_compat.cpp
winxp_compat_threadsafe_static.c
)
speech2_target(sapiserver)
target_compile_definitions(sapiserver PRIVATE
# Need to force this on, since I think clang's msvc compatibility
# is deciding to set a wrong __cplusplus (like MSVC, so it's not *exactly* clang's fault).
# The best way to fix it would probably involve using clang-cl frontend and passing the option (I think.)
# (nevermind, it's just broken.)
-DBOOST_ASIO_HAS_STD_INVOKE_RESULT=1
# Disable the "helpful" auto-link Boost.Config tries to do. CMake already has a functional
# dependency graph based on our input, so we don't need it.
-DBOOST_ALL_NO_LIB=1
)
target_link_libraries(sapiserver PRIVATE
#libc++
# subprojects
speech2::base
speech2::impl
speech2::api_sapi4
# SDK libraries
uuid.lib
ole32.lib
)

View file

@ -1,14 +0,0 @@
add_library(speech2_base STATIC
Thread.cpp
# Logging system
Logger.cpp
Logger_priv.cpp
StdoutSink.cpp
)
speech2_target(speech2_base)
add_library(speech2::base ALIAS speech2_base)

View file

@ -1,41 +0,0 @@
#include "Logger.hpp"
#include <base/Logger_priv.hpp>
#include <base/Thread.hpp>
namespace base {
inline auto& GlobalState() {
return logger_impl::LoggerGlobalState::The();
}
void LoggerAttachSink(LoggerSink& sink) {
GlobalState().AttachSink(sink);
}
MessageSeverity GetLogLevel() {
return GlobalState().GetLogLevel();
}
void SetLogLevel(MessageSeverity newLevel) {
GlobalState().SetLogLevel(newLevel);
}
Logger& Logger::Get(std::string_view key) {
return logger_impl::GetOrRegister(key);
}
Logger::Logger(ChannelId id)
: channelId(id) {
}
void Logger::VOut(MessageSeverity severity, std::string_view format, std::format_args args) {
logger_impl::MessageData data {
.time = std::chrono::system_clock::now(), .severity = severity, .channelId = channelId, .message = std::vformat(format, args)
};
// Push data into logger thread.
logger_impl::PushMessage(std::move(data));
}
} // namespace common

View file

@ -1,75 +0,0 @@
#pragma once
#include <base/Types.hpp>
#include <format>
#include <string_view>
namespace base {
/// A logger sink. Outputs messages to some device (a TTY), a file,
/// anything. A interface for the logger to spit stuff out.
///
/// # Notes
/// Sinks *do not* run on the main application thread. Instead, they run on a
/// single internal thread shared with the logging system.
/// Sinks should *not* block for large periods of time.
struct LoggerSink {
virtual void OutputMessage(std::string_view message) = 0;
};
enum class MessageSeverity { Debug, Info, Warning, Error, Fatal };
/// A channel ID. `enum class`es are used to avoid confusion with a normal u32,
/// and to also add addional interface type safety.
/// These are opaque, and only exposed here because it would be annoying to move elsewhere.
enum class ChannelId : u32 {};
/// Attach a sink to all Support loggers; allowing it to output logger messages.
void LoggerAttachSink(LoggerSink& sink);
MessageSeverity GetLogLevel();
void SetLogLevel(MessageSeverity newLevel);
/// An (asynchronous) logger.
struct Logger {
/// Gets or creates a logger for the specified channel.
static Logger& Get(std::string_view channel);
explicit Logger(ChannelId channel);
Logger(const Logger&) = delete;
Logger(Logger&&) = delete;
template <class... Args>
inline void Debug(std::string_view fmt, Args&&... args) {
VOut(MessageSeverity::Debug, fmt, std::make_format_args(args...));
}
template <class... Args>
inline void Info(std::string_view fmt, Args&&... args) {
VOut(MessageSeverity::Info, fmt, std::make_format_args(args...));
}
template <class... Args>
inline void Warning(std::string_view fmt, Args&&... args) {
VOut(MessageSeverity::Warning, fmt, std::make_format_args(args...));
}
template <class... Args>
inline void Error(std::string_view fmt, Args&&... args) {
VOut(MessageSeverity::Error, fmt, std::make_format_args(args...));
}
template <class... Args>
inline void Fatal(std::string_view fmt, Args&&... args) {
VOut(MessageSeverity::Fatal, fmt, std::make_format_args(args...));
}
private:
void VOut(MessageSeverity severity, std::string_view format, std::format_args args);
ChannelId channelId;
};
} // namespace collabvm

View file

@ -1,170 +0,0 @@
#include <base/Logger_priv.hpp>
#include <base/Thread.hpp>
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <deque>
#include <mutex>
#include <queue>
#include <string_view>
#include <thread>
namespace base::logger_impl {
static constexpr std::string_view SeverityToString(MessageSeverity sev) {
// This must match order of MessageSeverity.
const char* MessageSeverityStringTable[] = { "Debug", "Info", "Warn", "Error", "Fatal" };
return MessageSeverityStringTable[static_cast<std::size_t>(sev)];
}
/// Hash algorithm for channel IDs. In this case it's PJW-ELF.
/// I might switch to murmur or something if collisions are a problem,
/// but I don't think it's a problem.
ChannelId ChannelIDHash(const char* in) {
u32 hash = 0;
u32 high = 0;
while(*in) {
hash = (hash << 4) + *in++;
if((high = hash & 0xf0000000))
hash ^= high >> 23;
hash &= ~high;
}
return static_cast<ChannelId>(hash);
}
std::string_view ChannelToString(ChannelId id) {
auto& gs = LoggerGlobalState::The();
std::unique_lock lk(gs.loggerMapLock);
return gs.loggerMap[id].channelName;
}
/// comparator for [std::priority_queue]
struct LogMessageComparator {
constexpr bool operator()(const MessageData& mdLeft, const MessageData& mdRight) { return mdLeft.time > mdRight.time; }
};
struct LoggerThreadData {
// Logger thread stuff
std::thread loggerThread;
std::mutex logQueueMutex;
std::condition_variable logQueueCv;
std::priority_queue<MessageData, std::deque<MessageData>, LogMessageComparator> logQueue;
// could be an atomic_bool
bool logThreadShutdown = false;
bool ShouldUnblock() {
// N.B: ALL calls of this hold the lock.
// Always unblock if the logger thread needs to be shut down.
if(logThreadShutdown)
return true;
return !logQueue.empty();
}
void PushMessage(MessageData&& md) {
{
std::unique_lock lk(logQueueMutex);
logQueue.push(std::move(md));
}
logQueueCv.notify_one();
}
/// This thread drives the logging system.
static void LoggerThread(LoggerThreadData* self) {
// Fancy thread names.
SetThreadName("AsyncLogger");
auto& state = LoggerGlobalState::The();
while(true) {
// Shutdown if requested.
if(self->logThreadShutdown)
break;
{
std::unique_lock lk(self->logQueueMutex);
if(self->logQueue.empty()) {
// Await for messages.
self->logQueueCv.wait(lk, [self]() { return self->ShouldUnblock(); });
}
}
{
std::unique_lock lk(self->logQueueMutex);
// Flush the logger queue until there are no more messages.
while(!self->logQueue.empty()) {
const auto& msg = self->logQueue.top();
state.OutputMessage(msg);
self->logQueue.pop();
}
}
}
}
};
Unique<LoggerThreadData> threadData;
LoggerGlobalState& LoggerGlobalState::The() {
static LoggerGlobalState storage;
return storage;
}
LoggerGlobalState::LoggerGlobalState() {
// Spawn the logger thread
threadData = std::make_unique<LoggerThreadData>();
threadData->loggerThread = std::thread(&LoggerThreadData::LoggerThread, threadData.get());
}
LoggerGlobalState::~LoggerGlobalState() {
// Shut down the logger thread
threadData->logThreadShutdown = true;
threadData->logQueueCv.notify_all();
threadData->loggerThread.join();
}
void LoggerGlobalState::AttachSink(LoggerSink& sink) {
sinks.push_back(&sink);
}
void LoggerGlobalState::OutputMessage(const MessageData& data) {
// give up early if no sinks are attached
if(sinks.empty())
return;
if(data.severity < logLevel)
return;
auto formattedLoggerMessage = std::format("[{:%F %H:%M:%S}|{}|{}] {}", std::chrono::floor<std::chrono::milliseconds>(data.time),
SeverityToString(data.severity), ChannelToString(data.channelId), data.message);
for(auto sink : sinks)
sink->OutputMessage(formattedLoggerMessage);
}
Logger& GetOrRegister(std::string_view component) {
auto& gs = LoggerGlobalState::The();
std::unique_lock lk(gs.loggerMapLock);
auto hash = ChannelIDHash(component.data());
if(!gs.loggerMap.contains(hash)) {
// Insert a new entry into the logger map.
gs.loggerMap.insert_or_assign(hash, BoltedLoggerData { component, std::make_unique<Logger>(hash) });
}
return *gs.loggerMap[hash].logger.get();
}
void PushMessage(MessageData&& md) {
if(threadData)
threadData->PushMessage(std::move(md));
}
} // namespace base::logger_impl

View file

@ -1,59 +0,0 @@
#pragma once
#include <base/Logger.hpp>
#include <chrono>
#include <mutex>
#include <unordered_map>
#include <vector>
namespace base::logger_impl {
/// Message data. This is only used by logger sinks.
struct MessageData {
std::chrono::system_clock::time_point time;
MessageSeverity severity;
ChannelId channelId; // the channel ID.
std::string message; // DO NOT SET THIS, IT WILL BE OVERWRITTEN AND I WILL BE VERY SAD -lily
};
struct BoltedLoggerData {
std::string_view channelName;
Unique<Logger> logger;
};
/// Shared global state all loggers use.
struct LoggerGlobalState {
static LoggerGlobalState& The();
void AttachSink(LoggerSink& sink);
void OutputMessage(const MessageData& data);
/// Get the current log level.
MessageSeverity GetLogLevel() const { return logLevel; }
/// Set the current log level.
void SetLogLevel(MessageSeverity newLogLevel) { logLevel = newLogLevel; }
private:
LoggerGlobalState();
~LoggerGlobalState();
public:
std::vector<LoggerSink*> sinks;
MessageSeverity logLevel { MessageSeverity::Info };
std::unordered_map<ChannelId, BoltedLoggerData> loggerMap;
std::mutex loggerMapLock;
};
/// Gets or registers a new logger. This routine is threadsafe, and can be called
/// on any thread, like (most) parts of the logging system.
Logger& GetOrRegister(std::string_view component);
/// Push a logger message into the queue.
void PushMessage(MessageData&& md);
} // namespace base::logger_impl

View file

@ -1,6 +0,0 @@
// Sane windows.h
#pragma once
#define _WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <commctrl.h>
#undef _WIN32_LEAN_AND_MEAN

View file

@ -1,20 +0,0 @@
#include <base/StdoutSink.hpp>
#include "Logger.hpp"
namespace base {
StdoutLoggerSink& StdoutLoggerSink::The() {
static StdoutLoggerSink sink;
return sink;
}
void StdoutLoggerSink::OutputMessage(std::string_view message) {
fputs(message.data(), stdout);
fputc('\n', stdout);
fflush(stdout);
}
void LoggerAttachStdout() {
LoggerAttachSink(StdoutLoggerSink::The());
}
} // namespace base

View file

@ -1,17 +0,0 @@
#pragma once
#include <base/Logger.hpp>
namespace base {
/// A logger sink implementation that prints to standard output.
struct StdoutLoggerSink : public LoggerSink {
static StdoutLoggerSink& The();
void OutputMessage(std::string_view message) override;
};
/// Attach the stdout logger sink to the global Lucore logger.
void LoggerAttachStdout();
} // namespace base

View file

@ -1,11 +0,0 @@
#include <pthread.h>
#include <base/Thread.hpp>
namespace base {
void SetThreadNameImpl(const char* name, usize len) {
//COMMON_ASSERT(len <= 15, "name will overflow pthread_setname_np() buffer");
pthread_setname_np(pthread_self(), name);
}
}

View file

@ -1,15 +0,0 @@
#pragma once
#include <base/Types.hpp>
#include <string>
namespace base {
void SetThreadNameImpl(const char* name, usize len);
/// Sets the name of the current thread. Mainly for portability.
inline void SetThreadName(const std::string& name) {
SetThreadNameImpl(name.data(), name.length());
}
} // namespace collabvm

View file

@ -1,52 +0,0 @@
#pragma once
#include <cstdint>
#include <memory>
using u8 = std::uint8_t;
using i8 = std::int8_t;
using u16 = std::uint16_t;
using i16 = std::int16_t;
using u32 = std::uint32_t;
using i32 = std::int32_t;
using u64 = std::uint64_t;
using i64 = std::int64_t;
using usize = std::size_t;
using isize = std::intptr_t;
namespace base {
/// A little ergonomic wrapper over
/// std::unique_ptr<T[]>, for a "kinda-vector"
/// that lives on the heap and is statically sized
template <class T>
struct UniqueArray final {
explicit UniqueArray(usize size)
: array(std::make_unique<T[]>(size)),
size(size) {
}
UniqueArray(UniqueArray&& move) {
array = std::move(move.array);
size = move.size;
// invalidate
move.array = nullptr;
move.size = 0;
}
T& operator[](usize index) { return Get()[index]; }
const T& operator[](usize index) const { return Get()[index]; }
T* Get() { return array.get(); }
const T* Get() const { return array.get(); }
usize Size() const { return size; }
private:
std::unique_ptr<T[]> array {};
usize size {};
};
template<class T>
using Unique = std::unique_ptr<T>;
} // namespace common

View file

@ -1,71 +0,0 @@
#pragma once
#include <base/SaneWin.hpp>
#include <stdio.h>
namespace base {
// A relatively sane (non-intrinsic) COM smart pointer
// TODO: Allow downcasting to ComPtr<IUnknown, IID_IUnknown> (all COM objects implement this anyways)
//
template <class T = IUnknown, const IID* iid = &IID_IUnknown>
struct ComPtr {
//constexpr ComPtr() = default;
//explicit ComPtr(T* t) : interface_ptr(t) {}
constexpr ComPtr() : interface_ptr(nullptr) {}
// Assignment won't require AddRef() because most COM interfaces
// will automatically AddRef() upon querying them.
ComPtr& operator=(T* interface_) {
// Release an existing interface.
if(interface_ptr) {
printf("ComPtr<T>::operator= releasing %p (guid %08x)\n", interface_ptr, *iid);
interface_ptr->Release();
}
interface_ptr = interface_;
return *this;
}
ComPtr(const ComPtr& copy) {
if(interface_ptr) {
interface_ptr->AddRef();
interface_ptr = copy.interface_ptr;
}
}
~ComPtr() {
if(interface_ptr) {
printf("ComPtr<T>::~ComPtr releasing %p (guid %08x)\n", interface_ptr, *iid);
interface_ptr->Release();
}
}
// Helper to CoCreateInstance() on this pointer
HRESULT CreateInstance(REFCLSID clsid, DWORD ctx) {
return CoCreateInstance(clsid, nullptr, CLSCTX_ALL, *iid, reinterpret_cast<void**>(&interface_ptr));
}
// smart pointer overrides
constexpr T** operator&() { return &interface_ptr; }
constexpr T* operator->() { return interface_ptr; }
constexpr operator T*() const { return interface_ptr; }
T* Get() { return interface_ptr; }
const T* Get() const { return interface_ptr; }
// release pointer - you need to manage it yourself or put it
// into another ComSmartPtr then
T* ReleasePtr() {
auto old = interface_ptr;
interface_ptr = nullptr;
return old;
}
private:
T* interface_ptr { nullptr };
};
} // namespace base

View file

@ -1,35 +0,0 @@
add_library(speech2_impl
asio_src.cpp
beast_src.cpp
)
speech2_target(speech2_impl)
target_compile_definitions(speech2_impl PUBLIC
# Need to force this on, since I think clang's msvc compatibility
# is deciding to set a wrong __cplusplus (like MSVC, so it's not *exactly* clang's fault).
# The best way to fix it would probably involve using clang-cl frontend and passing the option (I think.)
# (nevermind, it's just broken.)
-DBOOST_ASIO_HAS_STD_INVOKE_RESULT=1
# We compile all of these header-only libraries in separate .cpp source files
# to decrease build churn
-DBOOST_ASIO_SEPARATE_COMPILATION=1
-DBOOST_BEAST_SEPARATE_COMPILATION=1
# Disable deprecated functionality and some things which add additional dependencies or are
# simply baggage we aren't ever going to use
-DBOOST_ASIO_NO_DEPRECATED=1
-DBOOST_ASIO_DISABLE_BOOST_ARRAY=1
-DBOOST_ASIO_DISABLE_BOOST_BIND=1
)
target_link_libraries(speech2_impl PUBLIC
Boost::asio
Boost::beast
Boost::coroutine
Boost::context
)
add_library(speech2::impl ALIAS speech2_impl)

View file

@ -1,5 +0,0 @@
// Since we're using (BOOST_)ASIO_SEPARATE_COMPILATION, we need
// to include the <(boost/)asio/impl/src.hpp> header in some TU.
// We use this one to explicitly do so.
#include <boost/asio/impl/src.hpp>

View file

@ -1 +0,0 @@
#include <boost/beast/src.hpp>

View file

@ -1,163 +0,0 @@
#include <stdio.h>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/write.hpp>
#include <boost/beast/core/basic_stream.hpp>
#include <boost/beast/core/flat_buffer.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/http/message_generator.hpp>
#include <boost/beast/http/read.hpp>
#include <boost/beast/http/string_body.hpp>
#include <boost/beast/http/write.hpp>
#include <format>
#include <iostream>
#include <memory>
#include "base/Logger.hpp"
#include "base/StdoutSink.hpp"
#include "speechapi.hpp"
namespace net = boost::asio;
namespace beast = boost::beast;
namespace bhttp = beast::http;
using net::ip::tcp;
auto coresv_to_cxx(boost::core::string_view sv) {
return std::string_view { sv.data(), sv.length() };
}
// A test coro
void test_out_of_line_coro(net::any_io_executor ioc,net::yield_context yc) {
net::steady_timer t{ioc};
t.expires_after(std::chrono::seconds(5));
t.async_wait(yc);
}
class session : public std::enable_shared_from_this<session> {
public:
explicit session(net::io_context& io_context, beast::basic_stream<net::ip::tcp> socket)
: socket_(std::move(socket)), strand_(io_context.get_executor()) {}
void go() {
auto self(shared_from_this());
net::spawn(
strand_,
[this, self](net::yield_context yield) {
try {
auto& logger = base::Logger::Get("HTTPSession");
bhttp::request<bhttp::string_body> req {};
beast::flat_buffer buffer {};
logger.Info("Wait test");
// mostly just to test if I can yield stuff from another member function.
// This seems to work, so /shrug
test_out_of_line_coro(socket_.get_executor(), yield);
logger.Info("Wait completed");
for(;;) {
socket_.expires_after(std::chrono::seconds(10));
bhttp::async_read(socket_, buffer, req, yield);
auto const routeTest = [&]() -> bhttp::message_generator {
bhttp::response<bhttp::string_body> resp { bhttp::status::bad_request, req.version() };
resp.set(bhttp::field::server, "Fucker Google/1.0");
resp.set(bhttp::field::content_type, "text/plain");
resp.keep_alive(false);
resp.body() = std::format("You requested \"{} {}\"", coresv_to_cxx(req.method_string()), coresv_to_cxx(req.target()));
resp.prepare_payload();
return resp;
};
logger.Info("HTTP request to {}", coresv_to_cxx(req.target()));
auto res = routeTest();
socket_.expires_after(std::chrono::seconds(10));
beast::async_write(socket_, std::move(res), yield);
if(!req.keep_alive()) {
socket_.close();
return;
}
}
} catch(std::exception& e) {
socket_.close();
}
},
net::detached);
}
private:
beast::basic_stream<net::ip::tcp> socket_;
net::strand<net::io_context::executor_type> strand_;
};
int main(int argc, char** argv) {
// CoInitialize(nullptr);
base::LoggerAttachStdout();
try {
if(argc != 2) {
std::cerr << "Usage: echo_server <port>\n";
return 1;
}
net::io_context io_context;
net::spawn(
io_context,
[&](net::yield_context yield) {
tcp::acceptor acceptor(io_context, tcp::endpoint(tcp::v4(), std::atoi(argv[1])));
for(;;) {
boost::system::error_code ec;
tcp::socket socket(io_context);
acceptor.async_accept(socket, yield[ec]);
if(!ec) {
auto stream = beast::basic_stream<net::ip::tcp>(std::move(socket));
std::make_shared<session>(io_context, std::move(stream))->go();
}
}
},
[](std::exception_ptr e) {
if(e)
std::rethrow_exception(e);
});
io_context.run();
} catch(std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
#if 0 // test spech shit
auto api = std::unique_ptr<ISpeechAPI>(ISpeechAPI::CreateSapi4());
if(auto hr = api->Initialize(); FAILED(hr)) {
printf("Failed to initialize speech API\n");
return 1;
}
if(auto hr = api->SelectVoice("Sam"); FAILED(hr)) {
printf("Failed to select voice\n");
return 1;
}
printf("Selected voice\n");
#endif
// CoUninitialize();
return 0;
}

View file

@ -1,11 +0,0 @@
# SAPI4 layer for speech2
add_library(speech2_sapi4
api_sapi4.cpp
guid_sapi4.cpp
audio_buffer.cpp
)
speech2_target(speech2_sapi4)
add_library(speech2::api_sapi4 ALIAS speech2_sapi4)

View file

@ -1,97 +0,0 @@
#include <stdio.h>
#include <base/comptr.hpp>
#include "sapi4/audio_buffer.hpp"
#include "sapi4/include/speech.h"
#include "speechapi.hpp"
struct SpeechAPI_SAPI4 : public ISpeechAPI {
virtual ~SpeechAPI_SAPI4() {
printf("~SpeechAPI_SAPI4\n");
// if(pAudioOut)
// pAudioOut->AddRef();
if(pCentral)
pCentral->Release();
};
HRESULT Initialize() override {
HRESULT hRes{};
printf("speech2: SpeechAPI_Sapi4::Initalize() begin: %p\n", this);
hRes = pEnum.CreateInstance(CLSID_TTSEnumerator, CLSCTX_INPROC);
if(FAILED(hRes))
return hRes;
pEnum->Reset();
// Fill out voices
EnumVoices();
return S_OK;
}
void EnumVoices() {
TTSMODEINFO found{};
while(!pEnum->Next(1, &found, nullptr)) {
voices.push_back({found.gModeID, found.szModeName});
}
pEnum->Reset();
}
const std::vector<VoiceInfo>& GetVoices() override {
return voices;
}
HRESULT SelectVoiceImpl(const GUID& guid) {
pAudioOut = new AudioOutBuffer();
pAudioOut->AddRef();
if(pCentral)
pCentral->Release();
ITTSCentral* central;
if(auto hr = pEnum->Select(guid, &central, static_cast<IUnknown*>(pAudioOut)); FAILED(hr))
return hr;
pCentral = central;
// From Microsoft Speech SDK 4.0 documentation:
// The engine will AddRef the interface, and release the interface when the engine is destroyed.
// Because of this the application will need to call Release on the audio object after the select call or audio objects will be leaked.
pAudioOut->Release();
return S_OK;
}
HRESULT SelectVoice(std::string_view voiceName) override {
TTSMODEINFO found {};
while(!pEnum->Next(1, &found, nullptr)) {
if(voiceName == found.szModeName) {
return SelectVoiceImpl(found.gModeID);
}
}
pEnum->Reset();
return S_OK;
}
private:
base::ComPtr<ITTSEnum, &IID_ITTSEnum> pEnum;
ITTSCentral* pCentral { nullptr };
// The above comment is also why this isn't a ComPtr.
AudioOutBuffer* pAudioOut { nullptr };
std::vector<VoiceInfo> voices{};
};
ISpeechAPI* ISpeechAPI::CreateSapi4() {
return new SpeechAPI_SAPI4();
}

View file

@ -1,185 +0,0 @@
#include "audio_buffer.hpp"
#include <stdio.h>
#include <winerror.h>
// Implements IAudioDest.
struct AudioOutBuffer::Dest : public IAudioDest {
Dest(AudioOutBuffer* pOut) : pOut(pOut) {}
~Dest() = default;
// IUnknown
STDMETHODIMP QueryInterface(const IID& riid, LPVOID* ppvObj) noexcept override { return pOut->QueryInterface(riid, ppvObj); }
STDMETHODIMP_(ULONG) AddRef() noexcept override {
m_iRefCount++;
return pOut->AddRef();
}
STDMETHODIMP_(ULONG) Release() noexcept override {
m_iRefCount--;
return pOut->Release();
}
// IAudioDest
STDMETHODIMP FreeSpace(DWORD* pFree, BOOL* pEOF) noexcept override { return E_NOTIMPL; }
STDMETHODIMP DataSet(PVOID pBuffer, DWORD dwSize) noexcept override {
// TODO
printf("AudioOutBuffer::Dest::DataSet()\n");
return E_NOTIMPL;
}
STDMETHODIMP BookMark(DWORD pos) noexcept override {
// Ignored
return S_OK;
}
private:
AudioOutBuffer* pOut;
ULONG m_iRefCount{};
};
// Implements IAudio.
struct AudioOutBuffer::Audio : public IAudio {
Audio(AudioOutBuffer* pOut) : pOut(pOut) {}
~Audio() {
};
// IUnknown
STDMETHODIMP QueryInterface(const IID& riid, LPVOID* ppvObj) noexcept override { return pOut->QueryInterface(riid, ppvObj); }
STDMETHODIMP_(ULONG) AddRef() noexcept override {
m_iRefCount++;
return pOut->AddRef();
}
STDMETHODIMP_(ULONG) Release() noexcept override {
m_iRefCount--;
return pOut->Release();
}
// IAudio
STDMETHODIMP Flush() noexcept override { return S_OK; }
STDMETHODIMP LevelGet(DWORD* pLevel) noexcept override { return E_NOTIMPL; }
STDMETHODIMP LevelSet(DWORD level) noexcept override { return E_NOTIMPL; }
STDMETHODIMP PassNotify(PVOID pSink, IID iid) noexcept override {
printf("AudioOutBuffer::Audio::PassNotify() %p\n", pSink);
if(pSink == nullptr) {
return E_INVALIDARG;
}
if(iid != IID_IAudioDestNotifySink)
return AUDERR_INVALIDNOTIFYSINK;
pOut->m_pDestNotifySink = static_cast<IAudioDestNotifySink*>(pSink);
return S_OK;
}
STDMETHODIMP PosnGet(PQWORD pos) noexcept override {
if(!pos)
return E_POINTER;
*pos = pOut->m_BufferWritten;
return S_OK;
}
STDMETHODIMP Claim() noexcept override { return S_OK; }
STDMETHODIMP UnClaim() noexcept override { return S_OK; }
STDMETHODIMP Start() noexcept override {
// TODO
return S_OK;
}
STDMETHODIMP Stop() noexcept override {
// TODO
return S_OK;
}
STDMETHODIMP TotalGet(PQWORD total) noexcept override { return PosnGet(total); }
STDMETHODIMP ToFileTime(PQWORD time, FILETIME* out) noexcept override { return E_NOTIMPL; }
STDMETHODIMP WaveFormatGet(SDATA* pOut) noexcept override {
// TODO
//IMalloc* pMalloc { nullptr };
//if(auto hr = CoGetMalloc(1, &pMalloc); FAILED(hr))
// return hr;
return S_OK;
}
STDMETHODIMP WaveFormatSet(SDATA data) noexcept override {
if(!data.pData)
return E_INVALIDARG;
return S_OK;
}
private:
AudioOutBuffer* pOut;
ULONG m_iRefCount{};
};
AudioOutBuffer::AudioOutBuffer() {
printf("AudioOutBuffer() %p\n", this);
m_Audio = new AudioOutBuffer::Audio(this);
m_AudioDestBuffer = new AudioOutBuffer::Dest(this);
}
AudioOutBuffer::~AudioOutBuffer() {
printf("~AudioOutBuffer() %p\n", this);
if(m_AudioDestBuffer)
delete m_AudioDestBuffer;
if(m_Audio)
delete m_Audio;
}
// IUnknown
STDMETHODIMP AudioOutBuffer::QueryInterface(const IID& riid, LPVOID* ppvObj) noexcept {
if(!ppvObj)
return E_NOINTERFACE;
*ppvObj = nullptr;
// This object only directly implements IUnknown, and proxies
// to other implementation objects for other interfaces.
if(riid == IID_IUnknown) {
*ppvObj = static_cast<void*>(this);
AddRef();
return S_OK;
}
// Return implementations
if(riid == IID_IAudio) {
*ppvObj = static_cast<void*>(m_Audio);
//m_Audio->AddRef();
return S_OK;
} else if(riid == IID_IAudioDest) {
*ppvObj = static_cast<void*>(m_AudioDestBuffer);
//m_AudioDestBuffer->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
STDMETHODIMP_(ULONG) AudioOutBuffer::AddRef() {
return InterlockedIncrement(&m_iRefCount);
}
STDMETHODIMP_(ULONG) AudioOutBuffer::Release() {
auto ulRefCount = InterlockedDecrement(&m_iRefCount);
if(ulRefCount == 0) {
printf("Deleting release AudioOutBuffer\n");
delete this;
}
return ulRefCount;
}

View file

@ -1,43 +0,0 @@
#pragma once
#include <windows.h>
#include "base/comptr.hpp"
#include "sapi4/include/speech.h"
// Implementation of SAPI 4 IAudio(Dest) to spit out wave format data to a buffer
// TODO: out-line definition so other components can call methods they need
struct AudioOutBuffer : public IUnknown {
// Implements IAudioDest.
struct Dest;
struct Audio;
AudioOutBuffer();
virtual ~AudioOutBuffer();
AudioOutBuffer(const AudioOutBuffer&) = delete;
AudioOutBuffer(AudioOutBuffer&&) = delete;
// IUnknown
STDMETHODIMP QueryInterface(const IID& riid, LPVOID* ppvObj) noexcept override;
STDMETHODIMP_(ULONG) AddRef() override;
STDMETHODIMP_(ULONG) Release() override;
private:
ULONG m_iRefCount{};
Dest* m_AudioDestBuffer{};
Audio* m_Audio{};
enum class State {
Clear,
WroteHeader,
WritingWaveData,
Done
};
//base::ComPtr<IAudioDestNotifySink, &IID_IAudioDestNotifySink> m_pDestNotifySink{};
IAudioDestNotifySink* m_pDestNotifySink{nullptr};
QWORD m_BufferWritten{};
};

View file

@ -1,6 +0,0 @@
// This file declares all the SAPI 4.0 GUID's.
#include <windows.h>
#include <initguid.h>
#include "sapi4/include/speech.h"

View file

@ -1,30 +0,0 @@
#include <windows.h>
#include <string>
#include <string_view>
#include <vector>
/// base class for access to text-to-speech APIs.
struct ISpeechAPI {
struct VoiceInfo {
GUID guid{}; // Optional. May not be filled out if the API doesn't do guids
std::string voiceName;
};
virtual ~ISpeechAPI() = default;
/// Creates a implementation of SpeechAPI for SAPI 4.
/// COM must be initalized before calling this function.
static ISpeechAPI* CreateSapi4();
/// Performs the bare level of initalization required to use the speech API
virtual HRESULT Initialize() = 0;
virtual const std::vector<VoiceInfo>& GetVoices() = 0;
/// Selects a voice.
virtual HRESULT SelectVoice(std::string_view voiceName) = 0;
//virtual HRESULT Speak(LPCSTR text, char** pOutputBuffer, size_t* pOutSize) = 0;
};

View file

@ -1,34 +0,0 @@
#include <windows.h>
typedef BOOL(WINAPI* PFN_INITIALIZECRITICALSECTIONEX)(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD dwFlags);
extern "C" {
PFN_INITIALIZECRITICALSECTIONEX pfnInitalizeCriticalSectionEx = nullptr;
BOOL WINAPI _InitalizeCriticalSectionEx_xp(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD dwFlags) {
// We ignore dwFlags, but pass dwSpinCount to InitializeCriticalSectionAndSpinCount,
// which DOES exist on XP.
static_cast<void>(dwFlags);
return InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount);
}
BOOL WINAPI LibInitalizeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD dwFlags) {
if(!pfnInitalizeCriticalSectionEx) {
pfnInitalizeCriticalSectionEx =
reinterpret_cast<PFN_INITIALIZECRITICALSECTIONEX>(GetProcAddress(GetModuleHandle("kernel32.dll"), "InitializeCriticalSectionEx"));
// Compatibilty.
if(!pfnInitalizeCriticalSectionEx)
pfnInitalizeCriticalSectionEx = _InitalizeCriticalSectionEx_xp;
}
return pfnInitalizeCriticalSectionEx(lpCriticalSection, dwSpinCount, dwFlags);
}
void WINAPI LibGetSystemTimePreciseAsFileTime(LPFILETIME lpSystemTimeAsFileTime) {
// TODO: Above
return GetSystemTimeAsFileTime(lpSystemTimeAsFileTime);
}
}

View file

@ -1,9 +0,0 @@
// This file declares forwarders for Windows imports which the newer VS 2022 CRT requires.
.globl "__imp__InitializeCriticalSectionEx@12"
"__imp__InitializeCriticalSectionEx@12":
.long "_LibInitalizeCriticalSectionEx@12"
.globl "__imp__GetSystemTimePreciseAsFileTime@4"
"__imp__GetSystemTimePreciseAsFileTime@4":
.long "_LibGetSystemTimePreciseAsFileTime@4"

View file

@ -1,104 +0,0 @@
/* This file provides routines used for thread-safe initialisation of static
* variables when /Zc:threadSafeInit is used (enabled by default).
*
* This is intended to override the default implementations from the Microsoft
* C++ Runtime which are compiled to target Windows Vista or later.
*
* Modeled on the reference implementation in thread_safe_statics.cpp in the
* Microsoft C++ Runtime.
*/
#include <assert.h>
#include <limits.h>
#include <pthread.h>
static const int UNINITIALIZED = 0;
static const int INITIALIZING = -1;
static const int EPOCH_BASE = INT_MIN;
/* Exposed as a public symbol in the reference implementation, so exposed it
* has to stay here too...
*/
int _Init_global_epoch = EPOCH_BASE;
__declspec(thread) int _Init_thread_epoch = EPOCH_BASE;
static pthread_mutex_t _Init_thread_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t _Init_thread_cond = PTHREAD_COND_INITIALIZER;
void __cdecl _Init_thread_lock()
{
pthread_mutex_lock(&_Init_thread_mutex);
}
void __cdecl _Init_thread_unlock()
{
pthread_mutex_unlock(&_Init_thread_mutex);
}
void __cdecl _Init_thread_wait_v2()
{
pthread_cond_wait(&_Init_thread_cond, &_Init_thread_mutex);
}
void __cdecl _Init_thread_notify()
{
pthread_cond_broadcast(&_Init_thread_cond);
}
void __cdecl _Init_thread_header(int* const pOnce)
{
_Init_thread_lock();
if (*pOnce == UNINITIALIZED)
{
*pOnce = INITIALIZING;
}
else
{
while (*pOnce == INITIALIZING)
{
_Init_thread_wait_v2();
if (*pOnce == UNINITIALIZED)
{
*pOnce = INITIALIZING;
_Init_thread_unlock();
return;
}
}
_Init_thread_epoch = _Init_global_epoch;
}
_Init_thread_unlock();
}
void __cdecl _Init_thread_abort(int* const pOnce)
{
_Init_thread_lock();
*pOnce = UNINITIALIZED;
_Init_thread_unlock();
_Init_thread_notify();
}
void __cdecl _Init_thread_footer(int* const pOnce)
{
_Init_thread_lock();
++_Init_global_epoch;
/* Probably unlikely condition... you would need to construct ~2 billion
* static objects before the "epoch" would roll up to the "INITIALIZING"
* constant and cause weird behaviour... the official implementation
* technically has this bug too.
*/
assert(_Init_global_epoch < INITIALIZING);
*pOnce = _Init_global_epoch;
_Init_thread_epoch = _Init_global_epoch;
_Init_thread_unlock();
_Init_thread_notify();
}

View file

@ -1,4 +0,0 @@
Files in this directory are the intellectual property of:
- sapi4: Microsoft Speech SDK 4.0.4.2512
- sapi5: Microsoft Speech SDK 5.1

View file

@ -1,13 +0,0 @@
# Hack but it works :)
set(BOOST_SUPERPROJECT_VERSION 1.85.0)
# Boost Context Have Sucks
set(BOOST_CONTEXT_ASSEMBLER clang_gas CACHE STRING "")
set(BOOST_CONTEXT_ASM_SUFFIX .S CACHE STRING "")
# Populate library list
file(STRINGS ${CMAKE_CURRENT_SOURCE_DIR}/list _COLLABVM3_BOOST_LIBRARY_LIST)
foreach(lib ${_COLLABVM3_BOOST_LIBRARY_LIST})
message(STATUS "Adding boost module ${lib}")
add_subdirectory(${lib})
endforeach()

View file

@ -1,11 +0,0 @@
# Welcome to hell
This is where all the boost libraries used by the server live
see ./list for them
# Reinitalizing
The following bash one liner was used to initalize this repo
`for f in $(cat list); do git submodule add https://github.com/boostorg/$f.git $f; cd $f; git checkout boost-1.82.0; cd ..; done`

View file

@ -1,10 +0,0 @@
#!/bin/bash
# note that this doesn't add the library to ./list
# you will need to do that yourself
# (it could be added but bleh)
lib=$1
git submodule add https://github.com/boostorg/$lib.git $lib
# remember to bump the version afterwards

@ -1 +0,0 @@
Subproject commit 32c5a6327cfdca5d41ce0f1d8849b811886daa2f

@ -1 +0,0 @@
Subproject commit 5ad7df63cd792fbdb801d600b93cad1a432f0151

@ -1 +0,0 @@
Subproject commit 23f6b27c0d9916b9932baac898ae3009817a9153

@ -1 +0,0 @@
Subproject commit e65367991cb5fbdb8a7cf218ae38f69ca9a0a9f5

@ -1 +0,0 @@
Subproject commit 447e0b3a331930f8708ade0e42683d12de9dfbc3

@ -1 +0,0 @@
Subproject commit 5bbcce0f6e855dc4009e2e6977c62e0520c39573

@ -1 +0,0 @@
Subproject commit 98b8be489fa7a74753a724d8d0772d90bcbed0fc

@ -1 +0,0 @@
Subproject commit 9fbfdcb3577e9427815d4f8cc25b3a25d5b9696b

View file

@ -1,10 +0,0 @@
#!/bin/bash
# bump all the repositories
for library in $(cat $PWD/list); do
pushd $library >/dev/null 2>&1
git checkout develop
git pull
git checkout $1
popd >/dev/null 2>&1
done

@ -1 +0,0 @@
Subproject commit ecdca0865d4d8fcfbf8e5b48914f2b5711c418ad

@ -1 +0,0 @@
Subproject commit ee0d6d543a37d9b7243682549e9ae359eb89daa9

@ -1 +0,0 @@
Subproject commit a08a5b55ee82e0c2487523471379ac53a23935dc

@ -1 +0,0 @@
Subproject commit 37c9bddf0bdefaaae0ca5852c1a153d9fc43f278

@ -1 +0,0 @@
Subproject commit 11385ec21012926e15a612e3bf9f9a71403c1e5b

@ -1 +0,0 @@
Subproject commit 6e697d796897b32b471b4f0740dcaa03d8ee57cc

@ -1 +0,0 @@
Subproject commit 6d214eb776456bf17fbee20780a034a23438084f

@ -1 +0,0 @@
Subproject commit 1bde50e400547e29336afe7ea0cd693d8c884fb6

@ -1 +0,0 @@
Subproject commit 9f285ef0c43c101e49b37bf5e6085e8d635887dc

@ -1 +0,0 @@
Subproject commit 083b41c17e34f1fc9b43ab796b40d0d8bece685c

@ -1 +0,0 @@
Subproject commit 1e1347c0b1910b9310ec1719edad8b0bf2fd03c8

@ -1 +0,0 @@
Subproject commit 85e637cb325208c2af9af791c3a1948b4888c6cd

@ -1 +0,0 @@
Subproject commit 50719b212349f3d1268285c586331584d3dbfeb5

@ -1 +0,0 @@
Subproject commit 9c3a0022b25d3e483f9100cc363bc93a72fd900a

@ -1 +0,0 @@
Subproject commit c9b436e5dfce85e8ae365e5aabbb872dd35c29eb

@ -1 +0,0 @@
Subproject commit b9170a02f102250b308c9f94ed6593c5f30eab39

@ -1 +0,0 @@
Subproject commit a0c8edba38a4d31b449fcf7b7ada455977342596

@ -1 +0,0 @@
Subproject commit 28b88d07bb4807445462c3f5dab0efde6f532d32

@ -1 +0,0 @@
Subproject commit 895335874d67987ada0d8bf6ca1725e70642ed49

@ -1 +0,0 @@
Subproject commit 6a573e4b8333ee63ee62ce95558c3667348db233

@ -1 +0,0 @@
Subproject commit 7d4c03fa032299f2d46149b7b3136c9fd43e4f81

@ -1 +0,0 @@
Subproject commit dea8e3445dc3ca29201498260307138b9460a70c

@ -1 +0,0 @@
Subproject commit 07ba0e376177409c396c109807c13b7181a98ebe

@ -1 +0,0 @@
Subproject commit 342e4c6d10d586058818daa84201a2d301357a53

@ -1 +0,0 @@
Subproject commit 4f7219965a399051bb0d8088ea4ab3929b1ac3f2

@ -1 +0,0 @@
Subproject commit 9f85ed6d62ff91c6dc4fc30e3a20e9049ec67585

@ -1 +0,0 @@
Subproject commit ed8f9cd32f4fde695d497502f696f6f861b68559

@ -1 +0,0 @@
Subproject commit 02e5821ab32c45fad719829e9644e5d681c9ba0b

View file

@ -1,67 +0,0 @@
algorithm
align
array
assert
atomic
bind
charconv
chrono
circular_buffer
concept_check
config
container
container_hash
context
conversion
core
coroutine
date_time
describe
detail
endian
exception
filesystem
function
functional
function_types
fusion
integer
intrusive
io
iterator
json
leaf
lexical_cast
lockfree
logic
move
mp11
mpl
numeric_conversion
optional
parameter
pool
predef
preprocessor
range
ratio
rational
regex
scope
smart_ptr
static_assert
static_string
system
throw_exception
tokenizer
tuple
type_index
typeof
type_traits
unordered
url
utility
variant2
winapi
asio
beast

@ -1 +0,0 @@
Subproject commit fdd4d0632dd0904f6e9c656c45397fe8ef985bc9

@ -1 +0,0 @@
Subproject commit 145778490c2d332c1411df6a5274a4b53ec3e091

@ -1 +0,0 @@
Subproject commit 7c01072629d83a7b54c99de70ef535d699ebd200

@ -1 +0,0 @@
Subproject commit 863d8b8d2b20f2acd0b5870f23e553df9ce90e6c

@ -1 +0,0 @@
Subproject commit b440c45c2810acbddc917db057f2e5194da1a199

@ -1 +0,0 @@
Subproject commit 50a1eae942effb0a9b90724323ef8f2a67e7984a

@ -1 +0,0 @@
Subproject commit c60db27762ff9cc16529e069c3c15f2fa898f994

@ -1 +0,0 @@
Subproject commit c07f2b8d37ded87f6f9d5bac867550f6e61282c1

@ -1 +0,0 @@
Subproject commit ec7da07ed13e0c61e50d945b574a12ae7ec83cf4

@ -1 +0,0 @@
Subproject commit 0fdfb49c3a6789e50169a44e88a07cc889001106

@ -1 +0,0 @@
Subproject commit c4ea7e40d365ba28faecef8917d5c3f1e0121bf9

@ -1 +0,0 @@
Subproject commit 2bb6b636796f7b008196888613f51f5bb347c77d

@ -1 +0,0 @@
Subproject commit d5b33caa7d564be9be6d962b18659b7741d764ac

@ -1 +0,0 @@
Subproject commit 564623136417068916495e2b24737054d607347c

Some files were not shown because too many files have changed in this diff Show more