LinuxQuestions.org
Visit Jeremy's Blog.
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 03-20-2013, 08:09 AM   #1
OAnimal74
LQ Newbie
 
Registered: Mar 2013
Posts: 8

Rep: Reputation: Disabled
Problems with programming ALSA


Hi everyone,

at my current project, I ran into trouble. What I am trying to do is to read some parameter (which I have no control of and can't predict its value) 10 times per second and play a sound that is associated with that value. I was able to program all this by preparing all the samples at the start of the application and feeding those to ALSA on the fly as I read the parameter value. However, the output sound that I get out of it, gets interrupted. What I would like to have is a continuous sound. Here is the code that illustrates the problem. For the first 10 seconds it produces the interrupted sound by feeding individual samples to ALSA and the next 10 seconds (after a 2s pause) it plays all the samples merged together (and this is how the first 10 seconds should sound as well). Here is my code:

Alsa.h
Code:
#ifndef ALSA_H
#define ALSA_H

#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QQueue>
#include <QVector>
#include <QByteArray>
#include <alsa/asoundlib.h>

class Audio : public QThread
{
	Q_OBJECT

public:
	//! Constructor!
	Audio(QObject* parent = 0);

	//! Destructor!
	~Audio();

	void PlaySound(const QByteArray& baData);

public slots:
	void Play();

private:
	//! Thread for playing audio.
	void run();

	//! Mutex for protecting data buffer.
	QMutex m_mutex;
	//! The buffer we are protecting.
	QQueue<QByteArray> m_qbaBuffer;
	//! The buffer was set condition event.
	QWaitCondition m_wcBufferSet;
	//! Terminate flag.
	bool m_bTerminate;

	//! Sound buffer
	QQueue<QByteArray> m_qbaSoundBuffer;
	//! Indicates the position in the current byte array
	int m_iCurrentPos;
	//! Current byte array
	QByteArray m_baCurrent;

	//! Alsa device handle.
	snd_pcm_t* m_pHandle;
	int m_iFramesInPeriod;
	int m_iFrameSize;

	int m_iSample;
	QVector<QByteArray> m_vbaSamples;

	QByteArray m_baAll;
};



#endif // ALSA_H
Alsa.cpp
Code:
#include <QApplication>
#include <QTimer>
#include <QTime>
#include <QDebug>
#include <math.h>
#include "Alsa.h"

#define NA_RATE		44100


Audio::Audio(QObject* parent)
	: QThread(parent), m_vbaSamples(100)
{
	// generate samples of frequencies from 400Hz to 1390Hz
	m_iSample = 0;
	float fAmp = 5000.0f;
	for (int i = 0; i < 100; i++) {
		float fFreq = 400.0f + 10.0f*i;
		float fW = 2*3.1415926535f*fFreq/NA_RATE;

		// 100ms samples
		for (int iA = 0; iA < 4410; iA++) {
			int iVal = (int)roundf(fAmp*sinf(iA*fW));
			char chLSB = iVal & 255;
			char chMSB = iVal >> 8;
			m_vbaSamples[i].append(chLSB);
			m_vbaSamples[i].append(chMSB);
		}

		m_baAll.append(m_vbaSamples[i]);
	}

	int rc;
	snd_pcm_hw_params_t *params;
	unsigned int val;
	snd_pcm_uframes_t FramesInPeriod;
	snd_pcm_uframes_t PeriodsInBuffer;


	rc = snd_pcm_open(&m_pHandle, "default", SND_PCM_STREAM_PLAYBACK, 1);

	if (rc < 0) {
		qDebug("unable to open pcm device: %s", snd_strerror(rc));
		return;
	}

	snd_pcm_hw_params_alloca(&params);

	rc = snd_pcm_hw_params_any(m_pHandle, params);
	if (rc < 0) {
		qDebug("Broken configuration: no PCM");
		return;
	}

	snd_pcm_hw_params_set_access(m_pHandle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
	snd_pcm_hw_params_set_format(m_pHandle, params, SND_PCM_FORMAT_S16_LE);
	snd_pcm_hw_params_set_channels(m_pHandle, params, 1);
	val = NA_RATE;
	snd_pcm_hw_params_set_rate_near(m_pHandle, params,	&val, 0);

	FramesInPeriod = 1024;
	PeriodsInBuffer = 4 * FramesInPeriod;

	rc = snd_pcm_hw_params_set_period_size_near(m_pHandle, params, &FramesInPeriod, 0);
	if (rc < 0) {
		qDebug("Unable to set period size");
		return;
	}
	rc = snd_pcm_hw_params_set_buffer_size_near(m_pHandle, params, &PeriodsInBuffer);
	if (rc < 0) {
		qDebug("Unable to set buffer size");
		return;
	}

	rc = snd_pcm_hw_params(m_pHandle, params);
	if (rc < 0) {
		qDebug("unable to set hw parameters: %s", snd_strerror(rc));
		return;
	}

	snd_pcm_hw_params_get_period_size(params, &FramesInPeriod, 0);
	snd_pcm_hw_params_get_buffer_size(params, &PeriodsInBuffer);

	m_iFramesInPeriod = FramesInPeriod;
	m_iFrameSize = 2;

	/* We want to loop for 5 seconds */
	//snd_pcm_hw_params_get_period_time(params, &val, 0);

	m_iCurrentPos = 0;
	m_baCurrent.clear();
	start();
}

Audio::~Audio()
{
	m_bTerminate = true;
	wait(500);
}

void Audio::run()
{
	m_bTerminate = false;

	while (true) {
		while (m_baCurrent.count() == 0) {
			// try to get new data
			m_mutex.lock();
			while (m_qbaSoundBuffer.isEmpty()) {
				m_wcBufferSet.wait(&m_mutex, 100);
				if (m_bTerminate)
					break;
			}

			if (m_bTerminate) {
				m_mutex.unlock();
				break;
			}

			// here we have a sample
			m_baCurrent = m_qbaSoundBuffer.dequeue();
			// move pointer to the beginning
			m_iCurrentPos = 0;
			// Release the protection.
			m_mutex.unlock();
		}

		// here we have data in the buffer
		const char* pch = m_baCurrent.constData();
		int iRC = 1;

		qDebug() << "iRC" << iRC << snd_pcm_avail(m_pHandle);
		while (m_iCurrentPos < m_baCurrent.count()) {
			iRC = snd_pcm_writei(
					m_pHandle,
					pch + m_iCurrentPos,
					m_iFramesInPeriod
					);

			QTime time = QTime::currentTime();
			qDebug() << "iRC" << iRC << time.toString("hh:mm:ss.zzz")
						<< snd_pcm_avail(m_pHandle);
			if (iRC > 0) {
				m_iCurrentPos += iRC * m_iFrameSize;
			}	else if (iRC == -EAGAIN) {
				// no space in buffer, sleep for 10[ms]
				usleep(10000);
				//snd_pcm_wait(m_pHandle, 50);
			}	else if (iRC == -EPIPE) {
				snd_pcm_prepare(m_pHandle);
			}
		}
		m_baCurrent.clear();
		m_iCurrentPos = 0;
	}
	snd_pcm_close(m_pHandle);
}

void Audio::PlaySound(const QByteArray& baData)
{
	m_mutex.lock();
	// synchronization - reject the old samples
	if (m_qbaSoundBuffer.count() > 2) {
		m_qbaSoundBuffer.clear();
	}

	m_qbaSoundBuffer.append(baData);

	m_mutex.unlock();
	m_wcBufferSet.wakeAll();
}

void Audio::Play()
{
	// for first 10s play individual samples
	if (m_iSample < 100)
		PlaySound(m_vbaSamples[m_iSample]);
	// at 12th second, play all samples merged together and listen to the difference
	else if (m_iSample == 120) {
		qDebug() << "*****************";
		PlaySound(m_baAll);
	}
	m_iSample++;
}

int main(int argc, char** argv)
{
	QApplication app(argc, argv);
	QTimer timer;
	Audio audio;

	QObject::connect(&timer, SIGNAL(timeout()), &audio, SLOT(Play()));

	timer.start(100);

	app.exec();

	return 0;
}
What am I doing wrong in the Audio::run method?
Note: I have to feed the samples one by one, because there's no way to predict, which one is going to be needed in the next iteration. And, I have to use the non-blocking snd_pcm_writei, because the app has to be responsive and I can't afford to have the thread wait until snd_pcm_writei returns in the blocking mode.

Last edited by OAnimal74; 03-20-2013 at 08:14 AM.
 
Old 03-20-2013, 12:21 PM   #2
dugan
LQ Guru
 
Registered: Nov 2003
Location: Canada
Distribution: Slackware
Posts: 8,553

Rep: Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569Reputation: 3569
Is there a reason you're trying to use ALSA directly, and not using Qt's classes (e.g. Phonon in Qt 4) to play audio?

Last edited by dugan; 03-20-2013 at 12:24 PM.
 
Old 03-20-2013, 03:40 PM   #3
OAnimal74
LQ Newbie
 
Registered: Mar 2013
Posts: 8

Original Poster
Rep: Reputation: Disabled
Quote:
Originally Posted by dugan View Post
Is there a reason you're trying to use ALSA directly, and not using Qt's classes (e.g. Phonon in Qt 4) to play audio?
Yes there is. Phonon is not an option, unfortunately.
To be more specific: the final application is not going to run on PC, but on an ARM processor, where resources are limited, so compiling Phonon with all its dependencies is not an option.

Last edited by OAnimal74; 03-21-2013 at 04:56 AM.
 
  


Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Alsa programming. Volume has changed. mrdebug Programming 0 07-08-2010 10:02 AM
Programming with ALSA API muskvar Programming 9 12-15-2009 02:40 PM
ALSA Programming Santosh_2839 Linux - General 0 10-23-2008 12:18 AM
ALSA Programming Santosh_2839 Linux - Software 0 09-25-2008 04:38 AM
ALSA programming hobase Programming 0 11-20-2007 08:53 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 08:27 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration