Jump to content
IGNORED

Sound Conversion


RXB

Recommended Posts

Yea but even if I do it still keeps playing. Like I said even muting does not seem to shut it up. It is like a crap shoot to what works and does not.

 

Like when I play the Accept tone or Honk tone they do not need a mute or stop to quit playing but most of the ones I make do.

 

Accept tone data from Grom0:

 

>06,>BF,>DF,>FF,>80,>05,>92,>0A,>01,>9F,>00

 

The bytes >BF,>DF,>FF silence generators 2, 3, & 4. >80 & >05 specifies the tone on generator 1. >92 specifies the volume level of generator 1. >0A specifies the duration. >9F silences generator 1. So the end result is that all 4 generators are set to their lowest volume level.

 

Honk tone data from Grom0:

 

>06,>BF,>DF,>FF,>80,>20,>90,>0A,>01,>9F,>00

 

The bytes >BF,>DF,>FF silence generators 2, 3, & 4. >80 & >20 specifies the tone on generator 1. >90 specifies the volume level of generator 1. >0A specifies the duration. >9F silences generator 1. So the end result is that all 4 generators are set to their lowest volume level.

 

It has been my experience that notes will generally not stop playing after their specified duration unless another note is specified to play next. Why? I don't know. The above examples refute your statement that the accept and honk tones do not need a mute or stop to quit playing, as they do indeed need such a thing.

 

Tony

 

Thanks Tony.

I think the reason they keep playing is the TI used a goofy method of Duration last and the last one just never quits as that is the last byte, very bad design by TI. Duration should have been first and then Generator then the list of tones with 0 to stop playing.

 

Also I discovered something I read years ago I forgot, the Sound list MUST be on a Even address or unpredictable result happen in GPL. That explains some of the tones I make not working. This is mentioned no where in the GPL manual. I was wrong about the lists.

 

By the way is there a tool to disassemble the sound list like you did? How did you figure out the silence generators? Did you do it by hand?

Edited by RXB
Link to comment
Share on other sites

Thanks Tony.

I think the reason they keep playing is the TI used a goofy method of Duration last and the last one just never quits as that is the last byte, very bad design by TI. Duration should have been first and then Generator then the list of tones with 0 to stop playing.

 

Also I discovered something I read years ago I forgot, the Sound list MUST be on a Even address or unpredictable result happen in GPL. That explains some of the tones I make not working. This is mentioned no where in the GPL manual. I was wrong about the lists.

 

By the way is there a tool to disassemble the sound list like you did? How did you figure out the silence generators? Did you do it by hand?

 

I don't know of a tool, I just know how they're constructed. Probably from years of looking at compiled code. ;)

 

Once you get the conversion tool working, I'll start throwing the curveballs at you. ;)

 

Tony

 

Nice, well got it to work. The problem is you CAN NOT use simultaneous tones at the same time. Or at least the I/O command will not do it unless you have a section of program that controls interrupts.

Now the GPL manual has a really crappy example of how it is done using variables that I do not know for sure are, but guessed at and they set the Timer and load the >8400 values into the sound chip from GPL.

After I get some time on it I may figure out how they did this.

 

Here is an example:

DATA >0385,>1790,>0303,>8A0B,>9003,>049F,>BFDF,>FF00

This line works like a champ.

But if I use generators 1 and 2 like this:

DATA >0685,>1790,>8A0B,>9003,>0304,>9FBF,>DFFF,>0000

It never shuts up and no data loaded will turn off the number 2 generator. This is as far as I can find out is a ISR/VDP interrupt problem with GPL. I see they have a solution in the GPL manual but it is horrid looking.

Link to comment
Share on other sites

I may also have mistaken the inclusion of the length byte in the data list, it may not be included.

 

That's correct, the duration byte is not included. The reason is that it's not actually sent to the sound generator, it's used by the ROM ISR as a count-down value.

 

Adamantyr

Link to comment
Share on other sites

If it can help, here is a small résumé of the sound chapter from the E/A manual.

 

1st byte: Length of string

8XXX Tone 1 freq   -|- string
9X   Tone 1 vol     |
AXXX Tone 2 freq    |
BX   Tone 2 vol     |
CXXX Tone 3 freq    |
DX   Tone 3 vol     |
EX   Noise control  |
FX   Noise vol     -|

Last byte: Duration, or 0 to stop

Link to comment
Share on other sites

So to fix the statement:

 

DATA >0685,>1790,>8A0B,>9003,>0304,>9FBF,>DFFF,>0000

 

It would be this:

 

DATA >0685,>1790,>AA0B,>B003,>049F,>BFDF,>FF00

 

>06 = number of bytes to load

>8517 = frequency of tone 1

>90 = volume of tone 1

>AA0B = frequency of tone 2

>B0 = volume of tone 2

>03 = duration of sound (not counted in 'number of bytes to load' list)

>04 = number of bytes to load

>9F = silence generator 1

>BF = silence generator 2

>DF = silence generator 3

>FF = silence generator 4

>00 = end of list, no more bytes to load

 

As long as no sounds were playing previously in generators 3 & 4, >049F,>BFDF,>FF00 could be replaced with >029F,>BF00.

 

One, I'd avoid using DATA for sound data. Use BYTE instead. Way less likely you'll have accidents that way.

 

BYTE 6,>85,>17,>90,>8A,>0B,>90,3
BYTE 4,>9F,BF,>DF,>FF,0

 

This should work. I usually set up a "silence" tone in a sound list and just branch to it. Remember you can have a sound list branch to another address.

 

Adamantyr

Link to comment
Share on other sites

So to fix the statement:

 

DATA >0685,>1790,>8A0B,>9003,>0304,>9FBF,>DFFF,>0000

 

It would be this:

 

DATA >0685,>1790,>AA0B,>B003,>049F,>BFDF,>FF00

 

>06 = number of bytes to load

>8517 = frequency of tone 1

>90 = volume of tone 1

>AA0B = frequency of tone 2

>B0 = volume of tone 2

>03 = duration of sound (not counted in 'number of bytes to load' list)

>04 = number of bytes to load

>9F = silence generator 1

>BF = silence generator 2

>DF = silence generator 3

>FF = silence generator 4

>00 = end of list, no more bytes to load

 

As long as no sounds were playing previously in generators 3 & 4, >049F,>BFDF,>FF00 could be replaced with >029F,>BF00.

 

One, I'd avoid using DATA for sound data. Use BYTE instead. Way less likely you'll have accidents that way.

 

BYTE 6,>85,>17,>90,>8A,>0B,>90,3
BYTE 4,>9F,BF,>DF,>FF,0

 

This should work. I usually set up a "silence" tone in a sound list and just branch to it. Remember you can have a sound list branch to another address.

 

Adamantyr

 

I am posting a BASIC2GPL demo of the project and the sound demos to show how I did them in a GPLHOW2.

Link to comment
Share on other sites

I/O writes data to the sound chip - the sound chip does not process sound lists. The interrupt routine does.

 

Read the section in Editor/Assembler that I directed you to earlier. It describes how to set up a sound list. Load the same scratchpad values from GPL and your sound list will play.

 

Thanks Tursi, but I am dong GPL not Assembly.

 

The sound chip is a dumb device. Every byte sent to it affects the sound based on the control bits set. However, you can set up a sound list just by storing the correct pointers in scratchpad, which GPL is quite capable of doing. The console interrupt routine, which IS active during GPL execution, will then play the sound list with no further interaction required.

 

I'm glad you got it working, Rich, though I saw you were working in GPL.

 

It has been my experience that notes will generally not stop playing after their specified duration unless another note is specified to play next. Why? I don't know. The above examples refute your statement that the accept and honk tones do not need a mute or stop to quit playing, as they do indeed need such a thing.

 

I didn't see this answered (though I suspect it's clear by now). The reason is that the hardware doesn't know anything about duration - the duration counter is used by the interrupt routine to know when to send the next bytes to the tone generator. The sound chip just does what it's told until it's told to do something else. ;)

Edited by Tursi
Link to comment
Share on other sites

If it can help, here is a small résumé of the sound chapter from the E/A manual.

 

1st byte: Length of string

