Author Topic: syscall to produce sound under linux?  (Read 9404 times)

nobody

  • Guest
syscall to produce sound under linux?
« on: May 20, 2005, 06:48:42 PM »
Hello everybody,

what syscall under linux do i need so that my computer can play sounds of a particular pitch and duration and how can i insert pauses of a particular duration?

I looked through systemcall-lists but couldn't find a call that looked like it could work except for sys_nanosleep (ax=162) (but i don't understand why it needs two (and not just one) parameters for duration).

Thanks and regards,
Uwe

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: syscall to produce sound under linux?
« Reply #1 on: May 20, 2005, 07:33:31 PM »
Hi Uwe,

> what syscall under linux do i need so that my computer can play sounds of a
> particular pitch and duration and how can i insert pauses of a particular
> duration?

There exists a "/dev/speaker", so I suspect you may be able to write to the speaker as a file. I don't know how to do it. If you've got a soundcard (I don't), you might find a "/dev/" entry for that, too (???). If you discover any info on that, please share it.

What I know how to do, is use sys_ioperm to get permission to access the ports, and then use the ports to beep the speaker (at a desired frequency - the duration is determined by turning it back off)

> I looked through systemcall-lists but couldn't find a call that looked like
> it could work except for sys_nanosleep (ax=162) (but i don't understand why
> it needs two (and not just one) parameters for duration).

Yeah, you'll need that one. IIRC, it's seconds and nanoseconds - the nanoseconds field shouldn't go over 999,999,999...

This is as far as I've gotten with it - lots of room for improvement. Works on my machine... The forum software is going to mangle the formatting of this horribly!

Best,
Frank


; Plays a tune, of sorts - sound, anyway.
; It is said that I'm not artistic.
;
; nasm -f elf [-g] play.asm
; ld [-s] -o play play.o
;
; to be able to run without being root:
; as root, "chown root:root play" "chmod +s play"

section .text
global _start
_start:
    nop      ; parking place for gdb

mov eax, 101   ; sys_ioperm
    mov ebx, 42h   ; starting port
    mov ecx, 20h   ; how many ports
    mov edx, 3     ; read/write
    int 80h

test eax, eax
    jns okay_ioperm
    neg eax
    call showeax
    mov ebx, eax
    jmp exit

okay_ioperm:

; set up PIT
    mov al, 10111110b
           ;.......|------ binary 16-bit counter
           ;....|||------- square wave generator
           ;..||---------- r/w bits 0-7, then 8-15
           ;||------------ select counter 2
    out 43h, al

mov esi, melody

playit_sam:      ; if she can take it, so can I.
    in al, 61h                  ; enable speaker
    or al, 00000011b
          ;.......|------- enable speaker on timer 2 gate
          ;......|-------- enable speaker data
    out 61h, al

lodsd                       ; get a note
    or eax, eax                 ; check for zero
    jz egress                   ; coda

out 42h, al                 ; play it
    mov al, ah
    out 42h ,al

lodsd                       ; for this long
    call delay

in al, 61h                  ; Turn Speaker OFF
    and al, 11111100b
    out 61h, al

lodsd                       ; for this long
    call delay

jmp short playit_sam        ; play more

egress:
    in al, 61h
    and al, 0FCh                ; Turn Speaker OFF. Please.
    out 61h, al

mov eax, 101   ; sys_ioperm
    mov ebx, 42h
    mov ecx, 20h
    mov edx, 0     ; give back ports
    int 80h

test eax, eax
    jns okay_exit
    neg eax
    call showeax
    mov ebx, eax
    jmp exit

okay_exit:

xor ebx, ebx
exit:
    mov eax, 1     ; sys_exit
    int 80h
;------------------

;------------------
delay:
    push ebx
    push ecx

cmp eax, byte 1
    jz .no_delay

xor ebx, ebx
.check_ns
    cmp eax, 1000000000   ; limit for nanosleep 999999999 ns...
    jb .okay_ns
    sub eax, 1000000000
    inc ebx
    jmp short .check_ns
.okay_ns

push eax     ; set up a time_spec structure on the stack
    push ebx  

.take_a_nap
    mov eax, 162    ; sys_nanosleep
    mov ebx, esp    ; same structure for "request"
    mov ecx, esp    ; and "remaining" - does this work?
    int 80h
    cmp eax, -4     ; -EINTR
    jz .take_a_nap

add esp, byte 8     ; remove time_spec "structure"

.no_delay:
    pop ecx
    pop ebx
    ret
;------------------

;-----------------------------------
; showeax - prints a decimal representation of eax to stdout
; expects - number to print in eax
; returns - nothing
;------------------------------------
showeax:
    pushad

xor edi, edi     ; counter
    mov esi, 10      ; divide by ten

push esi         ; double as LF :)
    inc edi
