2013-06-04 21 views
14

enter image description hereCome generare una forma d'onda audio programmaticamente durante la registrazione di Voice in iOS?

Come generare la forma d'onda audio programmaticamente durante la registrazione di Voice in iOS?

m lavorando su frequenze audio modulazione della voce in iOS ... tutto funziona bene ... solo bisogno di un po 'di più semplice modo per generare forma d'onda audio su rumore di rilevamento ...

Please non mi rimanda il codice tutorial di ... speakhere e auriotouch ... ho bisogno di alcuni suggerimenti migliori da sviluppatori di app native.

Ho registrato l'audio e l'ho fatto riprodurre dopo la registrazione. Ho creato una forma d'onda e uno screenshot allegato. Ma deve stato redatto nella visualizzazione, come la registrazione audio in corso

-(UIImage *) audioImageGraph:(SInt16 *) samples 
       normalizeMax:(SInt16) normalizeMax 
       sampleCount:(NSInteger) sampleCount 
       channelCount:(NSInteger) channelCount 
       imageHeight:(float) imageHeight { 

    CGSize imageSize = CGSizeMake(sampleCount, imageHeight); 
    UIGraphicsBeginImageContext(imageSize); 
    CGContextRef context = UIGraphicsGetCurrentContext(); 

    CGContextSetFillColorWithColor(context, [UIColor blackColor].CGColor); 
    CGContextSetAlpha(context,1.0); 
    CGRect rect; 
    rect.size = imageSize; 
    rect.origin.x = 0; 
    rect.origin.y = 0; 

    CGColorRef leftcolor = [[UIColor whiteColor] CGColor]; 
    CGColorRef rightcolor = [[UIColor redColor] CGColor]; 

    CGContextFillRect(context, rect); 

    CGContextSetLineWidth(context, 1.0); 

    float halfGraphHeight = (imageHeight/2)/(float) channelCount ; 
    float centerLeft = halfGraphHeight; 
    float centerRight = (halfGraphHeight*3) ; 
    float sampleAdjustmentFactor = (imageHeight/ (float) channelCount)/(float) normalizeMax; 

    for (NSInteger intSample = 0 ; intSample < sampleCount ; intSample ++) { 
     SInt16 left = *samples++; 
     float pixels = (float) left; 
     pixels *= sampleAdjustmentFactor; 
     CGContextMoveToPoint(context, intSample, centerLeft-pixels); 
     CGContextAddLineToPoint(context, intSample, centerLeft+pixels); 
     CGContextSetStrokeColorWithColor(context, leftcolor); 
     CGContextStrokePath(context); 

     if (channelCount==2) { 
      SInt16 right = *samples++; 
      float pixels = (float) right; 
      pixels *= sampleAdjustmentFactor; 
      CGContextMoveToPoint(context, intSample, centerRight - pixels); 
      CGContextAddLineToPoint(context, intSample, centerRight + pixels); 
      CGContextSetStrokeColorWithColor(context, rightcolor); 
      CGContextStrokePath(context); 
     } 
    } 

    // Create new image 
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); 

    // Tidy up 
    UIGraphicsEndImageContext(); 

    return newImage; 
} 

Avanti un metodo che prende un AVURLAsset, e restituisce PNG dati

