Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Sign In with Facebook Sign In with Google Sign In with OpenID

Categories

We have migrated to a new platform! Please note that you will need to reset your password to log in (your credentials are still in-tact though). Please contact lee@programmersheaven.com if you have questions.
Welcome to the new platform of Programmer's Heaven! We apologize for the inconvenience caused, if you visited us from a broken link of the previous version. The main reason to move to a new platform is to provide more effective and collaborative experience to you all. Please feel free to experience the new platform and use its exciting features. Contact us for any issue that you need to get clarified. We are more than happy to help you.

Sound in TP7.0

SettunSettun Posts: 8Member
Good day to you all, I'm currently attempting to port over some Basic code into pascal and I've encountered a stumbling block.

In basic the command 'Sound(Frequency,Duration)' produces a sound from the PC speaker of some defined frequency for some specified duration.

Okay now for the problem:
Convert that same line of basic code to generate a similar sound from the Adlib blaster card as seen in Tp7.0 running in Windows 7, under a Dos box.

Thanks in advance.

Comments

  • _Atex__Atex_ Posts: 163Member
    : Good day to you all, I'm currently attempting to port over some
    : Basic code into pascal and I've encountered a stumbling block.
    :
    : In basic the command 'Sound(Frequency,Duration)' produces a sound
    : from the PC speaker of some defined frequency for some specified
    : duration.
    :
    : Okay now for the problem:
    : Convert that same line of basic code to generate a similar sound
    : from the Adlib blaster card as seen in Tp7.0 running in Windows 7,
    : under a Dos box.
    :
    : Thanks in advance.
    :


    http://www.bsdg.org/swag/SOUND/0105.SBDSP2B.ZIP
  • SettunSettun Posts: 8Member
    I appreciate your feedback, however, this method requires the use of pre-recorded sound samples in external files. What I'm actually trying to accomplish is be able to specify the frequency and duration of each sound(note) I wish to produce from within the actual program without the use of external sound samples.

    Thanks for any help that can be provided.
  • SettunSettun Posts: 8Member
    After more web searching I've found this website which has...



    Overview
    Two of the most popular sound cards for the IBM-PC, the AdLib and the Sound Blaster, suffer from a real dearth of clear documentation for programmers. AdLib Inc. and Creative Labs, Inc. both sell developers' kits for their sound cards, but these are expensive, and (in the case of the Sound Blaster developers' kit) can be extremely cryptic.

    This document is intended to provide programmers with a FREE source of information about the programming of these sound cards.

    The information contained in this document is a combination of information found in the Sound Blaster Software Developer's Kit, and that learned by painful experience. Some of the information may not be valid for AdLib cards; if this is so, I apologize in advance.

    Please note that numbers will be given in hexadecimal, unless otherwise indicated. If a number is written out longhand (sixteen instead of 16) it is in decimal.


    Chapter One - Sound Card I/O
    The sound card is programmed by sending data to its internal registers via its two I/O ports:

    0388 (hex) - Address/Status port (R/W)
    0389 (hex) - Data port (W/O)
    The Sound Blaster Pro is capable of stereo FM music, which is accessed in exactly the same manner. Ports 0220 and 0221 (hex) are the address/ data ports for the left speaker, and ports 0222 and 0223 (hex) are the ports for the right speaker. Ports 0388 and 0389 (hex) will cause both speakers to output sound.

    The sound card possesses an array of two hundred forty-four registers; to write to a particular register, send the register number (01-F5) to the address port, and the desired value to the data port.

    After writing to the register port, you must wait twelve cycles before sending the data; after writing the data, eighty-four cycles must elapse before any other sound card operation may be performed.

    The AdLib manual gives the wait times in microseconds: three point three (3.3) microseconds for the address, and twenty-three (23) microseconds for the data.

    AdLib suggested that the delays be produced by reading the register port six times after writing to the register port, and reading the register port thirty-five times after writing to the data port. With the advent of much faster machines, however, this method is not necessarily reliable.

    The sound card registers are write-only.

    The address port also functions as a sound card status byte. To retrieve the sound card's status, simply read port 388. The status byte has the following structure:

    7 6 5 4 3 2 1 0
    both
    timers timer
    1 timer
    2 unused
    Bit 7 - set if either timer has expired.
    6 - set if timer 1 has expired.
    5 - set if timer 2 has expired.

    Chapter Two - The Registers
    The following table shows the function of each register in the sound card. Registers will be explained in detail after the table. Registers not listed are unused.

    Address Function
    01 Test LSI / Enable waveform control
    02 Timer 1 data
    03 Timer 2 data
    04 Timer control flags
    08 Speech synthesis mode / Keyboard split note select
    20..35 Amp Mod / Vibrato / EG type / Key Scaling / Multiple
    40..55 Key scaling level / Operator output level
    60..75 Attack Rate / Decay Rate
    80..95 Sustain Level / Release Rate
    A0..A8 Frequency (low 8 bits)
    B0..B8 Key On / Octave / Frequency (high 2 bits)
    BD AM depth / Vibrato depth / Rhythm control
    C0..C8 Feedback strength / Connection type
    E0..F5 Wave Select
    The groupings of twenty-two registers (20-35, 40-55, etc.) have an odd order due to the use of two operators for each FM voice. The following table shows the offsets within each group of registers for each operator.

    Channel 1 2 3 4 5 6 7 8 9
    Operator 1 00 01 02 08 09 0A 10 11 12
    Operator 2 03 04 05 0B 0C 0D 13 14 15
    Thus, the addresses of the attack/decay bytes for channel 3 are 62 for the first operator, and 65 for the second. (The address of the second operator is always the address of the first operator plus three).

    To further illustrate the relationship, the addresses needed to control channel 5 are:

    29 Operator 1 AM/VIB/EG/KSR/Multiplier
    2C Operator 2 AM/VIB/EG/KSR/Multiplier
    49 Operator 1 KSL/Output Level
    4C Operator 2 KSL/Output Level
    69 Operator 1 Attack/Decay
    6C Operator 2 Attack/Decay
    89 Operator 1 Sustain/Release
    8C Operator 2 Sustain/Release
    A4 Frequency (low 8 bits)
    B4 Key On/Octave/Frequency (high 2 bits)
    C4 Feedback/Connection Type
    E9 Operator 1 Waveform
    EC Operator 2 Waveform

    Explanations of Registers

    Byte 01 This byte is normally used to test the LSI device. All bits should normally be zero. Bit 5, if enabled, allows the FM chips to control the waveform of each operator.

    7 6 5 4 3 2 1 0
    unused WS unused

    Byte 02 Timer 1 Data. If Timer 1 is enabled, the value in this register will be incremented until it overflows. Upon overflow, the sound card will signal a TIMER interrupt (INT 08) and set bits 7 and 6 in its status byte. The value for this timer is incremented every eighty (80) microseconds.

    Byte 03 Timer 2 Data. If Timer 2 is enabled, the value in this register will be incremented until it overflows. Upon overflow, the sound card will signal a TIMER interrupt (INT 08) and set bits 7 and 5 in its status byte. The value for this timer is incremented every three hundred twenty (320) microseconds.

    Byte 04 Timer Control Byte

    7 6 5 4 3 2 1 0
    IRQ
    Reset Tmr1
    Mask Tmr2
    Mask unused Tmr2
    Ctrl Tmr3
    Ctrl

    bit 7 - Resets the flags for timers 1 & 2. If set, all other bits are ignored.
    bit 6 - Masks Timer 1. If set, bit 0 is ignored.
    bit 5 - Masks Timer 2. If set, bit 1 is ignored.
    bit 1 - When clear, Timer 2 does not operate.
    When set, the value from byte 03 is loaded into Timer 2, and incrementation begins.
    bit 0 - When clear, Timer 1 does not operate.
    When set, the value from byte 02 is loaded into Timer 1, and incrementation begins.

    Byte 08 CSM Mode / Keyboard Split.

    7 6 5 4 3 2 1 0
    CSM
    sel Key
    spl unused

    bit 7 - When set, selects composite sine-wave speech synthesis mode (all KEY-ON bits must be clear). When clear, selects FM music mode.
    bit 6 - Selects the keyboard split point (in conjunction with the F-Number data). The documentation in the Sound Blaster manual is utterly incomprehensible on this; I can't reproduce it without violating their copyright.

    Bytes 20-35 Amplitude Modulation / Vibrato / Envelope Generator Type / Keyboard Scaling Rate / Modulator Frequency Multiple

    7 6 5 4 3 2 1 0
    Amp
    Mod Vib EG
    Typ KSR Modulator Frequency
    Multiple

    bit 7 - Apply amplitude modulation when set; AM depth is controlled by the AM-Depth flag in address BD.
    bit 6 - Apply vibrato when set; vibrato depth is controlled by the Vib-Depth flag in address BD.
    bit 5 - When set, the sustain level of the voice is maintained until released; when clear, the sound begins to decay immediately after hitting the SUSTAIN phase.
    bit 4 - Keyboard scaling rate. This is another incomprehensible bit in the Sound Blaster manual. From experience, if this bit is set, the sound's envelope is foreshortened as it rises in pitch.
    bits 3-0 - These bits indicate which harmonic the operator will produce sound (or modulation) in relation to the voice's specified frequency:
    0 - one octave below
    1 - at the voice's specified frequency
    2 - one octave above
    3 - an octave and a fifth above
    4 - two octaves above
    5 - two octaves and a major third above
    6 - two octaves and a fifth above
    7 - two octaves and a minor seventh above
    8 - three octaves above
    9 - three octaves and a major second above
    A - three octaves and a major third above
    B - three octaves and a major third above
    C - three octaves and a fifth above
    D - three octaves and a fifth above
    E - three octaves and a major seventh above
    F - three octaves and a major seventh above

    Bytes 40-55 Level Key Scaling / Total Level

    7 6 5 4 3 2 1 0
    Scaling
    Level 24
    dB 12
    dB 6
    dB 3
    dB 1.5
    dB .75
    dB

    bits 7-6 - causes output levels to decrease as the frequency rises:
    00 - no change
    10 - 1.5 dB/8ve
    01 - 3 dB/8ve
    11 - 6 dB/8ve
    bits 5-0 - controls the total output level of the operator. All bits CLEAR is loudest; all bits SET is the softest. Don't ask me why.

    Bytes 60-75 Attack Rate / Decay Rate

    7 6 5 4 3 2 1 0
    Attack
    Rate Decay
    Rate

    bits 7-4 - Attack rate. 0 is the slowest, F is the fastest.
    bits 3-0 - Decay rate. 0 is the slowest, F is the fastest.

    Bytes 80-95 Sustain Level / Release Rate

    7 6 5 4 3 2 1 0
    Sustain
    Level Release
    Rate

    bits 7-4 - Sustain Level. 0 is the loudest, F is the softest.
    Bit 7 - 24 dB
    Bit 6 - 12 dB
    Bit 5 - 6 dB
    Bit 4 - 3 dB
    bits 3-0 - Release rate. 0 is the slowest, F is the fastest.

    Bytes A0-B8 Octave / F-Number / Key-On

    7 6 5 4 3 2 1 0
    F-Number (least significant byte)
    (A0-A8)

    7 6 5 4 3 2 1 0
    Unused Key
    On Octave F-Number
    most sig.
    (B0-B8)

    bit 5 - Channel is voiced when set, silent when clear.
    bits 4-2 - Octave (0-7). 0 is lowest, 7 is highest.
    bits 1-0 - Most significant bits of F-number.
    In octave 4, the F-number values for the chromatic scale and their corresponding frequencies would be:

    F Number Frequency
    (decimal) Note
    16B 277.2 C#
    181 293.7 D
    198 311.1 D#
    1B0 329.6 E
    1CA 349.2 F
    1E5 370.0 F#
    202 392.0 G
    220 415.3 G#
    241 440.0 A
    263 466.2 A#
    287 493.9 B
    2AE 523.3 C

    Bytes C0-C8 Feedback / Algorithm

    7 6 5 4 3 2 1 0
    unused Feedback Decay
    Alg

    bits 3-1 - Feedback strength. If all three bits are set to zero, no feedback is present. With values 1-7, operator 1 will send a portion of its output back into itself. 1 is the least amount of feedback, 7 is the most.
    bit 0 - If set to 0, operator 1 modulates operator 2. In this case, operator 2 is the only one producing sound. If set to 1, both operators produce sound directly. Complex sounds are more easily created if the algorithm is set to 0.

    Byte BD Amplitude Modulation Depth / Vibrato Depth / Rhythm

    7 6 5 4 3 2 1 0
    AM
    Dep Vib
    Dep Rhy
    Ena BD SD TOM Top
    Cym HH

    bit 7 - Set: AM depth is 4.8 dB
    Clear: AM depth is 1 dB
    bit 6 - Set: Vibrato depth is 14 cent
    Clear: Vibrato depth is 7 cent
    bit 5 - Set: Rhythm enabled (6 melodic voices)
    Clear: Rhythm disabled (9 melodic voices)
    bit 4 - Bass drum on/off
    bit 3 - Snare drum on/off
    bit 2 - Tom tom on/off
    bit 1 - Cymbal on/off
    bit 0 - Hi Hat on/off

    Note: KEY-ON registers for channels 06, 07, and 08 must be OFF in order to use the rhythm section. Other parameters such as attack/decay/sustain/release must also be set appropriately.

    Bytes E0-F5 Waveform Select

    7 6 5 4 3 2 1 0
    unused Waveform
    Select

    bits 1-0 - When bit 5 of address 01 is set, the output waveform will be distorted according to the waveform indicated by these two bits:



    Detecting a Sound Card

    According to the AdLib manual, the 'official' method of checking for a sound card is as follows:

    Reset both timers by writing 60h to register 4.
    Enable the interrupts by writing 80h to register 4.
    NOTE: this must be a separate step from number 1.
    Read the status register (port 388h). Store the result.
    Write FFh to register 2 (Timer 1).
    Start timer 1 by writing 21h to register 4.
    Delay for at least 80 microseconds.
    Read the status register (port 388h). Store the result.
    Reset both timers and interrupts (see steps 1 and 2).
    Test the stored results of steps 3 and 7 by ANDing them with E0h. The result of step 3 should be 00h, and the result of step 7 should be C0h. If both are correct, an AdLib-compatible board is installed in the computer.

    Making a Sound
    Many people have asked me, upon reading this document, what the proper register values should be to make a simple sound. Well, here they are.

    First, clear out all of the registers by setting all of them to zero. This is the quick-and-dirty method of resetting the sound card, but it works. Note that if you wish to use different waveforms, you must then turn on bit 5 of register 1. (This reset need be done only once, at the start of the program, and optionally when the program exits, just to make sure that your program doesn't leave any notes on when it exits.)

    Now, set the following registers to the indicated value:

    REGISTER VALUE DESCRIPTION
    20 01 Set the modulator's multiple to 1
    40 10 Set the modulator's level to about 40 dB
    60 F0 Modulator attack: quick; decay: long
    80 77 Modulator sustain: medium; release: medium
    A0 98 Set voice frequency's LSB (it'll be a D#)
    23 01 Set the carrier's multiple to 1
    43 00 Set the carrier to maximum volume (about 47 dB)
    63 F0 Carrier attack: quick; decay: long
    83 77 Carrier sustain: medium; release: medium
    B0 31 Turn the voice on; set the octave and freq MSB

    To turn the voice off, set register B0h to 11h (or, in fact, any value which leaves bit 5 clear). It's generally preferable, of course, to induce a delay before doing so.

    ___________________________________________________________________

    Okay so now I've found the ports to set, 388, 389.. I've found the list of registers listed right above this... And I'm making the leap of faith that the note will continue until I actually turn it off by setting B0h to 11h, so by inducing a delay before I set B0h to 11h (however this is accomplished) I should get my duration of the note. I've found the list of frequencies for the notes... How now do I send this information to the sound card to produce say the D# as outlined?

    Anyone like to take a go at coding this? Let's play the note for 15 seconds for a simple number.

    Thanks,
    Settun
  • _Atex__Atex_ Posts: 163Member
    : After more web searching I've found this website which has...
    :
    ......................................................................
    :
    : Anyone like to take a go at coding this? Let's play the note for 15
    : seconds for a simple number.
    :

    Here's a piece of code which does pretty much what you're looking for except is much more watered down, thus simple:[code][color=Blue]{ *** Compiler: TP7, works equally with DOSBox or NTVDM *** }


    program midi_demo_through_MPU401_chip;

    uses crt;

    const on=true;
    off=false;
    octave:shortint=-4; { octave offset }
    tempo:word=400; { tempo in msecs }
    pc_speaker:boolean=false;{ for testing only }
    beat:array[0..9] of real=(
    1,2,3,4,0.5,0.333,0.25,0.125,1.333,1.5);

    sample_tune:array[1..240] of byte=(
    255, 0, 2, 8, 2, 5, 8, 2, 5, 8, 2, 5,
    8, 7, 1, 9, 2, 1, 9, 0, 5, 9, 9, 5,
    9, 9, 5, 9, 7, 1, 9, 2, 0, 9, 0, 5,
    9, 11, 5, 9, 9, 5, 9, 7, 1, 9, 2, 0,
    9, 0, 5, 9, 11, 5, 9, 0, 5, 9, 9, 1,
    8, 2, 5, 8, 2, 5, 8, 2, 5, 8, 7, 1,
    9, 2, 1, 9, 0, 5, 9, 11, 5, 9, 9, 5,
    9, 7, 1, 9, 2, 0, 9, 0, 5, 9, 11, 5,
    9, 9, 5, 9, 7, 1, 9, 2, 0, 9, 0, 5,
    9, 11, 5, 9, 0, 5, 9, 9, 1, 8, 2, 8,
    8, 2, 6, 8, 4, 9, 8, 4, 4, 9, 0, 4,
    9, 11, 4, 9, 9, 4, 8, 7, 4, 8, 7, 5,
    9, 9, 5, 9, 11, 5, 9, 9, 8, 8, 4, 6,
    8, 6, 0, 8, 2, 8, 8, 2, 6, 8, 4, 9,
    8, 4, 4, 9, 0, 4, 9, 11, 4, 9, 9, 4,
    8, 7, 4, 9, 2, 8, 9, 9, 6, 9, 9, 1,
    8, 2, 8, 8, 2, 6, 8, 4, 9, 8, 4, 4,
    9, 0, 4, 9, 11, 4, 9, 9, 4, 8, 7, 4,
    8, 7, 5, 9, 9, 5, 9, 11, 5, 9, 9, 8,
    8, 4, 6, 8, 6, 0, 9, 2, 8, 9, 2, 6);

    { Format: octave,note,duration,octave,note,duration,.......

    Timing: 0 = beat Octave 0 = -5 Note: C = 0
    1 = beat * 2 1 = -4 C# = 1
    2 = beat * 3 2 = -3 D = 2
    3 = beat * 4 3 = -2 D# = 3
    4 = beat / 2 4 = -1 E = 4
    5 = beat / 3 5 = 0 F = 5
    6 = beat / 4 6 = +1 F# = 6
    7 = beat / 8 7 = +2 G = 7
    8 = beat / 0.75 8 = +3 G# = 8
    9 = beat * 1.5 9 = +4 A = 9
    10 = +5 A# = 10
    255 = pause B = 11

    }

    procedure midi_write;assembler;
    asm
    mov dx,$331
    mov cx,ax
    sub ax,ax
    @1:
    dec ah
    jz @2
    in al,dx
    and al,$40
    jnz @1
    @2:
    mov ax,cx
    dec dx
    out dx,al
    end;

    procedure midi_reset;assembler;
    asm
    mov dx,$331
    mov al,$ff
    out dx,al
    mov cx,ax
    mov ax,0
    @1:
    dec ah
    jz @2
    in al,dx
    and al,$80
    jnz @1
    @2:
    mov ax,cx
    dec dx
    in al,dx
    mov al,$3f
    inc dx
    mov cx,ax
    mov ax,0
    @3:
    dec ah
    jz @4
    in al,dx
    and al,$40
    jnz @3
    @4:
    mov ax,cx
    out dx,al
    end;

    { Plays a note using the sound card's MPU401 chip }
    procedure midi_note(status:boolean;channel,note,volume:byte);assembler;
    asm
    mov al,channel
    add al,$90
    mov bl,status
    and bl,1
    jnz @1
    sub al,$10
    @1:
    call midi_write
    mov al,note
    call midi_write
    mov al,volume
    call midi_write
    end;

    { Loops a tune, passed down via "t".
    Data must be in byte triplets: Octave,Note,Duration format
    Single channel only, it is possible to use multiple channels
    simultaneously, but w/ timing reworked instead of a simple delay }
    procedure loop_tune(var t;l{ength}:word); { length in byte triplets }
    var tseg,tofs:word;
    i,a,b,c:byte;
    ch:char;
    ext:boolean;
    begin
    ext:=false;tseg:=seg(t);tofs:=ofs(t);
    repeat for i:=1 to l do begin
    a:=mem[tseg:tofs+(i-1)*3];b:=mem[tseg:tofs+(i-1)*3+1];c:=mem[tseg:tofs+(i-1)*3+2];

    if a<>255 then
    if pc_speaker then sound(100*(a+octave)+b*10) else {<-- freq. is not accurate }
    midi_note(on,1,12*(a+octave)+b,127); { channel = 1 , volume = max. }
    delay(round(tempo*beat[c]));

    if keypressed then if readkey=#27 then begin ext:=true;break;end;

    end;until ext;if pc_speaker then nosound;
    end;


    begin
    if not pc_speaker then midi_reset;
    writeln('Press: Esc to Quit');
    loop_tune(sample_tune,sizeof(sample_tune) div 3);
    if not pc_speaker then midi_reset;
    end.[/color][/code]

  • SettunSettun Posts: 8Member
    Wonderful. Many thanks that is exactly what I was hunting for.

    Settun
Sign In or Register to comment.