Browse Source

Initial Caitlib code

tags/v0.9.0
falkTX 12 years ago
parent
commit
dcc46c7f46
11 changed files with 2861 additions and 1 deletions
  1. +39
    -0
      c++/caitlib/Makefile
  2. +700
    -0
      c++/caitlib/caitlib.c
  3. +254
    -0
      c++/caitlib/caitlib.h
  4. +33
    -0
      c++/caitlib/caitlib.pro
  5. +874
    -0
      c++/caitlib/list.h
  6. +357
    -0
      c++/caitlib/memory_atomic.c
  7. +105
    -0
      c++/caitlib/memory_atomic.h
  8. +151
    -0
      c++/caitlib/test.c
  9. +204
    -0
      src/caitlib.py
  10. +19
    -0
      src/caitlib_helpers.py
  11. +125
    -1
      src/caitlyn.py

+ 39
- 0
c++/caitlib/Makefile View File

@@ -0,0 +1,39 @@
#!/usr/bin/make -f
# Makefile for caitlib #
# ----------------------------- #
# Created by falkTX
#

include ../Makefile.mk

BUILD_C_FLAGS += -D_GNU_SOURCE -I.
BUILD_C_FLAGS += -fvisibility=hidden -fPIC -std=gnu99 -Werror
BUILD_C_FLAGS += $(shell pkg-config --cflags jack)

LINK_FLAGS += -fPIC -shared -lm -lpthread
LINK_FLAGS += $(shell pkg-config --libs jack)

OBJS = \
caitlib.o \
memory_atomic.o \

# --------------------------------------------------------------

all: caitlib.so test

doxygen: caitlib.doxygen
doxygen $<

caitlib.so: $(OBJS)
$(CC) $^ $(LINK_FLAGS) -o $@ && $(STRIP) $@

test: $(OBJS) test.o
$(CC) $^ -lm -lpthread -ljack -o $@ && $(STRIP) $@

# --------------------------------------------------------------

.c.o:
$(CC) -c $< $(BUILD_C_FLAGS) -o $@

clean:
rm -f $(OBJS) *.so *.dll

+ 700
- 0
c++/caitlib/caitlib.c View File

@@ -0,0 +1,700 @@
/*
* Caitlib
* Copyright (C) 2007 Nedko Arnaudov <nedko@arnaudov.name>
* Copyright (C) 2012 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the COPYING file
*/

#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <jack/jack.h>
#include <jack/midiport.h>

#include "caitlib.h"
#include "list.h"
#include "memory_atomic.h"

#define MAX_EVENT_DATA_SIZE 100
#define MIN_PREALLOCATED_EVENT_COUNT 100
#define MAX_PREALLOCATED_EVENT_COUNT 1000

//#define USE_LIST_HEAD_OUTS // incomplete

// ------------------------------------------------------------------------------------------

typedef struct list_head ListHead;
typedef pthread_mutex_t Mutex;

typedef struct _RawMidiEvent {
ListHead siblings;

jack_midi_data_t data[MAX_EVENT_DATA_SIZE];
size_t dataSize;
jack_nframes_t time;
} RawMidiEvent;

typedef struct _CaitlibOutPort {
#ifdef USE_LIST_HEAD_OUTS
ListHead siblings;
#endif
uint32_t id;
ListHead queue;
jack_port_t* port;
} CaitlibOutPort;

typedef struct _CaitlibInstance {
bool doProcess, wantEvents;

jack_client_t* client;
jack_port_t* midiIn;

#ifdef USE_LIST_HEAD_OUTS
ListHead outPorts;
#else
uint32_t midiOutCount;
CaitlibOutPort** midiOuts;
#endif

ListHead inQueue;
ListHead inQueuePendingRT;
Mutex mutex;

rtsafe_memory_pool_handle mempool;
} CaitlibInstance;

// ------------------------------------------------------------------------------------------

#define handlePtr ((CaitlibInstance*)ptr)

static
int jack_process(jack_nframes_t nframes, void* ptr)
{
if (! handlePtr->doProcess)
return 0;

void* portBuffer;
RawMidiEvent* eventPtr;

jack_position_t transportPos;
jack_transport_state_t transportState = jack_transport_query(handlePtr->client, &transportPos);

if (transportPos.unique_1 != transportPos.unique_2)
transportPos.frame = 0;

// MIDI In
if (handlePtr->wantEvents)
{
jack_midi_event_t inEvent;
jack_nframes_t inEventCount;

portBuffer = jack_port_get_buffer(handlePtr->midiIn, nframes);
inEventCount = jack_midi_get_event_count(portBuffer);

for (jack_nframes_t i = 0 ; i < inEventCount; i++)
{
if (jack_midi_event_get(&inEvent, portBuffer, i) != 0)
break;

if (inEvent.size > MAX_EVENT_DATA_SIZE)
continue;

/* allocate memory for buffer copy */
eventPtr = rtsafe_memory_pool_allocate(handlePtr->mempool);
if (eventPtr == NULL)
{
//LOG_ERROR("Ignored midi event with size %u because memory allocation failed.", (unsigned int)inEvent.size);
continue;
}

/* copy buffer data */
memcpy(eventPtr->data, inEvent.buffer, inEvent.size);
eventPtr->dataSize = inEvent.size;
eventPtr->time = transportPos.frame + inEvent.time;

/* Add event buffer to inQueuePendingRT list */
list_add(&eventPtr->siblings, &handlePtr->inQueuePendingRT);
}
}

if (pthread_mutex_trylock(&handlePtr->mutex) != 0)
return 0;

if (handlePtr->wantEvents)
list_splice_init(&handlePtr->inQueuePendingRT, &handlePtr->inQueue);

#ifdef USE_LIST_HEAD_OUTS
if (transportState == JackTransportRolling)
{
}
#else
// MIDI Out
if (handlePtr->midiOutCount > 0 && transportState == JackTransportRolling)
{
ListHead* entryPtr;
CaitlibOutPort* outPortPtr;

for (uint32_t i = 0; i < handlePtr->midiOutCount; i++)
{
outPortPtr = handlePtr->midiOuts[i];
portBuffer = jack_port_get_buffer(outPortPtr->port, nframes);
jack_midi_clear_buffer(portBuffer);

list_for_each(entryPtr, &outPortPtr->queue)
{
eventPtr = list_entry(entryPtr, RawMidiEvent, siblings);

if (transportPos.frame > eventPtr->time || transportPos.frame + nframes <= eventPtr->time)
continue;

if (jack_midi_event_write(portBuffer, eventPtr->time - transportPos.frame, eventPtr->data, eventPtr->dataSize) != 0)
break;
}
}
}
#endif

pthread_mutex_unlock(&handlePtr->mutex);

return 0;
}

#undef handlePtr

// ------------------------------------------------------------------------------------------
// Initialization