- (NSData *) renderPNGAudioPictogramForAssett:(AVURLAsset *)songAsset { 

    NSError * error = nil; 


    AVAssetReader * reader = [[AVAssetReader alloc] initWithAsset:songAsset error:&error]; 

    AVAssetTrack * songTrack = [songAsset.tracks objectAtIndex:0]; 

    NSDictionary* outputSettingsDict = [[NSDictionary alloc] initWithObjectsAndKeys: 

             [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey, 
             //  [NSNumber numberWithInt:44100.0],AVSampleRateKey, /*Not Supported*/ 
             //  [NSNumber numberWithInt: 2],AVNumberOfChannelsKey, /*Not Supported*/ 

             [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsNonInterleaved, 

             nil]; 


    AVAssetReaderTrackOutput* output = [[AVAssetReaderTrackOutput alloc] initWithTrack:songTrack outputSettings:outputSettingsDict]; 

    [reader addOutput:output]; 
    [output release]; 

    UInt32 sampleRate,channelCount; 

    NSArray* formatDesc = songTrack.formatDescriptions; 
    for(unsigned int i = 0; i < [formatDesc count]; ++i) { 
     CMAudioFormatDescriptionRef item = (CMAudioFormatDescriptionRef)[formatDesc objectAtIndex:i]; 
     const AudioStreamBasicDescription* fmtDesc = CMAudioFormatDescriptionGetStreamBasicDescription (item); 
     if(fmtDesc) { 

      sampleRate = fmtDesc->mSampleRate; 
      channelCount = fmtDesc->mChannelsPerFrame; 

      // NSLog(@"channels:%u, bytes/packet: %u, sampleRate %f",fmtDesc->mChannelsPerFrame, fmtDesc->mBytesPerPacket,fmtDesc->mSampleRate); 
     } 
    } 


    UInt32 bytesPerSample = 2 * channelCount; 
    SInt16 normalizeMax = 0; 

    NSMutableData * fullSongData = [[NSMutableData alloc] init]; 
    [reader startReading]; 


    UInt64 totalBytes = 0; 


    SInt64 totalLeft = 0; 
    SInt64 totalRight = 0; 
    NSInteger sampleTally = 0; 

    NSInteger samplesPerPixel = sampleRate/50; 


    while (reader.status == AVAssetReaderStatusReading){ 

     AVAssetReaderTrackOutput * trackOutput = (AVAssetReaderTrackOutput *)[reader.outputs objectAtIndex:0]; 
     CMSampleBufferRef sampleBufferRef = [trackOutput copyNextSampleBuffer]; 

     if (sampleBufferRef){ 
      CMBlockBufferRef blockBufferRef = CMSampleBufferGetDataBuffer(sampleBufferRef); 

      size_t length = CMBlockBufferGetDataLength(blockBufferRef); 
      totalBytes += length; 


      NSAutoreleasePool *wader = [[NSAutoreleasePool alloc] init]; 

      NSMutableData * data = [NSMutableData dataWithLength:length]; 
      CMBlockBufferCopyDataBytes(blockBufferRef, 0, length, data.mutableBytes); 


      SInt16 * samples = (SInt16 *) data.mutableBytes; 
      int sampleCount = length/bytesPerSample; 
      for (int i = 0; i < sampleCount ; i ++) { 

       SInt16 left = *samples++; 

       totalLeft += left; 



       SInt16 right; 
       if (channelCount==2) { 
        right = *samples++; 

        totalRight += right; 
       } 

       sampleTally++; 

       if (sampleTally > samplesPerPixel) { 

        left = totalLeft/sampleTally; 

        SInt16 fix = abs(left); 
        if (fix > normalizeMax) { 
         normalizeMax = fix; 
        } 


        [fullSongData appendBytes:&left length:sizeof(left)]; 

        if (channelCount==2) { 
         right = totalRight/sampleTally; 


         SInt16 fix = abs(right); 
         if (fix > normalizeMax) { 
          normalizeMax = fix; 
         } 


         [fullSongData appendBytes:&right length:sizeof(right)]; 
        } 

        totalLeft = 0; 
        totalRight = 0; 
        sampleTally = 0; 

       } 
      } 



      [wader drain]; 


      CMSampleBufferInvalidate(sampleBufferRef); 

      CFRelease(sampleBufferRef); 
     } 
    } 


    NSData * finalData = nil; 

    if (reader.status == AVAssetReaderStatusFailed || reader.status == AVAssetReaderStatusUnknown){ 
     // Something went wrong. return nil 

     return nil; 
    } 

    if (reader.status == AVAssetReaderStatusCompleted){ 

     NSLog(@"rendering output graphics using normalizeMax %d",normalizeMax); 

     UIImage *test = [self audioImageGraph:(SInt16 *) 
         fullSongData.bytes 
           normalizeMax:normalizeMax 
            sampleCount:fullSongData.length/4 
           channelCount:2 
            imageHeight:100]; 

     finalData = imageToData(test); 
    } 




    [fullSongData release]; 
    [reader release]; 

    return finalData; 
} 

ho

+0

dai un'occhiata a questo, questo potrebbe essere utile. http://developer.apple.com/library/ios/#samplecode/aurioTouch2/Introduction/Intro.html –

+0

Se hai un problema specifico nel raggiungerlo, è facile ricevere aiuto. Ma "Voglio forma d'onda" fa sì che le persone puntino ad alcuni campioni standard. – Vignesh

+0

@Vignesh: Ho allegato lo screenshot alla domanda che ho posto. Questo è il modo in cui ho bisogno dell'output che deve essere disegnato in quell'istante come la registrazione audio in corso. Grazie – iVenky

risposta

7

Se volete reale -time grafica derivata dall'ingresso microfonico, quindi utilizzare l'unità audio RemoteIO, che è quella utilizzata dalla maggior parte degli sviluppatori di app iOS nativi per l'audio a bassa latenza, e Metal o Open GL per disegnare forme d'onda, che offrono i frame rate più elevati. Avrai bisogno di un codice completamente diverso da quello fornito nella tua domanda per farlo, dato che AVAssetRecording, il disegno grafico Core Core e il rendering png sono troppo lenti per essere utilizzati.

Aggiornamento: con IOS 8 e successive, l'API metallo può essere in grado di rendere visualizzazioni grafiche con maggior rendimento di OpenGL.

Uodate 2: Ecco alcuni frammenti di codice per la registrazione audio dal vivo utilizzando Audio Units e disegnare mappe bit utilizzando metallo a Swift 3: https://gist.github.com/hotpaw2/f108a3c785c7287293d7e1e81390c20b

+1

Sarà utile se è possibile condividere alcuni snippet di codice per ottenere grafici in tempo reale utilizzando openGL per disegnare forme d'onda. Grazie mille per il tuo supporto – iVenky

+0

Se questo è iOS, utilizzerai le openGLes. Avete una scelta tra fisso (ES1) o shader pipeline (ES2). Non so se ci sono dei vantaggi nell'usare gli shader per disegnare questo tipo di cose. L'esempio di app per aurio aurioTouch ha un esempio di come disegnare i buffer di breve durata dal microfono in openGL. Se ricordo correttamente in questo esempio, disegnano un numero uguale di campioni in pixel. Tuttavia, per disegnare l'intera forma d'onda di una canzone più lunga (milioni di campioni), dovresti ridimensionare per adattare i campioni disegnati allo schermo. Dovresti anche usare qualche tipo di calcolo di picco o RMS. – omygaudio

7

si dovrebbe verificare EZAudio (https://github.com/syedhali/EZAudio), in particolare l'EZRecorder e l'EZAudioPlot (o EZAudioPlotGL accelerato dalla GPU).

C'è anche un progetto di esempio che fa esattamente ciò che si vuole, https://github.com/syedhali/EZAudio/tree/master/EZAudioExamples/iOS/EZAudioRecordExample

EDIT: Ecco il codice inline

/// In your interface 

/** 
Use a OpenGL based plot to visualize the data coming in 
*/ 
@property (nonatomic,weak) IBOutlet EZAudioPlotGL *audioPlot; 
/** 
The microphone component 
*/ 
@property (nonatomic,strong) EZMicrophone *microphone; 
/** 
The recorder component 
*/ 
@property (nonatomic,strong) EZRecorder *recorder; 

... 

/// In your implementation 

// Create an instance of the microphone and tell it to use this view controller instance as the delegate 
-(void)viewDidLoad { 
    self.microphone = [EZMicrophone microphoneWithDelegate:self startsImmediately:YES]; 
} 

// EZMicrophoneDelegate will provide these callbacks 
-(void)microphone:(EZMicrophone *)microphone 
hasAudioReceived:(float **)buffer 
    withBufferSize:(UInt32)bufferSize 
withNumberOfChannels:(UInt32)numberOfChannels { 
    dispatch_async(dispatch_get_main_queue(),^{ 
    // Updates the audio plot with the waveform data 
    [self.audioPlot updateBuffer:buffer[0] withBufferSize:bufferSize]; 
    }); 
} 

-(void)microphone:(EZMicrophone *)microphone hasAudioStreamBasicDescription:(AudioStreamBasicDescription)audioStreamBasicDescription { 
    // The AudioStreamBasicDescription of the microphone stream. This is useful when configuring the EZRecorder or telling another component what audio format type to expect. 

    // We can initialize the recorder with this ASBD 
    self.recorder = [EZRecorder recorderWithDestinationURL:[self testFilePathURL] 
             andSourceFormat:audioStreamBasicDescription]; 

} 

-(void)microphone:(EZMicrophone *)microphone 
    hasBufferList:(AudioBufferList *)bufferList 
    withBufferSize:(UInt32)bufferSize 
withNumberOfChannels:(UInt32)numberOfChannels { 

    // Getting audio data as a buffer list that can be directly fed into the EZRecorder. This is happening on the audio thread - any UI updating needs a GCD main queue block. This will keep appending data to the tail of the audio file. 
    if(self.isRecording){ 
    [self.recorder appendDataFromBufferList:bufferList 
          withBufferSize:bufferSize]; 
    } 

} 
+1

hey .. dovresti ** non ** postare solo le risposte al link – kleopatra

+1

scusami per il fatto che ... modifico la risposta –

1

stavo cercando la stessa cosa. (Fare ondate dai dati del registratore audio). Ho trovato una libreria che potrebbe essere utile e vale la pena di controllare il codice per comprendere la logica alla base di questo.

Il calcolo è tutto basato sulla formula del peccato e matematica. Questo è molto semplice se dai un'occhiata al codice!

https://github.com/stefanceriu/SCSiriWaveformView

o

https://github.com/raffael/SISinusWaveView

Questo è solo alcuni esempi che si possono trovare sul web.