2012-02-12 30 views
7

Tôi muốn đo khối lượng âm thanh xung quanh, không quá chắc chắn nếu tôi đang làm đúng.Tôi đang làm điều đúng để chuyển đổi decibel từ -120 - 0 thành 0 - 120

Tôi muốn tạo đồng hồ VU có dải từ 0 (yên tĩnh) đến 120 (rất ồn).

Tôi nhận được Đỉnh và Công suất trung bình nhưng rất cao trong môi trường yên tĩnh bình thường. Hãy cho tôi một số con trỏ.

- (void)viewDidLoad 
{ 
    [super viewDidLoad]; 
    // Do any additional setup after loading the view, typically from a nib. 


    //creating an audio CAF file in the temporary directory, this isn’t ideal but it’s the only way to get this class functioning (the temporary directory is erased once the app quits). Here we also specifying a sample rate of 44.1kHz (which is capable of representing 22 kHz of sound frequencies according to the Nyquist theorem), and 1 channel (we do not need stereo to measure noise). 

    NSDictionary* recorderSettings = [NSDictionary dictionaryWithObjectsAndKeys: 
             [NSNumber numberWithInt:kAudioFormatLinearPCM],AVFormatIDKey, 
             [NSNumber numberWithInt:44100],AVSampleRateKey, 
             [NSNumber numberWithInt:1],AVNumberOfChannelsKey, 
             [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsBigEndianKey, 
             [NSNumber numberWithBool:NO],AVLinearPCMIsFloatKey, 
             nil]; 
    NSError* error; 

    NSURL *url = [NSURL fileURLWithPath:@"/dev/null"]; 
    recorder = [[AVAudioRecorder alloc] initWithURL:url settings:recorderSettings error:&error]; 

    //enable measuring 
    //tell the recorder to start recording: 
    [recorder record]; 

    if (recorder) { 
     [recorder prepareToRecord]; 
     recorder.meteringEnabled = YES; 
     [recorder record]; 
     levelTimer = [NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector: @selector(levelTimerCallback:) userInfo: nil repeats: YES]; 

    } else 
    { 
     NSLog(@"%@",[error description]); 
    }   
} 

- (void)levelTimerCallback:(NSTimer *)timer { 
    [recorder updateMeters]; 

    const double ALPHA = 0.05; 
    double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0])); 
    lowPassResults = ALPHA * peakPowerForChannel + (1.0 - ALPHA) * lowPassResults;  

    NSLog(@"Average input: %f Peak input: %f Low pass results: %f", [recorder averagePowerForChannel:0], [recorder peakPowerForChannel:0], lowPassResults); 

    float tavgPow =[recorder averagePowerForChannel:0] + 120.0; 
    float tpPow = [recorder peakPowerForChannel:0] + 120.0; 

    float avgPow = tavgPow;//(float)abs([recorder averagePowerForChannel:0]); 
    float pPow = tpPow;//(float)abs([recorder peakPowerForChannel:0]); 

    NSString *tempAvg = [NSString stringWithFormat:@"%0.2f",avgPow]; 
     NSString *temppeak = [NSString stringWithFormat:@"%0.2f",pPow]; 
    [avg setText:tempAvg]; 
     [peak setText:temppeak]; 
    NSLog(@"Average input: %f Peak input: %f Low pass results: %f", avgPow,pPow , lowPassResults); 
} 

Trả lời

8

Công thức để chuyển đổi một biên độ tuyến tính để xi ben khi bạn muốn sử dụng 1.0 như bạn tham khảo (đối với 0dB), là

20 * log10(amp); 

Vì vậy, tôi không chắc chắn về mục đích từ nhìn mã của bạn, nhưng bạn có thể muốn

float db = 20 * log10([recorder averagePowerForChannel:0]); 

này sẽ đi từ vô cực ở một biên độ bằng không, để 0dB ở một biên độ 1. Nếu bạn thực sự cần nó để đi lên đến giữa 0 và 120 bạn có thể thêm 120 và sử dụng hàm tối đa ở mức 0.

Vì vậy, sau khi dòng trên:

db += 120; 
db = db < 0 ? 0 : db; 

Công thức bạn đang sử dụng dường như là công thức để chuyển đổi DB để amp, mà tôi nghĩ là trái ngược với những gì bạn muốn.

Chỉnh sửa: Tôi đọc lại và có vẻ như bạn có thể đã có giá trị decibel.

Nếu đây là trường hợp, chỉ không chuyển đổi sang biên độ và thêm 120.

Vì vậy Thay đổi

double peakPowerForChannel = pow(10, (0.05 * [recorder averagePowerForChannel:0])); 

để

double peakPowerForChannel = [recorder averagePowerForChannel:0]; 

và bạn sẽ không sao đi.

+0

