iZiach

Langeweile

Die iZiach war ein schönes Weihnachtsprojekt 2013. Ich hatte eine drei-reihige Kinder Ziach welche ich auf midi umbauen wollte.

Die Idee

Ein iPad mit Synthesizer APP via USB Adapter mit einer midi-Quelle verbinden. iPad und Synth hatte ich, es galt also nur noch eine midi-Quelle zu schaffen. Ich baute aus der Ziach die Stimmstöcke aus montierte Magnete auf die Ventile, und statt der Stimmstöcke montierte ich eine Platte mit Hall-gebern. Weiter verbaute ich einen Drucksensor damit ich später die Ton-Lautstärke modulieren konnte und eine Unterscheidung von Zug und Druck hatte.

Hardware

Als Hardware verwendete ich einen LPC Espresso Board (ich glaube Cortex M3) und schrieb die Firmware in C. Die Firmware simulierte ein USB midi-Keyboard das sich problemlos mit dem iPad verbinden lies. Soweit ist das ganze recht unspannend. Was aber wirklich tricky war war die Töne für ein Diatonisches Akkordeon (Bayrisch Ziach) zu bestimmen. Ich wollte umbedingt zur Laufzeit zwischen verschiedenen Tonarten wechseln, bzw. auch schnell mal andere exotische ausprobieren. Also ersann ich einen Algorithmus welcher einem von einer gegeben Skala (7 Töne) die ansprechenden Töne zu jeder Taste berechnet. Unten ist der source code dazu leider nicht sauber kommentiert (Weihnachten), aber wer so was sucht wird auch um dieses snipet froh sein. Leider hab ich keinen Schaltplan mehr der ging mal verloren.

P.S.

Es gab auch Probleme, der LPC nebst kostenlosem Compiler und Bibliotheken ist sehr Sperrig und wirkt zusammen gebastelt. Der Drucksensor hat am ADC Eingang so sehr gerauscht, dass ich ein Aktivfilter mit OP-AMP (Tiefpass) dazwischen schalten musste. Der LPC wurde vom iPad über USB mit Power versorgt, was auch nicht ewig hielt da der LPC sehr hungrig war und der Akku vom iPad dann wieder leer war. Aber man konnte spielen und es machte doch viel Freude.

// Calculates the midi notes for each key
// of a diatonic accordion based on a given scale (7 notes)
//
// Copyright (C) 2013-2018 by Karl Saam
//

#define MIDI_KEY_COUNT      (43 - 1) // Without Air flap
#define AKKORD_SIZE         3        // Notes per akkord used for BASS

typedef struct TONE_T_TAG {
    uint8_t nZug[AKKORD_SIZE];
    uint8_t nDruck[AKKORD_SIZE];
} tone_t;

typedef tone_t  tone_map_t[MIDI_KEY_COUNT];

#define N_DES0              N_CIS0
#define N_ES0               N_DIS0
#define N_GES0              N_FIS0
#define N_AS0               N_GIS0
#define N_B0                N_AIS0

#define N_DES1              N_CIS1
#define N_ES1               N_DIS1
#define N_GES1              N_FIS1
#define N_AS1               N_GIS1
#define N_B1                N_AIS1

#define N_DES2              N_CIS2
#define N_ES2               N_DIS2
#define N_GES2              N_FIS2
#define N_AS2               N_GIS2
#define N_B2                N_AIS2

#define N_DES3              N_CIS3
#define N_ES3               N_DIS3
#define N_GES3              N_FIS3
#define N_AS3               N_GIS3
#define N_B3                N_AIS3

#define N_DES4              N_CIS4
#define N_ES4               N_DIS4
#define N_GES4              N_FIS4
#define N_AS4               N_GIS4
#define N_B4                N_AIS4

#define N_DES5              N_CIS5
#define N_ES5               N_DIS5
#define N_GES5              N_FIS5
#define N_AS5               N_GIS5
#define N_B5                N_AIS5

#define N_DES6              N_CIS6
#define N_ES6               N_DIS6
#define N_GES6              N_FIS6
#define N_AS6               N_GIS6
#define N_B6                N_AIS6

// Define notes by midi note-number
#define N_C0                    24
#define N_CIS0                  25
#define N_D0                    26
#define N_DIS0                  27
#define N_E0                    28
#define N_F0                    29
#define N_FIS0                  30
#define N_G0                    31
#define N_GIS0                  32
#define N_A0                    33
#define N_AIS0                  34
#define N_H0                    35

#define N_C1                    36
#define N_CIS1                  37
#define N_D1                    38
#define N_DIS1                  39
#define N_E1                    40
#define N_F1                    41
#define N_FIS1                  42
#define N_G1                    43
#define N_GIS1                  44
#define N_A1                    45
#define N_AIS1                  46
#define N_H1                    47

