Snd. Sound (?!) - JulTob/Ada GitHub Wiki

16.17 Linux Sound

The Linux sound capabilities, called OSS, were developed by 4front technologies.You can find more advanced documentation at their website http://www.opensound.com. This section describes only the basic functions.

The newest Linux sound standard is ALSA.

Most distributions have OSS in the kernel by default, but there's no reason that OSS must be present--it can always be turned off for computers without a sound card. 16.17.1 Detecting a Sound Card

Open the file /dev/sndstatus. If there is no error, the computer has a sound card. 16.17.2 Playing Sound Samples

There are no C libraries or kernel calls to play sound samples. Instead, there is a device file called /dev/audio which plays sound samples in the .au sound format.

The .au sound format consists of a header describing the sound followed by the actual sound data. The header looks like this:

type AAUHeader is record Magic : integer; -- a unique number denoting a .au file, -- as used with the magic file, SND_MAGIC -- Hex 646E732E (bytes 2E, 73, 6E, 64) dataLocation : integer; -- offset or pointer to the sound data dataSize: integer; -- number of bytes of sound data dataFormat: integer; -- the data format code samplingRate : integer; -- the sampling rate channelCount : integer; -- the number of channels info1, info2, info3, info4 : character;-- name of sound end record;

dataLocation is an offset to the first byte of the sound data. If there's no sound name, it's 28, the size of the header. It can a pointer to the data, depending on the dataFormat code, but that doesn't apply if you're playing a .au file.

dataSize is the size of the sound data in bytes, not including the header.

dataFormat describes how the sound data is to be interpreted. Here is a table of some common values. Value Code Format 0 SND_FORMAT_UNSPECIFIED unspecified format 1 SND_FORMAT_MULAW_8 8-bit mu-law samples 2 SND_FORMAT_LINEAR_8 8-bit linear samples 3 SND_FORMAT_LINEAR_16 16-bit linear samples 4 SND_FORMAT_LINEAR_24 24-bit linear samples 5 SND_FORMAT_LINEAR_32 32-bit linear samples 6 SND_FORMAT_FLOAT floating-point samples 7 SND_FORMAT_DOUBLE double-precision float samples 8 SND_FORMAT_INDIRECT fragmented sampled data 10 SND_FORMAT_DSP_CORE DSP program 11 SND_FORMAT_DSP_DATA_8 8-bit fixed-point samples 12 SND_FORMAT_DSP_DATA_16 16-bit fixed-point samples 13 SND_FORMAT_DSP_DATA_24 24-bit fixed-point samples 14 SND_FORMAT_DSP_DATA_32 32-bit fixed-point samples 16 SND_FORMAT_DISPLAY non-audio display data 18 SND_FORMAT_EMPHASIZED 16-bit linear with emphasis 19 SND_FORMAT_COMPRESSED 16-bit linear with compression 20 SND_FORMAT_COMPRESSED_EMPHASIZED Combo of the two above 21 SND_FORMAT_DSP_COMMANDS Music Kit DSP commands

SamplingRate is the playback rate in hertz.CD quality samples are 44100.

channelCount is 1 for mono, 2 for stereo.

The info characters are a C null-terminated string giving a name for the sound. It's always at least 4 characters long, even if unused.

In order to play a sound, treat /dev/audio as if it were a device attached to your computer for playing .au sounds.Write a program to open /dev/audio for writing and write the .au sound to it.

Playing sounds is a natural candidate for multithreading because you don't want your entire program to stop while a sound is being played.

The following program uses the seqio generic package we developed above to play an .au sound through /dev/audio.

with seqio; with Ada.Text_IO; use Ada.Text_IO;

procedure playsnd is

-- simple program to play an .au sound file

package byteio is new seqio( short_short_integer ); -- sequential files of bytes

au_filename : constant string := "beep.au"; -- sound file to play. supply the name of the .au file to play

au_file: byteio.AFileID; -- the sound file dev_audio: byteio.AFileID; -- /dev/audio device

soundbyte : short_short_integer;

begin

Put_Line( "Playing " & au_filename & "...");

-- open the files

au_file := byteio.Open( au_filename, read => true); dev_audio := byteio.Open( "/dev/audio", read => false);

-- read until we run out of bytes, send all bytes to -- /dev/audio.The end of file will cause a seqio_error

begin -- nested block to catch the exception

loop
  byteio.Read( au_file, soundbyte );
  byteio.Write( dev_audio, soundbyte );
end loop;

exception when byteio.seqio_error => null; -- just leave block end;

-- close files

byteio.Close( au_file ); byteio.Close( dev_audio );

Put_Line( "All done" );

exception when others => Put_Line( "Oh, oh! An exception occurred!" ); byteio.Close( au_file ); byteio.Close( dev_audio ); raise;

end playsnd;

16.17.3 Using the Mixer

