Skip to content

Instantly share code, notes, and snippets.

@PeadarO
Forked from unvBell/Recorder.cpp
Created March 22, 2018 15:32
Show Gist options
  • Save PeadarO/ba4493d62cd5c8fd588bd466273b8415 to your computer and use it in GitHub Desktop.
Save PeadarO/ba4493d62cd5c8fd588bd466273b8415 to your computer and use it in GitHub Desktop.
#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();
}
#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