Writing sound drivers for KolibriOS

From KolibriOS wiki
Jump to: navigation, 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).

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


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:

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

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.