如何将 sysex 数据添加到 MusicTrack? (音频工具箱)
Posted
技术标签:
【中文标题】如何将 sysex 数据添加到 MusicTrack? (音频工具箱)【英文标题】:How to add sysex data to MusicTrack? (AudioToolbox) 【发布时间】:2014-05-07 13:58:08 【问题描述】:我正在尝试用二十一点等编写我的小型 midi 音序器,但坚持将 sysex 数据写入 MusicTrack。我使用以下代码插入 sysex 事件
// ---- Some code here --- //
PatternData pattern = sizeof(PatternData), i, signature.numerator, signature.denominator ;
CABarBeatTime beattime = CABarBeatTime((i * signature.numerator * signature.denominator) + 1, 1, 0, SUBBEAT_DIVISOR_DEFAULT);
// Convert beattime to timestamp
if ((MusicSequenceBarBeatTimeToBeats(sequence, &beattime, ×tamp)) != noErr)
return status;
// Add event
if ((status = MusicTrackNewMIDIRawDataEvent(track, timestamp, (MIDIRawData*)&pattern)) != noErr)
return status;
// ---- Some code here --- //
PatternData 是
typedef struct PatternData
UInt32 length; // Struct length
UInt8 index; // Pattern index
UInt8 bars; // Number of bars in patten
UInt8 beats; // Number of beats in pattern
PatternData;
我做错了什么,因为在调用 MusicSequenceFileCreate 后我得到了损坏的文件。 有人有如何将 sysex 数据添加到音乐曲目的示例吗?
【问题讨论】:
你为什么不使用MIDIRawData
结构?
@CL。使用 MIDIRawData 我得到了相同的结果。此外,我想使用我自己的数据结构。我将我的结构转换为 MIDIRawData。 (在lists.apple.com/archives/coreaudio-api/2006/Feb/msg00147.html 发现了这种模式的提及)
您必须将数据包装到 SysEx 消息中,并且必须将该消息包装到与 MIDIRawData
结构具有相同布局的结构中。无论如何,对于 SysEx,你的 manufacturer ID 是什么?
@CL。我没有制造商标识符。在我看来,sysex 是在 midi 文件中存储自定义数据的好主意。你能给我一个例子,如何将 sysex 和 sysex 消息中的结构包装到 MIDIRawData 中吗?
如果您没有购买ID,则无法使用SysEx消息。
【参考方案1】:
好的。我找到了正确的方法,就是这样:
UInt8 data[] = 0xF0, manufacturerId, databyte1, databyte2, databyte3, 0xF7 ;
MIDIRawData raw;
memcpy(raw.data, data, 0, sizeof(data));
raw.length = sizeof(data);
if ((status = MusicTrackNewMIDIRawDataEvent(track, timestamp, &raw)) != noErr)
return status;
【讨论】:
您不能使用memccpy
,因为您的数据可能包含零字节。 raw.data
的大小只有一个字节;你有一个缓冲区溢出并且正在破坏你的堆栈。
在其他情况下这是真的(几乎总是),但这段代码就像魅力一样。使用 MidiKit 应用程序或使用我的测试应用程序成功打开保存的文件。
但是当你说数据中的零字节时你是对的。我应该使用 memcpy 而不是 memccpy。【参考方案2】:
这里是一个例子,如何在一个 MIDI 轨道中录制正常的 MIDI 和 SYSEX 消息,并将它们保存在共享 iTunes 文件夹中的一个 MIDI 文件中(在 .plist 中将“应用程序支持 iTunes 文件共享”设置为“YES”): 见代码中的“calloc”!
#import "ViewController.h"
#import <CoreMIDI/MIDIServices.h>
#import <CoreMIDI/CoreMIDI.h>
#import "AppDelegate.h"
#include <sys/time.h>
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#import <AVFoundation/AVFoundation.h>
@interface ViewController ()
@end
@implementation ViewController
@synthesize SYSEX_8;
long secTempA = 0;
float secTempB = 0;
long secStartA = 0;
float secStartB = 0;
MusicTimeStamp timeStamp = 0;
MusicSequence recordSequence;
MusicTrack recordTrack;
MusicTimeStamp lenRec = 0;
MIDINoteMessage noteMessage;
MusicTrack track;
NSString *fileNameForSave = @"";
NSString *midiFileWritePath = @"";
NSString *documentsDirectoryPath = @"";
UIAlertView *infoStore;
UIAlertView *infoStoreError;
MIDIRawData *sysexData;
- (void)viewDidLoad
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// Get documents Directory
// (don't forget the ".plist" entry "Application supports iTunes file sharing YES"
NSArray *pathDocDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectoryPath = [pathDocDir objectAtIndex:0];
- (void)didReceiveMemoryWarning
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
- (IBAction)SYSEX_8_touchdown:(id)sender
NewMusicSequence(&recordSequence);
MusicSequenceNewTrack(recordSequence, &recordTrack);
MusicSequenceSetSequenceType(recordSequence, kMusicSequenceType_Beats);
timeStamp = 0;
struct timeval time;
gettimeofday(&time, NULL);
secStartA = time.tv_sec;
secStartB = time.tv_usec * 0.000001;
noteMessage.channel = 0x90; // Note ON
noteMessage.note = 0x3C;
noteMessage.velocity = 0x7F;
MusicTrackNewMIDINoteEvent(recordTrack, timeStamp, ¬eMessage);
NSLog(@"%02x %02x %02x", 0x90, 0x3C, 0x7F);
usleep(10000);
gettimeofday(&time, NULL);
secTempA = time.tv_sec;
secTempB = time.tv_usec * 0.000001;
secTempA = secTempA - secStartA;
secTempB = secTempB - secStartB;
timeStamp = (secTempA + secTempB) * 2;
noteMessage.channel = 0x90; // Note OFF
noteMessage.note = 0x3C;
noteMessage.velocity = 0x00;
MusicTrackNewMIDINoteEvent(recordTrack, timeStamp, ¬eMessage);
NSLog(@"%02x %02x %02x", 0x90, 0x3C, 0x00);
usleep(100000);
gettimeofday(&time, NULL);
secTempA = time.tv_sec;
secTempB = time.tv_usec * 0.000001;
secTempA = secTempA - secStartA;
secTempB = secTempB - secStartB;
timeStamp = (secTempA + secTempB) * 2;
Byte datatest[8];
UInt32 theSize = offsetof(MIDIRawData, data[0]) + (sizeof(UInt8) * sizeof(datatest));
sysexData = (MIDIRawData *)calloc(1, theSize);
sysexData->length = sizeof(datatest);
datatest[0] = 0xF0; // Start SYSEX
datatest[1] = 0x26;
datatest[2] = 0x79;
datatest[3] = 0x0E;
datatest[4] = 0x00;
datatest[5] = 0x00;
datatest[6] = 0x00;
datatest[7] = 0xF7; // End SYSEX
for (int j = 0; j < sizeof(datatest); j++)
sysexData->data[j] = datatest[j];
NSLog(@"%02x", sysexData->data[j]);
int status;
if ((status = MusicTrackNewMIDIRawDataEvent(recordTrack, timeStamp, sysexData) != noErr))
NSLog(@"error %i", status);
else
[self stopRecording];
- (void) stopRecording
CAShow(recordSequence); // To show all MIDI events !!!
UInt32 sz = sizeof(MusicTimeStamp);
lenRec = 0;
MusicSequenceGetIndTrack(recordSequence, 0, &track);
MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &lenRec, &sz);
if (lenRec > 0.1)
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss +zzzz"];
NSDate *startDate = [NSDate date];
NSTimeZone *zone = [NSTimeZone systemTimeZone];
NSInteger interval = [zone secondsFromGMTForDate:startDate];
startDate = [startDate dateByAddingTimeInterval:interval];
// NSLog(@"Date: %@", startDate);
NSString *strDate = [[NSString alloc] initWithFormat:@"%@", startDate];
NSArray *arr = [strDate componentsSeparatedByString:@" "];
NSString *str;
str = [arr objectAtIndex:0];
NSArray *arr_date = [str componentsSeparatedByString:@"-"];
int year = [[arr_date objectAtIndex:0] intValue];
int month = [[arr_date objectAtIndex:1] intValue];
int day = [[arr_date objectAtIndex:2] intValue];
str = [arr objectAtIndex:1];
NSArray *arr_time = [str componentsSeparatedByString:@":"];
int hours = [[arr_time objectAtIndex:0] intValue];
int minutes = [[arr_time objectAtIndex:1] intValue];
int seconds = [[arr_time objectAtIndex:2] intValue];
fileNameForSave = [NSString stringWithFormat:@"%@_%04d%02d%02d_%02d%02d%02d%@", @"$Record", year, month, day, hours, minutes, seconds, @".mid"];
midiFileWritePath = [documentsDirectoryPath stringByAppendingPathComponent:fileNameForSave];
infoStore = [[UIAlertView alloc]initWithTitle: @"Save as MIDI file ?"
message: [NSString stringWithFormat:@"\n%@", fileNameForSave]
delegate: self
cancelButtonTitle: @"YES"
otherButtonTitles: @"NO",nil];
[infoStore show]; // rest siehe unten !!!!!
else
MusicSequenceDisposeTrack(recordSequence, track);
DisposeMusicSequence(recordSequence);
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(int)buttonIndex
// deletion code here
if (alertView == infoStore)
if (buttonIndex == 0)
NSURL *midiURL = [NSURL fileURLWithPath:midiFileWritePath];
OSStatus status = 0;
status = MusicSequenceFileCreate(recordSequence, (__bridge CFURLRef)(midiURL), kMusicSequenceFile_MIDIType, kMusicSequenceFileFlags_EraseFile, 0);
if (status != noErr)
infoStoreError = [[UIAlertView alloc]initWithTitle: @"Information"
message: [NSString stringWithFormat:@"\nError storing MIDI file in: %@", documentsDirectoryPath]
delegate: self
cancelButtonTitle: nil
otherButtonTitles:@"OK",nil];
[infoStoreError show];
MusicSequenceDisposeTrack(recordSequence, track);
DisposeMusicSequence(recordSequence);
@end
【讨论】:
以上是关于如何将 sysex 数据添加到 MusicTrack? (音频工具箱)的主要内容,如果未能解决你的问题,请参考以下文章