Last active
December 29, 2015 10:39
-
-
Save fukuroder/7658921 to your computer and use it in GitHub Desktop.
wave file player (ASIO)
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
/* | |
* wave_play_asio.cpp | |
* | |
* Created by fukuroda (https://github.com/fukuroder) | |
*/ | |
// ASIO -> http://www.steinberg.net/en/company/developer.html | |
#include "asiosys.h" | |
#include "asio.h" | |
#include "asiodrivers.h" | |
// libsndfile -> http://www.mega-nerd.com/libsndfile/ | |
#include "sndfile.h" | |
#pragma comment(lib, "libsndfile-1.lib") | |
// STL | |
#include <iostream> | |
#include <array> | |
#include <vector> | |
// windows API | |
#include <windows.h> | |
// external references | |
extern AsioDrivers* asioDrivers; | |
// global variable | |
static SNDFILE* _snd_file; | |
static long _preferred_buffer_size; | |
static std::array<ASIOBufferInfo, 2> _buffer_infos; | |
static std::array<ASIOChannelInfo, 2> _channel_infos; | |
static std::vector<char> _read_buffer; | |
static HANDLE _stop_evt; | |
// function prototype | |
static void WritePCM_Int16LSB(long doubleBufferIndex, ASIOBool directProcess); | |
static void WritePCM_Int24LSB(long doubleBufferIndex, ASIOBool directProcess); | |
static void WritePCM_Int32LSB(long doubleBufferIndex, ASIOBool directProcess); | |
static void WritePCM_Float32LSB(long doubleBufferIndex, ASIOBool directProcess); | |
static void WritePCM_Float64LSB(long doubleBufferIndex, ASIOBool directProcess); | |
// entry | |
int main() | |
{ | |
try | |
{ | |
ASIOError asio_err = ASE_OK; | |
// open sound file (wav/ogg/flac...) | |
SF_INFO sf_info = {}; | |
_snd_file = sf_open("test.flac", SFM_READ, &sf_info); | |
if(_snd_file == nullptr) throw std::runtime_error("sf_open error"); | |
if(sf_info.frames == 0 || | |
sf_info.channels != 2 || | |
sf_info.samplerate != 44100) throw std::runtime_error("44.1kHz/stereo only"); | |
asioDrivers = new AsioDrivers(); | |
LONG num_asio_driver = asioDrivers->asioGetNumDev(); | |
if(num_asio_driver == 0) throw std::runtime_error("AsioDrivers:asioGetNumDev() is zero"); | |
char driver_name[64] = {}; | |
LONG l_result = asioDrivers->asioGetDriverName( | |
0, // first device | |
driver_name, | |
sizeof driver_name); | |
if(l_result != 0) throw std::runtime_error("AsioDrivers:asioGetDriverName() error"); | |
bool b_result = asioDrivers->loadDriver(driver_name); | |
if(b_result == false) throw std::runtime_error("AsioDrivers:loadDriver() error"); | |
ASIODriverInfo driver_info = {}; | |
asio_err = ASIOInit(&driver_info); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOInit() error"); | |
std::cout << "driver name:" << driver_info.name << std::endl; | |
long input_channels = 0L; | |
long output_channels = 0L; | |
asio_err = ASIOGetChannels(&input_channels, &output_channels); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOGetChannels() error"); | |
std::cout << "input channels:" << input_channels << std::endl; | |
std::cout << "output channels:" << output_channels << std::endl; | |
if(output_channels < 2) throw std::runtime_error("output_channels < 2"); | |
long input_latency; | |
long output_latency; | |
asio_err = ASIOGetLatencies(&input_latency, &output_latency); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOGetLatencies() error"); | |
std::cout << "input latency:" << input_latency << std::endl; | |
std::cout << "output latency:" << output_latency << std::endl; | |
long min_buffer_size = 0L; | |
long max_buffer_size = 0L; | |
long granularity = 0L; | |
asio_err = ASIOGetBufferSize( | |
&min_buffer_size, | |
&max_buffer_size, | |
&_preferred_buffer_size, | |
&granularity); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOGetBufferSize() error"); | |
std::cout << "buffer size:" << _preferred_buffer_size << std::endl; | |
ASIOSampleRate sample_rate = 0.0; | |
asio_err = ASIOGetSampleRate(&sample_rate); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOGetSampleRate() error"); | |
std::cout << "sample rate:" << sample_rate << std::endl; | |
if(sample_rate != 44100.0) throw std::runtime_error("sample_rate != 44100.0"); | |
for(auto ii = 0U; ii < _channel_infos.size(); ii++) | |
{ | |
_channel_infos[ii].channel = ii; | |
_channel_infos[ii].isInput = ASIOFalse; | |
asio_err = ASIOGetChannelInfo(&_channel_infos[ii]); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOGetChannelInfo() error"); | |
} | |
std::cout << "left channel name:" << _channel_infos[0].name << std::endl; | |
std::cout << "right channel name:" << _channel_infos[1].name << std::endl; | |
for(auto ii = 0U; ii < _buffer_infos.size(); ii++) | |
{ | |
_buffer_infos[ii].channelNum = ii; | |
_buffer_infos[ii].isInput = ASIOFalse; | |
} | |
ASIOCallbacks callbacks = {}; | |
if(_channel_infos[0].type == ASIOSTInt16LSB) | |
{ | |
// 16bit | |
std::cout << "sample type:ASIOSTInt16LSB" << std::endl; | |
_read_buffer.resize(2*16*_preferred_buffer_size); | |
callbacks.bufferSwitch = WritePCM_Int16LSB; | |
} | |
else if(_channel_infos[0].type == ASIOSTInt24LSB) | |
{ | |
// 24bit | |
std::cout << "sample type:ASIOSTInt24LSB" << std::endl; | |
_read_buffer.resize(2*32*_preferred_buffer_size); | |
callbacks.bufferSwitch = WritePCM_Int24LSB; | |
} | |
else if(_channel_infos[0].type == ASIOSTInt32LSB) | |
{ | |
// 32bit(lower 8 bits are zero) | |
std::cout << "sample type:ASIOSTInt32LSB" << std::endl; | |
_read_buffer.resize(2*32*_preferred_buffer_size); | |
callbacks.bufferSwitch = WritePCM_Int32LSB; | |
} | |
else if(_channel_infos[0].type == ASIOSTFloat32LSB) | |
{ | |
// 32bit single float | |
std::cout << "sample type:ASIOSTFloat32LSB" << std::endl; | |
_read_buffer.resize(2*32*_preferred_buffer_size); | |
callbacks.bufferSwitch = WritePCM_Float32LSB; | |
} | |
else if(_channel_infos[0].type == ASIOSTFloat64LSB) | |
{ | |
// 64bit double float | |
std::cout << "sample type:ASIOSTFloat64LSB" << std::endl; | |
_read_buffer.resize(2*64*_preferred_buffer_size); | |
callbacks.bufferSwitch = WritePCM_Float64LSB; | |
} | |
else | |
{ | |
throw std::runtime_error("sample type is not supported"); | |
} | |
asio_err = ASIOCreateBuffers( | |
_buffer_infos.data(), | |
_buffer_infos.size(), | |
_preferred_buffer_size, | |
&callbacks); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOCreateBuffers() error"); | |
// create stop event | |
_stop_evt = CreateEvent( | |
nullptr, | |
false, // auto reset | |
false, // non signaled state | |
nullptr); | |
asio_err = ASIOStart(); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOStart() error"); | |
// wait stop event | |
WaitForSingleObject(_stop_evt, INFINITE); | |
asio_err = ASIOStop(); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIOStop() error"); | |
asio_err = ASIODisposeBuffers(); | |
if(asio_err != ASE_OK) throw std::runtime_error("ASIODisposeBuffers() error"); | |
} | |
catch(std::exception& ex) | |
{ | |
std::cout << "error:" << ex.what() << std::endl; | |
} | |
ASIOExit(); | |
sf_close(_snd_file); | |
CloseHandle(_stop_evt); | |
return 0; | |
} | |
// 16bit | |
static void WritePCM_Int16LSB(long doubleBufferIndex, ASIOBool directProcess) | |
{ | |
short* write_ptr[] = { (short*)_buffer_infos[0].buffers[doubleBufferIndex], | |
(short*)_buffer_infos[1].buffers[doubleBufferIndex] }; | |
short* read_ptr = (short*)_read_buffer.data(); | |
sf_count_t read_count = sf_readf_short( | |
_snd_file, | |
read_ptr, | |
_preferred_buffer_size); | |
for (long ii = 0; ii < read_count; ii++) | |
{ | |
*write_ptr[0]++ = *read_ptr++; | |
*write_ptr[1]++ = *read_ptr++; | |
} | |
if (read_count < _preferred_buffer_size) | |
{ | |
SetEvent(_stop_evt); // stop | |
} | |
} | |
// 24bit | |
static void WritePCM_Int24LSB(long doubleBufferIndex, ASIOBool directProcess) | |
{ | |
char* write_ptr[] = { (char*)_buffer_infos[0].buffers[doubleBufferIndex], | |
(char*)_buffer_infos[1].buffers[doubleBufferIndex] }; | |
char* read_ptr = (char*)_read_buffer.data(); | |
sf_count_t read_count = sf_readf_int( | |
_snd_file, | |
(int*)read_ptr, | |
_preferred_buffer_size); | |
for (long ii = 0; ii < read_count; ii++) | |
{ | |
read_ptr++; | |
*write_ptr[0]++ = *read_ptr++; | |
*write_ptr[0]++ = *read_ptr++; | |
*write_ptr[0]++ = *read_ptr++; | |
read_ptr++; | |
*write_ptr[1]++ = *read_ptr++; | |
*write_ptr[1]++ = *read_ptr++; | |
*write_ptr[1]++ = *read_ptr++; | |
} | |
if (read_count < _preferred_buffer_size) | |
{ | |
SetEvent(_stop_evt); // stop | |
} | |
} | |
// 32bit(lower 8 bits are zero) | |
static void WritePCM_Int32LSB(long doubleBufferIndex, ASIOBool directProcess) | |
{ | |
int* write_ptr[] = { (int*)_buffer_infos[0].buffers[doubleBufferIndex], | |
(int*)_buffer_infos[1].buffers[doubleBufferIndex] }; | |
int* read_ptr = (int*)_read_buffer.data(); | |
sf_count_t read_count = sf_readf_int( | |
_snd_file, | |
read_ptr, | |
_preferred_buffer_size); | |
for (long ii = 0; ii < read_count; ii++) | |
{ | |
*write_ptr[0]++ = (*read_ptr++) & 0xFFFFFF00; | |
*write_ptr[1]++ = (*read_ptr++) & 0xFFFFFF00; | |
} | |
if (read_count < _preferred_buffer_size) | |
{ | |
SetEvent(_stop_evt); // stop | |
} | |
} | |
// 32bit single float | |
static void WritePCM_Float32LSB(long doubleBufferIndex, ASIOBool directProcess) | |
{ | |
float* write_ptr[] = { (float*)_buffer_infos[0].buffers[doubleBufferIndex], | |
(float*)_buffer_infos[1].buffers[doubleBufferIndex] }; | |
float* read_ptr = (float*)_read_buffer.data(); | |
sf_count_t read_count = sf_readf_float( | |
_snd_file, | |
read_ptr, | |
_preferred_buffer_size); | |
for (long ii = 0; ii < read_count; ii++) | |
{ | |
*write_ptr[0]++ = *read_ptr++; | |
*write_ptr[1]++ = *read_ptr++; | |
} | |
if (read_count < _preferred_buffer_size) | |
{ | |
SetEvent(_stop_evt); // stop | |
} | |
} | |
// 64bit double float | |
static void WritePCM_Float64LSB(long doubleBufferIndex, ASIOBool directProcess) | |
{ | |
double* write_ptr[] = { (double*)_buffer_infos[0].buffers[doubleBufferIndex], | |
(double*)_buffer_infos[1].buffers[doubleBufferIndex] }; | |
double* read_ptr = (double*)_read_buffer.data(); | |
sf_count_t read_count = sf_readf_double( | |
_snd_file, | |
read_ptr, | |
_preferred_buffer_size); | |
for (long ii = 0; ii < read_count; ii++) | |
{ | |
*write_ptr[0]++ = *read_ptr++; | |
*write_ptr[1]++ = *read_ptr++; | |
} | |
if (read_count < _preferred_buffer_size) | |
{ | |
SetEvent(_stop_evt); // stop | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment