[BACK]Return to audio-port.html CVS log [TXT][DIR] Up to [local] / www

File: [local] / www / Attic / audio-port.html (download) (as text)

Revision 1.7, Tue Jun 18 01:44:05 2002 UTC (21 years, 11 months ago) by jsyn
Branch: MAIN
Changes since 1.6: +2 -2 lines

make the OpenBSD logo on each page a link back to index.html; from
s@smith.net, ok deraadt@

<html>
 <head>
  <meta http-equiv="Content-Type"
	content="text/html; charset=iso-8859-1">
  <meta name="resource-type"
	content="document">
  <meta name="description"
	CONTENT="How to make an OpenBSD port; audio">
  <meta name="keywords"
	content="openbsd,ports,audio">
  <meta name="distribution"
	content="global">
  <meta name="copyright"
	content="This document copyright 1998-2002 by OpenBSD.">
  <title>Porting audio applications to OpenBSD</title>
  <link rev="made" HREF="mailto:www@openbsd.org">
 </head>
 <body text="#000000" bgcolor="#FFFFFF" link="#23238E">
<a href="index.html"><img alt="[OpenBSD]" height="30" width="141" src="images/smalltitle.gif" border="0"></a>

  <h1>Porting audio applications to OpenBSD</h1>

<p>
  This document currently deals with sampled sounds issues only. Contributions
  dealing with synthesizers and waveform tables are welcome.

