Community Projects

Archives Forums/Linux Discussion/Community Projects

skidracer(Posted 2005) [#1]
Given I have quite a bit of Cocoa development work underway on Apple and DirectX7 drivers were needed urgently under Windows it feels a little that things Linux haven't had much attention at Blitz Research.

Priority wise I'd say on the Linux jobs front there sits:

* compiling in embedded firefox / gtk widget (IDE online help)

* some form of packaged distribution for the popular distros that describes the x86vm(fullscreen) gl/glu(graphics) and stdc++6.0(legacy gcc) linking requirements

* ALSA sound driver

If anyone wants to lend a hand with any of these tasks free to email me.

I can provide some pointers for the ALSA driver, the package stuff needs someone with a bit more history of the Linux universe than I have and the Firefox widget I only just saw in action today in the distribution of some most excellent software I was playing with today ( Houdini Apprentice from www.sidefx.com ) so was quite chuffed an HTML browser solution for the IDE wasn't infact going to be that hard after all, whether BMK is up to the job of compiling a FireFox module may be another matter but i can provide some basic GTK2 code to get you started...


Craig Watson(Posted 2005) [#2]
I can take a look at ALSA but I can't make any promises of being able to get it working or the quality of the implementation if I do get it to work.

It is pretty easy to just get ALSA to just make some noise, so I'll see what I can do. If I have any luck I'll send you an email.

I'll see if I can figure out a process for building RPMs too. It seems most people's problems have been dependency related - packages would save us a lot of trouble. I don't think I can help you with Debs though.


computercoder(Posted 2005) [#3]
I can look into the DEBs. If you get the ALSA stuff going, and maybe I could assist you in that, I can see what to do for getting the DEB files built :-)


Craig Watson(Posted 2005) [#4]
Like I said, it was pretty easy to get ALSA to make some noise. I have it playing the OGG file and sound effects of Digesteroids, however the speed is messed up, there are latency issues, and some static depending on the settings. I'm going to have to spend a fair bit more time on it, but at least so far it's making noise and not seg faulting or something :P

If you're interested computercoder, I can build an executable based on the ALSA code thus far so you can check whether sound through Freeaudio ALSA works. It won't sound good, but it will make noise. Let me know.

The RPM stuff looks pretty straightforward to set up, but getting it to handle the development dependencies may be a problem. Basically I can pretty easily determine runtime dependencies by running ldd against a BlitzMax app (or RPM will do this for me) but it won't generate a list of required header files for example.

We could ensure that BlitzMax installs all required dependencies to run and _I think_ compile applications, but you'd still need to find and install the development packages if you want to build modules. I can probably determine all the development dependencies manually, but this would take a lot longer.

Oh, and I'll probably start a Worklog on this stuff for those who want to follow the progress.


Craig Watson(Posted 2005) [#5]
The sound is identical to the OSS version on my system. So now I'm going to have to ask for you guys to volunteer to help.

If anyone feels like helping me to test it, please send me an email. Click on my name at the top of the post to see the email in my profile.

To test, I have compiled the Rockout executable using the ALSA code. I will send this to you as a tar.gz to extract and place in the BlitzMax/Samples/hitoro folder.

Please test for sound output, quality and latency. Please ignore the other bugs in Rockout (flashing ship, etc.) as I'm only interested in testing the sound. You should probably also compile a version of Rockout for yourself based on the current Freeaudio OSS code for comparison.

I would appreciate particularly feedback from those experiencing difficulty getting BlitzMax to play sound.


BlitzSupport(Posted 2005) [#6]
Email sent.


Please ignore the other bugs in Rockout (flashing ship, etc.)


Interestingly, I saw this for the first time last night -- it's stuck on the red 'hit' flash. Did you notice when this started (in terms of Max versions)? It used to be fine! Let me know via email if you don't want to clog up this thread...


Craig Watson(Posted 2005) [#7]
Reply sent.

It used to work but I think it broke after some update. I think there was a change in the way some function worked which caused it. My own stuff works fine though, so I've never worried about it.

You should also try out Digesteroids if you have time. It's giving me Glibc Double Free errors.

You can download the compressed binary here: http://www.teamted.net/rockout.tar.gz

I'm going to clean up the source and then distribute it here also so people can try it out themselves. I'm certainly no expert on ALSA so all comments will be appreciated.

Feedback to my email or in this thread please. If necessary I'll start a new thread.

Oh, and ignore any messages you get in the console about buffer underruns. Unless you get more than one or two.


Craig Watson(Posted 2005) [#8]
I've now uploaded the source to the changes here: http://www.teamted.net/freeaudioalsa.tar.gz

These are the files freeaudio.bmx and freeaudio.cpp from BlitzMax/mod/pub.mod/freeaudio.mod. I would suggest that you make a copy of freeaudio.bmx and freeaudio.cpp before you replace these files. You could also just Synchronize Modules to get back the original versions.

For those interested, I'll post the changes to the source for both files. I'll post the code around the changes for your reference.
freeaudio.bmx
?MacOS
Import "-framework AudioUnit"
Import "-framework AudioToolbox"
?
?Linux
Import "-lasound"
?
Added the Linux section with an import for ALSA libasound. MacOS stuff wasn't changed, just for reference.
freeaudio.cpp
#ifdef __linux

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <pthread.h>

void *mixerthread(void *dev);

#define LINUXFRAG 2048

struct linuxaudio:audiodevice
{
	pthread_t	audiothread;
	int			threadid;
	snd_pcm_t 	*audio_fd;
	int 		playback;
	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-- && playback) sleep(1);
		return 0;
	}
};

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

void *mixerthread(void *v)
{
	sched_param		sched;	
	linuxaudio 		*dev;
	int			policy;
	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

	pthread_getschedparam(pthread_self(),&policy,&sched);
	sched.sched_priority++;//policy=SCHED_RR;
	pthread_setschedparam(pthread_self(),policy,&sched);

	//open audio device	
	dev=(linuxaudio*)v;	
	dev->playback=snd_pcm_open(&dev->audio_fd, strdup("default"), SND_PCM_STREAM_PLAYBACK, 0); //uses default audio device..
	if (dev->playback==-1)
	{
		printf("linuxaudio failed\n");
		dev->playback=0;
		return 0;
	}		
	//configure device
	if (snd_pcm_hw_params_any(dev->audio_fd, hwparams) < 0) {
		printf("linuxaudio failed at params any\n");
		return 0;
	}	
	if (snd_pcm_hw_params_set_access(dev->audio_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->audio_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->audio_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->audio_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->audio_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->audio_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->audio_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->audio_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->audio_fd, dev->buffer,LINUXFRAG/2)) < 0) {	//Half buffer for two channels?
			printf ("linuxaudio warning: buffer underrun occurred\n");
			if (snd_pcm_prepare(dev->audio_fd) < 0) {
				printf ("linuxaudio failed at preparing pcm\n");
				dev->playing=0; //die gracefully
			}
		}	
	}
	snd_pcm_drop(dev->audio_fd);
	snd_pcm_close (dev->audio_fd);
	dev->audio_fd=0;
	dev->playback=0;
	return 0;
}

#endif
All code from ifdef ___linux has been modified, scroll almost right down to the bottom of the file for the linux section.

I've tried to retain the method used with OSS for ALSA. Generally it would be recommended that we use interrupts and callbacks, but the thread stuff seems to work OK too. This should make clear the differences between ALSA and OSS - it shows you can use ALSA very much like OSS.

Tweaking the LINUXFRAG and fragment_count values might lead to lower latency.

Any suggestions or critique is appreciated.


computercoder(Posted 2005) [#9]
Craig,

I was thinking more about the audio in BlitzMax. Seems to me that maybe it should do a 'detect' for the audio system on the linux distro at hand. Granted OSS and ALSA are the primary sound support, but the others like aRts which we found out about for using inside of BlitzMax working on OSs like Linspire. What I am getting at is that we as developers will not want to create a package for each kind of sound system, or tell the user to change this or that in their system. Just have the BlitzMax app do it, and if the systems supported all fail, then tell the user.

Anyways, GREAT job working with OSS, ALSA, and aRts!


Craig Watson(Posted 2005) [#10]
While I would certainly like to provide an automatic detection routine, I'm not sure how this might affect the executable linking requirements, ie. would the user need to install libraries for eSound even if Blitz ends up using ALSA on their system? I'll have to test it out, but I don't think actual libraries are needed unless we try to use functions from them.

The other problem with this is it would increase the development environment requirements. It would mean the user would need the header files for aRts, ALSA, eSound, etc. Granted most distros include dev packages for all of these, but it's an additional support headache.

If we had a configure system then we could check for the availability of headers and libraries during the build process and leave the parts out that aren't available, so for example, if you didn't install aRts packages, then your executables won't include aRts support.

This may be possible to do under the current system, I'll have to check it out.

We also have to get Mark and Simon to decide on how this will work. I'm sure they're not keen to possibly have to support stuff they maybe don't understand and didn't program. In this case it may be better for us to provide a separate public audio module instead, and if we're going to go that far, I'd rather provide an advanced library that supports Windows, Mac, Linux and plays tracker formats and does other nice stuff, like Audiere. This was what I'd planned to attempt anyway after we got basic Linux Freeaudio working better.


computercoder(Posted 2005) [#11]
Sounds GREAT Craig! I'll help in any way I can if you need it.


Craig Watson(Posted 2005) [#12]
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.


skidracer(Posted 2005) [#13]
Great stuff Craig, will test shortly.