CaitlibHandle caitlib_init(const char* instanceName)
{
CaitlibInstance* handlePtr = (CaitlibInstance*)malloc(sizeof(CaitlibInstance));

if (handlePtr == NULL)
goto fail;

handlePtr->doProcess = true;
handlePtr->wantEvents = false;

#ifdef USE_LIST_HEAD_OUTS
INIT_LIST_HEAD(&handlePtr->outPorts);
#else
handlePtr->midiOuts = NULL;
handlePtr->midiOutCount = 0;
#endif

INIT_LIST_HEAD(&handlePtr->inQueue);
INIT_LIST_HEAD(&handlePtr->inQueuePendingRT);

if (! rtsafe_memory_pool_create(sizeof(RawMidiEvent), MIN_PREALLOCATED_EVENT_COUNT, MAX_PREALLOCATED_EVENT_COUNT, &handlePtr->mempool))
goto fail_free;

pthread_mutex_init(&handlePtr->mutex, NULL);

handlePtr->client = jack_client_open(instanceName, JackNullOption, 0);

if (handlePtr->client == NULL)
goto fail_destroyMutex;

handlePtr->midiIn = jack_port_register(handlePtr->client, "midi-in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);

if (handlePtr->midiIn == NULL)
goto fail_closeJack;

if (jack_set_process_callback(handlePtr->client, jack_process, handlePtr) != 0)
goto fail_closeJack;

if (jack_activate(handlePtr->client) != 0)
goto fail_closeJack;

return handlePtr;

fail_closeJack:
jack_client_close(handlePtr->client);

fail_destroyMutex:
pthread_mutex_destroy(&handlePtr->mutex);

//fail_destroy_pool:
rtsafe_memory_pool_destroy(handlePtr->mempool);

fail_free:
free(handlePtr);

fail:
return NULL;
}

#define handlePtr ((CaitlibInstance*)handle)

void caitlib_close(CaitlibHandle handle)
{
// wait for jack processing to end
handlePtr->doProcess = false;
pthread_mutex_lock(&handlePtr->mutex);

if (handlePtr->client)
{
#ifdef USE_LIST_HEAD_OUTS
ListHead* entryPtr;
CaitlibOutPort* outPortPtr;
#endif

jack_deactivate(handlePtr->client);
jack_port_unregister(handlePtr->client, handlePtr->midiIn);

#ifdef USE_LIST_HEAD_OUTS
list_for_each(entryPtr, &handlePtr->outPorts)
{
outPortPtr = list_entry(entryPtr, CaitlibOutPort, siblings);
jack_port_unregister(handlePtr->client, outPortPtr->port);
}
#else
for (uint32_t i = 0; i < handlePtr->midiOutCount; i++)
{
jack_port_unregister(handlePtr->client, handlePtr->midiOuts[i]->port);
free(handlePtr->midiOuts[i]);
}
#endif

jack_client_close(handlePtr->client);
}

pthread_mutex_unlock(&handlePtr->mutex);
pthread_mutex_destroy(&handlePtr->mutex);

rtsafe_memory_pool_destroy(handlePtr->mempool);

free(handlePtr);
}

uint32_t caitlib_create_port(CaitlibHandle handle, const char* portName)
{
uint32_t nextId = 0;

// wait for jack processing to end
handlePtr->doProcess = false;
pthread_mutex_lock(&handlePtr->mutex);

// re-allocate pointers (midiOutCount + 1) and find next available ID
{
#ifdef USE_LIST_HEAD_OUTS
ListHead* entryPtr;
CaitlibOutPort* outPortPtr;

list_for_each(entryPtr, &handlePtr->outPorts)
{
outPortPtr = list_entry(entryPtr, CaitlibOutPort, siblings);

if (outPortPtr->id == nextId)
{
nextId++;
continue;
}
}
#else
CaitlibOutPort* oldMidiOuts[handlePtr->midiOutCount];

for (uint32_t i = 0; i < handlePtr->midiOutCount; i++)
{
oldMidiOuts[i] = handlePtr->midiOuts[i];

if (handlePtr->midiOuts[i]->id == nextId)
nextId++;
}

if (handlePtr->midiOuts)
free(handlePtr->midiOuts);

handlePtr->midiOuts = (CaitlibOutPort**)malloc(sizeof(CaitlibOutPort*) * (handlePtr->midiOutCount+1));

for (uint32_t i = 0; i < handlePtr->midiOutCount; i++)
handlePtr->midiOuts[i] = oldMidiOuts[i];
#endif
}

// we can continue normal operation now
pthread_mutex_unlock(&handlePtr->mutex);
handlePtr->doProcess = true;

#ifdef USE_LIST_HEAD_OUTS
#else
// create new port
{
CaitlibOutPort* newPort = (CaitlibOutPort*)malloc(sizeof(CaitlibOutPort));

newPort->id = nextId;
newPort->port = jack_port_register(handlePtr->client, portName, JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
INIT_LIST_HEAD(&newPort->queue);

handlePtr->midiOuts[handlePtr->midiOutCount++] = newPort;
}
#endif

return nextId;
}

void caitlib_destroy_port(CaitlibHandle handle, uint32_t port)
{
// wait for jack processing to end
handlePtr->doProcess = false;
pthread_mutex_lock(&handlePtr->mutex);

#ifdef USE_LIST_HEAD_OUTS
#else
// re-allocate pointers (midiOutCount - 1)
{
CaitlibOutPort* oldMidiOuts[handlePtr->midiOutCount];

for (uint32_t i = 0; i < handlePtr->midiOutCount; i++)
oldMidiOuts[i] = handlePtr->midiOuts[i];

if (handlePtr->midiOuts)
free(handlePtr->midiOuts);

if (handlePtr->midiOutCount == 1)
{
handlePtr->midiOuts = NULL;
}
else
{
handlePtr->midiOuts = (CaitlibOutPort**)malloc(sizeof(CaitlibOutPort*) * (handlePtr->midiOutCount-1));

for (uint32_t i = 0, j = 0; i < handlePtr->midiOutCount; i++)
{
if (oldMidiOuts[i]->id != port)
handlePtr->midiOuts[j++] = oldMidiOuts[i];
}
}

handlePtr->midiOutCount--;
}
#endif

pthread_mutex_unlock(&handlePtr->mutex);
handlePtr->doProcess = true;

return;
(void)port;
}

// ------------------------------------------------------------------------------------------
// Input

void caitlib_want_events(CaitlibHandle handle, bool yesNo)
{
pthread_mutex_lock(&handlePtr->mutex);
handlePtr->wantEvents = yesNo;
pthread_mutex_unlock(&handlePtr->mutex);
}

MidiEvent* caitlib_get_event(CaitlibHandle handle)
{
static MidiEvent midiEvent;

ListHead* nodePtr;
RawMidiEvent* rawEventPtr;

pthread_mutex_lock(&handlePtr->mutex);
if (list_empty(&handlePtr->inQueue))
{
pthread_mutex_unlock(&handlePtr->mutex);
return NULL;
}

nodePtr = handlePtr->inQueue.prev;

list_del(nodePtr);

rawEventPtr = list_entry(nodePtr, RawMidiEvent, siblings);

pthread_mutex_unlock(&handlePtr->mutex);

// note off
if (rawEventPtr->dataSize == 3 && (rawEventPtr->data[0] & 0xF0) == 0x80)
{
midiEvent.type = MIDI_EVENT_TYPE_NOTE_OFF;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.note.note = rawEventPtr->data[1];
midiEvent.data.note.velocity = rawEventPtr->data[2];
}

// note on
else if (rawEventPtr->dataSize == 3 && (rawEventPtr->data[0] & 0xF0) == 0x90)
{
midiEvent.type = MIDI_EVENT_TYPE_NOTE_ON;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.note.note = rawEventPtr->data[1];
midiEvent.data.note.velocity = rawEventPtr->data[2];
}

// aftertouch
else if (rawEventPtr->dataSize == 3 && (rawEventPtr->data[0] & 0xF0) == 0xA0)
{
midiEvent.type = MIDI_EVENT_TYPE_AFTER_TOUCH;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.note.note = rawEventPtr->data[1];
midiEvent.data.note.velocity = rawEventPtr->data[2];
}

// control
else if (rawEventPtr->dataSize == 3 && (rawEventPtr->data[0] & 0xF0) == 0xB0)
{
midiEvent.type = MIDI_EVENT_TYPE_CONTROL;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.control.controller = rawEventPtr->data[1];
midiEvent.data.control.value = rawEventPtr->data[2];
}

// program
else if (rawEventPtr->dataSize == 2 && (rawEventPtr->data[0] & 0xF0) == 0xC0)
{
midiEvent.type = MIDI_EVENT_TYPE_PROGRAM;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.program.value = rawEventPtr->data[1];
}

// channel pressure
else if (rawEventPtr->dataSize == 2 && (rawEventPtr->data[0] & 0xF0) == 0xD0)
{
midiEvent.type = MIDI_EVENT_TYPE_CHANNEL_PRESSURE;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.pressure.value = rawEventPtr->data[1];
}

// pitch wheel
else if (rawEventPtr->dataSize == 3 && (rawEventPtr->data[0] & 0xF0) == 0xE0)
{
midiEvent.type = MIDI_EVENT_TYPE_PITCH_WHEEL;
midiEvent.channel = rawEventPtr->data[0] & 0x0F;
midiEvent.data.pitchwheel.value = ((rawEventPtr->data[2] << 7) | rawEventPtr->data[1]) - 8192;
}

else
{
return NULL;
}

rtsafe_memory_pool_deallocate(handlePtr->mempool, rawEventPtr);

return &midiEvent;
}

// ------------------------------------------------------------------------------------------
// Output (utils)

void caitlib_midi_encode_control(RawMidiEvent* eventPtr, uint8_t channel, uint8_t control, uint8_t value)
{
eventPtr->data[0] = MIDI_EVENT_TYPE_CONTROL | (channel & 0x0F);
eventPtr->data[1] = control;
eventPtr->data[2] = value;
eventPtr->dataSize = 3;
}

void caitlib_midi_encode_note_on(RawMidiEvent* eventPtr, uint8_t channel, uint8_t note, uint8_t velocity)
{
eventPtr->data[0] = MIDI_EVENT_TYPE_NOTE_ON | (channel & 0x0F);
eventPtr->data[1] = note;
eventPtr->data[2] = velocity;
eventPtr->dataSize = 3;
}

void caitlib_midi_encode_note_off(RawMidiEvent* eventPtr, uint8_t channel, uint8_t note, uint8_t velocity)
{
eventPtr->data[0] = MIDI_EVENT_TYPE_NOTE_OFF | (channel & 0x0F);
eventPtr->data[1] = note;
eventPtr->data[2] = velocity;
eventPtr->dataSize = 3;
}

void caitlib_midi_encode_aftertouch(RawMidiEvent* eventPtr, uint8_t channel, uint8_t note, uint8_t pressure)
{
eventPtr->data[0] = MIDI_EVENT_TYPE_AFTER_TOUCH | (channel & 0x0F);
eventPtr->data[1] = note;
eventPtr->data[2] = pressure;
eventPtr->dataSize = 3;
}

void caitlib_midi_encode_channel_pressure(RawMidiEvent* eventPtr, uint8_t channel, uint8_t pressure)
{
eventPtr->data[0] = MIDI_EVENT_TYPE_CHANNEL_PRESSURE | (channel & 0x0F);
eventPtr->data[1] = pressure;
eventPtr->dataSize = 2;
}

void caitlib_midi_encode_program(RawMidiEvent* eventPtr, uint8_t channel, uint8_t program)
{
eventPtr->data[0] = MIDI_EVENT_TYPE_PROGRAM | (channel & 0x0F);
eventPtr->data[1] = program;
eventPtr->dataSize = 2;
}

void caitlib_midi_encode_pitchwheel(RawMidiEvent* eventPtr, uint8_t channel, int16_t pitchwheel)
{
pitchwheel += 8192;
eventPtr->data[0] = MIDI_EVENT_TYPE_PITCH_WHEEL | (channel & 0x0F);
eventPtr->data[1] = pitchwheel & 0x7F;
eventPtr->data[2] = pitchwheel >> 7;
eventPtr->dataSize = 3;
}

// ------------------------------------------------------------------------------------------
// Output

void caitlib_put_event(CaitlibHandle handle, uint32_t port, const MidiEvent* event)
{
#ifdef USE_LIST_HEAD_OUTS
ListHead* entryPtr;
#endif
RawMidiEvent* eventPtr;
CaitlibOutPort* outPortPtr;
bool portFound = false;

eventPtr = rtsafe_memory_pool_allocate_sleepy(handlePtr->mempool);
eventPtr->time = event->time;

switch (event->type)
{
case MIDI_EVENT_TYPE_CONTROL:
caitlib_midi_encode_control(eventPtr, event->channel, event->data.control.controller, event->data.control.value);
break;
case MIDI_EVENT_TYPE_NOTE_ON:
caitlib_midi_encode_note_on(eventPtr, event->channel, event->data.note.note, event->data.note.velocity);
break;
case MIDI_EVENT_TYPE_NOTE_OFF:
caitlib_midi_encode_note_off(eventPtr, event->channel, event->data.note.note, event->data.note.velocity);
break;
case MIDI_EVENT_TYPE_AFTER_TOUCH:
caitlib_midi_encode_aftertouch(eventPtr, event->channel, event->data.note.note, event->data.note.velocity);
break;
case MIDI_EVENT_TYPE_CHANNEL_PRESSURE:
caitlib_midi_encode_channel_pressure(eventPtr, event->channel, event->data.pressure.value);
break;
case MIDI_EVENT_TYPE_PROGRAM:
caitlib_midi_encode_program(eventPtr, event->channel, event->data.program.value);
break;
case MIDI_EVENT_TYPE_PITCH_WHEEL:
caitlib_midi_encode_pitchwheel(eventPtr, event->channel, event->data.pitchwheel.value);
break;
default:
return;
}

pthread_mutex_lock(&handlePtr->mutex);

#ifdef USE_LIST_HEAD_OUTS
list_for_each(entryPtr, &handlePtr->outPorts)
{
outPortPtr = list_entry(entryPtr, CaitlibOutPort, siblings);

if (outPortPtr->id == port)
{
list_add_tail(&eventPtr->siblings, &outPortPtr->queue);
break;
}
}
#else
for (uint32_t i = 0; i < handlePtr->midiOutCount; i++)
{
outPortPtr = handlePtr->midiOuts[i];

if (outPortPtr->id == port)
{
portFound = true;
list_add_tail(&eventPtr->siblings, &outPortPtr->queue);
break;
}
}
#endif

if (! portFound)
printf("caitlib_put_event(%p, %i, %p) - failed to find port", handle, port, event);

pthread_mutex_unlock(&handlePtr->mutex);
}

void caitlib_put_control(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t control, uint8_t value)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_CONTROL;
event.channel = channel;
event.time = time;
event.data.control.controller = control;
event.data.control.value = value;
caitlib_put_event(handle, port, &event);
}

void caitlib_put_note_on(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t note, uint8_t velocity)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_NOTE_ON;
event.channel = channel;
event.time = time;
event.data.note.note = note;
event.data.note.velocity = velocity;
caitlib_put_event(handle, port, &event);
}

