depuis le 15 avril 2024

GUI pour Korg NTS-1 écrit en C++ avec la librairie GUI FLTK

Public: Musiciens

Korg produit des synthétiseurs, dont le Korg NTS-1.
 
Normalement il sert à jouer synchronisé avec d'autres appareils (Volca Drum, Volca Bass, Volca Sample), ou juste de processeur d'effets.
Son entrée audio permet même de servir d'effets pour guitare avec des sons vraiment exceptionnels, par exemple sustain infini!

Doc/downloads
https://www.korg.com/us/support/download/product/0/832/
https://www.korg.com/fr/products/dj/nts_1/index.php
Oscillateurs et effets à télécharger

Comme il est programmable, on peut coder en C/C++ des oscillateurs, mods, delays, reverbs,
et les transférer avec l'application GUI Digital Librarian vers le synthétiseur via un câble USB.

Seulement voilà, cette application ne tourne que sur Windows et Mac.
C'est pourquoi j'ai créé une interface GUI avec la librairie FLTK, pour transférer/supprimer des effets depuis Linux vers le NTS-1.


On peut charger 16 oscillateurs utilisateur, en plus des trois internes sortis d'usine. Ici le mien est plein.

Cliquer sur un slot demande si on veut supprimer ou le remplacer par un nouveau:

Après avoir supprimé le dernier oscillateur, j'en charge un nouveau:


On peut charger 8 reverbs utilisateur:


Code source de cette fenêtre

digital-librarian-linux.cpp
#include <stdio.h>
#include <stdlib.h>
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Button.H>
#include <FL/Fl_Double_Window.H>
#include <FL/Fl_Browser.H>
#include <FL/fl_draw.H>
#include <FL/Fl_File_Chooser.H>

