• Jona

    Based on the midi generator https://forum.pdpatchrepo.info/topic/10791/markovgenerator-a-music-generator-based-on-markov-chains-with-variable-length that i made with @ingox some time ago I made a Lua markov chain generator that generates endless markov chains from midi files.
    The patch needs the Ofelia, Cyclone and Zexy libraries.

    I have one question: Is there a way to keep the "original" sounds from the midi file? Perhaps if i store the midi control data together with the other values (pitch, velocity, program change, midi channel)? Somehow the midi program change values do not have the "right" sound information. If I play midi files with the [seq] object it sounds like it should. So for now i choose the sound manually but it would be nice if it sounds like it is meant to.
    LuaMidiMarkov.pd
    LuaMidiMarkov.PNG

    A silly example:

    posted in patch~ read more
  • Jona

    @Cuinjune thanks for pointing that out. I overlooked the first error message. I will replace it with the faster list drip with list store from @ingox as a subpatch. https://forum.pdpatchrepo.info/topic/11122/faster-list-drip-with-list-store-pd-0-48

    posted in patch~ read more
  • Jona

    @Cuinjune Hi. Thanks for trying. The error tells that there is no list in the generator yet. It should be already fixed, it was because of the order creation of Pure Data "Inits". Tell me if not.

    posted in patch~ read more
  • Jona

    I finished the Ofelia / Lua Markov Generator abstraction / patch.
    The markov generator is part of two patches but can easily be used as an abstraction.
    I want to use it for pattern variations of a sequencer for example.
    It just needs a Pure Data list as input and outputs a markov chain of variable order and length.
    Or draw into the array and submit it to the markov generator.
    The first patch is an experiment trying to create interesting sounds with the markov algorithm.
    In addition I used the variable Delay from the Pure Data help files:
    LuaMarkovGeneratorSynthesizer.pd
    LuaMarkovGeneratorSynthesizer.PNG
    The second patch creates markov chains at audio rate, it is quite cpu heavy but works until the 10th markov order.
    It is quite noisy but I was courius how it will sound:
    LuaMarkovGeneratorAudioRate.pd
    LuaMarkovGeneratorAudioRate.PNG

    And here is the Lua code.
    The core of the code is adapted from this python code: https://eli.thegreenplace.net/2018/elegant-python-code-for-a-markov-chain-text-generator/
    A few things that I do not really understand yet, but finally it works without errors (it was not easy sometimes ;) ):

    -- LUA MARKOV GENERATOR;
    function ofelia.list(fv);
    ;
    math.randomseed(os.time()- os.clock() * 1000);
    ;
    print("LUA MARKOV GENERATOR");
    local markovOrder = fv[1];
    print("Markov Order: ", math.floor(markovOrder));
    ;
    -- make dictionary;
    ;
    local function defaultdict(default_value_factory);
    ;
    local t = {};
    local metatable = {};
    metatable.__index = function(t, key);
    if not rawget(t, key) then;
    rawset(t, key, default_value_factory(key));
    end;
    return rawget(t, key);
    end;
    return setmetatable(t, metatable);
    end;
    ;
    -- make markov matrix;
    ;
    local model = defaultdict(function() return {} end);
    local data = {};
    for i = 1, #ofelia.markovInputList do;
    data[i] = ofelia.markovInputList[i];
    end;
    print("Data Size: ", #ofelia.markovInputList);
    for i = 1, markovOrder do;
    table.insert(data, data[i]);
    end;
    for i = 1, #data - markovOrder do;
    local state = table.concat({table.unpack(data, i, i + markovOrder - 1)}, "-");
    local next = table.unpack(data, i + markovOrder, i + markovOrder);
    model[state][next] = (model[state][next] or 0)+1;
    end;
    ;
    -- make tables from dict;
    ;
    local keyTbl = {};
    local nexTbl = {};
    local prbTbl = {};
    for key, value in pairs(model) do;
    for k, v in pairs(value) do;
    table.insert(keyTbl, key);
    table.insert(nexTbl, k);
    table.insert(prbTbl, v);
    end;
    end;
    ;
    print("Key: ", table.unpack(keyTbl));
    print("Nex: ", table.unpack(nexTbl));
    print("Prb: ", table.unpack(prbTbl));
    ;
    print("Make a Markov Chain...");
    ;
    function ofelia.markovChain();
    ;
    -- make start key;
    ;
    local startKey = {};
    if ofelia.randomStart == 1 then;
    local randomKey = math.random(#keyTbl);
    startKey = randomKey;
    else;
    startKey = 1;
    end;
    ;
    local markovString = keyTbl[startKey];
    local out = {};
    for match in string.gmatch(keyTbl[startKey], "[^-]+") do;
    table.insert(out, match);
    end;
    ;
    -- make markov chain;
    ;
    for i = 1, ofelia.markovChainLength do;
    ;
    -- weighted random choices;
    ;
    local choices = {};
    local weights = {};
    for j = 1, #keyTbl do;
    if markovString == keyTbl[j] then;
    table.insert(choices, nexTbl[j]);
    table.insert(weights, prbTbl[j]);
    end;
    end;
    ;
    -- print ("choices:", table.unpack(choices));
    -- print ("weights:", table.unpack(weights));
    ;
    local totalWeight = 0;
    for _, weight in pairs(weights) do;
    totalWeight = totalWeight + weight;
    end;
    rand = math.random() * totalWeight;
    local choice = nil;
    for i, weight in pairs(weights) do;
    if rand < weight then;
    choice = choices[i];
    break;
    else;
    rand = rand - weight;
    end;
    end;
    ;
    if math.type(choice) == "integer" then;
    choice = choice * (1.0);
    end;
    ;
    table.insert(out, choice);
    local lastStep = {table.unpack(out, #out - (markovOrder-1), #out)};
    markovString = table.concat(lastStep, "-");
    end;
    ;
    return {table.unpack(out, markovOrder + 1, #out)};
    end;
    end;
    ;
    

    posted in patch~ read more
  • Jona

    The markov generator works now. It was just a small formatting error.
    Here is a small experiment with the markov generator, but basically i want to use it for sequencing and it can be used for anything else, just needs string formatted data as input. i think it is quite a bit faster than the generator that i build together with @ingox some time ago. https://forum.pdpatchrepo.info/topic/10791/markovgenerator-a-music-generator-based-on-markov-chains-with-variable-length/1
    lua_markov_generator3b.pd

    posted in technical issues read more
  • Jona

    i build a lua markov generator inspired from this python code with the idea to use it with pure data / ofelia: https://eli.thegreenplace.net/2018/elegant-python-code-for-a-markov-chain-text-generator/
    finally the code works fine with the eclipse lua ide or with this ide https://studio.zerobrane.com/, but somehow not yet with pure data / ofelia.
    here is the (not yet working) patch: ofelia_markov.pd
    and here the lua code: markov_pd.lua

    math.randomseed(os.time()- os.clock() * 1000);
    -- make dictionary;
    function defaultdict(default_value_factory);
    local t = {};
    local metatable = {};
    metatable.__index = function(t, key);
    if not rawget(t, key) then;
    rawset(t, key, default_value_factory(key));
    end;
    return rawget(t, key);
    end;
    return setmetatable(t, metatable);
    end;
    ;
    ;
    -- make markov matrix;
    print('Learning model...')
    ;
    STATE_LEN = 3;
    print ("markov order: " , STATE_LEN) 
    model = defaultdict(function() return {} end)
    data = "00001111010100700111101010000005000700111111177111111";
    datasize = #data;
    print("data: ", data);
    print("datasize: ", #data);
    data = data .. data:sub(1, STATE_LEN);
    print("altered data: ", data);
    print("altered datasize: ", #data);
    for i = 1, (#data - STATE_LEN) do;
    state = data:sub(i, i + STATE_LEN-1);
    -- print("state: ", state)    
    local next = data:sub(i + STATE_LEN, i + STATE_LEN);
    -- print("next: ", next);
    model[state][next] = (model[state][next] or 0)+1;
    end;
    ;
    ;
    -- make markov chain;
    print('Sampling...');
    ;
    local keyTbl = {};
    local nexTbl = {};
    local prbTbl = {};
    for key, value in pairs(model) do;
    for k, v in pairs(value) do;
    table.insert(keyTbl, key);
    table.insert(nexTbl, k);
    table.insert(prbTbl, v);
    end;
    end;
    print ("keyTbl: ", table.unpack(keyTbl));
    print ("nexTbl: ", table.unpack(nexTbl));
    print ("prbTbl: ", table.unpack(prbTbl));
    ;
    ;
    -- make random key;
    local randomKey = keyTbl[math.random(#keyTbl)];
    state = randomKey;
    print("RandomKey: ", randomKey);
    ;
    -- make table from random key;
    local str = state;
    local stateTable = {};
    for i = 1, #str do;
    stateTable[i] = str:sub(i, i);
    end;
    ;
    out = stateTable;
    print ("random key as table: ", table.unpack(out));
    ;
    -- make markov chain;
    for i = 1, datasize do;
    ;
    -- weighted random choices;
    local choices = {};
    local weights = {};
    for j = 1, #keyTbl do;
    if state == keyTbl[j] then;
    table.insert(choices, nexTbl[j]);
    table.insert(weights, prbTbl[j]);        
    end;  
    end;
    -- print ("choices:",table.unpack(choices));
    -- print ("weights:",table.unpack(weights));
    ;
    local totalWeight = 0;
    for _, weight in pairs(weights) do;
    totalWeight = totalWeight + weight;
    end;
    rand = math.random() * totalWeight;     
    local choice = nil;
    for i, weight in pairs(weights) do;
    if rand < weight then;       
    choice = choices[i];
    choice = choice:sub(1,1);
    break;
    else;
    rand = rand - weight;
    end;
    end;  
    ;
    table.insert(out, choice); 
    state = string.sub(state, 2, #state) .. out[#out];
    -- print("choice", choice);
    -- print ("state", state);
    end;    
    ;
    print("markov chain: ", table.concat(out));
    

    somehow pure data / ofelia interprets the nexTbl values as a functions while they are strings?
    this is part of what the pure data console prints: nexTbl: function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30 function: 0000000003B9BF30
    ofelia: [string "package.preload['#d41b70'] = nil package.load..."]:93: attempt to index a function value (local 'choice')

    and this ist the output from the lua ide:

    Program 'lua.exe' started in 'C:\Users\Jonat\Downloads\ZeroBraneStudio\myprograms' (pid: 220).
    Learning model...
    markov order:   1
    data:   00001111010100700111101010000005000700111111177111111
    datasize:   53
    altered data:   000011110101007001111010100000050007001111111771111110
    altered datasize:   54
    Sampling...
    keyTbl:     5   7   7   7   1   1   1   0   0   0   0
    nexTbl:     0   0   1   7   7   1   0   5   7   1   0
    prbTbl:     1   2   1   1   1   17  7   1   2   7   13
    RandomKey:  1
    random key as table:    1
    markov chain:   111111000077701111100070001100000001100017011171111117
    Program completed in 0.06 seconds (pid: 220).
    

    posted in technical issues read more
  • Jona

    i implemented the generations family which remembers, in addition to the cellular automata rules, the generation of the cell and changes the color accordingly.
    here is some information about the generations: http://www.mirekw.com/ca/rullex_gene.html
    Ofelia2_GridXConwayGenerations.pd

    posted in patch~ read more
  • Jona

    Hi @Cuinjune, thank you for the feedback.

    1. Happily "Shift" works again. I meant the functions that shift the grid one step into one direction. It did not work anymore because i needed to adjust the functions to the 2 dimensional table.
    2. You are right, I do not need to return the table, i think your suggestion to do that internally with pd.Array() is the solution. Will try that tonight.
    3. It is fine for me to write a 2-dimensional lua table to a 1 dimensional [array]. With "flatten" I meant to make a 1 dimensional table out of a 2 dimensional table. With "deflatten" I meant to make a 2 dimensional table out of a 1 dimensional table. I think I did not found the right terms for that.
    4. Thanks. That was my mistake, it works again. I still think there is some strange behaviour under certain conditions with sending bangs into ofelia objects, but I am not sure anymore. I will check that and post a minimal patch if I still find that behaviour.

    posted in patch~ read more
  • Jona

    I implemented Conways Game of Life and variations into the Grid patch.
    The original game of life rules are applied if the second and third green and the third red toggle are selected (rule 23/3).
    Click some "cells" in the grid for the start position.
    The green toggles set the condition for the living cells.
    The red toggles set the condition for the dead cells.
    It is possible to change the rules while the game of life is running.
    Ofelia2_GridXConway.pd
    gridXConway.PNG

    posted in patch~ read more
  • Jona

    @Cuinjune the conway implementation does work now (with and without borders). to achieve that i changed the 1-dimensional table into a 2-dimensional table. because of that i could not figure out yet how to make "shift" work again (except "shift up", which already works), but i will find a solution. the second issue is (again) saving the grid, because pd seems to crash when i return a 2-dimensional table (even without setting the pd [array]).
    here is a minimal example of my issue: return_2dim_table.pd
    i also wonder if it makes more sense to save a 2-dimensional lua table into a [text] object (which seems kind of an equivalent) or an [array] object. if i save into [array] i have to flatten the lua table before saving and "de"flatten before i load it back into the lua table. or get and set the array/table line by line. basically my main question is: what is the best way to save and recall a 2-dimensional lua table in pure data? everything else does work (and i think better than before).
    i also put the matrix transformation functions (shift, invert and so on...) into single ofelia define objects (again), because i think it is better for keeping track of the code.
    Ofelia2_GridXMatrix.pd
    another issue that i found is that sending a bang into ofelia.bang does not work sometimes while a directly connected bang does work (the "Invert" bang in ofelia2_gridxmatrix.pd for example). which is not really a problem because there are easy workarounds, just wanted to mention it.
    i really appreciate all the possibilities of the ofelia library, and i think i just scratched the surface....
    edit: everything besides saving works again.

    posted in patch~ read more

Internal error.

Oops! Looks like something went wrong!