void caitlib_put_note_off(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t note, uint8_t velocity)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_NOTE_OFF;
event.channel = channel;
event.time = time;
event.data.note.note = note;
event.data.note.velocity = velocity;
caitlib_put_event(handle, port, &event);
}

void caitlib_put_aftertouch(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t note, uint8_t pressure)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_AFTER_TOUCH;
event.channel = channel;
event.time = time;
event.data.note.note = note;
event.data.note.velocity = pressure;
caitlib_put_event(handle, port, &event);
}

void caitlib_put_channel_pressure(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t pressure)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_CHANNEL_PRESSURE;
event.channel = channel;
event.time = time;
event.data.pressure.value = pressure;
caitlib_put_event(handle, port, &event);
}

void caitlib_put_program(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t program)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_PROGRAM;
event.channel = channel;
event.time = time;
event.data.program.value = program;
caitlib_put_event(handle, port, &event);
}

void caitlib_put_pitchwheel(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, int16_t value)
{
MidiEvent event;
event.type = MIDI_EVENT_TYPE_PITCH_WHEEL;
event.channel = channel;
event.time = time;
event.data.pitchwheel.value = value;
caitlib_put_event(handle, port, &event);
}

+ 254
- 0
c++/caitlib/caitlib.h View File

@@ -0,0 +1,254 @@
/*
* Caitlib
* Copyright (C) 2007 Nedko Arnaudov <nedko@arnaudov.name>
* Copyright (C) 2012 Filipe Coelho <falktx@falktx.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For a full copy of the GNU General Public License see the COPYING file
*/

#ifndef CAITLIB_INCLUDED
#define CAITLIB_INCLUDED

#ifdef __cplusplus
extern "C" {
#else
#include <stdbool.h>
#endif

#include <stdint.h>

#ifdef _WIN32
#define CAITLIB_EXPORT __declspec (dllexport)
#else
#define CAITLIB_EXPORT __attribute__ ((visibility("default")))
#endif

/*!
* @defgroup CaitlibAPI Caitlib API
*
* The Caitlib API
*
* @{
*/

/*!
* Native handle for all Caitlib calls.
*/
typedef void* CaitlibHandle;

/*!
* @defgroup MidiEventType MidiEvent Type
* @{
*/
#define MIDI_EVENT_TYPE_NULL 0x00 //!< Null Event.
#define MIDI_EVENT_TYPE_NOTE_OFF 0x80 //!< Note-Off Event, uses Note data.
#define MIDI_EVENT_TYPE_NOTE_ON 0x90 //!< Note-On Event, uses Note data.
#define MIDI_EVENT_TYPE_AFTER_TOUCH 0xA0 //!< [Key] AfterTouch Event, uses Note data.
#define MIDI_EVENT_TYPE_CONTROL 0xB0 //!< Control Event, uses Control data.
#define MIDI_EVENT_TYPE_PROGRAM 0xC0 //!< Program Event, uses Program data.
#define MIDI_EVENT_TYPE_CHANNEL_PRESSURE 0xD0 //!< Channel Pressure Event, uses Pressure data.
#define MIDI_EVENT_TYPE_PITCH_WHEEL 0xE0 //!< PitchWheel Event, uses PitchWheel data.
/**@}*/

/*!
* MidiEvent in Caitlib
*/
typedef struct _MidiEvent
{
/*!
* MidiEvent Type
*/
uint16_t type;

/*!
* MidiEvent Channel (0 - 16)
*/
uint8_t channel;

/*!
* MidiEvent Data (values depend on type)
*/
union MidiEventData {
struct MidiEventControl {
uint8_t controller;
uint8_t value;
} control;

struct MidiEventNote {
uint8_t note;
uint8_t velocity;
} note;

struct MidiEventPressure {
uint8_t value;
} pressure;

struct MidiEventProgram {
uint8_t value;
} program;

struct MidiEventPitchWheel {
int16_t value;
} pitchwheel;

#ifndef DOXYGEN
// padding for future events
struct _MidiEventPadding {
uint8_t pad[32];
} __padding;
#endif
} data;

/*!
* MidiEvent Time (in frames)
*/
uint32_t time;

} MidiEvent;

// ------------------------------------------------------------------------------------------

/*!
* @defgroup Initialization
*
* Functions for initialization and destruction of Caitlib instances.
* @{
*/

/*!
* Initialize a new Caitlib instance with name \a instanceName.\n
* Must be closed when no longer needed with caitlib_close().
*
* \note MIDI Input is disabled by default, call caitlib_want_events() to enable them.
* \note There are no MIDI Output ports by default, call caitlib_create_port() for that.
*/
CAITLIB_EXPORT
CaitlibHandle caitlib_init(const char* instanceName);

/*!
* Close a previously opened Caitlib instance.\n
*/
CAITLIB_EXPORT
void caitlib_close(CaitlibHandle handle);

/*!
* Create a new MIDI Output port with name \a portName.\n
* The return value is the ID for the port, which must be passed for any functions in the Output group.\n
* The ID will be >= 0 for a valid port, or -1 if an error occurred.
*/
CAITLIB_EXPORT
uint32_t caitlib_create_port(CaitlibHandle handle, const char* portName);

/*!
* Close a previously opened Caitlib instance.\n
* There's no need to call this function before caitlib_close().
*/
CAITLIB_EXPORT
void caitlib_destroy_port(CaitlibHandle handle, uint32_t port);

/**@}*/

// ------------------------------------------------------------------------------------------

/*!
* @defgroup Input
*
* Functions for MIDI Input handling.
* @{
*/

/*!
* Tell a Caitlib instance wherever if we're interested in MIDI Input.\n
* By default MIDI Input is disabled.\n
* It's safe to call this function multiple times during the lifetime of an instance.
*/
CAITLIB_EXPORT
void caitlib_want_events(CaitlibHandle handle, bool yesNo);

/*!
* Get a MidiEvent from a Caitlib instance buffer.\n
* When there are no more messages in the buffer, this function returns NULL.
*/
CAITLIB_EXPORT
MidiEvent* caitlib_get_event(CaitlibHandle handle);

/**@}*/

// ------------------------------------------------------------------------------------------

/*!
* @defgroup Output
*
* Functions for putting data into Caitlib instances (MIDI Output).
* @{
*/

/*!
* Put a MIDI event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_event(CaitlibHandle handle, uint32_t port, const MidiEvent* event);

/*!
* Put a MIDI Control event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_control(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t controller, uint8_t value);

/*!
* Put a MIDI Note On event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_note_on(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t note, uint8_t velocity);

/*!
* Put a MIDI Note Off event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_note_off(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t note, uint8_t velocity);

/*!
* Put a MIDI AfterTouch event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_aftertouch(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t note, uint8_t pressure);

/*!
* Put a MIDI Channel Pressure event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_channel_pressure(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t pressure);

/*!
* Put a MIDI Program event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_program(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, uint8_t program);

/*!
* Put a MIDI PitchWheel event into a Caitlib instance port.
*/
CAITLIB_EXPORT
void caitlib_put_pitchwheel(CaitlibHandle handle, uint32_t port, uint32_t time, uint8_t channel, int16_t value);

/**@}*/

// ------------------------------------------------------------------------------------------

/**@}*/

#ifdef __cplusplus
}
#endif

