PortAudio ile Ses Uygulamaları Yapmak

Bu yazımızda bir ses işleme kütüphanesi olan PortAudio’yu kullanarak bir C++ uygulaması yazacağız. PortAudio açık kaynak kodlu, c ile yazılmış, farklı platformlarda (windows, machintosh osx, linux …) derlenip, çalışan bir kütüphane.

Üzerinde geliştirme yapacağımız ortam, windows ortamı (windows xp ve windows 7). CodeBlocks ve MinGW kullanacağım. MinGW’yi C++ ve MSYS seçeneklerini dahil ederek kuruyoruz. Bu araçların kurulumu konusunda internette yeterli doküman var. Önce portaudio’nun sitesinin download kısmından, stable release’ı indiriyorum. Benim indirdiğim dosya pa_stable_v19_20111121.tgz dosyası. 7-zip ile bu dosyayı C dizinine açıyorum. Böylece C:\portaudio diye bir dizin oluşturmuş oldum.

Şimdi indirdiğim portaudio kütüphanesini MinGW shell’ini kullanarak derleyeceğim. Başlat -> Programlar -> MinGW -> MinGW shell’ini çalıştırıyorum. Daha sonra portaudio dizinine geçiyoruz, aşağıdaki komutları sırayla çalıştırıyoruz:

cd /c/portaudio

./configure

make clean

make

make install

./configure işlemini çalıştırırken anti-virüs uyarısı devreye girdi. Bir süreliğine anti-virüs programını disable etmek durumunda kaldım. Bu komutların çalışması, biraz zaman alıyor. Benim makinamda yaklaşık 15 dakika sürdü.

Daha sonra, C++ programı yazacağımız için, c++ bindings kısmını build ediyoruz;

cd bindings/cpp

Sırasıyla aşağıdaki komutları yazıyoruz:

./configure

make

make install

Herhangi bir hata çıkmadıysa artık, programımızı yazmaya geçebiliriz. CodeBlocks’ı açıyoruz. File -> New -> Project’i seçip, PortAudioCpp isminde yeni bir C++ console uygulaması create ediyoruz. Bu örneği, http://www.keithv.com/software/portaudio/ sitesindeki örneği kısaltarak, adapte ederek hazırladım. Amacım daha çok bu yazıda, portaudio kütüphanesini, başlangıç düzeyindeki insanlar için, baştan sona bütün aşamaları takip ederek, kendi bilgisayarlarında compile edip, çalıştırabilecek duruma gelmeleridir.

Project -> Build Options komutunu çalıştırıp, aşağıdaki gibi ayarları set ediyoruz:

Linker settings:

C:\portaudio\lib\.libs\libportaudio-2.dll

C:\portaudio\bindings\cpp\lib\.libs\libportaudiocpp-0.dll

Search directories

Compiler:

C:\portaudio\include

C:\portaudio\bindings\cpp\include

Linker:

C:\portaudio\lib\.libs

C:\portaudio\bindings\cpp\lib\.libs

Daha sonra File -> New -> File’ı seçip C/C++ header’ı seçiyoruz, isim olarak AudioBuffer.h diyoruz:

        // Class that can be used to record, playback and save audio data
// to a file.  It is designed to be a producer/consumer with the
// portaudio library.
//
// This class expects mono audio in INT16 (short) format.
//
// Copyright 2007 by Keith Vertanen.
 
#ifndef _AUDIO_BUFFER_H_
#define _AUDIO_BUFFER_H_
 
#include <vector>
#include <iostream>
#include <fstream>
 
#include "portaudiocpp/PortAudioCpp.hxx"
 
using namespace std;
 
typedef vector<short> VECTOR_SHORT;
typedef vector<short>::iterator VECTOR_SHORT_ITER;
 
class AudioBuffer
{
    public:
        AudioBuffer(int iSizeHint);
        ~AudioBuffer();
 
        int RecordCallback(const void* pInputBuffer,
                            void* pOutputBuffer,
                            unsigned long iFramesPerBuffer,
                            const PaStreamCallbackTimeInfo* timeInfo,
                            PaStreamCallbackFlags statusFlags);
        int PlaybackCallback(const void* pInputBuffer,
                            void* pOutputBuffer,
                            unsigned long iFramesPerBuffer,
                            const PaStreamCallbackTimeInfo* timeInfo,
                            PaStreamCallbackFlags statusFlags);
        void Clear();
        void WriteToFile(const string& strFilename);
        void ResetPlayback();
 
    private:
        VECTOR_SHORT        m_vectorSamples;                    // Holds the 16-bit mono samples
        VECTOR_SHORT_ITER   m_iPlaybackIter;                    // Tracks where we are during playback
 
};
 
#endif

Benzer şekilde File -> New -> File, C/C++ source diyerek AudioBuffer.cpp oluşturuyoruz.

        // Class that can be used to record, playback and save audio data
        // to a file.  It is designed to be a producer/consumer with the
        // portaudio library.
        //
        // This class expects mono audio in INT16 (short) format.
        //
        // Copyright 2007 by Keith Vertanen.
 
 
#include "AudioBuffer.h"
 
// Constructor, caller can give us a hint about the number of samples we may need to hold.
AudioBuffer::AudioBuffer(int iSizeHint)
{
    if (iSizeHint > 0)
        m_vectorSamples.reserve(iSizeHint);
 
    m_iPlaybackIter = m_vectorSamples.begin();
}
 
AudioBuffer::~AudioBuffer()
{
}
 
