diff options
-rw-r--r-- | MIDIPlayer.cpp | 80 | ||||
-rw-r--r-- | MIDIPlayer.h | 10 | ||||
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rw-r--r-- | midiplay.cpp | 81 |
5 files changed, 134 insertions, 41 deletions
diff --git a/MIDIPlayer.cpp b/MIDIPlayer.cpp index f2bba8c..2c0bc6a 100644 --- a/MIDIPlayer.cpp +++ b/MIDIPlayer.cpp @@ -6,13 +6,19 @@ #include <algorithm> #include <chrono> #include <iostream> +#include <string> #include <thread> +#include <unordered_set> namespace bp = boost::process; namespace fs = std::filesystem; using namespace std::chrono_literals; +namespace { + std::unordered_set<std::string> supported_devices{"AudioBox 22 VSL", "CH345"}; +} + MIDIPlayer::MIDIPlayer(const std::filesystem::path& path): m_child{}, m_dir{path}, @@ -23,6 +29,10 @@ MIDIPlayer::MIDIPlayer(const std::filesystem::path& path): if (list.size() > 0) { m_file = list[0]; } + + init_seq(); + iterate_ports(); + close_seq(); } void MIDIPlayer::start() @@ -30,7 +40,7 @@ void MIDIPlayer::start() if (m_child.valid() && m_child.running()) { stop(); } else { - m_child = bp::child(fmt::format("aplaymidi -p24 \"{}\"", m_file).c_str());//, bp::std_out > bp::null); + m_child = bp::child(fmt::format("aplaymidi -p{} \"{}\"", m_client, m_file).c_str());//, bp::std_out > bp::null); } } @@ -87,3 +97,71 @@ std::vector<std::string> MIDIPlayer::get_filelist() return result; } +void MIDIPlayer::init_seq(void) +{ + /* open sequencer */ + int err = snd_seq_open(&m_seq, "default", SND_SEQ_OPEN_DUPLEX, 0); + if (err < 0) + { + throw std::runtime_error("snd_seq_open()"); + } +} + +void MIDIPlayer::close_seq(void) +{ + int err = snd_seq_close(m_seq); + if (err < 0) + { + throw std::runtime_error("snd_seq_close()"); + } +} + +void MIDIPlayer::iterate_ports(void) +{ + bool found{}; + + snd_seq_client_info_t *cinfo; + snd_seq_port_info_t *pinfo; + + snd_seq_client_info_alloca(&cinfo); + snd_seq_port_info_alloca(&pinfo); + + snd_seq_client_info_set_client(cinfo, -1); + while (snd_seq_query_next_client(m_seq, cinfo) >= 0) { + int client = snd_seq_client_info_get_client(cinfo); + + snd_seq_port_info_set_client(pinfo, client); + snd_seq_port_info_set_port(pinfo, -1); + while (snd_seq_query_next_port(m_seq, pinfo) >= 0) { + /* port must understand MIDI messages */ + if (!(snd_seq_port_info_get_type(pinfo) + & SND_SEQ_PORT_TYPE_MIDI_GENERIC)) + continue; + /* we need both WRITE and SUBS_WRITE */ + if ((snd_seq_port_info_get_capability(pinfo) + & (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + != (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE)) + continue; + if (supported_devices.contains(std::string{snd_seq_client_info_get_name(cinfo)})) { + m_client = snd_seq_port_info_get_client(pinfo); + found = true; + } + //std::cout << fmt::format("DEBUG: {}", get_midi_port()) << std::endl; + //printf("%3d:%-3d %-32.32s %s\n", + // snd_seq_port_info_get_client(pinfo), + // snd_seq_port_info_get_port(pinfo), + // snd_seq_client_info_get_name(cinfo), + // snd_seq_port_info_get_name(pinfo)); + } + } + + if (!found) { + throw std::runtime_error("No MIDI device found"); + } +} + +int MIDIPlayer::get_midi_port() +{ + return m_client; +} + diff --git a/MIDIPlayer.h b/MIDIPlayer.h index e104321..811bea4 100644 --- a/MIDIPlayer.h +++ b/MIDIPlayer.h @@ -4,6 +4,7 @@ #include <vector> #include <boost/process.hpp> +#include <alsa/asoundlib.h> #include <filesystem> @@ -24,9 +25,18 @@ public: std::vector<std::string> get_filelist(); + int get_midi_port(); + private: + void init_seq(void); + void close_seq(void); + void iterate_ports(void); + boost::process::child m_child; std::filesystem::path m_dir; std::string m_file; + + snd_seq_t* m_seq; + int m_client; }; @@ -8,7 +8,7 @@ OBJS=$(SRCS:.cpp=.o) CXX=clang++ CXXFLAGS=-Wall -g -O2 -fPIC -std=c++20 -CXXLIBS=-lfcgi -lreichwein -lfmt +CXXLIBS=-lfcgi -lreichwein -lfmt -lasound all: $(TARGET) diff --git a/debian/control b/debian/control index 14ff375..7f46012 100644 --- a/debian/control +++ b/debian/control @@ -1,2 +1,2 @@ -Build-Depends: libfmt-dev, libboost-dev, libfcgi-dev, libreichwein-dev +Build-Depends: libfmt-dev, libboost-dev, libfcgi-dev, libreichwein-dev, libasound2-dev Depends: nginx, alsa-utils, spawn-fcgi diff --git a/midiplay.cpp b/midiplay.cpp index 34879ee..bc1ad08 100644 --- a/midiplay.cpp +++ b/midiplay.cpp @@ -2,6 +2,7 @@ #include <stdexcept> #include <string> +#include <iostream> #include <fcgiapp.h> @@ -79,53 +80,57 @@ std::string to_xml(const std::vector<std::string>& filelist, const std::string& } // namespace int main(int argc, char* argv[]) { - MIDIPlayer player; + try { + MIDIPlayer player; - std::string ok_data{"<data><status>ok</status><message>OK</message></data>"}; - std::string error_data{"<data><status>error</status><message>General Error</message></data>"}; + std::string ok_data{"<data><status>ok</status><message>OK</message></data>"}; + std::string error_data{"<data><status>error</status><message>General Error</message></data>"}; - int result = FCGX_Init(); - if (result != 0) { - return 1; // error on init - } - - FCGX_Request request; - - if (FCGX_InitRequest(&request, 0, 0) != 0) { - return 1; // error on init - } + int result = FCGX_Init(); + if (result != 0) { + return 1; // error on init + } - while (FCGX_Accept_r(&request) == 0) { - std::string method = FCGX_GetParam("REQUEST_METHOD", request.envp); + FCGX_Request request; - try { - if (method == "POST") { - FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); + if (FCGX_InitRequest(&request, 0, 0) != 0) { + return 1; // error on init + } - PostData data{request}; - std::string command {getCommand(request)}; - if (command == "start") { - player.start(); - FCGX_PutS(ok_data.c_str(), request.out); - } else if (command == "stop") { - player.stop(); - FCGX_PutS(ok_data.c_str(), request.out); - } else if (command == "getlist") { - FCGX_PutS(to_xml(player.get_filelist(), player.get_file()).c_str(), request.out); - } else if (command == "setfile") { - std::string filename = data.getXMLElement("data.value"); - player.set_file(filename); - FCGX_PutS(ok_data.c_str(), request.out); + while (FCGX_Accept_r(&request) == 0) { + std::string method = FCGX_GetParam("REQUEST_METHOD", request.envp); + + try { + if (method == "POST") { + FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); + + PostData data{request}; + std::string command {getCommand(request)}; + if (command == "start") { + player.start(); + FCGX_PutS(ok_data.c_str(), request.out); + } else if (command == "stop") { + player.stop(); + FCGX_PutS(ok_data.c_str(), request.out); + } else if (command == "getlist") { + FCGX_PutS(to_xml(player.get_filelist(), player.get_file()).c_str(), request.out); + } else if (command == "setfile") { + std::string filename = data.getXMLElement("data.value"); + player.set_file(filename); + FCGX_PutS(ok_data.c_str(), request.out); + } else { + FCGX_PutS(error_data.c_str(), request.out); + } } else { - FCGX_PutS(error_data.c_str(), request.out); + throw std::runtime_error(fmt::format("Bad request method: POST expected, got {}", method).c_str()); } - } else { - throw std::runtime_error(fmt::format("Bad request method: POST expected, got {}", method).c_str()); + } catch (const std::exception& ex) { + FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); + FCGX_PutS(("<data><status>error</status><message>"s + ex.what() + "</message></data>").c_str(), request.out); } - } catch (const std::exception& ex) { - FCGX_PutS("Content-Type: text/xml\r\n\r\n", request.out); - FCGX_PutS(("<data><status>error</status><message>"s + ex.what() + "</message></data>").c_str(), request.out); } + } catch (const std::exception& ex) { + std::cerr << "Error: " << ex.what() << std::endl; } return 0; |