Temporary commit
This commit is contained in:
parent
73686be925
commit
ac2120aa2e
26 changed files with 349 additions and 95 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -8,7 +8,10 @@ compile_commands.json
|
||||||
flatpak-repo
|
flatpak-repo
|
||||||
*.flatpak
|
*.flatpak
|
||||||
!build*.sh
|
!build*.sh
|
||||||
|
!build*.py
|
||||||
!build.gradle
|
!build.gradle
|
||||||
|
venv
|
||||||
|
__pycache__
|
||||||
.cxx
|
.cxx
|
||||||
.gradle
|
.gradle
|
||||||
/sdl-android-project/app/jni
|
/sdl-android-project/app/jni
|
||||||
|
|
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -30,7 +30,7 @@
|
||||||
url = https://github.com/google/oboe.git
|
url = https://github.com/google/oboe.git
|
||||||
[submodule "subprojects/protobuf"]
|
[submodule "subprojects/protobuf"]
|
||||||
path = subprojects/protobuf
|
path = subprojects/protobuf
|
||||||
url = /subprojects/protobuf
|
url = https://github.com/protocolbuffers/protobuf.git
|
||||||
[submodule "subprojects/fmt"]
|
[submodule "subprojects/fmt"]
|
||||||
path = subprojects/fmt
|
path = subprojects/fmt
|
||||||
url = https://github.com/fmtlib/fmt.git
|
url = https://github.com/fmtlib/fmt.git
|
||||||
|
|
|
@ -205,7 +205,6 @@ macro(prefix_all)
|
||||||
list(APPEND ${OUT_VAR} ${PREFIX}${ARG})
|
list(APPEND ${OUT_VAR} ${PREFIX}${ARG})
|
||||||
endforeach()
|
endforeach()
|
||||||
endmacro()
|
endmacro()
|
||||||
find_package(Protobuf REQUIRED)
|
|
||||||
find_package(gRPC CONFIG REQUIRED)
|
find_package(gRPC CONFIG REQUIRED)
|
||||||
|
|
||||||
set(_PROTOBUF_LIBPROTOBUF ${Protobuf_LIBRARY_RELEASE})
|
set(_PROTOBUF_LIBPROTOBUF ${Protobuf_LIBRARY_RELEASE})
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "file_backend.hpp"
|
#include "file_backend.hpp"
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include <log.hpp>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
std::optional<uint64_t> SDLMixerXBackend::get_max_samples() {
|
std::optional<uint64_t> SDLMixerXBackend::get_max_samples() {
|
||||||
return 100;
|
return 100;
|
||||||
|
@ -38,7 +39,9 @@ void SDLMixerXBackend::load(const char *filename) {
|
||||||
Mix_SetSoundFonts(NULL);
|
Mix_SetSoundFonts(NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DEBUG.writefln("Opening file: %s", filename);
|
||||||
file = open_file(filename);
|
file = open_file(filename);
|
||||||
|
DEBUG.writeln("Loading file...");
|
||||||
Mix_Music *output = Mix_LoadMUS_RW(get_sdl_file(this->file), 0);
|
Mix_Music *output = Mix_LoadMUS_RW(get_sdl_file(this->file), 0);
|
||||||
if (output == nullptr) {
|
if (output == nullptr) {
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
|
|
|
@ -93,8 +93,6 @@ void VgmStreamBackend::switch_stream(int idx) {
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
open = true;
|
open = true;
|
||||||
spec.channels = vf->channels;
|
|
||||||
spec.freq = vf->sample_rate;
|
|
||||||
}
|
}
|
||||||
void VgmStreamBackend::cleanup() {
|
void VgmStreamBackend::cleanup() {
|
||||||
streams.clear();
|
streams.clear();
|
||||||
|
|
|
@ -12,19 +12,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef __EMSCRIPTEN__
|
|
||||||
#define SAMPLES_PER_BUFFER (1024)
|
|
||||||
#define SAMP_POS_FRAC_BITS (22)
|
|
||||||
#else
|
|
||||||
#define SAMPLES_PER_BUFFER (256)
|
|
||||||
#define SAMP_POS_FRAC_BITS (24)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define VERA_SAMP_CLKS_PER_CPU_CLK ((25000000ULL << SAMP_POS_FRAC_BITS) / 512 / MHZ / 1000000)
|
|
||||||
#define YM_SAMP_CLKS_PER_CPU_CLK ((3579545ULL << SAMP_POS_FRAC_BITS) / 64 / MHZ / 1000000)
|
|
||||||
#define SAMPLE_BYTES (2 * sizeof(int16_t))
|
|
||||||
#define SAMP_POS_MASK (SAMPLES_PER_BUFFER - 1)
|
|
||||||
#define SAMP_POS_MASK_FRAC (((uint32_t)SAMPLES_PER_BUFFER << SAMP_POS_FRAC_BITS) - 1)
|
|
||||||
|
|
||||||
// windowed sinc
|
// windowed sinc
|
||||||
static const int16_t filter[512] = {
|
static const int16_t filter[512] = {
|
||||||
|
|
|
@ -5,6 +5,18 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#ifdef __EMSCRIPTEN__
|
||||||
|
#define SAMPLES_PER_BUFFER (1024)
|
||||||
|
#define SAMP_POS_FRAC_BITS (22)
|
||||||
|
#else
|
||||||
|
#define SAMPLES_PER_BUFFER (256)
|
||||||
|
#define SAMP_POS_FRAC_BITS (24)
|
||||||
|
#endif
|
||||||
|
#define VERA_SAMP_CLKS_PER_CPU_CLK ((25000000ULL << SAMP_POS_FRAC_BITS) / 512 / MHZ / 1000000)
|
||||||
|
#define YM_SAMP_CLKS_PER_CPU_CLK ((3579545ULL << SAMP_POS_FRAC_BITS) / 64 / MHZ / 1000000)
|
||||||
|
#define SAMPLE_BYTES (2 * sizeof(int16_t))
|
||||||
|
#define SAMP_POS_MASK (SAMPLES_PER_BUFFER - 1)
|
||||||
|
#define SAMP_POS_MASK_FRAC (((uint32_t)SAMPLES_PER_BUFFER << SAMP_POS_FRAC_BITS) - 1)
|
||||||
|
|
||||||
#define AUDIO_SAMPLERATE (25000000 / 512)
|
#define AUDIO_SAMPLERATE (25000000 / 512)
|
||||||
void audio_callback(void *userdata, Uint8 *stream, int len);
|
void audio_callback(void *userdata, Uint8 *stream, int len);
|
||||||
|
|
|
@ -13,25 +13,28 @@ extern "C" {
|
||||||
#include <file_backend.hpp>
|
#include <file_backend.hpp>
|
||||||
void ZsmBackend::load(const char *filename) {
|
void ZsmBackend::load(const char *filename) {
|
||||||
spec.format = AUDIO_S16SYS;
|
spec.format = AUDIO_S16SYS;
|
||||||
|
spec.samples = SAMPLES_PER_BUFFER;
|
||||||
|
spec.channels = 2;
|
||||||
|
spec.size = spec.samples * SAMPLE_BYTES;
|
||||||
file = open_file(filename);
|
file = open_file(filename);
|
||||||
char magic[2];
|
char magic[2];
|
||||||
file->read(magic, 2);
|
file->read(magic, 2, 1);
|
||||||
if (magic[0] != 0x7a || magic[1] != 0x6d) {
|
if (magic[0] != 0x7a || magic[1] != 0x6d) {
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
uint8_t version;
|
uint8_t version;
|
||||||
file->read(&version, 1);
|
file->read(&version, 1, 1);
|
||||||
uint8_t loop_point[3];
|
uint8_t loop_point[3];
|
||||||
file->read(loop_point, 3);
|
file->read(loop_point, 3, 1);
|
||||||
this->loop_point = loop_point[0] | ((uint32_t)(loop_point[1]) << 8) | ((uint32_t)(loop_point[2]) << 16);
|
this->loop_point = loop_point[0] | ((uint32_t)(loop_point[1]) << 8) | ((uint32_t)(loop_point[2]) << 16);
|
||||||
file->read(loop_point, 3);
|
file->read(loop_point, 3, 1);
|
||||||
this->pcm_offset = loop_point[0] | ((uint32_t)(loop_point[1]) << 8) | ((uint32_t)(loop_point[2]) << 16);
|
this->pcm_offset = loop_point[0] | ((uint32_t)(loop_point[1]) << 8) | ((uint32_t)(loop_point[2]) << 16);
|
||||||
file->read(&fm_mask, 1);
|
file->read(&fm_mask, 1, 1);
|
||||||
file->read(loop_point, 2);
|
file->read(loop_point, 2, 1);
|
||||||
this->psg_channel_mask = loop_point[0] | ((uint16_t)(loop_point[1]) << 8);
|
this->psg_channel_mask = loop_point[0] | ((uint16_t)(loop_point[1]) << 8);
|
||||||
file->read(loop_point, 2);
|
file->read(loop_point, 2, 1);
|
||||||
this->tick_rate = loop_point[0] | ((uint16_t)(loop_point[1]) << 8);
|
this->tick_rate = loop_point[0] | ((uint16_t)(loop_point[1]) << 8);
|
||||||
file->read(loop_point, 2); // Reserved.
|
file->read(loop_point, 2, 1); // Reserved.
|
||||||
music_data_start = file->get_pos();
|
music_data_start = file->get_pos();
|
||||||
while (true) {
|
while (true) {
|
||||||
ZsmCommand cmd = get_command();
|
ZsmCommand cmd = get_command();
|
||||||
|
@ -100,13 +103,21 @@ size_t ZsmBackend::render(void *buf, size_t maxlen) {
|
||||||
audio_step((int)(clocks));
|
audio_step((int)(clocks));
|
||||||
}
|
}
|
||||||
audio_render();
|
audio_render();
|
||||||
audio_callback(nullptr, (Uint8*)(buf), sample_type_len * maxlen);
|
maxlen *= sample_type_len;
|
||||||
return maxlen * sample_type_len;
|
if (audio_buf.size() < maxlen) {
|
||||||
|
size_t oldlen = audio_buf.size();
|
||||||
|
audio_buf.resize(audio_buf.size() + spec.size);
|
||||||
|
audio_callback(nullptr, (Uint8*)(audio_buf.data() + oldlen), spec.size);
|
||||||
|
}
|
||||||
|
memcpy(audio_buf.data(), buf, maxlen);
|
||||||
|
memmove(audio_buf.data(), audio_buf.data() + maxlen, audio_buf.size() - maxlen);
|
||||||
|
audio_buf.resize(audio_buf.size() - maxlen);
|
||||||
|
return maxlen;
|
||||||
}
|
}
|
||||||
ZsmCommand ZsmBackend::get_command() {
|
ZsmCommand ZsmBackend::get_command() {
|
||||||
ZsmCommandId cmdid;
|
ZsmCommandId cmdid;
|
||||||
uint8_t cmd_byte;
|
uint8_t cmd_byte;
|
||||||
file->read(&cmd_byte, 1);
|
file->read(&cmd_byte, 1, 1);
|
||||||
if (cmd_byte == 0x80) {
|
if (cmd_byte == 0x80) {
|
||||||
cmdid = ZsmEOF;
|
cmdid = ZsmEOF;
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,7 +139,7 @@ ZsmCommand ZsmBackend::get_command() {
|
||||||
return output;
|
return output;
|
||||||
} else if (cmdid == PsgWrite) {
|
} else if (cmdid == PsgWrite) {
|
||||||
uint8_t value;
|
uint8_t value;
|
||||||
file->read(&value, 1);
|
file->read(&value, 1, 1);
|
||||||
output.psg_write.reg = cmd_byte & 0x3F;
|
output.psg_write.reg = cmd_byte & 0x3F;
|
||||||
output.psg_write.val = value;
|
output.psg_write.val = value;
|
||||||
} else if (cmdid == FmWrite) {
|
} else if (cmdid == FmWrite) {
|
||||||
|
@ -139,11 +150,11 @@ ZsmCommand ZsmBackend::get_command() {
|
||||||
for (uint8_t i = 0; i < pairs; i++) {
|
for (uint8_t i = 0; i < pairs; i++) {
|
||||||
output.fm_write.regs[i].reg = value[0];
|
output.fm_write.regs[i].reg = value[0];
|
||||||
output.fm_write.regs[i].val = value[1];
|
output.fm_write.regs[i].val = value[1];
|
||||||
file->read(value, 2);
|
file->read(value, 2, 1);
|
||||||
}
|
}
|
||||||
} else if (cmdid == ExtCmd) {
|
} else if (cmdid == ExtCmd) {
|
||||||
uint8_t ext_cmd_byte;
|
uint8_t ext_cmd_byte;
|
||||||
file->read(&ext_cmd_byte, 1);
|
file->read(&ext_cmd_byte, 1, 1);
|
||||||
uint8_t bytes = ext_cmd_byte & 0x3F;
|
uint8_t bytes = ext_cmd_byte & 0x3F;
|
||||||
uint8_t ch = ext_cmd_byte >> 6;
|
uint8_t ch = ext_cmd_byte >> 6;
|
||||||
output.extcmd.channel = ch;
|
output.extcmd.channel = ch;
|
||||||
|
@ -155,7 +166,7 @@ ZsmCommand ZsmBackend::get_command() {
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < bytes; i++) {
|
for (size_t i = 0; i < bytes; i++) {
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
file->read(&byte, 1);
|
file->read(&byte, 1, 1);
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 0: {
|
case 0: {
|
||||||
output.extcmd.pcm[i] = byte;
|
output.extcmd.pcm[i] = byte;
|
||||||
|
|
|
@ -46,6 +46,7 @@ struct ZsmCommand {
|
||||||
};
|
};
|
||||||
class ZsmBackend : public PlaybackBackend {
|
class ZsmBackend : public PlaybackBackend {
|
||||||
File *file;
|
File *file;
|
||||||
|
std::vector<uint8_t> audio_buf;
|
||||||
uint32_t loop_point;
|
uint32_t loop_point;
|
||||||
uint32_t pcm_offset;
|
uint32_t pcm_offset;
|
||||||
uint8_t fm_mask;
|
uint8_t fm_mask;
|
||||||
|
|
|
@ -58,7 +58,7 @@ class RendererBackend {
|
||||||
void SetWindowSize(int w, int h);
|
void SetWindowSize(int w, int h);
|
||||||
void GetWindowsize(int *w, int *h);
|
void GetWindowsize(int *w, int *h);
|
||||||
RendererBackend();
|
RendererBackend();
|
||||||
~RendererBackend();
|
virtual ~RendererBackend();
|
||||||
friend void main_loop();
|
friend void main_loop();
|
||||||
friend void backend_init(void *userdata);
|
friend void backend_init(void *userdata);
|
||||||
};
|
};
|
||||||
|
|
122
build-appimage.py
Executable file
122
build-appimage.py
Executable file
|
@ -0,0 +1,122 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
from os import path
|
||||||
|
from sys import argv
|
||||||
|
import urllib
|
||||||
|
from subprocess import call, check_output
|
||||||
|
import shutil
|
||||||
|
import pathlib
|
||||||
|
from urllib import request
|
||||||
|
from urllib.request import urlretrieve
|
||||||
|
import lddwrap
|
||||||
|
import argparse
|
||||||
|
from resolve_library import resolve as resolve_library
|
||||||
|
basedir = path.realpath(path.dirname(__file__))
|
||||||
|
def download_always(url: str, filename: str) -> None:
|
||||||
|
urlretrieve(url, filename)
|
||||||
|
def download_if_not_found(url: str, filename: str) -> None:
|
||||||
|
if path.exists(filename):
|
||||||
|
return
|
||||||
|
download_always(url, filename)
|
||||||
|
def download_appimagetool():
|
||||||
|
url = "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage"
|
||||||
|
filename = "appimagetool"
|
||||||
|
download_if_not_found(url, filename)
|
||||||
|
def build(*args: list[str]):
|
||||||
|
call(args=["./build.sh", *args])
|
||||||
|
def remove_recursive(inpath: str):
|
||||||
|
print("remove_recursive(%s)" % inpath)
|
||||||
|
if path.isdir(inpath) and not path.islink(inpath):
|
||||||
|
for inner in os.listdir(inpath):
|
||||||
|
remove_recursive(path.join(inpath, inner))
|
||||||
|
print("os.removedirs(%s)" % inpath)
|
||||||
|
os.removedirs(inpath)
|
||||||
|
elif path.islink(inpath):
|
||||||
|
print("os.unlink(%s)" % inpath)
|
||||||
|
os.unlink(inpath)
|
||||||
|
else:
|
||||||
|
print("os.remove(%s)" % inpath)
|
||||||
|
os.remove(inpath)
|
||||||
|
def force_link(src: str, dst: str):
|
||||||
|
if path.exists(dst):
|
||||||
|
remove_recursive(dst)
|
||||||
|
os.symlink(src, dst)
|
||||||
|
def force_copy(src: str, dst: str):
|
||||||
|
print("force_copy(%s, %s)" % (src, dst))
|
||||||
|
if path.exists(dst) and path.isdir(dst):
|
||||||
|
dst = path.join(dst, path.basename(src))
|
||||||
|
if path.exists(dst):
|
||||||
|
remove_recursive(dst)
|
||||||
|
shutil.copy(src, dst)
|
||||||
|
def copy_libraries(libpath: str, previous: list[str] = []):
|
||||||
|
print("Getting libraries for '%s'..." % libpath)
|
||||||
|
for lib in lddwrap.list_dependencies(pathlib.Path(libpath)):
|
||||||
|
if str(lib.path) in previous:
|
||||||
|
continue
|
||||||
|
print(" - Library %s" % lib.soname, end="")
|
||||||
|
if lib.path == None:
|
||||||
|
print("")
|
||||||
|
continue
|
||||||
|
print(" (%s)" % lib.path)
|
||||||
|
inpaths = resolve_library(str(lib.path))
|
||||||
|
for inpath in inpaths:
|
||||||
|
outpath = path.join("AppDir/lib", path.basename(inpath))
|
||||||
|
force_copy(inpath, outpath)
|
||||||
|
previous.append(str(lib.path))
|
||||||
|
copy_libraries(str(lib.path), previous)
|
||||||
|
def overwrite_file(dst: str, contents: str):
|
||||||
|
if path.exists(dst):
|
||||||
|
remove_recursive(dst)
|
||||||
|
with open(dst, "wt+") as f:
|
||||||
|
f.write(contents)
|
||||||
|
def main() -> None:
|
||||||
|
P = argparse.ArgumentParser()
|
||||||
|
P.add_argument("-j", "--parallel", action=argparse._StoreAction, default=0, type=int, help="Specifies the number of jobs to use. Set to 1 to disable parallelism", dest="parallel")
|
||||||
|
P.add_argument("-D", "--define", default=[], action=argparse._AppendAction, help="Defines a CMake variable", dest="cmake_vars")
|
||||||
|
p = P.parse_args(argv[1:])
|
||||||
|
old_dir = os.curdir
|
||||||
|
os.chdir(basedir)
|
||||||
|
os.chdir("build")
|
||||||
|
args=["cmake", ".."]
|
||||||
|
for definition in p.cmake_vars:
|
||||||
|
args.append("-D%s" % definition)
|
||||||
|
ret = call(args);
|
||||||
|
if ret != 0:
|
||||||
|
exit(ret)
|
||||||
|
parallel = p.parallel
|
||||||
|
if parallel == 0:
|
||||||
|
parallel = os.cpu_count()
|
||||||
|
args=["cmake", "--build", ".", "-j%d" % parallel]
|
||||||
|
ret = call(args)
|
||||||
|
if ret != 0:
|
||||||
|
exit(ret)
|
||||||
|
download_appimagetool()
|
||||||
|
os.makedirs("AppDir", exist_ok=True)
|
||||||
|
for dir in ["bin", "lib", "share"]:
|
||||||
|
os.makedirs(path.join("AppDir", dir), exist_ok=True)
|
||||||
|
force_link(".", "AppDir/usr")
|
||||||
|
force_link(".", "AppDir/local")
|
||||||
|
force_link("lib", "AppDir/lib64")
|
||||||
|
force_copy("looper", "AppDir/bin")
|
||||||
|
copy_libraries("looper")
|
||||||
|
force_copy(path.join(basedir, "assets/com.complecwaft.Looper.desktop"), "AppDir")
|
||||||
|
force_copy(path.join(basedir, "assets/icon.svg"), "AppDir/looper.svg")
|
||||||
|
force_copy(path.join(basedir, "assets/icon.png"), "AppDir/looper.png")
|
||||||
|
overwrite_file("AppDir/AppRun", """#!/bin/bash
|
||||||
|
export LD_LIBRARY_PATH="$APPDIR/lib"
|
||||||
|
if [ "$1" = "--gdb" ]; then
|
||||||
|
shift
|
||||||
|
exec gdb --args "$APPDIR/usr/bin/looper" "$@"
|
||||||
|
else
|
||||||
|
exec "$APPDIR/usr/bin/looper" "$@"
|
||||||
|
fi""")
|
||||||
|
os.chmod("AppDir/AppRun", 0o777)
|
||||||
|
|
||||||
|
arch = check_output(["uname", "-m"])
|
||||||
|
print("Architecture: %s" % arch.decode())
|
||||||
|
os.environ["ARCH"] = arch.decode().removesuffix("\n")
|
||||||
|
call(args=["./appimagetool", "AppDir", "Looper.AppImage"])
|
||||||
|
os.chdir(old_dir)
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
5
build-appimage.sh
Executable file
5
build-appimage.sh
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
#!/bin/bash
|
||||||
|
pushd "$(dirname "$0")"
|
||||||
|
. venv/bin/activate
|
||||||
|
./build-appimage.py "$@"
|
||||||
|
popd
|
|
@ -9,7 +9,13 @@ on_err() {
|
||||||
exit $code
|
exit $code
|
||||||
}
|
}
|
||||||
have_image() {
|
have_image() {
|
||||||
docker images --format json | jq '.[].Names' --raw-output | grep -v null | jq '.[]' --raw-output | sed 's/:.*$//' | sed 's@^.*/@@' | grep -Fx "$1" >/dev/null 2>/dev/null
|
IMAGES="$(docker images --format json)"
|
||||||
|
if echo "$IMAGES" | jq '.Repository' | grep '^null$'; then
|
||||||
|
IMAGES="$(echo "$IMAGES" | jq '.[].Names' | grep -v null | jq '.[]' --raw-output)"
|
||||||
|
else
|
||||||
|
IMAGES="$(echo "$IMAGES" | jq '.Repository' --raw-output)"
|
||||||
|
fi
|
||||||
|
echo "$IMAGES" | sed 's/:.*$//' | sed 's@^.*/@@' | grep -Fx "$1" >/dev/null 2>/dev/null
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
trap on_err ERR
|
trap on_err ERR
|
||||||
|
@ -31,6 +37,7 @@ build_cmake() {
|
||||||
if [ "$first_arg" = "in-docker" ]; then
|
if [ "$first_arg" = "in-docker" ]; then
|
||||||
ARGS=( "$@" )
|
ARGS=( "$@" )
|
||||||
cd /src
|
cd /src
|
||||||
|
git config --global --add safe.directory /src
|
||||||
build_cmake subprojects/protobuf "${ARGS[@]}"
|
build_cmake subprojects/protobuf "${ARGS[@]}"
|
||||||
pushd subprojects/grpc
|
pushd subprojects/grpc
|
||||||
git apply ../../grpc.patch || true
|
git apply ../../grpc.patch || true
|
||||||
|
|
3
build.sh
3
build.sh
|
@ -24,8 +24,9 @@ run_command() {
|
||||||
trap on_err ERR
|
trap on_err ERR
|
||||||
}
|
}
|
||||||
trap on_err ERR
|
trap on_err ERR
|
||||||
|
cd "$(dirname "$0")"
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
cd build
|
cd build
|
||||||
run_command cmake .. -DDISABLE_GTK_UI=ON -DCMAKE_BUILD_TYPE=Debug
|
run_command cmake .. -DDISABLE_GTK_UI=ON -DCMAKE_BUILD_TYPE=Debug
|
||||||
run_command cmake --build . "$@"
|
run_command cmake --build . "$@"
|
||||||
cd ..
|
cd "$OLD_DIR"
|
||||||
|
|
14
dbus.hpp
14
dbus.hpp
|
@ -100,12 +100,12 @@ class MprisAPI : public sdbus::AdaptorInterfaces<org::mpris::MediaPlayer2_adapto
|
||||||
std::vector<track_id_t> Tracks() override;
|
std::vector<track_id_t> Tracks() override;
|
||||||
bool CanEditTracks() override;
|
bool CanEditTracks() override;
|
||||||
MprisAPI(sdbus::IConnection &connection, std::string objectPath, DBusAPI *dbus_api);
|
MprisAPI(sdbus::IConnection &connection, std::string objectPath, DBusAPI *dbus_api);
|
||||||
~MprisAPI();
|
virtual ~MprisAPI();
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
class DBusAPI
|
class DBusAPI
|
||||||
#ifdef DBUS_ENABLED
|
#ifdef DBUS_ENABLED
|
||||||
: public sdbus::AdaptorInterfaces<com::complecwaft::looper_adaptor, com::complecwaft::looper::Errors_adaptor, org::freedesktop::Application_adaptor>
|
: public sdbus::AdaptorInterfaces<com::complecwaft::looper_adaptor, com::complecwaft::looper::Errors_adaptor, org::freedesktop::Application_adaptor>
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
std::map<std::string, void*> handles;
|
std::map<std::string, void*> handles;
|
||||||
|
@ -184,13 +184,13 @@ class DBusAPI
|
||||||
DBusAPI(Playback *playback, sdbus::IConnection &connection, std::string objectPath, bool daemon);
|
DBusAPI(Playback *playback, sdbus::IConnection &connection, std::string objectPath, bool daemon);
|
||||||
#endif
|
#endif
|
||||||
DBusAPI(Playback *playback, bool daemon);
|
DBusAPI(Playback *playback, bool daemon);
|
||||||
~DBusAPI();
|
virtual ~DBusAPI();
|
||||||
static DBusAPI *Create(Playback *playback, bool daemon = false);
|
static DBusAPI *Create(Playback *playback, bool daemon = false);
|
||||||
};
|
};
|
||||||
class DBusAPISender : public Playback
|
class DBusAPISender : public Playback
|
||||||
#ifdef DBUS_ENABLED
|
#ifdef DBUS_ENABLED
|
||||||
, public sdbus::ProxyInterfaces<com::complecwaft::looper_proxy, com::complecwaft::looper::Errors_proxy, org::freedesktop::Application_proxy, sdbus::Peer_proxy>
|
, public sdbus::ProxyInterfaces<com::complecwaft::looper_proxy, com::complecwaft::looper::Errors_proxy, org::freedesktop::Application_proxy, sdbus::Peer_proxy>
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
// Cache
|
// Cache
|
||||||
double length, pitch, speed, tempo, volume;
|
double length, pitch, speed, tempo, volume;
|
||||||
|
@ -201,7 +201,7 @@ class DBusAPISender : public Playback
|
||||||
optional<std::string> last_error;
|
optional<std::string> last_error;
|
||||||
// Handle for error handling.
|
// Handle for error handling.
|
||||||
std::string handle;
|
std::string handle;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Public API for creating this object, and checking if it is needed.
|
// Public API for creating this object, and checking if it is needed.
|
||||||
/// @brief Checks if this is the only instance, by attempting creation and immediately deleting the created object.
|
/// @brief Checks if this is the only instance, by attempting creation and immediately deleting the created object.
|
||||||
|
@ -256,9 +256,9 @@ class DBusAPISender : public Playback
|
||||||
protected:
|
protected:
|
||||||
DBusAPISender(sdbus::IConnection &connection, std::string busName, std::string objectPath);
|
DBusAPISender(sdbus::IConnection &connection, std::string busName, std::string objectPath);
|
||||||
public:
|
public:
|
||||||
~DBusAPISender();
|
virtual ~DBusAPISender();
|
||||||
#else
|
#else
|
||||||
public:
|
public:
|
||||||
~DBusAPISender() = default;
|
~DBusAPISender() = default;
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,9 +27,9 @@ void CFile::close() {
|
||||||
fclose(file);
|
fclose(file);
|
||||||
file = NULL;
|
file = NULL;
|
||||||
}
|
}
|
||||||
size_t CFile::read(void *ptr, size_t len) {
|
size_t CFile::read(void *ptr, size_t size, size_t len) {
|
||||||
if (file == NULL) return 0;
|
if (file == NULL) return 0;
|
||||||
return fread(ptr, 1, len, file);
|
return fread(ptr, size, len, file);
|
||||||
}
|
}
|
||||||
void CFile::seek(size_t pos, SeekType seek_type) {
|
void CFile::seek(size_t pos, SeekType seek_type) {
|
||||||
int whence;
|
int whence;
|
||||||
|
@ -56,17 +56,7 @@ bool CFile::is_open() {
|
||||||
}
|
}
|
||||||
size_t rwops_read(SDL_RWops *rwops, void *ptr, size_t size, size_t maxnum) {
|
size_t rwops_read(SDL_RWops *rwops, void *ptr, size_t size, size_t maxnum) {
|
||||||
File *file = (File*)rwops->hidden.unknown.data1;
|
File *file = (File*)rwops->hidden.unknown.data1;
|
||||||
uint8_t *ptr8 = (uint8_t*)ptr;
|
return file->read(ptr, size, maxnum);
|
||||||
size_t out = 0;
|
|
||||||
size_t tmp;
|
|
||||||
for (size_t i = 0; i < maxnum; i++) {
|
|
||||||
tmp = file->read(ptr8 + (i * size), size);
|
|
||||||
out++;
|
|
||||||
if (tmp == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
}
|
||||||
int rwops_close(SDL_RWops *rwops) {
|
int rwops_close(SDL_RWops *rwops) {
|
||||||
File *file = (File*)rwops->hidden.unknown.data1;
|
File *file = (File*)rwops->hidden.unknown.data1;
|
||||||
|
@ -116,7 +106,7 @@ static file_streamfile *sf_open(file_streamfile *sf, const char *const filename,
|
||||||
static size_t sf_read(file_streamfile *sf, uint8_t *dst, offv_t offset, size_t length) {
|
static size_t sf_read(file_streamfile *sf, uint8_t *dst, offv_t offset, size_t length) {
|
||||||
File *file = sf->file;
|
File *file = sf->file;
|
||||||
file->seek(offset, SeekType::SET);
|
file->seek(offset, SeekType::SET);
|
||||||
return file->read(dst, length);
|
return file->read(dst, 1, length);
|
||||||
}
|
}
|
||||||
static size_t sf_size(file_streamfile *sf) {
|
static size_t sf_size(file_streamfile *sf) {
|
||||||
File *file = sf->file;
|
File *file = sf->file;
|
||||||
|
|
|
@ -20,12 +20,17 @@ class File {
|
||||||
const char *name;
|
const char *name;
|
||||||
File(const char *fname);
|
File(const char *fname);
|
||||||
inline virtual void open(const char *fname) { name = fname; }
|
inline virtual void open(const char *fname) { name = fname; }
|
||||||
virtual void close() = 0;
|
inline virtual void close() { }
|
||||||
virtual size_t read(void *ptr, size_t len) = 0;
|
virtual size_t read(void *ptr, size_t size, size_t len) = 0;
|
||||||
virtual void seek(size_t pos, SeekType seek_type) = 0;
|
virtual void seek(size_t pos, SeekType seek_type) = 0;
|
||||||
virtual size_t get_len();
|
virtual size_t get_len();
|
||||||
virtual size_t get_pos() = 0;
|
virtual size_t get_pos() = 0;
|
||||||
virtual bool is_open() = 0;
|
inline virtual bool is_open() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline virtual ~File() {
|
||||||
|
if (is_open()) close();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
class CFile : public File {
|
class CFile : public File {
|
||||||
protected:
|
protected:
|
||||||
|
@ -34,7 +39,7 @@ class CFile : public File {
|
||||||
CFile(const char *fname);
|
CFile(const char *fname);
|
||||||
void open(const char *fname) override;
|
void open(const char *fname) override;
|
||||||
void close() override;
|
void close() override;
|
||||||
size_t read(void *ptr, size_t len) override;
|
size_t read(void *ptr, size_t size, size_t len) override;
|
||||||
void seek(size_t pos, SeekType seek_type) override;
|
void seek(size_t pos, SeekType seek_type) override;
|
||||||
size_t get_pos() override;
|
size_t get_pos() override;
|
||||||
bool is_open() override;
|
bool is_open() override;
|
||||||
|
@ -45,7 +50,7 @@ class HttpFile : public File {
|
||||||
HttpFile(const char *url);
|
HttpFile(const char *url);
|
||||||
void open(const char *url) override;
|
void open(const char *url) override;
|
||||||
void close() override;
|
void close() override;
|
||||||
size_t read(void *ptr, size_t len) override;
|
size_t read(void *ptr, size_t size, size_t len) override;
|
||||||
void seek(size_t pos, SeekType type) override;
|
void seek(size_t pos, SeekType type) override;
|
||||||
size_t get_pos();
|
size_t get_pos();
|
||||||
bool is_open();
|
bool is_open();
|
||||||
|
@ -58,7 +63,7 @@ class AndroidFile : public File {
|
||||||
AndroidFile(const char *fname);
|
AndroidFile(const char *fname);
|
||||||
void open(const char *fname) override;
|
void open(const char *fname) override;
|
||||||
void close() override;
|
void close() override;
|
||||||
size_t read(void *ptr, size_t len) override;
|
size_t read(void *ptr, size_t size, size_t len) override;
|
||||||
void seek(size_t pos, SeekType seek_type) override;
|
void seek(size_t pos, SeekType seek_type) override;
|
||||||
size_t get_pos() override;
|
size_t get_pos() override;
|
||||||
bool is_open() override;
|
bool is_open() override;
|
||||||
|
|
1
log.hpp
1
log.hpp
|
@ -46,6 +46,7 @@ namespace Looper::Log {
|
||||||
void writesn(const char *msg, size_t n);
|
void writesn(const char *msg, size_t n);
|
||||||
void writes(std::string msg);
|
void writes(std::string msg);
|
||||||
virtual void writec(const char chr);
|
virtual void writec(const char chr);
|
||||||
|
inline virtual ~LogStream() { }
|
||||||
void vwritef(const char *fmt, va_list args);
|
void vwritef(const char *fmt, va_list args);
|
||||||
void writef(const char *fmt, ...);
|
void writef(const char *fmt, ...);
|
||||||
void vwritefln(const char *fmt, va_list args);
|
void vwritefln(const char *fmt, va_list args);
|
||||||
|
|
31
main.cpp
31
main.cpp
|
@ -140,8 +140,6 @@ extern "C" int looper_run_as_executable(std::vector<std::string> args) {
|
||||||
}
|
}
|
||||||
DEBUG.writeln("Initializing frontends...");
|
DEBUG.writeln("Initializing frontends...");
|
||||||
init_backends();
|
init_backends();
|
||||||
DEBUG.writeln("Initializing playback backends...");
|
|
||||||
init_playback_backends();
|
|
||||||
#ifdef DBUS_ENABLED
|
#ifdef DBUS_ENABLED
|
||||||
ProxyGlueBackend *proxy_backend = nullptr;
|
ProxyGlueBackend *proxy_backend = nullptr;
|
||||||
if ((disable_gui && !daemonize) || quit) {
|
if ((disable_gui && !daemonize) || quit) {
|
||||||
|
@ -230,21 +228,28 @@ extern "C" int looper_run_as_executable(std::vector<std::string> args) {
|
||||||
#endif
|
#endif
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
std::string current_process_type;
|
||||||
extern int looper_run_playback_process(std::vector<std::string> args);
|
extern int looper_run_playback_process(std::vector<std::string> args);
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
CLI::App app{DESCRIPTION};
|
CLI::App app{DESCRIPTION};
|
||||||
std::string process_type = "normal";
|
std::string process_type = "normal";
|
||||||
app.add_option<std::string, std::string>("--process-type", process_type);
|
app.add_option<std::string, std::string>("--process-type", process_type);
|
||||||
app.allow_extras();
|
app.allow_extras();
|
||||||
app.parse(argc, argv);
|
try {
|
||||||
executable_path = argv[0];
|
app.parse(argc, argv);
|
||||||
if (process_type == "playback") {
|
executable_path = argv[0];
|
||||||
return looper_run_playback_process(app.remaining(false));
|
current_process_type = "host";
|
||||||
} else if (process_type == "daemon") {
|
if (process_type == "playback") {
|
||||||
std::vector<std::string> opts = app.remaining(false);
|
current_process_type = "playback";
|
||||||
opts.push_back("-d");
|
return looper_run_playback_process(app.remaining(false));
|
||||||
return looper_run_as_executable(opts);
|
} else if (process_type == "daemon") {
|
||||||
} else {
|
std::vector<std::string> opts = app.remaining(false);
|
||||||
return looper_run_as_executable(app.remaining(false));
|
opts.push_back("-d");
|
||||||
|
return looper_run_as_executable(opts);
|
||||||
|
} else {
|
||||||
|
return looper_run_as_executable(app.remaining(false));
|
||||||
|
}
|
||||||
|
} catch (CLI::CallForHelp) {
|
||||||
|
looper_run_as_executable(std::vector<std::string>({"--help"}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
playback.cpp
10
playback.cpp
|
@ -1,5 +1,6 @@
|
||||||
#include "playback.h"
|
#include "playback.h"
|
||||||
#include "SDL_mixer.h"
|
#include "SDL_mixer.h"
|
||||||
|
#include "playback_backend.hpp"
|
||||||
#include <SDL_audio.h>
|
#include <SDL_audio.h>
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <vgmstream.h>
|
#include <vgmstream.h>
|
||||||
|
@ -82,7 +83,7 @@ void PlaybackInstance::SDLCallbackInner(Uint8 *stream, int len) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
|
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
|
||||||
size_t bytes_per_iter = ((bufsize / unit)) * unit;
|
size_t bytes_per_iter = (bufsize / unit) * unit;
|
||||||
while (st->numSamples() < (size_t)len) {
|
while (st->numSamples() < (size_t)len) {
|
||||||
if (process == nullptr) {
|
if (process == nullptr) {
|
||||||
return;
|
return;
|
||||||
|
@ -111,6 +112,8 @@ void PlaybackInstance::SDLCallbackInner(Uint8 *stream, int len) {
|
||||||
sbuf[i] *= real_volume;
|
sbuf[i] *= real_volume;
|
||||||
}
|
}
|
||||||
st->putSamples(sbuf, new_bufsize / unit);
|
st->putSamples(sbuf, new_bufsize / unit);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
|
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
|
||||||
|
@ -156,7 +159,7 @@ void PlaybackInstance::Load(const char *file, int idx) {
|
||||||
sdl_stream = SDL_NewAudioStream(backend_spec.format, backend_spec.channels, backend_spec.freq, spec.format, spec.channels, spec.freq);
|
sdl_stream = SDL_NewAudioStream(backend_spec.format, backend_spec.channels, backend_spec.freq, spec.format, spec.channels, spec.freq);
|
||||||
if (sdl_stream == nullptr) {
|
if (sdl_stream == nullptr) {
|
||||||
ERROR.writefln("SDL_NewAudioStream: %s", SDL_GetError());
|
ERROR.writefln("SDL_NewAudioStream: %s", SDL_GetError());
|
||||||
DEBUG.writefln("format: AUDIO_%s%d%s", sample_fmt.is_float ? "F" : sample_fmt.is_signed ? "S" : "U", sample_fmt.size, sample_fmt.endian ? "MSB" : "LSB");
|
DEBUG.writefln("format: AUDIO_%s%d%s", sample_fmt.is_float ? "F" : sample_fmt.is_signed ? "S" : "U", sample_fmt.size * 8, sample_fmt.endian ? "MSB" : "LSB");
|
||||||
set_error("Failed to create SDL audio stream");
|
set_error("Failed to create SDL audio stream");
|
||||||
set_signal(PlaybackSignalErrorOccurred);
|
set_signal(PlaybackSignalErrorOccurred);
|
||||||
}
|
}
|
||||||
|
@ -174,6 +177,7 @@ void PlaybackInstance::Load(const char *file, int idx) {
|
||||||
bufsize = 0;
|
bufsize = 0;
|
||||||
}
|
}
|
||||||
delete backend_spec_proxy;
|
delete backend_spec_proxy;
|
||||||
|
playback_ready.store(true);
|
||||||
} else {
|
} else {
|
||||||
ERROR.writeln("Failed to detect valid playback backend for file!");
|
ERROR.writeln("Failed to detect valid playback backend for file!");
|
||||||
set_error("Failed to detect valid backend for file.");
|
set_error("Failed to detect valid backend for file.");
|
||||||
|
@ -233,7 +237,7 @@ void PlaybackInstance::InitLoopFunction() {
|
||||||
AUDIO_F32SYS;
|
AUDIO_F32SYS;
|
||||||
#endif
|
#endif
|
||||||
desired.freq = 48000;
|
desired.freq = 48000;
|
||||||
desired.samples = 1024;
|
desired.samples = 100;
|
||||||
desired.channels = 2;
|
desired.channels = 2;
|
||||||
desired.callback = PlaybackInstance::SDLCallback;
|
desired.callback = PlaybackInstance::SDLCallback;
|
||||||
desired.userdata = this;
|
desired.userdata = this;
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
#include <google/protobuf/message.h>
|
#include <google/protobuf/message.h>
|
||||||
|
#include "util.hpp"
|
||||||
using namespace google::protobuf;
|
using namespace google::protobuf;
|
||||||
int sndfd;
|
int sndfd;
|
||||||
int rcvfd;
|
int rcvfd;
|
||||||
|
@ -80,15 +81,22 @@ void print_ipc_message(const google::protobuf::Message &msg, size_t level) {
|
||||||
auto &unknown_fields = reflect->GetUnknownFields(msg);
|
auto &unknown_fields = reflect->GetUnknownFields(msg);
|
||||||
DEBUG.writef_level(level, "Unknown fields: %d", unknown_fields.field_count());
|
DEBUG.writef_level(level, "Unknown fields: %d", unknown_fields.field_count());
|
||||||
}
|
}
|
||||||
|
void show_command(const std::string &cmdid, const google::protobuf::Message &msg) {
|
||||||
|
DEBUG.writefln("Command %s:", cmdid.c_str());
|
||||||
|
print_ipc_message(msg);
|
||||||
|
}
|
||||||
grpc::Status PlaybackProcessServiceImpl::Render(grpc::ServerContext *ctx, const RenderCommand *cmd, RenderResponseOrError *response) {
|
grpc::Status PlaybackProcessServiceImpl::Render(grpc::ServerContext *ctx, const RenderCommand *cmd, RenderResponseOrError *response) {
|
||||||
size_t maxlen = cmd->len();
|
size_t maxlen = cmd->len();
|
||||||
void *ptr = malloc(maxlen);
|
void *ptr = malloc(maxlen);
|
||||||
size_t len = cur_backend->render(ptr, maxlen);
|
size_t len = cur_backend->render(ptr, maxlen);
|
||||||
std::string buf = std::string(len, ' ', std::allocator<char>());
|
if (len == 0) {
|
||||||
memcpy(buf.data(), ptr, len);
|
DEBUG.writeln("Didn't get any audio when rendering");
|
||||||
free(ptr);
|
} else if (is_zeroes(ptr, len)) {
|
||||||
|
DEBUG.writeln("Buffer was only zeroes!");
|
||||||
|
}
|
||||||
auto output = new RenderResponse();
|
auto output = new RenderResponse();
|
||||||
output->set_data(buf);
|
output->set_data((const char*)ptr, len);
|
||||||
|
free(ptr);
|
||||||
output->set_len(len);
|
output->set_len(len);
|
||||||
response->set_allocated_output(output);
|
response->set_allocated_output(output);
|
||||||
return grpc::Status::OK;
|
return grpc::Status::OK;
|
||||||
|
@ -214,7 +222,7 @@ grpc::Status HostProcessImpl::WriteLog(grpc::ServerContext *ctx, const LogMessag
|
||||||
}
|
}
|
||||||
grpc::Status HostProcessImpl::SetAddress(grpc::ServerContext *ctx, const StringProperty *value, MaybeError *response) {
|
grpc::Status HostProcessImpl::SetAddress(grpc::ServerContext *ctx, const StringProperty *value, MaybeError *response) {
|
||||||
process->host_channel.value().construct_client(value->value());
|
process->host_channel.value().construct_client(value->value());
|
||||||
process->done = true;
|
process->started.notify_all();
|
||||||
return grpc::Status::OK;
|
return grpc::Status::OK;
|
||||||
}
|
}
|
||||||
grpc::Status PlaybackProcessServiceImpl::Set(grpc::ServerContext *ctx, const SetProperty *request, MaybeError *response) {
|
grpc::Status PlaybackProcessServiceImpl::Set(grpc::ServerContext *ctx, const SetProperty *request, MaybeError *response) {
|
||||||
|
@ -284,13 +292,16 @@ grpc::Status PlaybackProcessServiceImpl::Init(grpc::ServerContext *ctx, const In
|
||||||
auto filename = cmd->filename();
|
auto filename = cmd->filename();
|
||||||
auto idx = cmd->idx();
|
auto idx = cmd->idx();
|
||||||
for (auto &backend : PlaybackBackendHelper()) {
|
for (auto &backend : PlaybackBackendHelper()) {
|
||||||
|
DEBUG.writefln("Trying backend: %s", backend.second->get_name().c_str());
|
||||||
try {
|
try {
|
||||||
backend.second->init(filename.c_str(), idx);
|
backend.second->init(filename.c_str(), idx);
|
||||||
} catch (std::exception e) {
|
} catch (std::exception e) {
|
||||||
|
DEBUG.writeln("Cleaning up backend.");
|
||||||
backend.second->cleanup();
|
backend.second->cleanup();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
cur_backend = backend.second;
|
cur_backend = backend.second;
|
||||||
|
DEBUG.writefln("Using backend: %s", backend.second->get_name().c_str());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cur_backend == nullptr) {
|
if (cur_backend == nullptr) {
|
||||||
|
@ -301,6 +312,7 @@ grpc::Status PlaybackProcessServiceImpl::Init(grpc::ServerContext *ctx, const In
|
||||||
response->set_allocated_err(maybe_error);
|
response->set_allocated_err(maybe_error);
|
||||||
process->done = true;
|
process->done = true;
|
||||||
process->playback_process_channel.value().get_server()->get_server()->Shutdown();
|
process->playback_process_channel.value().get_server()->get_server()->Shutdown();
|
||||||
|
DEBUG.writefln("Couldn't find any backend.");
|
||||||
return grpc::Status::OK;
|
return grpc::Status::OK;
|
||||||
}
|
}
|
||||||
return grpc::Status::OK;
|
return grpc::Status::OK;
|
||||||
|
@ -347,7 +359,12 @@ StreamList serialize_stream_list(std::vector<PlaybackStream> streams) {
|
||||||
PlaybackProcess::PlaybackProcess(std::vector<std::string> args) {
|
PlaybackProcess::PlaybackProcess(std::vector<std::string> args) {
|
||||||
done = false;
|
done = false;
|
||||||
is_playback_process = true;
|
is_playback_process = true;
|
||||||
|
Looper::Log::init_logging();
|
||||||
init_playback_backends();
|
init_playback_backends();
|
||||||
|
DEBUG.writeln("Playback backends: ");
|
||||||
|
for (auto &backend : PlaybackBackendHelper()) {
|
||||||
|
DEBUG.writefln(" - %s", backend.second->get_id().c_str());
|
||||||
|
}
|
||||||
init_audio_data();
|
init_audio_data();
|
||||||
std::string address = args[0];
|
std::string address = args[0];
|
||||||
auto mk_service = [this]() -> grpc::Service* {
|
auto mk_service = [this]() -> grpc::Service* {
|
||||||
|
@ -357,6 +374,7 @@ PlaybackProcess::PlaybackProcess(std::vector<std::string> args) {
|
||||||
};
|
};
|
||||||
playback_process_channel = IPCChannel<PlaybackProcessService, HostProcess>(mk_service);
|
playback_process_channel = IPCChannel<PlaybackProcessService, HostProcess>(mk_service);
|
||||||
playback_process_channel.value().construct_client(address);
|
playback_process_channel.value().construct_client(address);
|
||||||
|
DEBUG.writefln("Host process address: %s", address.c_str());
|
||||||
{
|
{
|
||||||
StringProperty property;
|
StringProperty property;
|
||||||
property.set_value(playback_process_channel.value().get_server()->get_address());
|
property.set_value(playback_process_channel.value().get_server()->get_address());
|
||||||
|
@ -364,7 +382,6 @@ PlaybackProcess::PlaybackProcess(std::vector<std::string> args) {
|
||||||
MaybeError response;
|
MaybeError response;
|
||||||
playback_process_channel.value().get_stub()->SetAddress(&ctx, property, &response);
|
playback_process_channel.value().get_stub()->SetAddress(&ctx, property, &response);
|
||||||
}
|
}
|
||||||
Looper::Log::init_logging();
|
|
||||||
}
|
}
|
||||||
PlaybackProcess::PlaybackProcess(std::string filename, int idx) {
|
PlaybackProcess::PlaybackProcess(std::string filename, int idx) {
|
||||||
done = false;
|
done = false;
|
||||||
|
@ -383,9 +400,17 @@ PlaybackProcess::PlaybackProcess(std::string filename, int idx) {
|
||||||
new_args.push_back("playback");
|
new_args.push_back("playback");
|
||||||
new_args.push_back(address);
|
new_args.push_back(address);
|
||||||
pid = launch(new_args);
|
pid = launch(new_args);
|
||||||
while (!done) {
|
std::thread process_check_thread(std::mem_fn(&PlaybackProcess::threadfunc), this);
|
||||||
std::this_thread::yield();
|
std::unique_lock lk(start_mutex);
|
||||||
|
started.wait(lk);
|
||||||
|
if (done) {
|
||||||
|
ERROR.writeln("Playback process exited too early!");
|
||||||
|
throw std::exception();
|
||||||
}
|
}
|
||||||
|
lk.unlock();
|
||||||
|
done = true;
|
||||||
|
process_check_thread.join();
|
||||||
|
DEBUG.writeln("Playback process started.");
|
||||||
ClientContext ctx;
|
ClientContext ctx;
|
||||||
InitCommand cmd;
|
InitCommand cmd;
|
||||||
cmd.set_filename(filename);
|
cmd.set_filename(filename);
|
||||||
|
@ -502,9 +527,15 @@ size_t PlaybackProcess::render(void *buf, size_t maxlen) {
|
||||||
RenderResponseOrError response;
|
RenderResponseOrError response;
|
||||||
get_stub()->Render(&ctx, rend_cmd, &response);
|
get_stub()->Render(&ctx, rend_cmd, &response);
|
||||||
if (response.has_err()) {
|
if (response.has_err()) {
|
||||||
|
ERROR.writefln("Error rendering audio: %s", response.err().id().c_str());
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
std::string data = response.output().data();
|
std::string data = response.output().data();
|
||||||
|
if (data.length() == 0) {
|
||||||
|
WARNING.writeln("Rendering audio didn't produce anything!");
|
||||||
|
} else if (is_zeroes(data.data(), data.length())) {
|
||||||
|
DEBUG.writeln("RECV'd buffer was only zeroes!");
|
||||||
|
}
|
||||||
memcpy(buf, data.data(), data.length());
|
memcpy(buf, data.data(), data.length());
|
||||||
return data.length();
|
return data.length();
|
||||||
}
|
}
|
||||||
|
@ -519,3 +550,19 @@ PlaybackProcess::~PlaybackProcess() {
|
||||||
get_stub()->Quit(&ctx, quit_cmd, &output);
|
get_stub()->Quit(&ctx, quit_cmd, &output);
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
void PlaybackProcess::threadfunc() {
|
||||||
|
while (!process_running()) {
|
||||||
|
std::this_thread::yield();
|
||||||
|
if (done) return;
|
||||||
|
}
|
||||||
|
while (true) {
|
||||||
|
if (!process_running()) {
|
||||||
|
done = true;
|
||||||
|
started.notify_all();
|
||||||
|
}
|
||||||
|
if (done) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::this_thread::yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <future>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
#include "thirdparty/CRC.hpp"
|
#include "thirdparty/CRC.hpp"
|
||||||
#include "playback_backend.hpp"
|
#include "playback_backend.hpp"
|
||||||
#include <grpc/grpc.h>
|
#include <grpc/grpc.h>
|
||||||
|
@ -14,7 +16,6 @@
|
||||||
#include <grpcpp/server_builder.h>
|
#include <grpcpp/server_builder.h>
|
||||||
#include <grpcpp/server_context.h>
|
#include <grpcpp/server_context.h>
|
||||||
#include "rpc.hpp"
|
#include "rpc.hpp"
|
||||||
#include <google/protobuf/stubs/status.h>
|
|
||||||
#include "ipc/common.pb.h"
|
#include "ipc/common.pb.h"
|
||||||
#include "ipc/internal.pb.h"
|
#include "ipc/internal.pb.h"
|
||||||
#include "ipc/internal.grpc.pb.h"
|
#include "ipc/internal.grpc.pb.h"
|
||||||
|
@ -51,8 +52,10 @@ class PlaybackProcess {
|
||||||
friend class PlaybackProcessServiceImpl;
|
friend class PlaybackProcessServiceImpl;
|
||||||
void threadfunc();
|
void threadfunc();
|
||||||
int pid;
|
int pid;
|
||||||
|
std::mutex start_mutex;
|
||||||
|
std::condition_variable started;
|
||||||
bool is_playback_process = false;
|
bool is_playback_process = false;
|
||||||
bool done;
|
std::atomic_bool done;
|
||||||
std::optional<IPCChannel<HostProcess, PlaybackProcessService>> host_channel;
|
std::optional<IPCChannel<HostProcess, PlaybackProcessService>> host_channel;
|
||||||
std::optional<IPCChannel<PlaybackProcessService, HostProcess>> playback_process_channel;
|
std::optional<IPCChannel<PlaybackProcessService, HostProcess>> playback_process_channel;
|
||||||
inline std::unique_ptr<PlaybackProcessService::Stub> get_stub() {
|
inline std::unique_ptr<PlaybackProcessService::Stub> get_stub() {
|
||||||
|
|
1
requirements.txt
Normal file
1
requirements.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
lddwrap
|
20
resolve_library.py
Executable file
20
resolve_library.py
Executable file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
from sys import argv
|
||||||
|
from os import readlink
|
||||||
|
from os import path
|
||||||
|
def resolve(inpath: str) -> list[str]:
|
||||||
|
output = []
|
||||||
|
inpath = path.abspath(inpath)
|
||||||
|
dir=path.dirname(inpath)
|
||||||
|
while path.islink(inpath):
|
||||||
|
output.append(inpath)
|
||||||
|
inpath=readlink(inpath)
|
||||||
|
if not path.isabs(inpath):
|
||||||
|
inpath=path.join(dir, inpath)
|
||||||
|
dir=path.dirname(inpath)
|
||||||
|
return output
|
||||||
|
if __name__ == "__main__":
|
||||||
|
for i in argv[1:]:
|
||||||
|
paths=resolve(i)
|
||||||
|
for path in paths:
|
||||||
|
print(path)
|
29
rpc.hpp
29
rpc.hpp
|
@ -2,8 +2,12 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <grpcpp/security/server_credentials.h>
|
#include <grpcpp/security/server_credentials.h>
|
||||||
|
#include <grpcpp/server_builder.h>
|
||||||
#include <grpcpp/support/byte_buffer.h>
|
#include <grpcpp/support/byte_buffer.h>
|
||||||
#include <jsoncpp/json/json.h>
|
#include <grpc/grpc.h>
|
||||||
|
#include <grpc/support/port_platform.h>
|
||||||
|
#include <json/json.h>
|
||||||
|
#include <chrono>
|
||||||
#include <google/protobuf/descriptor.h>
|
#include <google/protobuf/descriptor.h>
|
||||||
#include <google/protobuf/generated_message_reflection.h>
|
#include <google/protobuf/generated_message_reflection.h>
|
||||||
#include <google/protobuf/message.h>
|
#include <google/protobuf/message.h>
|
||||||
|
@ -28,6 +32,8 @@
|
||||||
#include <grpc++/server.h>
|
#include <grpc++/server.h>
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include "log.hpp"
|
||||||
|
using namespace std::literals;
|
||||||
#define _FORMAT_CPP_TYPE(fdesc, value, prefix, int32, int64, uint32, uint64, float, double, bool, string, enum, message, ...) \
|
#define _FORMAT_CPP_TYPE(fdesc, value, prefix, int32, int64, uint32, uint64, float, double, bool, string, enum, message, ...) \
|
||||||
switch (fdesc->cpp_type()) { \
|
switch (fdesc->cpp_type()) { \
|
||||||
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: \
|
case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: \
|
||||||
|
@ -67,9 +73,10 @@ switch (fdesc->cpp_type()) { \
|
||||||
|
|
||||||
#define FORMAT_CPP_TYPE_TITLECASE(fdesc, value, prefix, ...) _FORMAT_CPP_TYPE(fdesc, value, prefix, Int32, Int64, UInt32, UInt64, Float, Double, Bool, String, Enum, Message __VA_OPT__(,) __VA_ARGS__)
|
#define FORMAT_CPP_TYPE_TITLECASE(fdesc, value, prefix, ...) _FORMAT_CPP_TYPE(fdesc, value, prefix, Int32, Int64, UInt32, UInt64, Float, Double, Bool, String, Enum, Message __VA_OPT__(,) __VA_ARGS__)
|
||||||
|
|
||||||
|
extern std::string current_process_type;
|
||||||
inline std::string generate_address() {
|
inline std::string generate_address() {
|
||||||
std::filesystem::path tmpdir = std::filesystem::temp_directory_path();
|
std::filesystem::path tmpdir = std::filesystem::temp_directory_path();
|
||||||
std::filesystem::path sockpath = tmpdir / "looper_playback.";
|
std::filesystem::path sockpath = tmpdir / std::filesystem::path("looper_" + current_process_type + ".");
|
||||||
const char *chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._";
|
const char *chars = "abcdefghijklmnopqrtsuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._";
|
||||||
for (size_t i = 0; i < 64; i++) {
|
for (size_t i = 0; i < 64; i++) {
|
||||||
char chr = chars[rand() % strlen(chars)];
|
char chr = chars[rand() % strlen(chars)];
|
||||||
|
@ -84,7 +91,10 @@ class IPCClient {
|
||||||
using Stub = std::unique_ptr<typename C::Stub>;
|
using Stub = std::unique_ptr<typename C::Stub>;
|
||||||
std::shared_ptr<grpc::ChannelInterface> channel;
|
std::shared_ptr<grpc::ChannelInterface> channel;
|
||||||
IPCClient(std::string address) {
|
IPCClient(std::string address) {
|
||||||
|
DEBUG.writefln("Connecting to '%s'...", address.c_str());
|
||||||
channel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials());
|
channel = grpc::CreateChannel(address, grpc::InsecureChannelCredentials());
|
||||||
|
channel->WaitForConnected(gpr_time_from_seconds(30, gpr_clock_type::GPR_CLOCK_MONOTONIC));
|
||||||
|
DEBUG.writefln("Connection successful!");
|
||||||
}
|
}
|
||||||
|
|
||||||
Stub client_stub() {
|
Stub client_stub() {
|
||||||
|
@ -113,6 +123,7 @@ class IPCServer {
|
||||||
builder.AddListeningPort(address, grpc::InsecureServerCredentials());
|
builder.AddListeningPort(address, grpc::InsecureServerCredentials());
|
||||||
builder.RegisterService(service);
|
builder.RegisterService(service);
|
||||||
server = std::shared_ptr<grpc::Server>(builder.BuildAndStart().release());
|
server = std::shared_ptr<grpc::Server>(builder.BuildAndStart().release());
|
||||||
|
DEBUG.writefln("Server listening on '%s'...", address.c_str());
|
||||||
}
|
}
|
||||||
template<class SI>
|
template<class SI>
|
||||||
void init() {
|
void init() {
|
||||||
|
@ -182,6 +193,10 @@ class IPCChannel {
|
||||||
return client.has_value();
|
return client.has_value();
|
||||||
}
|
}
|
||||||
Stub get_stub() {
|
Stub get_stub() {
|
||||||
|
if (!has_client()) {
|
||||||
|
ERROR.writeln("Attempt to get client stub for nonexistant client!");
|
||||||
|
throw std::exception();
|
||||||
|
}
|
||||||
return get_client()->client_stub();
|
return get_client()->client_stub();
|
||||||
}
|
}
|
||||||
ServerPtr get_server() {
|
ServerPtr get_server() {
|
||||||
|
@ -201,9 +216,13 @@ class IPCChannel {
|
||||||
init_client(client);
|
init_client(client);
|
||||||
}
|
}
|
||||||
void construct_client(std::string client_address) {
|
void construct_client(std::string client_address) {
|
||||||
if (this->client.has_value()) return;
|
if (this->client.has_value()) {
|
||||||
Client *client = new Client(client_address);
|
ERROR.writefln("Invalid attempt to construct client for address %s", client_address.c_str());
|
||||||
init_client(client);
|
return;
|
||||||
|
} else {
|
||||||
|
DEBUG.writefln("Constructing client for address %s...", client_address.c_str());
|
||||||
|
}
|
||||||
|
init_client(client_address);
|
||||||
}
|
}
|
||||||
/// @brief Constructs an IP channel with a newly-created server with a generated address.
|
/// @brief Constructs an IP channel with a newly-created server with a generated address.
|
||||||
IPCChannel(ServiceConstructor custom_service_constructor) {
|
IPCChannel(ServiceConstructor custom_service_constructor) {
|
||||||
|
|
11
util.hpp
11
util.hpp
|
@ -58,7 +58,7 @@ inline std::string to_string_with_decimals(double value, unsigned decimals) {
|
||||||
num_text = num_text.substr(0, found);
|
num_text = num_text.substr(0, found);
|
||||||
return num_text;
|
return num_text;
|
||||||
}
|
}
|
||||||
int chars_after_decimal = num_text.size() - found - 1;
|
int chars_after_decimal = num_text.size() - found - 1;
|
||||||
if (chars_after_decimal > decimals) {
|
if (chars_after_decimal > decimals) {
|
||||||
num_text = num_text.substr(0, found + decimals + 1);
|
num_text = num_text.substr(0, found + decimals + 1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -78,6 +78,15 @@ inline size_t combine_hashes(std::initializer_list<size_t> hashes) {
|
||||||
}
|
}
|
||||||
return std::hash<std::string>()(values);
|
return std::hash<std::string>()(values);
|
||||||
}
|
}
|
||||||
|
inline bool is_zeroes(void *ptr, size_t len) {
|
||||||
|
uint8_t *ptr8 = (uint8_t*)ptr;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
if (ptr8[i] != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
int launch(std::vector<std::string> args);
|
int launch(std::vector<std::string> args);
|
||||||
#ifdef MIN
|
#ifdef MIN
|
||||||
#undef MIN
|
#undef MIN
|
||||||
|
|
Loading…
Reference in a new issue