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ボタンが有効になるように改修します。 |