2014-04-14 20 views
6

Dobry wieczór. Próbuję napisać proste odtwarzacze midi z bankiem jakości dźwięku. W obliczu problemu odtwarzania plików midi. Problem polega na tym, że wszystkie utwory midi (perkusja, pady, bas, syntezator itp.) Grały, ale grają na jednym instrumencie. Znalazłem rozwiązanie dla OS X bu Potrzebuję rozwiązania dla iOS. Czy muszę utworzyć dla każdego instrumentu audioUnit za pomocą kAudioUnitSubType_Sampler?Odtwórz plik MIDI z wieloma instrumentami IOS

Pytanie Czy możliwa jest zmiana instrumentu na wybranym kanale w czasie rzeczywistym? Jak można go wdrożyć? Przepraszam za mój angielski ((

Oto mój kod, to nie działa prawidłowo..

// Create a client 
MIDIClientRef virtualMidi; 
Check(MIDIClientCreate(CFSTR("Virtual Client"), 
         MyMIDINotifyProc, 
         NULL, 
         &virtualMidi)); 


// Create an endpoint 
MIDIEndpointRef virtualEndpoint; 
Check(MIDIDestinationCreate(virtualMidi, CFSTR("Virtual Destination"), MyMIDIReadProc, samplerUnit, &virtualEndpoint)); 

// Initialise the music sequence 
NewMusicSequence(&midiSequence); 

if (!midiFilePath) { 
    midiFilePath = [[NSBundle mainBundle] 
        pathForResource:@"carelesswhisper" 
        ofType:@"mid"]; 

} 
NSLog(@"midiFilePath %@", midiFilePath); 

// Create a new URL which points to the MIDI file 
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath]; 

MidiParser *midiParser = [[MidiParser alloc] init]; 
NSData *data = [NSData dataWithContentsOfFile:midiFilePath]; 
[midiParser parseData:data]; 
NSString *midiInfo = [midiParser log]; 
NSLog(@"midiInfo %@", midiInfo); 

MusicSequenceLoadFlags loadFlags = 0; 
loadFlags = kMusicSequenceLoadSMF_ChannelsToTracks; 
MusicSequenceFileLoad(midiSequence, (__bridge CFURLRef) midiFileURL, 0, loadFlags); 

// Initialise the music player 
NewMusicPlayer(&midiPlayer); 

// ************* Set the endpoint of the sequence to be our virtual endpoint 
MusicSequenceSetMIDIEndpoint(midiSequence, virtualEndpoint); 

if (!soundBankFilePath) { 
    soundBankFilePath = [[NSBundle mainBundle] pathForResource:@"SGM-V2.01-1" ofType:@"sf2"]; 
} 
NSLog(@"soundBankFilePath %@", soundBankFilePath); 

NSURL *presetURL = [NSURL fileURLWithPath:soundBankFilePath]; 

// Initialise the sound font 
AUSamplerInstrumentData bpdata; 
bpdata.fileURL = (__bridge CFURLRef) presetURL; 
bpdata.bankMSB = kAUSampler_DefaultMelodicBankMSB; 
bpdata.bankLSB = kAUSampler_DefaultBankLSB; 
bpdata.instrumentType = kInstrumentType_SF2Preset; 

// set the kAUSamplerProperty_LoadPresetFromBank property 
result = AudioUnitSetProperty(samplerUnit, 
           kAUSamplerProperty_LoadInstrument, 
           kAudioUnitScope_Global, 
           0, 
           &bpdata, 
           sizeof(bpdata)); 
MusicPlayerSetSequence(midiPlayer, midiSequence); 
// Called to do some MusicPlayer setup. This just 
// reduces latency when MusicPlayerStart is called 
// MusicPlayerPreroll(midiPlayer); 
// Starts the music playing 
MusicPlayerStart(midiPlayer); 

// Get length of track so that we know how long to kill time for 
MusicTrack track; 
MusicTimeStamp len; 
UInt32 sz = sizeof(MusicTimeStamp); 
MusicSequenceGetIndTrack(midiSequence, 1, &track); 
MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, &len, &sz); 


while (1) { // kill time until the music is over 
    usleep (3 * 1000 * 1000); 
    MusicTimeStamp now = 0; 
    MusicPlayerGetTime (midiPlayer, &now); 
    if (now >= len) 
     break; 
} 

Odpowiedz

3

znalazłem odpowiedź dla każdego utworu w kolejności wymaga oddzielnego AUSampler

edycja: 1 października 2016. Przepraszam za długą odpowiedź.

tam jest mój kod do pliku odtwarzanie midi.

- (void)loadMidi:(NSString*)midiFilePath andSoundBank:(NSString*)soundBankFilePath { 

[self setupStereoStreamFormat]; 
[self createGraph]; 
Check(AUGraphInitialize (graph)); 
Check(AUGraphStart (graph)); 

// get URL midi file 
if (!midiFilePath) { 
    midiFilePath = [[NSBundle mainBundle] pathForResource:@"Ресницы" ofType:@"kar"]; 
} 
NSLog(@"midiFilePath %@", midiFilePath); 
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath]; 

