Just Jeff Posted April 8, 2017 Share Posted April 8, 2017 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 2 Quote Link to comment Share on other sites More sharing options...
Kylearan Posted April 13, 2017 Share Posted April 13, 2017 (edited) 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 April 13, 2017 by Kylearan Quote Link to comment Share on other sites More sharing options...
Derek Andrews Posted April 20, 2017 Share Posted April 20, 2017 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 ; tonesmidE equ $17midF equ $15midG equ $13midA equ $11midB equ $0Fsilent equ 0crotchet equ 12 ; durationsminim equ 24inter equ 3musicend 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 1 Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted April 20, 2017 Author Share Posted April 20, 2017 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 Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted April 27, 2017 Author Share Posted April 27, 2017 (edited) 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 April 27, 2017 by BNE Jeff Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted April 29, 2017 Author Share Posted April 29, 2017 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 Quote Link to comment Share on other sites More sharing options...
RevEng Posted April 29, 2017 Share Posted April 29, 2017 This isn't valid syntax: "lda Song,y+1" Use this instead: "lda Song+1,y" Quote Link to comment Share on other sites More sharing options...
Just Jeff Posted April 29, 2017 Author Share Posted April 29, 2017 Thanks! I changed it, but strangely- it didn't make a difference. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.