int AudioBuffer::RecordCallback(const void* pInputBuffer,
                                void* pOutputBuffer,
                                unsigned long iFramesPerBuffer,
                                const PaStreamCallbackTimeInfo* timeInfo,
                                PaStreamCallbackFlags statusFlags)
{
    short** pData = (short**) pInputBuffer;
 
    if (pInputBuffer == NULL)
    {
        cout << "AudioBuffer::RecordCallback, input buffer was NULL!" << endl;
        return paContinue;
    }
 
    // Copy all the frames over to our internal vector of samples
    for (unsigned long i = 0; i < iFramesPerBuffer; i++)
        m_vectorSamples.push_back(pData[0][i]);
 
    return paContinue;
}
 
int AudioBuffer::PlaybackCallback(const void* pInputBuffer,
                                    void* pOutputBuffer,
                                    unsigned long iFramesPerBuffer,
                                    const PaStreamCallbackTimeInfo* timeInfo,
                                    PaStreamCallbackFlags statusFlags)
{
    short**         pData   = (short**) pOutputBuffer;
    unsigned long   iOutput = 0;
 
    if (pOutputBuffer == NULL)
    {
        cout << "AudioBuffer::PlaybackCallback was NULL!" << endl;
        return paComplete;
    }
 
    // Output samples until we either have satified the caller, or we run out
    while (iOutput < iFramesPerBuffer)
    {
        if (m_iPlaybackIter == m_vectorSamples.end())
        {
            // Fill out buffer with zeros
            while (iOutput < iFramesPerBuffer)
            {
                pData[0][iOutput] = (short) 0;
                iOutput++;
            }
            return paComplete;
        }
 
        pData[0][iOutput] = (short) *m_iPlaybackIter;
 
        m_iPlaybackIter++;
        iOutput++;
    }
 
    return paContinue;
}
 
// Clear out any data in the buffer and prepare for a new recording.
void AudioBuffer::Clear()
{
    m_vectorSamples.clear();
}
 
// Dumpt the samples to a raw file
void AudioBuffer::WriteToFile(const string& strFilename)
{
    fstream fout(strFilename.c_str(), ios::out|ios::binary);
    short iSample;
    for (VECTOR_SHORT_ITER iter = m_vectorSamples.begin(); iter != m_vectorSamples.end(); iter++)
    {
        iSample = (short) *iter;
        fout.write((char *) &iSample, sizeof(short));
    }
    fout.close();
}
 
// Reset the playback index to the start of the samples
void AudioBuffer::ResetPlayback()
{
    m_iPlaybackIter = m_vectorSamples.begin();
}

main.cpp dosyasını da aşağıdaki gibi oluşturuyoruz:

#include <iostream>
#include "AudioBuffer.h"
 
const double    SAMPLE_RATE         = 16000.0;
const int       FRAMES_PER_BUFFER   = 64;
 
 
using namespace std;
 
int main()
{
    char    chWait;
    int     iInputDevice = -1;
    int     iOutputDevice = -1;
 
    // Create a object that is used to record, save to file and play the audio.
    AudioBuffer objAudioBuffer((int) (SAMPLE_RATE * 60));
 
    cout << "Setting up PortAudio..." << endl;
 
    // Set up the System:
    portaudio::AutoSystem autoSys;
    portaudio::System &sys = portaudio::System::instance();
 
    iInputDevice    = sys.defaultInputDevice().index();
    iOutputDevice   = sys.defaultOutputDevice().index();
 
    cout << "Opening recording input stream on " << sys.deviceByIndex(iInputDevice).name() << endl;
    portaudio::DirectionSpecificStreamParameters inParamsRecord(sys.deviceByIndex(iInputDevice), 1, portaudio::INT16, false, sys.deviceByIndex(iInputDevice).defaultLowInputLatency(), NULL);
    portaudio::StreamParameters paramsRecord(inParamsRecord, portaudio::DirectionSpecificStreamParameters::null(), SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff);
    portaudio::MemFunCallbackStream<AudioBuffer> streamRecord(paramsRecord, objAudioBuffer, &AudioBuffer::RecordCallback);
 
    cout << "Opening playback output stream on " << sys.deviceByIndex(iOutputDevice).name() << endl;
    portaudio::DirectionSpecificStreamParameters outParamsPlayback(sys.deviceByIndex(iOutputDevice), 1, portaudio::INT16, false, sys.deviceByIndex(iOutputDevice).defaultLowOutputLatency(), NULL);
    portaudio::StreamParameters paramsPlayback(portaudio::DirectionSpecificStreamParameters::null(), outParamsPlayback, SAMPLE_RATE, FRAMES_PER_BUFFER, paClipOff);
    portaudio::MemFunCallbackStream<AudioBuffer> streamPlayback(paramsPlayback, objAudioBuffer, &AudioBuffer::PlaybackCallback);
 
    cout << "Press enter to STOP recording.";
    streamRecord.start();
    cin.get(chWait);
    streamRecord.stop();
 
    cout << "Writing samples to audio.raw" << endl;
    objAudioBuffer.WriteToFile("audio.raw");
 
    cout << "Playing back samples." << endl;
    objAudioBuffer.ResetPlayback();
    streamPlayback.start();
    while (streamPlayback.isActive())
        sys.sleep(100);
    streamPlayback.stop();
 
    // Close the Stream (not strictly needed as terminating the System will also close all open Streams):
    streamRecord.close();
    streamPlayback.close();
 
    // Terminate the System (not strictly needed as the AutoSystem will also take care of this when it
    // goes out of scope):
    sys.terminate();
 
    return 0;
}

Compile edip, çalıştırdığımız zaman, mikrofondan gelen sesi kaydedip, tekrar çalan bir uygulama yapmış oluyoruz.

1 thought on “PortAudio ile Ses Uygulamaları Yapmak”

Yorum bırakın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Scroll to Top