/*=======================================================*/
// Demonstrate how to derive a class extending Fl_Browser with interactively resizable columns. erco 1.10 12/09/2005
class ColResizeBrowser : public Fl_Browser {
    Fl_Color  _colsepcolor;    // color of column separator lines 
    int       _showcolsep;    // flag to enable drawing column separators
    Fl_Cursor _last_cursor;    // saved cursor state info
    int       _dragging;    // 1=user dragging a column
    int       _dragcol;        // col# user is currently dragging
    int      *_widths;        // pointer to user's width[] array
    int       _nowidths[1];    // default width array (non-const)
    // CHANGE CURSOR Does nothing if cursor already set to value specified.
    void change_cursor(Fl_Cursor newcursor) {
        if ( newcursor != _last_cursor ) {
            fl_cursor(newcursor, FL_BLACK, FL_WHITE);
            _last_cursor = newcursor;
        }
    }
    // RETURN THE COLUMN MOUSE IS 'NEAR'   Returns -1 if none.
    int which_col_near_mouse() {
        int X,Y,W,H;
        Fl_Browser::bbox(X,Y,W,H);        // area inside browser's box()
        // EVENT NOT INSIDE BROWSER AREA? (eg. on a scrollbar)
        if ( ! Fl::event_inside(X,Y,W,H) ) {
            return(-1);
        }
        int mousex = Fl::event_x() + hposition();
        int colx = this->x();
        for ( int t=0; _widths[t]; t++ ) {
            colx += _widths[t];
            int diff = mousex - colx;
            // MOUSE 'NEAR' A COLUMN? Return column #
            if ( diff >= -4 && diff <= 4 ) {
                return(t);
            }
        }
        return(-1);
    }
protected:
    // MANAGE EVENTS TO HANDLE COLUMN RESIZING
    int handle(int e) {
    // Not showing column separators? Use default Fl_Browser::handle() logic
    if ( ! showcolsep() ) return(Fl_Browser::handle(e));
        // Handle column resizing
        int ret = 0;
        switch ( e ) {
            case FL_ENTER: {
                ret = 1;
                break;
            }
            case FL_MOVE: {
                if ( which_col_near_mouse() >= 0 ) {
                    change_cursor(FL_CURSOR_WE);
                } else {
                    change_cursor(FL_CURSOR_DEFAULT);
                }
                ret = 1;
                break;
            }
            case FL_PUSH: {
                int whichcol = which_col_near_mouse();
                if ( whichcol >= 0 ) {
                    // CLICKED ON RESIZER? START DRAGGING
                    ret = 1;
                    _dragging = 1;
                    _dragcol = whichcol;
                    change_cursor(FL_CURSOR_DEFAULT);
                }
                break;
            }
            case FL_DRAG: {
                if ( _dragging ) {
                    ret = 1;
                    // Sum up column widths to determine position
                    int mousex = Fl::event_x() + hposition();
                    int newwidth = mousex - x();
                    for ( int t=0; _widths[t] && t<_dragcol; t++ ) {
                        newwidth -= _widths[t];
                    }
                    if ( newwidth > 0 ) {
                        // Apply new width, redraw interface
                        _widths[_dragcol] = newwidth;
                        if ( _widths[_dragcol] < 2 ) {
                            _widths[_dragcol] = 2;
                        }
                        redraw();
                    }
                }
                break;
            }
            case FL_LEAVE:
            case FL_RELEASE: {
        _dragging = 0;    // disable drag mode
                change_cursor(FL_CURSOR_DEFAULT);    // ensure normal cursor
                ret = 1;
                break;
            }
        }
        if ( _dragging ) return(1);    // dragging? don't pass event to Fl_Browser
    return(Fl_Browser::handle(e) ? 1 : ret);
    }
    void draw() {
        // DRAW BROWSER
        Fl_Browser::draw();
        if ( _showcolsep ) {
            // DRAW COLUMN SEPARATORS
            int colx = this->x() - hposition();
            int X,Y,W,H;
            Fl_Browser::bbox(X,Y,W,H);
            fl_color(_colsepcolor);
            for ( int t=0; _widths[t]; t++ ) {
                colx += _widths[t];
                if ( colx > X && colx < (X+W) ) {
                    fl_line(colx, Y, colx, Y+H-1);
                }
            }
        }
    }
public:
    // CTOR
    ColResizeBrowser(int X,int Y,int W,int H,const char*L=0) : Fl_Browser(X,Y,W,H,L) {
        _colsepcolor = Fl_Color(FL_GRAY);
        _last_cursor = FL_CURSOR_DEFAULT;
        _showcolsep  = 0;
        _dragging    = 0;
        _nowidths[0] = 0;
        _widths      = _nowidths;
    }
    // GET/SET COLUMN SEPARATOR LINE COLOR
    Fl_Color colsepcolor() const {
        return(_colsepcolor);
    }
    void colsepcolor(Fl_Color val) {
        _colsepcolor = val;
    }
    // GET/SET DISPLAY OF COLUMN SEPARATOR LINES 1: show lines, 0: don't show lines
    int showcolsep() const {
        return(_showcolsep);
    }
    void showcolsep(int val) {
        _showcolsep = val;
    }
    // GET/SET COLUMN WIDTHS ARRAY Just like fltk method, but array is non-const.
    int *column_widths() const {
        return(_widths);
    }
    void column_widths(int *val) {
        _widths = val;
        Fl_Browser::column_widths(val);
    }
};
/*=======================================================*/
char strmidi[32]; // exemple "hw:1,0,0"
char in =-1; // souvent -i 1
char out=-1; // souvent -o 1

Fl_Button *but[4]; // boutons onglets
char ongletSelectionne = -1;
Fl_Color grisfonce = fl_rgb_color(50,50,50);
//Fl_Color grisclair = fl_rgb_color(80,80,80);
const char*    types[]={"osc","modfx","delfx","revfx"};
const char* strtypes[]={"Choisir un Oscillateur:","Choisir un Mod:","Choisir un Delay:","Choisir une Reverb:"};
const char*      dos[]={"osc","mod","delay","reverb"};
char selectedSlot = -1;

