PCMの再生1 |
PCM音源の再生ができれば、音声による警告音を出したりいろいろ応用ができます。しかし、技術情報が少なすぎます。丁寧に調べればAudacityのような本格的なサウンドアプリができますが、大変そうです。Windowsならすごく簡単なんですが.... 調べた限りでは、ほとんどがCで書かれたCUIアプリです。gtk,gtkmmを使ったGUIアプリは見当たりませんでした。最初はCでCUIアプリにしようかと思いましたが、どうせやるならgtkmmでGUIアプリでやろう!にしました。 最初の目標は、 正弦波を作成して再生する。 という簡単なプログラムです。 画面は、 Playボタンをクリックすると正弦波が再生されStopをクリックすると再生が停止するという仕様にしました。 PCMの処理に関してはクラスにして順次改良していきます。 alsa_pcm.h /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ /* * File: alsa_pcm.h * Author: sadaji sunaga * * Created on 2017/08/10, 11:43 */ #ifndef ALSA_PCM_H #define ALSA_PCM_H #include <alsa/asoundlib.h> class AlsaPcm { int16_t *m_pBuffer; snd_pcm_t *m_pcm; double m_sinwave_freq; double m_sampling_rate; public: AlsaPcm(); ~AlsaPcm(); bool setPlayParams(); bool Play(); }; #endif /* ALSA_PCM_H */alsa_pcm.cpp /* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ #include <iostream> #include <math.h> #include "alsa_pcm.h" #define PI 3.141592653589793 AlsaPcm::AlsaPcm() { int i; m_sinwave_freq = 440.0; m_sampling_rate = 48000.0; int samples = m_sampling_rate * 10; const unsigned int buffer_size =samples * 2; m_pBuffer = (int16_t *)malloc(buffer_size); if(m_pBuffer) { for (i = 0; i < samples; i++) { m_pBuffer[ i ] = 32767 * sin(2.0 * PI * m_sinwave_freq * i / m_sampling_rate); } } } AlsaPcm::~AlsaPcm() { if(m_pBuffer) { free(m_pBuffer); } } bool AlsaPcm::setPlayParams() { if (snd_pcm_set_params(m_pcm, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 48000, 1, 100000) ) { std::cout << "can't set PCM params." << std::endl; return false; } return true; } bool AlsaPcm::Play() { if (snd_pcm_open(&m_pcm, "hw:0", SND_PCM_STREAM_PLAYBACK, 0)) { std::cout << "can't openPCM device." << std::endl; return false; } if (setPlayParams() == false ) { return false; } int samples = m_sampling_rate * 10; int write_result = snd_pcm_writei(m_pcm, (const void*) m_pBuffer, samples); if (write_result < 0) { std::cout << "can't wite PCM data." << std::endl; return false; } snd_pcm_close(m_pcm); std::cout << "Play starts." << std::endl; return true; }main.cpp #include <gtkmm.h> #include <iostream> #include "alsa_pcm.h" class MainWin : public Gtk::Window { Gtk::Button on_btnPlay, m_btnStop; Gtk::HBox m_hbox; AlsaPcm m_AlsaPcm; public: MainWin(); private: void OnPlayClicked(); void OnStopClicked(); }; MainWin::MainWin() { on_btnPlay.set_label("Play"); m_btnStop.set_label("Stop"); // シグナルとスロットをコネクト on_btnPlay.signal_clicked().connect( sigc::mem_fun( *this, &MainWin::OnPlayClicked ) ); m_btnStop.signal_clicked().connect( sigc::mem_fun( *this, &MainWin::OnStopClicked ) ); m_hbox.pack_start( on_btnPlay ); m_hbox.pack_start( m_btnStop ); add( m_hbox ); show_all_children(); resize(240, 64); set_title("PCM sound"); } // スロット関数 void MainWin::OnPlayClicked() { m_AlsaPcm.Play(); } void MainWin::OnStopClicked() { std::cout << "Stop clicked." << std::endl; } int main( int argc, char *argv[] ) { Gtk::Main kit( argc, argv ); MainWin mainwin; Gtk::Main::run( mainwin ); return 0; }簡単なソースなので解説は省略します。 (手抜き) このプログラムではPlayボタンをクリックすると、 int write_result = snd_pcm_writei(m_pcm, (const void*) m_pBuffer, samples); で再生をするのですが、再生が終了して、 snd_pcm_close(m_pcm); が実行されます。 つまりsnd_pcm_writeiが終了するまでStopボタンをクリックしても反応しません。 Windowsなら再生をするとき、再生終了後の通知をcallback関数にするかWindowに通知するかを選んで、再生の処理はOSが裏でやってくれます。callbackの仕組みはALSAでも対応できます。しかしWindowsに比べたら面倒くさい。 次回はcallbackを使って再生中にStopボタンが有効になるように改修します。 |
目次へ |