[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH] gtkpbbuttons alsa support



Hi List,

as oss got obsolete in kernel 2.16 I hacked together alsa support for
gtkpbbuttons. I guess it still needs some more cleanup / better error
handling but I am using it since a week in everyday usage without
trouble.

As it is using the 'default' device I can now finally play many audio
sources simultaneously without gtkpbbuttons blocking it.

To make it work, just download gtkpbbuttons 0.6.8 and replace
audio.{c,h} with the ones in this file. Finally link with -lasound
linken ... thats all.

Soeren.
-- 
Sometimes, there's a moment as you're waking, when you become aware of
the real world around you, but you're still dreaming.

/* ----------------------------------------------------------------------------
 * audio.c
 * funtions for play audio samples
 *
 * Copyright 2002 Matthias Grimm
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 * ----------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#ifdef HAVE_SOUND

#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <malloc.h>
#include <linux/soundcard.h>
#include <alsa/asoundlib.h>
#include <audiofile.h>
#include <time.h>

#include "audio.h"

struct sample*
load_sample (char *samplename)
{
	AFfilehandle affd;     /* filehandle for soundfile from libaudiofile */
	AFframecount framecount;
	int dummy, channels, byteorder, framesize, precision, err = 0;
	struct sample *sample;

	if ((sample = (struct sample *) malloc(sizeof(struct sample))) == NULL)
		return NULL;

	if ((affd = afOpenFile(samplename, "r", 0))) {
		afGetSampleFormat(affd, AF_DEFAULT_TRACK, &dummy, &precision);
		channels = afGetChannels(affd, AF_DEFAULT_TRACK);
		byteorder = afGetVirtualByteOrder(affd, AF_DEFAULT_TRACK);
		framesize = (int) afGetFrameSize(affd, AF_DEFAULT_TRACK, 0);
		framecount = afGetFrameCount(affd, AF_DEFAULT_TRACK);
		sample->speed = (int) afGetRate(affd, AF_DEFAULT_TRACK);

		if (channels <= 2)
			sample->channels = channels;
		else
			err = -1;

		if (precision == 8)
			sample->format = SND_PCM_FORMAT_S8;
		else if (precision == 16) {
			if (byteorder == AF_BYTEORDER_LITTLEENDIAN)
				sample->format = SND_PCM_FORMAT_S16_LE;
			else
				sample->format = SND_PCM_FORMAT_S16_BE;
		} else
			err = -1;

		if (err == 0) {
			sample->framesize=framesize;
			sample->framecount=framecount;
			sample->audiodatalen = framecount * framesize;
			if ((sample->audiodata = (char *) malloc(sample->audiodatalen))) {
				if ((afReadFrames(affd, AF_DEFAULT_TRACK, sample->audiodata, framecount)) != framecount) {
					free(sample->audiodata);
					err = -1;
				}
			}
		}
		afCloseFile(affd);
	}

	if (err == -1) {
		free(sample);
		return NULL;
	}

	sample->periods=sample->framesize;
	sample->buffersize=(sample->periods*8192)>>2;

	return sample;
}

void
cleanup_audio(struct dspdata *dsp)
{
	if (dsp->active) {
		pthread_mutex_destroy(&dsp->mutex);
		pthread_cond_destroy(&dsp->cond);
	}
}

/* This function ist the sound playing thread.
 */