Fl_Box *span; // info orange "connecté", "pas connecté"
ColResizeBrowser* br;
Fl_Window* winpopup;
/*=======================================================
Regarde si le NTS-1 est connecté avec la commande amidi -l.
Si aucun engin MIDI n'est connecté la console dit seulement
Dir Device    Name\n

Sinon amidi dit
Dir Device    Name\n
IO  hw:1,0,0  NTS-1 digital kit NTS-1 digital\n        ← on veut retenir "hw:1,0,0" pour upload ultérieur
... et peut-être d'autres engins

Donc compter le nombre de lignes.
Si une seule ligne: aucun engin connecté.
Sinon scanner chaque ligne, si elle contient "NTS-1" c'est la bonne, extraire "hw:1,0,0" de l'index 4 jusqu'au 1er espace rencontré.
*/
bool NTS1Connected()
{
    //printf("Exec [%s]\n", cmd);
    char ligne[130];
    char numligne=0;

    FILE *fp = popen("amidi -l","r");
    if(fp){
        while (fgets(ligne, sizeof ligne, fp)){
            numligne++;
            //printf("%d %s", numligne, ligne);
            if(numligne>1){
                int n=0; //scanne la ligne pour voir si elle contient "NTS-1"
                while(1){
                    if(ligne[n+4]=='\n') break;
                    else if(ligne[n]=='N' && ligne[n+1]=='T' && ligne[n+2]=='S' && ligne[n+3]=='-' && ligne[n+4]=='1'){
                        //c'est la bonne ligne: extraire "hw:1,0,0" de l'index 4 jusqu'au 1er espace rencontré.
                        //printf("ligne %d c'est la bonne\n", numligne);
                        n=4; char mot[32]; char b=0;
                        while(ligne[n]!=' '){
                            mot[b++]=ligne[n++];
                        }
                        //mot[b]=0; printf("mot:{%s}\n",mot);
                        for(n=0; n<b; n++) strmidi[n]=mot[n]; strmidi[n]=0; // pas besoin d'inclure <string.h> juste pour strcpy(strmidi,mot);
                        break;
                    }
                    n++;
                }
            }
        }
        fclose(fp);
    }

    if(numligne==1) {printf("** NTS-1 non connecté **\n"); return false;}
    return true;
}
/*=======================================================
Identifie in out pour logue-cli "-i 1 -o 1", pour ultérieurement obtenir la liste des oscillateurs ou en uploader un
$ ./logue-cli probe -l
  Available MIDI inputs:
    in  0: Midi Through:Midi Through Port-0 14:0
    in  1: NTS-1 digital kit:NTS-1 digital kit NTS-1 digital 20:0

  Available MIDI ouputs:
    out 0: Midi Through:Midi Through Port-0 14:0
    out 1: NTS-1 digital kit:NTS-1 digital kit NTS-1 digital 20:0

Scanne la ligne pour voir si elle contient "NTS-1"
    Si oui, regarde les 5e et 6e octets: si "in" saute 2 espaces et récupère la valeur in= jusqu'au :
    sinon si c'est "out", récupère out=
*/
void getInOutValues(){
    char ligne[130];
    FILE *fp = popen("./logue-cli probe -l","r");
    if(fp){
        while (fgets(ligne, sizeof ligne, fp)){
            //printf("%s", ligne);
            int n=0; //scanne la ligne pour voir si elle contient "NTS-1"
            while(1){
                if(ligne[n+4]=='\n') break;
                else if(ligne[n]=='N' && ligne[n+1]=='T' && ligne[n+2]=='S' && ligne[n+3]=='-' && ligne[n+4]=='1'){
                    //printf("c'est la bonne ligne NTS-1\n");
                    //c'est la bonne ligne: regarde les 5e et 6e octets pour voir si c'est "in"
                    if(ligne[4]=='i' && ligne[5]=='n'){
                        n=8; char mot[16]; char b=0; // avance jusqu'au : pour récupérer la valeur
                        while(ligne[n]!=':'){mot[b++]=ligne[n++];}
                        mot[b]=0; //printf("in:{%s}\n",mot);
                        in=atoi(mot);
                    }
                    else if(ligne[4]=='o' && ligne[5]=='u' && ligne[6]=='t'){
                        n=8; char mot[16]; char b=0; // avance jusqu'au : pour récupérer la valeur
                        while(ligne[n]!=':'){mot[b++]=ligne[n++];}
                        mot[b]=0; //printf("out:{%s}\n",mot);
                        out=atoi(mot);
                    }
                    break;
                }
                n++;
            }
        }
        fclose(fp);
    }
    //printf("in:%d out:%d\n", in, out);
}
/*=======================================================*/
//0:OSC 1:MODFX 2:delay 3:reverb
void onglet_click(char id)
{
    //vide le tableau et remet les en-têtes
    br->clear();
     br->add("\t@B89@C7@l@b@.NAME\t@B112@C7@b@l@.VERSION\t@B120@C7@b@l@.API\t");

    char ligne[130];
    char cmd [64];
    int n = sprintf(cmd,"./logue-cli probe -m %s -i %d -o %d", types[id], in, out);
    //printf("click %s [%s]\n", types[id], cmd);

    // Remet en sombre l'ancien onglet slectionné. ça marche pas ça le met en gris clair, mais ne remet pas l'ancien en sombre. et redraw() plante
    //if(ongletSelectionne!=-1) but[ongletSelectionne]->color(grisfonce);
    //but[ongletSelectionne]->redraw();
    //but[id]->color(grisclair); // et met celui-ci en plus clair
    ongletSelectionne=id;

    FILE *fp = popen(cmd,"r"); // envoie la commande
    if(fp){
        while (fgets(ligne, sizeof ligne, fp))
        {
            printf("%s", ligne);
            if(ligne[0]=='['){ // ignore les 4 première lignes qui commencent par >
                //012345
                //[0]: "waves" v1.00-0 api:1.00-0 did:00000000 uid:00000000
                //[1]: free.
                //[10]: free.
                //[10]: "waves" v1.00-0 api:1.00-0 did:00000000 uid:00000000
                //printf("%s", ligne);
                char n=6;//à partir de quel octet sortir le nom, 7 si le slot a deux chiffres
                char slotvide=0;

                // récupère le numéro de slot
                char slot[3];
                if(ligne[2]==']'){ // slot sur un seul chiffre 0/9
                    slot[0] = ligne[1]; slot[1]=0;
                    if(ligne[5]=='f') slotvide=1; // le slot est vide si le caractère à l'index 5 est un f (de "free.")
                }
                else { // slot sur 2 chiffres 10+
                    slot[0]=ligne[1]; slot[1]=ligne[2]; slot[2]=0; n=7;
                    if(ligne[6]=='f') slotvide=1; // le slot est vide si le caractère à l'index 6 est un f (de "free.")
                }
                //printf("slot (%s) ", slot);

                //récupère le nom/version/API, si le slot n'est pas vide
                if(slotvide!=1){
                    char nom[32];     char a=0;
                    char version[20]; char b=0;
                    char api[20];     char c=0;
                    while(ligne[n]!='"') {nom[a++]=ligne[n++];}
                    nom[a]=0; //printf("nom|%s| ", nom);

                    n+=2; // saute " espace
                    // prend la version
                    while(ligne[n]!=' ') version[b++]=ligne[n++];
                    version[b]=0;//printf("version:[%s] ", version);

                    // prend l'API
                    n+=5;
                    while(ligne[n]!=' ') api[c++]=ligne[n++];
                    api[c]=0;//printf("api:[%s]\n", api);

                    //ajoute la ligne au tableau
                    char tmp[200];
                    n = sprintf(tmp,"@C255@.%s\t@C255@l@.%s\t@C255@.%s\t@C255@.%s", slot, nom, version, api);
                    br->add(tmp);
                }
                else{
                    //printf("%s -- -- --\n", slot);
                    char tmp[200];
                    n = sprintf(tmp,"@C255@.%s\t@C255@l@.--\t@C255@.--\t@C255@.--", slot);
                    br->add(tmp);
                }
            }
        }
        pclose(fp);
    }
}
/*=======================================================*/
void button_osc_click(Fl_Widget* w, void*) // Clic sur onglet Oscillateurs
{
    onglet_click(0);
}
/*=======================================================*/
void button_mod_click(Fl_Widget* w, void*) // Clic sur onglet Mod
{
    onglet_click(1);
}
/*=======================================================*/
void button_delay_click(Fl_Widget* w, void*) // Clic sur onglet Delay
{
    onglet_click(2);
}
/*=======================================================*/
void button_reverb_click(Fl_Widget* w, void*)// Clic sur onglet Reverb
{
    onglet_click(3);
}
/*=======================================================*/
void exec(char* cmd)
{
    printf("Exec [%s]\n", cmd);
    char ligne[130];
    FILE *fp = popen(cmd,"r");
    if(fp){
        while (fgets(ligne, sizeof ligne, fp)){
            printf("%s", ligne);
        }
        fclose(fp);
    }
}
/*=======================================================*/
//Exemple supprime l'osc du slot 6: ./logue-cli clear -m osc -s 6 -i 1 -o 1
void button_suppr_click(Fl_Widget *w, void *data)
{
    winpopup->hide();
    char cmd [130];
    int n = sprintf(cmd,"./logue-cli clear -m %s -s %d -i %d -o %d", types[ongletSelectionne], selectedSlot, in, out);
    printf("Suppr [%s]\n", cmd);
    exec(cmd);
    // Redemande au NTS-1 la liste des OSC/MOD/DELAY/ ou REVERB pour refaire la liste, normalement à jour
    onglet_click(ongletSelectionne);
}
/*=======================================================*/
void button_nouvo_click(Fl_Widget *w, void *data)
{
    winpopup->hide();
    //Choisir un fichier...
    Fl_File_Chooser chooser(
                    ".", // directory
                     "*.ntkdigunit\t*.mnlgxdunit\t*.prlgunit",
                     Fl_File_Chooser::SINGLE,
                     strtypes[ongletSelectionne]);
    chooser.preview(false);
    chooser.ok_label("Charger dans le NTS-1");
    chooser.directory(dos[ongletSelectionne]);
    chooser.color(FL_BLACK);
    chooser.textfont(FL_HELVETICA);
    chooser.textcolor(FL_WHITE);
    chooser.textsize(20);
    chooser.show();
    while (chooser.shown()) Fl::wait();
    if (chooser.value() == NULL) return;

    // et le charger vers le NTS-1
    printf("\nCharger %s [%s] dans le slot %d\n", types[ongletSelectionne], chooser.value(), selectedSlot);

    // Lance load.sh
    // exemple $ ./load.sh 1 1 hw:1,0,0 /media/149Go/C/FLTK/korg/osc/j6_v201.ntkdigunit 8
    char cmd [256];
    int n = sprintf(cmd,"./load.sh %d %d %s %s %d", in, out, strmidi, chooser.value(), selectedSlot);
    exec(cmd);

    // Redemande au NTS-1 la liste des OSC/MOD/DELAY/ ou REVERB pour refaire la liste, normalement à jour
    onglet_click(ongletSelectionne);
}
/*=======================================================*/
void ligne_click(Fl_Widget *w, void *data)
{
    Fl_Browser *fbrow = (Fl_Browser*)w;
    int index = fbrow->value();
    if (ongletSelectionne==-1 || index <= 1) return;
    selectedSlot = index-2;
    //printf("click %s slot %d\n", types[ongletSelectionne], selectedSlot);
    // Ouvre la fenêtre modale pour demander "supprimer ou charger un nouveau dans ce slot?"
    winpopup->show();
}
/*=======================================================*/
int main(int argc, char **argv)
{
    Fl_Window *win = new Fl_Window(820,700, "NTS-1 digital librarian");
    win->color(FL_BLACK);
    win->position((Fl::w() - win->w())/2, (Fl::h() - win->h())/2);

    // Titre blanc
    Fl_Box *box = new Fl_Box(0,20,260,40,"NTS-1 digital");
            box->box(FL_FLAT_BOX);
            box->color(FL_BLACK);
            box->labelcolor(FL_WHITE);
            box->labelfont(FL_BOLD);
            box->labelsize(30);

    // Span Info
    span = new Fl_Box(300,20,260,40,"Welcome");
    span->box(FL_FLAT_BOX);
    span->labelsize(20);
    span->color(FL_BLACK);
    span->labelcolor(fl_rgb_color(245, 127, 23));
    span->labelfont(FL_BOLD);

    // Boutons onglets
    static const char *captions[4]={"USER OSCILLATORS","USER MODULATION FX","USER DELAY FX","USER REVERB FX"};
    for(char n=0; n<4; n++){
        but[n] = new Fl_Button(n*5+(n*200),70, 200,30, captions[n]);
        but[n]->box(FL_FLAT_BOX);
        but[n]->color(grisfonce);
        but[n]->labelcolor(FL_WHITE);
    }
    but[0]->callback(button_osc_click);
    but[1]->callback(button_mod_click);
    but[2]->callback(button_delay_click);
    but[3]->callback(button_reverb_click);

    // Table
    int widths[] = {30,200,140,80}; // widths for each column
    br = new ColResizeBrowser(0,100, win->w(),win->h()-70);
    br->column_widths(widths);
    br->showcolsep(1);
    br->colsepcolor(FL_DARK1);
    br->column_char('\t'); // tabs as column delimiters
    br->type(FL_HOLD_BROWSER);//FL_MULTI_BROWSER);
    br->callback(ligne_click);
    br->color(FL_BLACK);

    //@l: large font   @b: bold   @B12: background-color 12  C255: text color white
    br->add("\t@B89@C7@l@b@.NAME\t@B112@C7@b@l@.VERSION\t@B120@C7@b@l@.API\t");
    //br->add("@C255@.0\t@C255@l@.waves\t@C255@.v1.00-0\t@C255@.1.00-0");
    //br->add("@C255@.1\t@C255@l@.Volca Bass\t@C255@.v1.00-1\t@C255@.1.01-0");
    //br->add("@C255@.2\t@C255@l@.808bass\t@C255@.v0.01-0\t@C255@.1.01-0");
    br->add("@C255@.0\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.1\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.2\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.3\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.4\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.5\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.6\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.7\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.8\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.9\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.10\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.11\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.12\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.13\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.14\t@C255@l@.--\t@C255@.--\t@C255@.--");
    br->add("@C255@.15\t@C255@l@.--\t@C255@.--\t@C255@.--");

    // Regarde si logue_cli est présent
    FILE *fp = fopen("logue-cli","r");
    if(!fp) {
        span->label("logue_cli est introuvable");
        //int a=system("xdg-open https://github.com/korginc/logue-sdk/blob/master/tools/logue-cli/get_logue_cli_linux.sh"); 
    }
    else{
        fclose(fp);
        // Regarde si le NTS-1 est branché
        if(!NTS1Connected())
            span->label("Branchez le NTS-1 en USB\npuis relancez l'appli");
            //printf("Aucun engin MIDI connecté. Branchez le NTS-1 en USB et redémarrez\n");
        else{
            span->label("Connecté");//printf("strMIDI:{%s}\n",strmidi);
            //Identifie les ports entrée/sortie pour renseigner les variables in out -i 1 -o 1
            getInOutValues();
        }
    }

    onglet_click(0); // Charge les oscillateurs

    win->end();
    win->show(argc, argv);

    //Fenêtre popup modale cachée pour l'instant
    winpopup = new Fl_Window(310,70);
        winpopup->set_modal();
        winpopup->color(FL_BLACK);
    Fl_Button *suppr = new Fl_Button(20,20, 100,30, "Supprimer");
        suppr->box(FL_FLAT_BOX);
        suppr->color(grisfonce);
        suppr->labelcolor(FL_WHITE);
        suppr->callback(button_suppr_click);
    Fl_Button *nouvo = new Fl_Button(150,20, 150,30, "Charger nouveau");
        nouvo->box(FL_FLAT_BOX);
        nouvo->color(grisfonce);
        nouvo->labelcolor(FL_WHITE);
        nouvo->callback(button_nouvo_click);

    return Fl::run();
}


