讓 PC 喇叭也能夠放出漂亮的音樂
前言

    現在的電腦配備中 ,VGA已經是蠻普遍的顯示器 ,而電腦音效也從
以往的PC喇叭躍升為Adlib卡、聲霸卡 ,但是為什麼筆者會想介紹PC喇
叭呢 ,因為電腦裡面一定有喇叭 ,如果寫的程式不是電腦遊戲的話 ,
一般PC喇叭就夠用了 ,尤其是撰寫教學軟體(俗稱CAI)更是把PC喇叭視
為標準配備 ;到底PC喇叭能不能發出比擬音效卡的音樂呢 ,本文將為
您介紹一番。


PC喇叭撥放VOC檔:

    .VOC檔是聲霸卡在錄音時 ,將音樂的類比訊號轉換成數位訊號 ,再
把這些數位化的資料儲存起來。問題是PC喇叭只能夠做ON/OFF的動作 ,
並不能夠做類比的變化 ,那麼應該要如何才能夠放音呢 ?

    PC喇叭可以放出五線譜上音樂 ,其原理只是將喇叭做ON/OFF的動作
罷了 ,例如想產生高音的話 ,只需將喇叭的ON/OFF動作加快 ,就可以產
生較尖銳的聲音 ,同理如果要產生低音 ,只需放慢ON/OFF喇叭的速度 ;
在比較尖銳的音效裡 ,輸出的能量比較多 ,是不是我們可以加以利用 ?


DEBUG TEST.VOC
-D 100
2A82:0100  43 72 65 61 74 69 76 65-20 56 6F 69 63 65 20 46  Creative Voice F
2A82:0110  69 6C 65 1A 1A 00 0A 01-29 11 01 E5 06 00 83 01  ile.....)..e....
                                                     ^^取樣值
