midi.zip/player.cpp
#include <string>
#include <iostream>
#include <memory>
#include <algorithm>
#include <boost/program_options.hpp>
#include <boost/thread.hpp>
#include <Windows.h>
#include "volume_manager.h"
#include "midi.h"
template<class InputIterator, class UnaryPredicate>
InputIterator find_if(InputIterator first, InputIterator last, UnaryPredicate pred) {
while (first != last) {
if (pred(*first)) return first;
++first;
}
return last;
}
void SetVolume(const unsigned int value) {
MMDevice device = MMDeviceEnumerator().GetDefaultAudioEndpoint(EDataFlow::eRender, ERole::eMultimedia);
AudioSessionManager manager = device.ActivateSessionManager();
const DWORD currentProcessId = ::GetCurrentProcessId();
while (true) {
AudioSessionEnumerator sessionEnumerator = manager.GetSessionEnumerator();
auto sessionIt = find_if(
sessionEnumerator.begin(), sessionEnumerator.end(),
[currentProcessId](AudioSessionControl &item) {
return currentProcessId == item.GetProcessId();
});
if (sessionIt == sessionEnumerator.end()) {
::Sleep(10);
continue;
}
(*sessionIt).SetVolume(static_cast<float>(value) / 100);
break;
}
}
int main(const int argc, const char * const * const argv) {
// タイミング不明でsignalを握りつぶすMIDIドライバー対策
boost::thread signalThread([]() {
const auto handler = [](DWORD) -> BOOL {::ExitProcess(0);return true;};
while (true) {
::SetConsoleCtrlHandler(nullptr, false);
::SetConsoleCtrlHandler(handler, false);
::SetConsoleCtrlHandler(handler, true);
::Sleep(50);
}
});
signalThread.detach();
SetAppDir();
boost::program_options::variables_map optList;
boost::program_options::options_description opt("オプション");
{
using boost::program_options::value;
opt.add_options()
("help,h", "ヘルプを表示")
("show-device", "使用できるMIDIデバイスを表示する")
("device,d", value<int>()->default_value(-1), "使用するMIDIデバイスを指定する、-1で自動")
("loop,l", value<unsigned int>()->default_value(1), "ループ再生回数、0で無限ループ")
("volume,v", value<unsigned int>()->default_value(100), "音量を設定(0〜100)")
("speed,s", value<double>()->default_value(1.0), "再生速度を設定")
;
}
const auto parseResult = boost::program_options::command_line_parser(argc, argv).options(opt).allow_unregistered().run();
try {
boost::program_options::store(parseResult, optList);
} catch (const boost::program_options::validation_error &error) {
std::cout << "InvalidOption: " << error.what() << std::endl << opt << std::endl;
return 1;
}
boost::program_options::notify(optList);
const std::vector<std::string> fileList = boost::program_options::collect_unrecognized(parseResult.options, boost::program_options::include_positional);
if (optList.count("help") != 0) {
std::cout << opt << std::endl;
return 0;
}
if (optList.count("show-device") != 0) {
::showMidiDevice();
return 0;
}
if (fileList.empty()) {
std::cout << opt << std::endl;
return 1;
}
if (isMidiDeviceNone()) {
std::cout << "MIDIポートが存在しないのでMIDI再生は行いません\n";
std::cout << "全ての処理が完了しました、終了する場合はENTERを押してください\n";
std::cin.get();
return 0;
}
const int deviceId = optList["device"].as<int>();
const unsigned int loop = optList["loop"].as<unsigned int>();
const unsigned int volume = optList["volume"].as<unsigned int>();
const double speed = optList["speed"].as<double>();
boost::thread setVolumeThread([volume](){SetVolume(volume);});
for (unsigned int i = 0; loop == 0 || i < loop; i++) {
for (const std::string &path : fileList) {
const std::unique_ptr<Midi> midi = ::loadmidi(path.c_str());
::MidiPlay(*midi, 0, deviceId, 100, speed);
}
}
setVolumeThread.interrupt();
std::cout << "\n全ての処理が完了しました、終了する場合はENTERを押してください\n";
std::cin.get();
return 0;
}