Pergunta

Estou procurando uma maneira de mixar 2 ou mais arquivos midi, cada um com seus próprios arquivos de fonte de som.Encontrei o código a seguir para um arquivo e tentei usar vários reprodutores de música, mas acho que essa não deveria ser a abordagem correta.Também ouço um som pop estranho a cada segundo.

Então, existe alguma outra maneira, talvez sem os métodos musicplayer e musicsequence, usando apenas unidades au?

Aqui está o código que encontrei em outro tópico:

-(void) playMusic:(NSString*) name
{
NSString *presetURLPath = [[NSBundle mainBundle] pathForResource:@"GortsMiniPianoJ1" ofType:@"SF2"];
NSURL * presetURL = [NSURL fileURLWithPath:presetURLPath]; 
[self loadFromDLSOrSoundFont: (NSURL *)presetURL withPatch: (int)3];

NSString *midiFilePath = [[NSBundle mainBundle] pathForResource:name ofType:@"mid"];
NSURL * midiFileURL = [NSURL fileURLWithPath:midiFilePath];

NewMusicPlayer(&musicPlayer);

if (NewMusicSequence(&musicSequence) != noErr) 
{
    [NSException raise:@"play" format:@"Can't create MusicSequence"];  
}

if(MusicSequenceFileLoad(musicSequence, (CFURLRef)midiFileURL, 0, 0 != noErr)) 
{
    [NSException raise:@"play" format:@"Can't load MusicSequence"];
}

MusicPlayerSetSequence(musicPlayer, musicSequence);
MusicSequenceSetAUGraph(musicSequence, _processingGraph);
MusicPlayerPreroll(musicPlayer);
MusicPlayerStart(musicPlayer);
}
Foi útil?

Solução

Múltiplas instâncias do MusicPlayer parecem um pouco estranhas (para manter a sincronia) e tenho dúvidas de que vários AUGraphs funcionariam.Eu não tentei sozinho.

Uma abordagem usando uma única instância de MusicPlayer seria carregar as faixas de cada arquivo midi em uma sequência 'master'.Você pode atribuir um nó AUSampler (com soundFont exclusivo) a cada trilha (MusicTrackSetDestNode) e conecte-os todos através de um AUMixer.Você precisaria gerenciar os tempos de cada arquivo midi (usando MusicTrackNewExtendedTempoEvent) para atribuir tempos de arquivo às faixas relevantes.Silenciar trilhas + volume da trilha é facilmente controlado no nível da trilha ou do mixer.

Acho que depende muito da natureza dos arquivos midi com os quais você está trabalhando.Fwiw - fiz muitas pesquisas sobre reprodução de arquivos midi no iOS e não há nada tão fácil de usar quanto o MusicPlayer.No entanto, o Biblioteca de baixo pode valer a pena dar uma olhada se o MusicPlayer não funcionar para você.

Especificando o número de entradas do mixer:

// set the bus count
UInt32 numBuses = BUS_COUNT; // a constant defined elsewhere
result = AudioUnitSetProperty(mixerUnit, 
                              kAudioUnitProperty_ElementCount, 
                              kAudioUnitScope_Input, 
                              0, 
                              &numBuses, 
                              sizeof(numBuses));

if (noErr != result) {[self printErrorMessage: @"Error setting Bus Count" withStatus: result]; return;}

Outras dicas

Eu tive exatamente o mesmo problema que você.Todas as faixas tocam com o primeiro instrumento de fonte sonora.Segui sua solução, mas não funcionou a princípio.Finalmente, resolvo o problema.Como você mencionou, a sequência de chamadas de funções realmente importa.É sim.Na verdade, a chamada da sequência deveria ser assim:

.....
MusicSequenceSetAUGraph(s, _processingGraph);
.......
MusicTrackSetDestNode(track[i], samplerNodes[i]);
......
[self loadFromDLSOrSoundFont];
......
MusicPlayerStart(p);

Isso funciona no meu projeto.

Então tentei definir os nós para cada trilha, mas isso não mudou nada.A fonte sonora é definida apenas para o primeiro samplerUnit.Veja como configuro o gráfico:

