Skip to content

Instantly share code, notes, and snippets.

@fukuroder
Last active December 29, 2015 10:39
Show Gist options
  • Save fukuroder/7658921 to your computer and use it in GitHub Desktop.
Save fukuroder/7658921 to your computer and use it in GitHub Desktop.
wave file player (ASIO)
/*
* 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