Explore the Gigatron music engine, using GLCC

Using, learning, programming and modding the Gigatron and anything related.
Forum rules
Be nice. No drama.
at67
Posts: 479
Joined: 14 May 2018, 08:29

Re: Explore the Gigatron music engine, using GLCC

Post by at67 »

denjhang wrote: 19 Mar 2022, 07:59 I want to make a vibrato-like effect, which requires the gigatron to emit audio frequencies that don't exist in the ROM's note table. These frequency values may lie between two adjacent pitches in the note table. So I made some small changes to store relative pitch changes in my note array. The increased and decreased pitch values are then written to keyH and keyL, but I find that my pitch adjustment algorithm seems to be somewhat imprecise. I'm not sure if keyL will exceed 100.
The keyH:keyL pair are used to increment oscH:oscL per channel, for efficiency Marcel coded this using a 15bit value in keyH:keyL that spans bits 7:0 in keyH and bits 6:0 in keyL; this means that bit 7 in keyL should be 0, (i.e. the top most bit of keyL=0). To answer your above question, keyL should not exceed 127.

In practice if you want to convert a 15bit linear value to a 15bit Gigatron internal frequency you do the following:

1) Start off with a 16bit unsigned value and mask off the top 15 bits.
2) Right shift the low byte by 1 bit.
3) Set the high bit of the low byte to zero if your right shift did not do it.

Or use a look up table, you only need a 256 byte look up table for the low byte; (technically you could get away with 128 bytes spanned in an interlace fashion across a 256byte page, which would leave 128 single interlaced bytes free for something else).

See this for a more comprehensive explanation and additional notes about wavX and wavA.
https://github.com/kervinck/gigatron-ro ... /Audio.txt

Here is the actual native code that updates each individual sound channel:

Code: Select all

ld(0x7f)                        #6 Update sound channel
anda([Y,oscL])                  #7
adda([Y,keyL])                  #8
st([Y,oscL])                    #9
anda(0x80,X)                    #10
ld([X])                         #11
adda([Y,oscH])                  #12
adda([Y,keyH])                  #13
st([Y,oscH])                    #14
anda(0xfc)                      #15
xora([Y,wavX])                  #16
ld(AC,X)                        #17
ld([Y,wavA])                    #18
ld(soundTable>>8,Y)             #19
adda([Y,X])                     #20
bmi(pc()+3)                     #21
bra(pc()+3)                     #22
anda(63)                        #23
ld(63)                          #23(!)
adda([sample])                  #24
st([sample])                    #25
denjhang
Posts: 50
Joined: 02 May 2021, 01:25
Location: yuenan
Contact:

Re: Explore the Gigatron music engine, using GLCC

Post by denjhang »

at67 wrote: 19 Mar 2022, 10:41
denjhang wrote: 19 Mar 2022, 07:59 I want to make a vibrato-like effect, which requires the gigatron to emit audio frequencies that don't exist in the ROM's note table. These frequency values may lie between two adjacent pitches in the note table. So I made some small changes to store relative pitch changes in my note array. The increased and decreased pitch values are then written to keyH and keyL, but I find that my pitch adjustment algorithm seems to be somewhat imprecise. I'm not sure if keyL will exceed 100.
The keyH:keyL pair are used to increment oscH:oscL per channel, for efficiency Marcel coded this using a 15bit value in keyH:keyL that spans bits 7:0 in keyH and bits 6:0 in keyL; this means that bit 7 in keyL should be 0, (i.e. the top most bit of keyL=0). To answer your above question, keyL should not exceed 127.

In practice if you want to convert a 15bit linear value to a 15bit Gigatron internal frequency you do the following:

1) Start off with a 16bit unsigned value and mask off the top 15 bits.
2) Right shift the low byte by 1 bit.
3) Set the high bit of the low byte to zero if your right shift did not do it.

Or use a look up table, you only need a 256 byte look up table for the low byte; (technically you could get away with 128 bytes spanned in an interlace fashion across a 256byte page, which would leave 128 single interlaced bytes free for something else).

See this for a more comprehensive explanation and additional notes about wavX and wavA.
https://github.com/kervinck/gigatron-ro ... /Audio.txt

Here is the actual native code that updates each individual sound channel:

Code: Select all