#endif // CAITLIB_INCLUDED

+ 33
- 0
c++/caitlib/caitlib.pro View File

@@ -0,0 +1,33 @@
# QtCreator project file

CONFIG = debug
CONFIG += link_pkgconfig warn_on # plugin shared

DEFINES = DEBUG
DEFINES += QTCREATOR_TEST

DEFINES += _GNU_SOURCE

PKGCONFIG = jack

TARGET = caitlib
TEMPLATE = app # lib
VERSION = 0.0.1

SOURCES = \
caitlib.c \
memory_atomic.c

SOURCES += \
test.c

HEADERS = \
caitlib.h

INCLUDEPATH = .

QMAKE_CFLAGS *= -fvisibility=hidden -fPIC -Wextra -Werror -std=gnu99

unix {
LIBS = -lm -lpthread
}

+ 874
- 0
c++/caitlib/list.h View File

@@ -0,0 +1,874 @@
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* Linux kernel header adapted for user-mode
* The 2.6.17-rt1 version was used.
*
* Original copyright holders of this code are unknown, they were not
* mentioned in the original file.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*****************************************************************************/

#ifndef _LINUX_LIST_H
#define _LINUX_LIST_H

#include <stddef.h>

#if !defined(offsetof)
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif

/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})

#define prefetch(x) (x = x)

/*
* These are non-NULL pointers that will result in page faults
* under normal circumstances, used to verify that nobody uses
* non-initialized list entries.
*/
#define LIST_POISON1 ((void *) 0x00100100)
#define LIST_POISON2 ((void *) 0x00200200)

/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/

struct list_head {
struct list_head *next, *prev;
};

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)

static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}

/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}

/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}

/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add_rcu(struct list_head * new,
struct list_head * prev, struct list_head * next)
{
new->next = next;
new->prev = prev;
// smp_wmb();
next->prev = new;
prev->next = new;
}

/**
* list_add_rcu - add a new entry to rcu-protected list
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as list_add_rcu()
* or list_del_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* list_for_each_entry_rcu().
*/
static inline void list_add_rcu(struct list_head *new, struct list_head *head)
{
__list_add_rcu(new, head, head->next);
}

/**
* list_add_tail_rcu - add a new entry to rcu-protected list
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as list_add_tail_rcu()
* or list_del_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* list_for_each_entry_rcu().
*/
static inline void list_add_tail_rcu(struct list_head *new,
struct list_head *head)
{
__list_add_rcu(new, head->prev, head);
}

/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}

/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}

/**
* list_del_rcu - deletes entry from list without re-initialization
* @entry: the element to delete from the list.
*
* Note: list_empty on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the list.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as list_del_rcu()
* or list_add_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* list_for_each_entry_rcu().
*
* Note that the caller is not permitted to immediately free
* the newly deleted entry. Instead, either synchronize_rcu()
* or call_rcu() must be used to defer freeing until an RCU
* grace period has elapsed.
*/
static inline void list_del_rcu(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->prev = LIST_POISON2;
}

/*
* list_replace_rcu - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* The old entry will be replaced with the new entry atomically.
*/
static inline void list_replace_rcu(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->prev = old->prev;
// smp_wmb();
new->next->prev = new;
new->prev->next = new;
old->prev = LIST_POISON2;
}

/**
* list_del_init - deletes entry from list and reinitialize it.
* @entry: the element to delete from the list.
*/
static inline void list_del_init(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
INIT_LIST_HEAD(entry);
}

/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del(list->prev, list->next);
list_add(list, head);
}

/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del(list->prev, list->next);
list_add_tail(list, head);
}

/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}

/**
* list_empty_careful - tests whether a list is
* empty _and_ checks that no other CPU might be
* in the process of still modifying either member
*
* NOTE: using list_empty_careful() without synchronization
* can only be safe if the only activity that can happen
* to the list entry is list_del_init(). Eg. it cannot be used
* if another CPU could re-list_add() it.
*
* @head: the list to test.
*/
static inline int list_empty_careful(const struct list_head *head)
{
struct list_head *next = head->next;
return (next == head) && (next == head->prev);
}

static inline void __list_splice(struct list_head *list,
struct list_head *head)
{
struct list_head *first = list->next;
struct list_head *last = list->prev;
struct list_head *at = head->next;

first->prev = head;
head->next = first;

last->next = at;
at->prev = last;
}

/**
* list_splice - join two lists
* @list: the new list to add.
* @head: the place to add it in the first list.
*/
static inline void list_splice(struct list_head *list, struct list_head *head)
{
if (!list_empty(list))
__list_splice(list, head);
}

/**
* list_splice_init - join two lists and reinitialise the emptied list.
* @list: the new list to add.
* @head: the place to add it in the first list.
*
* The list at @list is reinitialised
*/
static inline void list_splice_init(struct list_head *list,
struct list_head *head)
{
if (!list_empty(list)) {
__list_splice(list, head);
INIT_LIST_HEAD(list);
}
}

/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)

/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)

/**
* __list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*
* This variant differs from list_for_each() in that it's the
* simplest possible list iteration code, no prefetching is done.
* Use this for code that knows the list to be very short (empty
* or 1 entry) most of the time.
*/
#define __list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)

/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
pos = pos->prev)

/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)

/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))

/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member); \
prefetch(pos->member.prev), &pos->member != (head); \
pos = list_entry(pos->member.prev, typeof(*pos), member))

/**
* list_prepare_entry - prepare a pos entry for use as a start point in
* list_for_each_entry_continue
* @pos: the type * to use as a start point
* @head: the head of the list
* @member: the name of the list_struct within the struct.
*/
#define list_prepare_entry(pos, head, member) \
((pos) ? : list_entry(head, typeof(*pos), member))

/**
* list_for_each_entry_continue - iterate over list of given type
* continuing after existing point
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_continue(pos, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member); \
prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))

/**
* list_for_each_entry_from - iterate over list of given type
* continuing from existing point
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; prefetch(pos->member.next), &pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))

/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
* list_for_each_entry_safe_continue - iterate over list of given type
* continuing after existing point safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
* list_for_each_entry_safe_from - iterate over list of given type
* from existing point safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe_from(pos, n, head, member) \
for (n = list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.next, typeof(*n), member))

/**
* list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against
* removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe_reverse(pos, n, head, member) \
for (pos = list_entry((head)->prev, typeof(*pos), member), \
n = list_entry(pos->member.prev, typeof(*pos), member); \
&pos->member != (head); \
pos = n, n = list_entry(n->member.prev, typeof(*n), member))

/**
* list_for_each_rcu - iterate over an rcu-protected list
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*
* This list-traversal primitive may safely run concurrently with
* the _rcu list-mutation primitives such as list_add_rcu()
* as long as the traversal is guarded by rcu_read_lock().
*/
#define list_for_each_rcu(pos, head) \
for (pos = (head)->next; \
prefetch(rcu_dereference(pos)->next), pos != (head); \
pos = pos->next)

