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