Как программно сгенерировать форму звуковой волны при записи голоса в iOS?

введите описание изображения здесь

Как программно сгенерировать форму звуковой волны при записи голоса в iOS?

m работает над звуковой частотой модуляции голоса в iOS ... все работает нормально ... просто нужен лучший простой способ сгенерировать форму звуковой волны при обнаружении шума ...

Пожалуйста, не присылайте мне руководства по коду ... speakhere и auriotouch ... Мне нужны лучшие предложения от разработчиков нативных приложений.

Я записал звук и включил его после записи. Я создал осциллограмму и приложил снимок экрана. Но это должно быть отображено в виде, пока идет аудиозапись.

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

Затем метод, который принимает AVURLAsset и возвращает данные PNG.

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

у меня есть


person iVenky    schedule 04.06.2013    source источник
comment
проверьте это, это может быть полезно. developer.apple.com/library/ios/#samplecode/ aurioTouch2 /   -  person Agent Chocks.    schedule 04.06.2013
comment
Если у вас есть конкретная проблема, вам легко получить помощь, но я хочу, чтобы форма волны заставляла людей указывать на некоторые стандартные образцы.   -  person Vignesh    schedule 04.06.2013
comment
@Vignesh: Я приложил снимок экрана к заданному мною вопросу. Вот как мне нужен вывод, который должен быть нарисован в тот момент, когда идет аудиозапись. Спасибо   -  person iVenky    schedule 04.06.2013
comment
@iVenky, извини, я хотел спросить, что ты пытался этого добиться ?. А где ты застрял ?.   -  person Vignesh    schedule 04.06.2013
comment
@Vignesh Я попытался создать форму волны, подобную той, которую я прикрепил на скриншоте, и добился этого с помощью кода, который я добавил в вопрос. На самом деле я хотел добиться этого при записи самого звука. Но пока звук будет записан, и на основе записанного звука будет сгенерирована форма волны, и я сохраняю ее как изображение PNG.   -  person iVenky    schedule 04.06.2013
comment
@ivenky спасибо за обновление.   -  person Vignesh    schedule 04.06.2013
comment
@ivenky, если вам нужны волновые формы в реальном времени, которые у вас есть, используйте AudioQueue для записи. Запись с помощью AVAudioRecorder в этом случае не поможет.   -  person Vignesh    schedule 04.06.2013
comment
@Vignesh: Спасибо за ваше руководство   -  person iVenky    schedule 04.06.2013
comment
позвольте нам продолжить обсуждение в чате   -  person iVenky    schedule 04.06.2013
comment
Эта ссылка вам поможет. хорошего дня, github.com/ioslovers/ATTabandHoldAudioRecord   -  person    schedule 13.11.2013
comment
Я тоже нахожу решение.   -  person Bhaumik Desai    schedule 08.10.2015


Ответы (3)


Если вам нужна графика в реальном времени, полученная с микрофонного входа, используйте RemoteIO Audio Unit, который большинство разработчиков приложений iOS используют для звука с низкой задержкой, и Metal или Open GL для рисования сигналов, что даст вам самую высокую частоту кадров. . Для этого вам понадобится совершенно другой код, чем тот, что указан в вашем вопросе, поскольку AVAssetRecording, рисование линий Core Graphic и рендеринг png слишком медленны для использования.

Обновление: с iOS 8 и новее Metal API может отображать графические визуализации с даже большей производительностью, чем OpenGL.

Uodate 2: Вот несколько фрагментов кода для записи живого звука с использованием Audio Units и рисования битовых карт с использованием Metal в Swift 3: https://gist.github.com/hotpaw2/f108a3c785c7287293d7e1e81390c20b

person hotpaw2    schedule 04.06.2013
comment
Будет полезно, если вы сможете поделиться некоторыми фрагментами кода для получения графики в реальном времени с использованием openGL для рисования сигналов. Большое спасибо за вашу поддержку - person iVenky; 05.06.2013
comment
Если это iOS, то вы будете использовать openGLes. У вас есть выбор между фиксированным (ES1) или шейдерным конвейером (ES2). Я не знаю, есть ли преимущества в использовании шейдеров для рисования подобных вещей. В примере приложения iOS aurioTouch есть пример того, как рисовать короткие буферы с микрофона в openGL. Если я правильно помню, в этом примере они рисуют равное количество отсчетов в пикселях. Однако, чтобы нарисовать всю волновую форму более длинной песни (миллионы семплов), вам нужно будет масштабировать так, чтобы нарисованные семплы поместились на экране. Вы также должны использовать какой-либо расчет пикового значения или RMS. - person omygaudio; 10.06.2013
comment
Привет, @ hotpaw2, я попробовал этот код, и он работает! Спасибо, что поделились этим. Я думал упаковать это в Swift Package и упростить использование людьми после его небольшого изменения. Хотел уточнить у вас, нормально ли это - person C0D3; 19.09.2020

Вам следует попробовать EZAudio (https://github.com/syedhali/EZAudio), в частности EZRecorder и EZAudioPlot (или GPU -ускоренный EZAudioPlotGL).

Существует также пример проекта, который делает именно то, что вы хотите: https://github.com/syedhali/EZAudio/tree/master/EZAudioExamples/iOS/EZAudioRecordExample

РЕДАКТИРОВАТЬ: вот встроенный код

/// 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];
  }

}
person Haris Ali    schedule 01.01.2014
comment
эй .. вы не должны публиковать ответы, содержащие только ссылки - person kleopatra; 01.01.2014

Я искал то же самое. (Делая волну из данных диктофона). Я нашел некоторую библиотеку, которая может быть полезна и стоит проверить код, чтобы понять логику этого.

Все расчеты основаны на грехе и математической формуле. Это очень просто, если вы посмотрите на код!

https://github.com/stefanceriu/SCSiriWaveformView

or

https://github.com/raffael/SISinusWaveView

Это лишь несколько примеров, которые вы можете найти в Интернете.

person Hugo    schedule 14.05.2015