Created
September 1, 2016 10:47
-
-
Save taotao54321/18d213dd72528417da866c0b26c6291d 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
/** | |
* 正弦波を出力する。引数でバッファサイズを指定 | |
* (※SDLのドキュメントでは AudioSpec::samples はサンプル数って書いて | |
* るけど、実際はバッファサイズだと思う) | |
* | |
* メインスレッドで音を溜めてコールバックで出力する方式 | |
* スレッド間のデータのやりとりに boost::lockfree::spsc_queue を使用 | |
*/ | |
#include <string> | |
#include <array> | |
#include <algorithm> | |
#include <stdexcept> | |
#include <cstdio> | |
#include <cstdlib> | |
#include <cmath> | |
#include <cstdint> | |
#include <cassert> | |
#include <boost/lockfree/spsc_queue.hpp> | |
#include <SDL.h> | |
using namespace std; | |
namespace{ | |
constexpr int FPS = 60; | |
constexpr int SPEC_FREQ = 44100; | |
constexpr SDL_AudioFormat SPEC_FORMAT = AUDIO_S16SYS; | |
constexpr int SPEC_CHANNELS = 1; | |
constexpr int SIN_AMP = 16000; | |
constexpr int SIN_FREQ = 440; | |
bool is_pow2(unsigned int n) | |
{ | |
if(n == 0) return false; | |
return (n & (n-1)) == 0; | |
} | |
void warn(const char* msg) | |
{ | |
fputs(msg, stderr); | |
putc('\n', stderr); | |
} | |
void error(const char* msg) | |
{ | |
warn(msg); | |
exit(1); | |
} | |
void print_spec(const SDL_AudioSpec& spec, bool obtained) | |
{ | |
printf("freq : %d\n", spec.freq); | |
printf("format : %d\n", spec.format); | |
printf("channels : %u\n", spec.channels); | |
printf("samples : %u\n", spec.samples); | |
if(obtained){ | |
printf("silence : %u\n", spec.silence); | |
printf("size : %u\n", spec.size); | |
} | |
} | |
boost::lockfree::spsc_queue< | |
int16_t, | |
boost::lockfree::capacity<8*(SPEC_FREQ/FPS)> // 多めに取らないとunderflow発生 | |
> audio_queue; | |
void callback(void* /*userdata*/, uint8_t* stream, int len) | |
{ | |
assert(!(len&1)); | |
//printf("%d\n", len); | |
unsigned int n_sample = len / 2; | |
int16_t* p = reinterpret_cast<int16_t*>(stream); | |
size_t n_pop = audio_queue.pop(p, n_sample); | |
if(n_pop < n_sample){ | |
printf("UNDERFLOW: %lu samples\n", n_sample - n_pop); | |
fill_n(p+n_pop, 0, n_sample-n_pop); | |
} | |
} | |
void usage() | |
{ | |
error("audio2 <samples>"); | |
} | |
} | |
int main(int argc, char** argv) | |
{ | |
if(argc != 2) usage(); | |
unsigned long samples; | |
try{ | |
samples = stoul(argv[1]); | |
} | |
catch(const logic_error& e){ | |
error("invalid samples value"); | |
} | |
if(samples > 0x8000) error("samples too large"); | |
if(samples < 64) error("samples too small"); // 小さすぎるとハングするっぽい | |
if(!is_pow2(samples)) error("samples must be power of 2"); | |
if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER) != 0) error("SDL_Init() failed"); | |
atexit(SDL_Quit); | |
{ | |
const char* driver = SDL_GetCurrentAudioDriver(); | |
printf("Audio driver: %s\n", driver ? driver : "(null!?)"); | |
puts(""); | |
} | |
SDL_AudioSpec want = {}; // これで警告出るの納得いかない… | |
want.freq = SPEC_FREQ; | |
want.format = SPEC_FORMAT; | |
want.channels = SPEC_CHANNELS; | |
want.samples = samples; | |
want.callback = callback; | |
puts("[Desired spec]"); | |
print_spec(want, false); | |
puts(""); | |
SDL_AudioSpec have; | |
SDL_AudioDeviceID dev = SDL_OpenAudioDevice(nullptr, 0, &want, &have, 0); | |
if(!dev) error("SDL_OpenAudioDevice() failed"); | |
// samples は指定通りになるとは限らない | |
if(want.freq != have.freq || | |
want.format != have.format || | |
want.channels != have.channels) error("spec changed"); | |
puts("[Obtained spec]"); | |
print_spec(have, true); | |
puts(""); | |
SDL_PauseAudioDevice(dev, 0); | |
unsigned int t = 0; | |
array<int16_t, SPEC_FREQ/FPS> buf; | |
for(;;){ | |
// write_available() はバージョンが古いと使えない? | |
//size_t size = min(buf.size(), audio_queue.write_available()); | |
unsigned int n_written = 0; | |
while(n_written < buf.size()){ | |
int16_t sample = SIN_AMP * sin(2*M_PI * SIN_FREQ/SPEC_FREQ * t); | |
if(audio_queue.push(sample)){ | |
++n_written; | |
++t; | |
} | |
else{ | |
printf("OVERFLOW: %lu samples\n", buf.size() - n_written); | |
break; | |
} | |
} | |
// 書き込みが追いつかないとunderflowして音がブツ切れになるので | |
// ウェイトは少し減らす。overflowする分には問題ない | |
SDL_Delay(1000/FPS - 5); | |
} | |
SDL_CloseAudioDevice(dev); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment