gpt4 book ai didi

ios - 在 IOS 上读取 Midi 文件

转载 作者:技术小花猫 更新时间:2023-10-29 10:59:40 24 4
gpt4 key购买 nike

我正在寻找有关如何在 IOS 上播放 midi 文件的信息。我不需要任何 MIDI 输入或输出消息。我只是想读取 midi 文件并将轨道播放给用户,用每个音符代替钢琴声音样本。能够调整节奏将是另一个要求。

请注意,我对将 midi 文件转换为 wav 或其他格式不感兴趣。我想直接读取 midi 文件。

谁能给我指出一些信息的方向,以帮助我理解所需的过程。

干杯

最佳答案

我也需要这个功能。下面是骨架解析器的代码,它解析 NSData 对象中提供的 MIDI 文件数据(例如来自 NSData:dataWithContentsOfFile),并将它找到的内容写入可变字符串日志。真实的应用程序会以更有用的方式处理各种事件,但对于需要解析标准 MIDI 文件的任何人来说,这应该是一个很好的起点,因为它解决了大部分痛点。

        // MidiParser.h

#import <Foundation/Foundation.h>

typedef enum tagMidiTimeFormat
{
MidiTimeFormatTicksPerBeat,
MidiTimeFormatFramesPerSecond
} MidiTimeFormat;

@interface MidiParser : NSObject
{
NSMutableString *log;
NSData *data;
NSUInteger offset;

UInt16 format;
UInt16 trackCount;
MidiTimeFormat timeFormat;

UInt16 ticksPerBeat;
UInt16 framesPerSecond;
UInt16 ticksPerFrame;
}

@property (nonatomic, retain) NSMutableString *log;

@property (readonly) UInt16 format;
@property (readonly) UInt16 trackCount;
@property (readonly) MidiTimeFormat timeFormat;

- (BOOL) parseData: (NSData *) midiData;

@end

// MidiParser.m

#import "MidiParser.h"

#define kFileCorrupt @"File is corrupt"
#define kInvalidHeader @"Invalid MIDI header"
#define kInvalidTrackHeader @"Invalid Track header"

#define MAIN_HEADER_SIZE 6

#define META_SEQUENCE_NUMBER 0x0
#define META_TEXT_EVENT 0x1
#define META_COPYRIGHT_NOTICE 0x2
#define META_TRACK_NAME 0x3
#define META_INSTRUMENT_NAME 0x4
#define META_LYRICS 0x5
#define META_MARKER 0x6
#define META_CUE_POINT 0x7
#define META_CHANNEL_PREFIX 0x20
#define META_END_OF_TRACK 0x2f
#define META_SET_TEMPO 0x51
#define META_SMPTE_OFFSET 0x54
#define META_TIME_SIGNATURE 0x58
#define META_KEY_SIGNATURE 0x59
#define META_SEQ_SPECIFIC 0x7f

#define CHANNEL_NOTE_OFF 0x8
#define CHANNEL_NOTE_ON 0x9
#define CHANNEL_NOTE_AFTERTOUCH 0xA
#define CHANNEL_CONTROLLER 0xB
#define CHANNEL_PROGRAM_CHANGE 0xC
#define CHANNEL_AFTERTOUCH 0xD
#define CHANNEL_PITCH_BEND 0xE

#define MICRO_PER_MINUTE 60000000

@implementation MidiParser

@synthesize log;

@synthesize format;
@synthesize trackCount;
@synthesize timeFormat;

- (void) dealloc
{
[log release];
log = nil;

[super dealloc];
}

- (UInt32) readDWord
{
UInt32 value = 0;
[data getBytes:&value range:NSMakeRange(offset, sizeof(value))];
value = CFSwapInt32BigToHost(value);
offset += sizeof(value);
return value;
}

- (UInt16) readWord
{
UInt16 value = 0;
[data getBytes:&value range:NSMakeRange(offset, sizeof(value))];
value = CFSwapInt16BigToHost(value);
offset += sizeof(value);
return value;
}

- (UInt8) readByte
{
UInt8 value = 0;
[data getBytes:&value range:NSMakeRange(offset, sizeof(value))];
offset += sizeof(value);
return value;
}

- (UInt8) readByteAtRelativeOffset: (UInt32) o
{
UInt8 value = 0;
[data getBytes:&value range:NSMakeRange(offset + o, sizeof(value))];
return value;
}

- (UInt32) readVariableValue
{
UInt32 value = 0;

UInt8 byte;
UInt8 shift = 0;
do
{
value <<= shift;
[data getBytes:&byte range:NSMakeRange(offset, 1)];
offset++;
value |= (byte & 0x7f);
shift = 7;
} while ((byte & 0x80) != 0);

return value;
}

- (NSString *) readString: (int) length
{
char *buffer = malloc(length + 1);
memcpy(buffer, ([data bytes] + offset), length);
buffer[length] = 0x0;
NSString *string = [NSString stringWithCString:buffer encoding:NSASCIIStringEncoding];
free(buffer);
return string;
}

- (void) readMetaSequence
{
UInt32 sequenceNumber = 0;
sequenceNumber |= [self readByteAtRelativeOffset:0];
sequenceNumber <<= 8;
sequenceNumber |= [self readByteAtRelativeOffset:1];
[self.log appendFormat:@"Meta Sequence Number: %d\n", sequenceNumber];
}

- (void) readMetaTextEvent: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Text: %@\n", text];
}

- (void) readMetaCopyrightNotice: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Copyright: %@\n", text];
}

- (void) readMetaTrackName: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Track Name: %@\n", text];
}

- (void) readMetaInstrumentName: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Instrument Name: %@\n", text];
}

- (void) readMetaLyrics: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Text: %@\n", text];
}

- (void) readMetaMarker: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Marker: %@\n", text];
}

- (void) readMetaCuePoint: (UInt32) length
{
NSString *text = [self readString:length];
[self.log appendFormat:@"Meta Cue Point: %@\n", text];
}

- (void) readMetaChannelPrefix
{
UInt8 channel = [self readByteAtRelativeOffset:0];
[self.log appendFormat:@"Meta Channel Prefix: %d\n", channel];
}

- (void) readMetaEndOfTrack
{
[self.log appendFormat:@"Meta End of Track\n"];
}

- (void) readMetaSetTempo
{
UInt32 microPerQuarter = 0;
microPerQuarter |= [self readByteAtRelativeOffset:0];
microPerQuarter <<= 8;
microPerQuarter |= [self readByteAtRelativeOffset:1];
microPerQuarter <<= 8;
microPerQuarter |= [self readByteAtRelativeOffset:2];

UInt32 bpm = MICRO_PER_MINUTE / microPerQuarter;
[self.log appendFormat:@"Meta Set Tempo: Micro Per Quarter: %d, Beats Per Minute: %d\n", microPerQuarter, bpm];
}

- (void) readMetaSMPTEOffset
{
UInt8 byte = [self readByteAtRelativeOffset:0];
UInt8 hour = byte & 0x1f;
UInt8 rate = (byte & 0x60) >> 5;
UInt8 fps = 0;
switch(rate)
{
case 0: fps = 24; break;
case 1: fps = 25; break;
case 2: fps = 29; break;
case 3: fps = 30; break;
default: fps = 0; break;
}
UInt8 minutes = [self readByteAtRelativeOffset:1];
UInt8 seconds = [self readByteAtRelativeOffset:2];
UInt8 frame = [self readByteAtRelativeOffset:3];
UInt8 subframe = [self readByteAtRelativeOffset:4];
[self.log appendFormat:@"Meta SMPTE Offset (%d): %2d:%2d:%2d:%2d:%2d\n", fps, hour, minutes, seconds, frame, subframe];
}

- (void) readMetaTimeSignature
{
UInt8 numerator = [self readByteAtRelativeOffset:0];
UInt8 denominator = [self readByteAtRelativeOffset:1];
UInt8 metro = [self readByteAtRelativeOffset:2];
UInt8 thirty_seconds = [self readByteAtRelativeOffset:3];

[self.log appendFormat:@"Meta Time Signature: %d/%.0f, Metronome: %d, 32nds: %d\n", numerator, powf(2, denominator), metro, thirty_seconds];
}

- (void) readMetaKeySignature
{
UInt8 value = [self readByteAtRelativeOffset:0];
UInt8 accidentals = value & 0x7f;
BOOL sharps = YES;
NSString *accidentalsType = nil;
if((value & 0x80) != 0)
{
accidentalsType = [NSString stringWithString:@"Flats"];
sharps = NO;
}
else
{
accidentalsType = [NSString stringWithString:@"Sharps"];
}
UInt8 scale = [self readByteAtRelativeOffset:1];
NSString *scaleType = nil;
if(scale == 0)
{
scaleType = [NSString stringWithString:@"Major"];
}
else
{
scaleType = [NSString stringWithString:@"Minor"];
}
[self.log appendFormat:@"Meta Key Signature: %d %@ Type: %@\n", accidentals, accidentalsType, scaleType];
}

