|
|
|
@@ -8,6 +8,7 @@
|
|
|
|
|
#include <format>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <thread>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <cstdio>
|
|
|
|
@@ -15,6 +16,9 @@
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <cerrno>
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <iterator>
|
|
|
|
|
|
|
|
|
|
// https://github.com/pulseaudio/pulseaudio/blob/master/src/pulse/simple.c
|
|
|
|
|
// https://habr.com/en/articles/663352/#linux-and-pulseaudio
|
|
|
|
@@ -435,7 +439,6 @@ int pa_system_drain(pa_system* p, int *rerror)
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int pa_system_flush(pa_system* p, int *rerror)
|
|
|
|
|
{
|
|
|
|
|
pa_operation *o = NULL;
|
|
|
|
@@ -522,12 +525,84 @@ static void list_server_info(pa_context* c, pa_server_info* i, void* userdata)
|
|
|
|
|
auto *p = static_cast<pa_system *>(userdata);
|
|
|
|
|
pa_threaded_mainloop_signal(p->mainloop, 0);
|
|
|
|
|
}
|
|
|
|
|
static void stream_success_cb(pa_stream* s, int success, void* userdata)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int mix_sample_s16_pcm(int16_t a, int16_t b)
|
|
|
|
|
{
|
|
|
|
|
auto *p = static_cast<pa_system *>(userdata);
|
|
|
|
|
pa_threaded_mainloop_signal(p->mainloop, 0);
|
|
|
|
|
|
|
|
|
|
return (a + b) / 2.f;
|
|
|
|
|
//int m;
|
|
|
|
|
|
|
|
|
|
//a += 32768;
|
|
|
|
|
//b += 32768;
|
|
|
|
|
|
|
|
|
|
//if ((a < 32768) || (b < 32768)) {
|
|
|
|
|
// Viktor's first equation when both sources are quiet.
|
|
|
|
|
// (i.e. less than middle of the dynamic range)
|
|
|
|
|
// m = a * b / 32768;
|
|
|
|
|
//} else {
|
|
|
|
|
// Viktor's second equation when one or both sources are loud.
|
|
|
|
|
// m = 2 * (a + b) - (a * b) / 32768 - 65535;
|
|
|
|
|
//}
|
|
|
|
|
// Output is unsigned (0..65536) so convert back to signed (-3
|
|
|
|
|
//if (m == 65536) m = 65535;
|
|
|
|
|
//m -= 32768;
|
|
|
|
|
|
|
|
|
|
//return m;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// this dick
|
|
|
|
|
|
|
|
|
|
struct pcm_s16_data
|
|
|
|
|
{
|
|
|
|
|
std::vector<char> buffer;
|
|
|
|
|
explicit pcm_s16_data(const std::filesystem::path& file_path)
|
|
|
|
|
{
|
|
|
|
|
std::ifstream file(file_path, std::ios::binary | std::ios::ate);
|
|
|
|
|
file.unsetf(std::ios::skipws);
|
|
|
|
|
|
|
|
|
|
file.seekg(0, std::ios::end);
|
|
|
|
|
std::streamsize size = file.tellg();
|
|
|
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
|
|
|
|
|
|
buffer.reserve(size);
|
|
|
|
|
|
|
|
|
|
buffer.insert(buffer.begin(),
|
|
|
|
|
std::istream_iterator<char>(file),
|
|
|
|
|
std::istream_iterator<char>());
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
explicit pcm_s16_data(const std::vector<char>& pcm_buf)
|
|
|
|
|
{
|
|
|
|
|
buffer = pcm_buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pcm_s16_data operator + (const pcm_s16_data& rhs) const
|
|
|
|
|
{
|
|
|
|
|
std::vector<char> sum;
|
|
|
|
|
auto rhs_buf = rhs.buffer;
|
|
|
|
|
auto lhs_buf = this->buffer;
|
|
|
|
|
for (int i = 0; i < std::max(lhs_buf.size(), rhs_buf.size()); i++)
|
|
|
|
|
{
|
|
|
|
|
int a = 0;
|
|
|
|
|
if (i >= lhs_buf.size() && i < rhs_buf.size())
|
|
|
|
|
{
|
|
|
|
|
sum.push_back(rhs_buf[i]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (i >= rhs_buf.size() && i < lhs_buf.size())
|
|
|
|
|
{
|
|
|
|
|
sum.push_back(lhs_buf[i]);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sum.push_back(mix_sample_s16_pcm(lhs_buf[i], rhs_buf[i]));
|
|
|
|
|
}
|
|
|
|
|
return pcm_s16_data(sum);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
|
|
|
|
if (argc == 1 || argv[1] == "-h" || argv[1] == "help")
|
|
|
|
|
{
|
|
|
|
@@ -549,7 +624,20 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
/* Replace STDIN with the specified file if needed. */
|
|
|
|
|
if (argc > 1) {
|
|
|
|
|
|
|
|
|
|
if (argc < 3)
|
|
|
|
|
{
|
|
|
|
|
std::cerr << "Please provide two input PCM files!" << std::endl;
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pcm_s16_data song = pcm_s16_data(argv[1]);
|
|
|
|
|
pcm_s16_data sfx = pcm_s16_data(argv[2]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pcm_s16_data combined = song + sfx;
|
|
|
|
|
|
|
|
|
|
/*if (argc > 1) {
|
|
|
|
|
int fd;
|
|
|
|
|
if ((fd = open(argv[1], O_RDONLY)) < 0) {
|
|
|
|
|
fprintf(stderr, __FILE__": open() failed: %s\n", strerror(errno));
|
|
|
|
@@ -561,12 +649,13 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
close(fd);
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
/* Create a new playback stream */
|
|
|
|
|
if (!(s = pa_system_new(NULL, argv[0], PA_STREAM_PLAYBACK, NULL, "playback", &ss, NULL, NULL, &error))) {
|
|
|
|
|
fprintf(stderr, __FILE__": pa_simple_new() failed: %s\n", pa_strerror(error));
|
|
|
|
|
goto finish;
|
|
|
|
|
return -1;
|
|
|
|
|
//goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pa_context_get_server_info(s->context, reinterpret_cast<pa_server_info_cb_t>(list_server_info), s);
|
|
|
|
@@ -575,7 +664,12 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
|
|
|
|
|
pa_system_enumerate_devices(s);
|
|
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
|
if (pa_system_write(s, combined.buffer.data(), combined.buffer.size(), &error) < 0) {
|
|
|
|
|
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* for (;;) {
|
|
|
|
|
uint8_t buf[BUFSIZE];
|
|
|
|
|
ssize_t r;
|
|
|
|
|
|
|
|
|
@@ -589,32 +683,31 @@ int main(int argc, char* argv[]) {
|
|
|
|
|
}
|
|
|
|
|
fprintf(stderr, "%0.0f usec \r", (float)latency);
|
|
|
|
|
#endif
|
|
|
|
|
/* Read some data */
|
|
|
|
|
|
|
|
|
|
if ((r = read(STDIN_FILENO, buf, sizeof(buf))) <= 0) {
|
|
|
|
|
if (r == 0) /* EOF */
|
|
|
|
|
if (r == 0)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, __FILE__": read() failed: %s\n", strerror(errno));
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* and play it */
|
|
|
|
|
|
|
|
|
|
if (pa_system_write(s, buf, (size_t)r, &error) < 0) {
|
|
|
|
|
fprintf(stderr, __FILE__": pa_simple_write() failed: %s\n", pa_strerror(error));
|
|
|
|
|
goto finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(1s);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pa_stream_cork(s->stream, 1, stream_success_cb, s);
|
|
|
|
|
pa_stream_cork(s->stream, 1, success_cb, s);
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(1s);
|
|
|
|
|
|
|
|
|
|
pa_stream_cork(s->stream, 0, stream_success_cb, s);
|
|
|
|
|
pa_stream_cork(s->stream, 0, success_cb, s);
|
|
|
|
|
/* Make sure that every single sample was played */
|
|
|
|
|
if (pa_system_drain(s, &error) < 0) {
|
|
|
|
|
fprintf(stderr, __FILE__": pa_simple_drain() failed: %s\n", pa_strerror(error));
|
|
|
|
|