@@ -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 |
@@ -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); | |||
} |
@@ -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 |
@@ -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 | |||
} |
@@ -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 |
@@ -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); | |||
} |
@@ -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 */ |
@@ -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; | |||
} |
@@ -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) |
@@ -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 |
@@ -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) | |||