- (void) readMetaSeqSpecific: (UInt32) length
{
[self.log appendFormat:@"Meta Event Sequencer Specific: - Length: %d\n", length];
}

- (void) readNoteOff: (UInt8) channel parameter1: (UInt8) p1 parameter2: (UInt8) p2
{
[self.log appendFormat:@"Note Off (Channel %d): %d, Velocity: %d\n", channel, p1, p2];
}

- (void) readNoteOn: (UInt8) channel parameter1: (UInt8) p1 parameter2: (UInt8) p2
{
[self.log appendFormat:@"Note On (Channel %d): %d, Velocity: %d\n", channel, p1, p2];
}

- (void) readNoteAftertouch: (UInt8) channel parameter1: (UInt8) p1 parameter2: (UInt8) p2
{
[self.log appendFormat:@"Note Aftertouch (Channel %d): %d, Amount: %d\n", channel, p1, p2];
}

- (void) readControllerEvent: (UInt8) channel parameter1: (UInt8) p1 parameter2: (UInt8) p2
{
[self.log appendFormat:@"Controller (Channel %d): %d, Value: %d\n", channel, p1, p2];
}

- (void) readProgramChange: (UInt8) channel parameter1: (UInt8) p1
{
[self.log appendFormat:@"Program Change (Channel %d): %d\n", channel, p1];
}

- (void) readChannelAftertouch: (UInt8) channel parameter1: (UInt8) p1
{
[self.log appendFormat:@"Channel Aftertouch (Channel %d): %d\n", channel, p1];
}

- (void) readPitchBend: (UInt8) channel parameter1: (UInt8) p1 parameter2: (UInt8) p2
{
UInt32 value = p1;
value <<= 8;
value |= p2;
[self.log appendFormat:@"Pitch Bend (Channel %d): %d\n", channel, value];
}

