
소리이야기
컴퓨터의 디지털 속성은 연속적인 아날로그 시그널을 처리하기에는 적합하지 않다. 이런 사실은 신호를 샘플링하거나, 사람의 귀로는 차이를 알 수 없게 부드러운 소리 파형을 충분히 잘게 자르는 방법으로 처리된다.
소리가 전해지는 법

사운드의 기본 요소

디지털 사운드로의 변환
ADC(Analog-to-Digital Converter)
DAC(Digital-to-Analog Converter)
디지털 변환 과정
-표본화, 양자화, 부호화 과정 필

표본화(Sampling)
아날로그 파형을 디지털 형태로 변환하기 위해 표본을 취하는 것
표본화율(Sampling Rate)
1초 동안에 취한 표본수 (단위: Hz)
표본화율이 높을수록 원음을 잘 표현하나 데이터 공간은 증가

양자화 (Quantizing)
어느 정도의 정밀도로 표현할 것인지
-표본화된 각 점에서 값을 표현하기 위해 사용되는 비트 수
음의 해상도
-표본화하는 정밀도 (Sampling Resolution, Sampling Size)
-8 bit로 양자화 하면 값을 256(28) 단계로 표현 가능
-16bit로 양자화 하면 좀 더 세밀한 65,536(216) 단계로 값 표현 가능

표본화 및 양자화 정도에 따른 비교

부호화 (Coding)
표본화와 양자화를 거친 디지털 정보를 표현하는 과정
사운드 파일은 크기 때문에 부호화 과정에서 압축하여 저장
디지털 사운드 파일의 크기

파일의 크기
= 표본화율 x 해상도 x 모드(mono=1, stereo=2) x 시간(초)
1분 길이의 음악 CD
= 44,100(Hz) x 16(bit) x 2(stereo) x 60(초)
= 84,672,000bit
= 10,584,000 byte ≒ 10.6 MB
CD 한 장의 용량이 650MB이므로 그 안에는 약 10곡에서 15곡의 음악이 저장 가능
사운드의 기본 요소 - 용어
매체: 서로 가까이 있어서 밀치기에 충분한 여러 객체(공기입자)
사이클: 밀쳐진 객체의 이동
구간(주기) : 사이클을 완료하는데 걸린 시간
압축파형 : 어떤 객체를 반복적으로 밀어낸다면, 객체들이 서로 간에 밀어내는 패턴
파형의 크기 : 밀치는 강도
밀치는 속도 : 파형의 주파수(더 자주 밀칠 수록 파형의 주파수는 높아진다.)
디지털 오디오
(초당 샘플로 표현되는) 샘플율에 곱해진 (샘플당 비트로 표현되는) 비트깊이는 (초당 비트로 표현되는) 비트율이 된다. 비트율은 오디오의 1초를 나타내는 데 요구되는 비트의 수다. 높은 비트율은 높은 품질의 녹음을 제공하지만, 하드웨어가 더 많이 저장하고 처리해야 함을 의미 한다.
오디오 형식은 일반적으로 채널당 하나의 샘플을 프레임으로 합친다. 프레임은 시간 영역의 한 순간에 모든 오디오 채널을 나타낸다. 따라서 모노 사운드에 대해서 프레임은 하나의 샘플을 가진다. 스테레오는 두 개를 가진다. 하나의 시스템에 여러 소리 채널을 넣을 경우 채널이 배정 됐다고 한다. 어떤 오디오 형식은 여러 프레임을 패킷으로 합친다. LPCM(선형 펄스 코드 변조)은 패킷을 사용하지 않지만, 압축된 오디오 형식은 값을 근사하기 위해서 샘플의 그룹으로 수학적인 기법을 사용하기 때문에 이를 사용한다. 주어진 샘플은 주변의 샘플로 어느정도 예상이 가능하기 때문에, 압축 포맷은 압축되지 않은 LPCM 보다 훨씬 적은 데이터를 사용하여 유사한 파형을 만들기 위해서 정렬된 프레임에서 샘플 그룹을 사용할 수 있다.
위에서 어떤 형식으로 오디오를 나타내기 위해서 주어진 구간동안 데이터의 양을 나타내는 비트율에 대해서 언급하였다. PCM에서 비트율은 고정된 상수다. 즉, CD 품질 오디오는 초당 2채널 * 16비트 * 44,100 샘플을 가지기 때문에 초당 1,411,200비티율이나 , 1,411kbps를 가진다. 이 데이터율은 주어진 채널 수, 비트 깊이, 샘플율의 조합에 대해서는 절대로 변경되지 않기 때문에 PCM은 고정된 비트율을 가졌다고 한다. 압축 포맷은 가변 비트율을 가지며, 이는 데이터의 특정 부분을 압축하는데 필요한 데이터의 양이 변경됨을 의미한다. 코어 오디오는 가변 프레임율 형식을 지원한다. 즉, 어떤 주어진 프레임에 대한 데이터의 양은 가변이지만, 패킷은 동일한 크기로 유지된다. 코어 오디오는 패킷 비율이 변하더라도 가변 패킷율을 지원한다.
Viewcontroller.h
#import <UIKit/UIKit.h> | |
@interface ViewController : UIViewController | |
//2.파일 경로에 사운드 파일 만들기 | |
-(void)createSoundFile; | |
@end |
Viewcontroller.m
/* | |
코어오디오 api를 사용하기 위해서 AudioToolbox 라이브러리를 추가해줘야한다. | |
(왼쪽 네비게이션에서 프로젝트 아이콘 클릭 -> Build Phasses -> Link Binary With Libraries -> + 버튼 클릭 -> 찾아서 추가해줌) | |
*/ | |
#import <AudioToolbox/AudioToolbox.h> | |
#import "ViewController.h" | |
@interface ViewController () | |
@end | |
@implementation ViewController | |
/* | |
참고 | |
개발자 Apple | |
범주 오디오 파일 | |
형식 Binary | |
무엇입니까 AIF 파일? | |
AIF 파일은 AIFF (Audio Interchange File Format)를 사용하여 만든 오디오 파일입니다. .WAV 파일과 비슷한 비 압축 CD 음질의 오디오가 포함되어 있으며 일반적으로 표준 CD 오디오를 저장하는 데 사용됩니다. | |
추가 정보 | |
Apple Computer는 Electronic Arts의 .IFF 형식을 기반으로 AIFF 형식을 개발했습니다. 표준 AIFF 파일은 44.1KHz의 샘플링 속도를 사용하며 16 비트이며 스테레오 사운드 용으로 2 개의 채널이 있습니다. | |
*/ | |
#define SAMPLE_RATE 44100 //초당 44,100 번의 샘플 | |
#define DURATION 2.0 //길이는 2초 | |
//#define FILENAME_FORMAT @"%0.3f-square.aif" | |
//#define FILENAME_FORMAT @"%0.3f-saw.aif" | |
#define FILENAME_FORMAT @"%0.3f-sine.aif" | |
//2.사운드 파일 생성 하기 | |
-(void)createSoundFile{ | |
printf("createSoundFile - 사운드 파일 생성 하기 진입 \n"); | |
double hz = 10.0; | |
//1.도큐먼트 주소 얻기 | |
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES); | |
NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; | |
NSLog(@"paths : %@" , paths); | |
NSLog(@"basePath : %@" , basePath); | |
//2.도큐먼트 주소에 저장할 파일명 생성 | |
NSString *fileName = [NSString stringWithFormat:FILENAME_FORMAT,hz]; | |
NSString *filePath = [basePath stringByAppendingPathComponent:fileName]; | |
NSLog(@"filePath : %@" , filePath); | |
NSURL *fileURL = [NSURL fileURLWithPath:filePath]; | |
NSLog(@"fileURL : %@" , fileURL); | |
/* | |
출력 | |
paths : ( | |
"/var/mobile/Containers/Data/Application/4B1E0587-C5D7-423C-B86D-ECB72AA86C26/Documents" | |
) | |
basePath : /var/mobile/Containers/Data/Application/4B1E0587-C5D7-423C-B86D-ECB72AA86C26/Documents | |
filePath : /var/mobile/Containers/Data/Application/4B1E0587-C5D7-423C-B86D-ECB72AA86C26/Documents/test_recording.aif | |
fileURL : file:///var/mobile/Containers/Data/Application/4B1E0587-C5D7-423C-B86D-ECB72AA86C26/Documents/test_recording.aif | |
*/ | |
//오디오 스트림에 대한 오디오 데이터 형식 사양입니다. | |
//오디오 스트림의 가장 광범위한 특성을 정의 한다. | |
//즉, 얼마나 많은 채널을 가졌는지, 형식은 무엇인지, 비트율등을 포함한다. | |
AudioStreamBasicDescription asbd; | |
/* | |
ASBD (오디오 스트림 기본 설명)를 구성하여 선형 PCM 형식 또는 채널 크기가 같은 고정 비트 전송률 (CBR) 형식을 지정할 수 있습니다. | |
VBR (가변 비트 레이트) 오디오 및 채널 크기가 다른 CBR 오디오의 경우 | |
각 패킷은 AudioStreamPacketDescription 구조로 추가로 설명해야합니다. | |
필드 값이 0이면 값을 알 수 없거나 형식에 적용 할 수 없음을 나타냅니다. | |
다음과 같이 항상 새 오디오 스트림 기본 설명 구조의 필드를 0으로 초기화하십시오. | |
*/ | |
//asbd 0으로 초기화 | |
memset(&asbd, 0, sizeof(asbd)); | |
/* | |
하나의 패킷으로 표시되는 지속 기간을 판별하려면 다음과 같이 mSamplesate 필드와 mFramesPerPacket 필드를 사용하십시오. | |
duration = (1 / mSampleRate) * mFramesPerPacket | |
*/ | |
/* | |
Core Audio에서는 다음 정의가 적용됩니다. | |
- 오디오 스트림은 노래와 같은 사운드를 나타내는 연속 된 일련의 데이터입니다. | |
- 채널은 단음 오디오의 개별 트랙입니다. 모노 포닉 스트림에는 하나의 채널이 있습니다. 스테레오 스트림에는 두 개의 채널이 있습니다. | |
- 샘플은 오디오 스트림의 단일 오디오 채널에 대한 단일 수치입니다. | |
-프레임은 시간 일치 샘플의 모음입니다. | |
예를 들어, Linear PCM 스테레오 사운드 파일에는 프레임 당 2 개의 샘플이 있습니다. 하나는 왼쪽 채널 용이고 다른 하나는 오른쪽 채널 용입니다. | |
-패킷은 하나 이상의 연속 프레임의 집합이다. | |
패킷은 주어진 오디오 데이터 형식에 대해 가장 작은 의미있는 프레임 집합을 정의하며 | |
시간을 측정할 수 있는 가장 작은 데이터 단위입니다. | |
선형 PCM 오디오에서 패킷은 단일 프레임을 보유한다. | |
압축 형식에서는 일반적으로 더 많은 것을 유지합니다. 일부 형식에서는 패킷 당 프레임 수가 다릅니다. | |
-스트림의 샘플링 속도는 초당 압축되지 않은(또는 압축 해제된 형식에 해당하는) 오디오의 프레임 수입니다 | |
*/ | |
/* | |
소스 설명 | |
여기서는 44,100 데이터율을 가지는 하나의 채널 pcm인 스트림을 나타낸다. | |
16비트 샘플을 사용하기 때문에 각 프레임은 2바이트 (하나의 채널 * 2바이트 샘플 데이터)를 가질 것이다. | |
LPCM은 패킷을 사용하지 않고 (가변비트율 형식에만 유용함) 따라서 bytesPerFrame 과 bytesPerPacket은 동일한다. | |
또, PCM에 대해서 샘플이 빅엔디언이나, 반대인 리틀엔디언인지 나타내야만한다. | |
여기서는 빅엔디언 형식만을 취할 수 있는 AIFF 파일에 작성할 것이기 때문에, 빅엔디언을 ASBD에 설정할 필요가 있다. | |
샘플값이 각 바이트에 가용한 모든 비트를 사용하는지 여부를 나타내기 위해서 세 번째 플레그를 전달한다. | |
*/ | |
//1초당 들리는 샘플의 갯수를 단위로 나타낸것. 보통 44,100 hz 이다. 이 의미는 1초에 44,100개의 오디오 조각이 귀에 들린다는 뜻. | |
asbd.mSampleRate = SAMPLE_RATE; | |
//pcm : pulse code modulation , 펄스 부호 변조. 음성등의 아날로그 신호를 표준화(샘플링)하여 신호의 크기를 정수로 변환한 펄스 변조의 한 형식 | |
asbd.mFormatID = kAudioFormatLinearPCM; | |
asbd.mFormatFlags = kAudioFormatFlagIsBigEndian | kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked; | |
//참고 | |
//채널 : 오디오를 렌더링할 때는 물론 하나의 소리만 줄기차게 낼 수 있겠지만, 좌우로 귀가 둘인 사람들을 위해서, 두 개의 채널로 각각 다른 소리를 나오도록 할 수도 있다. 전자를 모노, 후자를 스테레오라고 표현한다. 요즘은 입체적인 소리를 구현하기 위해서 5.1 채널로 구성한 경우도 볼 수 있는데, 총 6개의 채널을 사용한 경우이다. 오디오 데이터는 이 채널이라는 개념을 포함해서 구성을 해야 한다 | |
//프레임 : 채널을 모두 합쳐서 하나의 샘플을 구성하고 있는 것을 프레임(frame)이라고 한다. 즉, 44100Hz 의 sample rate 으로 만들어진 오디오에는 초당 44100개의 frame 이 포함되어 있으며, 모노인 경우에는 sample 의 개수가 frame 의 개수와 동일하고, 스테레오인 경우에는 sample 의 개수가 88200개가 되는 것이라 생각하면 될 것 같다. | |
//하나의 샘플크기 : 하나의 오디오 샘플을 만들 때 얼마나 세밀하게 하느냐에 따라서 하나의 샘플 크기가 결정된다. 양자화라는 전문 용어를 쓰기도 하는데, 이런 건 별로 중요하지 않다. 샘플 하나를 1 byte 로 표현할 것인가, 2 byte 로 표현할 것인가, 이런 내용이다. 1 byte로 표현하면, 하나의 샘플이 256 단계, 2 byte로 표현하면 65536 단계로 나눠서 소리를 재현할 수 있으니, 잘게 나눠 놓은 것이 더 원음의 가까운 소리를 낼 수 있을 것이다. 이렇게 몇 바이트로 잘게 쪼갰느냐 하는 것이 샘플 크기(sample size)이다. | |
//하나의 프레임의 크기 = 채널 수 * 샘플크기 | |
//결국 프레임의 크기(frame size)라는 것은 채널과 샘플 크기를 합쳐서 나타내는 말이 된다. | |
//예를 들어, 스테레오의 2 byte 샘플 크기로 표현되는 오디오의 frame size 는 2 * 2 = 4 byte 가 된다. | |
//오디오 데이터의 각 프레임에 채널 수 | |
asbd.mChannelsPerFrame = 1; | |
//오디오 데이터 패킷의 프레임 수입니다. 압축되지 않은 오디오의 경우 값은 1입니다. 가변 비트 속도 형식의 경우 AAC의 경우 1024와 같이 값이 더 큰 정수입니다.Ogg Vorbis와 같은 패킷 당 프레임 수를 가변적으로 사용하는 형식의 경우 이 필드를 0으로 설정합니다. | |
asbd.mFramesPerPacket = 1; | |
//오디오 샘플 한 개에 대한 비트 수입니다. | |
asbd.mBitsPerChannel = 16; | |
//오디오 버퍼에서 한 프레임의 시작부터 다음 프레임의 시작까지의 바이트 수입니다.압축 형식의 경우 이 필드를 0으로 설정합니다. | |
asbd.mBytesPerFrame = 2; | |
//오디오 데이터 패킷의 바이트 수입니다. 가변 패킷 크기를 표시하려면 이 필드를 0으로 설정합니다. | |
//가변 패킷 크기를 사용하는 형식의 경우 AudioStreamPacketDescription 구조를 사용하여 각 패킷의 크기를 지정합니다. | |
asbd.mBytesPerPacket = 2; | |
//오디오 파일 객체를 나타내는 데이터 타입. | |
AudioFileID audioFile; | |
OSStatus audioErr = noErr; | |
//새 오디오 파일을 만들거나 URL에서 지정한 기존 파일을 초기화합니다. | |
audioErr = AudioFileCreateWithURL((__bridge CFURLRef)fileURL, kAudioFileAIFFType, &asbd, kAudioFileFlags_EraseFile, &audioFile); | |
assert(audioErr == noErr); | |
//초당 샘플 레이트 샘플에서 소리의 duration 초에 얼마나 많은 샘플이 필요한지 계산한다. | |
//전체 샘플 수 = 샘플율 + 시간(초) | |
//44,100hz 샘플로 2초간 진행하면 전체 샘플 수는 88,200개가 된다. | |
long maxSampleCount = SAMPLE_RATE * DURATION; | |
NSLog(@"maxSampleCount : %ld" ,maxSampleCount); | |
//maxSampleCount : 88,200 | |
long sampleCount = 0; | |
UInt32 bytesToWrite = 2; | |
//얼마나 많은 샘플이 파장에 있는지 알아야 할 필요가 있기 때문에 파형을 만드는 샘플을 위한 값을 계산할 수 있다. | |
double wavelengthInSamples = SAMPLE_RATE / hz; | |
NSLog(@"wavelengthInSamples : %f " , wavelengthInSamples); | |
//wavelengthInSamples : 4410.000000 | |
//20번 돈다. | |
while (sampleCount < maxSampleCount) { | |
//파형이 반복되는 주파수는 소리의 높낮이를 나타내는 음의 높이로 인식이 되는 것이다. | |
//파형의 모양은 소리에 음색이라고 하는 특성을 부여 한다. | |
NSLog(@"====================== [%ld] ======================" , sampleCount); | |
//i < 4410 | |
for (int i = 0; i < wavelengthInSamples; i++) { | |
// 사각 파형 - 두 값 사이를 변경한다. | |
// square wave | |
// SInt16 sample; | |
// | |
// if (i < wavelengthInSamples/2) { | |
// sample = CFSwapInt16HostToBig (SHRT_MAX); | |
// } else { | |
// sample = CFSwapInt16HostToBig (SHRT_MIN); | |
// } | |
// | |
// audioErr = AudioFileWriteBytes(audioFile, | |
// false, | |
// sampleCount*2, | |
// &bytesToWrite, | |
// &sample); | |
// assert (audioErr == noErr); | |
// | |
// sampleCount++ ; | |
// 톱니파형 - 파형의 길이에서 최소 값에서 최대 값으로 선형 증가를 하고, 다음 파형에서 최소로 다시 재설정된다. | |
// //saw | |
// //호스트의 기본 바이트 순서에서 16비트 정수를 빅엔디안 형식으로 변환한다. | |
// //SHRT_MAX : short int의 최댓값 32767 | |
// //SInt16 : -32768 ~ 32767 범위 | |
// SInt16 sample = CFSwapInt16HostToBig(((i / wavelengthInSamples) * SHRT_MAX * 2) - SHRT_MAX); | |
// | |
// NSLog(@"[ %d ]sample : %d " ,i, sample); //총 88,200개의 샘플 | |
// | |
// //오디오 파일에 오디오 데이터 바이트를 기록합니다. | |
// | |
// /* | |
// -시작 바이트 : sampleCount * 2 | |
// 오디오 데이터를 작성해야 하는 바이트 간격띄우기. | |
// -ioNumBytes : &bytesToWrite | |
// 입력 시 포인터는 쓸 바이트 수를 지정합니다. 출력 시, 실제로 쓰여진 바이트 수에 대한 포인터. | |
// -inBuffer : &sample | |
// 작성할 바이트를 포함하는 버퍼에 대한 포인터.. | |
// */ | |
// audioErr = AudioFileWriteBytes(audioFile, false, sampleCount * 2 , &bytesToWrite, &sample); | |
// | |
// assert(audioErr == noErr); | |
// | |
// sampleCount++; | |
//사인파형 - 삼각함수의 속성에 부합하느 곡선. 사인 함수는 간단한 조화운동을 나타낼 수 있기 때문에, | |
//좀더 자연스럽고, 현악기의 진동과 같은 자연적인 현상과 닮았다. | |
//sine wave | |
SInt16 sample = CFSwapInt16HostToBig((SInt16)SHRT_MAX * sin(2 * M_PI * (i/wavelengthInSamples))); | |
audioErr = AudioFileWriteBytes(audioFile, false, sampleCount*2, &bytesToWrite, &sample); | |
assert(audioErr == noErr); | |
sampleCount++ ; | |
} | |
} | |
//audioFile 닫기 | |
audioErr = AudioFileClose(audioFile); | |
assert(audioErr == noErr); | |
NSLog(@"wrote %ld samples " , sampleCount); | |
//wrote 88,200 samples | |
} | |
- (void)viewDidLoad { | |
[super viewDidLoad]; | |
printf("viewDidLoad 진입 \n"); | |
//2.사운드 파일 만들기 | |
[self createSoundFile]; | |
} | |
@end | |
사각파형

톱니파형

사인파형

버퍼
한 번에 하나의 샘플을 읽거나 쓰는것은 배우 비효율적이다. AudioFileWriteByte를 생각해보면, 마지막 두개의 파라미터는 파일에 작성할 바이트의 수와 샘플의 포인터다. 한번에 하나의 샘플을 파일에 쓰고, 샘플 예제에 200,000 이상 함수 호출의 오버헤드를 가지는 대신에 여러 샘플을 가질 수 있는 메모리 버퍼를 만들고, 한번에 파일에 쓸 수도 있다.
오디오 형식
mp3 파일은 pcm이나 윈도우 미디어 데이터를 포함할 수 없고, 오직 mp3데이터만 저장 가능하다. aiff 파일은 pcm 오디오 데이터를 다루지만, 빅엔디언일 경우만 가능하다. 반대로 wav 파일의 pcm은 리틀엔디언 이어야 한다. 다른 파일 형식은 이런 데이터 형식이 둔감하고, 여러 데이터 형식을 다룬다. 예를들어 mp4 파일 형식은 aac, pcm, ac3를 포함하는 여러 데이터 형식을 포함할 수 있다. 코어오디오에서는 caf 가 제일 둔감하다. mp3, aac 등 모든 오디오 형식을 포함한다.
참고
https://slidesplayer.org/slide/14470501/
https://evan-moon.github.io/2019/07/10/javascript-audio-waveform/
'아이폰 개발 > ios 개념&튜토리얼' 카테고리의 다른 글
코어 블루투스 - IOS Core Bluetooth (0) | 2020.10.25 |
---|---|
코어 오디오 - AudioStreamBasicDescription 정보출력 (0) | 2020.08.12 |
IOS 샌드박스 접근 로직 (0) | 2020.08.11 |
코어 오디오 - 오디오 정보 추출 (0) | 2020.08.06 |
ios 위변조 탐지 로직 (1) | 2020.08.05 |