From 21e4b2864c38be2ef383804ea9245758bc09eacb Mon Sep 17 00:00:00 2001 From: "Robin E. R. Davies" Date: Mon, 12 May 2025 11:22:03 -0400 Subject: [PATCH] Aux input test code. --- src/AuxIn.cpp | 188 +++++++++++++++++++++++++++++++++++++++++++++ src/AuxIn.hpp | 38 +++++++++ src/AuxInTest.cpp | 28 +++++++ src/CMakeLists.txt | 5 ++ todo.txt | 2 +- 5 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 src/AuxIn.cpp create mode 100644 src/AuxIn.hpp create mode 100644 src/AuxInTest.cpp diff --git a/src/AuxIn.cpp b/src/AuxIn.cpp new file mode 100644 index 0000000..056fcb4 --- /dev/null +++ b/src/AuxIn.cpp @@ -0,0 +1,188 @@ +#include "AuxIn.hpp" +#include +#include + +#include "ss.hpp" +#include "Finally.hpp" +#include +#include +#include +#include +#include + +#include +#include + +namespace fs = std::filesystem; + +namespace pipedal +{ + + class AuxInAlsaDeviceImpl : public AuxInAlsaDevice + { + public: + AuxInAlsaDeviceImpl(const std::filesystem::path &path); + virtual ~AuxInAlsaDeviceImpl() override; + void Open(const fs::path &path); + void Close(); + + private: + fs::path path; + void ThreadProc(); + + std::unique_ptr thread; + int fd = -1; + int cancelWrite = -1; + int cancelRead = -1; + size_t total_read = 0; + }; + + AuxInAlsaDevice::ptr AuxInAlsaDevice::Create(const std::filesystem::path &path) + { + return std::make_shared(path); + } + +} + +using namespace pipedal; + +AuxInAlsaDeviceImpl::AuxInAlsaDeviceImpl(const fs::path &path) +{ + this->path = path; + Open(path); + + this->thread = std::make_unique([this]() + { ThreadProc(); }); +} + +void AuxInAlsaDeviceImpl::Close() +{ + if (thread) + { + char buff[1]; + int nWritten = write(cancelWrite, buff, 1); + (void)nWritten; // ignore return. + thread->join(); + thread = nullptr; + } + if (cancelWrite != -1) + { + close(cancelWrite); + cancelWrite = 1; + } + if (cancelRead != -1) + { + close(cancelRead); + cancelRead = -1; + } + if (fd != -1) + { + close(fd); + fd = -1; + } +} + +AuxInAlsaDeviceImpl::~AuxInAlsaDeviceImpl() +{ + Close(); +} + +AuxInAlsaDevice::AuxInAlsaDevice() +{ +} +AuxInAlsaDevice::~AuxInAlsaDevice() +{ +} + +static void setNonBlocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} +static void setBlocking(int fd) +{ + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags & (~O_NONBLOCK)); +} +void AuxInAlsaDeviceImpl::Open(const fs::path &path) +{ + total_read = 0; + + fd = open(path.c_str(), O_RDONLY | O_NONBLOCK); // non-blocking so we can open it + if (fd == -1) + { + throw std::runtime_error(SS("Can't open file " << path)); + } + setBlocking(fd); // but use blocking semantics when reading. + int cancel_pipe[2]; + // create a socket pair to use when cancelling. + if (pipe(cancel_pipe) == -1) + { + throw std::runtime_error("Can't create pipe."); + } + this->cancelWrite = cancel_pipe[1]; + this->cancelRead = cancel_pipe[0]; + setNonBlocking(cancelRead); +} + +void AuxInAlsaDeviceImpl::ThreadProc() +{ + constexpr size_t BUFFER_SIZE = 65536; + ssize_t bytes_read; + int16_t samples[BUFFER_SIZE]; + + while (1) + { + // Read audio data from the FIFO + struct pollfd poll_fds[2]; + poll_fds[0].fd = fd; + poll_fds[0].events = POLLIN; + poll_fds[1].fd = cancelRead; + poll_fds[1].events = POLLIN; + + int rc = poll(poll_fds, 2, -1); + if (rc < 00) + { + throw std::runtime_error("select failed."); + } + if (poll_fds[1].revents) + { + break; + } + if (poll_fds[0].revents) + { + ssize_t bytes_read = read(fd, &samples, sizeof(samples)); + if (bytes_read < 0) + { + throw std::runtime_error("Auxin read failed."); + } + if (poll_fds[0].revents & POLLHUP) + { + std::cout << " End of stream. " << total_read << std::endl; + close(fd); + fd = -1; + // reopen the fifo. + total_read = 0; + fd = open(path.c_str(), O_RDONLY | O_NONBLOCK); + if (fd == -1) + { + throw std::runtime_error(SS("Can't open file " << path)); + } + setBlocking(fd); + } + else if (bytes_read > 0) + { + total_read += bytes_read; + std::cout << " Read " << bytes_read << std::endl; + sleep(1); + // Process the audio data, mix it with your main input + // Your mixing code here... + + // For example, if main_buffer has your main input: + // for (int i = 0; i < bytes_read/sizeof(int16_t); i++) { + // mixed_buffer[i] = main_buffer[i] + buffer[i]; + // } + } + } + } +} diff --git a/src/AuxIn.hpp b/src/AuxIn.hpp new file mode 100644 index 0000000..3600af6 --- /dev/null +++ b/src/AuxIn.hpp @@ -0,0 +1,38 @@ +// Copyright (c) 2025 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +#pragma once + +#include +#include + +namespace pipedal { + + class AuxInAlsaDevice { + protected: + AuxInAlsaDevice(); + virtual ~AuxInAlsaDevice(); + public: + using self=AuxInAlsaDevice; + using ptr = std::shared_ptr; + + + static ptr Create(const std::filesystem::path&path); + }; +} diff --git a/src/AuxInTest.cpp b/src/AuxInTest.cpp new file mode 100644 index 0000000..3fbfd76 --- /dev/null +++ b/src/AuxInTest.cpp @@ -0,0 +1,28 @@ +// Copyright (c) 2025 Robin Davies +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +#include "AuxIn.hpp" +using namespace pipedal; + +int main(int argc, char**argv) { + AuxInAlsaDevice::ptr auxIn = AuxInAlsaDevice::Create("/var/pipedal/pipedal_aux_input_fifo"); + + sleep(10000); +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index de7b3ba..30878ff 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -376,6 +376,11 @@ target_link_libraries(hotspotManagerTest PRIVATE ${PIPEDAL_LIBS}) set_target_properties(hotspotManagerTest PROPERTIES EXCLUDE_FROM_ALL true) +add_executable(AuxInTest + AuxIn.hpp + AuxIn.cpp + AuxInTest.cpp +) add_executable(pipedaltest testMain.cpp diff --git a/todo.txt b/todo.txt index 0df47b0..1bf8abf 100644 --- a/todo.txt +++ b/todo.txt @@ -11,4 +11,4 @@ pcm.pipedal_aux_in { } } -Move to /usr/local because...? +Move to /usr/local because...?q