.top
    xor edx, edx     ; zero edx for the divide
    div esi          ; quotient in eax, remainder in edx
    add dl, '0'      ; convert number to ascii char
    push edx         ; save it
    inc edi          ; bump counter
    or eax, eax      ; is quotient zero?
    jnz .top         ; if not, do more

.print
    mov eax, 4       ; sys_write
    mov ebx, 1       ; stdout
    mov ecx, esp     ; character's on the stack
    mov edx, 1       ; just one
    int 80h          ; do it
    pop edx          ; get rid of the character we've printed
    dec edi          ; decrement counter
    jnz .print       ; 'til done

popad
    ret
;-----------------------    

section .data

; Some equates, for your compositional assistance
; It is said that I'm not artistic.

A0 equ 21728
    B0 equ 19328
    C0 equ 18244
    D0 equ 16144
    E0 equ 14080
    F0 equ 13668
    G0 equ 12176
    A1 equ 10864
    B1 equ 9664
    C1 equ 9122
    D1 equ 8072
    E1 equ 7040
    F1 equ 6834
    G1 equ 6088
    A2 equ 5432
    B2 equ 4832
    C2 equ 4561
    D2 equ 4063
    E2 equ 3520
    F2 equ 3417
    G2 equ 3044
    A3 equ 2712
    B3 equ 2416
    C3 equ 2280
    D3 equ 2032
    E3 equ 1810
    F3 equ 1708
    G3 equ 1522
    A4 equ 1356
    B4 equ 1208
    C4 equ 1140
    D4 equ 1016
    E4 equ 905
    F4 equ 854
    G4 equ 762
    A5 equ 678
    B5 equ 604
    C5 equ 558

; Some "durations" for sys_nanosleep
    Z equ 1
    S equ 60000000
    Q equ 120000000
    P equ 240000000
    R equ 480000000
    L equ 1920000000

; The opus itself.
;
; It is said that I'm not artistic.
;
; Format is "note", "duration", "pause", "note", ... , 0
; Write your own!

melody:

dd G2, P, Z, E2, Q, Z, G2, Q, Q, G1, Q, Q
       dd G1, Q, Q, C2, Q, Q, B2, Q, Z, C2, Q, Z
       dd D2, Q, Q, G2, Q, Q, A3, Q, Z, G2, Q, Z
       dd A3, Q, Q, A2, Q, Q, A2, Q, Q, G2, Q, Q
       dd E2, Q, Z, G2, Q, Z, F2, Q, Z
       dd E2, Q, Z, D2, Q, Q, F2, Q, Z, G2, Q, Z
       dd A3, Q, Z, G2, Q, Z, F2, Q, Z, E2, Q, Z
       dd F2, Q, Z, E2, Q, Z, D2, Q, Z, B2, Q, Z
       dd C2, Q, Z, D2, Q, Z, E2, Q, Z, C2, Q, Z
       dd D2, Q, Q, G3, Q, Z, F3, Q, Z, E3, Q, Z
       dd D3, Q, Z, C3, Q, Z, B3, Q, Z, A3, Q, Z
       dd B3, Q, Z, C3, Q, Z, D3, Q, Z, B3, Q, Q
       dd G0, Q, Q, G0, R, Z, 0

nobody

  • Guest
Re: syscall to produce sound under linux?
« Reply #2 on: July 14, 2005, 04:17:57 PM »
Hi Frank,

thanks for your listing.

Sorry I didn't reply earlier but I didn't have much time lately to spend with assembly programming.

There are several lines I don't understand yet.

Why do you need "nop ; parking place for gdb" at the beginning?
Is port 42h the port for the speaker and why do you need 20h ports?
For "set up PIT" you use port 43h, to enable the speaker  port 61h and for sending a note 42h?
What's the meaning of "10111110b" after "set up PIT"? I read your comments but didn't really understand them.
And "00000011b" for port 61h enables 2 speakers?