- (BOOL) parseData:(NSData *)midiData
{
BOOL success = YES;
self.log = [[[NSMutableString alloc] init] autorelease];

@try
{
// Parse data
data = midiData;
offset = 0;

// If size is less than header size, then abort
NSUInteger dataLength = [data length];
if((offset + MAIN_HEADER_SIZE) > dataLength)
{
NSException *ex = [NSException exceptionWithName:kFileCorrupt
reason:kFileCorrupt userInfo:nil];
@throw ex;
}

// Parse header
if(memcmp([data bytes], "MThd", 4) != 0)
{
NSException *ex = [NSException exceptionWithName:kFileCorrupt
reason:kInvalidHeader userInfo:nil];
@throw ex;
}
offset += 4;

UInt32 chunkSize = [self readDWord];
[self.log appendFormat:@"Header Chunk Size: %d\n", chunkSize];

// Read format
format = [self readWord];
[self.log appendFormat:@"Format: %d\n", format];

// Read track count
trackCount = [self readWord];
[self.log appendFormat:@"Tracks: %d\n", trackCount];

// Read time format
UInt16 timeDivision = [self readWord];
if((timeDivision & 0x8000) == 0)
{
timeFormat = MidiTimeFormatTicksPerBeat;
ticksPerBeat = timeDivision & 0x7fff;
[self.log appendFormat:@"Time Format: %d Ticks Per Beat\n", ticksPerBeat];
}
else
{
timeFormat = MidiTimeFormatFramesPerSecond;
framesPerSecond = (timeDivision & 0x7f00) >> 8;
ticksPerFrame = (timeDivision & 0xff);
[self.log appendFormat:@"Time Division: %d Frames Per Second, %d Ticks Per Frame\n", framesPerSecond, ticksPerFrame];
}

// Try to parse tracks
UInt32 expectedTrackOffset = offset;
for(UInt16 track = 0; track < trackCount; track++)
{
if(offset != expectedTrackOffset)
{
[self.log appendFormat:@"Track Offset Incorrect for Track %d - Offset: %d, Expected: %d", track, offset, expectedTrackOffset];
offset = expectedTrackOffset;
}

// Parse track header
if(memcmp([data bytes] + offset, "MTrk", 4) != 0)
{
NSException *ex = [NSException exceptionWithName:kFileCorrupt
reason:kInvalidTrackHeader userInfo:nil];
@throw ex;
}
offset += 4;

UInt32 trackSize = [self readDWord];
expectedTrackOffset = offset + trackSize;
[self.log appendFormat:@"Track %d : %d bytes\n", track, trackSize];

UInt32 trackEnd = offset + trackSize;
UInt32 deltaTime;
UInt8 nextByte = 0;
UInt8 peekByte = 0;
while(offset < trackEnd)
{
deltaTime = [self readVariableValue];
[self.log appendFormat:@" (%05d): ", deltaTime];

// Peak at next byte
peekByte = [self readByteAtRelativeOffset:0];

// If high bit not set, then assume running status
if((peekByte & 0x80) != 0)
{
nextByte = [self readByte];
}

// Meta event
if(nextByte == 0xFF)
{
UInt8 metaEventType = [self readByte];
UInt32 metaEventLength = [self readVariableValue];
switch (metaEventType)
{
case META_SEQUENCE_NUMBER:
[self readMetaSequence];
break;

case META_TEXT_EVENT:
[self readMetaTextEvent: metaEventLength];
break;

case META_COPYRIGHT_NOTICE:
[self readMetaCopyrightNotice: metaEventLength];
break;

case META_TRACK_NAME:
[self readMetaTrackName: metaEventLength];
break;

case META_INSTRUMENT_NAME:
[self readMetaInstrumentName: metaEventLength];
break;

case META_LYRICS:
[self readMetaLyrics: metaEventLength];
break;

case META_MARKER:
[self readMetaMarker: metaEventLength];
break;

case META_CUE_POINT:
[self readMetaCuePoint: metaEventLength];
break;

case META_CHANNEL_PREFIX:
[self readMetaChannelPrefix];
break;

case META_END_OF_TRACK:
[self readMetaEndOfTrack];
break;

case META_SET_TEMPO:
[self readMetaSetTempo];
break;

case META_SMPTE_OFFSET:
[self readMetaSMPTEOffset];
break;

case META_TIME_SIGNATURE:
[self readMetaTimeSignature];
break;

case META_KEY_SIGNATURE:
[self readMetaKeySignature];
break;

case META_SEQ_SPECIFIC:
[self readMetaSeqSpecific: metaEventLength];
break;

default:
[self.log appendFormat:@"Meta Event Type: 0x%x, Length: %d\n", metaEventType, metaEventLength];
break;
}

offset += metaEventLength;
}
else if(nextByte == 0xf0)
{
// SysEx event
UInt32 sysExDataLength = [self readVariableValue];
[self.log appendFormat:@"SysEx Event - Length: %d\n", sysExDataLength];
offset += sysExDataLength;
}
else
{
// Channel event
UInt8 eventType = (nextByte & 0xF0) >> 4;
UInt8 channel = (nextByte & 0xF);
UInt8 p1 = 0;
UInt8 p2 = 0;

switch (eventType)
{
case CHANNEL_NOTE_OFF:
p1 = [self readByte];
p2 = [self readByte];
[self readNoteOff: channel parameter1: p1 parameter2: p2];
break;

case CHANNEL_NOTE_ON:
p1 = [self readByte];
p2 = [self readByte];
[self readNoteOn:channel parameter1:p1 parameter2:p2];
break;

case CHANNEL_NOTE_AFTERTOUCH:
p1 = [self readByte];
p2 = [self readByte];
[self readNoteAftertouch:channel parameter1:p1 parameter2:p2];
break;

case CHANNEL_CONTROLLER:
p1 = [self readByte];
p2 = [self readByte];
[self readControllerEvent:channel parameter1:p1 parameter2:p2];
break;

case CHANNEL_PROGRAM_CHANGE:
p1 = [self readByte];
[self readProgramChange:channel parameter1:p1];
break;

case CHANNEL_AFTERTOUCH:
p1 = [self readByte];
[self readChannelAftertouch:channel parameter1:p1];
break;

case CHANNEL_PITCH_BEND:
p1 = [self readByte];
p2 = [self readByte];
[self readPitchBend:channel parameter1:p1 parameter2:p2];
break;

default:
break;
}

}
}
}

}
@catch (NSException *exception)
{
success = NO;
[self.log appendString:[exception reason]];
}

return success;
}

@end

关于ios - 在 IOS 上读取 Midi 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7193695/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com