#define N_C2                    48
#define N_CIS2                  49
#define N_D2                    50
#define N_DIS2                  51
#define N_E2                    52
#define N_F2                    53
#define N_FIS2                  54
#define N_G2                    55
#define N_GIS2                  56
#define N_A2                    57
#define N_AIS2                  58
#define N_H2                    59

#define N_C3                    60
#define N_CIS3                  61
#define N_D3                    62
#define N_DIS3                  63
#define N_E3                    64
#define N_F3                    65
#define N_FIS3                  66
#define N_G3                    67
#define N_GIS3                  68
#define N_A3                    69
#define N_AIS3                  70
#define N_H3                    71

#define N_C4                    72
#define N_CIS4                  73
#define N_D4                    74
#define N_DIS4                  75
#define N_E4                    76
#define N_F4                    77
#define N_FIS4                  78
#define N_G4                    79
#define N_GIS4                  80
#define N_A4                    81
#define N_AIS4                  82
#define N_H4                    83

#define N_C5                    84
#define N_CIS5                  85
#define N_D5                    86
#define N_DIS5                  87
#define N_E5                    88
#define N_F5                    89
#define N_FIS5                  90
#define N_G5                    91
#define N_GIS5                  92
#define N_A5                    93
#define N_AIS5                  94
#define N_H5                    95

#define N_C6                    96
#define N_CIS6                  97
#define N_D6                    98
#define N_DIS6                  99
#define N_E6                    100
#define N_F6                    101
#define N_FIS6                  101
#define N_G6                    103
#define N_GIS6                  104
#define N_A6                    105
#define N_AIS6                  106
#define N_H6                    107

#define N_C7                    108

