925 lines · 25.5 KB · C++
RawDownload
/*----------------------------------------------------------------------------- Entaro Adun!  ChucK Manifold-Interface Amplitude Panning (MIAP).  Based on the research of Zachary Seldess, as presented in his AES paper and conference talk, "MIAP: Manifold-Interface Amplitude Panning in Max/MSP and Pure Data", which in turn is based off the work of Steven Ellison and his first implementation of "Barycentric Panning."  YouTube links: Part 1: https://www.youtube.com/watch?v=LUHVwQSkv9s Part 2: https://www.youtube.com/watch?v=RKvCAvHo7ZI  Paper: Seldess, Zachary. 2014. "MIAP: Manifold-Interface Amplitude Panning      in Max/MSP and Pure Data." Presentation at the annual conference  This implementation is pared down from the Max/MSP object (as one would expect from a ChucK Chugin). The functionality of the various nodes Seldess describes in his paper can be replicated by linking various nodes to another nodes.  This Chugin is very much open to contributions, and the author would like to see it evolve (of course, only if interest is generated, I find the subject somewhat fascinating).  Attribution: This Chugin is based off the work of Zachary Seldess' MaxMSP and Pure Date externsl, and also based on Meyer Sound’s SpaceMap(R) multi-channel panning feature of CueStation. SpaceMap and CueStation are trademarks of Meyer Sound Laboratories, Incorporated.-----------------------------------------------------------------------------*/ // this should align with the correct versions of these ChucK files#include "chuck_dl.h"#include "chuck_def.h" // general includes#include <stdio.h>#include <limits.h>#include <vector>#include <cmath>#include <algorithm> using namespace std; // declaration of chugin constructorCK_DLL_CTOR(miap_ctor);// declaration of chugin desctructorCK_DLL_DTOR(miap_dtor); CK_DLL_MFUN(miap_addNode);CK_DLL_MFUN(miap_updateNode);CK_DLL_MFUN(miap_linkNodes);CK_DLL_MFUN(miap_addTriset);CK_DLL_MFUN(miap_clearTrisets);CK_DLL_MFUN(miap_clearAll);CK_DLL_MFUN(miap_generateGrid); // settersCK_DLL_MFUN(miap_setPosition);CK_DLL_MFUN(miap_setConstantPower);CK_DLL_MFUN(miap_setSquareRoot);CK_DLL_MFUN(miap_setLinear); // settersCK_DLL_MFUN(miap_getPositionX);CK_DLL_MFUN(miap_getPositionY);CK_DLL_MFUN(miap_getNodeValue);CK_DLL_MFUN(miap_getNodeX);CK_DLL_MFUN(miap_getNodeY);CK_DLL_MFUN(miap_getActiveTriset);CK_DLL_MFUN(miap_getActiveNode);CK_DLL_MFUN(miap_getNumNodes);CK_DLL_MFUN(miap_getNumTrisets); // for Chugins extending UGen, this is mono synthesis function for 1 sampleCK_DLL_TICK(miap_tick); // this is a special offset reserved for Chugin internal datat_CKINT miap_data_offset = 0; // utility functionsvoid computeVector(float x1, float y1, float x2, float y2, float *v) {    v[0] = x1 - x2;    v[1] = y1 - y2;} // Euclidean distance between two coordinatesfloat distance(float x1, float y1, float x2, float y2) {    return sqrt(pow(x2 - x1, 2) + pow(y2 - y1, 2));} // cosine constant power panningfloat cosinePower(float p) {    return cos((0.5 * M_PI * p) + 1.5 * M_PI);} float dotProduct(float v[], float u[], int n) {    float result = 0.0;     for (int i = 0; i < n; i++) {        result += v[i]*u[i];    }     return result;} // area of a triangle given the lengths of its sidesfloat heronArea(float a, float b, float c) {    float s = (a + b + c) * 0.5;    return sqrt(s * (s - a) * (s - b) * (s - c));}  struct Node{    float x;    float y;    float value;     int index;    bool active;     vector<int> linkID;    vector<float> linkPercentage;};  class Triset{ public:     Triset( Node &n1, Node &n2, Node &n3, int idx)    : m_n1(&n1), m_n2(&n2), m_n3(&n3), index(idx)    {        dot02 = 0.0;        dot12 = 0.0;         u = 0.0;        v = 0.0;         ap = 0.0;        bp = 0.0;        cp = 0.0;         n1Index = m_n1->index;        n2Index = m_n2->index;        n3Index = m_n3->index;         n1Area = 0.0;        n2Area = 0.0;        n3Area = 0.0;         active = false;         precalculate();    }     Node * m_n1;    Node * m_n2;    Node * m_n3;     int index;    int active;     int n1Index;    int n2Index;    int n3Index;     // calculated upon initialization or if the user    // changes the coordinates of a node    void precalculate() {        ab = distance(m_n1->x, m_n1->y, m_n2->x, m_n2->y);        bc = distance(m_n2->x, m_n2->y, m_n3->x, m_n3->y);        ca = distance(m_n3->x, m_n3->y, m_n1->x, m_n1->y);         area = heronArea(ab, bc, ca);        areaScalar = 1.0/area;         // a few pointInTriset operations that never change        computeVector(m_n3->x, m_n3->y, m_n1->x, m_n1->y, v0);        computeVector(m_n2->x, m_n2->y, m_n1->x, m_n1->y, v1);         dot00 = dotProduct(v0, v0, 2);        dot01 = dotProduct(v0, v1, 2);        dot11 = dotProduct(v1, v1, 2);         invDenom = 1.0/(dot00 * dot11 - dot01 * dot01);    }     // http://blackpawn.com/texts/pointinpoly/    // many parameters were precalculated in the constructor to save on processing    // might make the code a bit more obscure    bool pointInTriset(float x, float y)    {        computeVector(x, y, m_n1->x, m_n1->y, v2);         // vector calculation        dot02 = dotProduct(v0, v2, 2);        dot12 = dotProduct(v1, v2, 2);         // compute barycentric coordinates        u = (dot11 * dot02 - dot01 * dot12) * invDenom;        v = (dot00 * dot12 - dot01 * dot02) * invDenom;         return (u >= 0) && (v >= 0) && ((u + v) < 1);    }     // finds the areas of the three triangles that make up the a triset    // those areas sum to 1.0, and the square root of each is set to be    // the value for that node    void setNodes(float x, float y)    {        ap = distance(m_n1->x, m_n1->y, x, y);        bp = distance(m_n2->x, m_n2->y, x, y);        cp = distance(m_n3->x, m_n3->y, x, y);         n3Area = heronArea(ab, bp, ap);        n2Area = heronArea(ca, ap, cp);        n1Area = area - n3Area - n2Area;         m_n1->value = n1Area * areaScalar;        m_n2->value = n2Area * areaScalar;        m_n3->value = n3Area * areaScalar;    } private:     float v0[2];    float v1[2];    float v2[2];     float dot00;    float dot01;    float dot11;    float invDenom;     // sides of the triset    float ab, bc, ca;     // area of the triest    float area;    float areaScalar;     // evaluated in pointInTriset    float dot02, dot12;    float u, v;     // evaluated in setTrisetNodes    float ap, bp, cp;    float n1Area, n2Area, n3Area;};  // class definition for internal Chugin dataclass MIAP{public:    // constructor    MIAP( t_CKFLOAT fs)    {        m_numNodes = 0;        m_numTrisets = 0;        m_panningType = CONSTANT_POWER;    }     // for Chugins extending UGen    SAMPLE tick( SAMPLE in )    {        return in;    }     // set parameter example    void addNode( t_CKFLOAT x, t_CKFLOAT y )    {        Node node;         node.x = x;        node.y = y;        node.value = 0.0;        node.active = false;        node.index = m_numNodes;         m_nodes.push_back( node );         m_numNodes++;    }     void addTriset( t_CKINT n1, t_CKINT n2, t_CKINT n3 )    {        // you can't add a triset without at least three nodes        if (m_numNodes < 3)        {            printf("You cannot add a triset without at least three nodes to connect to.");            return;        }         // you can't associate a node to a triset        // unless it's one of the created nodes        if (n1 >= m_numNodes || n2 >= m_numNodes || n3 >= m_numNodes)        {            printf("You cannot add a triset to a node that has not been created");            return;        }         Triset triset( m_nodes[n1], m_nodes[n2], m_nodes[n3], m_numTrisets );        triset.index = m_numTrisets;        m_trisets.push_back( triset );         m_numTrisets++;    }     void clearNodes() {        m_nodes.erase(m_nodes.begin(), m_nodes.begin() + m_nodes.size());        m_nodes.clear();        m_numNodes = 0;    }     void clearTrisets() {        m_trisets.erase(m_trisets.begin(), m_trisets.begin() + m_trisets.size());        m_trisets.clear();        m_numTrisets = 0;    }     void clearAll() {        clearTrisets();        clearNodes();    }     // links two nodes, wherein node `a` will send    // its value to node `b' and the percentage is how    // much of that value will be added to it    void linkNodes( t_CKINT a, t_CKINT b, t_CKFLOAT percentage)    {        // copy by reference        vector<int> &linkID = m_nodes[a].linkID;        vector<float> &linkPercentage = m_nodes[a].linkPercentage;         // find item position in vector        int pos = find(linkID.begin(), linkID.end(), b) - linkID.begin();         // if value is not already in linkID or linkPercentage vectors        // then we append it to the vector, else we update the percentage        if (pos == linkID.size()) {            linkID.push_back(b);            linkPercentage.push_back(percentage);        } else {            linkPercentage[pos] = percentage;        }    }     void setConstantPower()    {        m_panningType = CONSTANT_POWER;    }     void setSquareRoot()    {        m_panningType = SQUARE_ROOT;    }     void setLinear()    {        m_panningType = LINEAR;    }     // main user function for panning, sets the position of the    // object to be "panned" in xy space    void setPosition( t_CKFLOAT x, t_CKFLOAT y )    {        // clears all node values and active statuses        clearAllNodeValues();        updateTrisetNodeValues(x, y);         // storing our coordinates in case a triset        // needs to be updated when changing one of        // its node coordinates        m_x = x;        m_y = y;    }     // main function that does all the stuff    void updateTrisetNodeValues(float x, float y)    {        // first check if the coordinate is a already a node        for (int i = 0; i < m_numNodes; i++) {            if (m_nodes[i].x == x && m_nodes[i].y == y) {                m_nodes[i].value = 1.0;                updateNodeLink(&m_nodes[i]);                return;            }        }         // search for the triset the point falls in        for (int i = 0; i < m_numTrisets; i++) {            if (m_trisets[i].pointInTriset(x, y)) {                m_trisets[i].active = true;                m_trisets[i].setNodes(x, y);                updateTrisetLinks(m_trisets[i]);                return;            }        }    }     void updateNode( t_CKINT id, t_CKFLOAT x, t_CKFLOAT y )    {        // skips if no changes        if (m_nodes[id].x == x && m_nodes[id].y == y) {            return;        }         m_nodes[id].x = x;        m_nodes[id].y = y;         // updates any affected trisets        for (int i = 0; i < m_numTrisets; i++) {            if (m_trisets[i].n1Index == id || m_trisets[i].n2Index == id || m_trisets[i].n3Index == id) {                m_trisets[i].precalculate();            }        }         // update our trisets using our stored coordinates        clearAllNodeValues();        updateTrisetNodeValues(m_x, m_y);    }     // generates a MIAP grid that is rows by cols grid of Nodes,    // the nodes are created sequentially from left to right per row,    // this is a quick way to make a grid of equilateral triangles    void generateGrid(int rows, int cols)    {        float horzLen = 1.0;        float vertLen = pow(pow(horzLen, 2) - pow(horzLen/2.0, 2), 0.5);         float inverseMax = 0.0;        float xCenterNudge = 0.0;        float yCenterNudge = 0.0;         if (cols >= rows) {            inverseMax = 1.0/((cols- 1) * horzLen + horzLen * 0.5);            yCenterNudge = (1.0 - (((rows - 1) * vertLen) * inverseMax)) * 0.5;        } else {            inverseMax = 1.0/((rows - 1) * vertLen);            xCenterNudge = (1.0 - (((cols - 1) * horzLen + horzLen * 0.5) * inverseMax)) * 0.5;        }         // add our nodes        for (int i = 0; i < rows; i++) {            float offset = 0.0;            if (i % 2 != 0) {                offset = horzLen * 0.5;            }            for (int j = 0; j < cols; j++) {                float x = (j * horzLen + offset) * inverseMax + xCenterNudge;                float y = i * vertLen * inverseMax + yCenterNudge;                addNode(x, y);            }        }         // add our trisets        for (int i = 0; i < rows - 1; i++) {            for (int j = 0; j < cols - 1; j++) {                // trisets pointing down                {                    int n1 = j + (cols * i);                    int n2 = n1 + 1;                    int n3 = j + cols * (i + 1);                     if (i % 2 == 1) {                        n3++;                    }                     addTriset(n1, n2, n3);                }                // trisets pointing up                {                    int n1 = j + (cols * i) + 1;                    int n2 = j + cols * (i + 1);                    int n3 = n2 + 1;                      if (i % 2 == 1) {                        n1--;                    }                     addTriset(n1, n2, n3);                }            }        }    }     int getActiveTriset()    {        for (int i = 0; i < m_numTrisets; i++) {            if (m_trisets[i].active == true) {                return i;            }        }        return -1;

Lines 1–500 of 925.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t