Forum Rule: Always post complete source code & details to reproduce any issue!
Page 10 of 10 FirstFirst ... 8 9 10
Results 226 to 240 of 240

Thread: New Teensy 4.1 DIY Synthesizer

  1. #226
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    319
    Hi Rolf. Think of it this way:-

    Code:
    // Sysex data lenght [28];                            // Daten lenght max 256
    sysexData[sysexCount++] = 0xF0;                        // 0        - Start SysEx
    sysexData[sysexCount++] = 0x00;                        // 1        - ID
    sysexData[sysexCount++] = 0x00;                        // 2        - ID
    sysexData[sysexCount++] = 0x00;                        // 3        - ID
    sysexData[sysexCount++] = 0x00;                        // 4        - Device ID 0-64
    sysexData[sysexCount++] = patchNo;                     // 5        - Patch No
    sysexData[sysexCount++] = currentPatchBank;            // 6        - Folder No
    
    sysexData[sysexCount++] = /* the content of (data[0], 12) goes here */;    // 7  Patchname character 1
    sysexData[sysexCount++] = /* the content of (data[1], 12) goes here */;    // 8  Patchname character 2
    sysexData[sysexCount++] = /* the content of (data[2], 12) goes here */;    // 9  Patchname character 3
    sysexData[sysexCount++] = /* the content of (data[3], 12) goes here */;    // 10 Patchname character 4
    
    // and so on ...
    Greetings from Australia.

  2. #227
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Hi MatrixRat

    Thanks for your tip. That's a good idea

  3. #228
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Small update..

    Soon there will be SysEx DUMP for Jeannie. Then nothing stands in the way of the patch exchange.

    Click image for larger version. 

Name:	20220501_123341.jpg 
Views:	41 
Size:	120.3 KB 
ID:	28298

    The transmission path is selected with DESTINATION, eg Midi or USB
    DUMP TYPE selects a patch, bank, or all
    With DUMP-Bank the bank
    With source the patch
    With SYSEX-DUMP transmission to the PC or reception
    Transmission or reception is then initialized via the load/save button


    Sending and receiving from SysEx already works. But I still have to do some programming

    Click image for larger version. 

Name:	20220505_084324.jpg 
Views:	32 
Size:	224.1 KB 
ID:	28295

    Click image for larger version. 

Name:	20220505_092444.jpg 
Views:	30 
Size:	297.3 KB 
ID:	28296

    Click image for larger version. 