</p>

	Audio applications tend to be hard to port, as this is a domain where
	interfaces are not standardized at all, though approaches don't vary
	much between operating systems. 


  <h2><font color=#e00000>Using <code>ossaudio</code></font></h2>

  The <code>ossaudio</code> emulation is possibly the simplest way, but
  it won't always work, and it is not such a great idea usually.
  <ul>
  	<li>It redefines <code>ioctl</code>. If the code to port uses
	<code>ioctl</code> for more than audio, you will have to
	<code>#undef ioctl</code> and use the bare form with
	<code>_ossioctl</code>.

	<li>Some features of linux sound are not emulated.

	<li>Applications with correct linux sound support that is not
	Intel-specific tend to use these features.

  </ul>

  <h2><font color=#e00000>Using existing NetBSD or FreeBSD code</font></h2>
  Since we share part of the audio interface with NetBSD and FreeBSD,
  starting from a NetBSD port is reasonable. Be aware that some files 
  changed places, and that some entries in <code>sys/audioio.h</code>
  are obsolete.  Also, many ports tend to be incorrectly coded and to
  work on only one type of machine. Some changes are bound to be 
  necessary, though.  Read through the next part.

  <h2><font color=#e00000>Writing OpenBSD code</font></h2>
	  <h3><font color=#0000e0>Hardware independence</font></h3>

   <p>
	<strong>YOU SHOULDN'T ASSUME ANYTHING ABOUT THE AUDIO HARDWARE USED.
	</strong><br>
	Wrong code is code that only checks the <code>a_info.play.precision</code>
	field against 8 or 16 bits, and assumes unsigned or signed samples based
	on soundblaster behavior. You should check the sample type explicitly,
	and code according to that. Simple example:
	<pre>
    AUDIO_INIT_INFO(&amp;a_info);
    a_info.play.encoding = AUDIO_ENCODING_SLINEAR;
    a_info.play.precision = 16;
    a_info.play.sample_rate = 22050;
    error = ioctl(audio, AUDIO_SETINFO, &amp;a_info);
    if (error)
	/* deal with it */
    error = ioctl(audio, AUDIO_GETINFO, &amp;a_info);
    switch(a_info.play.encoding)
	{
    case AUDIO_ENCODING_ULINEAR_LE:
    case AUDIO_ENCODING_ULINEAR_BE:
	if (a_info.play.precision == 8)
	    /* ... */
	else 
	    /* ... */
	break;
    case ...

    default:
	/* don't forget to deal with what you don't know !!! For instance, */
	fprintf(stderr, 
		"Unsupported audio format (%d), ask ports@ about that\n",
		a_info.play.encoding);

	}
    /* now don't forget to check what sampling frequency you actually got */
	</pre>
  
  </p>
  This is about the smallest code fragment that will deal with most issues.

  	<h3><font color=#0000e0>16 bit formats and endianess</font></h3>
	In normal usage, you just ask for an encoding type (e.g.,
	<code>AUDIO_ENCODING_SLINEAR</code>, and you retrieve
	an encoding with endianess (e.g., <code>AUDIO_ENCODING_SLINEAR_LE</code>).
	Considering that a soundcard does not have to use the same endianess
	as your platform, you should be prepared to deal with that. 
	The easiest way is probably to prepare a full audio buffer, and to use
	<code>swab(3)</code> if an endianess change is required. 
	Dealing with external samples usually amounts to:
	<ol>
		<li>Parsing the sample format,
		<li>Getting the sample in,
		<li>Swapping endianess if it is not your native format,
		<li>Computing what you want to output into a buffer,
		<li>Swapping endianess if the sound card is not in your native format,
		<li>Playing the buffer.
	</ol>
	Obviously, you may be able to remove steps 3 and 5 if you are simply
	playing a sound sample which happens to be in your sound card native
	format.

	<h3><font color=#0000e0>Audio quality</font></h3>
	<p>
	Hardware may have some weird limitations, such as being unable to get
	over 22050 Hz in stereo, but up to 44100 in mono.  In such cases, you
	should give the user a change to state his preferences, then try your
	best to give the best performance possible. For instance, it is stupid
	to limit the frequency to 22050 Hz because you are outputting stereo.
	What if the user does not have a stereo sound system connected to his
	audio card output ?
	</p>

	<p>
	It is also stupid to hardcode soundblaster-like limitations into your
	program. You should be aware of these, but do try to get over the
	22050&nbsp;Hz/stereo barrier and check the results.
	</p>

	<h4>Sampling frequency</h4>
	You should definitely check the sampling frequency your card gives you
	back. A 5% discrepancy already amounts to a half-tone, and some people
	have much more accurate hearing than that, though most of us won't
	notice a thing.  Your application should be able to perform 
	resampling on the fly, possibly naively, or through devious 
	applications of Shannon's resampling formula if you can.

	<h4>Dynamic range</h4>
	<p>
	Samples don't always use the full range of values they could.  First,
        samples recorded with a low gain will not sound very loud on the 
	machine, forcing the user to turn the volume up.
	Second, on machines with badly isolated audio, low sound output means
	you mostly hear your machine heart-beat, and not the sound you expected.
	Finally, dumb conversion from 16 bits to 8 bits may leave you with only
	4 bits of usable audio, which makes for an awfully bad quality.
	</p>
	<p>
	If possible, the best solution is probably to scan the whole stream
	you are going to play ahead of time, and to scale it so that it fits
	the full dynamic range.  If you can't afford that, but you can manage 
	to get a bit of look-ahead on what you're going to play, you can 
	adjust the volume boost on the fly, you just have to make sure 
	that the boost factor stays at a low frequency compared to the 
	sound you want to play, and that you get absolutely <em>no
	overflows</em> -- those will always sound much worse than the 
	improvement you're trying to achieve.<br>
	As sound volume perception is logarithmic, using arithmetic shifts is usually
	enough. If your data is signed, you should explicitly code the shift as
	a division, as C <code>&gt;&gt;</code> operator is not portable on 
	signed data.
	</p>
	<p>
	If all else fails, you should at least try to provide the user with 
	a volume scaling option.
	</p>

	<h3><font color=#0000e0>Audio performance</font></h3>
	<p>
	Low-end applications usually don't have much to worry about.  Keep in
	mind that some of us do use OpenBSD on low-end 68030, and that if a
	sound application can run on that, it should.
	</p>

	<p>
	Don't forget to run benches.  Theoretical optimizations are just that:
	theoretical. Some hard figures should be collected to check what's a 
	sizeable improvement, and what's not.
	</p>

	<p>
	For high performance audio applications, such as mpegI-layer3, some 
	points should be taken into account:
	<ul>
	    <li>The audio interface does provide you with the natural hardware
	    blocksize. Using multiples of that for your output buffer is
	    essential.  Keep in mind that <code>write</code>, as a system call,
	    incurs a high cost compared to internal audio processing.

	    <li>Bandwidth is a very important factor when dealing with audio.
	    A useful way to optimize an audio player is to see it as a
	    decompressor. The longer you can keep with the compressed data, the
	    better usually. Very short loops that do very little processing are
	    usually a bad idea. It is generally much better to combine all 
	    processing into one loop.

	    <li>Some formats do incur more overhead than others. The
	    <code>AUDIO_GETENC</code> <code>ioctl</code> should be used 
	    to retrieve all formats that the audio device provides. 
	    Be especially aware of the 
	    <code>AUDIO_ENCODINGFLAG_EMULATED</code> flag. If your
	    application is already able to output all kinds of weird formats,
	    and reasonably optimized for that, try to use a native format at
	    all costs. On the other hand, the emulation code present in the
	    audio device can be assumed to be reasonably optimal, so don't
	    replace it with quickly hacked up code.
	</ul>
	</p>

	<p>A model you may have to follow to get optimal results is to first
	compile a small test program that enquires about the specific audio
	hardware available, then proceed to configure your program so that it
	deals optimally with this hardware. You may reasonably expect people
	who want good audio performance to recompile your port when they change
	hardware, provided it makes a difference.
	</p>

	<h3><font color=#0000e0>Real time or synchronized</font></h3>
	<p>
	Considering that OpenBSD is not real time, you may still wish to write
	audio applications that are mostly real time, for instance games. In
	such a case, you will have to lower the blocksize so that the sound
	effects don't get out of synch with the current game.  The problem
	with this if that the audio device may get starved, which yields
	horrible results.
	</p>
	<p>
	In case you simply want audio to be synchronized with some graphics
	output, but the behavior of your program is predictable, synchronization
	is easier to achieve. You just play your audio samples, and ask the
	audio device what you are currently playing with
	<code>AUDIO_GETOOFFS</code>, then use  that information to
	post-synchronize graphics.  Provided you ask sufficiently often (say,
	every tenth of a second), and as long as you have enough horse-power to
	run your application, you can get very good synchronization that way.
	You might have to tweak the figures by a constant offset, as there is
	some lag between what the audio reports, what's currently playing, and
	the time it takes for XWindow to display something.
	</p>
  <h2><font color=#e00000>Contributing code back</font></h2>
   <p>In the case of audio applications, working with the original program's
	author is very important. If his code does only work with soundblaster
	cards for instance, there is a good chance he will have to cope with
	other technology soon.
	</p>

	<p>
	<strong>If you don't sent your comments to him by then, your work will
	have been useless</strong>.</p>

	It may also be that the author has already noticed whatever problems
	you are currently dealing with, and is addressing them in his current
	development tree.  If the patches you are writing amount to more than
	a handful of lines, cooperation is almost certainly a very good idea.


  <hr>
  <a href="porting.html"><img height=24 width=24 src=back.gif
   border=0 alt=Porting></a> 
  <a href=mailto:www@openbsd.org>www@openbsd.org</a>
<br><small>$OpenBSD: audio-port.html,v 1.7 2002/06/18 01:44:05 jsyn Exp $</small>
 </body>
</html>