Rev. | 4 |
---|---|
大小 | 73,991 字节 |
时间 | 2011-09-03 15:22:57 |
作者 | toshinagata1964 |
Log Message | initial import |
/*
* MDTrack.c
*
* Created by Toshi Nagata on Sun Jun 17 2001.
Copyright (c) 2000-2011 Toshi Nagata. All rights reserved.
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.
*/
#include "MDHeaders.h"
#include <stdio.h> /* for sprintf() and standard I/O in MDTrackDump() */
#include <stdlib.h> /* for malloc(), realloc(), and free() */
#include <string.h> /* for memset() and strdup() */
#include <limits.h> /* for LONG_MAX */
#include <ctype.h> /* for isalpha() etc. */
#ifdef __MWERKS__
#pragma mark ====== Private definitions ======
#endif
#define kMDBlockSize 64 /* This number of MDEvent's are allocated per MDBlock */
typedef struct MDBlock MDBlock;
static MDBlock *sFreeBlocks = NULL; /* The pool of free MDBlock's */
struct MDBlock {
MDBlock * next; /* the next MDBlock in the linked list */
MDBlock * last; /* the last MDBlock in the linked list */
long size; /* the number of allocated MDEvent's */
long num; /* the number of actually containing MDEvent's */
MDEvent * events; /* the array of MDEvent's */
MDTickType largestTick; /* the max value of (MDGetTick(&events[i]) + MDHasDuration(&events[i]) ? MDGetDuration(&event[i]) : 0); may be kMDNegativeTick after modification, in which case it should be recached */
};
struct MDTrack {
long refCount; /* the reference count */
long num; /* the number of events */
long numBlocks; /* the number of blocks */
char * name; /* the track name */
char * devname; /* the device name */
MDTrackAttribute attribute; /* the track attribute (Rec/Solo/Mute) */
MDBlock * first; /* the first MDBlock */
MDBlock * last; /* the last MDBlock */
MDTickType duration; /* the track duration in ticks */
long nch[18]; /* the number of channel events (16: sysex, 17: non-MIDI) */
long dev; /* the device number */
short channel; /* the MIDI channel for this track */
/* (meaningful only if the parent MDSequence is single-channel mode) */
MDPointer * pointer; /* the first MDPointer related to this track
(MDPointer's are combined as a linked list) */
/* This is a 'mutable' member, i.e. it may be modified internally
even when a 'const MDTrack *' is passed. This behavior is
acceptable, because this member is strictly internal. */
};
struct MDPointer {
long refCount; /* the reference count */
MDTrack * parent; /* The parent sequence. */
MDBlock * block; /* The current block. */
long position; /* The current position. Can be -1, which means
"before the beginning". */
long index; /* The current index in the current block. */
MDPointer * next;
char removed; /* True if the 'current' event has been removed. */
char allocated; /* True if allocated by malloc() */
char autoAdjust; /* True if autoadjust is done after insert/delete (default is false) */
};
#ifdef __MWERKS__
#pragma mark -
#pragma mark ====== MDTrack functions ======
#endif
#ifdef __MWERKS__
#pragma mark ====== Block manipulation (private functions) ======
#endif
/* --------------------------------------
・ MDTrackAllocateBlock
-------------------------------------- */
static MDBlock *
MDTrackAllocateBlock(MDTrack *inTrack, MDBlock *inBlock, long inSize)
{
MDBlock *aBlock;
if (sFreeBlocks != NULL) {
/* MDBlock pool から持ってくる。size と events は設定済み */
aBlock = sFreeBlocks;
sFreeBlocks = sFreeBlocks->next;
} else {
/* ちょっとメモリをけちったやり方。 MDBlockRecord と buffer を同時に確保している */
aBlock = (MDBlock *)malloc(sizeof(*aBlock) + inSize * sizeof(aBlock->events[0]));
if (aBlock == NULL)
/* out of memory */
return NULL;
aBlock->size = inSize;
aBlock->events = (MDEvent *)(aBlock + 1);
}
aBlock->last = inBlock;
if (inBlock == NULL) {
/* top of list */
aBlock->next = inTrack->first;
inTrack->first = aBlock;
} else {
aBlock->next = inBlock->next;
inBlock->next = aBlock;
}
if (aBlock->next == NULL) {
/* bottom of list */
inTrack->last = aBlock;
} else {
aBlock->next->last = aBlock;
}
aBlock->num = 0;
aBlock->largestTick = kMDNegativeTick;
memset(aBlock->events, 0, aBlock->size * sizeof(aBlock->events[0]));
inTrack->numBlocks++;
return aBlock;
}
/* --------------------------------------
・ MDTrackDeallocateBlock
-------------------------------------- */
static void
MDTrackDeallocateBlock(MDTrack *inTrack, MDBlock *inBlock)
{
if (inBlock->last == NULL) {
inTrack->first = inBlock->next;
} else {
inBlock->last->next = inBlock->next;
}
if (inBlock->next == NULL) {
inTrack->last = inBlock->last;
} else {
inBlock->next->last = inBlock->last;
}
/* MDBlock pool に戻す */
inBlock->next = sFreeBlocks;
sFreeBlocks = inBlock;
inTrack->numBlocks--;
}
/* --------------------------------------
・ MDTrackClearBlock
-------------------------------------- */
static void
MDTrackClearBlock(MDTrack *inTrack, MDBlock *inBlock)
{
long i;
for (i = 0; i < inBlock->num; i++) {
/* パートナー、メッセージなどのポインタを処理して、メモリリーク・
ダングリングポインタが出ないようにする */
MDEventClear(&(inBlock->events[i]));
}
inBlock->num = 0;
MDTrackDeallocateBlock(inTrack, inBlock);
}
#ifdef __MWERKS__
#pragma mark ====== Basic Insert/Delete (private functions) ======
#endif
/* --------------------------------------
・ MDTrackInsertBlanks
-------------------------------------- */
static long
MDTrackInsertBlanks(MDTrack *inTrack, MDPointer *inPointer, long count)
{
MDBlock *block1, *block2;
long index, room, num2, tail;
MDPointer *ptr;
if (count <= 0)
return count;
block1 = inPointer->block;
index = inPointer->index;
if (inTrack->num == 0 || block1 == NULL) {
room = tail = 0;
} else {
/* room: the number of available space */
room = block1->size - block1->num;
/* tail: the number of events that needs to be moved (i.e. after index in block1) */
tail = block1->num - index;
}
if (room >= count) {
/* The current block have enough room for the required blanks */
MDEventMove(block1->events + index + count, block1->events + index, tail);
block1->num += count;
block1->largestTick = kMDNegativeTick;
} else {
/* Allocate new blocks until there are enough room */
block2 = block1;
while (room < count) {
block2 = MDTrackAllocateBlock(inTrack, block2, kMDBlockSize);
if (block2 == NULL) {
/* Out of memory: clean up the allocated blocks */
if (block1 != NULL) {
while (block1->next != NULL && block1->next->num == 0)
MDTrackDeallocateBlock(inTrack, block1->next);
}
return 0;
}
room += block2->size;
}
/* block2: the last allocated block */
/* num2: the number of events (possibly including blanks) in block2 */
num2 = block2->size - (room - count);
/* Move the events after index in block1 if necessary */
if (tail > 0) {
if (tail <= num2) {
MDEventMove(block2->events + num2 - tail, block1->events + index, tail);
} else {
/* block1->events[index..num-num2-1] ====> block2->last->events[size-(tail-num2)..size-1]
block1->events[num-num2..num-1] ====> block2->events[0..num2-1] */
MDEventMove(block2->events, block1->events + (block1->num - num2), num2);
MDEventMove(block2->last->events + block2->last->size - (tail - num2), block1->events + index, tail - num2);
}
}
/* update the num fields of modified blocks */
block2->num = num2; /* the last allocated block */
/* other blocks */
if (block1 == NULL)
block1 = inTrack->first;
while (block1 != block2) {
block1->num = block1->size;
block1 = block1->next;
}
}
inTrack->num += count;
if (inPointer->block == NULL) {
inPointer->block = inTrack->first;
inPointer->position = inPointer->index = 0;
} else if (inPointer->index == inPointer->block->size) {
/* The pointer was at the end of track, and the last block in the track
had maximum number of events. In this case, a new block must have been
allocated, so we move on to the next block and point to the first
event in that block. */
inPointer->block = inPointer->block->next;
inPointer->index = 0;
}
/* Update pointers */
/* (1) 0..inPointer->position-1 : no change
(2) inPointer->position : position not changed, block/index updated as in inPointer
(3) inPointer->position+1..inPointer->position+tail : position += count, block/index updated
(4) inPointer->position+tail+1.. : position += count, block/index not changed */
num2 = inPointer->position;
for (ptr = inTrack->pointer; ptr != NULL; ptr = ptr->next) {
if (ptr == inPointer || !ptr->autoAdjust)
continue;
index = ptr->position;
if (index > num2 + tail)
ptr->position += count; /* case 4 */
else if (index > num2) {
/* case 3 */
ptr->position = inPointer->position;
ptr->block = inPointer->block;
ptr->index = inPointer->index;
MDPointerSetRelativePosition(ptr, index - num2 + count);
} else if (index == num2) {
/* case 2 */
ptr->position = inPointer->position;
ptr->block = inPointer->block;
ptr->index = inPointer->index;
}
}
/* For debug */
for (ptr = inTrack->pointer; ptr != NULL; ptr = ptr->next) {
if (ptr == inPointer || ptr->autoAdjust)
MDPointerCheck(inPointer);
}
/* --------- */
return count;
}
/* --------------------------------------
・ MDTrackDeleteEvents
-------------------------------------- */
static long
MDTrackDeleteEvents(MDTrack *inTrack, MDPointer *inPointer, long count)
{
MDBlock *block, *block2;
long index, remain, i, n, tail;
MDPointer *ptr;
if (inTrack == NULL || inPointer == NULL || inPointer->parent != inTrack)
return 0;
if (count <= 0)
return count;
block = inPointer->block;
index = inPointer->index;
remain = count;
tail = 0;
while (remain > 0 && block != NULL) {
block->largestTick = kMDNegativeTick;
if (index + remain > block->num)
n = block->num - index;
else
n = remain;
for (i = 0; i < n; i++)
MDEventClear(block->events + index + i);
if (index + n < block->num) {
/* tail: the number of surviving events in the last modified block
(used later to modify pointers) */
tail = block->num - (index + n);
MDEventMove(block->events + index, block->events + index + n, tail);
}
block->num -= n;
remain -= n;
index = 0;
block = block->next;
}
/* Purge the empty blocks */
if (block == NULL)
block = inTrack->last;
while (block != NULL) {
int endFlag = 0;
if (block->num != 0) {
if (block == inPointer->block)
break;
block = block->last;
continue;
}
if (block == inPointer->block) {
/* inPointer is updated to point the next surviving event */
if (block->next != NULL) {
inPointer->block = block->next;
inPointer->index = 0;
} else {
inPointer->block = block->last;
if (block->last == NULL)
inPointer->index = 0;
else inPointer->index = block->last->num;
}
endFlag = 1;
}
block2 = block->last;
MDTrackDeallocateBlock(inTrack, block);
block = block2;
if (endFlag)
break;
}
inPointer->removed = 1;
inTrack->num -= count;
/* An ad-hoc sanity check */
if (inPointer->block != NULL && inPointer->block->num == inPointer->index && inPointer->block->next != NULL) {
inPointer->block = inPointer->block->next;
inPointer->index = 0;
}
/* Update pointers */
/* (1) 0..inPointer->position-1 : no change
(2) inPointer->position : position not changed, block/index updated as in inPointer
(3) inPointer->position+1..inPointer->position+count : position/block/index are made same as in inPointer
(4) inPointer->position+count+1..inPointer->position+count+tail : position -= count, block/index updated
(5) inPointer->position+count+tail+1 : position -= count, block/index not changed */
n = inPointer->position;
for (ptr = inTrack->pointer; ptr != NULL; ptr = ptr->next) {
if (inPointer == ptr || !ptr->autoAdjust)
continue;
index = ptr->position;
if (index > n + count + tail) /* case 5 */
ptr->position -= count;
else if (index >= n) {
ptr->position = inPointer->position;
ptr->block = inPointer->block;
ptr->index = inPointer->index;
if (index > n + count) {
/* case 4 */
MDPointerSetRelativePosition(ptr, index - (n + count));
} else ptr->removed = 1; /* case 3 and 2 */
} else { /* case 1 */ }
}
/* Another sanity check */
if (inPointer->parent->num == 0 && inPointer->position >= 0) {
inPointer->position = -1;
inPointer->index = -1;
inPointer->block = NULL;
}
/* For debug */
for (ptr = inTrack->pointer; ptr != NULL; ptr = ptr->next) {
if (ptr == inPointer || ptr->autoAdjust)
MDPointerCheck(inPointer);
}
/* --------- */
return count;
}
#ifdef __MWERKS__
#pragma mark ====== New/Retain/Release ======
#endif
/* --------------------------------------
・ MDTrackNew
-------------------------------------- */
MDTrack *
MDTrackNew(void)
{
MDTrack *newTrack = (MDTrack *)malloc(sizeof(*newTrack));
if (newTrack == NULL)
return NULL; /* out of memory */
memset(newTrack, 0, sizeof(*newTrack));
newTrack->refCount = 1;
newTrack->dev = -1;
return newTrack;
}
/* --------------------------------------
・ MDTrackRetain
-------------------------------------- */
void
MDTrackRetain(MDTrack *inTrack)
{
inTrack->refCount++;
}
/* --------------------------------------
・ MDTrackRelease
-------------------------------------- */
void
MDTrackRelease(MDTrack *inTrack)
{
if (--inTrack->refCount == 0) {
if (inTrack->num != 0)
MDTrackClear(inTrack);
/* Remove the MDPointer's from the linked list */
while (inTrack->pointer != NULL)
MDPointerSetTrack(inTrack->pointer, NULL);
free(inTrack);
}
}
/* --------------------------------------
・ MDTrackClear
-------------------------------------- */
void
MDTrackClear(MDTrack *inTrack)
{
MDPointer *pointer;
while (inTrack->first != NULL) {
MDTrackClearBlock(inTrack, inTrack->first);
}
inTrack->num = 0;
/* Reset the MDPointers */
for (pointer = inTrack->pointer; pointer != NULL; pointer = pointer->next) {
MDPointerSetPosition(pointer, -1);
}
}
/* --------------------------------------
・ MDTrackNewFromTrack
-------------------------------------- */
MDTrack *
MDTrackNewFromTrack(const MDTrack *inTrack)
{
MDPointer *src, *dest;
MDEvent *eventSrc, *eventDest;
long count, noteCount;
MDTrack *newTrack;
int i;
/* Allocate a new track */
newTrack = MDTrackNew();
if (newTrack == NULL)
return NULL;
/* Set up pointers */
dest = MDPointerNew(newTrack);
src = MDPointerNew((MDTrack *)inTrack);
if (src == NULL || dest == NULL)
return NULL;
MDPointerSetPosition(dest, 0);
/* Prepare the blank space */
count = MDTrackGetNumberOfEvents(inTrack);
if (MDTrackInsertBlanks(newTrack, dest, count) < count) {
MDTrackRelease(newTrack);
return NULL;
}
/* Copy the events */
noteCount = 0;
while ((eventDest = MDPointerForward(dest)) != NULL && (eventSrc = MDPointerForward(src)) != NULL) {
MDEventCopy(eventDest, eventSrc, 1);
}
MDPointerRelease(dest);
MDPointerRelease(src);
/* Copy random fields */
for (i = 0; i < 18; i++)
newTrack->nch[i] = inTrack->nch[i];
newTrack->dev = inTrack->dev;
newTrack->channel = inTrack->channel;
newTrack->duration = inTrack->duration;
if (inTrack->name != NULL)
newTrack->name = strdup(inTrack->name);
if (inTrack->devname != NULL)
newTrack->devname = strdup(inTrack->devname);
return newTrack;
}
/* Exchange the contents of two MDTracks. The tracks should not have any
"parents" such as MDSequence. Otherwise, the results are undefined. */
void
MDTrackExchange(MDTrack *inTrack1, MDTrack *inTrack2)
{
MDTrack tempTrack;
MDPointer *pointer;
/* Exchange the contents of the MDTrack struct */
tempTrack = *inTrack1;
*inTrack1 = *inTrack2;
*inTrack2 = tempTrack;
/* Update the MDPointers */
for (pointer = inTrack1->pointer; pointer != NULL; pointer = pointer->next)
pointer->parent = inTrack2;
for (pointer = inTrack2->pointer; pointer != NULL; pointer = pointer->next)
pointer->parent = inTrack1;
}
#ifdef __MWERKS__
#pragma mark ====== Accessor functions ======
#endif
/* --------------------------------------
・ MDTrackGetNumberOfEvents
-------------------------------------- */
long
MDTrackGetNumberOfEvents(const MDTrack *inTrack)
{
return inTrack->num;
}
/* --------------------------------------
・ MDTrackGetNumberOfChannelEvents
-------------------------------------- */
long
MDTrackGetNumberOfChannelEvents(const MDTrack *inTrack, short channel)
{
long n;
if (channel >= 0 && channel < 16)
return inTrack->nch[channel];
else {
n = 0;
for (channel = 0; channel < 16; channel++)
n += inTrack->nch[channel];
return n;
}
}
/* --------------------------------------
・ MDTrackGetNumberOfSysexEvents
-------------------------------------- */
long
MDTrackGetNumberOfSysexEvents(const MDTrack *inTrack)
{
return inTrack->nch[16];
}
/* --------------------------------------
・ MDTrackGetNumberOfNonMIDIEvents
-------------------------------------- */
long
MDTrackGetNumberOfNonMIDIEvents(const MDTrack *inTrack)
{
return inTrack->nch[17];
}
/* --------------------------------------
・ MDTrackGetDuration
-------------------------------------- */
MDTickType
MDTrackGetDuration(const MDTrack *inTrack)
{
return inTrack->duration;
}
/* --------------------------------------
・ MDTrackSetDuration
-------------------------------------- */
void
MDTrackSetDuration(MDTrack *inTrack, MDTickType inDuration)
{
inTrack->duration = inDuration;
}
#ifdef __MWERKS__
#pragma mark ====== Insert/Delete ======
#endif
/* --------------------------------------
・ MDTrackAppendEvents
-------------------------------------- */
long
MDTrackAppendEvents(MDTrack *inTrack, const MDEvent *inEvent, long count)
{
MDBlock *block;
long index, i, n, nn;
if (inTrack == NULL)
return 0;
/* Get the position of the last event */
block = inTrack->last;
if (block == NULL) {
block = MDTrackAllocateBlock(inTrack, NULL, kMDBlockSize);
if (block == NULL)
return 0;
index = 0;
} else index = block->num;
n = 0;
while (n < count) {
if (index >= block->size) {
block = MDTrackAllocateBlock(inTrack, block, kMDBlockSize);
if (block == NULL)
return n;
index = 0;
}
if (count > block->size - index)
nn = block->size - index;
else nn = count;
MDEventCopy(block->events + index, inEvent, nn);
for (i = 0; i < nn; i++) {
short ch = MDGetChannel(inEvent + i);
if (ch >= 0 && ch < 18)
inTrack->nch[ch]++;
}
block->num += nn;
block->largestTick = kMDNegativeTick;
index += nn;
inEvent += nn;
n += nn;
}
inTrack->num += n;
return n;
}
/* --------------------------------------
・ MDTrackMerge
-------------------------------------- */
MDStatus
MDTrackMerge(MDTrack *inTrack1, const MDTrack *inTrack2, MDPointSet **ioSet)
{
MDPointer *src1; /* The source position in inTrack1 */
MDPointer *src2; /* The source position in inTrack2 */
MDPointer *dest; /* The destination position */
MDEvent *eventSrc1, *eventSrc2, *eventDest;
long noteCount; /* The number of note-on's that have partners */
long destPosition;
long i;
MDTickType tick1, duration1, duration2;
MDPointSet *pset = NULL;
MDStatus result = kMDNoError;
MDBlock *block;
if (inTrack1 == NULL || inTrack2 == NULL || inTrack2->num == 0)
return kMDErrorNoEvents;
src1 = MDPointerNew(inTrack1);
dest = MDPointerNew(inTrack1);
if (src1 == NULL || dest == NULL)
return kMDErrorOutOfMemory;
src2 = MDPointerNew((MDTrack *)inTrack2);
if (src2 == NULL)
return kMDErrorOutOfMemory;
MDPointerSetPosition(src2, inTrack2->num - 1);
if (ioSet != NULL) {
pset = MDPointSetNew();
if (pset == NULL)
return kMDErrorOutOfMemory;
}
eventSrc2 = MDPointerCurrent(src2);
tick1 = MDGetTick(eventSrc2);
/* Prepare the blank space at tick = tick1 + 1 in inTrack1 */
MDPointerJumpToTick(dest, tick1 + 1);
if (inTrack1->num == 0)
i = 0;
else
i = MDPointerGetPosition(dest);
if (MDTrackInsertBlanks(inTrack1, dest, inTrack2->num) < inTrack2->num)
return kMDErrorOutOfMemory;
MDPointerSetPosition(dest, i - 1 + inTrack2->num);
MDPointerSetPosition(src1, i - 1);
/* i = inTrack1->num;
MDPointerSetPosition(dest, i);
if (MDTrackInsertBlanks(inTrack1, dest, inTrack2->num) < inTrack2->num)
return kMDErrorOutOfMemory;
MDPointerSetPosition(dest, inTrack1->num - 1);
MDPointerSetPosition(src1, i - 1); */
eventDest = MDPointerCurrent(dest);
eventSrc1 = MDPointerCurrent(src1);
noteCount = 0;
destPosition = MDPointerGetPosition(dest);
while (eventSrc2 != NULL) {
/* transfer the 'larger' event to dest */
unsigned char prefer_2_over_1 = 0;
/* MDTickType t1, t2; *//* for debug */
/* t1 = (eventSrc1 != NULL ? MDGetTick(eventSrc1) : kMDNegativeTick);
t2 = MDGetTick(eventSrc2); */
if (eventSrc1 != NULL) {
if (MDIsTickEqual(eventSrc1, eventSrc2)) {
if (ioSet != NULL && *ioSet != NULL) {
/* Consult *ioSet whether we should select eventSrc2 or not */
if (MDPointSetLookup(*ioSet, MDPointerGetPosition(dest), NULL))
prefer_2_over_1 = 1;
}
} else if (MDIsTickGreater(eventSrc2, eventSrc1)) {
prefer_2_over_1 = 1;
}
} else prefer_2_over_1 = 1;
if (prefer_2_over_1) {
MDEventCopy(eventDest, eventSrc2, 1);
/* fprintf(stderr, "MDTrackMerge: MDEventCopy %ld from %ld (t1=%ld, t2=%ld)\n", MDPointerGetPosition(dest), MDPointerGetPosition(src2), t1, t2); */
eventSrc2 = MDPointerBackward(src2);
if (pset != NULL) {
if (MDPointSetAdd(pset, destPosition, 1) != kMDNoError) {
result = kMDErrorOutOfMemory;
MDPointSetRelease(pset);
pset = NULL;
}
}
} else {
MDEventMove(eventDest, eventSrc1, 1);
/* fprintf(stderr, "MDTrackMerge: MDEventMove %ld from %ld (t1=%ld, t2=%ld)\n", MDPointerGetPosition(dest), MDPointerGetPosition(src1), t1, t2); */
eventSrc1 = MDPointerBackward(src1);
}
eventDest = MDPointerBackward(dest);
destPosition--;
}
for (i = 0; i < 18; i++) {
inTrack1->nch[i] += inTrack2->nch[i];
}
for (block = inTrack1->first; block != NULL; block = block->next)
block->largestTick = kMDNegativeTick;
MDPointerRelease(src2);
MDPointerRelease(src1);
MDPointerRelease(dest);
duration1 = inTrack1->duration;
duration2 = inTrack2->duration;
if (duration2 > duration1)
inTrack1->duration = duration2;
if (ioSet != NULL)
*ioSet = pset;
else if (pset != NULL)
MDPointSetRelease(pset);
/* MDTrackCheck(inTrack1);
MDTrackCheck(inTrack2); */
return result;
}
/* --------------------------------------
・ MDTrackUnmerge
-------------------------------------- */
static MDStatus
sMDTrackUnmergeSub(MDTrack *inTrack, MDTrack **outTrack, const MDPointSet *inSet, int deleteFlag)
{
MDPointer *src;
MDPointer *dest;
MDEvent *eventSrc, *eventDest;
long ptCount; /* The number of points in inSet */
long noteCount; /* The number of note-on's that have partners */
long destPosition;
long index, start, length;
int i;
MDTickType duration;
MDTrack *newTrack;
MDBlock *block;
if (inTrack == NULL || inSet == NULL || (ptCount = MDPointSetGetCount(inSet)) == 0)
return kMDErrorNoEvents;
/* Allocate a destination track */
newTrack = MDTrackNew();
if (newTrack == NULL)
return kMDErrorOutOfMemory;
src = MDPointerNew(inTrack);
dest = MDPointerNew(newTrack);
if (src == NULL || dest == NULL)
return kMDErrorOutOfMemory;
/* Prepare the blank space */
MDPointerSetPosition(dest, 0);
if (MDTrackInsertBlanks(newTrack, dest, ptCount) < ptCount)
return kMDErrorOutOfMemory;
MDPointerSetPosition(dest, 0);
destPosition = 0;
eventDest = MDPointerCurrent(dest);
duration = 0;
noteCount = 0;
/* Copy the events */
for (index = 0; index < MDPointSetGetIntervalCount(inSet); index++) {
start = MDPointSetGetStartPoint(inSet, index);
length = MDPointSetGetInterval(inSet, index);
MDPointerSetPosition(src, start);
eventSrc = MDPointerCurrent(src);
while (eventDest != NULL && eventSrc != NULL && --length >= 0) {
MDEventCopy(eventDest, eventSrc, 1);
/* Count the event kind */
if (MDIsChannelEvent(eventDest))
newTrack->nch[MDGetChannel(eventDest)]++;
else if (MDIsSysexEvent(eventDest))
newTrack->nch[16]++;
else
newTrack->nch[17]++;
/* Estimated duration */
if (MDGetKind(eventDest) == kMDEventNote)
duration = MDGetTick(eventDest) + MDGetDuration(eventDest) + 1;
else
duration = MDGetTick(eventDest) + 1;
eventSrc = MDPointerForward(src);
eventDest = MDPointerForward(dest);
destPosition++;
}
if (eventDest == NULL)
break;
}
/* Check if specified number of events have been copied */
if (destPosition < ptCount) {
MDTrackDeleteEvents(newTrack, dest, ptCount - destPosition);
ptCount = destPosition;
}
if (deleteFlag) {
/* Delete the events from the source track */
for (index = MDPointSetGetIntervalCount(inSet) - 1; index >= 0; index--) {
start = MDPointSetGetStartPoint(inSet, index);
length = MDPointSetGetInterval(inSet, index);
if (start < inTrack->num) {
MDPointerSetPosition(src, start);
if (start + length > inTrack->num)
length = inTrack->num - start;
if (length > 0)
MDTrackDeleteEvents(inTrack, src, length);
}
}
for (i = 0; i < 18; i++) {
inTrack->nch[i] -= newTrack->nch[i];
}
for (block = inTrack->first; block != NULL; block = block->next)
block->largestTick = kMDNegativeTick;
}
MDPointerRelease(src);
MDPointerRelease(dest);
newTrack->duration = duration;
/* MDTrackCheck(inTrack);
MDTrackCheck(newTrack); */
if (outTrack != NULL)
*outTrack = newTrack;
else if (newTrack != NULL)
MDTrackRelease(newTrack);
return kMDNoError;
}
MDStatus
MDTrackUnmerge(MDTrack *inTrack, MDTrack **outTrack, const MDPointSet *inSet)
{
return sMDTrackUnmergeSub(inTrack, outTrack, inSet, 1);
}
MDStatus
MDTrackExtract(MDTrack *inTrack, MDTrack **outTrack, const MDPointSet *inSet)
{
return sMDTrackUnmergeSub(inTrack, outTrack, inSet, 0);
}
/* --------------------------------------
・ MDTrackSplitByMIDIChannel
-------------------------------------- */
int
MDTrackSplitByMIDIChannel(MDTrack *inTrack, MDTrack **outTracks)
{
long count[16];
int i, n, nn;
MDPointer *pt;
MDEvent *ep;
MDPointSet *pset;
pt = MDPointerNew(inTrack);
if (pt == NULL)
return 0;
for (i = 0; i < 16; i++) {
count[i] = 0;
outTracks[i] = NULL;
}
while ((ep = MDPointerForward(pt)) != NULL) {
if (MDIsChannelEvent(ep))
count[MDGetChannel(ep)]++;
}
for (i = n = 0; i < 16; i++) {
if (count[i] > 0) {
n++;
nn = i;
}
}
if (n == 0) {
/* No channel events */
outTracks[0] = inTrack;
return 1;
}
if (n == 1) {
/* No need to split */
outTracks[nn] = inTrack;
return 1;
}
nn = n;
pset = MDPointSetNew();
if (pset == NULL) {
MDPointerRelease(pt);
return 0;
}
/* Split by channel */
for (i = 15; i >= 0; i--) {
if (count[i] == 0)
continue;
if (--n == 0) {
/* The last one */
outTracks[i] = inTrack;
break;
}
MDPointSetClear(pset);
MDPointerSetPosition(pt, -1);
while ((ep = MDPointerForward(pt)) != NULL) {
if (MDIsChannelEvent(ep) && MDGetChannel(ep) == i)
MDPointSetAdd(pset, MDPointerGetPosition(pt), 1);
}
if (MDTrackUnmerge(inTrack, &(outTracks[i]), pset) != kMDNoError) {
MDPointerRelease(pt);
MDPointSetRelease(pset);
return 0;
}
}
MDPointerRelease(pt);
MDPointSetRelease(pset);
return nn;
}
/* --------------------------------------
・ MDTrackMatchNoteOff
-------------------------------------- */
MDStatus
MDTrackMatchNoteOff(MDTrack *inTrack, const MDEvent *noteOffEvent)
{
MDBlock *bp;
int index;
unsigned char code = MDGetCode(noteOffEvent);
int channel = MDGetChannel(noteOffEvent);
MDTickType tick = MDGetTick(noteOffEvent);
/* Do not use MDPointer, but use internal block info directly (for efficiency) */
for (bp = inTrack->last; bp != NULL; bp = bp->last) {
MDEvent *ep;
for (index = bp->num - 1, ep = &(bp->events[index]); index >= 0; index--, ep--) {
if (MDGetKind(ep) == kMDEventInternalNoteOn && MDGetCode(ep) == code && MDGetChannel(ep) == channel) {
MDTickType duration = MDGetDuration(ep);
if (duration == 0 || duration == tick - MDGetTick(ep)) {
/* Found */
MDSetKind(ep, kMDEventNote);
MDSetDuration(ep, tick - MDGetTick(ep));
MDSetNoteOffVelocity(ep, MDGetNoteOffVelocity(noteOffEvent));
bp->largestTick = kMDNegativeTick;
return kMDNoError;
}
}
}
}
return kMDErrorOrphanedNoteOff;
}
/* --------------------------------------
・ MDTrackMatchNoteOffInTrack
-------------------------------------- */
MDStatus
MDTrackMatchNoteOffInTrack(MDTrack *inTrack, MDTrack *noteOffTrack)
{
/* Pair note-on with the corresponding note-off */
MDPointer *noteon, *noteoff;
MDEvent *eref1, *eref2;
MDBlock *block;
MDTickType largestTick = kMDNegativeTick;
noteon = MDPointerNew(inTrack);
noteoff = MDPointerNew(noteOffTrack);
if (noteon == NULL || noteoff == NULL)
return kMDErrorOutOfMemory;
while ((eref1 = MDPointerForward(noteon)) != NULL) {
if (MDGetKind(eref1) == kMDEventInternalNoteOn) {
MDPointerJumpToTick(noteoff, MDGetTick(eref1));
MDPointerBackward(noteoff);
while ((eref2 = MDPointerForward(noteoff)) != NULL) {
if (MDGetKind(eref2) == kMDEventInternalNoteOff
&& MDGetCode(eref1) == MDGetCode(eref2)
&& MDGetChannel(eref1) == MDGetChannel(eref2)) {
MDTickType tick2 = MDGetTick(eref2);
MDSetDuration(eref1, tick2 - MDGetTick(eref1));
MDSetNoteOffVelocity(eref1, MDGetNoteOffVelocity(eref2));
MDSetKind(eref2, kMDEventNull);
MDSetKind(eref1, kMDEventNote);
dprintf(2, "Paired note-event: tick %ld code %d vel %d/%d duration %ld\n", MDGetTick(eref1), MDGetCode(eref1), MDGetNoteOnVelocity(eref1), MDGetNoteOffVelocity(eref1), MDGetDuration(eref1));
if (tick2 > largestTick)
largestTick = tick2;
break;
}
}
}
}
MDPointerRelease(noteon);
MDPointerRelease(noteoff);
for (block = inTrack->first; block != NULL; block = block->next)
block->largestTick = kMDNegativeTick;
if (largestTick > MDTrackGetDuration(inTrack))
MDTrackSetDuration(inTrack, largestTick);
return kMDNoError;
}
static int
sMDTrackEventComparator(const void *a, const void *b)
{
MDTickType ticka, tickb;
ticka = MDGetTick((const MDEvent *)a);
tickb = MDGetTick((const MDEvent *)b);
if (ticka < tickb)
return -1;
else if (ticka > tickb)
return 1;
else return 0;
}
/* --------------------------------------
・ MDTrackChangeTick
-------------------------------------- */
MDStatus
MDTrackChangeTick(MDTrack *inTrack, MDTickType *newTick)
{
MDBlock *block;
/* int index, i; */
long n, count;
MDTickType largestTick;
/* MDTickType oldTick, tick; */
MDEvent *tempEvents;
#if 1
/* Move all events to a temporary array of MDEvent */
count = MDTrackGetNumberOfEvents(inTrack);
tempEvents = (MDEvent *)malloc(sizeof(MDEvent) * count);
if (tempEvents == NULL)
return kMDErrorOutOfMemory;
n = 0;
for (block = inTrack->first; block != NULL; block = block->next) {
MDEventMove(tempEvents + n, block->events, block->num);
n += block->num;
}
/* Modify tick */
for (n = 0; n < count; n++)
MDSetTick(&tempEvents[n], newTick[n]);
/* Sort event index by the new tick */
qsort(tempEvents, count, sizeof(MDEvent), sMDTrackEventComparator);
/* Move the events back */
n = 0;
for (block = inTrack->first; block != NULL; block = block->next) {
MDEventMove(block->events, tempEvents + n, block->num);
block->largestTick = kMDNegativeTick;
n += block->num;
}
free(tempEvents);
#else
/* Pass 1: Check the new tick order first */
oldTick = kMDNegativeTick;
index = 0;
for (block = inTrack->first; block != NULL; block = block->next) {
for (i = 0; i < block->num; i++) {
if (newTick[index] >= 0)
tick = newTick[index];
else
tick = MDGetTick(&block->events[i]);
if (oldTick > tick)
return kMDErrorTickDisorder;
oldTick = tick;
index++;
}
}
/* Pass 2: Change the tick */
index = 0;
for (block = inTrack->first; block != NULL; block = block->next) {
for (i = 0; i < block->num; i++) {
if (newTick[index] >= 0)
MDSetTick(&block->events[i], newTick[index]);
index++;
}
block->largestTick = kMDNegativeTick;
}
#endif
largestTick = MDTrackGetLargestTick(inTrack);
if (largestTick >= inTrack->duration)
inTrack->duration = largestTick + 1;
return kMDNoError;
}
/* --------------------------------------
・ MDTrackOffsetTick
-------------------------------------- */
MDStatus
MDTrackOffsetTick(MDTrack *inTrack, MDTickType offset)
{
MDBlock *block;
int i;
MDTickType tick;
for (block = inTrack->first; block != NULL; block = block->next) {
if (block->largestTick >= 0)
block->largestTick += offset;
for (i = 0; i < block->num; i++) {
MDEvent *ep = &block->events[i];
tick = MDGetTick(ep) + offset;
if (tick < 0) {
tick = 0;
block->largestTick = kMDNegativeTick;
}
MDSetTick(ep, tick);
}
}
inTrack->duration += offset;
tick = MDTrackGetLargestTick(inTrack);
if (tick >= inTrack->duration)
inTrack->duration = tick + 1;
return kMDNoError;
}
#ifdef __MWERKS__
#pragma mark ====== Duration search ======
#endif
/* --------------------------------------
・ MDTrackGetLargestTick
-------------------------------------- */
MDTickType
MDTrackGetLargestTick(MDTrack *inTrack)
{
long i;
MDTickType tick, largestTick, globalLargestTick;
MDBlock *block;
MDEvent *ep;
globalLargestTick = kMDNegativeTick;
for (block = inTrack->first; block != NULL; block = block->next) {
if (block->largestTick < 0) {
/* Recalc the largest tick and cache it */
largestTick = kMDNegativeTick;
for (i = 0; i < block->num; i++) {
ep = &block->events[i];
tick = MDGetTick(ep);
if (MDHasDuration(ep))
tick += MDGetDuration(ep);
if (tick > largestTick)
largestTick = tick;
}
block->largestTick = largestTick;
}
if (block->largestTick > globalLargestTick)
globalLargestTick = block->largestTick;
}
return globalLargestTick;
}
/* --------------------------------------
・ MDTrackSearchEventsWithDurationCrossingTick
-------------------------------------- */
MDPointSet *
MDTrackSearchEventsWithDurationCrossingTick(MDTrack *inTrack, MDTickType inTick)
{
MDPointSet *pset;
long position, i;
MDTickType tick, largestTick;
MDBlock *block;
MDEvent *ep;
pset = MDPointSetNew();
if (pset == NULL)
return NULL;
position = 0;
for (block = inTrack->first; block != NULL; block = block->next) {
if (block->largestTick < 0 || block->largestTick >= inTick) {
largestTick = kMDNegativeTick;
for (i = 0; i < block->num; i++) {
ep = &block->events[i];
tick = MDGetTick(ep);
if (tick >= inTick)
goto exit;
if (MDHasDuration(ep)) {
tick += MDGetDuration(ep);
if (tick >= inTick) {
if (MDPointSetAdd(pset, position + i, 1) != kMDNoError) {
MDPointSetRelease(pset);
return NULL;
}
}
}
if (tick > largestTick)
largestTick = tick;
}
block->largestTick = largestTick;
}
position += block->num;
}
exit:
return pset;
}
/* --------------------------------------
・ MDTrackSearchEventsWithSelector
-------------------------------------- */
MDPointSet *
MDTrackSearchEventsWithSelector(MDTrack *inTrack, MDEventSelector inSelector, void *inUserData)
{
MDPointSet *pset;
MDPointer *pt;
MDEvent *ep;
pset = MDPointSetNew();
pt = MDPointerNew(inTrack);
if (pset == NULL || pt == NULL)
return NULL;
while ((ep = MDPointerForwardWithSelector(pt, inSelector, inUserData)) != NULL) {
if (MDPointSetAdd(pset, MDPointerGetPosition(pt), 1) != kMDNoError) {
MDPointerRelease(pt);
MDPointSetRelease(pset);
return NULL;
}
}
MDPointerRelease(pt);
return pset;
}
#ifdef __MWERKS__
#pragma mark ====== Track/device/channel manipulations ======
#endif
/* --------------------------------------
・ MDTrackRemapChannel
-------------------------------------- */
void
MDTrackRemapChannel(MDTrack *inTrack, const unsigned char *newch)
{
long nnch[16];
long n;
MDBlock *block;
if (inTrack == NULL)
return;
for (n = 0; n < 16; n++)
nnch[n] = 0;
for (block = inTrack->first; block != NULL; block = block->next) {
MDEvent *ep = block->events;
for (n = 0; n < block->num; n++, ep++) {
if (MDIsChannelEvent(ep)) {
unsigned char ch;
ch = (newch[MDGetChannel(ep) & 15]) & 15;
MDSetChannel(ep, ch);
nnch[ch]++;
}
}
}
for (n = 0; n < 16; n++)
inTrack->nch[n] = nnch[n];
}
/* --------------------------------------
・ MDTrackSetDevice
-------------------------------------- */
void
MDTrackSetDevice(MDTrack *inTrack, long dev)
{
if (inTrack != NULL)
inTrack->dev = dev;
}
/* --------------------------------------
・ MDTrackGetDevice
-------------------------------------- */
long
MDTrackGetDevice(const MDTrack *inTrack)
{
if (inTrack != NULL)
return inTrack->dev;
else return -1;
}
/* --------------------------------------
・ MDTrackSetTrackChannel
-------------------------------------- */
void
MDTrackSetTrackChannel(MDTrack *inTrack, short ch)
{
if (inTrack != NULL)
inTrack->channel = ch;
}
/* --------------------------------------
・ MDTrackGetTrackChannel
-------------------------------------- */
short
MDTrackGetTrackChannel(const MDTrack *inTrack)
{
if (inTrack != NULL)
return inTrack->channel;
else return -1;
}
/* --------------------------------------
・ MDTrackSetName
-------------------------------------- */
MDStatus
MDTrackSetName(MDTrack *inTrack, const char *inName)
{
char *p;
if (inName == NULL)
p = NULL;
else {
p = (char *)malloc(strlen(inName) + 1);
if (p == NULL)
return kMDErrorOutOfMemory;
strcpy(p, inName);
}
if (inTrack->name != NULL)
free(inTrack->name);
inTrack->name = p;
return kMDNoError;
}
/* --------------------------------------
・ MDTrackGetName
-------------------------------------- */
void
MDTrackGetName(const MDTrack *inTrack, char *outName, long length)
{
if (inTrack->name == NULL) {
outName[0] = 0;
} else {
strncpy(outName, inTrack->name, length - 1);
outName[length - 1] = 0;
}
}
/* --------------------------------------
・ MDTrackSetDeviceName
-------------------------------------- */
MDStatus
MDTrackSetDeviceName(MDTrack *inTrack, const char *inName)
{
char *p;
if (inName == NULL)
p = NULL;
else {
p = (char *)malloc(strlen(inName) + 1);
if (p == NULL)
return kMDErrorOutOfMemory;
strcpy(p, inName);
}
if (inTrack->devname != NULL)
free(inTrack->devname);
inTrack->devname = p;
return kMDNoError;
}
/* --------------------------------------
・ MDTrackGetDeviceName
-------------------------------------- */
void
MDTrackGetDeviceName(const MDTrack *inTrack, char *outName, long length)
{
if (inTrack->devname == NULL) {
outName[0] = 0;
} else {
strncpy(outName, inTrack->devname, length - 1);
outName[length - 1] = 0;
}
}
/* --------------------------------------
・ MDTrackGuessName
-------------------------------------- */
void
MDTrackGuessName(MDTrack *inTrack, char *outName, long length)
{
MDPointer *ptr;
MDEvent *eref, *stopref;
long len;
ptr = MDPointerNew(inTrack);
/* Pass 1: find the track name meta event */
while ((eref = MDPointerForward(ptr)) != NULL) {
if (MDIsTextMetaEvent(eref) && MDGetCode(eref) == kMDMetaSequenceName)
break;
if (MDIsChannelEvent(eref) || MDIsSysexEvent(eref)) {
stopref = eref;
eref = NULL;
break; /* not found */
}
}
if (eref == NULL) {
/* Pass 2: find the first text meta event */
MDPointerSetPosition(ptr, -1);
while ((eref = MDPointerForward(ptr)) != NULL) {
if (MDIsTextMetaEvent(eref) && MDGetCode(eref) == kMDMetaText)
break;
if (eref == stopref)
break; /* not found */
}
}
if (eref != NULL && eref != stopref) {
len = MDGetMessagePartial(eref, (unsigned char *)outName, 0, length - 1);
outName[len] = 0;
} else {
outName[0] = 0;
}
MDPointerRelease(ptr);
}
/* --------------------------------------
・ MDTrackGuessDeviceName
-------------------------------------- */
void
MDTrackGuessDeviceName(MDTrack *inTrack, char *outName, long length)
{
MDPointer *ptr;
MDEvent *eref, *stopref;
char name[256];
char c;
char *p;
int n, port;
long len;
ptr = MDPointerNew(inTrack);
port = -1;
/* Pass 1: find the device name meta event */
while ((eref = MDPointerForward(ptr)) != NULL) {
if (MDIsTextMetaEvent(eref) && MDGetCode(eref) == kMDMetaDeviceName)
break;
if (MDIsChannelEvent(eref) || MDIsSysexEvent(eref)) {
stopref = eref;
eref = NULL;
break; /* not found */
}
}
if (eref == NULL) {
/* Pass 2: find the instrument name meta event */
MDPointerSetPosition(ptr, -1);
while ((eref = MDPointerForward(ptr)) != NULL) {
if (MDIsTextMetaEvent(eref) && MDGetCode(eref) == kMDMetaInstrumentName)
break;
if (eref == stopref)
break; /* not found */
}
}
if (eref == NULL || eref == stopref) {
/* Pass 3: find the port number meta event */
MDPointerSetPosition(ptr, -1);
while ((eref = MDPointerForward(ptr)) != NULL) {
if (MDGetKind(eref) == kMDEventPortNumber) {
port = MDGetData1(eref);
break;
}
if (eref == stopref)
break; /* not found */
}
}
if (eref == NULL || eref == stopref) {
/* Pass 4: guess from the track name */
MDTrackGuessName(inTrack, name, 250);
if (sscanf(name, "[%c%d]", &c, &n) == 2
|| sscanf(name, "(%c%d)", &c, &n) == 2
|| sscanf(name, "<%c%d>", &c, &n) == 2
|| sscanf(name, "{%c%d}", &c, &n) == 2
|| sscanf(name, "%c%d:", &c, &n) == 2) {
if (isalpha(c))
port = toupper(c) - 'A';
} else {
for (n = 0, p = name; name[n] != 0; n++) {
/* Purge non-alphanumeric characters */
if (isalnum(name[n]))
*p++ = toupper(name[n]);
}
*p = 0;
if (strncmp(name, "PART", 4) == 0 || strncmp(name, "PORT", 4) == 0) {
if (isalpha(name[4]))
port = name[4] - 'A';
else if (isdigit(name[4]))
port = atoi(name + 4);
} else port = 0;
}
}
name[0] = 0;
if (port >= 0) {
sprintf(name, "(Device %d)", (int)(port + 1));
} else if (eref != NULL && eref != stopref) {
len = MDGetMessagePartial(eref, (unsigned char *)name, 0, 255);
name[len] = 0;
}
if (name[0] == 0) {
/* No clue */
strcpy(name, "(Device 1)");
}
strncpy(outName, name, length - 1);
outName[length - 1] = 0;
MDPointerRelease(ptr);
}
#ifdef __MWERKS__
#pragma mark ====== Attribute manipulations ======
#endif
MDTrackAttribute
MDTrackGetAttribute(const MDTrack *inTrack)
{
return inTrack->attribute;
}
void
MDTrackSetAttribute(MDTrack *inTrack, MDTrackAttribute inAttribute)
{
inTrack->attribute = inAttribute;
}
#ifdef __MWERKS__
#pragma mark ====== Debugging functions ======
#endif
void
MDTrackDump(const MDTrack *inTrack)
{
MDPointer *pt;
MDEvent *ev;
char buf[256];
FILE *fp = fopen("Alchemusica.dump", "w");
if (fp == NULL)
fp = stdout;
pt = MDPointerNew((MDTrack *)inTrack);
while ((ev = MDPointerForward(pt)) != NULL) {
fprintf(fp, "%12ld ", (long)MDGetTick(ev));
MDEventToKindString(ev, buf, sizeof buf);
fprintf(fp, "%s ", buf);
fprintf(fp, "%d %d %d %ld ", (int)MDGetCode(ev), (int)MDGetChannel(ev), (int)MDGetData1(ev), (long)MDGetDuration(ev));
fprintf(fp, "(@%p)\n", ev);
}
if (fp != NULL)
fclose(fp);
}
MDStatus
MDTrackRecache(MDTrack *inTrack, int check)
{
MDPointer *pt1, *pt2;
MDEvent *ev1;
long nch[18];
long i, pos;
MDTickType tick, lastTick;
MDBlock *block;
pt1 = MDPointerNew((MDTrack *)inTrack);
pt2 = MDPointerNew((MDTrack *)inTrack);
if (pt1 == NULL || pt2 == NULL)
return kMDErrorOutOfMemory;
lastTick = 0;
for (i = 0; i < 18; i++)
nch[i] = 0;
pos = -1;
while ((ev1 = MDPointerForward(pt1)) != NULL) {
pos = MDPointerGetPosition(pt1);
if (check && (MDGetKind(ev1) < 1 || MDGetKind(ev1) > kMDEventStop))
fprintf(stderr, "#%ld: invalid event kind %d\n", pos, (int)MDGetKind(ev1));
tick = MDGetTick(ev1);
if (check && tick < lastTick)
fprintf(stderr, "#%ld: tick disorder %qd (last tick = %qd)\n", pos, (long long)tick, (long long)lastTick);
lastTick = tick;
if (MDIsChannelEvent(ev1)) {
if (check && (unsigned)(MDGetChannel(ev1)) >= 16)
fprintf(stderr, "#%ld: channel number (%ud) >= 16\n", pos, (unsigned)MDGetChannel(ev1));
else
nch[MDGetChannel(ev1)]++;
} else if (MDIsSysexEvent(ev1)) {
nch[16]++;
} else nch[17]++;
}
++pos;
if (check && pos != inTrack->num)
fprintf(stderr, "The track->num (%ld) does not match the number of events (%ld)\n", pos, inTrack->num);
inTrack->num = pos;
if (check && lastTick >= inTrack->duration)
fprintf(stderr, "The tick of the last event (%qd) exceeds the track duration (%qd)\n",
(long long)lastTick, (long long)inTrack->duration);
for (i = 0; i < 18; i++) {
if (check && nch[i] != inTrack->nch[i]) {
fprintf(stderr, "The track->nch[%d] (%ld) does not seem correct (%ld)\n", (int)i, inTrack->nch[i], nch[i]);
}
inTrack->nch[i] = nch[i];
}
lastTick = kMDNegativeTick;
for (block = inTrack->first; block != NULL; block = block->next) {
tick = kMDNegativeTick;
for (i = 0; i < block->num; i++) {
MDTickType tick2;
ev1 = &block->events[i];
tick2 = MDGetTick(ev1);
if (MDHasDuration(ev1))
tick2 += MDGetDuration(ev1);
if (tick2 > tick)
tick = tick2;
}
if (check && tick != block->largestTick) {
fprintf(stderr, "The largestTick(%qd) does not match the largest tick(%qd) in block %p\n", (long long)block->largestTick, (long long)tick, block);
}
block->largestTick = tick;
if (tick > lastTick)
lastTick = tick;
}
if (lastTick >= inTrack->duration) {
if (check)
fprintf(stderr, "The track duration (%qd) is not greater than the largest tick (%qd)\n", (long long)inTrack->duration, (long long)lastTick);
inTrack->duration = lastTick + 1;
}
MDPointerRelease(pt1);
MDPointerRelease(pt2);
return kMDNoError;
}
#ifdef __MWERKS__
#pragma mark ====== MDPointer manipulations (private functions) ======
#endif
static void
MDTrackAttachPointer(const MDTrack *inTrack, MDPointer *inPointer)
{
if (inTrack == NULL || inPointer == NULL)
return;
inPointer->next = inTrack->pointer;
/* pointer is a 'mutable' member, so this cast is acceptable */
((MDTrack *)inTrack)->pointer = inPointer;
}
static void
MDTrackDetachPointer(const MDTrack *inTrack, MDPointer *inPointer)
{
MDPointer *currRef, *prevRef;
if (inTrack == NULL || inPointer == NULL)
return;
currRef = inTrack->pointer;
prevRef = NULL;
while (currRef != NULL) {
if (currRef == inPointer) {
if (prevRef == NULL) {
/* pointer is a 'mutable' member, so this cast is acceptable */
((MDTrack *)inTrack)->pointer = currRef->next;
} else {
prevRef->next = currRef->next;
}
break;
}
prevRef = currRef;
currRef = currRef->next;
}
}
#ifdef __MWERKS__
#pragma mark -
#pragma mark ====== MDPointer functions ======
#endif
/* --------------------------------------
・ MDPointerNew
-------------------------------------- */
MDPointer *
MDPointerNew(MDTrack *inTrack)
{
MDPointer *theRef = (MDPointer *)malloc(sizeof(MDPointer));
if (theRef == NULL)
return NULL; /* out of memory */
theRef->refCount = 1;
theRef->parent = NULL;
theRef->next = NULL;
theRef->block = NULL;
theRef->position = -1;
theRef->index = 0;
theRef->removed = 0;
theRef->autoAdjust = 0;
/* theRef->allocated = 1; */
if (inTrack != NULL)
MDPointerSetTrack(theRef, inTrack);
return theRef;
}
/* --------------------------------------
・ MDPointerRetain
-------------------------------------- */
void
MDPointerRetain(MDPointer *inPointer)
{
inPointer->refCount++;
}
/* --------------------------------------
・ MDPointerRelease
-------------------------------------- */
void
MDPointerRelease(MDPointer *inPointer)
{
if (inPointer == NULL)
return;
if (--inPointer->refCount == 0) {
MDPointerSetTrack(inPointer, NULL);
free(inPointer);
}
}
/* --------------------------------------
・ MDPointerCopy
-------------------------------------- */
void
MDPointerCopy(MDPointer *inDest, const MDPointer *inSrc)
{
if (inDest->parent == inSrc->parent) {
inDest->block = inSrc->block;
inDest->position = inSrc->position;
inDest->index = inSrc->index;
inDest->removed = inSrc->removed;
} else {
MDPointerSetPosition(inDest, MDPointerGetPosition(inSrc));
}
}
/* --------------------------------------
・ MDPointerSetTrack
-------------------------------------- */
void
MDPointerSetTrack(MDPointer *inPointer, MDTrack *inTrack)
{
if (inPointer->parent != inTrack) {
if (inPointer->parent != NULL)
MDTrackDetachPointer(inPointer->parent, inPointer);
inPointer->parent = inTrack;
if (inTrack != NULL)
MDTrackAttachPointer(inTrack, inPointer);
inPointer->position = -1;
if (inTrack != NULL)
inPointer->block = inTrack->first;
else inPointer->block = NULL;
inPointer->index = -1;
inPointer->removed = 0;
}
}
/* --------------------------------------
・ MDPointerGetTrack
-------------------------------------- */
MDTrack *
MDPointerGetTrack(const MDPointer *inPointer)
{
return inPointer->parent;
}
/* --------------------------------------
・ MDPointerUpdateBlock
-------------------------------------- */
static int
MDPointerUpdateBlock(MDPointer *inPointer)
{
long num;
long position;
num = inPointer->parent->num;
position = inPointer->position;
if (num == 0) {
inPointer->block = NULL;
inPointer->index = inPointer->position = -1;
return 0;
} else if (position < num / 2) {
/* Search the position starting from the first block */
inPointer->block = inPointer->parent->first;
inPointer->index = 0;
if (position < 0) {
inPointer->index = inPointer->position = -1;
return 0;
} else {
while (position >= inPointer->block->num) {
position -= inPointer->block->num;
inPointer->block = inPointer->block->next;
}
inPointer->index = position;
return 1;
}
} else {
/* Search the position starting from the last block */
inPointer->block = inPointer->parent->last;
inPointer->index = inPointer->block->num;
if (position >= num) {
inPointer->position = num;
return 0;
} else {
long offset = num - position;
while (offset > inPointer->block->num) {
offset -= inPointer->block->num;
inPointer->block = inPointer->block->last;
}
inPointer->index = inPointer->block->num - offset;
return 1;
}
}
}
/* --------------------------------------
・ MDPointerSetPosition
-------------------------------------- */
int
MDPointerSetPosition(MDPointer *inPointer, long inPos)
{
if (inPointer->parent == NULL)
return 0; /* always false */
inPointer->position = inPos;
inPointer->removed = 0;
return MDPointerUpdateBlock(inPointer);
}
/* --------------------------------------
・ MDPointerSetRelativePosition
-------------------------------------- */
int
MDPointerSetRelativePosition(MDPointer *inPointer, long inOffset)
{
long num;
if (inPointer->parent == NULL)
return 0; /* always false */
num = inPointer->parent->num;
if (inOffset == 0 || num == 0)
return (inPointer->position >= 0 && inPointer->position < num); /* do nothing */
inPointer->removed = 0;
if (inPointer->position + inOffset < 0) {
inPointer->block = inPointer->parent->first;
inPointer->index = inPointer->position = -1;
return 0;
} else if (inPointer->position + inOffset >= num) {
inPointer->block = inPointer->parent->last;
inPointer->index = inPointer->block->num;
inPointer->position = num;
return 0;
} else {
inPointer->position += inOffset;
/* Move temporarily to the top of the current block */
inOffset += inPointer->index;
inPointer->index = 0;
if (inOffset > 0) {
/* Search forward */
while (inOffset >= inPointer->block->num) {
inOffset -= inPointer->block->num;
inPointer->block = inPointer->block->next;
}
inPointer->index = inOffset;
} else if (inOffset < 0) {
/* Search backward */
do {
inPointer->block = inPointer->block->last;
inOffset += inPointer->block->num;
} while (inOffset < 0);
inPointer->index = inOffset;
}
return 1;
}
}
/* --------------------------------------
・ MDPointerGetPosition
-------------------------------------- */
long
MDPointerGetPosition(const MDPointer *inPointer)
{
return inPointer->position;
}
/* --------------------------------------
・ MDPointerSetAutoAdjust
-------------------------------------- */
void
MDPointerSetAutoAdjust(MDPointer *inPointer, char flag)
{
inPointer->autoAdjust = (flag != 0);
}
/* --------------------------------------
・ MDPointerIsAutoAdjust
-------------------------------------- */
int
MDPointerIsAutoAdjust(const MDPointer *inPointer)
{
return inPointer->autoAdjust;
}
/* --------------------------------------
・ MDPointerIsRemoved
-------------------------------------- */
int
MDPointerIsRemoved(const MDPointer *inPointer)
{
return inPointer->removed;
}
/* --------------------------------------
・ MDPointerJumpToTick
-------------------------------------- */
int
MDPointerJumpToTick(MDPointer *inPointer, MDTickType inTick)
{
long num;
MDEvent event;
if (inPointer->parent == NULL)
return 0; /* always false */
num = inPointer->parent->num;
/* There are no events */
if (num == 0)
return 0;
inPointer->removed = 0;
/* Move to the top of the current block */
inPointer->position -= inPointer->index;
if (inPointer->block == NULL)
inPointer->block = inPointer->parent->first;
inPointer->index = 0;
/* dummy event for comparison only */
MDEventInit(&event);
MDSetTick(&event, inTick);
/* Look for the block whose first event >= inTick */
if (MDIsTickGreaterOrEqual(inPointer->block->events, &event)) {
/* The first event is already >= inTick */
/* Search backward */
while (inPointer->block->last != NULL) {
if (MDIsTickLess(inPointer->block->last->events, &event))
break;
inPointer->block = inPointer->block->last;
inPointer->position -= inPointer->block->num;
}
} else {
/* Search forward */
do {
inPointer->position += inPointer->block->num;
inPointer->block = inPointer->block->next;
} while (inPointer->block != NULL && MDIsTickLess(inPointer->block->events, &event));
}
if (inPointer->block != NULL &&
(inPointer->block->last == NULL ||
MDIsTickLess(inPointer->block->last->events + inPointer->block->last->num - 1, &event))) {
/* If this is the first block, or the last event in the previous block is < inEvent,
then the first event in this block is the goal */
inPointer->index = 0;
} else {
/* The goal is contained in the last block */
if (inPointer->block == NULL) {
inPointer->block = inPointer->parent->last;
} else {
/* inPointer->block->last should not be NULL (that case is already processed in the
previous "if" statement) */
inPointer->block = inPointer->block->last;
}
inPointer->position -= inPointer->block->num;
/* Look in this block for the first event which is >= inTick */
/* (If there are no such events, then the current position becomes the
"end of sequence") */
for (inPointer->index = 0; inPointer->index < inPointer->block->num; inPointer->index++) {
if (MDIsTickGreaterOrEqual(inPointer->block->events + inPointer->index, &event))
break;
}
inPointer->position += inPointer->index;
}
return (inPointer->position < num);
}
/* --------------------------------------
・ MDPointerJumpToLast
-------------------------------------- */
int
MDPointerJumpToLast(MDPointer *inPointer)
{
if (inPointer->parent == NULL)
return 0;
if (inPointer->parent->last == NULL)
return 0;
inPointer->position = inPointer->parent->num - 1;
inPointer->block = inPointer->parent->last;
inPointer->index = inPointer->parent->last->num - 1;
inPointer->removed = 0;
return 1;
}
/* --------------------------------------
・ MDPointerLookForEvent
-------------------------------------- */
int
MDPointerLookForEvent(MDPointer *inPointer, const MDEvent *inEvent)
{
long savePos;
MDBlock *saveBlock;
MDTickType tick;
if (inPointer->parent == NULL || inPointer->parent->num == 0 || inEvent == NULL)
return 0;
/* Move to the top event in the current block */
inPointer->position -= inPointer->index;
inPointer->index = 0;
savePos = inPointer->position;
saveBlock = inPointer->block;
tick = MDGetTick(inEvent);
/* Search forward */
while (inPointer->block != NULL && MDGetTick(inPointer->block->events) <= tick) {
/* This if-statement is violating ANSI standard (i.e. it assumes arbitrary two pointers
can be compared). However, this is allowed in most platforms. */
if (inPointer->block->events <= inEvent
&& inEvent < inPointer->block->events + inPointer->block->num) {
goto found;
}
inPointer->position += inPointer->block->num;
inPointer->block = inPointer->block->next;
}
inPointer->position = savePos;
inPointer->block = saveBlock;
/* Search backward */
while (inPointer->block != NULL && MDGetTick(inPointer->block->events) >= tick) {
if (inPointer->block->last != NULL) {
inPointer->block = inPointer->block->last;
inPointer->position -= inPointer->block->num;
} else break;
/* Another illegal if-statement */
if (inPointer->block->events <= inEvent
&& inEvent < inPointer->block->events + inPointer->block->num) {
goto found;
}
}
/* Not found */
inPointer->position = savePos;
inPointer->block = saveBlock;
return 0;
found:
/* Found */
inPointer->index = inEvent - inPointer->block->events;
inPointer->position += inPointer->index;
inPointer->removed = 0;
return 1;
}
/* --------------------------------------
・ MDPointerNextPos
-------------------------------------- */
static int
MDPointerNextPos(MDPointer *inPointer)
{
inPointer->removed = 0;
if (inPointer->block == NULL) {
if (inPointer->parent == NULL || inPointer->parent->num == 0)
return 0; /* No event: no change */
MDPointerUpdateBlock(inPointer);
}
if (++(inPointer->position) == 0) {
inPointer->block = inPointer->parent->first;
inPointer->index = 0;
if (inPointer->block == NULL)
return 0;
} else {
if (++(inPointer->index) >= inPointer->block->num) {
if (inPointer->block->next == NULL) {
return 0;
}
inPointer->block = inPointer->block->next;
inPointer->index = 0;
}
}
return 1;
}
/* --------------------------------------
・ MDPointerPreviousPos
-------------------------------------- */
static int
MDPointerPreviousPos(MDPointer *inPointer)
{
if (inPointer->position <= 0) {
if (inPointer->position == 0) {
inPointer->block = NULL;
inPointer->index = -1;
inPointer->position = -1;
}
return 0;
} else {
if ((inPointer->position)-- == inPointer->parent->num) {
inPointer->block = inPointer->parent->last;
inPointer->index = inPointer->block->num - 1;
} else {
if (--(inPointer->index) < 0) {
inPointer->block = inPointer->block->last; /* This should not be NULL */
inPointer->index = inPointer->block->num - 1;
}
}
}
inPointer->removed = 0;
return 1;
}
/* --------------------------------------
・ MDPointerCurrent
-------------------------------------- */
MDEvent *
MDPointerCurrent(const MDPointer *inPointer)
{
if (inPointer->parent == NULL ||
(inPointer->position < 0 || inPointer->position >= inPointer->parent->num)) {
return NULL;
} else {
return inPointer->block->events + inPointer->index;
}
}
/* --------------------------------------
・ MDPointerForward
-------------------------------------- */
MDEvent *
MDPointerForward(MDPointer *inPointer)
{
if (MDPointerNextPos(inPointer)) {
return MDPointerCurrent(inPointer);
} else return NULL;
}
/* --------------------------------------
・ MDPointerBackward
-------------------------------------- */
MDEvent *
MDPointerBackward(MDPointer *inPointer)
{
if (MDPointerPreviousPos(inPointer)) {
return MDPointerCurrent(inPointer);
} else return NULL;
}
/* --------------------------------------
・ MDPointerForwardWithSelector
-------------------------------------- */
MDEvent *
MDPointerForwardWithSelector(MDPointer *inPointer, MDEventSelector inSelector, void *inUserData)
{
MDEvent *ep;
long position;
while ((ep = MDPointerForward(inPointer)) != NULL) {
position = MDPointerGetPosition(inPointer);
if ((*inSelector)(ep, position, inUserData))
return ep;
}
return NULL;
}
/* --------------------------------------
・ MDPointerBackwardWithSelector
-------------------------------------- */
MDEvent *
MDPointerBackwardWithSelector(MDPointer *inPointer, MDEventSelector inSelector, void *inUserData)
{
MDEvent *ep;
long position;
while ((ep = MDPointerBackward(inPointer)) != NULL) {
position = MDPointerGetPosition(inPointer);
if ((*inSelector)(ep, position, inUserData))
return ep;
}
return NULL;
}
/* --------------------------------------
・ MDPointerSetPositionWithPointSet
-------------------------------------- */
int
MDPointerSetPositionWithPointSet(MDPointer *inPointer, MDPointSet *inPointSet, long offset, long *outIndex)
{
long index, position, pos, len;
if (inPointSet == NULL)
return 0;
index = position = 0;
while ((len = MDPointSetGetInterval(inPointSet, index)) >= 0) {
if (offset - position < len) {
pos = MDPointSetGetStartPoint(inPointSet, index);
if (outIndex != NULL)
*outIndex = index;
return MDPointerSetPosition(inPointer, pos + (offset - position));
}
position += len;
index++;
}
if (index > 0)
index--;
if (outIndex != NULL)
*outIndex = index;
return MDPointerSetPosition(inPointer, position);
}
/* --------------------------------------
・ MDPointerForwardWithPointSet
-------------------------------------- */
MDEvent *
MDPointerForwardWithPointSet(MDPointer *inPointer, MDPointSet *inPointSet, long *index)
{
long pt, n;
if (index == NULL) {
n = -1;
index = &n;
}
if (!MDPointerNextPos(inPointer)) {
/* No more event */
*index = -1;
return NULL;
}
if (*index >= 0) {
if ((pt = MDPointSetGetEndPoint(inPointSet, *index)) >= 0) {
if (inPointer->position >= pt) {
(*index)++;
pt = MDPointSetGetStartPoint(inPointSet, *index);
if (pt < 0)
goto end_of_pset;
MDPointerSetRelativePosition(inPointer, pt - inPointer->position);
}
return MDPointerCurrent(inPointer);
} else goto end_of_pset; /* This should not happen */
} else {
if (!MDPointSetLookup(inPointSet, inPointer->position, index)) {
pt = MDPointSetGetStartPoint(inPointSet, *index);
if (pt < 0)
goto end_of_pset;
MDPointerSetRelativePosition(inPointer, pt - inPointer->position);
}
return MDPointerCurrent(inPointer);
}
end_of_pset:
/* No more point in pointSet */
MDPointerSetPosition(inPointer, inPointer->parent->num);
return NULL;
}
/* --------------------------------------
・ MDPointerBackwardWithPointSet
-------------------------------------- */
MDEvent *
MDPointerBackwardWithPointSet(MDPointer *inPointer, MDPointSet *inPointSet, long *index)
{
long pt, n;
if (index == NULL) {
n = -1;
index = &n;
}
if (!MDPointerPreviousPos(inPointer)) {
*index = -1;
return NULL;
}
if (*index >= 0) {
if ((pt = MDPointSetGetStartPoint(inPointSet, *index)) >= 0) {
if (inPointer->position < pt) {
(*index)--;
pt = MDPointSetGetEndPoint(inPointSet, *index);
if (pt < 0)
goto pset_exhausted;
MDPointerSetRelativePosition(inPointer, (pt - 1) - inPointer->position);
}
return MDPointerCurrent(inPointer);
} else goto pset_exhausted; /* This should not happen */
} else {
if (!MDPointSetLookup(inPointSet, inPointer->position, index)) {
(*index)--;
pt = MDPointSetGetEndPoint(inPointSet, *index);
if (pt < 0)
goto pset_exhausted;
MDPointerSetRelativePosition(inPointer, (pt - 1) - inPointer->position);
}
return MDPointerCurrent(inPointer);
}
pset_exhausted:
MDPointerSetPosition(inPointer, -1);
return NULL;
}
/* --------------------------------------
・ MDPointerInsertAnEvent
-------------------------------------- */
MDStatus
MDPointerInsertAnEvent(MDPointer *inPointer, const MDEvent *inEvent)
{
MDEvent *ep1, *ep2;
MDTickType tick, ptick, tick1, tick2;
MDStatus sts = kMDNoError;
MDTrack *track;
if (inPointer == NULL)
return kMDNoError;
track = MDPointerGetTrack(inPointer);
/* Check whether the pointer is in the correct position */
ep1 = MDPointerBackward(inPointer);
ep2 = MDPointerForward(inPointer);
tick1 = (ep1 == NULL ? kMDNegativeTick : MDGetTick(ep1));
tick2 = (ep2 == NULL ? kMDMaxTick : MDGetTick(ep2));
tick = MDGetTick(inEvent);
if (tick1 > tick || tick > tick2)
MDPointerJumpToTick(inPointer, tick);
/* Insert the event */
if (MDTrackInsertBlanks(inPointer->parent, inPointer, 1) == 1) {
ep1 = MDPointerCurrent(inPointer);
MDEventCopy(ep1, inEvent, 1);
if (MDHasDuration(ep1))
ptick = MDGetTick(ep1) + MDGetDuration(ep1);
else ptick = kMDNegativeTick;
/* Update nch[] fields */
if (MDIsChannelEvent(ep1))
track->nch[MDGetChannel(ep1) & 15]++;
else if (MDIsSysexEvent(ep1))
track->nch[16]++;
else track->nch[17]++;
} else sts = kMDErrorOutOfMemory;
/* Update track duration if necessary */
if (sts == kMDNoError) {
tick1 = (ptick > tick ? ptick : tick);
if (tick1 >= MDTrackGetDuration(track))
MDTrackSetDuration(track, tick1 + 1);
}
return sts;
}
/* --------------------------------------
・ MDPointerDeleteAnEvent
-------------------------------------- */
MDStatus
MDPointerDeleteAnEvent(MDPointer *inPointer, MDEvent *outEvent)
{
MDEvent *ep1;
MDTrack *track;
if (inPointer == NULL || (ep1 = MDPointerCurrent(inPointer)) == NULL)
return kMDNoError;
track = MDPointerGetTrack(inPointer);
if (outEvent != NULL)
MDEventCopy(outEvent, ep1, 1);
if (MDIsChannelEvent(ep1))
track->nch[MDGetChannel(ep1) & 15]--;
else if (MDIsSysexEvent(ep1))
track->nch[16]--;
else track->nch[17]--;
MDTrackDeleteEvents(inPointer->parent, inPointer, 1);
return kMDNoError;
}
/* --------------------------------------
・ MDPointerReplaceAnEvent
-------------------------------------- */
MDStatus
MDPointerReplaceAnEvent(MDPointer *inPointer, const MDEvent *inEvent, MDEvent *outEvent)
{
MDEvent *ep;
MDTrack *track;
MDTickType oldTick;
if (inPointer == NULL || (ep = MDPointerCurrent(inPointer)) == NULL)
return kMDNoError;
track = MDPointerGetTrack(inPointer);
if (outEvent != NULL)
MDEventCopy(outEvent, ep, 1);
if (MDIsChannelEvent(ep))
track->nch[MDGetChannel(ep) & 15]--;
else if (MDIsSysexEvent(ep))
track->nch[16]--;
else track->nch[17]--;
oldTick = MDGetTick(ep);
MDEventCopy(ep, inEvent, 1);
MDSetTick(ep, oldTick);
if (MDIsChannelEvent(ep))
track->nch[MDGetChannel(ep) & 15]++;
else if (MDIsSysexEvent(ep))
track->nch[16]++;
else track->nch[17]++;
if (oldTick != MDGetTick(inEvent))
return MDPointerChangeTick(inPointer, MDGetTick(inEvent), -1);
else if (MDHasDuration(inEvent)) {
MDTickType tick1 = MDGetTick(inEvent) + MDGetDuration(inEvent);
if (tick1 >= MDTrackGetDuration(track))
MDTrackSetDuration(track, tick1 + 1);
if (inPointer->block->largestTick >= 0 && tick1 > inPointer->block->largestTick)
inPointer->block->largestTick = tick1;
}
return kMDNoError;
}
/* --------------------------------------
・ MDPointerChangeTick
-------------------------------------- */
MDStatus
MDPointerChangeTick(MDPointer *inPointer, MDTickType inTick, long inPosition)
{
MDTrack *track;
MDEvent *ep, *ep1;
MDTickType tick, tick_last, tick_next;
MDPointer *newPointer = NULL;
MDStatus sts = kMDNoError;
int oldAdjustFlag;
if (inPointer == NULL || (ep = MDPointerCurrent(inPointer)) == NULL)
return kMDNoError;
tick = MDGetTick(ep);
if (tick == inTick)
return kMDNoError;
track = MDPointerGetTrack(inPointer);
if (inPosition < 0) {
/* Check whether in-place change is possible */
tick_last = ((ep1 = MDPointerBackward(inPointer)) != NULL ? MDGetTick(ep1) : kMDNegativeTick);
MDPointerForward(inPointer);
tick_next = ((ep1 = MDPointerForward(inPointer)) != NULL ? MDGetTick(ep1) : kMDMaxTick);
MDPointerBackward(inPointer);
if (tick_last <= inTick && inTick <= tick_next) {
MDSetTick(ep, inTick);
goto exit;
}
}
/* Insert/delete is required */
newPointer = MDPointerNew(track);
if (newPointer == NULL)
return kMDErrorOutOfMemory;
if (inPosition >= 0) {
MDPointerSetPosition(newPointer, inPosition + (inPosition >= MDPointerGetPosition(inPointer) ? 1 : 0));
tick_last = ((ep1 = MDPointerBackward(newPointer)) != NULL ? MDGetTick(ep1) : kMDNegativeTick);
MDPointerForward(newPointer);
tick_next = ((ep1 = MDPointerForward(newPointer)) != NULL ? MDGetTick(ep1) : kMDMaxTick);
MDPointerBackward(newPointer);
}
if (inPosition < 0 || tick_last > inTick || inTick > tick_next)
MDPointerJumpToTick(newPointer, inTick);
if (MDPointerGetPosition(newPointer) == MDPointerGetPosition(inPointer)) {
MDSetTick(ep, inTick);
MDPointerRelease(newPointer);
goto exit;
}
oldAdjustFlag = MDPointerIsAutoAdjust(inPointer);
MDPointerSetAutoAdjust(inPointer, 1);
MDPointerSetAutoAdjust(newPointer, 1);
/* Insert a blank */
if (MDTrackInsertBlanks(track, newPointer, 1) == 1) {
ep1 = MDPointerCurrent(newPointer);
ep = MDPointerCurrent(inPointer); /* May have moved while inserting a blank */
MDEventMove(ep1, ep, 1);
MDSetTick(ep1, inTick);
/* Delete the previous position */
MDTrackDeleteEvents(track, inPointer, 1);
ep = MDPointerCurrent(newPointer); /* May have moved while deleting */
} else sts = kMDErrorOutOfMemory;
MDPointerSetPosition(inPointer, MDPointerGetPosition(newPointer));
MDPointerSetAutoAdjust(inPointer, oldAdjustFlag);
MDPointerRelease(newPointer);
exit:
/* Let the track duration greater than the last tick */
if (sts == kMDNoError) {
if (MDHasDuration(ep))
inTick += MDGetDuration(ep);
if (inTick >= MDTrackGetDuration(track))
MDTrackSetDuration(track, inTick + 1);
if (inPointer->block->largestTick >= 0 && inTick > inPointer->block->largestTick)
inPointer->block->largestTick = inTick;
}
return sts;
}
/* --------------------------------------
・ MDPointerCheck
-------------------------------------- */
MDStatus
MDPointerCheck(const MDPointer *inPointer)
{
MDTrack *track;
MDBlock *block;
long pos;
int err = 0;
if (inPointer == NULL)
return kMDNoError;
track = inPointer->parent;
if (track == NULL) {
fprintf(stderr, "MDPointerCheck: track is NULL\n");
err++;
}
pos = inPointer->position;
if (pos == -1) {
if (inPointer->block != NULL) {
fprintf(stderr, "MDPointerCheck: position is -1 but block is not NULL\n");
err++;
}
if (inPointer->index != -1) {
fprintf(stderr, "MDPointerCheck: position is -1 but index is not -1\n");
err++;
}
} else {
block = track->first;
if (block == NULL) {
fprintf(stderr, "MDPointerCheck: position (%ld) >= 0 but track has no data\n", pos);
err++;
} else if (inPointer->block == NULL) {
fprintf(stderr, "MDPointerCheck: position (%ld) >= 0 but block is NULL\n", pos);
err++;
} else {
while (block != NULL && pos >= block->num) {
if (pos == block->num && block->next == NULL)
break;
pos -= block->num;
block = block->next;
}
if (block == NULL) {
fprintf(stderr, "MDPointerCheck: block exhausts before position (%ld)\n", inPointer->position);
err++;
} else if (block != inPointer->block || pos != inPointer->index) {
fprintf(stderr, "MDPointerCheck: position (%ld) is index %d in block %p but inPointer claims index %d in block %p\n", inPointer->position, (int)(pos), block, (int)inPointer->index, inPointer->block);
err++;
}
}
}
return (err > 0 ? kMDErrorInternalError : kMDNoError);
}