Page 1 of 1

Sound

Posted: Sat May 11, 2002 12:03 am
by Peter Sejfried
Hello,

I think, that every OS should have routines to play sounds. (WAV, MOD, MP3 etc.). But Here is very big trouble. How to init your Sound Card? Of course OS is being write in 32 - bit PMode.

Does anyone know how to initialze every sound card which is compatible with Sound Blaster?

I know that I can download e.g. SBINIT.COM but then I must have different SBINIT to every Sound Blaster Card. I ask about universal Sound Blaster Driver like VBE (Vesa for Graphic Card) or how to init it using assembler.

Best Regards
Peter

Re:Sound

Posted: Sat May 11, 2002 4:43 pm
by Tim
Something like the Linux drivers for your sound card might be the best source of information like this; you're lucky if your sound card can emulate something standard like the Sound Blaster. But you're pretty much stuck if your sound card requires a proprietary init program even for Linux operation.

Re:Sound

Posted: Sun May 12, 2002 1:07 am
by K.J.
For Soundblaster cards, check out Creative's opensource section:

http://opensource.creative.com/

Hope that helps,
K.J.

Re:Sound

Posted: Sun May 12, 2002 3:35 pm
by Peter Sejfried
Ok!!! Thanks K.J. I'll check it at the moment.

Re:Sound

Posted: Sun May 12, 2002 4:39 pm
by f2
Is sound something hard to program into your OS? I haven't gotten there yet...

I'm still trying to set up the GDT and switch to PMode! My computer keeps restarting every time I do?!

Re:Sound

Posted: Sat May 18, 2002 1:08 pm
by crazybuddha
Tommy wrote: Is sound something hard to program into your OS? I haven't gotten there yet...

I'm still trying to set up the GDT and switch to PMode! My computer keeps restarting every time I do?!
Programming for the internal speaker is not that bad. Building drivers for sound cards will drive you to drink. It's been awhile since I've worked on it, so perhaps there is more standardization now and better references, but frankly I doubt it.

Re:Sound

Posted: Tue May 21, 2002 8:24 pm
by DynatOS

Re:Sound

Posted: Thu Jun 13, 2002 1:01 pm
by Christopher
Once up on a time, I tried to write a MIDI-player in plain assembler. Okey, I never got the player to work, but I found this BASIC-code under the coding time:

; SBDetected = 0
; FOR port = &H210 TO &H280 STEP &H10
; OUT port + &H6, 1
; FOR Count = 1 TO 100
; OUT port + &H6, 0
; Stat = INP(port + &HE)
; Stat = INP(port + &HA)
; IF Stat = &HAA THEN EXIT FOR
; NEXT Count
; IF Stat = &HAA THEN SBDetected = 1: BasePort = port: EXIT FOR
; NEXT port
; IF SBDetected = 0 THEN
; PRINT "Sorry, this program requires a SoundBlaster compatible sound card!"
; PRINT : END
; END IF

So I wrote a likely routine in assembler:

FM.Init:
Pusha
Mov   CX, 0x200

FM.InitLoop1:
Add   CX, 0x10
Mov   DX, CX
Add   DX, 0x6
Mov AL, 1
Out DX, AL
Mov   BH, 0

FM.InitLoop2:
Inc   BH
Mov   DX, CX
Add   DX, 0x6
Mov AL, 0
Out DX, AL

Mov   DX, CX
Add   DX, 0xE
In   AL, DX
Mov   DX, CX
Add   DX, 0xA
In   AL, DX
Cmp   AL, 0xAA
Je   FM.InitLoop2Done

Cmp   BH, 100
Jne   FM.InitLoop2
Jmp   FM.InitLoop2Done2

FM.InitLoop2Done:
Mov   Word [FM.BasePort], CX
Mov   CX, 0x280

FM.InitLoop2Done2:
Cmp   CX, 0x280
Jne   FM.InitLoop1
Popa
Ret

And that code worked for me, maybe it'll work for you. Remember that it only finds SoundBlaster compatible sound cards and it's baseadress.

Best Regards
// Christopher Vigren

Re:Sound

Posted: Mon Jun 17, 2002 12:57 am
by K.J.
Now that I think about it, lots of BASIC programs have soundblaster routines in them. Check out:
http://www.allbasiccode.com/