Compilation de cette application

Pour compiler l'appli il faut installer la lib FLTK 1.3 et son paquet de développement:
$ sudo apt install libftlk1.3 libfltk1.3-dev
$ cd /home/joe/korg
$ fltk-config --compile digital-librarian-linux.cpp
Vous pouvez même la compiler sur Windows et Mac, en cas de problème de compilation regarder ici documentation FLTK 1.3

Si vous avez déja l'exécutable compilé, pour le déployer sur une autre machine Debian/Ubuntu il suffit d'installer la librairie FLTK 1.3:
$ sudo apt-get install libfltk1.3
il faut aussi une copie de logue_cli (le mettre près de l'exé), qui permet de transférer en ligne de commande.

Un script shell est lancé par l'exécutable pour uploader les modules vers le synthétiseur:
load.sh
#!/usr/bin/env sh

# Exemple to load OSC J6 in slot 8
#                $1 $2   $3      $4                                                $5
#$ ./load.sh 1 1 hw:1,0,0 /home/joe/osc/j6_v201.ntkdigunit 8

./logue-cli load -v -i $1 -o $2 -u $4 -s $5 -d > load.log
tail 1 load.log| sed 's/,//g' | sed 's/}//g' | sed 's/{//g' | sed 's/>//g' | sed 's/^ *//g' > load.sysex
amidi -p $3 -S `cat load.sysex`

rm load.log
rm load.sysex


amidi doit être installé, testez avec la commande
$ amidi -l
$ sudo apt-get install amidi
Un dossier osc, un dossier mod, un dossier delay, un dossier reverb
Ces dossiers contiennent les fichiers à transférer vers le NTS-1
Ce n'est pas obligatoire car les fichiers à transférer peuvent être n'importe où, mais c'est plus pratique.

Comment lancer cette application

Depuis une console:
$ cd /home/joe/korg
$ ./digital-librarian-linux
ou
$ ./home/joe/digital-librarian-linux
ou double-cliquer sur l'icone de l'exécutable,
ou créer un lanceur dans la barre de lancement rapide en glissant/déplaçant l'exé sur la barre,
ou créer un fichier .dektop

Si vous avez un Korg NTS-1 et vous aimez Linux, je vous invite fortement à
Télécharger le kit complet: exécutable Ubuntu/source/scripts Shell/oscillateurs, mods, delay, reverbs (497Ko)

Le Korg NTS-1 est en vente sur Amazon pour 89€

Vos commentaires