// get URL SFbank 
if (!soundBankFilePath) { 
    soundBankFilePath = [[NSBundle mainBundle] pathForResource:@"SGM-V2.01-1" ofType:@"sf2"]; 
} 
NSLog(@"soundBankFilePath %@", soundBankFilePath); 
bankUrl = [NSURL fileURLWithPath:soundBankFilePath]; 

// create sequence from midi 
sequence = 0; 

Check(NewMusicSequence(&sequence)); 
Check(MusicSequenceFileLoad (sequence, (__bridge CFURLRef)(midiFileURL), 0, 0)); 

MidiParser *parser = [[MidiParser alloc] init]; 
[parser parseData:[NSData dataWithContentsOfFile:midiFilePath]]; 
NSLog(@"PARSE MIDI %@", [parser log]); 
metaLyrics = [[NSMutableArray alloc] initWithArray:[parser syllableArray]]; 

// do not delete set sequense to graph 
Check(MusicSequenceSetAUGraph(sequence, graph)); 

[self getMidiNodesArray]; 

// read each track and set instruments & effects & volume for AUSamplers 
[self parseSequence]; 

CAShow(sequence); 
MusicTrack tempoTrack; 
MusicSequenceGetTempoTrack(sequence, &tempoTrack); 
NSDictionary *infoDict = (__bridge NSDictionary *)(MusicSequenceGetInfoDictionary(sequence)); 
float tempo = [[infoDict valueForKey:@"tempo"] floatValue]; 

CAShow(tempoTrack); 

NSLog(@"Tempo in sequence %f", tempo); 

// Load the sequence into the music player 
Check(NewMusicPlayer (&player)); 
// setup speed player 
Check(MusicPlayerSetPlayRateScalar(player, 1)); 
Check(MusicPlayerSetSequence(player, sequence)); 
Check(MusicPlayerSetTime(player, 0)); 
MusicPlayerPreroll(player); 
} 

więc analizować midi kod Utwory:

- (void) parseSequence { 
// get numbers of tracks 
UInt32 numTracks; 
Check(MusicSequenceGetTrackCount(sequence, &numTracks)); 
NSLog(@"Number of tarcks %d", (unsigned int)numTracks); 

// mute some tracks if needed 
NSSet *mutedTracks = [NSSet setWithObjects: @"11", nil]; 

// mute unused channels 
NSLog(@"LOADING TRACKS"); 
for (UInt32 i = 0; i < numTracks; ++i) { 
    MusicTrack track; 
    MusicTimeStamp trackLength; 
    UInt32 propsize = sizeof(MusicTimeStamp); 
    Check(MusicSequenceGetIndTrack(sequence, i, &track)); 
    Check(MusicTrackGetProperty(track, kSequenceTrackProperty_TrackLength, 
           &trackLength, &propsize)); 
    // log track info if needed 
    CAShow(track); 
    MusicEventIterator myIterator; 
    MusicTimeStamp timeStamp; 
    MusicEventType eventType; 
    const void *refData = 0; 
    UInt32 dataSize; 

    Check(NewMusicEventIterator(track, &myIterator)); 
    Boolean hasCurrentEvent; 
    Check(MusicEventIteratorHasCurrentEvent (myIterator, &hasCurrentEvent)); 
    NSMutableSet *instrumentsSet = [[NSMutableSet alloc] init]; 
    int noActions = 0; 

    while (hasCurrentEvent) { 
     MusicEventIteratorGetEventInfo(myIterator, &timeStamp, &eventType, &refData, &dataSize); 


     if (eventType == 7) { 
      NSData *dataChaunk = [[NSData alloc] initWithBytes:refData length:dataSize]; 
      void *channelByte_0 = 0; 
      void *channelByte_1 = 0; 
      void *channelByte_2 = 0; 
      void *channelByte_3 = 0; 

      [dataChaunk getBytes:&channelByte_0 range:NSMakeRange(0, 1)]; 
      [dataChaunk getBytes:&channelByte_1 range:NSMakeRange(1, 1)]; 
      [dataChaunk getBytes:&channelByte_2 range:NSMakeRange(2, 1)]; 
      [dataChaunk getBytes:&channelByte_3 range:NSMakeRange(3, 1)]; 
      Byte command = (int)channelByte_0; 


      if (command < 208 && command >= 192) { 
       // setup track on AUsampler 
       if (![instrumentsSet containsObject:[NSString stringWithFormat:@"%d",(command & 0xf)]]) { 
        [self setDestNode:(command & 0xf) forTrack:track]; 
        [self setInstrumentMSB:(int)channelByte_1 presetLSB:0 trackID:(command & 0xf)]; 
       } 
       [instrumentsSet addObject:[NSString stringWithFormat:@"%d",(command & 0xf)]]; 

      } else if (command <192 && command >= 176){ 
       switch ((NSInteger)channelByte_1) { 
        case 0: // bank select MSB 
         NSLog(@"CHANNEL %d CONTROLLER 0xB bankMSB value %d", command & 0xf, (int)channelByte_2); 
         break; 
        case 7: // chanell volume 
         [self setVolume:(int)channelByte_2 inChannel:(command & 0xf)]; 
         break; 
        case 10: // pan 
         [self setPan:(int)channelByte_2 inChannel:(command & 0xf)]; 
         break; 
        case 32: // bank select LSB 
         NSLog(@"CHANNEL %d CONTROLLER 0xB bankLSB value %d", command & 0xf, (int)channelByte_2); 
         break; 
        case 94: 
         NSLog(@"CHANNEL %d CONTROLLER 0xB setEffect value %d", command & 0xf, (int)channelByte_2); 
         break; 


        default: 
         break; 
       } 
      } else 

       noActions++; 
     } 
     // do work here 
     MusicEventIteratorNextEvent (myIterator); 
     MusicEventIteratorHasCurrentEvent (myIterator, &hasCurrentEvent); 
    } 
    NSLog(@"No actions count %d", noActions); 


    if ([mutedTracks count] > 0 && [mutedTracks containsObject:[NSString stringWithFormat:@"%d", (unsigned int)i]]) 
    { 
     Boolean mute = true; 
     Check(MusicTrackSetProperty(track, kSequenceTrackProperty_MuteStatus, &mute, sizeof(mute))); 
     printf ("played tracks %u\n", (unsigned int)i); 
    } 
    instrumentsSet = nil; 
} 


UInt32 nodeInd = [[midiNodesArray objectAtIndex:9] intValue]; 
NSLog(@"setPercussionBankMSB %d for %d track %d node", 1, 9, (unsigned int)nodeInd); 
AUNode node; 
AudioUnit unit; 
Check(AUGraphGetIndNode(graph, nodeInd, &node)); 
Check(AUGraphNodeInfo(graph, node, 0, &unit)); 


AUSamplerInstrumentData bpdata; 
bpdata.fileURL = (__bridge CFURLRef) bankUrl; 
bpdata.bankMSB = kAUSampler_DefaultPercussionBankMSB; 
bpdata.bankLSB = kAUSampler_DefaultBankLSB; 
bpdata.instrumentType = kInstrumentType_SF2Preset; 
bpdata.presetID = (UInt8) 1; 
Check(AudioUnitSetProperty (unit, 
          kAUSamplerProperty_LoadInstrument, 
          kAudioUnitScope_Global, 0, 
          &bpdata, sizeof(bpdata))); 

[self setVolume:20 inChannel:0]; 
} 