#define __list_for_each_rcu(pos, head) \
for (pos = (head)->next; \
rcu_dereference(pos) != (head); \
pos = pos->next)

/**
* list_for_each_safe_rcu - iterate over an rcu-protected list safe
* against removal of list entry
* @pos: the &struct list_head to use as a loop counter.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*
* This list-traversal primitive may safely run concurrently with
* the _rcu list-mutation primitives such as list_add_rcu()
* as long as the traversal is guarded by rcu_read_lock().
*/
#define list_for_each_safe_rcu(pos, n, head) \
for (pos = (head)->next; \
n = rcu_dereference(pos)->next, pos != (head); \
pos = n)

/**
* list_for_each_entry_rcu - iterate over rcu list of given type
* @pos: the type * to use as a loop counter.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* This list-traversal primitive may safely run concurrently with
* the _rcu list-mutation primitives such as list_add_rcu()
* as long as the traversal is guarded by rcu_read_lock().
*/
#define list_for_each_entry_rcu(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
prefetch(rcu_dereference(pos)->member.next), \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))


/**
* list_for_each_continue_rcu - iterate over an rcu-protected list
* continuing after existing point.
* @pos: the &struct list_head to use as a loop counter.
* @head: the head for your list.
*
* This list-traversal primitive may safely run concurrently with
* the _rcu list-mutation primitives such as list_add_rcu()
* as long as the traversal is guarded by rcu_read_lock().
*/
#define list_for_each_continue_rcu(pos, head) \
for ((pos) = (pos)->next; \
prefetch(rcu_dereference((pos))->next), (pos) != (head); \
(pos) = (pos)->next)

/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/

struct hlist_head {
struct hlist_node *first;
};

struct hlist_node {
struct hlist_node *next, **pprev;
};

#define HLIST_HEAD_INIT { .first = NULL }
#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}

static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}

static inline int hlist_empty(const struct hlist_head *h)
{
return !h->first;
}

static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}

static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}

/**
* hlist_del_rcu - deletes entry from hash list without re-initialization
* @n: the element to delete from the hash list.
*
* Note: list_unhashed() on entry does not return true after this,
* the entry is in an undefined state. It is useful for RCU based
* lockfree traversal.
*
* In particular, it means that we can not poison the forward
* pointers that may still be used for walking the hash list.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as hlist_add_head_rcu()
* or hlist_del_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* hlist_for_each_entry().
*/
static inline void hlist_del_rcu(struct hlist_node *n)
{
__hlist_del(n);
n->pprev = LIST_POISON2;
}

static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}

/*
* hlist_replace_rcu - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* The old entry will be replaced with the new entry atomically.
*/
static inline void hlist_replace_rcu(struct hlist_node *old,
struct hlist_node *new)
{
struct hlist_node *next = old->next;

new->next = next;
new->pprev = old->pprev;
// smp_wmb();
if (next)
new->next->pprev = &new->next;
*new->pprev = new;
old->pprev = LIST_POISON2;
}

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}


/**
* hlist_add_head_rcu - adds the specified element to the specified hlist,
* while permitting racing traversals.
* @n: the element to add to the hash list.
* @h: the list to add to.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as hlist_add_head_rcu()
* or hlist_del_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* hlist_for_each_entry_rcu(), used to prevent memory-consistency
* problems on Alpha CPUs. Regardless of the type of CPU, the
* list-traversal primitive must be guarded by rcu_read_lock().
*/
static inline void hlist_add_head_rcu(struct hlist_node *n,
struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
n->pprev = &h->first;
// smp_wmb();
if (first)
first->pprev = &n->next;
h->first = n;
}

/* next must be != NULL */
static inline void hlist_add_before(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
next->pprev = &n->next;
*(n->pprev) = n;
}

static inline void hlist_add_after(struct hlist_node *n,
struct hlist_node *next)
{
next->next = n->next;
n->next = next;
next->pprev = &n->next;

if(next->next)
next->next->pprev = &next->next;
}

/**
* hlist_add_before_rcu - adds the specified element to the specified hlist
* before the specified node while permitting racing traversals.
* @n: the new element to add to the hash list.
* @next: the existing element to add the new element before.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as hlist_add_head_rcu()
* or hlist_del_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* hlist_for_each_entry_rcu(), used to prevent memory-consistency
* problems on Alpha CPUs.
*/
static inline void hlist_add_before_rcu(struct hlist_node *n,
struct hlist_node *next)
{
n->pprev = next->pprev;
n->next = next;
// smp_wmb();
next->pprev = &n->next;
*(n->pprev) = n;
}

/**
* hlist_add_after_rcu - adds the specified element to the specified hlist
* after the specified node while permitting racing traversals.
* @prev: the existing element to add the new element after.
* @n: the new element to add to the hash list.
*
* The caller must take whatever precautions are necessary
* (such as holding appropriate locks) to avoid racing
* with another list-mutation primitive, such as hlist_add_head_rcu()
* or hlist_del_rcu(), running on this same list.
* However, it is perfectly legal to run concurrently with
* the _rcu list-traversal primitives, such as
* hlist_for_each_entry_rcu(), used to prevent memory-consistency
* problems on Alpha CPUs.
*/
static inline void hlist_add_after_rcu(struct hlist_node *prev,
struct hlist_node *n)
{
n->next = prev->next;
n->pprev = &prev->next;
// smp_wmb();
prev->next = n;
if (n->next)
n->next->pprev = &n->next;
}

#define hlist_entry(ptr, type, member) container_of(ptr,type,member)

#define hlist_for_each(pos, head) \
for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
pos = pos->next)

#define hlist_for_each_safe(pos, n, head) \
for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
pos = n)

