-
-
Save PeadarO/ba4493d62cd5c8fd588bd466273b8415 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include "Recorder.hpp" | |
namespace { | |
/** | |
* 処理に失敗していたら例外を投げる | |
* @param message エラーメッセージ | |
* @param result 処理の結果 | |
* @throws runtime_error 処理が失敗していた時 | |
*/ | |
void throwIfError(const char* message, MMRESULT result) { | |
if(result == MMSYSERR_NOERROR) { return; } | |
// エラーメッセージの取得 | |
LPSTR buffer; | |
FormatMessageA( | |
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | |
nullptr, | |
result, | |
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
reinterpret_cast<LPSTR>(&buffer), | |
0, | |
nullptr | |
); | |
auto str = std::string{ message }; | |
if(buffer) { | |
str = str + '\n' + buffer; | |
} | |
// バッファの解放 | |
LocalFree(buffer); | |
throw std::runtime_error{ str }; | |
} | |
} | |
// プロシージャ | |
DWORD CALLBACK Recorder::threadProc(LPVOID param) { | |
auto self = reinterpret_cast<Recorder*>(param); | |
MSG msg; | |
BOOL result; | |
do { | |
result = GetMessage(&msg, nullptr, 0, 0); | |
if(msg.message == WIM_DATA) { | |
try { | |
auto waveIn = reinterpret_cast<HWAVEIN>(msg.wParam); | |
auto hdr = reinterpret_cast<LPWAVEHDR>(msg.lParam); | |
throwIfError("waveInAddBuffer", waveInAddBuffer(waveIn, hdr, sizeof(WAVEHDR))); | |
// コールバックの呼び出し | |
self->callback_(self->buffer_.data() + self->currentBuffer_*self->bufferSize_, self->bufferSize_); | |
self->currentBuffer_ ^= 1; | |
} | |
catch(const std::exception& e) { | |
MessageBoxA(nullptr, e.what(), typeid(e).name(), MB_ICONERROR); | |
} | |
} | |
} while(msg.message != WIM_CLOSE && result != 0 && result != -1); | |
return 0; | |
} | |
// データの解放 | |
void Recorder::finalize() { | |
if(thread_) { return; } | |
CloseHandle(thread_); | |
if(!waveIn_) { return; } | |
waveInStop(waveIn_); | |
waveInUnprepareHeader(waveIn_, &headers_[1], sizeof(WAVEHDR)); | |
waveInUnprepareHeader(waveIn_, &headers_[0], sizeof(WAVEHDR)); | |
waveInClose(waveIn_); | |
} | |
// ctor | |
Recorder::Recorder(const Callback& callback, size_t bufferSize, int samples) | |
try | |
: waveIn_ (nullptr) | |
, currentBuffer_(0) | |
, bufferSize_ (bufferSize) | |
, callback_ (callback) | |
{ | |
buffer_.resize(bufferSize*2); | |
std::fill(buffer_.begin(), buffer_.end(), 0); | |
// フォーマットの設定 | |
const WAVEFORMATEX format = { | |
/*.wFormatTag =*/WAVE_FORMAT_PCM, | |
/*.nChannels =*/1, | |
/*.nSamplesPerSec =*/samples, | |
/*.nAvgBytesPerSec =*/samples*sizeof(int16_t), | |
/*.nBlockAlign =*/sizeof(int16_t), | |
/*.wBitsPerSample =*/16, | |
/*.cbSize =*/0, | |
}; | |
// スレッドの生成 | |
DWORD threadId; | |
thread_ = CreateThread(nullptr, 0, threadProc, this, 0, &threadId); | |
if(!thread_) { | |
throw std::runtime_error{ "CreateThread" }; | |
} | |
// 初期化 | |
throwIfError("waveInOpen", waveInOpen( | |
&waveIn_, | |
WAVE_MAPPER, | |
&format, | |
threadId, | |
reinterpret_cast<DWORD_PTR>(this), | |
CALLBACK_THREAD | |
)); | |
// ヘッダの初期化 | |
headers_[0].lpData = reinterpret_cast<LPSTR>(&buffer_[0] + 0); | |
headers_[0].dwBufferLength = bufferSize * sizeof(int16_t); | |
headers_[0].dwLoops = 1; | |
headers_[0].dwFlags = 0; | |
headers_[1].lpData = reinterpret_cast<LPSTR>(&buffer_[0] + bufferSize); | |
headers_[1].dwBufferLength = bufferSize * sizeof(int16_t); | |
headers_[1].dwLoops = 1; | |
headers_[1].dwFlags = 0; | |
for(auto&& header : headers_) { | |
throwIfError("waveInPrepareHeader", waveInPrepareHeader (waveIn_, &header, sizeof(WAVEHDR))); | |
throwIfError("waveInAddBuffer", waveInAddBuffer (waveIn_, &header, sizeof(WAVEHDR))); | |
} | |
// 録音開始 | |
throwIfError("waveInStart", waveInStart(waveIn_)); | |
} | |
catch(...) { | |
finalize(); | |
throw; | |
} | |
// dtor | |
Recorder::~Recorder() { | |
finalize(); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <array> | |
#include <functional> | |
#include <vector> | |
#include <cstdint> | |
#include <Windows.h> | |
/** | |
* 録音を行うクラス | |
*/ | |
class Recorder final { | |
using Callback = std::function<void(const int16_t* wave, size_t size)>; | |
HANDLE thread_; | |
HWAVEIN waveIn_; | |
std::vector<int16_t> buffer_; | |
std::array<WAVEHDR, 2> headers_; | |
int currentBuffer_; | |
size_t bufferSize_; | |
Callback callback_; | |
static DWORD CALLBACK threadProc(LPVOID param); | |
void finalize(); | |
public: | |
/** | |
* ctor | |
* @param callback 録音バッファがいっぱいになった時に呼ばれるコールバック | |
* @param bufferSize 録音バッファのサイズ | |
* @param samples サンプリング周波数 (default:44100) | |
* @throws runtime_error 初期化に失敗した時 | |
*/ | |
explicit Recorder(const Callback& callback, size_t bufferSize, int samples = 44100); | |
~Recorder(); | |
Recorder(const Recorder&) =delete; | |
Recorder& operator=(const Recorder&) =delete; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment