oPossum 1,083 Posted June 23, 2011 Share Posted June 23, 2011 16 voices of 16 bit x 257 interpolated wavetable - derived from this code for the Launchpad. PWM output, but could easily be adapted to use IIC DAC. This is preliminary and eXperimental. The CPU core seems to be running slower than it should and I don't yet know why. Right now it is limited to 9 voices. The 9600 bps UART on the dev board was used for this demo, but standard MIDI can also be used - just change the bit rate divisor and wire up the usual opto circuit for MIDI. This is part of a larger project I am working on. //main.c #include "msp430fr5739.h" void synth_init(void); void set_note(int, int, int); static int assign[16]; void note_on(int n, int v) { int i; for(i = 0; i < 16; ++i) { if(assign[i] == -1) { assign[i] = n; set_note(i, n, v); break; } } } void note_off(int n) { int i; for(i = 0; i < 16; ++i) { if(assign[i] == n) { assign[i] = -1; set_note(i, -1, 0); break; } } } void midi_ex(unsigned a, unsigned b, unsigned c) { ++PJOUT; switch(a & 0xF0) { case 0x90: // Note on if(c) { note_on(b, c); break; } // fall thru case 0x80: // Note off note_off(; break; /* case 0xA0: // Note aftertouch break; case 0xB0: // Controller break; case 0xC0: // Program change break; case 0xD0: // Channel aftertouch break; case 0xE0: // Pitch bend break; case 0xF0: // Sysex / Meta break; */ } } void midi(unsigned c) { static const unsigned ps[8] = { 2, 2, 2, 2, 1, 1, 2, 4 }; static unsigned state = 0; static unsigned rs = 0; static unsigned d1 = 0; if(c & 0x80) { // First octect of sequence rs = c; state = ps[(rs >> 4) & 7]; } else { switch(state) { case 0: // Discard break; case 1: // Second and final octet midi_ex(rs, c, 0); break; case 2: // Second octet of three d1 = c; ++state; break; case 3: // Third and final octet midi_ex(rs, d1, c); state = 2; break; //case 4: // Sysex data // break; } } } void main(void) { unsigned n; WDTCTL = WDTPW + WDTHOLD; CSCTL0 = 0xA500; // Unlock clock registers CSCTL1 = DCORSEL | DCOFSEL1 | DCOFSEL0; // 24 MHz CSCTL2 = 0x0333; // Use DCO clock for ACLK, SMCLK and MCLK CSCTL3 = 0x0000; // Set all clock dividers to 1 //CSCTL4 = //CSCTL5 = P1DIR = 0x01; // PWM audio out P1REN = 0x00; // P1OUT = 0x00; // P1SEL0 = 0x01; // Enable Timer A output P1SEL1 = 0x00; // P2DIR = 0x00; // P2REN = 0x00; // P2OUT = 0x00; // P2SEL0 = 0x00; // P2SEL1 = 0x03; // Enable UART UCA0 P3DIR = 0x10; // P3REN = 0x00; // P3OUT = 0x00; // P3SEL0 = 0x10; // SMCLK output P3SEL1 = 0x10; // SMCLK output P4DIR = 0x00; // P4REN = 0x00; // P4OUT = 0x00; // P4SEL0 = 0x00; // P4SEL1 = 0x00; // PJDIR = 0x0F; // 4 LEDs PJREN = 0x00; // PJOUT = 0x0F; // PJSEL0 = 0x00; // PJSEL1 = 0x00; // UCA0CTLW0 = 0x0080; // UCA0BRW = 24000000 / 9600; // Fraunchpad UART //UCA0BRW = 24000000 / 31250; // Standard MIDI bit rate synth_init(); //set_note(0, 69, 700); // 440 Hz test for(n = 0; n < 16; ++n) assign[n] = -1; for(; { if(UCA0IFG & 1) { UCA0IFG &= ~1; n = UCA0RXBUF; midi(n); } } } ;synth.asm .cdecls C, LIST, "msp430fr5739.h" smplrt .equ 32000 ; Sample rate .text .global set_tick .global get_tick .global synth_init .global set_note .bss tick, 2 .bss pwm_out, 2 .bss phase_inc, 6 * 16 ; Phase increment LSW/MSW (from note table) ; Level .bss phase_acc, 4 * 16 ; Phase accumulator LSW/MSW set_tick mov R12, &tick reta get_tick mov &tick, R12 reta synth_init mov #0x0210, &TA0CTL ; Timer A config: SMCLK, count up mov #750, &TA0CCR0 ; Setup Timer A period for 32000 sps mov #375, &TA0CCR1 ; Setup Timer A compare mov #0x00E0, &TA0CCTL1 ; Setup Timer A reset/set output mode ; mov #phase_inc, R12 ; Clear all phase inc and accum mov #5 * 16, R14 ; Word count clr 0(R12) ; Clear word incd R12 ; Next word dec R14 ; Dec word count jne $ - 8 ; Loop until all words done... ; eint ; Enable interupts bis #0x0010, &TA0CCTL0 ; Enable PWM interupt ; reta ; ; synth_isr ; mov &pwm_out, &TA0CCR1 ; Output sample ; push R4 ; Wavetable pointer push R5 ; Phase increment / level pointer push R6 ; Phase accumulator pointer push R7 ; Voice count push R8 ; Wave sample pointer / next sample push R9 ; Wave sample push R10 ; Voice mix accumulator MSW push R11 ; Voice mix accumulator LSW ; mov #sine, R4 ; Get wavetable pointer mov #phase_inc, R5 ; Setup phase increment pointer mov #phase_acc, R6 ; Setup phase accumulator pointer mov #16, R7 ; Setup voice count mov #9, R7 ; clr R10 ; Clear voice mix clr R11 ; voice_loop ; mov @R6+, &MPYS32L ; Get phase acc LSW (fraction) to multiplier clr &MPYS32H ; mov @R6+, R8 ; Get phase acc MSW (wave table index) mov.b R8, R8 ; Clear MSB (use mask for smaller / larger tables) add R4, R8 ; Add wavetable pointer mov @R8+, R9 ; Get wave sample mov @R8, R8 ; Get next wave sample sub R9, R8 ; Calc delta mov R8, &OP2L ; Multiply by delta subc R8, R8 ; Sign extend delta mov R8, &OP2H ; add @R5+, -4(R6) ; Update phase acc addc @R5+, -2(R6) ; add &RES1, R9 ; Add interpolation to sample mov R9, &MPYS ; Multiply by voice level mov @R5+, &OP2L ; add &RES0, R11 ; Update mix addc &RES1, R10 ; dec R7 ; Dec voice count jne voice_loop ; Next voice... ; add #375, R10 ; Bias to center of PWM range mov R10, &pwm_out ; ; dec &tick ; jc $ + 6 ; clr &tick ; ; pop R11 ; pop R10 ; pop R9 ; pop R8 ; pop R7 ; pop R6 ; pop R5 ; pop R4 ; reti ; ; set_note ; push R14 ; Save level mov R12, R14 ; Voice * 6 add R14, R12 ; (+1 = *2) add R14, R12 ; (+1 = *3) rla R12 ; (*2 = *6) add #phase_inc, R12 ; Add phase inc pointer cmp #128, R13 ; Out of range note values are note off jhs note_off ; clr R14 ; Clear octave count tst_note ; cmp #116, R13 ; Within note table? jge get_pi ; Yes... inc R14 ; Inc octave count add #12, R13 ; Add octave to note jmp tst_note ; Check again... get_pi ; Get phase increment sub #116, R13 ; Adjust for first note in table rla R13 ; MIDI note * 4 rla R13 ; add #notes, R13 ; Add note table pointer mov @R13+, R15 ; Get LSW mov @R13, R13 ; Get MSW tst R14 ; Shifting required? jeq set_phase ; No... shift_phase ; rra R13 ; Shift phase inc rrc R15 ; dec R14 ; Dec octave count jne shift_phase ; Repeat until zero... set_phase ; mov R15, 0(R12) ; Set phase inc mov R13, 2(R12) ; pop 4(R12) ; Set voice level reta ; Return ; note_off ; incd SP ; Discard level clr 0(R12) ; Clear phase inc clr 2(R12) ; .if 0 ; Note: Abrupt return to zero causes poping clr 4(R12) ; Clear level add #phase_acc - phase_inc, R12 ; Phase accum pointer clr 0(R12) ; Clear phase accum clr 2(R12) ; .endif ; reta ; Return ; ; notes ; MIDI Note Frequency .if smplrt == 32000 ; 32000 sps .long 3483828 ; 116 G#8 6644.87457275391 .long 3690988 ; 117 A8 7040.00091552734 .long 3910465 ; 118 A#8 7458.62007141113 .long 4142993 ; 119 B8 7902.13203430176 .long 4389349 ; 120 C9 8372.01881408691 .long 4650353 ; 121 C#9 8869.84443664551 .long 4926877 ; 122 D9 9397.27210998535 .long 5219845 ; 123 D#9 9956.06422424316 .long 5530233 ; 124 E9 10548.0823516846 .long 5859077 ; 125 F9 11175.3025054932 .long 6207476 ; 126 F#9 11839.8208618164 .long 6576592 ; 127 G9 12543.8537597656 .endif ; ; .if smplrt == 48000 ; 48000 sps .long 2322552 ; 116 G#8 6644.87457275391 .long 2460658 ; 117 A8 7039.99900817871 .long 2606977 ; 118 A#8 7458.62102508545 .long 2761996 ; 119 B8 7902.13394165039 .long 2926232 ; 120 C9 8372.01690673828 .long 3100235 ; 121 C#9 8869.84348297119 .long 3284585 ; 122 D9 9397.27306365967 .long 3479896 ; 123 D#9 9956.06231689453 .long 3686822 ; 124 E9 10548.0823516846 .long 3906052 ; 125 F9 11175.3044128418 .long 4138318 ; 126 F#9 11839.8227691650 .long 4384395 ; 127 G9 12543.8547134399 .endif ; sine .int 0 .int 804 .int 1608 .int 2410 .int 3212 .int 4011 .int 4808 .int 5602 .int 6393 .int 7179 .int 7962 .int 8739 .int 9512 .int 10278 .int 11039 .int 11793 .int 12539 .int 13279 .int 14010 .int 14732 .int 15446 .int 16151 .int 16846 .int 17530 .int 18204 .int 18868 .int 19519 .int 20159 .int 20787 .int 21403 .int 22005 .int 22594 .int 23170 .int 23731 .int 24279 .int 24811 .int 25329 .int 25832 .int 26319 .int 26790 .int 27245 .int 27683 .int 28105 .int 28510 .int 28898 .int 29268 .int 29621 .int 29956 .int 30273 .int 30571 .int 30852 .int 31113 .int 31356 .int 31580 .int 31785 .int 31971 .int 32137 .int 32285 .int 32412 .int 32521 .int 32609 .int 32678 .int 32728 .int 32757 .int 32767 .int 32757 .int 32728 .int 32678 .int 32609 .int 32521 .int 32412 .int 32285 .int 32137 .int 31971 .int 31785 .int 31580 .int 31356 .int 31113 .int 30852 .int 30571 .int 30273 .int 29956 .int 29621 .int 29268 .int 28898 .int 28510 .int 28105 .int 27683 .int 27245 .int 26790 .int 26319 .int 25832 .int 25329 .int 24811 .int 24279 .int 23731 .int 23170 .int 22594 .int 22005 .int 21403 .int 20787 .int 20159 .int 19519 .int 18868 .int 18204 .int 17530 .int 16846 .int 16151 .int 15446 .int 14732 .int 14010 .int 13279 .int 12539 .int 11793 .int 11039 .int 10278 .int 9512 .int 8739 .int 7962 .int 7179 .int 6393 .int 5602 .int 4808 .int 4011 .int 3212 .int 2410 .int 1608 .int 804 .int 0 .int -804 .int -1608 .int -2410 .int -3212 .int -4011 .int -4808 .int -5602 .int -6393 .int -7179 .int -7962 .int -8739 .int -9512 .int -10278 .int -11039 .int -11793 .int -12539 .int -13279 .int -14010 .int -14732 .int -15446 .int -16151 .int -16846 .int -17530 .int -18204 .int -18868 .int -19519 .int -20159 .int -20787 .int -21403 .int -22005 .int -22594 .int -23170 .int -23731 .int -24279 .int -24811 .int -25329 .int -25832 .int -26319 .int -26790 .int -27245 .int -27683 .int -28105 .int -28510 .int -28898 .int -29268 .int -29621 .int -29956 .int -30273 .int -30571 .int -30852 .int -31113 .int -31356 .int -31580 .int -31785 .int -31971 .int -32137 .int -32285 .int -32412 .int -32521 .int -32609 .int -32678 .int -32728 .int -32757 .int -32767 .int -32757 .int -32728 .int -32678 .int -32609 .int -32521 .int -32412 .int -32285 .int -32137 .int -31971 .int -31785 .int -31580 .int -31356 .int -31113 .int -30852 .int -30571 .int -30273 .int -29956 .int -29621 .int -29268 .int -28898 .int -28510 .int -28105 .int -27683 .int -27245 .int -26790 .int -26319 .int -25832 .int -25329 .int -24811 .int -24279 .int -23731 .int -23170 .int -22594 .int -22005 .int -21403 .int -20787 .int -20159 .int -19519 .int -18868 .int -18204 .int -17530 .int -16846 .int -16151 .int -15446 .int -14732 .int -14010 .int -13279 .int -12539 .int -11793 .int -11039 .int -10278 .int -9512 .int -8739 .int -7962 .int -7179 .int -6393 .int -5602 .int -4808 .int -4011 .int -3212 .int -2410 .int -1608 .int -804 .int 0 ; Interrupt Vectors .sect ".int53" ; TA0CCR0 CCIFG0 .short synth_isr ; ; .end ; RobG, Rickta59, jsolarski and 3 others 6 Quote Link to post Share on other sites
bluehash 1,581 Posted June 23, 2011 Share Posted June 23, 2011 Looking good and wow blue LEDs? What's the assembly code for? Your the first to use the Fraunchpad for a project here. Looking forward for more. Quote Link to post Share on other sites
timotet 44 Posted June 23, 2011 Share Posted June 23, 2011 That's cool good job Quote Link to post Share on other sites
xpg 127 Posted June 23, 2011 Share Posted June 23, 2011 That is indeed very cool. And good track selection :-) /Paul Quote Link to post Share on other sites
oPossum 1,083 Posted June 24, 2011 Author Share Posted June 24, 2011 Looking good and wow blue LEDs? Unfortunately the 8 blue LED are split across two ports. I use the four on port J to show MIDI message activity. Each time a message is parsed the port is incremented, so the LED are a binary count of MIDI messages. What's the assembly code for? C code: main() - Setup clocks and ports. Check for UART rx and send to MIDI parser. midi() - Parse serial data into MIDI messages. midi_ex() - Dispatch MIDI message note_on() - Find a free voice and assign the note to it note_off() - Find the note assignment and turn the voice off Asm code: synth_init() - Initialize the synthesis hardware (Timer A0) and setup initial values in RAM. synth_isr() - Synthesis Interrupt Service Routine. Do NCO for each voice and sum the NCO outputs. Send audio sample to Timer A0 for PWM. This is the most important code. Study this if you want to understand simple wavetable synthesis. set_note() - Convert a MIDI note value to a phase increment value and setup a voice. A lookup table is used for the highest octave, lower octaves are divided by 2 as needed. An out of range note value will turn off the voice (phase increment set to 0). set_tick() - Set a value that will decrement down to zero at the synth sample rate. Can be used for timing in the mainline code. get_tick() - Get the tick count RobG and bluehash 2 Quote Link to post Share on other sites
oPossum 1,083 Posted June 24, 2011 Author Share Posted June 24, 2011 Here is synth.asm rewritten in C. I briefly tested it and is seems to work properly. It can only do 3 voices - the asm code can do 9 (should do 16 - not sure why it is running slow). // synth.c #include "msp430fr5739.h" #include "string.h" static unsigned tick = 0; static unsigned sample = 0; static struct TVOICE { unsigned long pi; unsigned vl; union { unsigned long l; struct { unsigned l; unsigned h; } w; } pa; } voices[16]; void set_tick(unsigned n) { tick = n; } unsigned get_tick(void) { return tick; } void synth_init(void) { TA0CTL = 0x0210; // Timer A config: SMCLK, count up TA0CCR0 = 750; // Setup Timer A period for 32000 sps TA0CCR1 = 375; // Setup Timer A compare TA0CCTL1 = 0x00E0; // Setup Timer A reset/set output mode // memset(voices, 0, sizeof(voices)); // Clear voice structure // _EINT(); // Enable interupts TA0CCTL0 |= 0x0010; // Enable PWM interupt } static int wave[257] = { 0, 804, 1608, 2410, 3212, 4011, 4808, 5602, 6393, 7179, 7962, 8739, 9512, 10278, 11039, 11793, 12539, 13279, 14010, 14732, 15446, 16151, 16846, 17530, 18204, 18868, 19519, 20159, 20787, 21403, 22005, 22594, 23170, 23731, 24279, 24811, 25329, 25832, 26319, 26790, 27245, 27683, 28105, 28510, 28898, 29268, 29621, 29956, 30273, 30571, 30852, 31113, 31356, 31580, 31785, 31971, 32137, 32285, 32412, 32521, 32609, 32678, 32728, 32757, 32767, 32757, 32728, 32678, 32609, 32521, 32412, 32285, 32137, 31971, 31785, 31580, 31356, 31113, 30852, 30571, 30273, 29956, 29621, 29268, 28898, 28510, 28105, 27683, 27245, 26790, 26319, 25832, 25329, 24811, 24279, 23731, 23170, 22594, 22005, 21403, 20787, 20159, 19519, 18868, 18204, 17530, 16846, 16151, 15446, 14732, 14010, 13279, 12539, 11793, 11039, 10278, 9512, 8739, 7962, 7179, 6393, 5602, 4808, 4011, 3212, 2410, 1608, 804, 0, -804, -1608, -2410, -3212, -4011, -4808, -5602, -6393, -7179, -7962, -8739, -9512, -10278, -11039, -11793, -12539, -13279, -14010, -14732, -15446, -16151, -16846, -17530, -18204, -18868, -19519, -20159, -20787, -21403, -22005, -22594, -23170, -23731, -24279, -24811, -25329, -25832, -26319, -26790, -27245, -27683, -28105, -28510, -28898, -29268, -29621, -29956, -30273, -30571, -30852, -31113, -31356, -31580, -31785, -31971, -32137, -32285, -32412, -32521, -32609, -32678, -32728, -32757, -32767, -32757, -32728, -32678, -32609, -32521, -32412, -32285, -32137, -31971, -31785, -31580, -31356, -31113, -30852, -30571, -30273, -29956, -29621, -29268, -28898, -28510, -28105, -27683, -27245, -26790, -26319, -25832, -25329, -24811, -24279, -23731, -23170, -22594, -22005, -21403, -20787, -20159, -19519, -18868, -18204, -17530, -16846, -16151, -15446, -14732, -14010, -13279, -12539, -11793, -11039, -10278, -9512, -8739, -7962, -7179, -6393, -5602, -4808, -4011, -3212, -2410, -1608, -804, 0 }; #pragma vector = TIMER0_A0_VECTOR __interrupt void synth_isr(void) { register int *wp, s, sn; // Wavetable pointer and samples register long w = 0; // Clear sum of voices register unsigned n = 3; // 16 voices to do register struct TVOICE *v = voices; // Make pointer to voice TA0CCR1 = sample; // Output sample do { // wp = wave + (v->pa.w.h & 0x00FF); // Make pointer to wavetable sample s = *wp++; // Get sample from wavetable sn = *wp; // Get next sample from wavetable s += (((long int)(sn - s) * v->pa.w.l) >> 16); // Interpolate w += ((long int)s * v->vl); // Level and sum v->pa.l += v->pi; // Update phase accumulator ++v; // Next voice } while(--n); // sample = (w >> 16) + 375; // Scale and offset sample, save if(tick) --tick; // Decrement tick if not zero } static unsigned long notes[12] = { 3483828, // 116 G#8 6644.87457275391 3690988, // 117 A8 7040.00091552734 3910465, // 118 A#8 7458.62007141113 4142993, // 119 B8 7902.13203430176 4389349, // 120 C9 8372.01881408691 4650353, // 121 C#9 8869.84443664551 4926877, // 122 D9 9397.27210998535 5219845, // 123 D#9 9956.06422424316 5530233, // 124 E9 10548.0823516846 5859077, // 125 F9 11175.3025054932 6207476, // 126 F#9 11839.8208618164 6576592 // 127 G9 12543.8537597656 }; void set_note(unsigned vi, unsigned n, unsigned l) { struct TVOICE *v = &voices[vi]; unsigned o = 0; if(n > 127) { // Out of range notes are note off v->pi = 0; // Clear phase increment //v->vl = 0; // Clear level //v->PA.pa = 0; // Clear phase accumulator return; // Return } // while(n < 116) { // Note must be in table n += 12; // Octave higher ++o; // Inc octave count } // v->pi = notes[n - 116] >> o; // Set phase increment v->vl = l; // Set level } Quote Link to post Share on other sites
Mac 67 Posted June 24, 2011 Share Posted June 24, 2011 Please forgive me but where can I find out about the -> construct in your example, please? v->pa.l += v->pi; Quote Link to post Share on other sites
gordon 229 Posted June 24, 2011 Share Posted June 24, 2011 Any C material. it's the structure member dereference operator. Quote Link to post Share on other sites
Mac 67 Posted June 24, 2011 Share Posted June 24, 2011 Any C material. it's the structure member dereference operator. Ok. Found it in section 6.2 of K&R. A bit incomprehensible or cryptic at this stage for me. On the other hand, learning about the 'register' keyword was fantastic. Thanks... Cheerful regards, Mike Quote Link to post Share on other sites
NatureTM 100 Posted June 25, 2011 Share Posted June 25, 2011 That's incredible, and I will be studying your code, thanks! Quote Link to post Share on other sites
oPossum 1,083 Posted June 26, 2011 Author Share Posted June 26, 2011 The way the parser handles running status and note on messages is not clear, so here is an explanation... --- Running status allows the first octet of a MIDI message to be ommited if it is the same as the last message. For example, this sequence of three messages: 0x80 0x11 0x22 / 0x80 0x33 0x44 / 0x80 0x55 0x66 Can be sent as: 0x80 0x11 0x22 0x33 0x44 0x55 0x66 --- MIDI messages begin with an octet that has the msb set (0x80 to 0xFF). All folowing octets will not have the msb set (0x00 to 0x7F). Most messages are two or three octets long. The exception are system messages than can be 1 to many octets. The MIDI parser checks if the msb of an octet is set and will initialize a state machine that will then handle the following octets. If the message is two octets long the state machine is set to state 1. If the message is three octets long the state machine is set to state 2. If the message is System Common the state machine is set to state 4. void midi(unsigned c) { static const unsigned ps[8] = { 2, 2, 2, 2, 1, 1, 2, 4 }; // Initial state for message static unsigned state = 0; static unsigned rs = 0; static unsigned d1 = 0; if(c & 0x80) { // First octet of sequence if(c > 0xF7) return; // Ignore Real Time Category messages (one octet) rs = c; // Save first octet (running status) state = ps[(rs >> 4) & 7]; // Set inital state of state machine } ... State 1 handles messages that are 2 octets long. The first octet was saved in the variable rs, so when the next octet is received the message is complete and can be processed. The state machine remains in state 1 to handle running status. ... else { switch(state) { case 0: // Discard break; case 1: // Second and final octet midi_ex(rs, c, 0); break; States 2 and 3 handle messges that are 3 octets long. When the second octet is received it is stored in the varialble d1 and the state is incremented to 3. When the third octet is received it is handled by state 3. The message is now complete and can be processed. The state is set to 2 to handle running status. case 2: // Second octet of three d1 = c; ++state; break; case 3: // Third and final octet midi_ex(rs, d1, c); state = 2; break; State 4 would handle System Common messages. This is currently not implemented. //case 4: // System Common // break; } --- A note on message with a veloctiy of 0 is handled as a note off message (with velocity of 0). This allows running status to be used with a mix of note on and note off messages. For example, these messages: 0x90 0x12 0x34 / 0x80 0x12 0x00 / 0x90 0x56 0x78 / 0x80 0x56 0x00 Can be converted to this: 0x90 0x12 0x34 / 0x90 0x12 0x00 / 0x90 0x56 0x78 / 0x90 0x56 0x00 And then running status used: 0x90 0x12 0x34 0x12 0x00 0x56 0x78 0x56 0x00 The switch/case statement that handles message dispatch checks if the velocity of a note on message is zero and will fall thru to the note off handler. This will result in a call to note_off() when a note on message has a velocity of zero. void midi_ex(unsigned a, unsigned b, unsigned c) { ++PJOUT; switch(a & 0xF0) { case 0x90: // Note on if(c) { // Check for non-zero velocity note_on(b, c); break; } // fall thru case 0x80: // Note off note_off(; break; Quote Link to post Share on other sites
RobG 1,892 Posted June 26, 2011 Share Posted June 26, 2011 There are couple of things I would like to comment on. Looks like your parser is channel unaware. This may be a problem when there are more than one used: 0x90 0x40 0x7F ch1 note on 0x81 0x40 0x7F ch2 note off, wrong note is turned off I guess this isn't big deal because you could filter MIDI channels on your port. RealTime Category messages (status 0xF8 to 0xFF,) will cancel running status in your parser, all running status data after RT message will be lost. RT messages are one byte and can be inserted anywhere, even between 2 data bytes. They should not cancel running status. Most of keyboards with sequencers will be sending those out. Quote Link to post Share on other sites
oPossum 1,083 Posted June 26, 2011 Author Share Posted June 26, 2011 Responding to all channels is intentional - I don't want to have to edit MIDI files to get all notes to play. I just turn off channel 10 (percussion) and let the synth play everything else. A future version may be multitimbral and respond to program change messages on a per channel basis. Thanks for the reminder about RTC. Easy fix to handle them... void midi(unsigned c) { static const unsigned ps[8] = { 2, 2, 2, 2, 1, 1, 2, 4 }; // Initial state for message static unsigned state = 0; static unsigned rs = 0; static unsigned d1 = 0; if(c & 0x80) { // First octet of sequence if(c > 0xF7) return; // Ignore Real Time Category messages (single octet) rs = c; // Save first octet (running status) state = ps[(rs >> 4) & 7]; // Set inital state of state machine } ... The parser wan't intended to be comprehensive - just enough to make some noise. Proper handling of all the 0xFx messages will require more code. Quote Link to post Share on other sites
Markus Gritsch 0 Posted January 30, 2012 Share Posted January 30, 2012 @oPossum: Can you please name the tune? I know the melody, but cannot remember it's name. Quote Link to post Share on other sites
oPossum 1,083 Posted January 30, 2012 Author Share Posted January 30, 2012 Cantina Quote Link to post Share on other sites
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.