2A82:0120  80 FF FB 28 7A 45 B2 CC-62 F8 37 3D 32 B9 07 91  ..{(zE2Lbx7=29..
2A82:0130  F4 C8 77 A4 F2 EE 70 78-99 71 F2 B6 11 8F AC 74  tHw$rnpx.qr6..,t


    用DEBUG來看看VOC檔 ,不難發現檔頭有個"Creative Voice File"的
字眼 ,再看看取樣頻率 ,其計算方式 3E8h/(100h-取樣值83h)=取樣頻率
1000/(256-131)=8 ...... 代表此檔為8K取樣。

軟體規化:
        OUT     43h,90h                 ;啟始喇叭
        OUT     61h,13h                 ;

        OUT     42h,00h  ←─────┐ ;撥放VOC資料
        OUT     42h,資料 ── loop ─┘ ;(迴圈)

        OUT     43h,B6h                 ;
        OUT     42h,33h                 ;關閉喇叭結束
        OUT     42h,05h                 ;
        OUT     61h,10h                 ;


用PC喇叭撥放VOC檔的程式列表(PLAYVOC.ASM):
code    segment
        assume  cs:code
        org     100h
start   proc    near
        jmp     next
mark1   db      '┌─────────────────────┐',0ah,0dh
        db      '│Sound Blaster .VOC Player for PC Speacker.│',0ah,0dh
        db      '│                                          │',0ah,0dh
        db      '│                      Written By Werong Ho│',0ah,0dh
        db      '└─────────────────────┘',0ah,0dh,'$'
vochead db      'Creative Voice File',00h
jp      db      0ah,0dh,'$'
msg_1   db      'Digit-Data Filename : $'
msg_2   db      'Please Input Digitized Song Filename (SOUNDATA.DA)',0ah,0dh
        db      '-> $'
msg_3   db      'Please input Recording Frequency (6-22, default=$'
msg_4   db      '):$'
msg_5   db      'Hit any key to start to play .',0ah,0dh,'$'
msg_6   db      'Playing... (ESC:Stop    Space:Pause )',0ah,0dh,'$'
msg_7   db      'Can not open Digitized Song File.',0ah,0dh,'$'
msg_8   db      'file is not D/A Format !',0ah,0dh,'$'
msg_9   db      '00$'
msg_10  db      'Memory not enough than 160K !',0ah,0dh,'$'

old_8   dw      0000h,0000h
old_1b  dw      0000h,0000h
old_23  dw      0000h,0000h
old_24  dw      0000h,0000h

counter1 dw     0000h                   ;計時器 ,每發生幾次int8就
counter2 dw     0000h                   ;執行一次原來的int_8
clock   dw      0000h                    ;修改int_8中斷速率
keyin   db      20h,00h                 ;輸入VOC檔名
        db      100h dup (0)            ;
head1   dw      00h                      ;讀檔時的handel暫存區
keyclk  db      03h,00h,00,00,00,00,00  ;輸入取樣頻率
defclock dw     0000h                   ;讀出的VOC檔取樣頻率
select  db      00h                     ;選擇使用0-1那一區段
buffer1 dw      0000h                   ;64K區段
block1  dw      0000h                   ;區段資料數
write1  dw      0000h                   ;已用資料數
buffer2 dw      0000h                   ;64K區段
block2  dw      0000h                   ;區段資料數
write2  dw      0000h                   ;已用資料數
buffer3 db      100h dup (?)            ;讀取VOC檔頭資料暫存區
stop    db      00h                     ;暫停
break   db      00h                     ;錯誤判定
no_data db      00h                     ;已無.VOC資料可讀

new_1b :
new_23 :
        mov     byte ptr cs:break,01h
        iret
new_24 :
        mov     byte ptr cs:break,01h
        mov     al,00h
        iret

new_8 :
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
        cmp     byte ptr cs:stop,01h
        jz      L4
        mov     ax,cs
        mov     ds,ax
        cmp     byte ptr ds:select,00h
        jnz     L1
        mov     ax,ds:block1
        mov     bx,ds:write1
        cmp     ax,bx
        jz      L1
        mov     ax,ds:buffer1
        mov     es,ax
        inc     word ptr ds:write1
        jmp     L2
L1 :
        mov     byte ptr ds:select,01h
        mov     ax,ds:block2
        mov     bx,ds:write2
        cmp     ax,bx
        jz      L5
        mov     ax,ds:buffer2
        mov     es,ax
        inc     word ptr ds:write2
        jmp     L2
L5 :
        mov     byte ptr ds:select,00h
        jmp     L4
L2 :
        mov     al,00h
        out     42h,al
        mov     al,es:[bx]
        out     42h,al
L4 :
        mov     al,20h
        out     20h,al
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        dec     word ptr cs:counter1
        jz      L6
        pop     ax
        iret
L6 :
        mov     ax,cs:counter2
        mov     cs:counter1,ax
        pop     ax
        jmp     dword ptr cs:old_8
;L3 :
        mov     byte ptr ds:stop,01h
        jmp     L5

mem_err :
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_10
        int     21h
        mov     ax,4cffh
        int     21h
next :
        mov     sp,0ff00h
        mov     ah,4ah
        mov     bx,1000h
        int     21h
        jb      mem_err
        mov     ah,48h
        mov     bx,1000h
        int     21h
        jb      mem_err
        mov     cs:buffer1,ax
        mov     ah,48h
        mov     bx,1000h
        int     21h
        jb      mem_err
        mov     cs:buffer2,ax

        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark1
        int     21h
        cmp     byte ptr es:[0080h],00h
        jz      L7
        mov     ah,09h
        mov     dx,offset msg_1
        int     21h
        mov     si,offset keyin+2
        mov     di,0082h
L9 :
        mov     al,es:[di]
        cmp     al,0dh
        jz      L8
        cmp     al,00h
        jz      L8
        mov     ds:[si],al
        inc     di
        inc     si
        jmp     L9
L8 :
        mov     byte ptr ds:[si],'$'
        mov     ah,09h
        mov     dx,offset keyin+2
        int     21h
        mov     byte ptr ds:[si],00h
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        jmp     L10
L7 :
        mov     ah,09h
        mov     dx,offset msg_2
        int     21h
        mov     ah,0ah
        mov     dx,offset keyin
        int     21h
        mov     bx,offset keyin+2
        mov     ah,00h
        mov     al,cs:keyin+1
        add     bx,ax
        mov     byte ptr cs:[bx],00h
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        jmp     L10
L10 :
        mov     ax,cs
        mov     ds,ax
        mov     ax,3d02h
        mov     dx,offset keyin+2
        int     21h
        jnb     L11
        mov     ah,09h
        mov     dx,offset msg_7
        int     21h
        mov     ax,4cffh
        int     21h
L11 :
        mov     cs:head1,ax
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,0020h
        mov     dx,offset buffer3
        int     21h
        mov     ah,3eh
        mov     bx,cs:head1
        int     21h
        mov     di,offset buffer3
        mov     si,offset vochead
L13 :
        mov     al,cs:[si]
        cmp     al,00h
        jz      L12
        mov     ah,cs:[di]
        inc     si
        inc     di
        cmp     ah,al
        jz      L13
        mov     ah,09h
        mov     dx,offset msg_8
        int     21h
        mov     ax,4cffh
        int     21h
L12 :
        mov     bx,offset buffer3+1eh
        mov     ax,cs:[bx]
        not     ax
        mov     ah,00h
        mov     bx,ax
        mov     ax,03e8h
        xor     dx,dx
        div     bx
        mov     cs:defclock,ax
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_3
        int     21h
        mov     ax,cs:defclock
        mov     si,offset msg_9+1
        mov     bx,000ah
L14 :
        xor     dx,dx
        div     bx
        add     dl,30h
        mov     cs:[si],dl
        dec     si
        or      ax,ax
        jnz     L14
        mov     ah,09h
        mov     dx,offset msg_9
        int     21h
        mov     ah,09h
        mov     dx,offset msg_4
        int     21h
        mov     ah,0ah
        mov     dx,offset keyclk
        int     21h
        cmp     byte ptr cs:keyclk+1,00h
        jz      L15
        mov     si,offset keyclk+2
        xor     ax,ax
        mov     bx,000ah
L16 :
        xor     dx,dx
        mul     bx
        mov     dl,cs:[si]
        inc     si
        sub     dl,30h
        cmp     dl,0ah
        jnb     L17
        add     ax,dx
        jmp     L16
L17 :
        xor     dx,dx
        div     bx
        mov     cs:defclock,ax
        jmp     L15
L15 :
        mov     ax,cs:defclock
        cmp     ax,0006h
        jb      L23
        cmp     ax,0017h
        jb      L24
        mov     word ptr cs:defclock,0016h
        jmp     L24
L23 :
        mov     word ptr cs:defclock,0006h
        jmp     L24
L24 :
        mov     ax,04a6h
        mov     bx,cs:defclock
        xor     dx,dx
        div     bx
        mov     cs:clock,ax
        mov     bx,0037h
        xor     dx,dx
        mul     bx
        mov     cs:counter1,ax
        mov     cs:counter2,ax
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        mov     ah,09h
        mov     dx,offset msg_5
        int     21h
        mov     ah,07h
        int     21h             ;pause
        mov     ah,09h
        mov     dx,offset msg_6
        int     21h
        NOP                     ;error free
        mov     ax,3508h
        int     21h
        mov     cs:old_8,bx
        mov     cs:old_8+2,es
        mov     ax,351bh
        int     21h
        mov     cs:old_1b,bx
        mov     cs:old_1b+2,es
        mov     ax,3523h
        int     21h
        mov     cs:old_23,bx
        mov     cs:old_23+2,es
        mov     ax,3524h
        int     21h
        mov     cs:old_24,bx
        mov     cs:old_24+2,es
        NOP
        mov     ax,cs
        mov     ds,ax
        mov     ax,251bh
        mov     dx,offset new_1b
        int     21h
        mov     ax,2523h
        mov     dx,offset new_23
        int     21h
        mov     ax,2524h
        mov     dx,offset new_24
        int     21h
        NOP
        mov     ax,3d02h
        mov     dx,offset keyin+2
        int     21h
        mov     cs:head1,ax
        mov     ax,cs:buffer1
        mov     ds,ax
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,0ffffh
        mov     dx,0000h
        int     21h
        mov     cs:block1,ax
        mov     ax,cs:buffer2
        mov     ds,ax
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,0ffffh
        mov     dx,0000h
        int     21h
        mov     cs:block2,ax

        mov     ax,cs
        mov     ds,ax
        cli
        mov     al,90h
        out     43h,al
        mov     al,13h
        out     61h,al
        mov     ax,cs
        mov     ds,ax
        cli
        mov     ax,2508h
        mov     dx,offset new_8
        int     21h
        cli
        mov     al,34h
        out     43h,al
        mov     ax,cs:clock
        out     40h,al
        xchg    ah,al
        out     40h,al
        sti
L21 :
        mov     ax,cs:block1
        mov     bx,cs:write1
        cmp     ax,bx
        jnz     L26
        mov     ax,cs:block2
        mov     bx,cs:write2
        cmp     ax,bx
        jnz     L26
        jmp     L25
L26 :
        cmp     byte ptr cs:break,01h
        jnz     L30
        jmp     L25
L30 :
        cmp     byte ptr cs:no_data,00h
        jnz     L22
        mov     ax,cs:block1
        mov     bx,cs:write1
        cmp     ax,bx
        jz      L19
        mov     ax,cs:block2
        mov     bx,cs:write2
        cmp     ax,bx
        jz      L20
L22 :
        mov     ah,01h
        int     16h
        jz      L21
        mov     ax,0000h
        int     16h
        cmp     ax,3920h
        jz      L29
        cmp     ax,011bh
        jnz     L21
        jmp     L25
L29 :
        xor     byte ptr cs:stop,01h
        jmp     L21
L19 :
        mov     ax,cs:buffer1
        mov     ds,ax
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,0fffeh
        mov     dx,0000h
        int     21h
        mov     cs:block1,ax
        mov     word ptr cs:write1,0000h
        jmp     L27
L20 :
        mov     ax,cs:buffer2
        mov     ds,ax
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,0fffeh
        mov     dx,0000h
        int     21h
        mov     cs:block2,ax
        mov     word ptr cs:write2,0000h
        jmp     L27
L27 :
        or      ax,ax
        jnz     L28
        mov     byte ptr cs:no_data,01h
L28 :
        jmp     L21

L25 :
        mov     al,0ffh
        out     40h,al
        NOP
        out     40h,al
        xor     ax,ax
        mov     es,ax
        mov     ax,cs:old_8
        mov     bx,cs:old_8+2
        cli
        mov     es:[0020h],ax
        mov     es:[0022h],bx
        sti
        mov     ax,cs:old_1b
        mov     bx,cs:old_1b+2
        cli
        mov     es:[006ch],ax
        mov     es:[006eh],bx
        sti
        mov     ax,cs:old_23
        mov     bx,cs:old_23+2
        cli
        mov     es:[008ch],ax
        mov     es:[008eh],bx
        sti
        mov     ax,cs:old_24
        mov     bx,cs:old_24+2
        cli
        mov     es:[0090h],ax
        mov     es:[0092h],bx
        sti
        mov     al,0b6h
        out     43h,al
        mov     al,33h
        out     42h,al
        mov     al,05h
        out     42h,al
        mov     al,10h
        out     61h,al
        mov     ax,4cffh
        int     21h
start   endp
code    ends
        end     start


Q:放音以後有明顯的尖叫聲 ?
A:在高速的機器下這種現像才會改善 ,不然就只好將撥放速度加快 ,
    若是自己撰寫程式的話 ,如果能避免使用INT_8 , 這種情形也有可
    能改善 ,因為這樣主機執行速度也會增加。

Q:撥音時 ,偶會聽到雜音 ?
A:因為取樣[頻率設定為8的時後 ,表示每秒需8KBYTE記憶體 ,每撥
    放64K以後 ,程式會讀取硬碟內的語音檔 ,此時會嚴重降低執行速度
     ,所以音調就變了 ,若要改善此現像 ,可改在RAM DISK下執行。




一位元數位錄放音機

    或許您會發現聲霸卡錄音出來的資料檔實在是太大了 ,對於生產軟
軟體的公司而言 ,會增加磁片數量造成生產的成本 ,所以我們又想再試
其它方法可不可以減少資料檔的內容。

    對了 ,既然喇叭發出聲音是靠ON/OFF的動作 ,如果我們改記錄這個
狀態而非音量高低 ,不就可以節省更多的空間 ,原先記錄的是八位元的
音量高低 ,現在只記錄一位元的喇叭狀態 ,這樣資料豈不可以縮小為原
先的八分之一 ,根據這個理論我們再做下一個實驗。


硬體電路:
IC LM339 (電壓比較器)
VR 100K  (三隻腳)



                     GND
                ┌──┴───────┐
                │    12              │
                ├┐                  │
                ├┘                  │
                │1   3         6    7│
                └┬─┬────┬─┬┘
                  │ VCC        │  └────┐
                  │            ↓            ☉註二
                 註一      ┌─VR──┬──-┘
                          VCC   100K   GND

         註一:接至印表埠第13號腳
         註二:輸入音源(就是音樂喇叭的那兩條線)
         註三:VCC電壓+5V ,調整VR可變電阻可調錄音品質


    我們使用印表埠當做輸入訊號的傳輸埠 ,電路上的可變電阻可以用
來調整比較電壓高低 ,當電壓高過指定的位準 ,我們記錄為"1" ,低於指
定電壓則記錄為"0" ,最後將這些訊號記錄成檔案便完成了。

    最後當您準備錄音時 ,請先執行TEST ,並調整可變電阻使其發音狀
    況達到最好 ,才使用錄音程式 REC.EXE 來錄音。


數位音效測試程式(TEST.ASM):
code    segment
        assume  cs:code,ds:code
        org     100h
start   proc    near
        jmp     next
next :
        mov     cx,0100h
L3 :
        loop    L3
        mov     ah,01h
        int     16h
        jnz     L2
        mov     dx,03bdh        ;印表埠
        in      al,dx
        test    al,10h
        jnz     L1
        mov     al,30h          ;喇叭OFF
        out     61h,al          ;
        jmp     next
L1 :
        mov     al,32h          ;喇叭ON
        out     61h,al          ;
        jmp     next
L2 :
        mov     al,30h          ;關閉喇叭並結束程式
        out     61h,al
        mov     ax,0000h
        int     16h
        mov     ax,4cffh
        int     21h
start   endp
code    ends
        end     start

=========================================================================

一位元錄音機錄音程式(REC.ASM):
code    segment
        assume  cs:code,ds:code
        org     100h
start   proc    near
        jmp     next
old_8   dw      00,00
buffer1 dw      00
buffer2 dw      00
input   db      10h,00,'                                '
head1   dw      00
playbyte dw     00
playbit db      01
error   db      'Memory not enough than 128K !',0ah,0dh,'$'
msg_1   db      'Enter file name : $'
msg_2   db      'Press [Enter] Start ,any key About.',0ah,0dh,'$'
port    dw      03bdh                   ;【輸入port】
clock1  dw      0227h
clock2  dw      0227h
new_8 :
        push    ax
        push    bx
        push    dx
        push    ds
        mov     ax,cs:buffer2
        mov     ds,ax
        mov     bx,cs:playbyte
        mov     ah,cs:playbit
        mov     dx,cs:port              ;print port
        in      al,dx
        test    al,10h
        pushf
        mov     al,ds:[bx]
        popf
        jnz     L5
        push    ax
        mov     al,30h
        out     61h,al
        pop     ax
        clc
        jmp     L6
L5 :
        push    ax
        mov     al,32h
        out     61h,al
        pop     ax
        stc
        jmp     L6
L6 :
        rcl     al,01h
        rol     ah,01h
        mov     ds:[bx],al
        cmp     ah,01h
        jnz     L7
        inc     bx
        cmp     bx,0000h
        jnz     L7
L8 :
        mov     bx,cs:buffer2
        add     bx,1000h
        mov     cs:buffer2,bx
        mov     bx,0000h
L7 :
        mov     cs:playbyte,bx
        mov     ds:[bx],al
        mov     cs:playbit,ah
        pop     ds
        pop     dx
        pop     bx
        dec     word ptr cs:clock1
        jz      L9
        mov     ax,cs:clock2
        mov     ds:clock1,ax
        mov     al,20h
        out     20h,al
        pop     ax
        iret
L9 :
        mov     al,20h
        out     20h,al
        pop     ax
        iret
mem_err :
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset error
        int     21h
        mov     ax,4cffh
        int     21h
next :
        mov     ah,4ah
        mov     bx,0400h
        int     21h
        mov     sp,03e00h
        mov     ah,48h
        mov     bx,0fffeh
        int     21h
        mov     ah,48h
        int     21h
        jb      mem_err
        mov     cs:buffer1,ax
        mov     cs:buffer2,ax
        mov     ax,3508h
        int     21h
        mov     cs:old_8,bx
        mov     cs:old_8+2,es
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_1
        int     21h
        mov     ah,0ah
        mov     dx,offset input
        int     21h
        mov     bx,offset input+2
        mov     ah,00h
        mov     al,cs:input+1
        add     bx,ax
        mov     byte ptr cs:[bx],00h
        mov     ah,3ch
        xor     cx,cx
        mov     dx,offset input+2
        int     21h
        jb      mem_err
        mov     cs:head1,ax
        nop
        mov     ax,cs
        mov     ds,ax
        mov     ax,2508h
        mov     dx,offset new_8
        int     21h
        cli
        mov     al,40h
        out     36h,al
;       mov     ax,0077h                ;10K Bit 取樣
        mov     ax,004ah                ;16K Bit 取樣
        out     40h,al
        xchg    ah,al
        out     40h,al
        sti
L3 :
        mov     ah,01h
        int     16h
        jz      L3
        mov     ax,0000h
        int     16h
        cli
        mov     al,0ffh
        out     40h,al
        out     40h,al
        sti
        mov     ax,cs:old_8+2
        mov     ds,ax
        mov     dx,cs:old_8
        mov     ax,2508h
        int     21h
        mov     ax,cs:buffer1
        mov     ds,ax
        nop
L4 :
        mov     ax,cs:buffer1
        mov     bx,cs:buffer2
        cmp     ax,bx
        jz      L2
        mov     ds,ax
        mov     ah,40h
        mov     bx,cs:head1
        mov     cx,8000h
        mov     dx,0000h
        int     21h
        mov     ah,40h
        mov     bx,cs:head1
        mov     cx,8000h
        mov     dx,8000h
        int     21h
        mov     ax,cs:buffer1
        add     ax,1000h
        mov     cs:buffer1,ax
        jmp     L4
L2 :
        mov     ax,cs:buffer1
        mov     ds,ax
        mov     ah,40h
        mov     bx,cs:head1
        mov     cx,cs:playbyte
        mov     dx,0000h
        int     21h
        mov     ah,3eh
        mov     bx,cs:head1
        int     21h
        mov     ax,4cffh
        int     21h
start   endp
code    ends
        end     start


=============================================================================
一位元數位放音機(PLAY.ASM)
code    segment
        assume  cs:code,ds:code
        org     100h
start   proc    near
        jmp     next
mark1   db      '┌────────────────────────┐',0ah,0dh
        db      '│ Digitized Sound Player PC Speaker Version 0.88 │',0ah,0dh
        db      '│ (C) Written By Werong Ho              [8 Bit]  │',0ah,0dh
        db      '└────────────────────────┘',0ah,0dh
        db      '$'
mark2   db      'Memory Space :$'
mark3   db      'Digit-Data Filename : $'
mark4   db      'Loading $'
mark5   db      'OK.',0ah,0dh,'$'
mark6   db      ' Bytes ... $'
mark7   db      'Please input Recording Frequency (1-24, default=$'
mark8   db      '):$'
mark9   db      'Hit any key to start to play .',0ah,0dh,'$'
mark10  db      0ah,0dh,'Playing... (ESC:Stop    Space:Pause )',0ah,0dh,'$'
mark11  db      'Memory : $'
mark12  db      'Please Input Digitized Song Filename (SOUNDATA.DA)',0ah,0dh
        db      '-> $'

msg_1   db      'Can not open Digitized Song File.',0ah,0dh,'$'
msg_2   db      0ah,0dh,'Base Memory not enough !',0ah,0dh,'$'
msg_3   db      0ah,0dh,'Play Over !',0ah,0dh,'$'
msg_4   db      0ah,0dh,'Ctrl-Break !',0ah,0dh,'$'
msg_5   db      0ah,0dh,'file is not D/A Format !',0ah,0dh,'$'
msg_6   db      0ah,0dh,'Escape Key Press !',0ah,0dh,'$'
msg_7   db      '  [Pause]$'
msg_8   db      '                   $'

jp      db      0ah,0dh,'$'

sourceadds dw    00,00
startadds  dw    00,00
endadds    dw    00,00
playbit db      01h
clock   dw      00
old_8   dw      00,00
old_1b  dw      00,00
old_23  dw      00,00
break   db      00
playover db     00
counter1 dw     00
counter2 dw     00

input   db      0f0h,00h
file1   db      100h dup (?)
head1   dw      00

buffer1 db      100h dup (?)
pu_ds   dw      00
pu_mem  dw      00
pu_long dw      00,00
pu_khz  db      00
stop    db      00

show_play :
        mov     ax,cs
        mov     ds,ax
        mov     al,20h
        call    clear
        mov     ax,cs:sourceadds
        mov     bx,cs:sourceadds+2
        mov     cx,cs:startadds
        mov     dx,cs:startadds+2
        sub     cx,ax
        jnb     L26
        sub     dx,1000h
L26 :
        sub     dx,bx
        test    dx,0f00h
        jz      L29
        add     cx,8000h
L29 :
        rol     dx,01h
        rol     dx,01h
        rol     dx,01h
        rol     dx,01h
        and     dx,00ffh
        mov     ax,cx
        mov     bx,0400h
        div     bx
        xor     dx,dx
        mov     si,offset buffer1+3h
        mov     byte ptr cs:[si+1],'K'
        mov     byte ptr cs:[si+2],'$'
L28 :
        xor     dx,dx
        mov     bx,000ah
        div     bx
        add     dl,30h
        mov     cs:[si],dl
        dec     si
        cmp     ax,0000h
        jnz     L28
        mov     ah,09h
        mov     dx,offset buffer1
        int     21h
        mov     dx,offset msg_8
        cmp     byte ptr cs:stop,00h
        jz      L32
        mov     dx,offset msg_7
L32 :
        mov     ah,09h
        int     21h
        ret

return :
        mov     al,0ffh
        out     40h,al
        out     40h,al
        xor     ax,ax
        mov     es,ax
        mov     ax,cs:old_8
        mov     bx,cs:old_8+2
        cli
        mov     es:[0020h],ax
        mov     es:[0022h],bx
        sti
        mov     ax,cs:old_1b+2
        mov     ds,ax
        mov     dx,cs:old_1b
        mov     ax,251bh
        int     21h
        mov     ax,cs:old_23+2
        mov     ds,ax
        mov     dx,cs:old_23
        mov     ax,2523h
        int     21h
        mov     ax,cs
        mov     ds,ax
        mov     es,ax
        ret
khz :
        push    ax
        push    bx
        push    dx
        mov     bx,ax
        mov     ax,04a6h
        xor     dx,dx
        div     bx
        mov     cs:clock,ax
        mov     ax,bx
        mov     bx,0037h
        xor     dx,dx
        mul     bx
        mov     cs:counter1,ax
        mov     cs:counter2,ax
        pop     dx
        pop     bx
        pop     ax
        ret
new_8 :
        push    ax
        push    bx
        push    cx
        push    dx
        push    ds
        push    es
        mov     ax,cs
        mov     ds,ax
        cmp     byte ptr ds:stop,01h
        jnz     L31
        jmp     L4
L31 :
        mov     ax,ds:startadds+2
        mov     es,ax
        mov     bx,ds:startadds
        mov     al,es:[bx]
        mov     ah,ds:playbit
        rol     ah,01h
        rcl     al,01h
        jnb     L1
        mov     es:[bx],al
        mov     al,32h
        out     61h,al
        jmp     L2
L1 :
        mov     es:[bx],al
        mov     al,30h
        out     61h,al
        jmp     L2
L2 :
        mov     ds:playbit,ah
        cmp     ah,01h
        jnz     L3
        inc     bx
        mov     ax,es
        cmp     bx,8000h
        jnz     L5
        add     ax,0800h
        mov     bx,0000h
L5 :
        mov     ds:startadds+2,ax
        mov     ds:startadds,bx
L3 :
        mov     ax,ds:startadds
        mov     bx,ds:startadds+2
        mov     cx,ds:endadds
        mov     dx,ds:endadds+2
        cmp     ax,cx
        jnz     L4
        cmp     bx,dx
        jb      L4
        mov     al,0ffh
        out     40h,al
        out     40h,al
        mov     byte ptr ds:playover,01h
        xor     ax,ax
        mov     es,ax
        mov     ax,ds:old_8
        mov     bx,ds:old_8+2
        cli
        mov     es:[0020h],ax
        mov     es:[0022h],bx
        sti
L4 :
        pop     es
        pop     ds
        pop     dx
        pop     cx
        pop     bx
        dec     word ptr cs:counter1
        jz      L6
        mov     al,20h
        out     20h,al
        pop     ax
        iret
L6 :
        mov     ax,cs:counter2
        mov     cs:counter1,ax
        pop     ax
        jmp     dword ptr cs:old_8

new_1b :
new_23 :
        mov     byte ptr cs:break,01h
        iret

clear :
        push    bx
        push    cx
        mov     bx,offset buffer1
        mov     cx,00ffh
L7 :
        mov     cs:[bx],al
        inc     bx
        loop    L7
        pop     cx
        pop     bx
        ret

next :
        mov     cs:pu_ds,ds
        mov     sp,03f00h
        mov     ah,4ah
        mov     bx,0800h
        int     21h
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark1
        int     21h
        mov     ah,09h
        mov     dx,offset mark2
        int     21h
        mov     al,20h
        call    clear
        mov     ah,48h
        mov     bx,0fffeh
        int     21h
        mov     cs:pu_mem,bx
        push    bx
        mov     ah,48h
        int     21h
        mov     cs:sourceadds+2,ax
        mov     cs:startadds+2,ax
        mov     cs:endadds+2,ax
        pop     bx
        mov     byte ptr cs:buffer1+4,'K'
        mov     byte ptr cs:buffer1+5,'$'

        mov     si,offset buffer1+3
        mov     ax,bx
        mov     bx,0040h
        xor     dx,dx
        div     bx
L8 :
        xor     dx,dx
        mov     bx,000ah
        div     bx
        add     dl,30h
        mov     cs:[si],dl
        dec     si
        cmp     ax,0000h
        jnz     L8
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset buffer1
        int     21h
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        nop                             ;show memory space
        mov     ds,cs:pu_ds
        mov     al,ds:[80h]
        cmp     al,00h
        jz      L9
        mov     si,offset file1
        mov     di,0081h
L11 :
        mov     al,ds:[di]
        cmp     al,20h
        jnz     L10
        inc     di
        loop    L11
L10 :
        mov     al,ds:[di]
        cmp     al,0dh
        jz      L12
        cmp     al,00h
        jz      L12
        mov     cs:[si],al
        inc     di
        inc     si
        jmp     L10
L12 :
        mov     byte ptr cs:[si],00h
        mov     byte ptr cs:[si+1],'$'
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark3
        int     21h
        mov     ah,09h
        mov     dx,offset file1
        int     21h
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        jmp     L13
L9 :
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark12
        int     21h
        mov     ah,0ah
        mov     dx,offset input
        int     21h
        mov     bx,offset input+2
        mov     ah,00h
        mov     al,cs:input+1
        add     bx,ax
        mov     byte ptr cs:[bx],00h
        mov     byte ptr cs:[bx+1],'$'
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        jmp     L13
L13 :                                   ;input file name complete
        mov     ax,3508h
        int     21h
        mov     cs:old_8,bx
        mov     cs:old_8+2,es
        mov     ax,351bh
        int     21h
        mov     cs:old_1b,bx
        mov     cs:old_1b+2,es
        mov     ax,3523h
        int     21h
        mov     cs:old_23,bx
        mov     cs:old_23+2,es
        nop                             ;get_interupt
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark4
        int     21h
        mov     ax,3d02h
        mov     dx,offset file1
        int     21h
        jnb     L14
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        mov     ah,09h
        mov     dx,offset msg_1
        int     21h
        mov     ax,4cffh
        int     21h
L14 :
        mov     cs:head1,ax
        mov     ax,4202h
        mov     bx,cs:head1
        xor     cx,cx
        xor     dx,dx
        int     21h
        mov     cs:pu_long,ax
        mov     cs:pu_long+2,dx
        mov     di,ax
        mov     si,dx
        mov     al,20h
        call    clear
        mov     bp,offset buffer1+6h
        mov     byte ptr cs:[bp+1],24h
L15 :
        mov     ax,si
        mov     bx,000ah
        xor     dx,dx
        div     bx
        mov     si,ax
        mov     ax,di
        mov     bx,000ah
        div     bx
        mov     di,ax
        add     dl,30h
        mov     cs:[bp],dl
        dec     bp
        cmp     si,0000h
        jnz     L15
        cmp     di,0000h
        jnz     L15
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset buffer1
        int     21h
        mov     ah,09h
        mov     dx,offset mark6
        int     21h
        mov     ax,4200h
        mov     bx,cs:head1
        xor     cx,cx
        xor     dx,dx
        int     21h
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,0002h
        mov     dx,offset buffer1
        int     21h
        mov     al,cs:buffer1
        cmp     al,53h
        jz      L16
        mov     ah,3eh
        mov     bx,cs:head1
        int     21h
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_5
        int     21h
        mov     ax,4cffh
        int     21h
L16 :
        mov     al,cs:buffer1+1
        mov     cs:pu_khz,al
        mov     ah,00h
        call    khz
        nop                             ;setup counter/clock
        mov     ax,cs:pu_long
        mov     bx,cs:pu_long+2
        ror     ax,01h
        ror     ax,01h
        ror     ax,01h
        ror     ax,01h
        and     ax,0fffh
        ror     bx,01h
        ror     bx,01h
        ror     bx,01h
        ror     bx,01h
        and     bx,0f000h
        add     ax,bx
        inc     ax
        mov     bx,cs:pu_mem
        cmp     ax,bx
        jb      L17
        mov     ah,3eh
        mov     bx,cs:head1
        int     21h
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_2
        int     21h
        mov     ax,4cffh
        int     21h                     ;check memory enough
L17 :
        mov     ax,cs:endadds+2
        mov     ds,ax
        mov     ah,3fh
        mov     bx,cs:head1
        mov     cx,8000h
        mov     dx,0000h
        int     21h
        cmp     ax,8000h
        jnz     L18
        mov     ax,ds
        add     ax,800h
        mov     cs:endadds+2,ax
        jmp     L17
L18 :
        mov     cs:endadds,ax
        mov     ah,3eh
        mov     bx,cs:head1
        int     21h
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark5
        int     21h
        nop                             ;loading complete
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark7
        int     21h
        mov     si,offset buffer1+1
        mov     byte ptr cs:[si+1],24h
        mov     byte ptr cs:[si],20h
        mov     byte ptr cs:[si-1],20h
        mov     al,cs:pu_khz
        mov     ah,00h
L27 :
        xor     dx,dx
        mov     bx,000ah
        div     bx
        add     dl,30h
        mov     cs:[si],dl
        dec     si
        cmp     ax,0000h
        jnz     L27
        mov     ah,09h
        mov     dx,offset buffer1
        int     21h
        mov     ah,09h
        mov     dx,offset mark8
        int     21h
        nop                                     ;show default clock
        mov     byte ptr cs:file1,00h
        mov     ah,0ah
        mov     dx,offset input
        int     21h
        mov     si,offset file1
        xor     ax,ax
L20 :
        xor     dx,dx
        mov     bx,000ah
        mul     bx
        mov     dl,cs:[si]
        inc     si
        sub     dl,30h
        cmp     dl,0ah
        jnb     L19
        mov     dh,00h
        add     ax,dx
        jmp     L20
L19 :
        xor     dx,dx
        mov     bx,000ah
        div     bx
        cmp     ax,0019h
        jnb     L21
        cmp     ax,0000h
        jz      L21
        call    khz
L21 :
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        mov     ah,09h
        mov     dx,offset mark9
        int     21h
        mov     ah,07h
        int     21h
        nop
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset mark10
        int     21h
        mov     ah,09h
        mov     dx,offset mark11
        int     21h
        nop
        mov     ax,cs
        mov     ds,ax
        mov     ax,251bh
        mov     dx,offset new_1b
        int     21h
        mov     ax,2523h
        mov     dx,offset new_23
        int     21h
        nop
        mov     ah,01h
        mov     cx,2020h
        int     10h
        mov     ax,cs
        mov     ds,ax
        mov     ax,2508h
        mov     dx,offset new_8
        int     21h
        cli
        mov     al,40h
        out     36h,al
        mov     ax,cs:clock
        out     40h,al
        xchg    ah,al
        out     40h,al
        sti
        nop
L22 :
        mov     ah,03h
        mov     bh,00h
        int     10h
        push    dx
        call    show_play
        pop     dx
        mov     ah,02h
        mov     bh,00h
        int     10h
        cmp     byte ptr cs:playover,01h
        jz      L23
        cmp     byte ptr cs:break,01h
        jz      L24
        mov     ah,01h
        int     16h
        jz      L22
        mov     ax,0000h
        int     16h
        cmp     ax,3920h
        jnz     L30
        xor     byte ptr cs:stop,01h
        jmp     L22
L30 :
        cmp     ax,011bh
        jnz     L22
        call    return
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_6
        int     21h
        jmp     L25
L23 :
        call    return
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_3
        int     21h
        jmp     L25
L24 :
        call    return
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset msg_4
        int     21h
        jmp     L25
L25 :
        mov     ah,01h
        mov     cx,0d0eh
        int     10h
        mov     ax,cs
        mov     ds,ax
        mov     ah,09h
        mov     dx,offset jp
        int     21h
        mov     ax,4cffh
        int     21h
start   endp
code    ends
        end     start



[軟體使用說明]
REC.EXE 是錄音程式 ,程式第16行是使用的PORT
        第135及136行是取樣頻率設定 ,前面加分號表示不使用 ,預設值
        是使用16K Bit取樣。

        鍵入後 ,再輸入檔名即開始錄音 ,錄音時會順便放音給您聽 ,本
        程式啟動後 ,會讓電腦時間停止 ,以求得高速的錄音效果 ,並請
        您使用較高速的機器錄音(筆者使用386 54MHz) ,以免當機。

   錄音程式限用主記憶體 ,所以盡可能空出多一點記憶體供軟體使用 ,當
   使用10K Bit取樣 ,每秒約用1.2K BYTE空間 ,若使用16K Bit取樣 ,每
   秒需2K BYTE空間 ,若主記憶體不夠用 ,可能會當機。

TEST.EXE測試錄音效果 ,程式會把接收到的資料放出來 ,並不會錄起來 ,
        此動作是為了讓使用者能調整錄音品質 ,及調整可變電阻至合適
        的響應頻率 ,以得到較好的錄音品質。

PLAY.EXE是放音程式 ,請將完整的錄音資料檔檔頭用PCTOOLS修改一下 ,第
        一個BYTE擺53h ,用以識別此檔是語音資料檔 ;第二個BYTE請修改
        成您的錄音取樣頻率 ,例如您的取樣頻率是10K Bit ,請填入0Ah ,
        若您是取樣16K Bit ,請填入 10h。

    數位語音放因軟體 ,以Bit為單位做取樣 ,取樣頻率約為
16100.612 Bit/Sec ,也就是每秒要用掉約2K Bytes來放音。
  (一般16K Bit取樣 ,錄音4分50秒左右約需580K記憶體)

結論:
    我們在錄音、放音的實驗中 ,如果採VOC的錄音方式 ,即使再小聲
我們都可以錄的到 ,可是放音時如果主機速度不夠快 ,常會聽到尖銳的
聲音 ,其為模擬的音效無法確實還原 ;如果使用後者的錄音方式 ,可以
輕鬆的錄出蠻真實的音樂 ,雖比不上聲霸卡的真實 ,但表現的效果最好
 ,同時資料檔也是最小。

小恐龍工作坊 提供