You wrote after ".take_a_nap":
mov eax, 162 ; sys_nanosleep
mov ebx, esp ; same structure for "request"
mov ecx, esp ; and "remaining" - does this work?
int 80h

Why not:
mov eax, 162
pop ebx
pop ecx
int 80h
?
And you won't need "add esp, byte 8" then.
(And in case you use "add esp, byte 8" why "byte 8"? Isn't esp a doubleword?)


Do you have the time to answer all these questions.....  

Best, Uwe :-)

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: syscall to produce sound under linux?
« Reply #3 on: July 15, 2005, 01:39:32 AM »
Hi Uwe,

> thanks for your listing.

You're welcome.

> Sorry I didn't reply earlier but I didn't have much time lately to spend with
> assembly programming.

Sometimes life comes along and intrudes on the important things - like assembly programming. :)

> There are several lines I don't understand yet.
>
> Why do you need "nop ; parking place for gdb" at the beginning?

Well, you don't. If gdb is going to be able to "step" from the beginning, it likes to have a one-byte instruction to replace with a breakpoint. So I sometimes throw in a "nop" at the beginning of my programs, just to make sure it's got one. I've found it makes gdb happier, but it isn't really a necessary part of the "program".

> Is port 42h the port for the speaker and why do you need 20h ports?

42h is the Programmable Interrupt Timer. I imagine it would have made a better program to call sys_ioperm twice, once for 42h - 43h and again for 60h - 62h (or whatever we actually need). It just seemed easier to grab the whole range at once. Keep in mind, it was my first attempt to use ports in Linux - there are probably a lot of things wrong with it.

> For "set up PIT" you use port 43h, to enable the speaker  port 61h and for sending
> a note 42h?
> What's the meaning of "10111110b" after "set up PIT"? I read your comments but
> didn't really understand them.

Well, they got crunched by the forum software, which doesn't help... For full info on which bit does what, on which port, I'd recommend Ralf Brown's Interrupt List - "ports.lst", not the interrupt list itself. http://pobox.com/~ralf - or google for it if I've remembered that wrong.

Essentially, we're telling the timer chip that we want counter 2, and how we want it set up. I don't know what all the options are. If there's a "sine wave" option, it probably sounds better, but I don't think it's on the menu. Lookitup. Or experiment.

> And "00000011b" for port 61h enables 2 speakers?

Ummm, it enables one speaker (only one I've got, on my systerm), and says we want to to follow counter 2 on the PIT - I think. Again, "ports.lst", for more info.

> You wrote after ".take_a_nap":
> mov eax, 162 ; sys_nanosleep
> mov ebx, esp ; same structure for "request"
> mov ecx, esp ; and "remaining" - does this work?
> int 80h
>
> Why not:
> mov eax, 162
> pop ebx
> pop ecx
> int 80h
> ?

'Cause it doesn't do the same thing. I guess I confuse people when I use "push" to build a structure on the stack. We want two structures - one for time requested, and one for time remaining. Each structure consists of a dword for seconds, and a dword for nanoseconds. A pointer to one in ecx, and a pointer to the other in ebx. If sys_nanosleep is interrupted, it'll return -EINTR, and the "remaining" structure is filled in with the time remaining. I've tried to use just one structure, on the stack, for both - so if this happens, the "request" structure will be overwritten by time remaining, and re-used when we re-call sys_nanosleep. I have no idea if this all works. Don't know how to test it. Set some busywork going, sleep for a long time, and compare against a stopwatch, I suppose. Works good enough for beepin' the speaker, anyway.

What you're proposing puts seconds and nanoseconds in ecx and ebx. When sys_nanosleep tries to use these as addresses, it'll almost certainly segfault.

> And you won't need "add esp, byte 8" then.

That's true.

> (And in case you use "add esp, byte 8" why "byte 8"? Isn't esp a doubleword?)

Yeah... "add" is one of several instructions that have a "signed byte" form. The operand is stored as a byte, and sign-extended to a dword (or word, in 16-bit code) before it's used. Just use -O999 on the command-line and let Nasm do it.

> Do you have the time to answer all these questions.....  

No. Well, not "in full". There's a lot to it! Glad to try to help out a little bit, though. :)

Best,
Frank