8XXX Tone 1 freq   -|- string
9X   Tone 1 vol     |
AXXX Tone 2 freq    |
BX   Tone 2 vol     |
CXXX Tone 3 freq    |
DX   Tone 3 vol     |
EX   Noise control  |
FX   Noise vol     -|

Last byte: Duration, or 0 to stop

 

There are also two undocumented "commands" to sound lists. I found these (by reading the console ISR) when I was writing my sound player for Owen. I even added my own command.

 

The first byte in a sound list can be:

 

>00 - GOTO

>FF - Toggle from VRAM to GROM or GROM to VRAM

>xx - anything else is the number of bytes that follow

 

The undocumented >00 and >FF commands work as follows:

 

When the ISR sound player sees >00 as the first byte, it takes the next two bytes as an address of where to find the sound list. So, this means you can:

 

1. String sound lists together

2. Loop a sound, or list of sounds, forever

 

The reading of the address ends the current sound processing and the new list at the specified address will be processed during the next execution of the ISR. So basically the GOTO takes 16.6mS of time, which may be a pause long enough to be audible? If the last sound did not stop the audio from playing, then that last note before the GOTO will at least be 16.6mS longer.

 

The >FF command toggles the address indicator (VRAM or GROM), then loads the next two bytes as an address, just like >00. On the next execution of the ISR, the new address will be used to find a sound list, but the ISR will look in VRAM vs. GROM or GROM vs. VRAM.

 