// Scale array[7] = { N_FIS2, N_GIS2, N_AIS2, N_H2, N_CIS3, N_D3, N_F3....}
void BuildDiatonic(uint8_t* pScale, tone_t* pNKey;)
{
    uint8_t     nKey;
    int         nOkt;
    uint8_t     nScale;
    uint8_t     nNote;
    uint8_t     nLastNote;

    memset(pNKey, 0x00, sizeof(tone_map_t));

    // 1. Reihe Druck
    nOkt = -1;
    nScale = 2;
    nLastNote = 0;
    for(nKey = 0; nKey < 10; nKey++)
    {
        nNote = pScale[nScale]  + 7;
        if(nNote < nLastNote)
        {
            nOkt++;
        }
        pNKey[DISK_1 + nKey].nDruck[0] = nNote + (nOkt * N_OKT);
        nScale += 2;
        if(nScale > 4)
        {
            nScale = 0;
        }
        nLastNote = nNote;
    }

    // 1. Reihe Zug
    nOkt = -1;
    nScale = 4;
    nLastNote = 0;
    for(nKey = 0; nKey < 10; nKey++)
    {
        nNote = pScale[nScale] + 7;
        if(nNote < nLastNote)
        {
            nOkt++;
        }
        pNKey[DISK_1 + nKey].nZug[0] = nNote + (nOkt * N_OKT);
        switch(nScale)
        {
        case 1:
            nScale = 3;
            break;
        case 3:
            nScale = 5;
            break;
        case 4:
        case 5:
            nScale = 6;
            break;
        case 6:
            nScale = 1;
            break;
        }
        nLastNote = nNote;
    }

    // 1. Reihe Bass Druck
    pNKey[BASS_1 + 0].nDruck[0] = (pScale[0] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_1 + 1].nDruck[0] = (pScale[0] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_1 + 1].nDruck[1] = (pScale[2] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_1 + 1].nDruck[2] = (pScale[4] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 1. Reihe Bass Zug
    pNKey[BASS_1 + 0].nZug[0] = (pScale[4] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_1 + 1].nZug[0] = (pScale[4] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_1 + 1].nZug[1] = (pScale[6] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_1 + 1].nZug[2] = (pScale[1] + 7) - (0 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 1. Reihe übergangs Bass Druck
    pNKey[BASS_1 + 2].nDruck[0] = (pScale[2] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 1. Reihe übergangs Bass Zug
    pNKey[BASS_1 + 2].nZug[0] = (pScale[3] + 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 2. Reihe Druck
    nOkt = 0;
    nScale = 0;
    nLastNote = 0;
    for(nKey = 0; nKey < 11; nKey++)
    {
        nNote = pScale[nScale];
        if(nNote < nLastNote)
        {
            nOkt++;
        }
        pNKey[DISK_2 + nKey].nDruck[0] = nNote + (nOkt * N_OKT);
        nScale += 2;
        if(nScale > 4)
        {
            nScale = 0;
        }
        nLastNote = nNote;
    }

    // 2. Reihe Zug
    nOkt = 0;
    nScale = 3;
    nLastNote = 0;
    for(nKey = 0; nKey < 11; nKey++)
    {
        nNote = pScale[nScale];
        if(nNote < nLastNote)
        {
            nOkt++;
        }
        pNKey[DISK_2 + nKey].nZug[0] = nNote + (nOkt * N_OKT);
        switch(nScale)
        {
        case 1:
            nScale = 3;
            break;
        case 3:
            nScale = 4;
            break;
        case 4:
            nScale = 6;
            break;
        case 6:
            nScale = 1;
            break;
        }
        nLastNote = nNote;
    }

    // 2. Reihe Bass Druck
    pNKey[BASS_2 + 0].nDruck[0] = (pScale[0] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_2 + 1].nDruck[0] = (pScale[0] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 1].nDruck[1] = (pScale[2] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 1].nDruck[2] = (pScale[4] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 2. Reihe Bass Zug
    pNKey[BASS_2 + 0].nZug[0] = (pScale[4] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_2 + 1].nZug[0] = (pScale[4] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 1].nZug[1] = (pScale[6] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 1].nZug[2] = (pScale[1] - 0) - (0 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 2. Reihe übergangs Bass Druck
    pNKey[BASS_2 + 2].nDruck[0] = (pScale[2] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_2 + 3].nDruck[0] = (pScale[2] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 3].nDruck[1] = (pScale[4] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 3].nDruck[2] = (pScale[6] - 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 2. Reihe übergangs Bass Zug
    pNKey[BASS_2 + 2].nZug[0] = (pScale[3] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_2 + 3].nZug[0] = (pScale[3] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 3].nZug[1] = (pScale[5] + 0) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_2 + 3].nZug[2] = (pScale[0] + 0) - (0 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 3. Reihe Druck
    nOkt = 1;
    nScale = 0;
    nLastNote = 0;
    for(nKey = 0; nKey < 10; nKey++)
    {
        nNote = pScale[nScale] - 7;
        if(nNote < nLastNote)
        {
            nOkt++;
        }
        pNKey[DISK_3 + nKey].nDruck[0] = nNote + (nOkt * N_OKT);
        nScale += 2;
        if(nScale > 4)
        {
            nScale = 0;
        }
        nLastNote = nNote;
    }

    // 3. Reihe Zug
    nOkt = 1;
    nScale = 3;
    nLastNote = 1;
    for(nKey = 0; nKey < 10; nKey++)
    {
        nNote = pScale[nScale] - 7;
        if(nNote < nLastNote)
        {
            nOkt++;
        }
        pNKey[DISK_3 + nKey].nZug[0] = nNote + (nOkt * N_OKT);
        switch(nScale)
        {
        case 1:
            nScale = 3;
            break;
        case 3:
            nScale = 4;
            break;
        case 4:
            nScale = 6;
            break;
        case 6:
            nScale = 1;
            break;
        }
        nLastNote = nNote;
    }

    // 3. Reihe Bass Druck
    pNKey[BASS_3 + 0].nDruck[0] = (pScale[0] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_3 + 1].nDruck[0] = (pScale[0] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 1].nDruck[1] = (pScale[2] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 1].nDruck[2] = (pScale[4] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 3. Reihe Bass Zug
    pNKey[BASS_3 + 0].nZug[0] = (pScale[4] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_3 + 1].nZug[0] = (pScale[4] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 1].nZug[1] = (pScale[6] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 1].nZug[2] = (pScale[1] - 7) - (0 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 3. Reihe übergangs Bass Druck
    pNKey[BASS_3 + 2].nDruck[0] = (pScale[2] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_3 + 3].nDruck[0] = (pScale[2] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 3].nDruck[1] = (pScale[4] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 3].nDruck[2] = (pScale[6] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    // 3. Reihe übergangs Bass Zug
    pNKey[BASS_3 + 2].nZug[0] = (pScale[3] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);

    pNKey[BASS_3 + 3].nZug[0] = (pScale[3] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 3].nZug[1] = (pScale[5] - 7) - (1 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
    pNKey[BASS_3 + 3].nZug[2] = (pScale[0] - 7) - (0 * N_OKT) + (BASS_OKT_OFFS * N_OKT);
}

int main(void)
{
    // Use one of this examples

    // STEIRISCH CFG
    uint8_t pScale[7] = { N_F2, N_G2, N_A2, N_AIS2, N_C3, N_D3, N_E3 };
    tone_t p_tone;
    BuildDiatonic(pScale, &p_tone);

    // BALKAN A ARABIC:
    uint8_t pScale[7] = { N_CIS2, N_F2, N_FIS2, N_GIS2, N_AIS2, N_CIS3, N_D3 };
    tone_t p_tone;
    BuildDiatonic(pScale, &p_tone);

    // BALKAN_B_ARABIC:
    uint8_t pScale[7] = { N_GIS2, N_AIS2, N_H2, N_CIS3, N_D3, N_E3, N_G3 };
    tone_t p_tone;
    BuildDiatonic(pScale, &p_tone);

    // BALKAN_FIS:
    uint8_t pScale[7] = { N_FIS2, N_GIS2, N_AIS2, N_H2, N_CIS3, N_D3, N_F3 };
    tone_t p_tone;
    BuildDiatonic(pScale, &p_tone);
}