Jump to content
IGNORED

My Music Engine


Recommended Posts

We're putting the band back together!

 

I've never attempted to write a music engine or really look into one. It wasn't that hard but I bet someone knows a more efficient and/or fancy way to do it. (Yes I know my table is inside the loop but its just easier to look at that way for now. Also I snagged the note values from Kirk Israel) I haven't quite figured out why I get a high pitched beep when the song repeats either.

MusicRoutine:
	dec NoteDuration	; Have we gone 30 or 60 frames yet?
	bne OSwait		; If not, then skip to the end
	inc NoteCounter		; Go to the next note
	inc NoteCounter		; Twice because durations are in the table as well
	ldx NoteCounter		; Put the note location in the index
	lda Tune,x			; Load the frequency
	sta AUDF0			; Store the frequency
	lda Tune,x+1		; Load the duration
	sta NoteDuration	; Store the new duration in RAM
	lda NoteCounter		; See if its time to repeat the song
	cmp #72				; 36 notes plus 36 note durations
	beq ResetCounter	; if so, reset the counter
	jmp OSwait			; Don't reset the counter
	
ResetCounter
	lda #0
	sta NoteCounter
	jmp OSwait
	
Tune: 					; 35 notes and pauses
	.byte 0,0,24,30,27,30,30,30,27,30,24,28,-1,2,24,28,-1,2,24,60 	;9 notes
	.byte 27,28,-1,2,27,28,-1,2,27,60,24,30,20,28,-1,2,20,60 		;9
	.byte 24,30,27,30,30,30,27,30,24,28,-1,2,24,28,-1,2,24,28,-1,2 	;10
	.byte 24,30,27,28,-1,2,27,30,24,30,27,30,30,120 				;7

OSwait:

Tune2.asm

Tune.bin

  • Like 2
Link to comment
Share on other sites

I haven't quite figured out why I get a high pitched beep when the song repeats either.

 

You're loading and storing frequency and duration values based on NoteCounter before testing if it has reached #72. That means your routine will use values #72 and #73 from your table as well, which are no longer valid tune values but in fact are the values of the "sta WSYNC" immediately following the table (which is $85 $02, resulting in a frequency value of $85 played for 2 frames).

 

Check for #70 instead and it should work. Or better yet, do the check before you fetch tune data and you should be able to get rid of the two leading 0 bytes in your data as well.

 

