#include "MIDIPlayer.h" #include "config.h" #include #include #include #include #include #include #include #include #include namespace bp = boost::process; namespace fs = std::filesystem; using namespace std::chrono_literals; namespace { std::unordered_set supported_devices{"AudioBox 22 VSL", "CH345", "M2"}; } MIDIPlayer::MIDIPlayer(Config& config): m_config(config), m_child{}, m_dir{m_config.get_file_path()}, m_file{} { std::vector list = get_filelist(); if (list.size() > 0) { m_file = list[0]; } init_seq(); iterate_ports(); close_seq(); } void MIDIPlayer::start() { if (m_child.valid() && m_child.running()) { stop(); } else { m_child = bp::child(fmt::format("/home/ernie/code/midiplay/aplaymidi-mp -c -p{} \"{}\"", m_client, m_file).c_str());//, bp::std_out > bp::null); if (!m_child.valid() || !m_child.running()) { throw std::runtime_error("aplaymidi not started"); } } } void MIDIPlayer::stop() { // note:: m_child.terminate() would kill via SIGKILL, preventing note offs if (m_child.valid()) { int result = kill(m_child.native_handle(), SIGTERM); if (result < 0) { std::cerr << "Error in MIDIPlayer::stop(): kill() unsuccessful\n"; } } } bool MIDIPlayer::is_playing() { if (!m_child.valid()) { return false; } return m_child.running(); } void MIDIPlayer::set_file(const std::string& filename) { m_file = filename; if (is_playing()) { stop(); std::this_thread::sleep_for(100ms); start(); } } std::string MIDIPlayer::get_file() { return m_file; } std::vector MIDIPlayer::get_filelist() { std::vector result; for (auto const& dir_entry: fs::directory_iterator{m_dir}) { fs::path entry{dir_entry.path()}; fs::path extension = entry.extension(); if (extension == ".midi" || extension == ".mid") { result.push_back(entry.filename()); } if (result.size() == 99) { break; } } std::sort(result.begin(), result.end()); 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; }