To find some(UGH! I still can't believe how bad a job whoever took over that site did with the design and color choices).

K.J.

Re:Sound

Posted: Fri Jun 21, 2002 9:03 am
by crazybuddha
I just finished a little experiment in which a sound is played during kernel setup (sort of like displaying a message. What I was actually shooting for was a barebones implementation. I'm going to share here basically what I did for this.

First of all, I found a tiny WAV file. Then I wrote a small C program to read the size of the data from the header, then strip out the header by writing the data to another file. This isn't actually necessary, but I just wanted the raw sample data to read off a floppy into a specific spot in memory

//--------------------------------------------
FILE *fp;
   FILE *out;
   char data[4096]; // bigger than WAV file
   int data_size[1];
   
   if( !(fp = fopen( "in.wav", "r" )))
      exit(0);
   
   if( !(out = fopen( "out.wav", "w" )))
      exit(0);

   fseek( fp, 40, 0 );

   fread( data_size, sizeof(int), 1, fp);

   fread( data, sizeof(char), *data_size, fp);

   fwrite( data, sizeof(char), *data_size, out);

   fclose( fp );
   fclose( out );
//--------------------------------------------

Then I used partcopy to put in on the floppy after the setup code (which in this case was two sectors after the boot sector). My stripped file was 678 bytes long.

partcopy out.wav 0 678 -f0 600

Within the boot loader, I read four sectors off the floppy into a particular spot in memory (0x1300). Four sectors was enough to get all of the 678 bytes of data.

//--------------------------------------------
   mov bx, 0x1300 ; destination offset
   mov al, 4 ; number of sectors to read
   mov cl, 4 ; sector start
   
   call read_sectors   

read_sectors:
   mov ah, 0x02   ; read sectors function
   mov ch, 0      ; cylinder 0
   mov dl, [bootdrv]    ; drive number
   mov dh, 0      ; head number
   int 0x13      ; call bios disk i/o
jc read_sectors
            
   mov dx,0x3f2 ; Turn off floppy motor
   mov al,0x0c
out dx,al

   ret
//--------------------------------------------

Now that I had sound samples at a particular spot in memory, I needed to set up my SB-compatible sound card, the DMA for transferring the data from memory, and write an ISR for responding to the interrupt the sound card will generate when it's finished processing the data.

The base port for SB could be anywhere from 0x220 - 0x280. I was lucky. It was 0x220. Once you have the base port, the other ports are relative to it. The way you detect a SB-compatible DSP chip is to reset by writing "1" to the Reset port (base + 0x06), waiting, then writing "0" to it. Then you poll bit 7 in the Read-Buffer Status port (base + 0x0E) until it's set. Finally you read a byte from the Read Data port (base + 0x0A). If that byte is 0xAA, then you're all set. Otherwise, you have to try a different base port or you don't have an SB-compatible chip. If the following code is a bit cryptic, sorry. I didn't clean it up.

//--------------------------------------------
   mov dx, 0x220   ; base port
   add dx, 0x06
   mov al, 1
   out dx, al

   sub al, al
waitloop:
   dec al
   jnz waitloop
   
   out dx, al
   
   xor cx, cx
   add dl, 8   ; 0xE
nodata:
   in al, dx
   or al, al
   jns again
   
   sub dl, 4   ; 0xA
   in al, dx
   jmp finis
   
again:
   loop nodata

finis:
   mov [reg32], eax ; show the byte read
   call printreg32 ; handle error here

//--------------------------------------------

In my case, when I ran this much, I saw the 000000AA on the screen, so I knew I was good to proceed. Once the DSP has been reset like this, you can write and read from it. I wrote a macro to simplify the many calls I would make.

;--------------------------------------------
%macro DSP_WRITE 1
   mov ah, byte %1
   call write_dsp
%endmacro
;--------------------------------------------

Heres the actual routine for writing a byte to the DSP. You keep checking the Write Status port (base + 0x0C) until bit 7 is clear, then you send the byte to the same port

;--------------------------------------------
write_dsp:
   push dx
   mov edx, 0x220
   add dx, 0x0C
@write_busy:
   in al, dx
   or al, al   ; ALSO and al, 0X80 jnz @busy
   js @write_busy
   
   mov al, ah ; AH contains byte to write
   out dx, al
   pop dx
   ret
;--------------------------------------------

Here's a series of commands for the DSP.

;--------------------------------------------

   DSP_WRITE 0x40   ; set sample rate
   DSP_WRITE 165   ; 11025 Hz ( 256 - (1000000/rate))
   DSP_WRITE 0x41   ; enable speaker
   DSP_WRITE 0xD0   ; pause 8-bit DMA
   DSP_WRITE 0xD5   ; pause 16-bit DMA

;--------------------------------------------

Then I was ready to set up the DMA. Oops, I forget to say that I set up a ISR for IRQ5 (which I knew was the interrupt from my control settings in NT).

Re:Sound

Posted: Fri Jun 21, 2002 9:06 am
by crazybuddha
;--------------------------------------------
dsp_isr:
???push ax
???push dx
???
???mov???edx,0x220
???add???edx,0x0E
???in???al,dx
???mov???eax,0x20
???out???0x20,al
???
???pop dx
???pop ax
???iret
;--------------------------------------------

Here's the DMA setup.

;--------------------------------------------

???mov???eax,1??????; mask out the DMA channel while we reprogram it
???add???eax,4
???out???0x0a,al

???xor???eax,eax??????; clear byte pointer flip-flop
???out???0x0c,al

???mov???eax,01001001b??????; mode
???out???0x0b,al
???
???mov edx, 1??????; channel
???shl edx, 1??????; trick to get address port from channel #
???
???mov ax, 0x1300???; FIXME hardcoded offset
???out dx, al???;
???mov al, ah???; loaded in two stages
???out dx, al
???
???inc edx??????; set the size (length - 1)
???mov ax, 0x677???; FIXME hardcoded length
???out dx, al???;
???mov al, ah???; loaded in two stages
???out dx, al
???
???mov al, 0???; page=0
???out 0x83, al???; 8-bit Page Register for Channel 1

???mov???eax,1??????; unmask the DMA channel 1
???out???0x0a,al

;--------------------------------------------

Then, I was ready to tell the DSP to get funky:

;--------------------------------------------
???DSP_WRITE 0x14???; start 8-bit DMA transfer
???DSP_WRITE 0x77???; low byte of (length - 1)
???DSP_WRITE 0x06???; high byte of (length - 1)
???
;--------------------------------------------

That's it! It worked the first time out. On my machine. YMMV...a lot.

Re:Sound

Posted: Fri Jun 21, 2002 9:29 am
by Pype.Clicker
modern chipsets (including mine :) seems to have builtin AC97 sound chip. Did anyone here already has some code for that chip ?

Re:Sound

Posted: Fri Jun 21, 2002 10:18 am
by crazybuddha
I think if someone coughed up a comparable minimal implementation using something like PCI DMA and AC97, it would be a welcome contribution.

My crusty old example suits my aging hardware setup. My interests tend toward lower bandwidth solutions, so I'm not likely to go buy any new hardware any time soon.