(Or so I think. Haven't tested it. :-))

Edited by Kylearan
Link to comment
Share on other sites

I have something very similar that I have written for the Interton family of games in Signetics 2650. A couple of enhancements though. I set up equates to make it easier to convert the music to code:

 

midD equ $1A ; tones
midE equ $17
midF equ $15
midG equ $13
midA equ $11
midB equ $0F
silent equ 0
crotchet equ 12 ; durations
minim equ 24
inter equ 3
musicend equ $ff ;stop

'inter' is a short pause I insert between notes.

'music end' is used by the subroutine to see when the tune has ended.

It is also possible to pass the start address of the music data to the subroutine via an indirect RAM address. Thus I would have a standalone subroutine that simply needs the music code in a block of data with a 'musicend' byte to indicate its termination. This could be one of many tunes. Simply set the address of the block of music you want to play in to RAM and call the subroutine.

 

This is what the music looks like:

 

twinkle:
db silent
db inter
db midD
db crotchet
db silent
db inter
db midD
db crotchet
db silent
db inter
db midA
db crotchet ......

 

... db minim
db silent
db musicend

  • Like 1
Link to comment
Share on other sites

 

You're loading and storing frequency and duration values based on NoteCounter before testing if it has reached #72. That means your routine will use values #72 and #73 from your table as well, which are no longer valid tune values but in fact are the values of the "sta WSYNC" immediately following the table (which is $85 $02, resulting in a frequency value of $85 played for 2 frames).

 

Check for #70 instead and it should work. Or better yet, do the check before you fetch tune data and you should be able to get rid of the two leading 0 bytes in your data as well.

 

(Or so I think. Haven't tested it. :-))

 

That was pretty much what was going on- thanks... But to fix it, what I did was load NoteCounter into the index register (x) before I increment NoteCounter twice (instead of the other way around) which allowed me to get rid of those two zeros and check for #70 instead of #72 as well.

 

I still had a split second high pitched beep when I would first launch it which I believe was an effect of initializing the volume level at $7 at the start of the program. So I also initialized AUDF0, with the first note of the song so its now unnoticeable.

 

Which brings me to the next question... Using this method, there seems to be that there are no frequency values that would make true silence between notes- correct? (The separations I have between identical notes is actually a different note, not a break.)

Tune.bin

Tune3.asm

Link to comment
Share on other sites

I have something very similar that I have written for the Interton family of games in Signetics 2650. A couple of enhancements though. I set up equates to make it easier to convert the music to code:

 

midD equ $1A ; tones

midE equ $17

midF equ $15

midG equ $13

midA equ $11

midB equ $0F

silent equ 0

crotchet equ 12 ; durations

minim equ 24

inter equ 3

musicend equ $ff ;stop

 

'inter' is a short pause I insert between notes.

'music end' is used by the subroutine to see when the tune has ended.

It is also possible to pass the start address of the music data to the subroutine via an indirect RAM address. Thus I would have a standalone subroutine that simply needs the music code in a block of data with a 'musicend' byte to indicate its termination. This could be one of many tunes. Simply set the address of the block of music you want to play in to RAM and call the subroutine.

 

Cool I think I'll do something like this. Took me a minute to figure out your British music terms..

Edited by BNE Jeff
Link to comment
Share on other sites

OK.. I created a digital version that is working, but is way,way out of tune. I've re-checked my math but can't quite figure out where I went wrong,

 

Well commented .asm attached if anyone wants to take a look.. ;-)

; Digital Mary Had a Little Lamb
; 
; 29 April 2017

; Mary Had A Little Lamb
; EDCDEEE
; DDDEGG
; EDCDEEE
; EDDEDC

; dasm MHALL.asm -f3 -v0 -sMHALL.sym -lMHALL.lst -oMHALL.bin
; Attempt to produce a simple in-tune song

    PROCESSOR 6502
    
    include vcs.h      
    
;==============================================================================
; Define Notes
;==============================================================================

C_FIVE    = 141  ; C5 is 523.25 Hz. 6507 runs at 1,193,333 Hz.
		; 1,193,333/523.25= 2280.6 machine cycles
		; Divide by 2 for each half of the square wave
		; equals 1140.3 machine cycles
		; the wait loops are 8 cycles which means I need to loop
		; through approximately 142 times for each half wave
		; Subtract 1 for the 5 cycle delay in initial AUDV0 loadings
D_FIVE    = 125  ; 
E_FIVE    = 112  ; 
F_FIVE    = 105  ; 
G_FIVE    = 94   ; 
A_FIVE    = 83   ; 
B_FIVE    = 74   ; 

END_SONG  = 0	 ;

WHOLE_NOTE = 255
HALF_NOTE = 128
QUARTER_NOTE = 64

 
    SEG.U VARS
    
    ORG $80             

    ; Start of Cartridge
    ; Tell DASM to start here. RAM begins at $80
    
Hold5:			ds 1	; Used to hold wave high or low
CurrentNoteDuration: 	ds 1	; Quarter note, half note, etc
CurrentNote:		ds 1	; Which note the song is on

    ; define the segment for code
    SEG CODE    
    
    ; 2K ROM starts at $F800, 4K ROM starts at $F000
    ORG $F800
    
     
InitSystem:

	sei			; Set Interrupt
	cld  			; Clear the decimal bit.
	ldx #$FF		; Start at the top of the stack
	txs			; Transfer to the stack
	lda #0		
ClearMem:
	sta 0,X		; Store zero at (0+X)
	dex		; Do all of RAM
	bne ClearMem	; Repeat if we are not down to zero

;==============================================================================
; Load Tune
;==============================================================================

LoadTune:
	lda Song,y		; Load the current note of the song (half wave value)
	sta Hold5		; Store it in RAM
	lda Song,y+1		; Load the duration of the note
	sta CurrentNoteDuration ; Store it in RAM
           
Tone:
	lda	#$0F          ; 2 set volume high for high part of square wave
    sta AUDV0           ; 3
;==============================================================================
; Wave High
;==============================================================================
WaitHigh:			; Loop to hold high part of wave
	dec Hold5		; 5 
	bne WaitHigh		; 3 8 8 machine cycles per loop through
WaitHighFine:			; Future use for fine tuning notes with a 5 cycle loop
	;dex			; Future use for fine tuning notes with a 5 cycle loop
	;bne WaitHighFine	; 
	lda Song,y		; 4 Re-load the current note of the song
	sta Hold5		; 3 Store it in RAM for low half of the wave

;==============================================================================
; Wave Low
;==============================================================================
Interval:
    lda #$00            ; 2 Turn off volume for bottom of square wave
    sta AUDV0           ; 3
WaitLow:				; 
	dec Hold5			; 5
	bne WaitLow			; 3 8 Holds 8 with the branch taken
WaitLowFine:
	;dex				; Future use
	;bne WaitLowFine		; 
	
;==============================================================================
; Next CurrentNote?
;==============================================================================
	dec CurrentNoteDuration ; 5
	bne Tone		; 3
;==============================================================================
; Get Next CurrentNote
;==============================================================================	
	inc CurrentNote			; Increment to next note
	inc CurrentNote			; Again to skip over duration
	lda CurrentNote
	tay				; Put it in index
	lda Song,y			; Load the note
	beq StartOver			; if END_SONG then go to reset
	sta Hold5			; otherwise, store in RAM
	lda Song,y+1			; load the note duration
	sta CurrentNoteDuration	        ; Store it in RAM
    jmp LoadTune			; Back to the top
    
StartOver:
	ldy #0
	sty CurrentNote
	sty Hold5
	sty CurrentNoteDuration
	jmp LoadTune
    
Song:
    byte E_FIVE,HALF_NOTE,D_FIVE,HALF_NOTE,C_FIVE,HALF_NOTE
    byte D_FIVE,HALF_NOTE,E_FIVE,HALF_NOTE,E_FIVE,HALF_NOTE,E_FIVE,WHOLE_NOTE
    byte D_FIVE,HALF_NOTE,D_FIVE,HALF_NOTE,D_FIVE,WHOLE_NOTE
    byte E_FIVE,HALF_NOTE,G_FIVE,HALF_NOTE,G_FIVE,WHOLE_NOTE
    byte E_FIVE,HALF_NOTE,D_FIVE,HALF_NOTE,C_FIVE,HALF_NOTE	
    byte D_FIVE,HALF_NOTE,E_FIVE,HALF_NOTE,E_FIVE,HALF_NOTE,E_FIVE,HALF_NOTE
    byte E_FIVE,HALF_NOTE,D_FIVE,HALF_NOTE,D_FIVE,HALF_NOTE	
    byte E_FIVE,HALF_NOTE,D_FIVE,HALF_NOTE,C_FIVE,WHOLE_NOTE	
    byte END_SONG


; End of Cartridge


    ORG $FFFA        ; set address to 6507 Interrupt Vectors 
    .WORD InitSystem ; NMI
    .WORD InitSystem ; RESET
    .WORD InitSystem ; IRQ

MHALL.bin

MHALL.asm

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...
 Share

  • Recently Browsing   0 members

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