You control the mixer chip, if your sound card has one, by using the ioctl() kernel call. If there is no mixer, the ioctl() function returns -1. Mixer Functions Table SOUND_MIXER_NRDEVICES 17 Number of mixer functions on this computer SOUND_MIXER_VOLUME 0 The master volume setting SOUND_MIXER_BASS 1 Bass setting SOUND_MIXER_TREBLE 2 Treble setting SOUND_MIXER_SYNTH 3 FM synthesizer volume SOUND_MIXER_PCM 4 /dev/dsp volume SOUND_MIXER_SPEAKER 5 internal speaker volume, if attached to sound card SOUND_MIXER_LINE 6 "line in" jack volume SOUND_MIXER_MIC 7 microphone jack volume SOUND_MIXER_CD 8 CD input volume SOUND_MIXER_IMIX 9 Recording monitor volume SOUND_MIXER_ALTPCM 10 volume of alternate codec, on some cards SOUND_MIXER_RECLEV 11 Recording level volume SOUND_MIXER_IGAIN 12 Input gain SOUND_MIXER_OGAIN 13 Output gain SOUND_MIXER_LINE1 14 Input source 1 (aux1) SOUND_MIXER_LINE2 15 Input source 2 (aux2) SOUND_MIXER_LINE3 16 Input source 3 (line)

Reading or writing values have a special bit set [Ken check using soundcard.h].

Ioctl calls return an integer value. Volume levels can be 0 to 100, but many sound cards do not offer 100 levels of volume. /dev/mixer will set the volume to setting nearest to your requested volume.

[Not complete--KB]

Sound_mixer_volume : constant integer := 0; Sound_Mixer_Read : constant integer := ?; Sound_Mixer_Write : constant integer := ?;

oldVolume : integer;

ioctlResult := Ioctl( mixer_file_id, sound_mixer_read + sound_mixer_volume, oldVolume );

-- master volume now in oldVolume

if ioctlResult = -1 then Put_Line( "No mixer installed " ); end if;

newVolume := 75;

ioctlResult := ioctl( mixer_file_id, sound_mixer_write + sound_mixer_volume, newVolume );

-- master volume is 75%

16.17.4 Recording Sound Samples

Recording sounds works is the reverse process of playing sounds. Open /dev/dsp for reading, and it returns sound data. However, before you can begin recording from /dev/dsp, you need to describe how you want the recording done. You need to select the input source, sample format (sometimes called as number of bits), number of channels (mono/stereo) , and the sampling rate (speed). These are set by using the ioctl function on the /dev/dsp file.

To select the input source, you'll need to use /dev/mixer.

[Not finished--KB

Sound_Mixer_Recsrc : constant integer := ?; Sound_Mixer_Read : constant integer := ?; Sound_Mixer_Write : constant integer := ?;

newInputSource := Sound_Mixer_Mic;

ioctlResult := ioctl( mixer_file_id, sound_mixer_write + sound_mixer_recsrc, newInputSource );

Common formats

/* Audio data formats (Note! U8=8 and S16_LE=16 for compatibility) */ AFMT_QUERY 16#00000000# Returns current format AFMT_IMA_ADPCM 16#00000004# ADPCM compressed data AFMT_U8 16#00000008# Unsigned bytes AFMT_S16_LE 16#00000010# Little endian signed 16 bits AFMT_S16_BE 16#00000020# Big endian signed 16 bits AFMT_S8 16#00000040# Signed bytes AFMT_U16_LE 16#00000080# Little endian U16 bits AFMT_U16_BE 16#00000100# Big endian U16 bits AFMT_MPEG 16#00000200# MPEG (2) audio

sndctl_dsp_setfmt : constant integer := ?;

newFormat : integer;

newFormat := 16#0000010#;

ioctlResult := ioctl( dsp_id, sndctl_dsp_setfmt, newFormat ); -- recording format now 16 bit signed little endian

if newFormat /= 16#00000010 then Put_Line( "Sound card doesn't support recording format" ); end if;

Selecting mono or stereo recording is a matter of 0 or 1 respectively.

sndctl_dsp_stereo : constant integer := ?;

stereo : integer;

stereo := 1;

...

ioctlResult := ioctl( dsp_id, sndctl_dsp_stereo, stereo ); -- recording format now stereo

if stereo /= 1 then Put_Line( "Sound card doesn't support stereo" ); end if;

Finally, select a sampling rate.

sndctl_dsp_speed : constant integer := ?;

newSpeed : integer;

newSpeed:= 44100;

ioctlResult := ioctl( dsp_id, sndctl_dsp_speed, newFormat ); -- recording now CD quality sampling speed

if newSpeed /= 44100 then Put_Line( "Sound card doesn't support sampling speed" ); end if;

Now read /dev/dsp for the raw sound data. If you want to save the sound as an .au file, you'll have to create the .au header information to attach to the sound data.