diff --git a/main.cpp b/main.cpp index 39cdbd7..511e0b8 100644 --- a/main.cpp +++ b/main.cpp @@ -1,300 +1,299 @@ #include #include #include #include #include #include #include #include #include #include constexpr double PI = 3.14159265358979846264; template class SoundSynth { public: SoundSynth() { for(int i=0; i{0, 1}); // a alphabet.emplace_back(std::initializer_list{1, 0, 0, 0}); // b alphabet.emplace_back(std::initializer_list{1, 0, 1, 0}); // c alphabet.emplace_back(std::initializer_list{1, 0, 0}); // d alphabet.emplace_back(std::initializer_list{0}); // e alphabet.emplace_back(std::initializer_list{0, 0, 1, 0}); // f alphabet.emplace_back(std::initializer_list{1, 1, 0}); // g alphabet.emplace_back(std::initializer_list{0, 0, 0, 0}); // h alphabet.emplace_back(std::initializer_list{0, 0}); // i alphabet.emplace_back(std::initializer_list{0, 1, 1, 1}); // j alphabet.emplace_back(std::initializer_list{1, 0, 1}); // k alphabet.emplace_back(std::initializer_list{0, 1, 0, 0}); // l alphabet.emplace_back(std::initializer_list{1, 1}); // m alphabet.emplace_back(std::initializer_list{1, 0}); // n alphabet.emplace_back(std::initializer_list{1, 1, 1}); // o alphabet.emplace_back(std::initializer_list{0, 1, 1, 0}); // p alphabet.emplace_back(std::initializer_list{1, 1, 0, 1}); // q alphabet.emplace_back(std::initializer_list{0, 1, 0}); // r alphabet.emplace_back(std::initializer_list{0, 0, 0}); // s alphabet.emplace_back(std::initializer_list{1}); // t alphabet.emplace_back(std::initializer_list{0, 0, 1}); // u alphabet.emplace_back(std::initializer_list{0, 0, 0, 1}); // v alphabet.emplace_back(std::initializer_list{0, 1, 1}); // w alphabet.emplace_back(std::initializer_list{1, 0, 0, 1}); // x alphabet.emplace_back(std::initializer_list{1, 0, 1, 1}); // y alphabet.emplace_back(std::initializer_list{1, 1, 0, 0}); // z } ~SoundSynth() { if(stream != NULL) { Pa_CloseStream(stream); stream = NULL; } Pa_Terminate(); } auto open() -> bool { PaError err = Pa_Initialize(); if(err != paNoError) { print_error(err); return false; } PaDeviceIndex index = Pa_GetDefaultOutputDevice(); if(index == paNoDevice) { std::cerr << "ERROR: No audio output device" << std::endl; return false; } const PaDeviceInfo* pInfo = Pa_GetDeviceInfo(index); if(pInfo != 0) { std::cout << "INFO: Output device name: " << pInfo->name << std::endl; } else { std::cerr << "ERROR: No device info available" << std::endl; return false; } PaStreamParameters outputParameters; outputParameters.device = index; outputParameters.channelCount = 2; outputParameters.sampleFormat = paFloat32; outputParameters.suggestedLatency = pInfo->defaultLowOutputLatency; outputParameters.hostApiSpecificStreamInfo = NULL; err = Pa_OpenStream( &stream, NULL, // no input &outputParameters, sample_rate, paFramesPerBufferUnspecified, paClipOff, &paCallback, reinterpret_cast(this) ); if(err != paNoError) { print_error(err); return false; } return true; } auto start() -> bool { if(stream == NULL) return false; cur = 0; cur_sine = 0; up = true; PaError err = Pa_StartStream(stream); return (err == paNoError); } auto stop() -> bool { if(stream == NULL) return false; PaError err = Pa_CloseStream(stream); return (err == paNoError); } auto print_error(PaError err) -> void { std::cerr << "ERROR: " << Pa_GetErrorText(err) << std::endl; } auto add(const char* s) -> void { bool last_letter = false; for(; *s; s++) { char c = *s; if(std::isalpha(c)) { if(last_letter) pending.push(toSample(short_gap)); auto rep = alphabet[std::tolower(c)-'a']; pending.push(toSampleMorse(rep[0])); for(int i=1; i<(int)rep.size(); ++i) { pending.push(toSample(inter)); pending.push(toSampleMorse(rep[i])); } last_letter = true; } else if(c == ' ') { pending.push(toSample(medium_gap)); last_letter = false; } else { std::cout << "Ignoring '" << c << "'" << std::endl; - last_letter = false; } } } auto toSampleMorse(int type) -> int { if(type == 0) return toSample(dot); return toSample(dash); } auto toSample(float dur) -> int { return (int)(dur*(float)sample_rate); } auto wait() -> void { while(Pa_IsStreamActive(stream)) { Pa_Sleep(50); } } private: int paCallbackMethod(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags) { float* out = (float*)outputBuffer; (void)timeInfo; // Prevent unused variable warnings. (void)statusFlags; (void)inputBuffer; if(pending.empty()) return paComplete; int tot = pending.front(); for(unsigned long i=0; i= sine_sampling) cur_sine = 0; *out++ = sine[cur_sine]; // avoid % because it's slow *out++ = sine[cur_sine]; cur_sine++; } else { *out++ = 0.f; *out++ = 0.f; } if(++cur >= tot) { pending.pop(); if(pending.empty()) return paComplete; tot = pending.front(); cur = 0; cur_sine = 0; up = !up; } } return paContinue; } static int paCallback(const void* inputBuffer, void* outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void* userData) { return ((SoundSynth*)userData)->paCallbackMethod( inputBuffer, outputBuffer, framesPerBuffer, timeInfo, statusFlags); } PaStream* stream = NULL; int sine_per; std::array sine; std::queue pending; bool up = false; int cur = 0, cur_sine = 0; std::vector> alphabet; - const float dot = 0.3f; + const float dot = 0.2f; const float dash = 3.f*dot; const float inter = dot; const float short_gap = 3.f*dot; const float medium_gap = 7.f*dot; }; auto load_sayings(std::vector& sayings) -> bool { std::ifstream in("sayings.txt"); if(!in.is_open()) return false; std::string line; while(std::getline(in, line)) { sayings.push_back(line); } return true; } auto main(int argc, char* argv[]) -> int { std::string s; if(argc < 2) { srand((unsigned)time(NULL)); std::vector sayings; load_sayings(sayings); s = sayings[rand()%(int)sayings.size()]; } else { s = argv[1]; } SoundSynth sound_synth; if(!sound_synth.open()) return EXIT_FAILURE; sound_synth.add(s.c_str()); sound_synth.start(); sound_synth.wait(); return EXIT_SUCCESS; }