Sto costruendo un'applicazione per iPhone che genera musica di chitarra casuale riproducendo le note di chitarra registrate individualmente in formato "caf". Queste note variano in durata da 3 a 11 secondi, a seconda della quantità di sustain.Uso di Novocaine in un'app audio
Originariamente utilizzavo AVAudioPlayer per la riproduzione, e nel simulatore a 120 bpm, suonavo le note da 16 °, cantato magnificamente, ma sul mio telefono, non appena ho aumentato il tempo di poco superiore a 60 bpm riproducendo solo 1/4 osserva, correva come un cane e non voleva restare in tempo. La mia esaltazione è stata molto breve.
Per ridurre la latenza, ho provato a implementare la riproduzione tramite unità audio utilizzando il progetto Apple MixerHost come modello per un motore audio, ma continuavo a ricevere un errore di accesso errato dopo averlo spinto e collegato tutto.
Dopo molte ore di riflessione, ho rinunciato a quella linea di pensiero e ho optato invece per il motore audio Novocaine.
Ora mi sono imbattuto in un muro di mattoni cercando di collegarlo al mio modello.
Sul livello più elementare, il mio modello è un oggetto Neck contenente un oggetto NSDictionary of Note.
Ogni oggetto Note sa quale stringa e tasto del manico della chitarra è acceso e contiene il proprio AVAudioPlayer.
Costruisco un manico della chitarra cromatico contenente 122 note (6 corde per 22 tasti) o 144 note (6 corde per 24 tasti) a seconda della dimensione del collo selezionata nelle preferenze dell'utente.
Io uso queste note come il mio unico punto di verità in modo che tutte le note scalari generate dal motore musicale siano puntatori a questo secchio di note cromatiche.
@interface Note : NSObject <NSCopying>
{
NSString *name;
AVAudioPlayer *soundFilePlayer;
int stringNumber;
int fretNumber;
}
Parto sempre fuori la riproduzione con la radice nota o un accordo della scala selezionata e quindi generare la nota di giocare la prossima così io sono sempre giocando una nota dietro la nota generata. In questo modo, la prossima nota da giocare è sempre pronta per essere pronta.
il controllo della riproduzione di queste note è un ottenuta con il seguente codice:
- (void)runMusicGenerator:(NSNumber *)counter
{
if (self.isRunning) {
Note *NoteToPlay;
// pulseRate is the time interval between beats
// staticNoteLength = 1/4 notes, 1/8th notes, 16th notes, etc.
float delay = self.pulseRate/[self grabStaticNoteLength];
// user setting to play single, double or triplet notes.
if (self.beatCounter == CONST_BEAT_COUNTER_INIT_VAL) {
NoteToPlay = [self.GuitarNeck generateNoteToPlayNext];
} else {
NoteToPlay = [self.GuitarNeck cloneNote:self.GuitarNeck.NoteToPlayNow];
}
self.GuitarNeck.NoteToPlayNow = NoteToPlay;
[self callOutNoteToPlay];
[self performSelector:@selector(runDrill:) withObject:NoteToPlay afterDelay:delay];
}
- (Note *)generateNoteToPlayNext
{
if ((self.musicPaused) || (self.musicStopped)) {
// grab the root note on the string to resume
self.NoteToPlayNow = [self grabRootNoteForString];
//reset the flags
self.musicPaused = NO;
self.musicStopped = NO;
} else {
// Set NoteRingingOut to NoteToPlayNow
self.NoteRingingOut = self.NoteToPlayNow;
// Set NoteToPlaNowy to NoteToPlayNext
self.NoteToPlayNow = self.NoteToPlayNext;
if (!self.NoteToPlayNow) {
self.NoteToPlayNow = [self grabRootNoteForString];
// now prep the note's audio player for playback
[self.NoteToPlayNow.soundFilePlayer prepareToPlay];
}
}
// Load NoteToPlayNext
self.NoteToPlayNext = [self generateRandomNote];
}
- (void)callOutNoteToPlay
{
self.GuitarNeck.NoteToPlayNow.soundFilePlayer.delegate = (id)self;
[self.GuitarNeck.NoteToPlayNow.soundFilePlayer setVolume:1.0];
[self.GuitarNeck.NoteToPlayNow.soundFilePlayer setCurrentTime:0];
[self.GuitarNeck.NoteToPlayNow.soundFilePlayer play];
}
di ciascuna nota AVAudioPlayer viene caricato come segue:
- (AVAudioPlayer *)buildStringNotePlayer:(NSString *)nameOfNote
{
NSString *soundFileName = @"S";
soundFileName = [soundFileName stringByAppendingString:[NSString stringWithFormat:@"%d", stringNumber]];
soundFileName = [soundFileName stringByAppendingString:@"F"];
if (fretNumber < 10) {
soundFileName = [soundFileName stringByAppendingString:@"0"];
}
soundFileName = [soundFileName stringByAppendingString:[NSString stringWithFormat:@"%d", fretNumber]];
NSString *soundPath = [[NSBundle mainBundle] pathForResource:soundFileName ofType:@"caf"];
NSURL *fileURL = [NSURL fileURLWithPath:soundPath];
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:nil];
return notePlayer;
}
Qui è dove vengo un fiasco.
Secondo la pagina Novocaine Github ...
Riproduzione Audio
Novocaine *audioManager = [Novocaine audioManager];
[audioManager setOutputBlock:^(float *audioToPlay, UInt32 numSamples, UInt32 numChannels) {
// All you have to do is put your audio into "audioToPlay".
}];
Ma nel progetto scaricato, è possibile utilizzare il seguente codice per caricare l'audio ...
// AUDIO FILE READING OHHH YEAHHHH
// ========================================
NSURL *inputFileURL = [[NSBundle mainBundle] URLForResource:@"TLC" withExtension:@"mp3"];
fileReader = [[AudioFileReader alloc]
initWithAudioFileURL:inputFileURL
samplingRate:audioManager.samplingRate
numChannels:audioManager.numOutputChannels];
[fileReader play];
fileReader.currentTime = 30.0;
[audioManager setOutputBlock:^(float *data, UInt32 numFrames, UInt32 numChannels)
{
[fileReader retrieveFreshAudio:data numFrames:numFrames numChannels:numChannels];
NSLog(@"Time: %f", fileReader.currentTime);
}];
Qui è dove davvero inizio a essere confuso perché il primo metodo utilizza un float e il secondo utilizza un URL.
Come si passa un file "caf" a un float? Non sono sicuro di come implementare Novocaine - è ancora confusa nella mia testa.
Le mie domande che spero che qualcuno mi può aiutare con sono i seguenti ...
Are Novocaine oggetti simili agli oggetti AVAudioPlayer, solo più versatili e ottimizzato al massimo per una latenza minima? cioè unità di riproduzione audio (/ registrazione/generazione) autonoma?
Posso utilizzare Novocaine nel mio modello così com'è? vale a dire 1 oggetto di Novocaine per nota cromatica o dovrei avere 1 oggetto novocain che contiene tutte le note cromatiche? O semplicemente memorizzo l'URL nella nota e lo trasmetto a un giocatore di Novocaine?
Come posso mettere il mio audio in "audioToPlay" quando il mio audio è un file "caf" e "audioToPlay" prendere un float?
Se includo e dichiaro una proprietà Novocaine in Note.m, devo quindi rinominare la classe in Note.mm per utilizzare l'oggetto Novocaine?
Come si riproducono più oggetti Novocaine contemporaneamente per riprodurre accordi e intervalli?
Posso eseguire il loop della riproduzione di un oggetto Novocaine?
Posso impostare la lunghezza di riproduzione di una nota? Ad esempio, riprodurre una nota di 10 secondi per solo 1 secondo?
Posso modificare il codice sopra riportato per usare Novocaine?
Il metodo che sto utilizzando per runMusicGenerator è quello corretto da utilizzare per mantenere un tempo che sia conforme agli standard professionali?