[OpenBSD]

Przenoszenie oprogramowania audio do systemu OpenBSD

Ten dokument dotyczy tylko tematu digitalizacji dźwięku (sampled sound). Część dotycząca syntezy oraz wavetable (waveform tables) jest pożądana.

Programy audio są trudne do zaadoptowania, jako iż jest to problem nieustandaryzowania interface'ów, jakkolwiek podejście do tego problemu nie różni się zbytnio pomiędzy systemami operacyjnymi.

Użycie ossaudio

Emulacja ossaudio jest możliwie najprostszym rozwiązaniem, lecz nie będzie zawsze działać. Obecnie nie jest także najlepszym rozwiązaniem.

Użycie istniejącego kodu z NetBSD lub FreeBSD

Od czasu kiedy rozpoczęliśmy wymianę interfaców audio z NetBSD oraz FreeBSD, rozpoczęcie pracy od zaimportowania poru NetBSD jast dobtym pomyslem. Uważaj jednak, gdyż częśc plików znajduje się w innych lokalizacjach, a niektóre wpisy w sys/audioio.h są nieaktualne. Także niektóre porty mają tendencje do błędów w kodzie oraz są specyficzne dla określonej architektury sprzętowej. Dlatego też wamagane jast dokonanie zmian w kodzie. Przeczytaj uważnie poniższy paragraf.

Pisanie kodu pod OpenBSD

Niezależność sprzętowa

NIE POWINIENEŚ SUGEROWAĆ SIĘ UŻYWANYMI PRZEZ SIEBIE KATRAMI AUDIO.
Błędnym kodem jest taki kod, który sprawdza tylko pole a_info.play.precision w celu ustalenia rozdzielczości próbek 8 lub 16 bitowych oraz zakłada arytmetykę bez lub ze znakiem opartą na zachowaniu kart soundblaster. Powinieneś sprawdzić dokładny rodzaj sampla i kodować stosując otrzymane informacje. Prosty przykład:

    AUDIO_INIT_INFO(&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, &a_info);
    if (error)
	/* deal with it */
    error = ioctl(audio, AUDIO_GETINFO, &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 */
	

Jest to możliwie najkrótszy fragment kodu, który uwzględnia większość tych zagadnień.

Format 16 bitowy formats i kolejność bajtów

Przy normalnym użyciu, po prostu pytasz o typ kodowania liczb (np., AUDIO_ENCODING_SLINEAR), wtedy otrzymujesz odpowiedź o kolejności bajtów (np., AUDIO_ENCODING_SLINEAR_LE). Rozważając fakt, że karta dźwiękowa nie używa tej samej kolejności bajtów co twoja platworma, powinieneś być na to przygotowny. Najprostszym sposobem jest najprawopodobniej przygotowanie pełnego bufora audio i użycie swab(3) jeśli zamiana bajtów jest potrzebna. Użycie zewnętrzych sampli sprowadza się zwykle do wykorzystania następującej procedury:
  1. Parsowanie formatu sampla,
  2. Odczyt sampla,
  3. Zamiana kolejności bajtów, jeśli nie jest on w natywnym formacie,
  4. przeliczenie tego co chcesz wysłać do bufora,
  5. Zamiana kolejności bajtów, jeśli karta dźwiękowa nie jest w twoim natywnym formacie,
  6. Odtworzenie danych z bufora.
Oczywiście możesz pominąć kroki 3 i 5, jeśli tylko odgrywasz dźwięk zapisany w natywnym formacie twojej karty dźwiękowej.

Jakość dźwięku

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 ?

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 Hz/stereo barrier and check the results.

Sampling frequency

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.

Dynamic range

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.

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 no overflows -- those will always sound much worse than the improvement you're trying to achieve.
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 >> operator is not portable on signed data.

If all else fails, you should at least try to provide the user with a volume scaling option.

Audio performance

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.

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.

For high performance audio applications, such as mpegI-layer3, some points should be taken into account:

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.

Real time or synchronized

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.

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 AUDIO_GETOOFFS, 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.

Contributing code back

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.

If you don't sent your comments to him by then, your work will have been useless.

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.
Porting www@openbsd.org
Originally [OpenBSD: audio-port.html,v 1.7]
Translation: $ audio-port.html,v 1.3 2003/02/14 15:36:00 Stygmat Exp $
$OpenBSD$