gaje

2016/07/04

Modules

Filed under: — Gan @ 7:13 AM
{
    "n{
    "name": "midigw",
    "type": "Module",
    "member": [
        {
            "name": "MIDI channel",
            "type": "NumberSelector",
            "min": 1,
            "max": 16
        },
        {
            "name": "Voices",
            "type": "Selector",
            "choices": ["mono", "duo", "poly 4", "poly 5", "poly 6", "poly 8", "poly 10", "poly 16"]
        },
        {
            "name": "Retrigger",
            "type": "Switch",
            "default": True
        }
    ]
}

{
    "name": "amplifier",
    "type": "Module",
    "member": [
        { "name": "Voice", "type": "Knob", "max": 15 },
        { "name": "Initial", "type": "Knob" },
        { "name": "Scale", "type": "Knob" },
        { "name": "Expression Sensitivity": "type": "Knob" },

        { "name": "Modulation", "type": "Input" }
    }
}

{
    "name": "envelope generator",
    "type": "Module",
    "member": [
        {
            "name": "Hit",
            "type": "Group",
            "member": [
                { "name": "Intensity", "type": "Knob" },
                { "name": "Velocity", "type": "Knob" }
            ]
        },
        {
            "name": "Attack",
            "type": "Group",
            "member": [
                { "name": "Initial Level", "type": "Knob" },
                { "name": "Velocity", "type": "Knob" },
                { "name": "Time", "type": "Knob" }
            ]
        },
        {
            "name": "1st Decay",
            "type": "Group",
            "member": [
                { "name": "Level", "type": "Knob" },
                { "name": "Time", "type": "Knob" }
            ]
        },
        {
            "name": "2nd Decay",
            "type" "Group",
            "member": [
                { "name": "Level", "type": "Knob" },
                { "name": "Velocity", "type" : "Knob" },
                { "name": "Time", "type": "Knob" }
            ]
        },
        { "name": "Release Time": "type": "Knob" },
    ]
}

2016/07/01

Module Description

Filed under: — Gan @ 8:36 PM

Outline

Here is an example of schema and generated table for MIDI Gateway module:

{
    "name": "midigw",
    "type": "Module",
    "member": [
        {
            "name": "MIDI channel",
            "type": "NumberSelector",
            "min": 1,
            "max": 16
        },
        {
            "name": "Voices",
            "type": "Selector",
            "choices": ["mono", "duo", "poly 4", "poly 5", "poly 6", "poly 8", "poly 10", "poly 16"]
        },
        {
            "name": "Retrigger",
            "type": "Switch",
            "default": True
        }
    ]
}

#define P_N1_MIDI_CHANNEL_OFFSET 0
#define P_L_VOICES_OFFSET        1
#define P_B_RETRIGGER_OFFSET     2
#define P_BUFFER_SIZE            3

static uint8_t p_buffer[P_BUFFER_SIZE];

#define P_N1_MIDI_CHANNEL (*(uint8_t*)&p_buffer[P_N1_MIDI_CHANNEL_OFFSET])
#define P_N1_MIDI_CHANNEL_VALUE_OFFSET 1

#define P_L_VOICES       (*(uint8_t*)&p_buffer[P_L_VOICES_OFFSET])
#define P_L_VOICES__MONO     0
#define P_L_VOICES__DUO      1
#define P_L_VOICES__POLY_4   2
#define P_L_VOICES__POLY_5   3
#define P_L_VOICES__POLY_6   4
#define P_L_VOICES__POLY_8   5
#define P_L_VOICES__POLY_10  6
#define P_L_VOICES__POLY_16  7

int id_offset = 1;

There are two ways to modify a component parameter. They are:

  • Direct control using a component address
  • Indirect control through a unit

Component Address

Every component has its unique address in order to exchange its parameter via data bus. A component address is a 4 byte integer like IP address. Thus, you have two ways of address management — static and dynamic.

Unit

*** TODO: rename unit to union

Analog 3 will have many parameters that may cause difficulty in control them. Unit helps simplifying synth manipulation.

A unit is a virtual component that consists of zero or more components or units. It has its own control parameter. A unit is a component on data bus. Thus it has a component address. Updating the unit parameter causes changes in its sub-components at once.

An example usage of units is building polyphonic perspective. When you build a four-voice polyphonic synthesizer, you make a unit for each synth parameter (such as VCF cutoff frequency) and recruits corresponding physical components of the four voices. Its representation looks following:

{
    "name": "cutoff frequency",
    "type": "KnobUnit",
    "members": [
        "voice1.vcf.cutoff_frequency",
        "voice2.vcf.cutoff_frequency",
        "voice3.vcf.cutoff_frequency",
        "voice4.vcf.cutoff_frequency"
    ]
}

Another example is bundling multiple parameters to simplify manipulation.

{
    "name": "sharpness",
    "type": "KnobUnit",
    "members": [
        "envelope_generator.hit.intensity",
        "envelope_generator.attack.time",
        "envelope_generator.1st_decay.time"
    ]
}

2016/06/23

Module Management Message

Filed under: — Gan @ 9:49 PM

Overview

Module Management Messages are sent via extended data frame.

Module management messages have two categories that are broadcast and direct. Each category has different set of commands.

Identifier

The first 11 MSB of the address must be recessive (1) followed by 1-bit message category indicator whose values are broadcast (recessive:1) and direct (dominant:0). The next one bit is continuous frame flag. The rest 16-bit is component identifier.

// module management message data frame
struct mmm_data_id {
    int11 padding = 0x7ff;
    int1 category; // 1: broadcast, 0: direct
    int1 continuous; // 1: continue, 0: last frame
    int16 component_id;
};

