moogah Posted June 3, 2006 Report Share Posted June 3, 2006 So, after nearly a year of studying and soldering I've finally got a project that is worth writing some of my own code for (hallucinogen's 808 clone). I plan on using a modifided MBSeq to trigger the 808 through DOUT pins, saving me the need to build an AOUT which doesn't have enough pins for all the sounds anyway. Accent will be achieved by sending some voltage level (derived with a simple voltage divider) to all the instruments and using a switch that is also triggered by a DOUT pin (this DOUT module is handy!). So, in order for this design to work each instrument needs 3 things: a short 5v pulse to trigger it, a switch to turn the accent on/off via another 5v signal and a voltage level for the accent. Now, as it is the SEQ has just about everything I need, so I really do need to do much work. All I really need to do is set DOUT pins along with the midi events that are being triggered. This has two advantages: it's simple and it allows the sequecer to behave just like a normal MBSeq to external equipment (so I can write basslines right along with the drums ;D). Now, after tracing through the source code a bit I've identified midi_event.inc as the file where most of my changes will need to be. When a midi note is triggered in a drum track I need to first read it's value to determine which instrument it is and then set the appropriate DOUT pin(s). Here is the code fragment that most interests me:MIDI_EVNT_Send call MIOS_MIDI_BeginStream ;; branch depending on MIDI status SET_BSR MIDI_EVNT0 swapf MIDI_EVNT0, W, BANKED andlw 0x07 JUMPTABLE_2BYTES_UNSECURE ; 8 entries rgoto MIDI_EVNT_Send_8x ; Note Off rgoto MIDI_EVNT_Send_9x ; Note On rgoto MIDI_EVNT_Send_Ax ; Aftertouch rgoto MIDI_EVNT_Send_Bx ; Controller rgoto MIDI_EVNT_Send_Cx ; Program Change rgoto MIDI_EVNT_Send_Dx ; Channel Pressure rgoto MIDI_EVNT_Send_Ex ; Pitch Bend rgoto MIDI_EVNT_Send_Fx ; AOUT ;; sending three bytes: MIDI_EVNT_Send_8x ; Note Off MIDI_EVNT_Send_9x ; Note OnNow, aside from the fact that I don't yet understand how the JUMPTABLE implements a switch statement there is something quite suspicious about this code.. see it yet? The functions for sending note on and off messages are empty! Surely this cannot be correct so I must not be looking in the right place, no?Moving on from there for a miniute there is one more dilemma, to trigger the sounds I only need a very short gate signal, meaning that the note off event should be sent almost immediatly after the note on. Possibly this is easy to do by simply setting the gate length for the drum tracks to a very small value, but I suspect that even the smallest gate length is still a good bit longer than 1ms. Furthermore this would make it difficult to layer drum sounds from external equipment, so idealy there will be special handling of notes sent to DOUT pins in order to keep the gate length very short. Possibly I could simply add some simple differetiators in hardware to accomplish this as well, but additonal hardware just seems uneccessary at this point Quote Link to comment Share on other sites More sharing options...
stryd_one Posted June 3, 2006 Report Share Posted June 3, 2006 Jumping to any of the four of these does the same thing. All of these messages are 3 byte types. You can see 2 byte types next up.... ;; sending three bytes: MIDI_EVNT_Send_8x ; Note Off MIDI_EVNT_Send_9x ; Note On MIDI_EVNT_Send_Ax ; Aftertouch MIDI_EVNT_Send_Bx ; Controller movff MIDI_EVNT0, WREG call MIOS_MIDI_TxBufferPut movff MIDI_EVNT1, WREG andlw 0x7f call MIOS_MIDI_TxBufferPut movff MIDI_EVNT_VALUE, WREG andlw 0x7f call MIOS_MIDI_TxBufferPut rgoto MIDI_EVNT_Send_EndThe jumptable explanation will take longer to type so thats the next post Quote Link to comment Share on other sites More sharing options...
stryd_one Posted June 3, 2006 Report Share Posted June 3, 2006 MIDI_EVNT_Send call MIOS_MIDI_BeginStream ;; branch depending on MIDI status SET_BSR MIDI_EVNT0 swapf MIDI_EVNT0, W, BANKED andlw 0x07 JUMPTABLE_2BYTES_UNSECURE ; 8 entries rgoto MIDI_EVNT_Send_8x ; Note Off rgoto MIDI_EVNT_Send_9x ; Note On rgoto MIDI_EVNT_Send_Ax ; Aftertouch rgoto MIDI_EVNT_Send_Bx ; Controller rgoto MIDI_EVNT_Send_Cx ; Program Change rgoto MIDI_EVNT_Send_Dx ; Channel Pressure rgoto MIDI_EVNT_Send_Ex ; Pitch Bend rgoto MIDI_EVNT_Send_Fx ; AOUT ;; sending three bytes: MIDI_EVNT_Send_8x ; Note Off MIDI_EVNT_Send_9x ; Note OnOK we start with MIDI_EVNT0 which has the 1st byte in it. The swapf swaps the low and high nibble, which moves the channel to the 'left' and the midi message to the 'right'. For EG a Note On on channel 5 is 0x94. After the swapf, it's 0x49The AND 0x07 then clears the channel (that's the 0 in 07) and subtracts 8 from the event type (that's the 7). After this, our example would be 1 (Note it doesn't actually subtract 8, it ANDs with 7, but it has the effect of subtracting 8 )This effectively makes the jumptable move down 1 extra line and then run the appropriate code.If the example were a CC on channel 15, the byte would be 0xBE, which would change to 0xEB, then 0x0B and 0x03, so the jumptable causes 3 lines jumped afterwards, which puts it on rgoto MIDI_EVNT_Send_Bx ; ControllerDoes that make sense? You might like to take a look at the Seq and MIOS source to see how the jumptable and the MIOS_HLP_GetIndex_2bytes work. Quote Link to comment Share on other sites More sharing options...
moogah Posted June 5, 2006 Author Report Share Posted June 5, 2006 Thanks stryd, got quite a few AHA!'s in there. It's going to be some time before I get used to the fact that functions dont return unless you specify it! Same with how variables are handled, arguments aren't really passed etc..Good stuff tho, even with the C wrapper available I feel like I get a much better idea of what I'm working with in assembly. Quote Link to comment Share on other sites More sharing options...
illogik Posted June 5, 2006 Report Share Posted June 5, 2006 hey moogah;Moving on from there for a miniute there is one more dilemma, to trigger the sounds I only need a very short gate signal, meaning that the note off event should be sent almost immediatly after the note on. Possibly this is easy to do by simply setting the gate length for the drum tracks to a very small value, but I suspect that even the smallest gate length is still a good bit longer than 1ms. Furthermore this would make it difficult to layer drum sounds from external equipment, so idealy there will be special handling of notes sent to DOUT pins in order to keep the gate length very short. Possibly I could simply add some simple differetiators in hardware to accomplish this as well, but additonal hardware just seems uneccessary at this pointhave you talked to hallucinogen? he doesn't trigger the sounds with the aout; he also uses the dout (running with MIDIO128 but still; dout), he just put in some code so that the douts give ~1ms pulse; could be what you are looking forcheers Quote Link to comment Share on other sites More sharing options...
moogah Posted June 5, 2006 Author Report Share Posted June 5, 2006 Indeed I got the idea from him! I was hoping to draw him into the forum again, but I would like work through the problem myself as a learning excersise. Quote Link to comment Share on other sites More sharing options...
TK. Posted June 5, 2006 Report Share Posted June 5, 2006 The simplest solution is to clear the appr. DOUT registers within the USER_SR_Service_Finish hook after the serial chain has been updated. This ensures a pulse of 1 mS (if the SRIO update cycle is set to 1)See also http://www.midibox.org/forum/index.php?topic=2701.msg17627#msg17627Best Regards, Thorsten. Quote Link to comment Share on other sites More sharing options...
moogah Posted June 8, 2006 Author Report Share Posted June 8, 2006 !!Thats some good stuff right there! Thanks TK! Quote Link to comment Share on other sites More sharing options...
moogah Posted June 9, 2006 Author Report Share Posted June 9, 2006 I got some time to work on this today. Checking the velocity value was easy, but checking the note numbers was a bit more difficult. Here is what I've got (with a single accent and only the BD and SD): movlw 0x5a ; If velocity is > 90 cpfsgt MIDI_EVNT_VALUE MIOS_DOUT_PinSet1(ACCENT_PIN) ; Single level of accent shared by all instruments movf MIDI_ENVT1, 0 ; Move note value to W to use ANDL* andlw 11101011 ; "Binary complement" value for BD (Note#20), ; result is 0 when values match (?) movwf SOME_REG ; move W into a register to use DCFSNZ dcfsnz SOME_REG, 1 MIOS_DOUT_PinSet1(BD_PIN) ; play BD, pin cleared elsewhere andlw 11101010 ;" Binary Complement" for SD (#21) movwf SOME_REG dcfsnz SOME_REG, 1 MIOS_DOUT_PinSet1(SD_PIN) ; play SD, pin cleared elsewhere returnThe process of moving the note value into W to use the AND on it and then moving back into an F to check if it is zero seems suspicious to me. Although even doing all this for all 12 instruments adds less than a 10th of a millisec, by my calculations anyway. Is there a way to check if W is zero? Or perform an AND on a f?. I also have the feeling that "Binary Complement" is the wrong term too. Quote Link to comment Share on other sites More sharing options...
stryd_one Posted June 9, 2006 Report Share Posted June 9, 2006 Binary complement sounds about right to me... What about ANDWF? Quote Link to comment Share on other sites More sharing options...
johnh Posted June 9, 2006 Report Share Posted June 9, 2006 Binary complement sounds about right to me... What about ANDWF?Or maybe XORLW and not have to worry about the complement? Then you could just branch based on the zero flag... Quote Link to comment Share on other sites More sharing options...
moogah Posted June 10, 2006 Author Report Share Posted June 10, 2006 Thanks guys. XOR and BZ are what I'm looking for I think. The datasheet is a bit vague here but I think I can specify a relative branch location, right? something like PC+2?got a question right off the bat, how do I identify a binary number? 00010100b?EDIT:how about this: movf MIDI_ENVT1, 0 ; Move note value to W to use ANDL* xorlw 00010100 ; value for BD (Note#20) bz PlayBD ; if the result was 0 play BD PlayBD MIOS_DOUT_PinSet1(BD_PIN) ; play BD, pin cleared elsewhere movf MIDI_ENVT1, 0 ; Move note value to W to use ANDL* xorlw 00010101 ; value for BD (Note#20) bz PlaySD ; if the result was 0 play SD PlaySD MIOS_DOUT_PinSet1(SD_PIN) ; play BD, pin cleared elsewhere This brings me to another problem.. I had originally tried to use the debugger to get an idea of how the SEQ works, however, it doesn't get any further than the init loop as there is no external stimulus. Has anyone tried to setup a scenario for the SEQ? I assume this is not practical, so how have you gone about testing code fragments like this? Also, is there anything like a break statement to skip over the remaining checks? EDIT: Like this mabey: CheckBD movf MIDI_ENVT1, 0 ; Move note value to W to use XORLW xorlw 00010100 ; value for BD (Note#20) bnz CheckSD ; if the result was 0 play BD nop PlayBD MIOS_DOUT_PinSet1(BD_PIN) ; play BD, pin cleared elsewhere goto MIDI_EVNT_SendAx CheckSD movf MIDI_ENVT1, 0 ; Move note value to W to use ANDL* xorlw 00010101 ; value for BD (Note#20) bnz CheckCH ; if the result was 0 play BD nop PlaySD MIOS_DOUT_PinSet1(SD_PIN) ; play BD, pin cleared elsewhere goto MIDI_EVNT_SendAx The NOP's are there because, assuming I'm reading the datasheet right, the BNZ command will skip the next line if the Z bit is set (meaning the compare was a match) EDIT: (been a busy day!) I've got a few questions about this USER_SR_Service_Finish function. Some of the trouble I'm having is due to the fact I can barely understand German, much less tech-speak in German so I'm probably asking stuff that is in Hallucinogen's thread. It seems that the function has been updated since then as I don't see the call to CS_MENU_TIMER in the snippets from you or Hallucinogen there. Now, what is causing me trouble is understanding how the Registers work to address the DOUT. Does each register address one of the 595's? I see that the MIOS_DOUT_SRSet function sets the SR specified in WREG to the value of MIOS_PARAMETER1. However, if this is the case the code provided would be clearing nearly every LED on the device, certianly not what we were looking for. If I understand what is going on I will be addressing SR's 0x04 and 0x05 which clears the first two registers on the second DOUT board USER_SR_Service_Finish clrf MIOS_PARAMETER1 movlw 0x00 call MIOS_DOUT_SRSet movlw 0x01 call MIOS_DOUT_SRSet movlw 0x02 call MIOS_DOUT_SRSet return USER_SR_Service_Finish ;; ---[ handle with control surface variables (flashing cursor, etc) ]--- goto CS_MENU_TIMER Quote Link to comment Share on other sites More sharing options...
moogah Posted June 11, 2006 Author Report Share Posted June 11, 2006 Ok, new post time. This is what I've got done today MIDI_EVNT_Send_9x ; Note On movlw 0x5a ; If velocity is > 90 cpfsgt MIDI_EVNT_VALUE MIOS_DOUT_PinSet1(ACCENT_PIN) ; Single level of accent shared by all instruments CheckCH movf MIDI_ENVT1, 0 ; Move note value to W to use ANDL* xorlw CH_NOTE ; value for CH (Note#20) bnz CheckRS ; if the result was 0 play CH PlayCH MIOS_DOUT_PinSet1(CH_PIN) ; play CH, pin cleared elsewhere goto MIDI_EVNT_SendAx CheckRS movf MIDI_ENVT1, 0 ; xorlw RS_NOTE bnz CheckBD PlayRS MIOS_DOUT_PinSet1(RS_PIN) goto MIDI_EVNT_SendAx CheckBD movf MIDI_ENVT1, 0 xorlw BD_NOTE bnz CheckSD PlayBD MIOS_DOUT_PinSet1(BD_PIN) goto MIDI_EVNT_SendAx CheckSD movf MIDI_ENVT1, 0 xorlw SD_NOTE bnz CheckCP PlaySD MIOS_DOUT_PinSet1(SD_PIN) goto MIDI_EVNT_SendAx CheckCP movf MIDI_ENVT1, 0 xorlw CP_NOTE bnz CheckOH PlayCP MIOS_DOUT_PinSet1(CP_PIN) goto MIDI_EVNT_SendAx CheckOH movf MIDI_ENVT1, 0 xorlw OH_NOTE bnz CheckMA PlayOH MIOS_DOUT_PinSet1(OH_PIN) goto MIDI_EVNT_SendAx CheckMA movf MIDI_ENVT1, 0 xorlw MA_NOTE bnz CheckCB PlayMA MIOS_DOUT_PinSet1(MA_PIN) goto MIDI_EVNT_SendAx CheckCB movf MIDI_ENVT1, 0 xorlw CB_NOTE bnz CheckLT PlayCB MIOS_DOUT_PinSet1(CB_PIN) goto MIDI_EVNT_SendAx CheckLT movf MIDI_ENVT1, 0 xorlw LT_NOTE bnz CheckMT PlayLT MIOS_DOUT_PinSet1(LT_PIN) goto MIDI_EVNT_SendAx CheckMT movf MIDI_ENVT1, 0 xorlw MT_NOTE bnz CheckHT PlayMT MIOS_DOUT_PinSet1(MT_PIN) goto MIDI_EVNT_SendAx CheckHT movf MIDI_ENVT1, 0 xorlw HT_NOTE bnz CheckCY PlayHT MIOS_DOUT_PinSet1(HT_PIN) goto MIDI_EVNT_SendAx CheckCY movf MIDI_ENVT1, 0 xorlw CY_NOTE bnz MIDI_EVNT_Send_Ax PlayCY MIOS_DOUT_PinSet1(CY_PIN) and USER_SR_Service_Finish ;; ---[ handle with control surface variables (flashing cursor, etc) ]--- clrf MIOS_PARAMETER1 movlw 0x04 call MIOS_DOUT_SRSet movlw 0x05 call MIOS_DOUT_SRSet goto CS_MENU_TIMER Now, aside from being a bit long to look over I think this will run with a minimal impact on the SEQ. It would be nice if I didn't have to movwf MIDI_ENVT1 each time. Even so the worst case as I understand it is when a every instrument is triggered at the same time, meaning that around 300 instructions cycles will pass before everything has been triggered(EDIT: this doesn't take into account the number of instructions in each MIOS_DOUT_PinSet call). This is still only about 1/3 of a millisecond ontop of whatever latencies are already part of the SEQ. Now, what I need to do is define constants for each DOUT pin and for each note value, that part is fairly easy but a "memory map" for MIOS and the SEQ would be really nice so as to easily find registers that have not been used. The only question I have at this point is how best to assign a default value to these constants, putting them in USER_Init or somewhere nearby makes sense to me. There is one thing that has been bugging me about app_defines.h ;; track record structure SEQ_TRKSTATEx EQU 0x00 ; flags: see seq_core.inc (SEQ_TRKSTATE_*) SEQ_TRKMUTED0x EQU 0x01 ; muted steps 1-8 SEQ_TRKMUTED1x EQU 0x02 ; muted steps 9-16 SEQ_TRKPLYTICKx EQU 0x03 ; number of MIDI clock ticks until next step will be played SEQ_TRKSTEPx EQU 0x04 ; the track step SEQ_TRKPOSx EQU 0x05 ; the track position SEQ_TRKQUEUELx EQU 0x06 ; length counter ("note off" once it is zero) SEQ_D_TRKMUTED_0x EQU 0x00 ; muted steps 1- 4 SEQ_D_TRKMUTED_1x EQU 0x01 ; muted steps 5- 8 SEQ_D_TRKMUTED_2x EQU 0x02 ; muted steps 9-12 SEQ_D_TRKMUTED_3x EQU 0x03 ; muted steps 13-16 SEQ_D_TRKACC_0x EQU 0x04 ; accented steps 1- 4 SEQ_D_TRKACC_1x EQU 0x05 ; accented steps 5- 8 SEQ_D_TRKACC_2x EQU 0x06 ; accented steps 9-12 SEQ_D_TRKACC_3x EQU 0x07 ; accented steps 13-16 this shows different constants being assigned to the same register.. right? EDIT: I found the little note at the bottom of app_defines.h about what address space is left open. ;; free for your pleasure: 0x360-0x37f ;; and 0x380-0x3ff if the AIN handler is disabled BD_PIN EQU 0x360 SD_PIN EQU 0x361 LT_PIN EQU 0x362 MT_PIN EQU 0x363 HT_PIN EQU 0x364 OH_PIN EQU 0x365 CH_PIN EQU 0x366 CY_PIN EQU 0x367 CP_PIN EQU 0x368 CB_PIN EQU 0x369 RS_PIN EQU 0x36a MA_PIN EQU 0x36b ACCENT_PIN EQU 0x36c BD_NOTE EQU 0x36d SD_NOTE EQU 0x36e LT_NOTE EQU 0x36f MT_NOTE EQU 0x370 HT_NOTE EQU 0x371 OH_NOTE EQU 0x372 CH_NOTE EQU 0x373 CY_NOTE EQU 0x374 CP_NOTE EQU 0x375 CB_NOTE EQU 0x376 RS_NOTE EQU 0x377 MA_NOTE EQU 0x378 BD_ACCENT EQU 0x379 SD_ACCENT EQU 0x37a LT_ACCENT EQU 0x37b MT_ACCENT EQU 0x37c HT_ACCENT EQU 0x37d OH_ACCENT EQU 0x37e CH_ACCENT EQU 0x37f CY_ACCENT EQU 0x380 CP_ACCENT EQU 0x381 CB_ACCENT EQU 0x382 RS_ACCENT EQU 0x383 MA_ACCENT EQU 0x384 Quote Link to comment Share on other sites More sharing options...
stryd_one Posted June 11, 2006 Report Share Posted June 11, 2006 I assume that the byte you XORLW with will be different for each event yeh? Have you considered using one of the compare W with F commands? It might save you some code size and allow it to be more modular. That whole repeated code things looks sus....Edit: won't bother, seeing as I now know about the custom mappings, which disallows a sequential approach Quote Link to comment Share on other sites More sharing options...
moogah Posted June 11, 2006 Author Report Share Posted June 11, 2006 I'm pretty sure you end up with the same thing that way, once you work it out. Same goes for modularity, you have to check for all 12 note values, reguardless and you have to branch out when you find a match. The simplest way is to map all the instruments to sequential note numbers, this way they can be checked in a simple loop. This loses the ability to make your own mapping, so I don't consider it ideal. Quote Link to comment Share on other sites More sharing options...
stryd_one Posted June 11, 2006 Report Share Posted June 11, 2006 The simplest way is to map all the instruments to sequential note numbers, this way they can be checked in a simple loop. This loses the ability to make your own mapping, so I don't consider it ideal. Ahh you didn't mention custom mappings before. Yeh you'll have to do it the way you said above. FWIW the sequential way works out somewhat lighter on codesize and instructions... It's a tradeoff, features vs size/speed, which is pretty typical. I'd stick with they way you've got it so you can include custom mappings. 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.