⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 native_midi_mac.c

📁 SDL_mixer 是一个基于 SDL 的混音器
💻 C
📖 第 1 页 / 共 2 页
字号:
	int			channel_pitch_bend[16];		int			lastEventTime = 0;	int			tempo = 500000;	double		Ippqn = 1.0 / (1000*ppqn);	double		tick = tempo * Ippqn;	MIDIEvent	*eventPos = evntlist;	MIDIEvent	*noteOffPos;	Uint32 		*tunePos, *endPos;	Uint32		*tuneSequence;	size_t		tuneSize;		/* allocate space for the tune header */	tuneSize = 5000;	tuneSequence = (Uint32 *)malloc(tuneSize * sizeof(Uint32));	if (tuneSequence == NULL)		return NULL;		/* Set starting position in our tune memory */	tunePos = tuneSequence;	endPos = tuneSequence + tuneSize;	/* Initialise the arrays */	memset(part_poly,0,sizeof(part_poly));		memset(channel_to_part,-1,sizeof(channel_to_part));	memset(channel_pan,-1,sizeof(channel_pan));	memset(channel_vol,-1,sizeof(channel_vol));	memset(channel_pitch_bend,-1,sizeof(channel_pitch_bend));		*numParts = 0;		/*	 * Now the major work - iterate over all GM events,	 * and turn them into QuickTime Music format.	 * At the same time, calculate the max. polyphony for each part,	 * and also the part->instrument mapping.	 */	while(eventPos)	{		int status = (eventPos->status&0xF0)>>4;		int channel = eventPos->status&0x0F;		int part = channel_to_part[channel];        int velocity, pitch;        int value, controller;        int bend;        int newInst;				/* Check if we are running low on space... */		if((tunePos+16) > endPos)		{			/* Resize our data storage. */			Uint32 		*oldTuneSequence = tuneSequence;			tuneSize += BUFFER_INCREMENT;			tuneSequence = (Uint32 *)realloc(tuneSequence, tuneSize * sizeof(Uint32));			if(oldTuneSequence != tuneSequence)				tunePos += tuneSequence - oldTuneSequence;			endPos = tuneSequence + tuneSize;		}				switch (status)		{		case MIDI_STATUS_NOTE_OFF:			assert(part>=0 && part<=31);			/* Keep track of the polyphony of the current part */			part_poly[part]--;			break;		case MIDI_STATUS_NOTE_ON:			if (part < 0)			{				/* If no part is specified yet, we default to the first instrument, which				   is piano (or the first drum kit if we are on the drum channel)				*/				int newInst;								if (channel == 9)					newInst = kFirstDrumkit + 1;		/* the first drum kit is the "no drum" kit! */				else					newInst = kFirstGMInstrument;				part = channel_to_part[channel] = *numParts;				part_to_inst[(*numParts)++] = newInst;			}			/* TODO - add support for more than 32 parts using eXtended QTMA events */			assert(part<=31);						/* Decode pitch & velocity */			pitch = eventPos->data[0];			velocity = eventPos->data[1];						if (velocity == 0)			{				/* was a NOTE OFF in disguise, so we decrement the polyphony */				part_poly[part]--;			}			else			{				/* Keep track of the polyphony of the current part */				int foo = ++part_poly[part];				if (part_poly_max[part] < foo)					part_poly_max[part] = foo;				/* Now scan forward to find the matching NOTE OFF event */				for(noteOffPos = eventPos; noteOffPos; noteOffPos = noteOffPos->next)				{					if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_OFF						&& channel == (eventPos->status&0x0F)						&& pitch == noteOffPos->data[0])						break;					/* NOTE ON with velocity == 0 is the same as a NOTE OFF */					if ((noteOffPos->status&0xF0)>>4 == MIDI_STATUS_NOTE_ON						&& channel == (eventPos->status&0x0F)						&& pitch == noteOffPos->data[0]						&& 0 == noteOffPos->data[1])						break;				}								/* Did we find a note off? Should always be the case, but who knows... */				if (noteOffPos)				{					/* We found a NOTE OFF, now calculate the note duration */					int duration = (int)((noteOffPos->time - eventPos->time)*tick);										REST_IF_NECESSARY();					/* Now we need to check if we get along with a normal Note Event, or if we need an extended one... */					if (duration < 2048 && pitch>=32 && pitch<=95 && velocity>=0 && velocity<=127)					{						qtma_StuffNoteEvent(*tunePos, part, pitch, velocity, duration);						tunePos++;					}					else					{						qtma_StuffXNoteEvent(*tunePos, *(tunePos+1), part, pitch, velocity, duration);						tunePos+=2;					}				}			}			break;		case MIDI_STATUS_AFTERTOUCH:			/* NYI - use kControllerAfterTouch. But how are the parameters to be mapped? */			break;		case MIDI_STATUS_CONTROLLER:			controller = eventPos->data[0];			value = eventPos->data[1];			switch(controller)			{			case 0:	/* bank change - igore for now */				break;			case kControllerVolume:				if(channel_vol[channel] != value<<8)				{					channel_vol[channel] = value<<8;					if(part>=0 && part<=31)					{						REST_IF_NECESSARY();						qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);						tunePos++;					}				}				break;			case kControllerPan:				if(channel_pan[channel] != (value << 1) + 256)				{					channel_pan[channel] = (value << 1) + 256;					if(part>=0 && part<=31)					{						REST_IF_NECESSARY();						qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);						tunePos++;					}				}				break;			default:				/* No other controllers implemented yet */;				break;			}						break;		case MIDI_STATUS_PROG_CHANGE:			/* Instrument changed */			newInst = eventPos->data[0];						/* Channel 9 (the 10th channel) is different, it indicates a drum kit */			if (channel == 9)				newInst += kFirstDrumkit;			else				newInst += kFirstGMInstrument;			/* Only if the instrument for this channel *really* changed, add a new part. */			if(newInst != part_to_inst[part])			{				/* TODO maybe make use of kGeneralEventPartChange here,				   to help QT reuse note channels?				*/				part = channel_to_part[channel] = *numParts;				part_to_inst[(*numParts)++] = newInst;				if(channel_vol[channel] >= 0)				{					REST_IF_NECESSARY();					qtma_StuffControlEvent(*tunePos, part, kControllerVolume, channel_vol[channel]);					tunePos++;				}				if(channel_pan[channel] >= 0)				{					REST_IF_NECESSARY();					qtma_StuffControlEvent(*tunePos, part, kControllerPan, channel_pan[channel]);					tunePos++;				}				if(channel_pitch_bend[channel] >= 0)				{					REST_IF_NECESSARY();					qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, channel_pitch_bend[channel]);					tunePos++;				}						}			break;		case MIDI_STATUS_PRESSURE:			/* NYI */			break;		case MIDI_STATUS_PITCH_WHEEL:			/* In the midi spec, 0x2000 = center, 0x0000 = - 2 semitones, 0x3FFF = +2 semitones			   but for QTMA, we specify it as a 8.8 fixed point of semitones			   TODO: detect "pitch bend range changes" & honor them!			*/			bend = (eventPos->data[0] & 0x7f) | ((eventPos->data[1] & 0x7f) << 7);						/* "Center" the bend */			bend -= 0x2000;						/* Move it to our format: */			bend <<= 4;						/* If it turns out the pitch bend didn't change, stop here */			if(channel_pitch_bend[channel] == bend)				break;						channel_pitch_bend[channel] = bend;			if(part>=0 && part<=31)			{				/* Stuff a control event */				REST_IF_NECESSARY();				qtma_StuffControlEvent(*tunePos, part, kControllerPitchBend, bend);				tunePos++;			}						break;		case MIDI_STATUS_SYSEX:			if (eventPos->status == 0xFF && eventPos->data[0] == 0x51) /* Tempo change */			{				tempo = (eventPos->extraData[0] << 16) +					(eventPos->extraData[1] << 8) +					eventPos->extraData[2];								tick = tempo * Ippqn;			}			break;		}				/* on to the next event */		eventPos = eventPos->next;	} 		/* Finally, place an end marker */	*tunePos = kEndMarkerValue;		return tuneSequence;}Uint32 *BuildTuneHeader(int part_poly_max[32], int part_to_inst[32], int numParts){	Uint32			*myHeader;	Uint32			*myPos1, *myPos2;		/* pointers to the head and tail long words of a music event */	NoteRequest		*myNoteRequest;	NoteAllocator	myNoteAllocator;		/* for the NAStuffToneDescription call */	ComponentResult	myErr = noErr;	int				part;	myHeader = NULL;	myNoteAllocator = NULL;	/*	 * Open up the Note Allocator	 */	myNoteAllocator = OpenDefaultComponent(kNoteAllocatorComponentType,0);	if (myNoteAllocator == NULL)		goto bail;		/*	 * Allocate space for the tune header	 */	myHeader = (Uint32 *)			NewPtrClear((numParts * kNoteRequestEventLength + kMarkerEventLength) * sizeof(Uint32));	if (myHeader == NULL)		goto bail;		myPos1 = myHeader;		/*	 * Loop over all parts	 */	for(part = 0; part < numParts; ++part)	{		/*		 * Stuff request for the instrument with the given polyphony		 */		myPos2 = myPos1 + (kNoteRequestEventLength - 1); /* last longword of general event */		qtma_StuffGeneralEvent(*myPos1, *myPos2, part, kGeneralEventNoteRequest, kNoteRequestEventLength);		myNoteRequest = (NoteRequest *)(myPos1 + 1);		myNoteRequest->info.flags = 0;		/* I'm told by the Apple people that the Quicktime types were poorly designed and it was 		 * too late to change them. On little endian, the BigEndian(Short|Fixed) types are structs		 * while on big endian they are primitive types. Furthermore, Quicktime failed to 		 * provide setter and getter functions. To get this to work, we need to case the 		 * code for the two possible situations.		 * My assumption is that the right-side value was always expected to be BigEndian		 * as it was written way before the Universal Binary transition. So in the little endian		 * case, OSSwap is used.		 */#if SDL_BYTEORDER == SDL_LIL_ENDIAN		myNoteRequest->info.polyphony.bigEndianValue = OSSwapHostToBigInt16(part_poly_max[part]);		myNoteRequest->info.typicalPolyphony.bigEndianValue = OSSwapHostToBigInt32(0x00010000);#else		myNoteRequest->info.polyphony = part_poly_max[part];		myNoteRequest->info.typicalPolyphony = 0x00010000;#endif		myErr = NAStuffToneDescription(myNoteAllocator,part_to_inst[part],&myNoteRequest->tone);		if (myErr != noErr)			goto bail;				/* move pointer to beginning of next event */		myPos1 += kNoteRequestEventLength;	}	*myPos1 = kEndMarkerValue;		/* end of sequence marker */bail:	if(myNoteAllocator)		CloseComponent(myNoteAllocator);	/* if we encountered an error, dispose of the storage we allocated and return NULL */	if (myErr != noErr) {		DisposePtr((Ptr)myHeader);		myHeader = NULL;	}	return myHeader;}#endif /* MacOS native MIDI support */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -