Community Projects

Miscellaneous Forums/Linux Discussion/Community Projects

The forum is locked. These are the original blitzbasic.com forums, kept so they can still be read. No new topics, replies or edits can be made here. To join in, head over to the Blitz3D+ forums.

OK, I've wrapped up the work on ALSA for now.

As above:
freeaudio.bmx
?MacOS
Import "-framework AudioUnit"
Import "-framework AudioToolbox"
?
?Linux
Import "-lasound"
?


And:
#ifdef __linux

#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <pthread.h>
#include <alsa/asoundlib.h>

void *mixerthread(void *dev);

#define LINUXFRAG 2048

struct linuxaudio:audiodevice
{
	pthread_t	audiothread;
	int			threadid;
	int			audio_fd;
	snd_pcm_t 	*alsaaudio_fd;
	int			playing;
	int			fragsize,fragcount;
	int			buffersize;	//in bytes
	short		*buffer;
	
	int reset()
	{
		playing=0;
		mix=new mixer(LINUXFRAG);
		mix->freq=44100;
		mix->channels=2;
		buffer=new short[LINUXFRAG];
		buffersize=LINUXFRAG*2;
		pthread_attr_t	attr;
		pthread_attr_init(&attr);
		threadid=pthread_create(&audiothread,&attr,mixerthread,(void*)this);	
		return 0;
	}
	
	int close()
	{	
		int		timeout;
		playing=0;
		timeout=5;
		while (timeout-- && audio_fd) sleep(1);
		return 0;
	}
};

audiodevice *OpenSystemAudio()
{
	return new linuxaudio();
}

void initAudioALSA(linuxaudio *dev)
{
	if (snd_pcm_open(&dev->alsaaudio_fd, strdup("default"), SND_PCM_STREAM_PLAYBACK, 0)==-1) 	//uses default audio device..
	{
	dev->alsaaudio_fd=0;
	}
}

void initAudioOSS(linuxaudio *dev)
{
	dev->audio_fd=open("/dev/dsp",O_WRONLY,0);		//|O_NONBLOCK
	if (dev->audio_fd==-1)
	{
	dev->audio_fd=0;
	}
}

void *playAudioOSS(linuxaudio *dev)
{
	int				data,res;

 		data=0x03000c;		//2 fragments of 4096 samples
		res=ioctl(dev->audio_fd,SNDCTL_DSP_SETFRAGMENT,&data);		
		data=AFMT_S16_LE;
		res=ioctl(dev->audio_fd,SNDCTL_DSP_SETFMT,&data);
		data=2;
		res=ioctl(dev->audio_fd,SNDCTL_DSP_CHANNELS,&data);		
		data=44100;
		res=ioctl(dev->audio_fd,SNDCTL_DSP_SPEED,&data);
		
		dev->playing=1;
		while (dev->playing)
		{
			dev->mix->mix16(dev->buffer);
			int n=write(dev->audio_fd,dev->buffer,dev->buffersize);
		}
		
		close(dev->audio_fd);
}