Get midi utwory z wykresu:

- (void) getMidiNodesArray { 
UInt32 nodeCount; 
Check(AUGraphGetNodeCount (graph, &nodeCount)); 
AudioUnit outSynth; 

if (!midiNodesArray) { 
    midiNodesArray = [[NSMutableArray alloc] init]; 
} 

for (UInt32 i = 0; i < nodeCount; ++i) 
{ 
    AUNode node; 
    Check(AUGraphGetIndNode(graph, i, &node)); 

    AudioComponentDescription desc; 
    Check(AUGraphNodeInfo(graph, node, &desc, 0)); 

    if (desc.componentSubType == kAudioUnitSubType_Sampler) { 
     Check(AUGraphNodeInfo(graph, node, 0, &outSynth)); 
     [midiNodesArray addObject:[NSString stringWithFormat:@"%d", (unsigned int)i]]; 
    } 
} 
} 
+0

Witam, tak jak nie udało się rozdzielić ścieżki i przypisać każdy do innego AUSampler ?? Próbuję to rozgryźć w ciągu ostatnich godzin> _ < – Hazneliel

+0

Utwórz żądaną liczbę instrumentów midi -> AudioUnit midiUnit_0, midiUnit_1, midiUnit_2, midiUnit_3, midiUnit_4, midiUnit_5; Skonfiguruj wykres -> Sprawdź (AUGraphConnectNodeInput (wykres, midiNode_0, 0, mixerNode, 0)); Sprawdź (AUGraphConnectNodeInput (wykres, midiNode_1, 0, mixerNode, 1)); Sprawdź (AUGraphConnectNodeInput (wykres, midiNode_2, 0, mixerNode, 2)); Sprawdź (AUGraphConnectNodeInput (wykres, midiNode_3, 0, mixerNode, 3)); Sprawdź (AUGraphConnectNodeInput (wykres, midiNode_4, 0, mixerNode, 4)); Sprawdź (AUGraphConnectNodeInput (wykres, midiNode_5, 0, mixerNode, 5)); – John

+0

Witam, John Próbuję zrobić to samo, ale bez powodzenia. Czy możesz pomóc, podając resztę kodu, na przykład metody "NewMusicSequence" i "NewMusicPlayer". Wielkie dzięki –