/**
* hlist_for_each_entry - iterate over list of given type
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(tpos, pos, head, member) \
for (pos = (head)->first; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)

/**
* hlist_for_each_entry_continue - iterate over a hlist continuing after existing point
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_continue(tpos, pos, member) \
for (pos = (pos)->next; \
pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)

/**
* hlist_for_each_entry_from - iterate over a hlist continuing from existing point
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_from(tpos, pos, member) \
for (; pos && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)

/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @n: another &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \
for (pos = (head)->first; \
pos && ({ n = pos->next; 1; }) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = n)

/**
* hlist_for_each_entry_rcu - iterate over rcu list of given type
* @tpos: the type * to use as a loop counter.
* @pos: the &struct hlist_node to use as a loop counter.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*
* This list-traversal primitive may safely run concurrently with
* the _rcu list-mutation primitives such as hlist_add_head_rcu()
* as long as the traversal is guarded by rcu_read_lock().
*/
#define hlist_for_each_entry_rcu(tpos, pos, head, member) \
for (pos = (head)->first; \
rcu_dereference(pos) && ({ prefetch(pos->next); 1;}) && \
({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
pos = pos->next)

#endif

+ 357
- 0
c++/caitlib/memory_atomic.c View File

@@ -0,0 +1,357 @@
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* Non-sleeping memory allocation
*
* Copyright (C) 2006,2007 Nedko Arnaudov <nedko@arnaudov.name>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*****************************************************************************/

#include <stdlib.h>
#include <assert.h>

#include "memory_atomic.h"
#include "list.h"

#define TRUE 1
#define FALSE 0

struct rtsafe_memory_pool
{
size_t data_size;
size_t min_preallocated;
size_t max_preallocated;

struct list_head used;
unsigned int used_count;

struct list_head unused;
unsigned int unused_count;
};

#define RTSAFE_GROUPS_PREALLOCATE 1024

int
rtsafe_memory_pool_create(
size_t data_size,
size_t min_preallocated,
size_t max_preallocated,
rtsafe_memory_pool_handle * pool_handle_ptr)
{
struct rtsafe_memory_pool * pool_ptr;

assert(min_preallocated <= max_preallocated);

pool_ptr = malloc(sizeof(struct rtsafe_memory_pool));
if (pool_ptr == NULL)
{
return FALSE;
}

pool_ptr->data_size = data_size;
pool_ptr->min_preallocated = min_preallocated;
pool_ptr->max_preallocated = max_preallocated;

INIT_LIST_HEAD(&pool_ptr->used);
pool_ptr->used_count = 0;

INIT_LIST_HEAD(&pool_ptr->unused);
pool_ptr->unused_count = 0;

rtsafe_memory_pool_sleepy((rtsafe_memory_pool_handle)pool_ptr);
*pool_handle_ptr = pool_ptr;

return TRUE;
}

#define pool_ptr ((struct rtsafe_memory_pool *)pool_handle)

void
rtsafe_memory_pool_destroy(
rtsafe_memory_pool_handle pool_handle)
{
struct list_head * node_ptr;

assert(pool_ptr->used_count == 0); /* called should deallocate all chunks prior releasing pool itself */
assert(list_empty(&pool_ptr->used));

while (pool_ptr->unused_count != 0)
{
assert(!list_empty(&pool_ptr->unused));

node_ptr = pool_ptr->unused.next;

list_del(node_ptr);
pool_ptr->unused_count--;

free(node_ptr);
}

assert(list_empty(&pool_ptr->unused));

free(pool_ptr);
}

/* adjust unused list size */
void
rtsafe_memory_pool_sleepy(
rtsafe_memory_pool_handle pool_handle)
{
struct list_head * node_ptr;

while (pool_ptr->unused_count < pool_ptr->min_preallocated)
{
node_ptr = malloc(sizeof(struct list_head) + pool_ptr->data_size);
if (node_ptr == NULL)
{
return;
}

list_add_tail(node_ptr, &pool_ptr->unused);
pool_ptr->unused_count++;
}

while (pool_ptr->unused_count > pool_ptr->max_preallocated)
{
assert(!list_empty(&pool_ptr->unused));

node_ptr = pool_ptr->unused.next;

list_del(node_ptr);
pool_ptr->unused_count--;

free(node_ptr);
}
}

/* find entry in unused list, fail if it is empty */
void *
rtsafe_memory_pool_allocate(
rtsafe_memory_pool_handle pool_handle)
{
struct list_head * node_ptr;

if (list_empty(&pool_ptr->unused))
{
return NULL;
}

node_ptr = pool_ptr->unused.next;
list_del(node_ptr);
pool_ptr->unused_count--;
pool_ptr->used_count++;

return (node_ptr + 1);
}

/* move from used to unused list */
void
rtsafe_memory_pool_deallocate(
rtsafe_memory_pool_handle pool_handle,
void * data)
{
list_add_tail((struct list_head *)data - 1, &pool_ptr->unused);
pool_ptr->used_count--;
pool_ptr->unused_count++;
}

void *
rtsafe_memory_pool_allocate_sleepy(
rtsafe_memory_pool_handle pool_handle)
{
void * data;

do
{
rtsafe_memory_pool_sleepy(pool_handle);
data = rtsafe_memory_pool_allocate(pool_handle);
}
while (data == NULL);

return data;
}

/* max alloc is DATA_MIN * (2 ^ POOLS_COUNT) - DATA_SUB */
#define DATA_MIN 1024
#define DATA_SUB 100 /* alloc slightly smaller chunks in hope to not allocating additional page for control data */

struct rtsafe_memory_pool_generic
{
size_t size;
rtsafe_memory_pool_handle pool;
};

struct rtsafe_memory
{
struct rtsafe_memory_pool_generic * pools;
size_t pools_count;
};

int
rtsafe_memory_init(
size_t max_size,
size_t prealloc_min,
size_t prealloc_max,
rtsafe_memory_handle * handle_ptr)
{
size_t i;
size_t size;
struct rtsafe_memory * memory_ptr;

//LOG_DEBUG("rtsafe_memory_init() called.");

memory_ptr = malloc(sizeof(struct rtsafe_memory));
if (memory_ptr == NULL)
{
goto fail;
}

size = DATA_MIN;
memory_ptr->pools_count = 1;

while ((size << memory_ptr->pools_count) < max_size + DATA_SUB)
{
memory_ptr->pools_count++;

if (memory_ptr->pools_count > sizeof(size_t) * 8)
{
assert(0); /* chances that caller really need such huge size are close to zero */
goto fail_free;
}
}

memory_ptr->pools = malloc(memory_ptr->pools_count * sizeof(struct rtsafe_memory_pool_generic));
if (memory_ptr->pools == NULL)
{
goto fail_free;
}

size = DATA_MIN;

for (i = 0 ; i < memory_ptr->pools_count ; i++)
{
memory_ptr->pools[i].size = size - DATA_SUB;

if (!rtsafe_memory_pool_create(
memory_ptr->pools[i].size,
prealloc_min,
prealloc_max,
&memory_ptr->pools[i].pool))
{
while (i > 0)
{
i--;
rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool);
}

goto fail_free_pools;
}

size = size << 1;
}

*handle_ptr = (rtsafe_memory_handle)memory_ptr;

return TRUE;

fail_free_pools:
free(memory_ptr->pools);

fail_free:
free(memory_ptr);

fail:
return FALSE;
}

#define memory_ptr ((struct rtsafe_memory *)handle_ptr)
void
rtsafe_memory_uninit(
rtsafe_memory_handle handle_ptr)
{
unsigned int i;

//LOG_DEBUG("rtsafe_memory_uninit() called.");

for (i = 0 ; i < memory_ptr->pools_count ; i++)
{
//LOG_DEBUG("Destroying pool for size %u", (unsigned int)memory_ptr->pools[i].size);
rtsafe_memory_pool_destroy(memory_ptr->pools[i].pool);
}

free(memory_ptr->pools);

free(memory_ptr);
}

void *
rtsafe_memory_allocate(
rtsafe_memory_handle handle_ptr,
size_t size)
{
rtsafe_memory_pool_handle * data_ptr;
size_t i;

//LOG_DEBUG("rtsafe_memory_allocate() called.");

/* pool handle is stored just before user data to ease deallocation */
size += sizeof(rtsafe_memory_pool_handle);

for (i = 0 ; i < memory_ptr->pools_count ; i++)
{
if (size <= memory_ptr->pools[i].size)
{
//LOG_DEBUG("Using chunk with size %u.", (unsigned int)memory_ptr->pools[i].size);
data_ptr = rtsafe_memory_pool_allocate(memory_ptr->pools[i].pool);
if (data_ptr == NULL)
{
//LOG_DEBUG("rtsafe_memory_pool_allocate() failed.");
return FALSE;
}

*data_ptr = memory_ptr->pools[i].pool;

//LOG_DEBUG("rtsafe_memory_allocate() returning %p", (data_ptr + 1));
return (data_ptr + 1);
}
}

/* data size too big, increase POOLS_COUNT */
//LOG_WARNING("Data size is too big");
return FALSE;
}

void
rtsafe_memory_sleepy(
rtsafe_memory_handle handle_ptr)
{
unsigned int i;

for (i = 0 ; i < memory_ptr->pools_count ; i++)
{
rtsafe_memory_pool_sleepy(memory_ptr->pools[i].pool);
}
}

void
rtsafe_memory_deallocate(
void * data)
{
//LOG_DEBUG("rtsafe_memory_deallocate(%p) called.", data);
rtsafe_memory_pool_deallocate(
*((rtsafe_memory_pool_handle *)data -1),
(rtsafe_memory_pool_handle *)data - 1);
}

+ 105
- 0
c++/caitlib/memory_atomic.h View File

@@ -0,0 +1,105 @@
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* Non-sleeping memory allocation
*
* Copyright (C) 2006,2007 Nedko Arnaudov <nedko@arnaudov.name>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
*****************************************************************************/

#ifndef MEMORY_ATOMIC_H__7B572547_304D_4597_8808_990BE4476CC3__INCLUDED
#define MEMORY_ATOMIC_H__7B572547_304D_4597_8808_990BE4476CC3__INCLUDED

#ifdef __cplusplus
extern "C" {
#endif
#if 0
} /* Adjust editor indent */
#endif

typedef void * rtsafe_memory_pool_handle;

/* will sleep */
int
rtsafe_memory_pool_create(
size_t data_size, /* chunk size */
size_t min_preallocated, /* min chunks preallocated */
size_t max_preallocated, /* max chunks preallocated */
rtsafe_memory_pool_handle * pool_ptr);

/* will sleep */
void
rtsafe_memory_pool_destroy(
rtsafe_memory_pool_handle pool);

/* may sleep */
void
rtsafe_memory_pool_sleepy(
rtsafe_memory_pool_handle pool);

/* will not sleep, returns NULL if no memory is available */
void *
rtsafe_memory_pool_allocate(
rtsafe_memory_pool_handle pool);

/* may sleep */
void *
rtsafe_memory_pool_allocate_sleepy(
rtsafe_memory_pool_handle pool);

/* will not sleep */
void
rtsafe_memory_pool_deallocate(
rtsafe_memory_pool_handle pool,
void * data);

typedef void * rtsafe_memory_handle;

/* will sleep */
int
rtsafe_memory_init(
size_t max_size,
size_t prealloc_min,
size_t prealloc_max,
rtsafe_memory_handle * handle_ptr);

/* will not sleep */
void *
rtsafe_memory_allocate(
rtsafe_memory_handle handle_ptr,
size_t size);

void
rtsafe_memory_sleepy(
rtsafe_memory_handle handle_ptr);

/* will not sleep */
void
rtsafe_memory_deallocate(
void * data);

void
rtsafe_memory_uninit(
rtsafe_memory_handle handle_ptr);

#if 0
{ /* Adjust editor indent */
#endif
#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* #ifndef MEMORY_ATOMIC_H__7B572547_304D_4597_8808_990BE4476CC3__INCLUDED */

+ 151
- 0
c++/caitlib/test.c View File

@@ -0,0 +1,151 @@
/* -*- Mode: C ; c-basic-offset: 2 -*- */
/*****************************************************************************
*
* DESCRIPTION:
*
*
* NOTES:
*
*
*****************************************************************************/

#include "caitlib.h"

#include <unistd.h>

int main()
{
CaitlibHandle handle = caitlib_init("test");
const uint32_t port = caitlib_create_port(handle, "midi-outX");

int sampleRate = 44100; //jack_get_sample_rate(client);

int m = sampleRate/1000; // whatever

// 0 Par ch=1 c=7 v=99
// 0 Par ch=1 c=10 v=63
// 0 Par ch=1 c=0 v=0
caitlib_put_control(handle, port, 0*m, 0, 7, 99);
caitlib_put_control(handle, port, 0*m, 0, 10, 63);
caitlib_put_control(handle, port, 0*m, 0, 0, 0);

// 0 PrCh ch=1 p=0 -- TODO jack_midi_put_program()

// 0 On ch=1 n=64 v=90
// 325 Off ch=1 n=64 v=90
// 384 On ch=1 n=62 v=90
// 709 Off ch=1 n=62 v=90
// 768 On ch=1 n=60 v=90
// 1093 Off ch=1 n=60 v=90
caitlib_put_note_on(handle, port, 0*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 325*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 384*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 709*m, 0, 62, 90);
caitlib_put_note_on(handle, port, 768*m, 0, 60, 90);
caitlib_put_note_off(handle, port, 1093*m, 0, 60, 90);

// 1152 On ch=1 n=62 v=90
// 1477 Off ch=1 n=62 v=90
// 1536 On ch=1 n=64 v=90
// 1861 Off ch=1 n=64 v=90
// 1920 On ch=1 n=64 v=90
// 2245 Off ch=1 n=64 v=90
caitlib_put_note_on(handle, port, 1152*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 1477*m, 0, 62, 90);
caitlib_put_note_on(handle, port, 1536*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 1861*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 1920*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 2245*m, 0, 64, 90);

// 2304 On ch=1 n=64 v=90
// 2955 Off ch=1 n=64 v=90
// 3072 On ch=1 n=62 v=90
// 3397 Off ch=1 n=62 v=90
// 3456 On ch=1 n=62 v=90
// 3781 Off ch=1 n=62 v=90
caitlib_put_note_on(handle, port, 2304*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 2955*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 3072*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 3397*m, 0, 62, 90);
caitlib_put_note_on(handle, port, 3456*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 3781*m, 0, 62, 90);

// 3840 On ch=1 n=62 v=90
// 4491 Off ch=1 n=62 v=90
// 4608 On ch=1 n=64 v=90
// 4933 Off ch=1 n=64 v=90
// 4992 On ch=1 n=67 v=90
// 5317 Off ch=1 n=67 v=90
caitlib_put_note_on(handle, port, 3840*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 4491*m, 0, 62, 90);
caitlib_put_note_on(handle, port, 4608*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 4933*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 4992*m, 0, 67, 90);
caitlib_put_note_off(handle, port, 5317*m, 0, 67, 90);

// 5376 On ch=1 n=67 v=90
// 6027 Off ch=1 n=67 v=90
// 6144 On ch=1 n=64 v=90
// 6469 Off ch=1 n=64 v=90
// 6528 On ch=1 n=62 v=90
// 6853 Off ch=1 n=62 v=90
caitlib_put_note_on(handle, port, 5376*m, 0, 67, 90);
caitlib_put_note_off(handle, port, 6027*m, 0, 67, 90);
caitlib_put_note_on(handle, port, 6144*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 6469*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 6528*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 6853*m, 0, 62, 90);

// 6912 On ch=1 n=60 v=90
// 7237 Off ch=1 n=60 v=90
// 7296 On ch=1 n=62 v=90
// 7621 Off ch=1 n=62 v=90
// 7680 On ch=1 n=64 v=90
// 8005 Off ch=1 n=64 v=90
caitlib_put_note_on(handle, port, 6912*m, 0, 60, 90);
caitlib_put_note_off(handle, port, 7237*m, 0, 60, 90);
caitlib_put_note_on(handle, port, 7296*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 7621*m, 0, 62, 90);
caitlib_put_note_on(handle, port, 7680*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 8005*m, 0, 64, 90);

// 8064 On ch=1 n=64 v=90
// 8389 Off ch=1 n=64 v=90
// 8448 On ch=1 n=64 v=90
// 9099 Off ch=1 n=64 v=90
// 9216 On ch=1 n=62 v=90
// 9541 Off ch=1 n=62 v=90
caitlib_put_note_on(handle, port, 8064*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 8389*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 8448*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 9099*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 9216*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 9541*m, 0, 62, 90);

// 9600 On ch=1 n=62 v=90
// 9925 Off ch=1 n=62 v=90
// 9984 On ch=1 n=64 v=90
// 10309 Off ch=1 n=64 v=90
// 10368 On ch=1 n=62 v=90
// 10693 Off ch=1 n=62 v=90
caitlib_put_note_on(handle, port, 9600*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 9925*m, 0, 62, 90);
caitlib_put_note_on(handle, port, 9984*m, 0, 64, 90);
caitlib_put_note_off(handle, port, 10309*m, 0, 64, 90);
caitlib_put_note_on(handle, port, 10368*m, 0, 62, 90);
caitlib_put_note_off(handle, port, 10693*m, 0, 62, 90);

// 10752 On ch=1 n=60 v=90
// 12056 Off ch=1 n=60 v=90
caitlib_put_note_on(handle, port, 10752*m, 0, 60, 90);
caitlib_put_note_off(handle, port, 12056*m, 0, 60, 90);

// 13824 Par ch=1 c=64 v=0 -- ??

while (1)
sleep(1);

caitlib_close(handle);

return 0;
}

+ 204
- 0
src/caitlib.py View File

@@ -0,0 +1,204 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Caitlib ctypes definitions for usage in python applications
# Copyright (C) 2012 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

# Imports (Global)
from ctypes import *
from sys import platform

# Load Caitlib shared library
try:
if platform == "darwin":
caitlib = cdll.LoadLibrary("caitlib.dylib")
elif platform in ("win32", "win64", "cygwin"):
caitlib = cdll.LoadLibrary("caitlib.dll")
else:
caitlib = cdll.LoadLibrary("caitlib.so")
except:
caitlib = None

# ---------------------------------------------------------------------------------------------------------------------
# Pre-definitions

c_enum = c_int
c_uchar = c_uint8

# ---------------------------------------------------------------------------------------------------------------------
# Defines

MIDI_EVENT_TYPE_NULL = 0x00
MIDI_EVENT_TYPE_NOTE_OFF = 0x80
MIDI_EVENT_TYPE_NOTE_ON = 0x90
MIDI_EVENT_TYPE_AFTER_TOUCH = 0xA0
MIDI_EVENT_TYPE_CONTROL = 0xB0
MIDI_EVENT_TYPE_PROGRAM = 0xC0
MIDI_EVENT_TYPE_CHANNEL_PRESSURE = 0xD0
MIDI_EVENT_TYPE_PITCH_WHEEL = 0xE0

# ---------------------------------------------------------------------------------------------------------------------
# Types

CaitlibHandle = c_void_p

# ---------------------------------------------------------------------------------------------------------------------
# Structs

class MidiEventControl(Structure):
_fields_ = [
("controller", c_uint8),
("value", c_uint8)
]

class MidiEventNote(Structure):
_fields_ = [
("note", c_uint8),
("velocity", c_uint8)
]

class MidiEventPressure(Structure):
_fields_ = [
("value", c_uint8)
]

class MidiEventProgram(Structure):
_fields_ = [
("value", c_uint8)
]

class MidiEventPitchWheel(Structure):
_fields_ = [
("value", c_int16)
]

class _MidiEventPadding(Structure):
_fields_ = [
("pad", ARRAY(c_uint8, 32))
]

class MidiEventData(Union):
_fields_ = [
("control", MidiEventControl),
("note", MidiEventNote),
("pressure", MidiEventPressure),
("program", MidiEventProgram),
("pitchwheel", MidiEventPitchWheel),
("__padding", _MidiEventPadding),
]

class MidiEvent(Structure):
_fields_ = [
("type", c_uint16),
("channel", c_uint8),
("data", MidiEventData),
("time", c_uint32)
]

# ---------------------------------------------------------------------------------------------------------------------
# Callbacks

# ---------------------------------------------------------------------------------------------------------------------
# Functions (Initialization)

caitlib.caitlib_init.argtypes = [c_char_p]
caitlib.caitlib_init.restype = CaitlibHandle

caitlib.caitlib_close.argtypes = [CaitlibHandle]
caitlib.caitlib_close.restype = None

caitlib.caitlib_create_port.argtypes = [CaitlibHandle, c_char_p]
caitlib.caitlib_create_port.restype = c_uint32

caitlib.caitlib_destroy_port.argtypes = [CaitlibHandle, c_uint32]
caitlib.caitlib_destroy_port.restype = None

def init(instanceName):
return caitlib.caitlib_init(instanceName.encode("utf-8"))

def close(handle):
caitlib.caitlib_close(handle)

def create_port(handle, portName):
return caitlib.caitlib_create_port(handle, portName.encode("utf-8"))

def destroy_port(handle, port):
caitlib.caitlib_destroy_port(handle, port)

# ---------------------------------------------------------------------------------------------------------------------
# Functions (Input)

caitlib.caitlib_want_events.argtypes = [CaitlibHandle, c_bool]
caitlib.caitlib_want_events.restype = None

caitlib.caitlib_get_event.argtypes = [CaitlibHandle]
caitlib.caitlib_get_event.restype = POINTER(MidiEvent)

def want_events(handle, yesNo):
caitlib.caitlib_want_events(handle, yesNo)

def get_event(handle):
return caitlib.caitlib_get_event(handle)

# ---------------------------------------------------------------------------------------------------------------------
# Functions (Output)

caitlib.caitlib_put_event.argtypes = [CaitlibHandle, c_uint32, POINTER(MidiEvent)]
caitlib.caitlib_put_event.restype = None

caitlib.caitlib_put_control.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint8, c_uint8]
caitlib.caitlib_put_control.restype = None

caitlib.caitlib_put_note_on.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint8, c_uint8]
caitlib.caitlib_put_note_on.restype = None

