他人の空似自作物置場

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;
}