void *playAudioALSA(linuxaudio *dev)
{
	unsigned int val;
	snd_pcm_uframes_t periodsize;
	snd_pcm_hw_params_t *hwparams;
	snd_pcm_hw_params_alloca(&hwparams);
	int output_rate;
	int channels;
	int fragment_size;
	int fragment_count;
	fragment_size=LINUXFRAG;  //overall buffer size
	fragment_count=2; //2 - 16 fragment count - 2 minimum, the lower it is potentially the lower the latency

	//configure device
		if (snd_pcm_hw_params_any(dev->alsaaudio_fd, hwparams) < 0) {
			//printf("linuxaudio failed at params any\n");
			return 0;
		}	
		if (snd_pcm_hw_params_set_access(dev->alsaaudio_fd, hwparams,SND_PCM_ACCESS_RW_INTERLEAVED) < 0) {
			//printf("linuxaudio failed at set access\n");
			return 0;
		}	
		
		if (snd_pcm_hw_params_set_format(dev->alsaaudio_fd, hwparams,SND_PCM_FORMAT_S16_LE) < 0) {
			//printf("linuxaudio failed at set format\n");
			return 0;
		}
		val = 44100;
		if (snd_pcm_hw_params_set_rate_near(dev->alsaaudio_fd, hwparams,&val, 0) < 0) {
			/* Try 48KHZ too */
			//printf("linuxaudio - %d HZ not available, trying 48000HZ\n", output_rate);
			val = 48000;
			if (snd_pcm_hw_params_set_rate_near(dev->alsaaudio_fd, hwparams,&val, 0) < 0) {
				//printf("linuxaudio failed at setting output rate (%d)\n", output_rate);
				return 0;
			}
			dev->mix->freq=val;		
		}
		channels=2;
		if (snd_pcm_hw_params_set_channels(dev->alsaaudio_fd, hwparams, channels) < 0) {
			//printf("linuxaudio failed at set channels (%d)\n", channels);
			return 0;
		}
		periodsize = (fragment_size) / 4; // bytes -> frames for 16-bit,stereo - should be a minimum of 512
		if (snd_pcm_hw_params_set_period_size_near(dev->alsaaudio_fd, hwparams,&periodsize, 0) < 0) {
			//printf("linuxaudio failed at set period size (%d)\n", (int)periodsize);			
			return 0;
		}
		val = fragment_count;
		if (snd_pcm_hw_params_set_periods_near(dev->alsaaudio_fd, hwparams,&val, 0) < 0) {
			//printf("linuxaudio failed at set periods (%d)\n", val);			
			//should attempt a one by one period increase up to 16?
			return 0;
		}
		if (snd_pcm_hw_params(dev->alsaaudio_fd, hwparams) < 0) {
			//printf("linuxaudio failed at installing hw params\n");
			return 0;
		}
		//loop while playing sound
		dev->playing=1;
		while (dev->playing)
		{
			dev->mix->mix16(dev->buffer);
			if ((snd_pcm_writei (dev->alsaaudio_fd, dev->buffer,LINUXFRAG/2)) < 0) {	//Half buffer for two channels?
				//printf ("linuxaudio warning: buffer underrun occurred\n");
				if (snd_pcm_prepare(dev->alsaaudio_fd) < 0) {
					//printf ("linuxaudio failed at preparing pcm\n");
					dev->playing=0; //die gracefully
				}
			}	
		}
		snd_pcm_drop(dev->alsaaudio_fd);
		snd_pcm_close (dev->alsaaudio_fd);
		dev->alsaaudio_fd=0; 
}

void *mixerthread(void *v)
{
	sched_param		sched;	
	linuxaudio 		*dev;
	int				policy;
	bool		usealsa;
	usealsa=1;
	pthread_getschedparam(pthread_self(),&policy,&sched);
	sched.sched_priority++;//policy=SCHED_RR;
	pthread_setschedparam(pthread_self(),policy,&sched);
	
	dev=(linuxaudio*)v;
	//try alsa
	initAudioALSA(dev);
	if (dev->alsaaudio_fd==0) 	//uses default audio device..
	{
		//try OSS
		initAudioOSS(dev);
		if (dev->audio_fd==0)
		{
			printf("linuxaudio failed\n");
			return 0;
		}
		usealsa=0;				
	}		
	if (usealsa=1) {
	playAudioALSA(dev);
	}
	else {
	playAudioOSS(dev);
	}
	dev->audio_fd=0;
	return 0;
}

#endif

I've tried to separate OSS and ALSA functionality. The system will try ALSA then fallback to OSS, and if that fails too, fail completely. The way it is structured it should be relatively easy to add new sound system support.

I should point out this is the first time I've really tried to program anything in C in about six years. Please feel free to offer any improvements. The code I added is completely public domain.