ld(0x7f)                        #6 Update sound channel
anda([Y,oscL])                  #7
adda([Y,keyL])                  #8
st([Y,oscL])                    #9
anda(0x80,X)                    #10
ld([X])                         #11
adda([Y,oscH])                  #12
adda([Y,keyH])                  #13
st([Y,oscH])                    #14
anda(0xfc)                      #15
xora([Y,wavX])                  #16
ld(AC,X)                        #17
ld([Y,wavA])                    #18
ld(soundTable>>8,Y)             #19
adda([Y,X])                     #20
bmi(pc()+3)                     #21
bra(pc()+3)                     #22
anda(63)                        #23
ld(63)                          #23(!)
adda([sample])                  #24
st([sample])                    #25

Since the keyH, keyL and the like obtained in GLCC are actually integers, I am also used to using integers instead of binary (I am not good at operating binary numbers). So I compiled a very simple formula to shift the pitch without looking up a table.

Code: Select all

struct Note *play_note(struct Note *note, int stepCount){
        while (stepCount == note->start_step) {
          channel_t *chan = &channel(note->ch);
          int off = note->noteoffset * 2;
		  int keyL = SYS_Lup(notesTable + off - 2);
		  int keyH = SYS_Lup(notesTable + off - 1);
//		  int key_sum,keyL_new,keyH_new,key_sum_new;
//		  int keyL_new,keyH_new,key_sum_new;
		  
          chan->wavA = 128 - note->volume;
          chan->wavX = note->waveform;
		  
		  if(note->relative_pitch==0){
          chan->keyL = SYS_Lup(notesTable + off - 2);
          chan->keyH = SYS_Lup(notesTable + off - 1);			  			  
		  }else{
			  //
			  if(keyL+note->relative_pitch<0){
				  chan->keyL=-(keyL+note->relative_pitch)%127;
				  chan->keyH=keyH+(keyL+note->relative_pitch)/127;
			  }
			  //
			  else if(keyL+note->relative_pitch>127){
				  chan->keyL=(keyL+note->relative_pitch)%127;
				  chan->keyH=keyH+(keyL+note->relative_pitch)/127;
				 				  
			  }else{
				  chan->keyH=keyH;
				  chan->keyL=keyL+note->relative_pitch;
			  }
			
//			chan->keyL = keyL_new;
//		    chan->keyH = keyH_new;		

		  }
		printf("keyH=%i,keyL=%i,relative_pitch=%i\n",chan->keyH,chan->keyL,note->relative_pitch);		 		  
        note += 1;
        }
        return note;
}
Attachments
step18_v5a_32k.gt1
(7.68 KiB) Downloaded 19 times
step18.c
(2.51 KiB) Downloaded 19 times
denjhang
Posts: 50
Joined: 02 May 2021, 01:25
Location: yuenan
Contact:

Re: Explore the Gigatron music engine, using GLCC

Post by denjhang »

After the modification, my pitch bend calculation formula is more accurate. (There seems to be some glitches in the previous ones)

Code: Select all

struct Note *play_note(struct Note *note, int stepCount){
        while (stepCount == note->start_step) {
          channel_t *chan = &channel(note->ch);
          int off = note->noteoffset * 2;
		  int keyL = SYS_Lup(notesTable + off - 2);
		  int keyH = SYS_Lup(notesTable + off - 1);
//		  int key_sum,keyL_new,keyH_new,key_sum_new;
//		  int keyL_new,keyH_new,key_sum_new;
		  
          chan->wavA = 128 - note->volume;
          chan->wavX = note->waveform;
		  
		  if(note->relative_pitch==0){
          chan->keyL = SYS_Lup(notesTable + off - 2);
          chan->keyH = SYS_Lup(notesTable + off - 1);			  			  
		  }else{
			  //If keyL+pitch bend value is less than 0
			  if(keyL+note->relative_pitch<0){
				  chan->keyL=127+(keyL+note->relative_pitch)%127;
				  chan->keyH=keyH+(keyL+note->relative_pitch)/127-1;
			  }
			  //If keyL+pitch bend value is greater than 127
			  else if(keyL+note->relative_pitch>127){
				  chan->keyL=(keyL+note->relative_pitch)%127;
				  chan->keyH=keyH+(keyL+note->relative_pitch)/127;
				 				  
			  }else{
				  chan->keyH=keyH;
				  chan->keyL=keyL+note->relative_pitch;
			  }
			
//			chan->keyL = keyL_new;
//		    chan->keyH = keyH_new;		

		  }
//		printf("keyH=%i,keyL=%i,relative_pitch=%i\n",chan->keyH,chan->keyL,note->relative_pitch);		 		  
        note += 1;
        }
        return note;
}

Attachments
step21.c
(2.87 KiB) Downloaded 20 times
step21_v5a_32k.gt1
(7.85 KiB) Downloaded 19 times
Post Reply