caitlib.caitlib_put_note_off.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint8, c_uint8]
caitlib.caitlib_put_note_off.restype = None

caitlib.caitlib_put_aftertouch.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint8, c_uint8]
caitlib.caitlib_put_aftertouch.restype = None

caitlib.caitlib_put_channel_pressure.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint8]
caitlib.caitlib_put_channel_pressure.restype = None

caitlib.caitlib_put_program.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint8]
caitlib.caitlib_put_program.restype = None

caitlib.caitlib_put_pitchwheel.argtypes = [CaitlibHandle, c_uint32, c_uint32, c_uint8, c_uint16]
caitlib.caitlib_put_pitchwheel.restype = None

def put_event(handle, port, event):
caitlib.caitlib_put_event(handle, port, event)

def put_control(handle, port, time, channel, controller, value):
caitlib.caitlib_put_control(handle, port, time, channel, controller, value)

def put_note_on(handle, port, time, channel, note, velocity):
caitlib.caitlib_put_note_on(handle, port, time, channel, note, velocity)

def put_note_off(handle, port, time, channel, note, velocity):
caitlib.caitlib_put_note_off(handle, port, time, channel, note, velocity)

def put_aftertouch(handle, port, time, channel, note, pressure):
caitlib.caitlib_put_aftertouch(handle, port, time, channel, note, pressure)