Sending a remote frame to a module management message ID requests broadcasting some data of the specified component.

struct mmm_remote_id {
    int11 padding = 0x7ff;    
    int2 request_type; // 0: the primary value of the component
                       // 1: full description of the component without sub-component IDs
                       // 2: full description of the component with sub-component IDs
                       // 3: list of listening signal IDs
    int16 component_id;
};

Data

Broadcast Message

data[0]   : command
data[1:n] : (variable length, n < 8) arguments

commands:
0x00 <number> : the primary value
0x01 <stream> : full description
0x02          : call for component manager ID
0x03 <short>  : answer component manager ID
0x04          : call for ID of performance signal generator
0x05 <short>  : answer performance signal generator ID

Direct Message

data[0]     : command
data[1:3]   : target component address (2byte)
data[3:3+n] : (variable length, n < 5) arguments

comm  arguments                          mnemonic
-------------------------------------------------------------------------------
0x00  value:number                       set value
0x01  signalID:int16[,gain:int16]        start listening to a signal
0x02  signalID:int16                     stop listening to a signal
0x03  unionID:int16[,gain:int16]         link to a union
0x04  unionID:int16                      unlink from a union
0x05  unionID:int16,description:stream   create a union (only for the manager)
0x06  unionID:int16                      delete a union (only for the manager)

Modifier

Filed under: — Gan @ 9:48 PM

A modifier frame includes only a value as a number in data field.

Performance Signal

Filed under: — Gan @ 9:42 PM

Overview

Performance signals are basically conversions of MIDI messages. The MIDI Gateway module picks up channel voice messages of a certain MIDI channel, interprets them as single or multi voice performance signals. MIDI also has messages for multi voices such as expression control. These messages go into a special voice called “omnivoice”.

Each voice has its CAN ID. Every module listens to a particular voice and omnivoice.

Identifier

Performance signal uses <number_of_voices> + 1 for the omunivoice CAN identifiers. All identifiers should fit the standard data frame ID, i.e., 11bit. The identifier for the omnivoice must be the largest among all voice identifiers.

Performance Signal Frame Structure

Performance signals are sent using standard data frames. The frame data does not include indicator of Analaog3 signal type. Signal receivers know the signal type by frame identifier numbers.

struct can_standard_data_frame {
    int11 id;
    uint4 data_length;
    uint8 data[];
};
A performance signal frame data consists of one byte message followed by one or more bytes of values.
 ex)
 performanceSignal.data_length = 2;
 performacneSignal.data[] = { 0x09, 0x7f }; // note on with velocity = 128

If the message byte has four zero MSB’s, the rest four LSB’s are equivalent to four MSB’s of a MIDI channel voice message. In other words, you can easily convert a MIDI channel voice message to an A3 performance signal message by shifting 4 bit rightward:

performanceSignal.data[0] = midi.channel_voice_message >> 4;

0x08 Note-Off <uint8:note> <number:velocity>
0x09 Note-On <uint8:note> <number:velocity>
0x0a Poly Key Pressure <uint8:note> <number:value>
0x0b Control Change
0x0c Program Change
0x0d Channel Pressure
0x0e Pitch Bend

The values are one or multi-byte big endian (network order) unsigned integer.

 

Trigger

Filed under: — Gan @ 9:19 PM

2016/06/22

Bus Data

Filed under: — Gan @ 9:37 PM

Use Cases

The purposes of Analog 3 data bus are:

  1. To share performance control data (performance signal)
    I start with translating MIDI to “Analog 3 Voice Driving Protocol”.
  2. To exchange module control signals, such as LFO and envelopes (modifier)
    This takes the same role with control voltage. The design should be flexible for types of signal.
  3. To control and manage modules (module management message)
    • change a parameter (values, wires)
    • read a parameter
    • list parameters
    • manage IDs
  4. To synchronize modules (trigger)

CAN ID Addressing

Analog3 mainly uses two types of CAN frames — standard data frame and extended data frame that are described as follows.

struct standard_data_frame = {
uint11 id;
uint4 data_length;
uint8 data[];
};

struct extended_data_frame = {
uint29 id;
uint4 data_length;
uint8 data[];
};

A single component has one or more IDs for:

  • The module ID in extended address space
  • Zero or more output ID in standard address space

An output port falls into one of following ranks according to its signal priority, listed with priority in descending order

  • trigger
  • modifier
  • performance signal

CAN IDs are dynamically assigned with respect to modules’ ranks.

We use extended address space for component IDs, but their 11-bit MSBs must be all 1. On the other hand, any signal frame ID must avoid address 0x7ff. Thus, any output signal is guaranteed to win any module management messages.

 

2015/02/18

Software Synth Image: Eventying is a Component

Filed under: — Gan @ 4:30 PM
  • Perspective: hardware
    • Rack
      • SynthModule
        • Knob
        • Selector
        • Port
    • Wires
      • Wire (IDs are valid within the perspective)
  • Perspective: voice
    • details are TBD here…

 

Architecture

Filed under: — Gan @ 3:15 PM

Requirements

  • A hardware module must be independent — it should be able to run without external controller.

Organizer provides images of synthesizer modules as software objects.

One of the important goals of Analog3 is to build a “synthesizer” image using software, configure the Hardware Modules and Hardware Control Surface as a reflection of the software synth image.  Once the configuration is done, the “synthesizer” should be able to run (be performed) without software control.

analog3 architecture

2015/01/04

Polyphonic

Filed under: — Gan @ 11:44 AM
  • Voice management
  • Midi decoder
  • Auto tune
Older Posts »

Powered by WordPress