Xin chào Michael, cảm ơn bạn đã trả lời. tôi tin rằng giá trị trung bìnhPowerForChannel là giá trị decibel trong -x sẽ chuyển đổi thành 0 - 120 giá trị – Desmond

+1

@Desmond: Ok, do đó, bước cuối cùng tôi đề nghị thay đổi peakPowerForChannel để sử dụng giá trị decibel trực tiếp. Bạn đang thêm 120 sau. Bạn cũng sẽ cần phải chắc chắn rằng nó không phải là ít hơn số không bằng cách sử dụng một tối đa (0, db) như thế nào tôi đã làm với 'db = db <0? 0: db; ' –

+0

cảm ơn Michael, tuy nhiên decibel là rất rất cao trong một căn phòng yên tĩnh .... tôi tải về một ứng dụng decibel10 để kiểm tra chống lại nó decibel khác nhau là rất lớn. Các ứng dụng hiển thị khoảng 40db, mỏ cho thấy 70db. Mục tiêu chính của tôi ở đây là để kiểm tra xem người dùng có đang tạo ra tiếng ồn hay không. nếu nó vượt quá ngưỡng sẽ kích hoạt một cái gì đó. – Desmond

1

Trên thực tế, phạm vi của decibel là từ -160 đến 0, nhưng nó có thể đi đến những giá trị tích cực (AVAudioRecorder Class Reference - averagePowerForChannel: Phương pháp).

Sau đó là tốt hơn ghi db += 160; thay vì db += 120;. Tất nhiên bạn cũng có thể đặt một bù đắp để sửa chữa nó.

22

Apple sử dụng bảng tra cứu trong mẫu SpeakHere chuyển đổi từ dB sang giá trị tuyến tính được hiển thị trên đồng hồ đo mức. Điều này là để tiết kiệm năng lượng thiết bị (tôi đoán).

Tôi cũng cần điều này, nhưng không nghĩ rằng một vài tính toán nổi mỗi 1/10s (tỷ lệ làm mới của tôi) sẽ tốn rất nhiều năng lượng thiết bị. Vì vậy, thay vì xây dựng một bảng tôi đúc mã của họ vào:

float  level;    // The linear 0.0 .. 1.0 value we need. 
const float minDecibels = -80.0f; // Or use -60dB, which I measured in a silent room. 
float  decibels = [audioRecorder averagePowerForChannel:0]; 

if (decibels < minDecibels) 
{ 
    level = 0.0f; 
} 
else if (decibels >= 0.0f) 
{ 
    level = 1.0f; 
} 
else 
{ 
    float root   = 2.0f; 
    float minAmp   = powf(10.0f, 0.05f * minDecibels); 
    float inverseAmpRange = 1.0f/(1.0f - minAmp); 
    float amp    = powf(10.0f, 0.05f * decibels); 
    float adjAmp   = (amp - minAmp) * inverseAmpRange; 

    level = powf(adjAmp, 1.0f/root); 
} 

Tôi đang sử dụng một AVAudioRecorder, do đó bạn thấy nhận của dB với averagePowerForChannel:, nhưng bạn có thể điền giá trị dB của riêng bạn có.

Ví dụ của Apple đã sử dụng các tính toán double, điều mà tôi không hiểu vì việc đo độ chính xác âm thanh là quá đủ và chi phí thiết bị ít hơn.

Không cần phải nói, giờ đây bạn có thể chia tỷ lệ này được tính level thành 0 .. 120 phạm vi của bạn với một đơn giản level * 120.0f.

Mã trên có thể được tăng tốc khi chúng tôi sửa chữa root tại 2.0f, bằng cách thay thế powf(adjAmp, 1.0f/root) bằng sqrtf(adjAmp); nhưng đó là một điều nhỏ nhặt, và một trình biên dịch rất tốt có thể làm điều này cho chúng ta. Và tôi gần như chắc chắn rằng inverseAmpRange sẽ được tính một lần tại thời gian biên dịch.

-3

Chỉ cần đặt giá trị tối đa và tối thiểu của bạn. Giống như bạn nhận được khoảng 0-120. Nếu bạn muốn phạm vi 0-60. Đơn giản chỉ cần chia giá trị một nửa để có phạm vi nửa và vân vân ..

0

tôi thực hiện một mô hình hồi quy để chuyển đổi mối quan hệ ánh xạ giữa các dữ liệu được tạo ra từ wav NSRecorder và các dữ liệu decibel từ NSRecorder.averagePowerForChannel

NSRecorder.averagePowerForChannel (dB) = -80 + 6 log2 (wav_RMS)

Trường hợp wav_RMS là giá trị trung bình của dữ liệu wav gốc trong thời gian ngắn, nghĩa là 0,1 giây.

Các vấn đề liên quan