void
play_sample (void *arg)
{
	struct dspdata *dsp = (struct dspdata *) arg;
	dsp->playbeep = 0;
	for (;;) {
		if (dsp->playbeep == 0) {
			pthread_mutex_lock(&dsp->mutex);
			pthread_cond_wait(&dsp->cond, &dsp->mutex);
			pthread_mutex_unlock(&dsp->mutex);
		}
		dsp->playbeep = 0;
		snd_pcm_t *pcm_handle;          
		snd_pcm_hw_params_t *hwparams;
		char *pcm_name="default";

		struct sample* s=dsp->sample;
		snd_pcm_hw_params_alloca(&hwparams);

		if (snd_pcm_open(&pcm_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0) < 0) {
			fprintf(stderr, "Error opening PCM device %s\n", pcm_name);
			return;
		}

		if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0) {
			fprintf(stderr, "Can not configure this PCM device.\n");
			return;
		}
		if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
			fprintf(stderr, "Error setting access.\n");
			return;
		}
		if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, s->format) < 0) {
			fprintf(stderr, "Error setting format.\n");
			return;
		}

		if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &s->speed, 0) < 0) {
			fprintf(stderr, "Error setting rate.\n");
			return;
		}

		/* Set number of channels */
		if (snd_pcm_hw_params_set_channels_near(pcm_handle, hwparams, &s->channels) < 0) {
			fprintf(stderr, "Error setting channels.\n");
			return;
		}

		/* Set number of periods. Periods used to be called fragments. */ 
		if (snd_pcm_hw_params_set_periods_near(pcm_handle, hwparams, &s->periods, 0) < 0) {
			fprintf(stderr, "Error setting periods.\n");
			return;
		}

		/* Set buffer size (in frames). The resulting latency is given by */
		/* latency = periodsize * periods / (rate * bytes_per_frame)     */
		if (snd_pcm_hw_params_set_buffer_size_near(pcm_handle, hwparams, &s->buffersize) < 0) {
			fprintf(stderr, "Error setting buffersize.\n");
			return;
		}

		/* Apply HW parameter settings to */
		/* PCM device and prepare device  */
		if (snd_pcm_hw_params(pcm_handle, hwparams) < 0) {
			fprintf(stderr, "Error setting HW params.\n");
			return;
		}

		int pcmreturn;
		/* Write num_frames frames from buffer data to    */ 
		/* the PCM device pointed to by pcm_handle.       */
		/* Returns the number of frames actually written. */
		while ((pcmreturn = snd_pcm_writei(pcm_handle, s->audiodata, s->framecount)) < 0) {
			snd_pcm_prepare(pcm_handle);
			fprintf(stderr, "<<<<<<<<<<<<<<< Buffer Underrun >>>>>>>>>>>>>>>\n");
		}

		/* Stop PCM device and drop pending frames */
		snd_pcm_drop(pcm_handle);

		/* Stop PCM device after pending frames have been played */ 
		if (snd_pcm_close(pcm_handle) < 0)
		{
			fprintf(stderr, "snd_pcm_close error\n");
			return;
		}
	}
	pthread_exit(NULL);
}

/* This function wakes the sound playing thread to do
 * a beep. If the audio subsystem is disabled
 * (dsp->active = 0) it does nothing.
 */
void
trigger_beep(struct dspdata *dsp, struct sample *sample)
{
	if (dsp->active)        /* audio subsystem active and valid? */
		if ((dsp->sample = sample)) {  /* is there a sample to play? */
			pthread_mutex_lock(&dsp->mutex);   /* ok, give me one ping */
			dsp->playbeep = 1;
			pthread_cond_signal(&dsp->cond);
			pthread_mutex_unlock(&dsp->mutex);
		}
}

/* This function sets up the sound playing thread.
 * It starts the thread or if an error occur cleans
 * up all the audio stuff
 */
int
init_sound_thread(struct dspdata *dsp)
{
	pthread_attr_t attr;
	int rc = 0;

	dsp->maxblocksize = 0;
	dsp->thread = 0;
	pthread_mutex_init(&dsp->mutex, NULL);
	pthread_cond_init (&dsp->cond, NULL);
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
	if ((pthread_create(&dsp->thread, &attr, play_sample, (void *) dsp)) != 0) {
		cleanup_audio(dsp);
		rc = -1;
	}
	pthread_attr_destroy(&attr);
	return rc;
}

#endif
#ifndef INCLUDE_AUDIO_H
#define INCLUDE_AUDIO_H
/* ----------------------------------------------------------------------------
 * audio.h
 * prototypes and definitions for audio.c.
 *
 * Copyright 2002 Matthias Grimm
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 * ----------------------------------------------------------------------------*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <audiofile.h>
#include <alsa/asoundlib.h>

#define DEFAULT_AUDIO	"/dev/dsp"

struct dspdata {
	int	active;       /* =1, if sound system is active */
	char *audiodev;
	int maxblocksize;
	int playbeep;
	pthread_mutex_t mutex;
	pthread_cond_t cond;
	pthread_t thread;
	struct sample *sample;   /* sound to play */
};

struct sample {
	char *audiodata;
	int audiodatalen;
	int format;
	unsigned int channels;
	unsigned int speed;
	unsigned int framesize;
	int framecount;
    unsigned int periods;
    snd_pcm_uframes_t buffersize;
};

/* prototypes */
struct sample* load_sample (char *samplename);

void cleanup_audio(struct dspdata *dsp);
void play_sample (void *arg);

void trigger_beep(struct dspdata *dsp, struct sample *sample);
int init_sound_thread(struct dspdata *dsp);

#endif    /* INCLUDE_AUDIO_H */

Reply to: