Writing sound drivers for KolibriOS

From KolibriOS wiki
Revision as of 13:34, 24 January 2011 by Hidnplayr (talk | contribs)
Jump to navigation Jump to search

Intro

Layers

Sound layers.gif

An application wich wants to output sound has to:

  1. create a buffer using the SND_CREATE_BUFF service routine of Infinity.
  2. Set the format of the buffer using the SND_SET_FORMAT service routine of Infinity.
  3. Start playing using the SND_PLAY service routine of Infinity.
  4. 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).

<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 </asm>


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:

<asm>

     stdcall KernelAlloc, 0x8000
     mov [ctrl.buffer], eax
     mov edi, eax
     mov ecx, 0x8000/4
     xor eax, eax
     cld
     rep stosd

</asm>

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.