AudioComponentDescription MixerUnitDescription;
MixerUnitDescription.componentType          = kAudioUnitType_Mixer;
MixerUnitDescription.componentSubType       = kAudioUnitSubType_MultiChannelMixer;
MixerUnitDescription.componentManufacturer  = kAudioUnitManufacturer_Apple;
MixerUnitDescription.componentFlags         = 0;
MixerUnitDescription.componentFlagsMask     = 0;

AudioComponentDescription cd = {};
cd.componentManufacturer     = kAudioUnitManufacturer_Apple;
cd.componentType = kAudioUnitType_MusicDevice; // type - music device
cd.componentSubType = kAudioUnitSubType_Sampler; // sub type - sampler to convert our MIDI

result = NewAUGraph (&_processingGraph);
result = AUGraphAddNode(self.processingGraph, &MixerUnitDescription, &mixerNode);

result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode);

result = AUGraphAddNode (self.processingGraph, &cd, &samplerNode2);

cd.componentType = kAudioUnitType_Output;  // Output
cd.componentSubType = kAudioUnitSubType_RemoteIO;  // Output to speakers

result = AUGraphAddNode (self.processingGraph, &cd, &ioNode);
result = AUGraphOpen (self.processingGraph);

result = AUGraphConnectNodeInput (self.processingGraph, samplerNode, 0, mixerNode, 0);
result = AUGraphConnectNodeInput (self.processingGraph, samplerNode2, 0, mixerNode, 1);

result = AUGraphConnectNodeInput (self.processingGraph, mixerNode, 0, ioNode, 0);
result = AUGraphNodeInfo (self.processingGraph, samplerNode, 0, &_samplerUnit);
result = AUGraphNodeInfo (self.processingGraph, samplerNode2, 0, &_samplerUnit2);
result = AUGraphNodeInfo (self.processingGraph, ioNode, 0, &_ioUnit);

Este é o método de exemplo das páginas de desenvolvedores da Apple que modifiquei para atribuir uma fonte de som a um específico samplerUnit:

-(OSStatus) loadFromDLSOrSoundFont: (NSURL *)bankURL withPatch: (int)presetNumber withAudioUnit:(AudioUnit)auUnit{

OSStatus result = noErr;

// fill out a bank preset data structure
AUSamplerBankPresetData bpdata;
bpdata.bankURL  = (__bridge CFURLRef) bankURL;
bpdata.bankMSB  = kAUSampler_DefaultMelodicBankMSB;
bpdata.bankLSB  = kAUSampler_DefaultBankLSB;
bpdata.presetID = (UInt8) presetNumber;

// set the kAUSamplerProperty_LoadPresetFromBank property
result = AudioUnitSetProperty(auUnit,
                          kAUSamplerProperty_LoadPresetFromBank,
                          kAudioUnitScope_Global,
                          0,
                          &bpdata,
                          sizeof(bpdata));

// check for errors
NSCAssert (result == noErr,
       @"Unable to set the preset property on the Sampler. Error code:%d '%.4s'",
       (int) result,
       (const char *)&result);

return result;
}

Então eu fiz isso duas vezes para inserir cada faixa musicSequence:

if(MusicSequenceFileLoad(tmpSequence, (__bridge CFURLRef)midiFileURL, 0, 0 != noErr)) 
{
    [NSException raise:@"play" format:@"Can't load MusicSequence"];
}


MusicSequenceGetIndTrack(tmpSequence, 0, &tmpTrack);
MusicSequenceNewTrack(musicSequence, &track);
MusicTimeStamp trackLen = 0;
UInt32 trackLenLen = sizeof(trackLen);
MusicTrackGetProperty(tmpTrack, kSequenceTrackProperty_TrackLength, &trackLen, &trackLenLen);
MusicTrackCopyInsert(tmpTrack, 0, trackLenLen, track, 0);

E finalmente:

MusicTrackSetDestNode(track, samplerNode);
MusicTrackSetDestNode(track2, samplerNode2);

Mas isso não atribuirá a fonte sonora ao samplerUnit2:

[self loadFromDLSOrSoundFont: (NSURL *)presetURL2 withPatch: (int)0 withAudioUnit:self.samplerUnit2];

Atribuindo a samplerUnit funciona bem.Alguma idéia do que estou perdendo aqui?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top