2013-06-04 10 views
14

enter image description hereJak programowo generować falę dźwiękową podczas nagrywania głosu w systemie iOS?

Jak programowo generować falę dźwiękową podczas nagrywania głosu w systemie iOS?

m pracujących na częstotliwości dźwięku modulacji głosu w iOS ... wszystko działa poprawnie ... po prostu potrzebuję trochę najlepszą prosty sposób generowania kształtu fali dźwiękowej na hałas wykrywania ...

Proszę nie patrz mi kod tutoriale ... speakhere i auriotouch ... potrzebuję najlepszych wskazówek od twórców aplikacji natywnych.

Po nagraniu nagrałem dźwięk i nagrałem go. Stworzyłem przebieg i załączony zrzut ekranu. Ale musi to zostało sporządzone w widoku jako nagrania audio w toku

-(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; 
} 

Następny metoda, która zajmuje AVURLAsset i zwraca PNG danych

- (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; 
} 

mam

+0

Sprawdź to, może to być pomocne. http://developer.apple.com/library/ios/#samplecode/aurioTouch2/Introduction/Intro.html –

+0

Jeśli masz konkretny problem z uzyskaniem go, łatwo jest uzyskać pomoc. "Chcę formularza fali" sprawia, że ​​ludzie wskazują niektóre standardowe próbki. – Vignesh

+0

@Vignesh: Załączam zrzut ekranu w pytaniu, które zadałem. W ten sposób potrzebuję wyjścia, które zostało narysowane w tym momencie jako trwające nagranie audio. Dzięki – iVenky

Odpowiedz

7

Jeśli chcesz rzeczywistym -czasowa grafika pochodząca z wejścia mikrofonowego, a następnie użyj urządzenia RemoteIO Audio Unit, które jest używane przez większość natywnych twórców aplikacji dla systemu iOS dla dźwięku o niskim opóźnieniu, oraz Metal lub Open GL do rysowania przebiegów, co zapewni najwyższą liczbę klatek na sekundę. Będziesz potrzebował zupełnie innego kodu niż ten podany w pytaniu, ponieważ AVAssetRecording, rysowanie linii Core Graphic i png są o wiele za wolne.

Aktualizacja: z iOS 8 i nowszymi, Metal API może być w stanie renderować graficzne wizualizacje z jeszcze większą wydajnością niż OpenGL.

Uodate 2: Oto niektóre fragmenty kodu do nagrywania audio na żywo przy użyciu urządzeń audio i rysunek bitowe mapy za pomocą metalowych w Swift 3: https://gist.github.com/hotpaw2/f108a3c785c7287293d7e1e81390c20b

+1

Pomocne będzie udostępnienie fragmentów kodu w celu uzyskania grafiki w czasie rzeczywistym z wykorzystaniem technologii OpenGL do rysowania przebiegów. Wielkie dzięki za wsparcie – iVenky

+0

Jeśli to system iOS, użyjesz OpenGL. Masz wybór pomiędzy ustalonym (ES1) lub cieniowanym potokiem (ES2). Nie wiem, czy korzystanie z shaderów ma jednak zalety, aby narysować takie rzeczy. Przykład aplikacji na iOS aurioTouch ma przykład, jak rysować krótkie długości buforów z mikrofonu w openGL. Jeśli poprawnie przywołam w tym przykładzie, narysują taką samą liczbę próbek do pikseli. Aby narysować cały przebieg dłuższego utworu (miliony próbek), musisz jednak skalować, aby pasował do narysowanych sampli do ekranu. Powinieneś także użyć jakiegoś obliczenia szczytowego lub RMS. – omygaudio

7

należy sprawdzić EZAudio (https://github.com/syedhali/EZAudio), a konkretnie EZRecorder i EZAudioPlot (lub przyspieszany przez GPU EZAudioPlotGL).

Istnieje również projekt przykład, że robi dokładnie to, co chcesz, https://github.com/syedhali/EZAudio/tree/master/EZAudioExamples/iOS/EZAudioRecordExample

EDIT: Tutaj jest inline kod

/// 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

hej .. nie powinieneś ** nie ** zamieszczać odpowiedzi tylko na link – kleopatra

+1

Przepraszam za to ... będę edytować odpowiedź –

Powiązane problemy