Writing sound drivers for KolibriOS
Intro
Layers
An application wich wants to output sound has to:
- create a buffer using the SND_CREATE_BUFF service routine of Infinity.
- Set the format of the buffer using the SND_SET_FORMAT service routine of Infinity.
- Start playing using the SND_PLAY service routine of Infinity.
- Update the sound samples in the buffer.
Infinity gets the samples from all sound buffers of all applications that output sound and mixes them together in one buffer wich is then send to sound.obj.
When Infinity is called for the first time, it inits itself and then calls sound.obj wich also initialises. Infinity calls the DEV_CALLBACK service routine of sound.obj to insert the address of the callback function to the [ctrl.user_callback] variable. Sound.obj is responsible for calling this callback function every time the buffers need to be refilled. The driver, sound.obj will do this when it gets an interrupt from the soundcard. Before doing this, the interrupt handler should check to see if the interrupt was generated because of an 'end of buffer' event. It is possible that there are multiple, e.g. 2, buffers (like in HDA) wich sound.obj needs to fill, one after the other. In this case, one buffer is given to Infinity to refill it with new portion of samples, while the second is being outputed by souncard.
Here is an example HDA IRQ handler, where you can see the piece of code that changes buffers and calls the callback function of Infinity. (I have selected that piece by ;!!!!!!!!!!! [ .... ;!!!!!!!!!!! ] brackets).
<syntaxhighlight lang="asm"> align 4 proc hda_irq ;+
- if DEBUG_IRQ
- mov esi, msgIRQ
- call SysMsgBoardStr
- end if
mov edx, ICH6_REG_INTSTS call azx_readl test eax, eax jnz @f popa ret @@:
- !!!!!!!!!!! [
mov ebx, eax ; status mov eax, 1 shl 4 test ebx, eax jz @f
mov al, SD_INT_MASK mov edx, ICH6_REG_SD_STS + SDO0_SD_OFFSET call azx_writeb
mov eax, [civ_val] inc eax and eax, 1 mov [civ_val], eax
mov ebx, dword [buff_list+eax*4] cmp [ctrl.user_callback], 0 je @f stdcall [ctrl.user_callback], ebx
- !!!!!!!!!!! ]
@@:
; clear rirb int mov edx, ICH6_REG_RIRBSTS call azx_readb
test al, RIRB_INT_MASK jz .l1 test al, RIRB_INT_RESPONSE jz @f
call azx_update_rirb @@: mov al, RIRB_INT_MASK mov edx, ICH6_REG_RIRBSTS call azx_writeb .l1:
ret
endp </syntaxhighlight>
Sound.obj is also responsible for creating the sound buffers that will be read by souncard and filled in by Infinity.
Every buffer has to be page aligned and be exactly 16Kbytes.
So for two buffers we would have:
<syntaxhighlight lang="asm">
stdcall KernelAlloc, 0x8000 mov [ctrl.buffer], eax
mov edi, eax mov ecx, 0x8000/4 xor eax, eax cld rep stosd
</syntaxhighlight>
Infinity does not need to know how the sound is outputed to the sound card, it is the matter of sound.obj to operate with it. All that Infinity has to know is address of the sound buffer and when to fill it (all this information is given to it by sound.obj). This approach gives us HAL (hardware abstraction layer) wich makes the KolibriOS sound system universal and usefull for any soundcard.