My sound player, unlike the ISR, is designed to read sound lists from CPU RAM instead of VRAM or GROM. I included support for the >00 command, but the >FF was not applicable (I don't switch to VRAM lists, but I suppose I could, but the console ISR can be used for that.) I did add my own >FE command that allows you to loop a sound a certain number of times. Kind of like the GOTO but with a counter, so you could repeat sections of sounds to build up repeating parts of a song for example. For complete tunes it could same quite a bit of sound data space. If you are interested, I posted the code (search the forum) and it is heavily commented.

Link to comment
Share on other sites

Here is an example:

DATA >0385,>1790,>0303,>8A0B,>9003,>049F,>BFDF,>FF00

This line works like a champ.

But if I use generators 1 and 2 like this:

DATA >0685,>1790,>8A0B,>9003,>0304,>9FBF,>DFFF,>0000

It never shuts up and no data loaded will turn off the number 2 generator. This is as far as I can find out is a ISR/VDP interrupt problem with GPL. I see they have a solution in the GPL manual but it is horrid looking.

 

In your second example, you inadvertently used the ISR sound player's >FF command:

>06 - length, six sound bytes follow
 >85
 >17
 >90
 >8A
 >0B
 >90

 >03 - duration of sound, used to count down by the ISR

>03 - length, three bytes follow
 >04
 >9F
 >BF

 >DF - duration of sound... 3.7 seconds in this case

>FF - command to switch from VRAM to GROM, and use the address that follows:
 >00 - 1st byte of new sound list address
 >00 - 2nd byte of new sound list address

So, whatever is in GROM memory at >0000 is what would start playing next, which would be garbage.

Edited by matthew180
Link to comment
Share on other sites

 

>FF = silence generator 4

>00 = end of list, no more bytes to load

 

If >FF did not mean to silence Generator 4 (noise), there would be no way to silence that generator. As shown in a previous example in this thread, it is used by the beep and honk commands in Grom0.

 

If >00 did not mean end of list, no more bytes to load; there would be no way to end the list, same example.

 

Refer to TI INTERN, pages 99, 101, 104, & 105.

 

Tony

 

It depends on the context of where the byte is. To the sound generator, >FF is silence generator 4. To the ISR sound list player, >FF is a memory-switch.

 

Sound data is really easy to mess up, especially if you persist in using DATA statements. One byte off and you get some pretty crazy stuff happening.

 

In the example given:

 

DATA >0685,>1790,>8A0B,>9003,>0304,>9FBF,>DFFF,>0000

 

Converting it to BYTE so it's easier to follow:

 

BYTE 6,>85,>17,>90,>8A,>0B,>90,3
BYTE 3,>04,>9F,>BF,>DF
BYTE >FF,>00,>00

 

As you can see, the problem was that there's an extra byte of value of 3 throwing it off. The >FF command will branch to GROM address 0, which creates the garbage cacophony.

 

The corrected version:

BYTE 6,>85,>17,>90,>8A,>0B,>90,3
BYTE 4,>9F,>BF,>DF,>FF,0
BYTE 0

 

Now everything's in order.

 

Adamantyr

  • Like 1
Link to comment
Share on other sites

If >FF did not mean to silence Generator 4 (noise), there would be no way to silence that generator. As shown in a previous example in this thread, it is used by the beep and honk commands in Grom0.

 

If >00 did not mean end of list, no more bytes to load; there would be no way to end the list, same example.

 

Refer to TI INTERN, pages 99, 101, 104, & 105.

 

Tony

 

The sound generator is a state machine, and in the idle state, >00 and >FF have a different meaning, i.e. they are *not* the same as >FF data and >00 duration bytes.

 

Yes, >FF means "silence" and >00 means no more data, but *only* when they are in the proper place in the data.

 

From idle, the first byte that is read is a length of bytes that follow to send to the sound chip. The code loops without checks until the specified number of bytes have been read and output to the sound chip. During that loop, the data bytes only means something to the sound chip.

 

After the specified number of bytes have been processed, a single byte is read that indicates the duration of the sound. The sound player then decrements that count every time the ISR is executed until it is zero, then the sound player goes back to the idle state, at which time the "command" bytes come in to play.

 

In RXB's example of the sound that does not stop:

 

DATA >0685,>1790,>8A0B,>9003,>0304,>9FBF,>DFFF,>0000

 

The first byte, >06, is the number of bytes to send to the sound chip. The sound player will simply loop the next 6 bytes without even caring what they are (it is just data for the sound chip.) So, >85, >17, >90, >8A, >0B, and >90 get sent in the loop. The code then takes one more byte for the duration, in this case the >03. The sound generator then decrements that count for the next 3 executions until it is zero.

 

At this point the sound player goes back to get the next "command", which in this case is >03, meaning send the next 3 bytes to the sound chip: >04, >9F, >BF, then reads a duration byte, which is >DF (223 decimal). Since the ISR is called every 16.6mS, it will take 16.6 * 223 executions to count down the duration which ends up being about 3.7018 seconds.

 

Now the sound player goes to get the next "command" byte, which is now >FF, and that means to toggle VRAM/GROM memory and use the next two bytes as the new address of the sound list, and in this case it ends up being address >0000.

 

In order to stop a sound, the >00 byte MUST be the duration, which is read right after the loop that sends data to the sound chip.

 

The routine starts on pg. 33 of the TI Intern at address >09E8. Reading the command byte starts at address >0A24.

 

09E8 0A11 SLA  R1,R1       R1 contains the bitmap for ISR facilities to process
09EA 183D JOC  >0A66       If no sound process, jump to done

09EC D0A0 MOVB @>83CE,R2   Get the duration byte of a previous sound, or the "start sound" byte >01
09EE 83CE
09F0 133A JEQ  >0A66       If >00 then no sound to process, jump to end

* A sound is playing or a new sound has been specified with >01 at >83CE
* R14 is apparently set up with >FF01 (VRAM) or >FF00 (GROM)
*
09F2 780E SB   R14,@>83CE  Decrement the count.  For a new sound, the result is >00
09F4 83CE
09F6 1637 JNE  >0A66       If not 0, then a previous sound is not done, jump to end

* Address >83CC points to a VRAM or GROM address of the sound list to play.
*
09F8 C0E0 MOV  @>83CC,R3   Move the pointer to the new sound list to R3
09FA 83CC
09FC C14E MOV  R14,R5      The least-significant bit of R14 is 1 for VRAM
09FE 0915 SRL  R5,1        GROM or VRAM?
0A00 180A JOC  >0A16       1 = VRAM, then jump

* Sound list in GROM, so set up GROM address and
* set R6 to point to the GROM read port
*
0A02 06A0 BL   @>0864      Otherwise set up GROM read, push GROM address on substack
0A04 0864
0A06 0205 LI   R5,>0402
0A08 0402
0A0A A14D A    R13,R5      GROM write address
0A0C D543 MOVB R3,*R5      Write GROM address
0A0E D560 MOVB @>83E7,*R5
0A10 83E7
0A12 C18D MOV  R13,R6      Set read address to R6
0A14 1007 JMP  >0A24       Done setting up GROM, jump VRAM setup

* Sound list in VRAM, set VRAM read address and 
* set up R6 to point to the VRAM read data port.
*
0A16 0205 LI   R5,>8C02    VDPWA for VRAM address
0A18 8C02
0A1A D560 MOVB @>83E7,*R5  Write LSB of R3 (sound list address) to VDP read-address setup
0A1C 83E7
0A1E D543 MOVB R3,*R5      Send the MSB of R3 to VDP to finish setting up read address
0A20 0206 LI   R6,>8800    VDPRD, set R6 to the VDP read data port
0A22 8800

* Process sound list.
* R3 contains the address of the sound list.
* R6 now points to the GROM port of VRAM port to read bytes from.
*
0A24 D216 MOVB *R6,R8      Fetch "command" byte
0A26 130F JEQ  >0A46       Jump if "command" is >00
0A28 9220 CB   @>0A9C,R8   Addres >0A9C is ROM is a value >FFBF,
0A2A 0A9C                  and CB will compare only the even address, i.e. >FF
0A2C 130A JEQ  >0A42       Jump if "command" is >FF (switch VRAM/GROM)

* Not >00 or >FF command, so must be the length of the sound list.
*
0A2E 0988 SRL  R8,R8       Otherwise the "command" is sound list length, so move it to the LSB
0A30 A0C8 A    R8,R3       Adjust the sound-list address pointer in R3 by the "sound data" length
0A32 D816 MOVB *R6,@>8400  Read byte from list (VRAM or GROM) and send to sound chip
0A34 8400
0A36 0608 DEC  R8          Decrement bytes sent to sound chip
0A38 16FC JNE  >0A32       Not done, loop

* Data has been sent to sound chip, now process the duration of this sound.
*
0A3A 05C3 INCT R3          Adjust R3 to account for the command and duration bytes
0A3C D096 MOVB *R6,R2      Fetch the duration byte
0A3E 1309 JEQ  >0A52       If duration is zero, sound list is done
0A40 1009 JMP  >0A54       Skip "command" subroutines

* Command >FF, toggle VRAM/GROM and GOTO following address
*
0A42 2BA0 XOR  @>0378,R14  Process >FF command, toggle VRAM/GROM access
0A44 0378

* Command >00, GOTO sound list at the following address
* Reads two bytes for the new sound list address and sets
* up the address in R3 which is eventually written to >83CC
*
0A46 D0D6 MOVB *R6,R3      Fetch MSB of new address into MSB of R3
0A48 0202 LI   R2,>0100    Set R2 to indicate a sound is ready (>01 will be loaded to >83CE)
0A4A 0100
0A4C D816 MOVB *R6,@>83E7  Fetch LSB of new address (>83E7 is the LSB of R3)
0A4E 83E7
0A50 1001 JMP  >0A54       End of >00 command

* R2 is already zero, so it is unclear as to what this is for...
0A52 7082 SB   R2,R2       Set R2 to zero

* Set up new sound list.
*
0A54 C803 MOV  R3,@>83CC   Set the pointer to the new sound list in >83CC
0A56 83CC
0A58 D802 MOVB R2,@>83CE   Set >83CE to the sound list duration (>01 for a GOTO command)
0A5A 83CE
0A5C 0285 CI   R5,>8C02    Check if the sound list is in VRAM
0A5E 8C02
0A60 1302 JEQ  >0A66       If so, skip GROM stack maintenance

* Source sound list was GROM, so adjust the stack
0A62 06A0 BL   @>0842      Otherwise POP GROM address from substack
0A64 0842

* END of sound processing...
0A66 0A11 SLA  R1,R1       ISR continues...

Edited by matthew180
Link to comment
Share on other sites

It looks like you're pretty close, but I noticed in your video that you still were not sure how to do multiple tones.

 

It might be useful to take a step backwards, and forget about sound lists for a moment, but to first learn how the sound chip works. Sorry if I'm repeating data already given, but I'll try to zip through each part.

 

So the sound chip in the TI is a relatively simple device that has a very limited number of functions. It has four independent channels. Three of these channels generate square wave tones, and one of them generates noises. Each of the four channels has an independent volume setting.

 

The sound chip is an 8-bit device and all control is handled by writing commands to it one byte at a time. It is safe to write multiple bytes without delay, unlike the documented access to the VDP. Every byte written will have some effect on the output sound.

 

There are three commands that the chip can understand:

 

First, you can set a tone frequency. Although TI BASIC specifies frequencies in the somewhat human-understandable Hertz range, the sound chip itself simply uses a countdown timer. The lower the timer value, the higher the pitch of the tone. You can convert from Hertz to sound chip counter using the formula 111860.8/Hz = N. The value N is allowed to range from >001 (approximately 56khz) to >3FF (approximately 100hz). (In fact, >000 is allowed too, but it does not work with the math. >000 is the lowest pitch tone, one tiny step lower than >001). This magic number of 111860.8 is based on the hardware clock frequency and the internal operation of the sound chip.

 

Because the tone specification is more than 8-bits long, it is sent in two bytes. The least significant nibble goes in the first byte, along with the command bits (which I'll cover below), and the most significant two nibbles go in the second byte. Note that both bytes change the sound immediately, so must be sent close together.

 

The command bits go in the high nibble of the first byte, and for a frequency they specify which of channels 1, 2 or 3 the tone is meant for. For channel 1, the command bits are >8, for channel 2, they are >A, and for channel 3 they are >C.

 

For instance, the frequency 110Hz is converted like so: 111860.8/110 = 1017 (roughly). 1017 is >3F9 in hex. The first byte, then, is >09 (the least significant nibble) and will have the command bits added. For channel one, it is sent as >89, for channel two, it is >A9, and for channel three, it is >C9. The second byte in all cases is simply >3F (the two most significant nibbles).

 

The second operation the sound chip supports is to play different types of noise. Noise can only be played on channel 4, and tones can not be played on channel 4. In TI BASIC, noises are specified by a negative number from -1 to -8. By making the number positive and subtracting one (so the range is 0-7), you can use the same values in assembly or GPL. Noise control takes only one byte, so the noise type goes into the least significant nibble of the byte, and the command bits, as usual, go in the most significant nibble. For noise control, the command bits are always >E. So, for instance, to play noise -7, make positive and subtract one to get >6. Add the command bits of >E to the high nibble, and >E6 is the byte to send.

 

The third and final operation supported by the sound chip is to set volume. In truth, it is setting attenuation, but the result is the same. TI BASIC sets the volume with a range from 0 (loudest) to 30 (silent). In truth, however the sound chip only has 16 settings. So to convert, first divide the TI BASIC setting by 2, giving a range from >0 to >F (15). This now fits in one nibble, so with the command bits, setting volume takes just one byte. All four channels have an independent volume setting, and corresponding command bits. For channel 1 volume, it's >9, for channel 2 volume, it's >B, for channel 3 volume, it's >D, and for channel 4 (noise) volume, it's >F. So to set a volume attenuation of 8, divide by two to get >4, then merge in the command bits. Channel 1 would be >94, 2 would be >B4, 3 would be >D4 and 4 would be >F4.

 

The command bits are summarized here (and they were summarized earlier in this thread too):

 

>8x xx - Channel 1 frequency
>9x    - Channel 1 volume
>Ax xx - channel 2 frequency
>Bx    - channel 2 volume
>Cx xx - channel 3 frequency
>Dx    - channel 3 volume
>Ex    - Noise type
>Fx    - noise volume

 

The covers the sound bytes themselves, but the sound list is a different beast.

 

The sound list is not a part of the hardware at all, rather, it is loaded by software in the console interrupt routine. It's been covered in good detail so I don't want to repeat too much of it, except to note that it is just a wrapper that sends bytes to the sound chip, and then delays before sending more. Excluding the special commands that Matthew documented, the format is:

 

<number of bytes to send>, <sound chip data> x bytes, <duration in jiffies to wait>

 

Number of bytes specifies how many bytes of sound chip data to send, and these are all sent one after the other. They can contain anything from a single byte command, to setting all channels and all volumes (this would take 11 bytes).

 

A jiffy is a single frame - on NTSC consoles frames occur 60 times a second (50 on PAL). So NTSC jiffies are 16.6ms long. This means that TI BASIC durations would be divided by 16.6 to get the true value for duration.

 

The above sequence is repeated as many times as desired, and this can allow playback of complex sounds including music with a single command (as in the Pop Goes the Weasel example in the GPLHow2 video!) A duration of 0 ends the processing.

 

Of course, ending the processing doesn't mean the sound ends, the sound chip keeps doing whatever the last thing it was instructed to do. For this reason, a sound list usually ends by muting all the channels it used, setting the volume attenuation on each channel to >F. In fact, when TI BASIC makes a sound list for a CALL SOUND, it explicitly mutes all channels not being used.

 

Some examples will hopefully help. I think you already mastered single tones, but to recap, here are some example conversions. I've bolded the count, italicized the duration, and the rest is sound data.

 

CALL SOUND(1000,500,0) - >03,>80,>0E,>90,>3C, >04,>9F,>BD,>DF,>FF,>00

 

In the first block, we load 3 bytes. The first byte is >80. The command bits of >8 tell the sound chip to expect a channel 1 frequency, and the least significant nibble is 0. The next byte, >0E, contains the most significant nibbles of the counter -- putting them together we get >0E0. >0E0 is 224 in decimal. If we do our math, we want 500hz. 111860.8/500 = 223.7216 - but we can't have fractions, so we round up to 224.

 

The next byte is >90. The command bits of >9 tell the sound chip to expect a channel 1 volume attenuation nibble, which in this case is 0. The CALL SOUND passed a 0, which, divided by 2, is still 0.

 

Now that all three bytes are loaded, the last byte in the first group is the duration, >3C frames. Our math says this value should be the time in milliseconds divided by 16.6, so 1000/16.6 = 60.24. Again rounding the fraction, the result is 60, which is >3C in hex.

 

At this point the playback routine delays until those 60 frames have elapsed. When they are done, it looks at the next block, which loads 4 bytes. These four bytes set the volume attenuation of each of the four channels to >F, which is silent (BASIC value 30). Look at the command bits to see this. They are followed by a duration of 0, which means to do no more list processing.

 

Noise works exactly the same way:

 

CALL SOUND(500,-6,10) - >02,>E5,>F5,>1E, >04,>9F,>BD,>DF,>FF,>00

 

This time we only need to load two bytes. >E5 sets the noise type, due to the >E command bits. -6 made positive is 6, subtract one to get 5.

 

The volume of 10 is divided by 2 to get 5, and the command bits for noise channel volume is >F, so we get >F5.

 

The duration byte is >1E, which is 30 decimal. 500/16.6 gives us 30.12, rounded down to this value.

 

After the 30 frames elapse, the usual silence-all-channels sequence ends it.

 

For more channels, you just add the necessary bytes. For example, all four channels:

 

CALL SOUND(250,110,2,220,4,440,8,-2,10) - >0B,>89,>3F,>91, >AC,>1F,>B2, >CE,>0F,>D4, >E1,>F5, >0F, >04,>9F,>BD,>DF,>FF,>00

 

We start with a count of >0B, which means 11 bytes follow for the sound chip - setting everything!

 

The first byte is >89 - the command bits of >8 tell us it's voice 1 frequency, with a least significant nibble of >9. The most significant nibbles in the next byte are >3F, giving us >3F9. We expected 110hz, so 111860.8/110 = 1016.9, round up to 1017, which is >3F9 in hex!

 

The next byte is the >91, the command bits of >9 tell us that it's voice 1 volume, and the value is >1. Volume of 2 divided by 2 = 1, so this is expected.

 

Next we have >AC. The command bits >A mean voice 2 frequency, and combining the next byte, the value is >1FC. 111860.8/220 = 508.45, and 508 is >1FC in hex.

 

Next the >B2, command bits >B means voice two volume, and the value is >2. 4/2 = 2.

 

For the >CE, command >C is voice 3 frequency. The >0F byte is included to give a value of >0FE.

 

>D4 - command >D means voice 3 volume, setting is 4. 8/2=4!

 

>E1 - Command >E means noise type, and the type is 1. (-2 -> 2, 2-1 = 1).

 

and >F5, command >F means voice 4/noise volume, and 10/2=5.

 

All these voices play for >0F frames. 250/16.6=15.06, and 15 is >0F in hex. After this, the usual silencing bytes silence all channels.

 

For what it's worth, there are no rules to these bytes. It is okay to run channel 2 without using channel 1, and you can freely change volume and frequency independent of each other. It's also legal to omit the second byte of the frequency specification, although this will rarely be useful due to how little difference changing just the least significant nibble makes. You can also provide the bytes in any order you like.

 

You can play music simply by chaining notes one after the other in the sound list, with appropriate durations on each.

 

You also don't have to mute all the channels at the end, you could mute only the channels you used, for instance. It's good practice to mute them all, though, to be certain nobody else's sounds are playing. Anything you don't mute will continue doing whatever it was doing when the list ends, though!

 

Hope that's helpful.

Link to comment
Share on other sites

It looks like you're pretty close, but I noticed in your video that you still were not sure how to do multiple tones.

 

It might be useful to take a step backwards, and forget about sound lists for a moment, but to first learn how the sound chip works. Sorry if I'm repeating data already given, but I'll try to zip through each part.

 

So the sound chip in the TI is a relatively simple device that has a very limited number of functions. It has four independent channels. Three of these channels generate square wave tones, and one of them generates noises. Each of the four channels has an independent volume setting.

 

The sound chip is an 8-bit device and all control is handled by writing commands to it one byte at a time. It is safe to write multiple bytes without delay, unlike the documented access to the VDP. Every byte written will have some effect on the output sound.

 

There are three commands that the chip can understand:

 

First, you can set a tone frequency. Although TI BASIC specifies frequencies in the somewhat human-understandable Hertz range, the sound chip itself simply uses a countdown timer. The lower the timer value, the higher the pitch of the tone. You can convert from Hertz to sound chip counter using the formula 111860.8/Hz = N. The value N is allowed to range from >001 (approximately 56khz) to >3FF (approximately 100hz). (In fact, >000 is allowed too, but it does not work with the math. >000 is the lowest pitch tone, one tiny step lower than >001). This magic number of 111860.8 is based on the hardware clock frequency and the internal operation of the sound chip.

 

Because the tone specification is more than 8-bits long, it is sent in two bytes. The least significant nibble goes in the first byte, along with the command bits (which I'll cover below), and the most significant two nibbles go in the second byte. Note that both bytes change the sound immediately, so must be sent close together.

 

The command bits go in the high nibble of the first byte, and for a frequency they specify which of channels 1, 2 or 3 the tone is meant for. For channel 1, the command bits are >8, for channel 2, they are >A, and for channel 3 they are >C.

 

For instance, the frequency 110Hz is converted like so: 111860.8/110 = 1017 (roughly). 1017 is >3F9 in hex. The first byte, then, is >09 (the least significant nibble) and will have the command bits added. For channel one, it is sent as >89, for channel two, it is >A9, and for channel three, it is >C9. The second byte in all cases is simply >3F (the two most significant nibbles).

 

The second operation the sound chip supports is to play different types of noise. Noise can only be played on channel 4, and tones can not be played on channel 4. In TI BASIC, noises are specified by a negative number from -1 to -8. By making the number positive and subtracting one (so the range is 0-7), you can use the same values in assembly or GPL. Noise control takes only one byte, so the noise type goes into the least significant nibble of the byte, and the command bits, as usual, go in the most significant nibble. For noise control, the command bits are always >E. So, for instance, to play noise -7, make positive and subtract one to get >6. Add the command bits of >E to the high nibble, and >E6 is the byte to send.

 

The third and final operation supported by the sound chip is to set volume. In truth, it is setting attenuation, but the result is the same. TI BASIC sets the volume with a range from 0 (loudest) to 30 (silent). In truth, however the sound chip only has 16 settings. So to convert, first divide the TI BASIC setting by 2, giving a range from >0 to >F (15). This now fits in one nibble, so with the command bits, setting volume takes just one byte. All four channels have an independent volume setting, and corresponding command bits. For channel 1 volume, it's >9, for channel 2 volume, it's >B, for channel 3 volume, it's >D, and for channel 4 (noise) volume, it's >F. So to set a volume attenuation of 8, divide by two to get >4, then merge in the command bits. Channel 1 would be >94, 2 would be >B4, 3 would be >D4 and 4 would be >F4.

 

The command bits are summarized here (and they were summarized earlier in this thread too):

 

>8x xx - Channel 1 frequency
>9x    - Channel 1 volume
>Ax xx - channel 2 frequency
>Bx    - channel 2 volume
>Cx xx - channel 3 frequency
>Dx    - channel 3 volume
>Ex    - Noise type
>Fx    - noise volume

 

The covers the sound bytes themselves, but the sound list is a different beast.

 

The sound list is not a part of the hardware at all, rather, it is loaded by software in the console interrupt routine. It's been covered in good detail so I don't want to repeat too much of it, except to note that it is just a wrapper that sends bytes to the sound chip, and then delays before sending more. Excluding the special commands that Matthew documented, the format is:

 

<number of bytes to send>, <sound chip data> x bytes, <duration in jiffies to wait>

 

Number of bytes specifies how many bytes of sound chip data to send, and these are all sent one after the other. They can contain anything from a single byte command, to setting all channels and all volumes (this would take 11 bytes).

 

A jiffy is a single frame - on NTSC consoles frames occur 60 times a second (50 on PAL). So NTSC jiffies are 16.6ms long. This means that TI BASIC durations would be divided by 16.6 to get the true value for duration.

 

The above sequence is repeated as many times as desired, and this can allow playback of complex sounds including music with a single command (as in the Pop Goes the Weasel example in the GPLHow2 video!) A duration of 0 ends the processing.

 

Of course, ending the processing doesn't mean the sound ends, the sound chip keeps doing whatever the last thing it was instructed to do. For this reason, a sound list usually ends by muting all the channels it used, setting the volume attenuation on each channel to >F. In fact, when TI BASIC makes a sound list for a CALL SOUND, it explicitly mutes all channels not being used.

 

Some examples will hopefully help. I think you already mastered single tones, but to recap, here are some example conversions. I've bolded the count, italicized the duration, and the rest is sound data.

 

CALL SOUND(1000,500,0) - >03,>80,>0E,>90,>3C, >04,>9F,>BD,>DF,>FF,>00

 

In the first block, we load 3 bytes. The first byte is >80. The command bits of >8 tell the sound chip to expect a channel 1 frequency, and the least significant nibble is 0. The next byte, >0E, contains the most significant nibbles of the counter -- putting them together we get >0E0. >0E0 is 224 in decimal. If we do our math, we want 500hz. 111860.8/500 = 223.7216 - but we can't have fractions, so we round up to 224.

 

The next byte is >90. The command bits of >9 tell the sound chip to expect a channel 1 volume attenuation nibble, which in this case is 0. The CALL SOUND passed a 0, which, divided by 2, is still 0.

 

Now that all three bytes are loaded, the last byte in the first group is the duration, >3C frames. Our math says this value should be the time in milliseconds divided by 16.6, so 1000/16.6 = 60.24. Again rounding the fraction, the result is 60, which is >3C in hex.

 

At this point the playback routine delays until those 60 frames have elapsed. When they are done, it looks at the next block, which loads 4 bytes. These four bytes set the volume attenuation of each of the four channels to >F, which is silent (BASIC value 30). Look at the command bits to see this. They are followed by a duration of 0, which means to do no more list processing.

 

Noise works exactly the same way:

 

CALL SOUND(500,-6,10) - >02,>E5,>F5,>1E, >04,>9F,>BD,>DF,>FF,>00

 

This time we only need to load two bytes. >E5 sets the noise type, due to the >E command bits. -6 made positive is 6, subtract one to get 5.

 

The volume of 10 is divided by 2 to get 5, and the command bits for noise channel volume is >F, so we get >F5.

 

The duration byte is >1E, which is 30 decimal. 500/16.6 gives us 30.12, rounded down to this value.

 

After the 30 frames elapse, the usual silence-all-channels sequence ends it.

 

For more channels, you just add the necessary bytes. For example, all four channels:

 

CALL SOUND(250,110,2,220,4,440,8,-2,10) - >0B,>89,>3F,>91, >AC,>1F,>B2, >CE,>0F,>D4, >E1,>F5, >0F, >04,>9F,>BD,>DF,>FF,>00

 

We start with a count of >0B, which means 11 bytes follow for the sound chip - setting everything!

 

The first byte is >89 - the command bits of >8 tell us it's voice 1 frequency, with a least significant nibble of >9. The most significant nibbles in the next byte are >3F, giving us >3F9. We expected 110hz, so 111860.8/110 = 1016.9, round up to 1017, which is >3F9 in hex!

 

The next byte is the >91, the command bits of >9 tell us that it's voice 1 volume, and the value is >1. Volume of 2 divided by 2 = 1, so this is expected.

 

Next we have >AC. The command bits >A mean voice 2 frequency, and combining the next byte, the value is >1FC. 111860.8/220 = 508.45, and 508 is >1FC in hex.

 

Next the >B2, command bits >B means voice two volume, and the value is >2. 4/2 = 2.

 

For the >CE, command >C is voice 3 frequency. The >0F byte is included to give a value of >0FE.

 

>D4 - command >D means voice 3 volume, setting is 4. 8/2=4!

 

>E1 - Command >E means noise type, and the type is 1. (-2 -> 2, 2-1 = 1).

 

and >F5, command >F means voice 4/noise volume, and 10/2=5.

 

All these voices play for >0F frames. 250/16.6=15.06, and 15 is >0F in hex. After this, the usual silencing bytes silence all channels.

 

For what it's worth, there are no rules to these bytes. It is okay to run channel 2 without using channel 1, and you can freely change volume and frequency independent of each other. It's also legal to omit the second byte of the frequency specification, although this will rarely be useful due to how little difference changing just the least significant nibble makes. You can also provide the bytes in any order you like.

 

You can play music simply by chaining notes one after the other in the sound list, with appropriate durations on each.

 

You also don't have to mute all the channels at the end, you could mute only the channels you used, for instance. It's good practice to mute them all, though, to be certain nobody else's sounds are playing. Anything you don't mute will continue doing whatever it was doing when the list ends, though!

 

Hope that's helpful.

 

Thanks now all I need to figure out is how to do the Simultaneous sound lists.

Presently looking at the GPL manual shows an example:

I/O 1,@SOUND

CLR @TR

B02 CZ @TR

BS B02

 

 

TR is >8379 and SOUND is the pointer to the sound list in GRAM.

But no where does it state Simultaneous sounds being used, all examples are only single channel.

Needless to say a real lack of programing data is present in GPL for Sounds.

Edited by RXB
Link to comment
Share on other sites

"GPL sounds" are no different than "assembly sounds" or "BASIC sounds" (except in BASIC you don't get much control.)

 

To get simultaneous sounds, you need to enable more than 1 sound generator at a time, which you can easily do if you can set up ISR sound player data as we have been talking about.

 

You have 3 "voices" and 1 "noise" channel to work with, and each can be set to its own frequency and volume. As for different durations on each voice, that is a bit more tricky, and is somewhat of a pain with the current sound player. The ISR sound player starts a sound list and waits for the full duration specified before you can get another sound in. You have to mix voices yourself with the data, which makes it really hard to arbitrarily start multiple sounds, like with sound effects happening in a game in response to events, etc.

Link to comment
Share on other sites

Thanks now all I need to figure out is how to do the Simultaneous sound lists.

 

There are no simultaneous sound lists. You get one sound list at a time. A sound list can control as many channels as desired. Driving the channels is up to you, like Matthew says.

 

Needless to say a real lack of programing data is present in GPL for Sounds.

 

You have all the pieces you need. Try some experiments like the lists I provided in my example, and try to create some new lists yourself based on the information, and I think it will become clear to you. There's just a slight misunderstanding you need to overcome.

Link to comment
Share on other sites

I've created an example for you Rich (or anyone else interested). This is a GPL conversion of a TI BASIC program that plays part of 'Somewhere Over the Rainbow'. It uses multiple channels and a single sound list to play the song -- in fact the GPL just starts the sound list and then goes into an infinite loop, showing that no program interaction is needed.

 

This is the listing (GPL with BASIC comments):

 

 

* Plays the opening to 'Somewhere over the Rainbow'
* direct conversion from CALL SOUND statements in TI BASIC
* this version takes 368 bytes.

AORG >6000

* Standard cartridge header	
DATA >AA01,>0100,>0000,>600C,>0000,>0000
DATA >0000,>601C,>0B47,>504C,>2052,>4149
DATA >4E42,>4F57

* >601C (entry point)	
I/O 0,SNDLST

* loop forever (press QUIT to exit)
* note the music will stop on its own
LP
BR LP

SNDLST
* each entry includes a mute on the noise channel, just as padding
* note that the volumes never change, so the list could be made shorter
* by only setting the volume once at the beginning.

*440 CALL SOUND(922,196,6,233,6,311,0)
DATA >0A8B,>2393,>A01E,>B3C8,>16D0,>FF38

*450 CALL SOUND(922,392,6,466,6,622,0)
DATA >0A8D,>1193,>A00F,>B3C4,>0BD0,>FF38

*460 CALL SOUND(461,294,6,466,6,587,0)
DATA >0A8C,>1793,>A00F,>B3CF,>0BD0,>FF1C

*470 CALL SOUND(230,294,6,392,6,466,0)
DATA >0A8C,>1793,>AD11,>B3C0,>0FD0,>FF0E

*480 CALL SOUND(230,294,6,440,6,523,0)
DATA >0A8C,>1793,>AE0F,>B3C6,>0DD0,>FF0E

*490 CALL SOUND(461,294,6,466,6,587,0)
DATA >0A8C,>1793,>A00F,>B3CF,>0BD0,>FF1C

*500 CALL SOUND(461,277,6,523,6,622,0)
DATA >0A84,>1993,>A60D,>B3C4,>0BD0,>FF1C

*510 CALL SOUND(922,208,6,262,6,311,0)
DATA >0A8A,>2193,>AB1A,>B3C8,>16D0,>FF38

*520 CALL SOUND(461,156,6,392,6,523,0)
DATA >0A8D,>2C93,>AD11,>B3C6,>0DD0,>FF1C

*530 CALL SOUND(461,156,6,370,6,523,0)
DATA >0A8D,>2C93,>AE12,>B3C6,>0DD0,>FF1C

*540 CALL SOUND(461,196,6,349,6,466,0)
DATA >0A8B,>2393,>A114,>B3C0,>0FD0,>FF1C

*550 CALL SOUND(461,196,6,311,6,466,0)
DATA >0A8B,>2393,>A816,>B3C0,>0FD0,>FF1C

*560 CALL SOUND(461,175,6,294,6,466,0)
DATA >0A8F,>2793,>AC17,>B3C0,>0FD0,>FF1C

*570 CALL SOUND(461,165,6,277,6,466,0)
DATA >0A86,>2A93,>A419,>B3C0,>0FD0,>FF1C

*580 CALL SOUND(922,156,6,208,6,262,0)
DATA >0A8D,>2C93,>AA21,>B3CB,>1AD0,>FF38

*590 CALL SOUND(922,175,6,311,6,415,0)
DATA >0A8F,>2793,>A816,>B3CE,>10D0,>FF38

*600 CALL SOUND(461,233,6,311,6,392,0)
DATA >0A80,>1E93,>A816,>B3CD,>11D0,>FF1C

*610 CALL SOUND(230,233,6,262,6,311,0)
DATA >0A80,>1E93,>AB1A,>B3C8,>16D0,>FF0E

*620 CALL SOUND(230,233,6,294,6,349,0)
DATA >0A80,>1E93,>AC17,>B3C1,>14D0,>FF0E

*630 CALL SOUND(461,233,6,330,6,392,0)
DATA >0A80,>1E93,>A315,>B3CD,>11D0,>FF1C

*640 CALL SOUND(461,233,6,277,6,415,0)
DATA >0A80,>1E93,>A419,>B3CE,>10D0,>FF1C

*650 CALL SOUND(461,220,6,294,6,349,0)
DATA >0A8C,>1F93,>AC17,>B3C1,>14D0,>FF1C

*660 CALL SOUND(230,220,6,247,6,294,0)
DATA >0A8C,>1F93,>A51C,>B3CC,>17D0,>FF0E

*670 CALL SOUND(230,220,6,262,6,311,0)
DATA >0A8C,>1F93,>AB1A,>B3C8,>16D0,>FF0E

*680 CALL SOUND(461,208,6,294,6,349,0)
DATA >0A8A,>2193,>AC17,>B3C1,>14D0,>FF1C

*690 CALL SOUND(461,208,6,294,6,392,0)
DATA >0A8A,>2193,>AC17,>B3CD,>11D0,>FF1C

*700 CALL SOUND(1383,196,6,233,6,311,0)
DATA >0A8B,>2393,>A01E,>B3C8,>16D0,>FF53

* AND SILENCE AFTER THE LAST NOTE, THEN END
DATA >049F,>BFDF,>FF00

 

 

 

Once that makes sense, have a look at this one. In this next one, the code is exactly the same, but I remove redundant data from the sound list to cut it nearly in half. Since the sound chip doesn't stop playing a tone unless it is told to, the sound list actually only needs to contain the CHANGES from the last note. It plays exactly the same but takes nearly half the space.

 

 

* Plays the opening to 'Somewhere over the Rainbow'
* direct conversion from CALL SOUND statements in TI BASIC

* This is a more advanced sound list that removes
* redundant data to save space! You'll see that it
* sounds exactly the same! Instead of 368 bytes, it
* takes 226 bytes, saving 142 bytes on the sound list

AORG >6000

* Standard cartridge header	
DATA >AA01,>0100,>0000,>600C,>0000,>0000
DATA >0000,>601C,>0B47,>504C,>2052,>4149
DATA >4E42,>4F57

* >601C (entry point)	
I/O 0,SNDLST

* loop forever (press QUIT to exit)
* note the music will stop on its own
LP
BR LP

SNDLST
* this version contains an optimized list. Because the sound
* chip keeps playing the last data sent to it, each entry in
* the sound list only needs to contain the changed information!
* the first entry should still set up everything, though!

*440 CALL SOUND(922,196,6,233,6,311,0)
DATA >0A8B,>2393,>A01E,>B3C8,>16D0,>FF38

* in particular, we never need to change the volume again
* after the first case, as it never changes. We also do not
* need to load frequencies on channels that don't change.

* note that the sound lists are no longer a direct replacement
* for the CALL SOUND statement, though, since they are dependent
* on the /previous/ CALL SOUND too. However, this can save a lot
* of memory space and improves performance since redundant data
* doesn't have to be copied to the sound chip.

*450 CALL SOUND(922,392,6,466,6,622,0)
DATA >068D,>11A0,>0FC4,>0B38

*460 CALL SOUND(461,294,6,466,6,587,0)
DATA >048C,>17CF,>0B1C

*470 CALL SOUND(230,294,6,392,6,466,0)
DATA >04AD,>11C0,>0F0E

*480 CALL SOUND(230,294,6,440,6,523,0)
DATA >04AE,>0FC6,>0D0E

*490 CALL SOUND(461,294,6,466,6,587,0)
DATA >04A0,>0FCF,>0B1C	

*500 CALL SOUND(461,277,6,523,6,622,0)
DATA >0684,>19A6,>0DC4,>0B1C

*510 CALL SOUND(922,208,6,262,6,311,0)
DATA >068A,>21AB,>1AC8,>1638

*520 CALL SOUND(461,156,6,392,6,523,0)
DATA >068D,>2CAD,>11C6,>0D1C

*530 CALL SOUND(461,156,6,370,6,523,0)
DATA >02AE,>121C

*540 CALL SOUND(461,196,6,349,6,466,0)
DATA >068B,>23A1,>14C0,>0F1C

*550 CALL SOUND(461,196,6,311,6,466,0)
DATA >02A8,>161C

*560 CALL SOUND(461,175,6,294,6,466,0)
DATA >048F,>27AC,>171C

*570 CALL SOUND(461,165,6,277,6,466,0)
DATA >0486,>2AA4,>191C

*580 CALL SOUND(922,156,6,208,6,262,0)
DATA >068D,>2CAA,>21CB,>1A38

*590 CALL SOUND(922,175,6,311,6,415,0)
DATA >068F,>27A8,>16CE,>1038

*600 CALL SOUND(461,233,6,311,6,392,0)
DATA >0480,>1ECD,>111C

*610 CALL SOUND(230,233,6,262,6,311,0)
DATA >04AB,>1AC8,>160E

*620 CALL SOUND(230,233,6,294,6,349,0)
DATA >04AC,>17C1,>140E

*630 CALL SOUND(461,233,6,330,6,392,0)
DATA >04A3,>15CD,>111C

*640 CALL SOUND(461,233,6,277,6,415,0)
DATA >04A4,>19CE,>101C

*650 CALL SOUND(461,220,6,294,6,349,0)
DATA >068C,>1FAC,>17C1,>141C

*660 CALL SOUND(230,220,6,247,6,294,0)
DATA >04A5,>1CCC,>170E

*670 CALL SOUND(230,220,6,262,6,311,0)
DATA >04AB,>1AC8,>160E

*680 CALL SOUND(461,208,6,294,6,349,0)
DATA >068A,>21AC,>17C1,>141C

*690 CALL SOUND(461,208,6,294,6,392,0)
DATA >02CD,>111C

*700 CALL SOUND(1383,196,6,233,6,311,0)
DATA >068B,>23A0,>1EC8,>1653

* AND SILENCE AFTER THE LAST NOTE, THEN END
DATA >049F,>BFDF,>FF00

 

 

You should be able to assemble and run either of these, they are set up as cartridges. I've also provided pre-compiled binaries ready to load in Classic99 or whatever else, and the source is also in this zip file.

 

gplSound.zip

 

I'll attach the TI BASIC program too. This is a TIFILES format file.

 

RAINBOW.zip

Edited by Tursi
Link to comment
Share on other sites

"GPL sounds" are no different than "assembly sounds" or "BASIC sounds" (except in BASIC you don't get much control.)

 

To get simultaneous sounds, you need to enable more than 1 sound generator at a time, which you can easily do if you can set up ISR sound player data as we have been talking about.

 

You have 3 "voices" and 1 "noise" channel to work with, and each can be set to its own frequency and volume. As for different durations on each voice, that is a bit more tricky, and is somewhat of a pain with the current sound player. The ISR sound player starts a sound list and waits for the full duration specified before you can get another sound in. You have to mix voices yourself with the data, which makes it really hard to arbitrarily start multiple sounds, like with sound effects happening in a game in response to events, etc.

 

I suppose you are talking only about Assembly doing sounds as I am dong only GPL.

Link to comment
Share on other sites

I've created an example for you Rich (or anyone else interested). This is a GPL conversion of a TI BASIC program that plays part of 'Somewhere Over the Rainbow'. It uses multiple channels and a single sound list to play the song -- in fact the GPL just starts the sound list and then goes into an infinite loop, showing that no program interaction is needed.

 

This is the listing (GPL with BASIC comments):

 

 

* Plays the opening to 'Somewhere over the Rainbow'
* direct conversion from CALL SOUND statements in TI BASIC
* this version takes 368 bytes.

AORG >6000

* Standard cartridge header	
DATA >AA01,>0100,>0000,>600C,>0000,>0000
DATA >0000,>601C,>0B47,>504C,>2052,>4149
DATA >4E42,>4F57

* >601C (entry point)	
I/O 0,SNDLST

* loop forever (press QUIT to exit)
* note the music will stop on its own
LP
BR LP

SNDLST
* each entry includes a mute on the noise channel, just as padding
* note that the volumes never change, so the list could be made shorter
* by only setting the volume once at the beginning.

*440 CALL SOUND(922,196,6,233,6,311,0)
DATA >0A8B,>2393,>A01E,>B3C8,>16D0,>FF38

*450 CALL SOUND(922,392,6,466,6,622,0)
DATA >0A8D,>1193,>A00F,>B3C4,>0BD0,>FF38

*460 CALL SOUND(461,294,6,466,6,587,0)
DATA >0A8C,>1793,>A00F,>B3CF,>0BD0,>FF1C

*470 CALL SOUND(230,294,6,392,6,466,0)
DATA >0A8C,>1793,>AD11,>B3C0,>0FD0,>FF0E

*480 CALL SOUND(230,294,6,440,6,523,0)
DATA >0A8C,>1793,>AE0F,>B3C6,>0DD0,>FF0E

*490 CALL SOUND(461,294,6,466,6,587,0)
DATA >0A8C,>1793,>A00F,>B3CF,>0BD0,>FF1C

*500 CALL SOUND(461,277,6,523,6,622,0)
DATA >0A84,>1993,>A60D,>B3C4,>0BD0,>FF1C

*510 CALL SOUND(922,208,6,262,6,311,0)
DATA >0A8A,>2193,>AB1A,>B3C8,>16D0,>FF38

*520 CALL SOUND(461,156,6,392,6,523,0)
DATA >0A8D,>2C93,>AD11,>B3C6,>0DD0,>FF1C

*530 CALL SOUND(461,156,6,370,6,523,0)
DATA >0A8D,>2C93,>AE12,>B3C6,>0DD0,>FF1C

*540 CALL SOUND(461,196,6,349,6,466,0)
DATA >0A8B,>2393,>A114,>B3C0,>0FD0,>FF1C

*550 CALL SOUND(461,196,6,311,6,466,0)
DATA >0A8B,>2393,>A816,>B3C0,>0FD0,>FF1C

*560 CALL SOUND(461,175,6,294,6,466,0)
DATA >0A8F,>2793,>AC17,>B3C0,>0FD0,>FF1C

*570 CALL SOUND(461,165,6,277,6,466,0)
DATA >0A86,>2A93,>A419,>B3C0,>0FD0,>FF1C

*580 CALL SOUND(922,156,6,208,6,262,0)
DATA >0A8D,>2C93,>AA21,>B3CB,>1AD0,>FF38

*590 CALL SOUND(922,175,6,311,6,415,0)
DATA >0A8F,>2793,>A816,>B3CE,>10D0,>FF38

*600 CALL SOUND(461,233,6,311,6,392,0)
DATA >0A80,>1E93,>A816,>B3CD,>11D0,>FF1C

*610 CALL SOUND(230,233,6,262,6,311,0)
DATA >0A80,>1E93,>AB1A,>B3C8,>16D0,>FF0E

*620 CALL SOUND(230,233,6,294,6,349,0)
DATA >0A80,>1E93,>AC17,>B3C1,>14D0,>FF0E

*630 CALL SOUND(461,233,6,330,6,392,0)
DATA >0A80,>1E93,>A315,>B3CD,>11D0,>FF1C

*640 CALL SOUND(461,233,6,277,6,415,0)
DATA >0A80,>1E93,>A419,>B3CE,>10D0,>FF1C

*650 CALL SOUND(461,220,6,294,6,349,0)
DATA >0A8C,>1F93,>AC17,>B3C1,>14D0,>FF1C

*660 CALL SOUND(230,220,6,247,6,294,0)
DATA >0A8C,>1F93,>A51C,>B3CC,>17D0,>FF0E

*670 CALL SOUND(230,220,6,262,6,311,0)
DATA >0A8C,>1F93,>AB1A,>B3C8,>16D0,>FF0E

*680 CALL SOUND(461,208,6,294,6,349,0)
DATA >0A8A,>2193,>AC17,>B3C1,>14D0,>FF1C

*690 CALL SOUND(461,208,6,294,6,392,0)
DATA >0A8A,>2193,>AC17,>B3CD,>11D0,>FF1C

*700 CALL SOUND(1383,196,6,233,6,311,0)
DATA >0A8B,>2393,>A01E,>B3C8,>16D0,>FF53

* AND SILENCE AFTER THE LAST NOTE, THEN END
DATA >049F,>BFDF,>FF00

 

 

 

Once that makes sense, have a look at this one. In this next one, the code is exactly the same, but I remove redundant data from the sound list to cut it nearly in half. Since the sound chip doesn't stop playing a tone unless it is told to, the sound list actually only needs to contain the CHANGES from the last note. It plays exactly the same but takes nearly half the space.

 

 

* Plays the opening to 'Somewhere over the Rainbow'
* direct conversion from CALL SOUND statements in TI BASIC

* This is a more advanced sound list that removes
* redundant data to save space! You'll see that it
* sounds exactly the same! Instead of 368 bytes, it
* takes 226 bytes, saving 142 bytes on the sound list

AORG >6000

* Standard cartridge header	
DATA >AA01,>0100,>0000,>600C,>0000,>0000
DATA >0000,>601C,>0B47,>504C,>2052,>4149
DATA >4E42,>4F57

* >601C (entry point)	
I/O 0,SNDLST

* loop forever (press QUIT to exit)
* note the music will stop on its own
LP
BR LP

SNDLST
* this version contains an optimized list. Because the sound
* chip keeps playing the last data sent to it, each entry in
* the sound list only needs to contain the changed information!
* the first entry should still set up everything, though!

*440 CALL SOUND(922,196,6,233,6,311,0)
DATA >0A8B,>2393,>A01E,>B3C8,>16D0,>FF38

* in particular, we never need to change the volume again
* after the first case, as it never changes. We also do not
* need to load frequencies on channels that don't change.

* note that the sound lists are no longer a direct replacement
* for the CALL SOUND statement, though, since they are dependent
* on the /previous/ CALL SOUND too. However, this can save a lot
* of memory space and improves performance since redundant data
* doesn't have to be copied to the sound chip.

*450 CALL SOUND(922,392,6,466,6,622,0)
DATA >068D,>11A0,>0FC4,>0B38

*460 CALL SOUND(461,294,6,466,6,587,0)
DATA >048C,>17CF,>0B1C

*470 CALL SOUND(230,294,6,392,6,466,0)
DATA >04AD,>11C0,>0F0E

*480 CALL SOUND(230,294,6,440,6,523,0)
DATA >04AE,>0FC6,>0D0E

*490 CALL SOUND(461,294,6,466,6,587,0)
DATA >04A0,>0FCF,>0B1C	

*500 CALL SOUND(461,277,6,523,6,622,0)
DATA >0684,>19A6,>0DC4,>0B1C

*510 CALL SOUND(922,208,6,262,6,311,0)
DATA >068A,>21AB,>1AC8,>1638

*520 CALL SOUND(461,156,6,392,6,523,0)
DATA >068D,>2CAD,>11C6,>0D1C

*530 CALL SOUND(461,156,6,370,6,523,0)
DATA >02AE,>121C

*540 CALL SOUND(461,196,6,349,6,466,0)
DATA >068B,>23A1,>14C0,>0F1C

*550 CALL SOUND(461,196,6,311,6,466,0)
DATA >02A8,>161C

*560 CALL SOUND(461,175,6,294,6,466,0)
DATA >048F,>27AC,>171C

*570 CALL SOUND(461,165,6,277,6,466,0)
DATA >0486,>2AA4,>191C

*580 CALL SOUND(922,156,6,208,6,262,0)
DATA >068D,>2CAA,>21CB,>1A38

*590 CALL SOUND(922,175,6,311,6,415,0)
DATA >068F,>27A8,>16CE,>1038

*600 CALL SOUND(461,233,6,311,6,392,0)
DATA >0480,>1ECD,>111C

*610 CALL SOUND(230,233,6,262,6,311,0)
DATA >04AB,>1AC8,>160E

*620 CALL SOUND(230,233,6,294,6,349,0)
DATA >04AC,>17C1,>140E

*630 CALL SOUND(461,233,6,330,6,392,0)
DATA >04A3,>15CD,>111C

*640 CALL SOUND(461,233,6,277,6,415,0)
DATA >04A4,>19CE,>101C

*650 CALL SOUND(461,220,6,294,6,349,0)
DATA >068C,>1FAC,>17C1,>141C

*660 CALL SOUND(230,220,6,247,6,294,0)
DATA >04A5,>1CCC,>170E

*670 CALL SOUND(230,220,6,262,6,311,0)
DATA >04AB,>1AC8,>160E

*680 CALL SOUND(461,208,6,294,6,349,0)
DATA >068A,>21AC,>17C1,>141C

*690 CALL SOUND(461,208,6,294,6,392,0)
DATA >02CD,>111C

*700 CALL SOUND(1383,196,6,233,6,311,0)
DATA >068B,>23A0,>1EC8,>1653

* AND SILENCE AFTER THE LAST NOTE, THEN END
DATA >049F,>BFDF,>FF00

 

 

You should be able to assemble and run either of these, they are set up as cartridges. I've also provided pre-compiled binaries ready to load in Classic99 or whatever else, and the source is also in this zip file.

 

gplSound.zip

 

I'll attach the TI BASIC program too. This is a TIFILES format file.

 

RAINBOW.zip

 

Thank you very much. Where did you find this:

 

>8x xx - Channel 1 frequency

>9x - Channel 1 volume

>Ax xx - channel 2 frequency

>Bx - channel 2 volume

>Cx xx - channel 3 frequency

>Dx - channel 3 volume

>Ex - Noise type

>Fx - noise volume

 

I have never seen this list anywhere I look. GPL manuals, EA manual, RyteData mags, Micropendiums? Where did it come from?

Edited by RXB
Link to comment
Share on other sites

Thank you very much. Where did you find this:

 

>8x xx - Channel 1 frequency

>9x - Channel 1 volume

>Ax xx - channel 2 frequency

>Bx - channel 2 volume

>Cx xx - channel 3 frequency

>Dx - channel 3 volume

>Ex - Noise type

>Fx - noise volume

 

I have never seen this list anywhere I look. GPL manuals, EA manual, RyteData mags, Micropendiums? Where did it come from?

 

Page 314 in the Editor Assembler manual shows a similar chart, but they show it as bit patterns instead of hex values, which is less clear.

 

You can also find the information laid out in a much more clear and comprehensive manner in the TI Tech pages, written and maintained by Theirry Nouspikel.

 

Main Page: http://www.nouspikel.com/ti99/titechpages.htm

Sound Chip: http://nouspikel.group.shef.ac.uk//ti99/tms9919.htm

 

Adamantyr

Link to comment
Share on other sites

Thank you very much. Where did you find this:

 

>8x xx - Channel 1 frequency

>9x - Channel 1 volume

>Ax xx - channel 2 frequency

>Bx - channel 2 volume

>Cx xx - channel 3 frequency

>Dx - channel 3 volume

>Ex - Noise type

>Fx - noise volume

 

I have never seen this list anywhere I look. GPL manuals, EA manual, RyteData mags, Micropendiums? Where did it come from?

 

I wrote it. But this information comes from the very first place I told you to look, the Editor/Assembler manual. Adamantyr just gave you the page number again. It's also been said a few times in this thread. I just tried to put it all in a more straight-forward manner for someone who isn't used to bit-twiddling. :)

 

If you want to get into the more advanced features of the TI, you may have to pay attention even when people are talking "assembly". GPL still has to interact with the hardware to do some things. If you look at the console and BASIC GROMs, when they communicates with the sound chip they are not consistent -- sometimes they write directly to @>8400 (the sound chip hardware), and sometimes they set up a sound list via I/O. So it's important to understand what's going on under the hood for a deeper understanding. And since GPL documentation is sparse, that means you may have to read assembly documentation. But GPL can do nearly everything assembly can. You can write bytes directly to @>8400 with a ST commaand, and you can manually set up the scratchpad for sound lists by using DST (but I/O is much simpler - I learned that from your Howto, actually! So we are teaching each other!) Indeed, the only thing GPL can't do in an assembly manner is direct access to the VDP and GROM ports (both will be corrupted by the interpreter if you try).

 

The point is: read everything, adapt what's relevant!

 

BTW: please edit your quotes to only what's necessary for your reply? :)

  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...