2011-07-26 6 views
1

내 앱 중 하나에는 분당 지정된 횟수 (bpm)의 클릭 사운드를 재생하는 간단한 메트로놈 스타일 기능이 있습니다. 나는 소리를 재생하는 메서드를 호출하는 지정된 bpm에서 계산 된 간격으로 NSTimer를 시작하여이 작업을 수행합니다.오디오 사운드가 제 시간에 재생되지 않는 이유는 무엇입니까?

NSLog 라인을 재생 방법에 넣으면 NSTimer가 약 1 밀리 초까지 정확하게 실행된다는 것을 알 수 있습니다. 그러나 사운드 출력을 오디오 편집기에 기록한 다음 클릭 간격을 측정하면 균등하지는 않습니다. 예를 들어, 150 bpm의 경우 타이머는 400 밀리 초마다 실행됩니다. 그러나 대부분의 사운드는 395 밀리 초 후에 재생되며, 3 번째 또는 4 번째 사운드는 418 밀리 초 후에 재생됩니다.

그래서 소리가 일정하게 지연되지는 않지만 오히려 더 짧고 긴 간격의 패턴을 따릅니다. iOS의 사운드 타이밍이 더 낮아진 것처럼 보이며 각 사운드 이벤트를 가장 가까운 사용 가능 지점으로 반올림하여 필요에 따라 반올림하여 전반적인 트랙을 유지합니다.

나는 이것을 시스템 사운드, AVAudioPlayer 및 OpenAL과 함께 시도했으며 세 가지 방법 모두에서 똑같은 결과를 얻었습니다. 각 메서드를 사용하면 뷰가로드 될 때 모든 설정을 수행하므로 사운드를 재생할 때마다 반드시 재생해야합니다. AVAudioPlayer를 사용하여 사운드가 재생 될 때마다 두 번째 타이머를 사용하여 prepareToPlay를 호출 했으므로 다음 시간에 초기화 할 준비가되었지만 동일한 결과가 나타납니다.

// set up the context and device 
ALCcontext *context; 
ALCdevice *device; 
OSStatus result; 
device = alcOpenDevice(NULL); // select the "preferred device" 
if (device) { 
    context = alcCreateContext(device, NULL); // use the device to make a context 
    alcMakeContextCurrent(context); // set the context to the currently active one 
} 

// open the sound file 
NSString *soundFilePath = [[NSBundle mainBundle] pathForResource:@"TempoClick" ofType:@"caf"]; 
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath]; 
AudioFileID fileID; 
result = AudioFileOpenURL((CFURLRef)soundFileURL, kAudioFileReadPermission, 0, &fileID); 
if (result != 0) DLog(@"cannot open file %@: %ld", soundFilePath, result); 

// get the size of the file data 
UInt32 fileSize = 0; 
UInt32 propSize = sizeof(UInt64); 
result = AudioFileGetProperty(fileID, kAudioFilePropertyAudioDataByteCount, &propSize, &fileSize); 
if (result != 0) DLog(@"cannot find file size: %ld", result); 
DLog(@"file size: %li", fileSize); 

// copy the data into a buffer, then close the file 
unsigned char *outData = malloc(fileSize); 
AudioFileOpenURL((CFURLRef)soundFileURL, kAudioFileReadPermission, 0, &fileID); // we get a "file is not open" error on the next line if we don't open this again 
result = AudioFileReadBytes(fileID, false, 0, &fileSize, outData); 
if (result != 0) NSLog(@"cannot load data: %ld", result); 
AudioFileClose(fileID); 
alGenBuffers(1, &tempoSoundBuffer); 
alBufferData(self.tempoSoundBuffer, AL_FORMAT_MONO16, outData, fileSize, 44100); 
free(outData); 
outData = NULL; 

// connect the buffer to the source and set some preferences 
alGenSources(1, &tempoSoundSource); 
alSourcei(tempoSoundSource, AL_BUFFER, tempoSoundBuffer); 
alSourcef(tempoSoundSource, AL_PITCH, 1.0f); 
alSourcef(tempoSoundSource, AL_GAIN, 1.0f); 
alSourcei(tempoSoundSource, AL_LOOPING, AL_FALSE); 

그리고 재생 방법에 난 그냥 전화 :

alSourcePlay(self.tempoSoundSource); 

사람이 무엇을 설명 할 수 여기에

은 ( this tutorial에서 적응)의 viewDidLoad에서 OpenAL에 사운드를 설정하기위한 코드입니다 여기서 일어나는 일, 어떻게 해결할 수 있을까요?

UPDATE 1 :

나는이 내 클릭 소리마다 400 밀리 초를 재생하는 프로젝트에 타이머를 추가 빠른 테스트 있도록, 오디오 장치와 짧은 소리를 재생 다른 프로젝트. 이 경우 타이밍은 거의 완벽합니다. 따라서 NSTimer는 괜찮지 만 시스템 사운드는 AVAudioPlayer와 OpenAL이 오디오 단위보다 재생이 덜 정확합니다.

UPDATE 2 :

난 그냥 오디오 장치를 사용하는 내 프로젝트를 재 이제 오디오는 더 정확하게 다시 많이 연주된다. 어느 방향 으로든 가끔씩 최대 4 밀리 초씩 변동하는 경우가 있지만 다른 오디오 방법보다 좋습니다. 다른 방법들이 모두 짧고 짧고 짧고 긴 간격의 패턴을 보여주는 이유는 여전히 궁금합니다. 오디오 재생 시간을 반올림하여 프레임 속도에 매핑하는 것과 같습니다. 설명 할 수있는 사람이나 다른 오디오 방법에 대한 해결책을 제공 할 수있는 사람들에게이 질문을 공개하십시오.

+0

확실하지 않지만 NSLog가 지연을 초래할 수도 있습니다. – Ahmed

+0

@Ahmed : 좋은 생각인데 NSLog를 주석 처리하고 같은 결과를 얻었습니다. – arlomedia

+0

가능한 [아이폰에 실시간 정확한 오디오 시퀀서를 프로그래밍하는 방법?] (http://stackoverflow.com/questions/907137/how-to-program-a-real-time-accurate-audio-sequencer) -on-the-iphone) – benzado

답변

1

그래, 알아 냈어.

Float32 preferredBufferSize = .001; 
UInt32 size = sizeof(preferredBufferSize); 
AudioSessionSetProperty(kAudioSessionProperty_PreferredHardwareIOBufferDuration, size, &preferredBufferSize); 

때 I을 : 진짜 이유는 오디오 장치는 내가 다른 프로젝트에서 채택 된 내 오디오 장치 클래스는, 다음과 같이 오디오 세션에서 버퍼 시간 속성을 설정 한 것을 다른 오디오 방법보다 더 나은 일 OpenAL 버전이나 AVAudioPlayer 버전에이 코드를 추가하면 오디오 단위와 마찬가지로 수 밀리 초 내에 정확성을 얻게됩니다. 그러나 System Sounds는 여전히 정확하지 않았습니다. 버퍼 크기를 늘리고 재생 간격이 덜 정확하다는 것을 관찰하여 연결을 확인할 수 있습니다.

물론 오디오 장치를 사용하기 위해 하루 종일 프로젝트를 수정 한 후에 만이 문제를 해결했습니다. C++에서 컴파일을 조정하고 중단 처리기를 테스트하는 등입니다. 같은 문제에서 다른 사람을 구할 수 있기를 바랍니다. .

1

NSTimer는 메소드가 실제로 실행되는 것을 보장하지 않습니다. 여기

상세 정보 : How to program a real-time accurate audio sequencer on the iphone?

편집에 관한 :

AVAudioPlayer 자체를 초기화하는 데 약간의 시간이 소요됩니다. prepareToPlay를 호출하면 호출을 시작할 때 현재로드 된 사운드를 즉시 재생할 수 있도록 자체를 초기화합니다.재생이 중지되면 자체적으로 초기화되지 않으므로 prepareToPlay를 다시 호출하여 다시 초기화해야합니다. 디스크리트 사운드 재생이 아닌 스트림 y 재생에이 클래스를 사용하는 것이 가장 좋습니다.

OpenAL을 사용하면 버퍼를로드하고 소스에 연결하여 재생하면 지연이 발생하지 않아야합니다.

오디오 유닛 코드를 .mm 파일로 캡슐화 한 다음 C 모듈로 컴파일하지 않고 .m 모듈에서 호출 할 수 있습니다.

+1

내 NSLogs의 타임 스탬프에 따르면이 방법은 가장 가까운 밀리 초로 시작됩니다. 이는 제 목적에 충분히 근접합니다. 위의 링크는 더 정확한 타이머를 만드는 데 중점을 둡니다. 그러나 제 경우에는 타이머가 문제가 아닌 것으로 보입니다. – arlomedia

+0

"오디오 장치 코드를 .mm 파일로 캡슐화 한 다음 C 모듈로 컴파일하지 않고 .m 모듈에서 호출 할 수 있습니다." 그거 확실하니? 나는 그 일을 할 수 없었다. 명확히 할 수 있다면 다음과 같은 질문을 게시했습니다. http://stackoverflow.com/questions/6944465/how-can-i-use-a-few-c-files-in-my-project-without -c 파일마다 컴파일 – arlomedia