def put_channel_pressure(handle, port, time, channel, pressure):
caitlib.caitlib_put_channel_pressure(handle, port, time, channel, pressure)

def put_program(handle, port, time, channel, program):
caitlib.caitlib_put_program(handle, port, time, channel, program)

def put_pitchwheel(handle, port, time, channel, value):
caitlib.caitlib_put_pitchwheel(handle, port, time, channel, value)

+ 19
- 0
src/caitlib_helpers.py View File

@@ -0,0 +1,19 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Helper functions for extra caitlib functionality
# Copyright (C) 2012 Filipe Coelho <falktx@falktx.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file

import caitlib

+ 125
- 1
src/caitlyn.py View File

@@ -25,6 +25,7 @@ from PyQt4.QtGui import QApplication, QMainWindow

# Imports (Custom Stuff)
import ui_caitlyn
from caitlib_helpers import *
from shared_settings import *

try:
@@ -97,6 +98,127 @@ class CaitlynMainW(QMainWindow, ui_caitlyn.Ui_CaitlynMainW):

self.item1 = CaitlynCanvasBox(self.scene)

# Sequencer test code
self.m_seq = caitlib.init("Caitlyn")
self.m_port1 = caitlib.create_port(self.m_seq, "out1")

m = 44

caitlib.put_control(self.m_seq, self.m_port1, 0*m, 0, 7, 99)
caitlib.put_control(self.m_seq, self.m_port1, 0*m, 0, 10, 63)
caitlib.put_control(self.m_seq, self.m_port1, 0*m, 0, 0, 0)

# 0 PrCh ch=1 p=0 -- TODO jack_midi_put_program()

# 0 On ch=1 n=64 v=90
# 325 Off ch=1 n=64 v=90
# 384 On ch=1 n=62 v=90
# 709 Off ch=1 n=62 v=90
# 768 On ch=1 n=60 v=90
# 1093 Off ch=1 n=60 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 0*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 325*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 384*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 709*m, 0, 62, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 768*m, 0, 60, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 1093*m, 0, 60, 90)

# 1152 On ch=1 n=62 v=90
# 1477 Off ch=1 n=62 v=90
# 1536 On ch=1 n=64 v=90
# 1861 Off ch=1 n=64 v=90
# 1920 On ch=1 n=64 v=90
# 2245 Off ch=1 n=64 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 1152*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 1477*m, 0, 62, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 1536*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 1861*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 1920*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 2245*m, 0, 64, 90)

# 2304 On ch=1 n=64 v=90
# 2955 Off ch=1 n=64 v=90
# 3072 On ch=1 n=62 v=90
# 3397 Off ch=1 n=62 v=90
# 3456 On ch=1 n=62 v=90
# 3781 Off ch=1 n=62 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 2304*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 2955*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 3072*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 3397*m, 0, 62, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 3456*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 3781*m, 0, 62, 90)

# 3840 On ch=1 n=62 v=90
# 4491 Off ch=1 n=62 v=90
# 4608 On ch=1 n=64 v=90
# 4933 Off ch=1 n=64 v=90
# 4992 On ch=1 n=67 v=90
# 5317 Off ch=1 n=67 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 3840*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 4491*m, 0, 62, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 4608*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 4933*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 4992*m, 0, 67, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 5317*m, 0, 67, 90)

# 5376 On ch=1 n=67 v=90
# 6027 Off ch=1 n=67 v=90
# 6144 On ch=1 n=64 v=90
# 6469 Off ch=1 n=64 v=90
# 6528 On ch=1 n=62 v=90
# 6853 Off ch=1 n=62 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 5376*m, 0, 67, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 6027*m, 0, 67, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 6144*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 6469*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 6528*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 6853*m, 0, 62, 90)

# 6912 On ch=1 n=60 v=90
# 7237 Off ch=1 n=60 v=90
# 7296 On ch=1 n=62 v=90
# 7621 Off ch=1 n=62 v=90
# 7680 On ch=1 n=64 v=90
# 8005 Off ch=1 n=64 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 6912*m, 0, 60, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 7237*m, 0, 60, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 7296*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 7621*m, 0, 62, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 7680*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 8005*m, 0, 64, 90)

# 8064 On ch=1 n=64 v=90
# 8389 Off ch=1 n=64 v=90
# 8448 On ch=1 n=64 v=90
# 9099 Off ch=1 n=64 v=90
# 9216 On ch=1 n=62 v=90
# 9541 Off ch=1 n=62 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 8064*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 8389*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 8448*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 9099*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 9216*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 9541*m, 0, 62, 90)

# 9600 On ch=1 n=62 v=90
# 9925 Off ch=1 n=62 v=90
# 9984 On ch=1 n=64 v=90
# 10309 Off ch=1 n=64 v=90
# 10368 On ch=1 n=62 v=90
# 10693 Off ch=1 n=62 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 9600*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 9925*m, 0, 62, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 9984*m, 0, 64, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 10309*m, 0, 64, 90)
caitlib.put_note_on(self.m_seq, self.m_port1, 10368*m, 0, 62, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 10693*m, 0, 62, 90)

# 10752 On ch=1 n=60 v=90
# 12056 Off ch=1 n=60 v=90
caitlib.put_note_on(self.m_seq, self.m_port1, 10752*m, 0, 60, 90)
caitlib.put_note_off(self.m_seq, self.m_port1, 12056*m, 0, 60, 90)

def saveSettings(self):
self.settings.setValue("Geometry", self.saveGeometry())

@@ -109,8 +231,10 @@ class CaitlynMainW(QMainWindow, ui_caitlyn.Ui_CaitlynMainW):
}

def closeEvent(self, event):
self.scene.removeItem(self.item1)
if self.m_seq:
caitlib.close(self.m_seq)

self.scene.removeItem(self.item1)
self.saveSettings()
QMainWindow.closeEvent(self, event)



Loading…
Cancel
Save