Name:	20220505_084723.jpg 
Views:	29 
Size:	368.5 KB 
ID:	28297

    Never ending programming. Greetings, Rolf

  4. #229
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Quote Originally Posted by MatrixRat View Post
    Hi Rolf. Think of it this way:-

    Code:
    // Sysex data lenght [28];                            // Daten lenght max 256
    sysexData[sysexCount++] = 0xF0;                        // 0        - Start SysEx
    sysexData[sysexCount++] = 0x00;                        // 1        - ID
    sysexData[sysexCount++] = 0x00;                        // 2        - ID
    sysexData[sysexCount++] = 0x00;                        // 3        - ID
    sysexData[sysexCount++] = 0x00;                        // 4        - Device ID 0-64
    sysexData[sysexCount++] = patchNo;                     // 5        - Patch No
    sysexData[sysexCount++] = currentPatchBank;            // 6        - Folder No
    
    sysexData[sysexCount++] = /* the content of (data[0], 12) goes here */;    // 7  Patchname character 1
    sysexData[sysexCount++] = /* the content of (data[1], 12) goes here */;    // 8  Patchname character 2
    sysexData[sysexCount++] = /* the content of (data[2], 12) goes here */;    // 9  Patchname character 3
    sysexData[sysexCount++] = /* the content of (data[3], 12) goes here */;    // 10 Patchname character 4
    
    // and so on ...
    Greetings from Australia.
    Hallo,
    I optimized my code a bit. I use call-by-referense for string function and variables.
    That uses less memory

    Code:
    //*************************************************************************
    // usbMidi send SystemExclusive
    //*************************************************************************
    FLASHMEM void mySendSysEx(void)
    {
        screenFlagSysex = true;    // draw ProgressBar
        
        byte sysexData[512];            // SysEx buffer 349 Byte
        sysexChecksum = 0;
        
        uint8_t patchNo = (sysexSource + 1);            // Patch No 1-128
        uint8_t currentPatchBank = sysexBank;            // A-P = 0-15
        String numString = (patchNo);
        String bankString = char(currentPatchBank + 65);
        String fileString = (bankString + "/" + numString);
        uint8_t data_len = NO_OF_PARAMS;
        uint16_t sysexCount = 0;
        
        // get patch parameter from sd card as String
        File patchFile = SD.open(fileString.c_str());
        String data[data_len]; //Array of data read in
        recallPatchData(patchFile, data);
        patchFile.close();
        
        // convert string to sysex                                                           // len / index / value
        sysexData[sysexCount++] = 0xF0;                                            // -    0    - Start SysEx
        sysexData[sysexCount++] = 0x00;                                            // -    1    - ID
        sysexData[sysexCount++] = 0x00;                                            // -    2    - ID
        sysexData[sysexCount++] = 0x00;                                            // -    3    - ID
        sysexData[sysexCount++] = 0x00;                                            // -    4    - Device ID 0-64
        sysexData[sysexCount++] = patchNo;                                        // -    5    - Patch No
        sysexData[sysexCount++] = currentPatchBank;                                // -    6    - Bank No
        float_to_string(data[0], 12, sysexCount, sysexData);                    // (0)    12    7    - Patch Name
        float_to_sysex1Byte(data[1], sysexCount, sysexData);                    // (1)    1    19  - oscALevel (0 - 1.00)
        float_to_sysex1Byte(data[2], sysexCount, sysexData);                    // (2)    1    20  - oscBLevel (0 - 1.00)
        float_to_sysex2Bytes(data[3], sysexCount, sysexData);                    // (3)    2    21  - noiseLevel ( -1.00 - +1.00)
        uint8_to_sysex1Byte(data[4], sysexCount, sysexData);                    // (4)    1    23  - unison (0 - 2)
        uint8_to_sysex1Byte(data[5], sysexCount, sysexData);                    // (5)    1    24  - oscFX    (0 - 6)
        float_to_sysex5Bytes(data[6], sysexCount, sysexData);                    // (6)    5     25  - detune (0 - 1.00000)
        //sysexCount = String_to_bin(data[7], 4, sysexCount);                    // (7)             - lfoSyncFreq (not available)
        float_to_string(data[8], 4, sysexCount, sysexData);                        // (8)    4    30    - midiClkTimeInterval
        //sysexCount = String_to_bin(data[9], 4, sysexCount);                    // (9)            - lfoTempoValue (not available)
        float_to_sysex5Bytes(data[10], sysexCount, sysexData);                    // (10) 5     34  - keytrackingAmount (0 - 1.0000)
        float_to_sysex5Bytes(data[11], sysexCount, sysexData);                    // (11)    5    39  - glideSpeed (0 - 1.00000)
        int8_to_sysex2Bytes(data[12], sysexCount, sysexData);                    // (12)    2    44  - oscPitchA (-24 - +24)
        int8_to_sysex2Bytes(data[13], sysexCount, sysexData);                    // (13) 2     46  - oscPitchB (-24 - +24)
        uint8_to_sysex1Byte(data[14], sysexCount, sysexData);                    // (14) 1     48  - oscWaveformA (0 - 63)
        uint8_to_sysex1Byte(data[15], sysexCount, sysexData);                    // (15)    1    49    - oscWaveformB (0 - 63)
        //sysexCount = String_to_bin(data[16], 1, sysexCount, &sysexData[0]);    // (16)            - pwmSource (1) (not available)
        float_to_sysex1Byte(data[17], sysexCount, sysexData);                    // (17) 1   50  - pwmAmtA (0 - 0.99)
        float_to_sysex1Byte(data[18], sysexCount, sysexData);                    // (18) 1   51  - pwmAmtB (0 - 0.99)
        //sysexCount = String_to_bin(data[19], 6, sysexCount);                    // (19)            - pwmRate (not available)
        float_to_sysex2Bytes(data[20], sysexCount, sysexData);                    // (20) 2     52  - pwA (-1.00 - +1.00)
        float_to_sysex2Bytes(data[21], sysexCount, sysexData);                    // (21) 2     54  - pwB (-1.00 - +1.00)
        float_to_string(data[22], 4, sysexCount, sysexData);                    // (22) 4    56  - filterRes (0 - 15.0)
        uint14_to_sysex2Bytes(data[23], sysexCount, sysexData);                    // (23) 2     60  - filterFreq (18 - 12000)
        float_to_string(data[24], 4, sysexCount, sysexData);                    // (24) 4     62  - filterMix (0 - -99.0)
        float_to_sysex2Bytes(data[25], sysexCount, sysexData);                    // (25) 2    66  - filterEnv (-1.00 - +1.00)
        float_to_sysex5Bytes(data[26], sysexCount, sysexData);                    // (26) 5    67  - oscLfoAmt (0 - 1.00000)
        float_to_sysex5Bytes(data[27], sysexCount, sysexData);                    // (27) 5     73  - oscLfoRate (0 - 40.0000)
        uint8_to_sysex1Byte(data[28], sysexCount, sysexData);                    // (28) 1     78  - oscLFOWaveform (0 - 12)
        uint8_to_sysex1Byte(data[29], sysexCount, sysexData);                    // (29) 1     79  - oscLfoRetrig (0 - 1)
        //sysexCount = String_to_bin(data[30], 1, sysexCount);                    // (30)         - oscLFOMidiClkSync (not available)
        uint8_to_sysex1Byte(data[31], sysexCount, sysexData);                    // (31) 1     80  - myFilterLFORateValue (1 - 127)
        uint8_to_sysex1Byte(data[32], sysexCount, sysexData);                    // (32) 1    81  - filterLfoRetrig (0 - 1)    
        //sysexCount = String_to_bin(data[33], 1, sysexCount);                    // (33)         - oscLFOMidiClkSync (not available)
        float_to_sysex5Bytes(data[34], sysexCount, sysexData);                    // (34) 5     82  - filterLfoAmt (0 - 1.00000)
        uint8_to_sysex1Byte(data[35], sysexCount, sysexData);                    // (35) 1    87  - filterLFOWaveform (0 - 12)
        uint14_to_sysex2Bytes(data[36], sysexCount, sysexData);                    // (36) 2    88  - filterAttack (0 - 11880)
        uint14_to_sysex2Bytes(data[37], sysexCount, sysexData);                    // (37) 2     90  - filterDecay (0 - 11880)
        //float_to_sysex1Byte(data[38], sysexCount, sysexData);                    // (38) 1    92  - filterSustain (0 - 1.00)
        uint14_to_sysex2Bytes(data[39], sysexCount, sysexData);                    // (39) 2    94  - filterRelease (0 - 11880)
        uint14_to_sysex2Bytes(data[40], sysexCount, sysexData);                    // (40) 2    96  - ampAttack (0 - 11880)
        uint14_to_sysex2Bytes(data[41], sysexCount, sysexData);                    // (41) 2     98  - ampDecay (0 - 11880)
        //float_to_sysex1Byte(data[42], sysexCount, sysexData);                    // (42) 1    100  - ampSustain (0 - 1.00)
        uint14_to_sysex2Bytes(data[43], sysexCount, sysexData);                    // (43) 2     101  - ampRelease (0 - 11880)
        //sysexCount = float_to_sysex5Bytes(data[44], sysexCount, &sysexData[0]);// (44)          - fxAmt (not available)
        //sysexCount = float_to_sysex5Bytes(data[45], sysexCount, &sysexData[0]);// (45)            - fxMix (not available)
        //sysexCount = float_to_sysex5Bytes(data[46], sysexCount, &sysexData[0]);// (46)           - pitchEnv (not available)
        uint8_to_sysex1Byte(data[47], sysexCount, sysexData);                    // (47) 1    102  - velocitySens (0-4)
        uint8_to_sysex1Byte(data[48], sysexCount, sysexData);                    // (48) 1    103  - chordDetune (0 - 127)
        uint8_to_sysex1Byte(data[49], sysexCount, sysexData);                    // (49) 1    104  - FxPot1value (0 - 127)
        uint8_to_sysex1Byte(data[50], sysexCount, sysexData);                    // (50) 1    105  - FxPot2value (0 - 127)
        uint8_to_sysex1Byte(data[51], sysexCount, sysexData);                    // (51) 1    106  - FxPot3value (0 - 127)
        uint8_to_sysex1Byte(data[52], sysexCount, sysexData);                    // (52) 1    107  - FxPrgNo (0 - 15)
        uint8_to_sysex1Byte(data[53], sysexCount, sysexData);                    // (53) 1    108  - FxMixValue (0 - 127)
        float_to_sysex5Bytes(data[54], sysexCount, sysexData);                    // (54) 5    109  - FxMixValue (10000 - 60000)
        uint8_to_sysex1Byte(data[55], sysexCount, sysexData);                    // (55) 1    114  - Osc1WaveBank (0 - 15)
        uint8_to_sysex1Byte(data[56], sysexCount, sysexData);                    // (56) 1    115  - Osc1WaveBank (0 - 15)
        uint8_to_sysex1Byte(data[57], sysexCount, sysexData);                    // (57) 1    116  - myBoost (0 - 1)
        float_to_sysex5Bytes(data[58], sysexCount, sysexData);                    // (58) 5    117  - pitchEnvA (-1.00 - +1.00)
        float_to_sysex5Bytes(data[59], sysexCount, sysexData);                    // (59) 5    119  - pitchEnvB (-1.00 - +1.00)
        float_to_sysex2Bytes(data[60], sysexCount, sysexData);                    // (60) 2    121  - driveLevel (Osc level) (0.00 - 1.25)
        float_to_sysex5Bytes(data[61], sysexCount, sysexData);                    // (61) 5    123  - myFilVelocity (0 - 1.00)
        float_to_sysex5Bytes(data[62], sysexCount, sysexData);                    // (62) 2    125  - myAmpVelocity (0 - 1.00)
        uint8_to_sysex1Byte(data[63], sysexCount, sysexData);                    // 1    127 - myUnisono (0-2)
        //sysexCount = uint8_to_sysex1Byte(data[64], sysexCount, &sysexData[0]);//         - dummy
        //sysexCount = uint8_to_sysex1Byte(data[65], sysexCount, &sysexData[0]);//         - dummy
        uint8_to_sysex1Byte(data[66], sysexCount, sysexData);                    // 1    128 - WShaperNo (0-14)
        float_to_sysex5Bytes(data[67], sysexCount, sysexData);                    // 5    129 - WShaperDrive (0.10 - 5.00)
        float_to_sysex5Bytes(data[68], sysexCount, sysexData);                    // 5    134 - LFO1phase (0 - 180.0)
        float_to_sysex5Bytes(data[69], sysexCount, sysexData);                    // 5    139 - LFO2phase (0 - 180.0)
        for (uint8_t i = 0; i < 16; i++) {
            uint8_to_sysex1Byte(data[70+i], sysexCount, sysexData);                // 16    144 - SeqNote1Buf (0 - 127)
        }
        for (uint8_t i = 0; i < 16; i++) {
            uint8_to_sysex1Byte(data[86+i], sysexCount, sysexData);    // 16    160 - SeqNoteBufStatus (0 - 1)
        }
        uint14_to_sysex2Bytes(data[102], sysexCount, sysexData);                // 2     176 - SEQbpmValue (101 - 462)
        float_to_sysex5Bytes(data[103], sysexCount, sysexData);                    // 5    178 - SEQdivValue (float)
        uint8_to_sysex1Byte(data[104], sysexCount, sysexData);                    // 1    183 - SEQstepNumbers (0 - 15)
        float_to_sysex5Bytes(data[105], sysexCount, sysexData);                    // 5    184 - SEQGateTime (float)
        uint8_to_sysex1Byte(data[106], sysexCount, sysexData);                    // 1    189 - SEQdirection (0 - 3)
        uint8_to_sysex1Byte(data[107], sysexCount, sysexData);                    // 1    190 - oscDetuneSync (0 - 1)
        int8_to_sysex2Bytes(data[108], sysexCount, sysexData);                    // 2    191 - oscPitchA (-12 - +12)
        float_to_sysex5Bytes(data[109], sysexCount, sysexData);                    // 5    193 - oscMasterTune (float)
        float_to_sysex5Bytes(data[110], sysexCount, sysexData);                    // 5    198 - OscVCFMOD (float)
        for (uint8_t i = 0; i < 16; i++) {
            uint8_to_sysex1Byte(data[111], sysexCount, sysexData);                // 16    203 - SeqVeloBuf (0 - 127)
        }
        for (uint8_t i = 0; i < 48; i++) {
            uint8_to_sysex1Byte(data[127], sysexCount, sysexData);                // 48    219 - SeqNote1Buf (0 - 127)
        }
        for (uint8_t i = 0; i < 16; i++) {
            uint8_to_sysex1Byte(data[175], sysexCount, sysexData);                // 16    267 - SeqNoteCount (0 - 3)
        }
        uint8_to_sysex1Byte(data[191], sysexCount, sysexData);                    // 1    283 - SEQmode (0 - 2)
        uint8_to_sysex1Byte(data[192], sysexCount, sysexData);                    // 1    284 - SEQMidiClkSwitch (0 - 1)
        uint8_to_sysex1Byte(data[193], sysexCount, sysexData);                    // 1    285 - LadderFilterpassbandgain (0 - 127)
        uint8_to_sysex1Byte(data[194], sysexCount, sysexData);                    // 1    286 - LadderFilterDrive (1 - 127)
        int8_to_sysex2Bytes(data[195], sysexCount, sysexData);                    // 2    287 - envelopeType1 (-8 - +8)
        int8_to_sysex2Bytes(data[196], sysexCount, sysexData);                    // 2    289 - envelopeType2 (-8 - +8)
        float_to_sysex5Bytes(data[197], sysexCount, sysexData);                    // 5    291 - PitchWheelAmt (float)
        float_to_sysex5Bytes(data[198], sysexCount, sysexData);                    // 5    296 - MODWheelAmt (float)
        uint8_to_sysex1Byte(data[199], sysexCount, sysexData);                    // 1    301 - myFilter (1 - 2)
        float_to_sysex5Bytes(data[200], sysexCount, sysexData);                    // 5    302 - pwmRateA (-10.00 - + 10.00)
        float_to_sysex5Bytes(data[201], sysexCount, sysexData);                    // 5    307 - pwmRateB (-10.00 - + 10.00)
        uint14_to_sysex2Bytes(data[202], sysexCount, sysexData);                // 2    312 - LFO1fadeTime (0 - 12000) Fade in
        uint14_to_sysex2Bytes(data[203], sysexCount, sysexData);                // 2    314 - LFO1releaseTime (0 - 12000) Fade out
        float_to_sysex5Bytes(data[204], sysexCount, sysexData);                    // 5    316 - filterFM (0.00000 - 1.00000)  Osc1
        float_to_sysex5Bytes(data[205], sysexCount, sysexData);                    // 5    321 - filterFM2 (0.00000 - 1.00000)    Osc2
        uint14_to_sysex2Bytes(data[206], sysexCount, sysexData);                // 2    326 - LFO2fadeTime (0 - 12000) Fade in
        uint14_to_sysex2Bytes(data[207], sysexCount, sysexData);                // 2    328 - LFO2releaseTime (0 - 12000) Fade out
        float_to_sysex5Bytes(data[208], sysexCount, sysexData);                    // 5    330 - Osc1ModAmt (0.00000 - 1.00000) Fx Mod
        int8_to_sysex2Bytes(data[209], sysexCount, sysexData);                    // 2    335 - LFO1enCurve (-8 - +8)
        int8_to_sysex2Bytes(data[210], sysexCount, sysexData);                    // 2    337 - LFO2enCurve (-8 - +8)
        uint8_to_sysex1Byte(data[211], sysexCount, sysexData);                    // 1    339 - LFO1mode (0 - 1)
        uint8_to_sysex1Byte(data[212], sysexCount, sysexData);                    // 1    340 - LFO2mode (0 - 1)
        float_to_sysex5Bytes(sysexChecksum, sysexCount, sysexData);                // 5    341 - SysEx checksum
        sysexData[sysexCount++] = 0xF7;                                            // 1    346 - End SysEx (0xF7)
        
        usbMIDI.sendSysEx(sysexCount, sysexData, true);        // send SysEx data
        
    }
    
    
    
    
    //*************************************************************************
    // convert float to SysEx string
    //*************************************************************************
    FLASHMEM void float_to_string (String value, uint8_t len, uint16_t &sysexCount, byte *sysexData)
    {
        for (uint8_t i = 0; i < len; i++) {
            sysexData[sysexCount++] = value[i];
            sysexChecksum += value[i];
        }
    }
    
    
    
    
    //*************************************************************************
    // convert float into SysEx uint7Bit  (float 0 - 0.127)
    //*************************************************************************
    FLASHMEM void float_to_sysex1Byte (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t val = (value.toFloat() * 100);
        sysexData[sysexCount++] = val; 
        sysexChecksum += val;
    }
    
    
    
    
    
    
    
    
    //*************************************************************************
    // convert float into SysEx uint7Bit  (- 127 - +127)
    //*************************************************************************
    FLASHMEM void int8_to_sysex2Bytes (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t l_byte = (uint8_t)(value.toInt());
        uint8_t h_byte = 0;
        
        if (l_byte > 127) {
            l_byte = 256 - l_byte;            // make positive value
            h_byte = 1;                        // minus Sign
        }
    
    
        sysexData[sysexCount++] = h_byte;    // Sign (- = 1 / + = 0)
        sysexChecksum += h_byte;
        sysexData[sysexCount++] = l_byte;    // data
        sysexChecksum += l_byte;
    }
    
    
    //*************************************************************************
    // convert string into SysEx int7 (float -1.27 - +1.27)
    //*************************************************************************
    FLASHMEM void float_to_sysex2Bytes (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t h_byte = int8_t(value.toFloat() * 100);                            
        int8_t l_byte = h_byte;
        
        // calc h_byte
        h_byte >>= 7;
        
        // l_byte is sign 
        if (l_byte < 0) {    
            l_byte = l_byte * (-1);
        }
        
        sysexData[sysexCount++] = h_byte;    // Sign (- = 1 / + = 0)
        sysexChecksum += h_byte;
        sysexData[sysexCount++] = l_byte;    // data
        sysexChecksum += l_byte;
    }
    
    
    //*************************************************************************
    // convert float into SysEx uint14 (float 0.00 - 16.383)
    //*************************************************************************
    FLASHMEM uint16_t float_to_uint14bit (String value, uint16_t sysexCount, byte *sysexData)
    {
        uint8_t h_byte = int8_t(value.toFloat() * 100);                            
        int8_t l_byte = h_byte;
        
        // calc h_byte
        h_byte >>= 7;
        
        // l_byte is sign 
        if (l_byte < 0) {    
            l_byte = l_byte * (-1);
        }
        
        sysexData[sysexCount++] = h_byte;    // Sign (- = 1 / + = 0)
        sysexChecksum += h_byte;
        sysexData[sysexCount++] = l_byte;    // data
        sysexChecksum += l_byte;
        
        return sysexCount;
    }
    
    
    //*************************************************************************
    // convert 7bit value into SysEx 1Byte ( 0-127)
    //*************************************************************************
    FLASHMEM void uint8_to_sysex1Byte (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t val =  value.toInt();
        sysexData[sysexCount++] = val;
        sysexChecksum += val;
        
    }
    
    
    //*************************************************************************
    // convert 14bit value into SysEx 2Byte (0-16383)
    //*************************************************************************
    FLASHMEM void uint14_to_sysex2Bytes (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint16_t var16 = (value.toInt());
        uint16_t var16_x = var16;
        
        // calc h_byte & l_byte -----------
        var16 = var16 << 1;
        uint8_t h_byte = var16 >> 8;
        uint8_t l_byte = var16_x & 0x7F;
        
        sysexData[sysexCount++] = h_byte;
        sysexChecksum += h_byte;
        sysexData[sysexCount++] = l_byte;
        sysexChecksum += l_byte;    
    }
    
    
    
    
    //*************************************************************************
    // convert 32bit float value into SysEx 5Byte
    //*************************************************************************
    FLASHMEM void float_to_sysex5Bytes(String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t sysexBytes[5];
        uint8_t temp_val = 0;
        uint8_t bit_val = 0;
        
        union {
            float fval;
            byte bval[4];
        } floatAsBytes;
        
        floatAsBytes.fval = (value.toFloat());
        
        //  Bit mask
        for (uint8_t i = 0; i < 4; i++) {
            
            // Bit 0-6 to Byte 1-4
            sysexBytes[i] = floatAsBytes.bval[i] & 0x7F;
            
            // Bit7 to 5.Byte
            temp_val = floatAsBytes.bval[i];
            bit_val = bit_val << 1;
            if (temp_val >= 128) {
                bit_val = (bit_val | 0x01);
            }
        }
        sysexBytes[4] = bit_val;
        
        // write sysex buffer
        for (uint8_t i = 0; i < 5; i++){
            sysexData[sysexCount++] = sysexBytes[i];
            sysexChecksum += sysexBytes[i];
        }
    }

  5. #230
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    319
    Hi Rolf. Looks like you're making good progress. Have been quietly following this thread and learned some useful stuff so you are one of my many teachers here.

    Must also mention, Paul, Defragster, KurtE, MJS513, FrankB (hope you are not eating too much popcorn), Oddson and many others, Thank you all.

    Mmm, there's some good stuff in that last code snippet.

    Am planning to use the Audio tool to re-create an analog synth I built in the late '70s but not before I've got a decent set of knobs for it. Had a Sequencer and the Oscilloscope was permanently connected, so was triggered when a note began and the Y axis would be looking at the output. Displayed the effect of envelope, filter cutoff and resonance which was really useful for teaching musicians how to operate it.

    Keep up the good work.

  6. #231
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Hi MatrixRat
    The SysEx dump for a patch is very complex. The patch is stored on the SD card as a text string.
    This contains values in 32-bit floating point and 16-bit integer values.
    You can still save some SysEX data if you convert individual function bits into a 7-bit value.

    For example here:

    Code:
    uint8_to_sysex1Byte(data[29], sysexCount, sysexData);    // (29) 1Byte    oscLfoRetrig (0 - 1)
    uint8_to_sysex1Byte(data[32], sysexCount, sysexData);    // (32) 1Byte    filterLfoRetrig (0 - 1)
    uint8_to_sysex1Byte(data[57], sysexCount, sysexData);    // (57) 1Byte    myBoost (0 - 1)
    uint8_to_sysex1Byte(data[107], sysexCount, sysexData);    // (107) 1Byte   oscDetuneSync (0 - 1)
    uint8_to_sysex1Byte(data[86+i], sysexCount, sysexData);   // (86 - 101) 16Bytes   SeqNoteBufStatus (0 - 1)
    ...

  7. #232
    Senior Member
    Join Date
    Aug 2019
    Location
    Melbourne Australia
    Posts
    319
    Thanks Rolf. Got me thinking, should be soldering.

    I understand that the logical choice is to send floats as ascii so we can put the decimal point where needed. But, say if we only have one digit before the decimal point then we don't necessarily need to send the decimal point itself and what's right of point can (i think) be compacted and sent across three sysex "bytes". Thus the payload is reduced and TX and RX handlers will process stuff quicker than midi can send the verbatim ascii float. Of course it's up to the handler to sort out the details and give us back two floats.

    A theoretical snip from a Midi Implementation map might look a bit like this:-

    Code:
     
     0x00 //MSD = float1 first digit, LSD = float2 first digit - 0-7 fits both cases
     0x00 //bits 15-21
     0x00 //bits 8-14
     0x00 //bits 0-7 -> packed right of point float2
     0x00 //bits 15-21
     0x00 //bits 8-14
     0x00 //bits 0-7 -> packed right of point float1
    If that works then two floats use seven sysex "bytes".

    Actually not quite correct, would need to shuffle more bits but you get the idea?
    Last edited by MatrixRat; 05-10-2022 at 02:01 PM.

  8. #233
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    I programmed different conversions for different floating point and integer values

    Code:
    //*************************************************************************
    // convert float (0.0 - 0.127) into SysEx 1Byte
    //*************************************************************************
    FLASHMEM void float_to_sysex1Byte (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t val = (value.toFloat() * 100);
        sysexData[sysexCount++] = val; 
        sysexChecksum += val;
    }
    
    //*************************************************************************
    // convert float (-1.27 - +1.27) into SysEx 2Byte
    //*************************************************************************
    FLASHMEM void float_to_sysex2Bytes (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t h_byte = int8_t(value.toFloat() * 100);                            
        int8_t l_byte = h_byte;
        
        // h_byte is Sign
        h_byte >>= 7;
        
        // l_byte is value
        if (l_byte < 0) {    
            l_byte = l_byte * (-1);
        }
        
        sysexData[sysexCount++] = h_byte;    // Sign (- = 1 / + = 0)
        sysexChecksum += h_byte;
        sysexData[sysexCount++] = l_byte;    // value
        sysexChecksum += l_byte;
    }
    
    //*************************************************************************
    // convert 32bit float into SysEx 5Byte  
    //*************************************************************************
    FLASHMEM void float_to_sysex5Bytes(String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t sysexBytes[5];
        uint8_t temp_val = 0;
        uint8_t bit_val = 0;
        
        union {
            float fval;
            byte bval[4];
        } floatAsBytes;
        
        floatAsBytes.fval = (value.toFloat());
        
        //  Bit mask
        for (uint8_t i = 0; i < 4; i++) {
            
            // Bit 0-6 to Byte 1-4
            sysexBytes[i] = floatAsBytes.bval[i] & 0x7F;
            
            // Bit7 to 5.Byte
            temp_val = floatAsBytes.bval[i];
            bit_val = bit_val << 1;
            if (temp_val >= 128) {
                bit_val = (bit_val | 0x01);
            }
        }
        sysexBytes[4] = bit_val;
        
        // write sysex buffer
        for (uint8_t i = 0; i < 5; i++){
            sysexData[sysexCount++] = sysexBytes[i];
            sysexChecksum += sysexBytes[i];
        }
    }
    
    //*************************************************************************
    // convert uint7bit (0-127) into SysEx 1Byte
    //*************************************************************************
    FLASHMEM void uint7_to_sysex1Byte (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint8_t val =  value.toInt();
        sysexData[sysexCount++] = val;
        sysexChecksum += val;
        
    }
    
    //*************************************************************************
    // convert uint14bit (0-16383) into SysEx 2Byte
    //*************************************************************************
    FLASHMEM void uint14_to_sysex2Bytes (String value, uint16_t &sysexCount, byte *sysexData)
    {
        uint16_t var16 = (value.toInt());
        uint16_t var16_x = var16;
        
        // calc h_byte & l_byte -----------
        var16 = var16 << 1;
        uint8_t h_byte = var16 >> 8;
        uint8_t l_byte = var16_x & 0x7F;
        
        sysexData[sysexCount++] = h_byte;
        sysexChecksum += h_byte;
        sysexData[sysexCount++] = l_byte;
        sysexChecksum += l_byte;    
    }
    Last edited by Rolfdegen; 05-10-2022 at 06:56 PM.

  9. #234
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    SysEx Dump works now. See short video.

    It was a bit more complicated than I thought. First, conversion routines had to be developed for all 212 parameter values in the Jeannie, which convert 8/16-bit integer values and 32-bit floating-point values into a 7-bit format for the SysEx dump. The same had to be converted back again.
    This was followed by functions for the transmission and reception of SysEx data. Last but not least, a simple menu for operation had to be developed.



    The transmission path is selected with Destination (Midi or USB)
    DUMP type is either a patch or a bank.
    DUMP-Bank sets the bank (A-P) for transmission or reception.
    Source is either the patch no. or when receiving the space in the selected patch bank.
    SYSEX-DUMP is set to send or receive SysEX data.
    The "Load/Save" button starts transmission or reception.
    A progress bar shows the progress of the transfer.



    Video: https://youtu.be/xRoLsR9HgU0

    Greetings Rolf

  10. #235
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Test of the pickup function



    Youtube: https://youtu.be/iXR4-jLjfkk

  11. #236
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    New Future. Added a 3rd LFO.
    Small video about AMP modulation with LFO 3

    https://youtu.be/1wHLFC4NroQ

    Youtube: https://youtu.be/1wHLFC4NroQ

  12. #237
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Hi Teensys..

    In our DIY synthesizer "Jeannie" we have now added a variety of modulations.



    Youtube https://youtu.be/SI8ghhvm3WY

  13. #238
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    More than 400 Jeannie patches. Enjoy listening..



    Youtube https://youtu.be/k39YhrJgMqE

  14. #239
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    New Jeannie Demo from tubeohm.com



    Youtube: https://youtu.be/dHu4UNJWN_M

  15. #240
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    307
    Hello..


    I'm still in the process of integrating a SuperSaw into the Jeannie. 6 saw teeth per oscillator. This corresponds to 12 per vote.


    Unfortunately, the whole thing doesn't sound right yet. Below is the source code and an audio sample with an oscillator.
    Maybe someone has an idea about it ?




    Supersaw with small spread value (saw_inc32)
    Audio Link: https://www.sequencer.de/synthesizer...7ab2cc5d59.mp3


    My Supersaw waveform code
    Code:
    // Phase increment for SupperSaw
    saw_inc32 += 6378593;
    
    
    case WAVEFORM_SUPERSAW:
    for (i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
    uint32_t ph_1 = phasedata[i];
    uint32_t ph_2 = ph_1 + (saw_inc32 * 1);
    uint32_t ph_3 = ph_1 + (saw_inc32 * 2);
    uint32_t ph_4 = ph_1 + (saw_inc32 * 3);
    uint32_t ph_5 = ph_1 + (saw_inc32 * 4);
    int16_t val_1 = signed_multiply_32x16t(magnitude * 0.40f, ph_1);
    int16_t val_2 = signed_multiply_32x16t(magnitude * 0.15f, ph_2);
    int16_t val_3 = signed_multiply_32x16t(magnitude * 0.15f, ph_3);
    int16_t val_4 = signed_multiply_32x16t(magnitude * 0.15f, ph_4);
    int16_t val_5 = signed_multiply_32x16t(magnitude * 0.15F, ph_5);
    *bp++ = val_1 + val_2 + val_3 + val_4 + val_5;
    }
    break;


    Oscillator from Teensy Audio Lib
    Code:
    /* Audio Library for Teensy 3.X
     * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
     *
     * Development of this audio library was funded by PJRC.COM, LLC by sales of
     * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
     * open source software by purchasing Teensy or other PJRC products.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     
     ElectroTechnique 2020
     Added WAVEFORM_SILENT, sync()                           
     */
    
    
    #ifndef synth_waveform_h_
    #define synth_waveform_h_
    
    
    #include <Arduino.h>
    #include "AudioStream.h"
    #include "arm_math.h"
    
    
    
    
    // waveforms.c
    extern "C" {
    extern const int16_t AudioWaveformSine[257];
    }
    
    
    extern uint8_t LFO1mode;
    extern uint8_t LFO2mode;
    extern uint8_t LFO3mode;
    extern boolean lfo1oneShoot; 
    extern boolean lfo2oneShoot;
    extern boolean lfo3oneShoot;  
    extern uint8_t LFO1phase;
    extern uint8_t LFO2phase;
    extern uint8_t LFO3phase;
    extern uint8_t lfo1ph;
    extern uint8_t lfo2ph;
    extern uint8_t lfo3ph;
    extern int8_t Lfo3Modoutput;
    extern boolean LFO1randomFlag;
    extern boolean LFO2randomFlag;
    extern boolean LFO3randomFlag;
    extern uint32_t LFO1delayTime;
    extern uint32_t LFO2delayTime;
    extern uint32_t LFO3delayTime;
    
    
    // Oscillator
    #define WAVEFORM_SINE                            0
    #define WAVEFORM_TRIANGLE                        1
    #define WAVEFORM_SAWTOOTH                        3
    #define WAVEFORM_SQUARE                            2
    #define WAVEFORM_PULSE                            4
    #define WAVEFORM_SAWTOOTH_REVERSE                5
    #define WAVEFORM_SAMPLE_HOLD                    6
    #define WAVEFORM_TRIANGLE_VARIABLE                7
    #define WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE        8
    #define WAVEFORM_BANDLIMIT_SAWTOOTH                9
    #define WAVEFORM_BANDLIMIT_SQUARE                10
    #define WAVEFORM_BANDLIMIT_PULSE                11
    #define WAVEFORM_ARBITRARY                        12
    #define WAVEFORM_SILENT                            19
    
    
    // LFO1 (OSC)
    #define WAVEFORM_ARBITRARY1                        20
    #define WAVEFORM_SAMPLE_HOLD1                    21
    
    
    // LFO2 (FILTER)
    #define WAVEFORM_ARBITRARY2                        22
    #define WAVEFORM_SAMPLE_HOLD2                    23
    
    
    // LFO3 (AMP)
    #define WAVEFORM_ARBITRARY3                        24
    #define WAVEFORM_SAMPLE_HOLD3                    25
    
    
    #define PWM_WAVEFORM_SINE                        26
    #define PWM_WAVEFORM_TRIANGLE                    27
    #define PWM_WAVEFORM_SAWTOOTH                    28
    #define PWM_WAVEFORM_SQUARE                        29
    
    
    
    
    
    
    typedef struct step_state
    {
      int offset ;
      bool positive ;
    } step_state ;
    
    
    
    
    class BandLimitedWaveform
    {
    public:
      BandLimitedWaveform (void) ;
      int16_t generate_sawtooth (uint32_t new_phase, int i) ;
      int16_t generate_square (uint32_t new_phase, int i) ;
      int16_t generate_pulse (uint32_t new_phase, uint32_t pulse_width, int i) ;
      void init_sawtooth (uint32_t freq_word) ;
      void init_square (uint32_t freq_word) ;
      void init_pulse (uint32_t freq_word, uint32_t pulse_width) ;
      
    
    
    private:
      int32_t lookup (int offset) ;
      void insert_step (int offset, bool rising, int i) ;
      int32_t process_step (int i) ;
      int32_t process_active_steps (uint32_t new_phase) ;
      int32_t process_active_steps_saw (uint32_t new_phase) ;
      int32_t process_active_steps_pulse (uint32_t new_phase, uint32_t pulse_width) ;
      void new_step_check_square (uint32_t new_phase, int i) ;
      void new_step_check_pulse (uint32_t new_phase, uint32_t pulse_width, int i) ;
      void new_step_check_saw (uint32_t new_phase, int i) ;
    
    
      
      uint32_t phase_word ;
      int32_t dc_offset ;
      step_state states [32] ; // circular buffer of active steps
      int newptr ;         // buffer pointers into states, AND'd with PTRMASK to keep in buffer range.
      int delptr ;
      int32_t  cyclic[16] ;    // circular buffer of output samples
      bool pulse_state ;
      uint32_t sampled_width ; // pulse width is sampled once per waveform
    };
    
    
    
    
    class AudioSynthWaveformTS : public AudioStream
    {
    public:
      AudioSynthWaveformTS(void) : AudioStream(0,NULL),
        phase_accumulator(0), phase_increment(0), phase_offset(0),
        magnitude(0), pulse_width(0x40000000),
        arbdata(NULL), sample(0), tone_type(WAVEFORM_SINE),
        tone_offset(0),syncFlag(0)  {
      }
    
    
      void frequency(float freq) {
        if (freq < 0.0) {
          freq = 0.0;
        } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
          freq = AUDIO_SAMPLE_RATE_EXACT / 2;
        }
        phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
        if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
      }
      void phase(float angle) {
        if (angle < 0.0) {
          angle = 0.0;
        } else if (angle > 360.0) {
          angle = angle - 360.0;
          if (angle >= 360.0) return;
        }
        phase_offset = angle * (4294967296.0 / 360.0);
      }
      void sync() {
        syncFlag = 1;
      }       
      void amplitude(float n) { // 0 to 1.0
        if (n < 0) {
          n = 0;
        } else if (n > 1.0) {
          n = 1.0;
        }
        magnitude = n * 65536.0;
      }
      void offset(float n) {
        if (n < -1.0) {
          n = -1.0;
        } else if (n > 1.0) {
          n = 1.0;
        }
        tone_offset = n * 32767.0;
      }
      void pulseWidth(float n) {  // 0.0 to 1.0
        if (n < 0) {
          n = 0;
        } else if (n > 1.0) {
          n = 1.0;
        }
        pulse_width = n * 4294967296.0;
      }
      void begin(short t_type) {
        phase_offset = 0;
        tone_type = t_type;
        if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
          band_limit_waveform.init_square (phase_increment) ;
        else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
          band_limit_waveform.init_pulse (phase_increment, pulse_width) ;
        else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
          band_limit_waveform.init_sawtooth (phase_increment) ;
      }
      void begin(float t_amp, float t_freq, short t_type) {
        amplitude(t_amp);
        frequency(t_freq);
        phase_offset = 0;
        begin (t_type);
      }
      void arbitraryWaveform(const int16_t *data, float maxFreq) {
        arbdata = data;
      }
      virtual void update(void);
    
    
    private:
      uint32_t phase_accumulator;
      uint32_t phase_increment;
      uint32_t phase_offset;
      int32_t  magnitude;
      uint32_t pulse_width;
      const int16_t *arbdata;
      int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
      int16_t  sample2; // for WAVEFORM_SAMPLE_HOLD and oneShot
      short  tone_type;
      int16_t tone_offset;
      int16_t syncFlag;
      BandLimitedWaveform band_limit_waveform ;
    };
    
    
    
    
    class AudioSynthWaveformModulatedTS : public AudioStream
    {
    public:
      AudioSynthWaveformModulatedTS(void) : AudioStream(2, inputQueueArray),
        phase_accumulator(0), phase_increment(0), modulation_factor(32768),
        magnitude(0), arbdata(NULL), sample(0), tone_offset(0),
        tone_type(WAVEFORM_SINE), modulation_type(0), syncFlag(0) {
      }
    
    
      void frequency(float freq) {
        if (freq < 0.0) {
          freq = 0.0;
        } else if (freq > AUDIO_SAMPLE_RATE_EXACT / 2) {
          freq = AUDIO_SAMPLE_RATE_EXACT / 2;
        }
        phase_increment = freq * (4294967296.0 / AUDIO_SAMPLE_RATE_EXACT);
        if (phase_increment > 0x7FFE0000u) phase_increment = 0x7FFE0000;
      }
      void amplitude(float n) { // 0 to 1.0
        if (n < 0) {
          n = 0;
        } else if (n > 1.0) {
          n = 1.0;
        }
        magnitude = n * 65536.0;
      }
       void sync() {
        syncFlag = 1;
      }              
      void offset(float n) {
        if (n < -1.0) {
          n = -1.0;
        } else if (n > 1.0) {
          n = 1.0;
        }
        tone_offset = n * 32767.0;
      }
      void begin(short t_type) {
        tone_type = t_type;
        if (t_type == WAVEFORM_BANDLIMIT_SQUARE)
          band_limit_waveform.init_square (phase_increment) ;
        else if (t_type == WAVEFORM_BANDLIMIT_PULSE)
          band_limit_waveform.init_pulse (phase_increment, 0x80000000u) ;
        else if (t_type == WAVEFORM_BANDLIMIT_SAWTOOTH || t_type == WAVEFORM_BANDLIMIT_SAWTOOTH_REVERSE)
          band_limit_waveform.init_sawtooth (phase_increment) ;
      }
      void begin(float t_amp, float t_freq, short t_type) {
        amplitude(t_amp);
        frequency(t_freq);
        begin (t_type) ;
      }
      void arbitraryWaveform(const int16_t *data, float maxFreq) {
        arbdata = data;
      }
      void frequencyModulation(float octaves) {
        if (octaves > 12.0) {
          octaves = 12.0;
        } else if (octaves < 0.1) {
          octaves = 0.1;
        }
        modulation_factor = octaves * 4096.0;
        modulation_type = 0;
      }
      void phaseModulation(float degrees) {
        if (degrees > 9000.0) {
          degrees = 9000.0;
        } else if (degrees < 30.0) {
          degrees = 30.0;
        }
        modulation_factor = degrees * (65536.0 / 180.0);
        modulation_type = 1;
      }
      virtual void update(void);
    
    
    private:
      audio_block_t *inputQueueArray[2];
      uint32_t phase_accumulator;
      uint32_t phase_increment;
      uint32_t modulation_factor;
      int32_t  magnitude;
      const int16_t *arbdata;
      uint32_t phasedata[AUDIO_BLOCK_SAMPLES];
      int16_t  sample; // for WAVEFORM_SAMPLE_HOLD
      int16_t  tone_offset;
      uint8_t  tone_type;
      uint8_t  modulation_type;
      int16_t   syncFlag;
      BandLimitedWaveform band_limit_waveform ;
      uint32_t saw_inc32;
    };
    
    
    
    
    #endif
    Thanks and greetings
    Last edited by Rolfdegen; Yesterday at 09:13 PM.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •