2013년 10월 13일 일요일

[번역중] 쓸떼없는 24bit/192kHz 음원(고품질 음원)의 다운로드...

[원제] 24bit/192kHz 음원(고품질 음원)의 다운로드... 그리고 왜 이것들이 쓸떼없는 짓인지

[출처] http://people.xiph.org/~xiphmont/demo/neil-young.html

이 내용들은 지난달 음악가 Neil Young과 애플사의 Steve Jobs가 압축되지 않은 스튜디오 음질의 디지털 음악 다운로드를 제공하는 것에 대해서 논의하는 내용을 내타내고있다.
많은 문서와 사용자들의 해설은 압축되지 않은 24bit 192kHz의 음원 다운로드에 대한 예측에 대해서 특별히 열정적이다.
24bit/192kHz(고품질 음원)은 Mr. Young's 그룹과 몇 달전에 나눈 나의 대화에서 눈에 띄는 특징이다.

불행하게도, 24bit/192kHz(고품질 음원) 형식으로 음악을 배포할 가치가 없다. 그것의 재생 안정도는 약간 16bit/44.1kHz혹은 16bit/48kHz보다 약간 하위에 있다.
그리고, 6배는 더 저장 공간을 차지한다.

오늘날 디지털방식으로 배포되는 음악의 경험과 음악 품질과 관련된 몇가지 실제적인 문제들이 있다. 24bit/192kHz(고품질 음원)는 이러한 문제점들 중 아무것도 해결하지 못한다. 모든 이들이 만병통치약으로 알려진 24bit/192kHz(고품질 음원)으로 추출될때 우리는 어떤 실제적인 개선을 느끼지 못하게 될 것이다.

먼저, 나쁜 소식들.

몇 주전에 나는 24bit/192kHz의 음원(고품질 음원)을 다운로드하는 것에 확신을 가지고, 어떻게 이러한 사실들을 동의 할 수 없는지를 알고 싶어 하는 지적이고 과학적인 지식을 가지고 있는 개인들과 대화를 가졌었다.그들은 자세한 해답을 요구하는 좋은 질문들을 했다.

나는 또한 어떤 동기에 의해서 이런 고품질(high-rate)의 디지털 음원을 옹호하는 분위기가 되었는지에 흥미를 가지고 있었다.그에 대한응답들은 몇몇 사람들이 기본적인 신호 이론과 샘플링 원리를 심하게 놀라운것으로 이해하고 있는 것을 보여준다.수학적, 기술적, 그리고 생리학적인 부분에서 잘못이해하고 있는 것들이 대화에서 많이 나타났고, 자주 중요한 오디오 전문 기술을 가지고 있는 전문가에 의해서도 주장되어 진다. 몇 사람은 샘플링 이론은 디지털 오디오가 실제로 어떻게 작동하는지를 잘 설명하지 못한다고 주장한다.

잘못된 정보와 미신은 단지 허풍선이만 만들어 낸다. 그래서, 실제로 적용되는 몇가지 개선점을 제안하기 이전에 24bit/192 kHz(고품질 음원)의 배포는 왜 쓸떼없는 짓인지에 대한 약간의 기본지식을 다루도록 하자.

여러분, 당신의 귀를 살펴봅시다.

귀는 달팽이관의 공진 기저막에 있는 모(발)세포(hair cell)를 통해서 듣는 기능을 수행한다.각각의 모세포는 효과적으로 세포의 각각의 위치에 해당하는 좁은 파장대역으로 조절한다. 감지되는 파장대의 피크는 맨 중앙에 있고 인접하고 있는 모세포 간에 약간의 겹치는 파장이 존재하면서 꼬깔콘 모양을 이루면서 양쪽으로 갈수록 줄어 들게 된다.


위의 왼쪽 그림: 기저막이 베이지색으로 표현된 인간의 달팽이과늬 해부학적 단면도. 각 세포는 그것의 길이에 따라서 서로 다른 주파수에서 공진하도록 되어 있다. 외각에 가까운 곳은 높은 주파수에서 그리고  꼭대기 부분으로 갈수록 낮은 주파수에서 공진하는 세포로 되어 있다. 몇가지 공진 주파수에 대한 추정치들이 표시되어 있다.

위의 오른쪽 그림: 겹치는 필터의 뱅크처럼 보이는 기저막 세포에 해당하는 모세포의 응답 특성을 보여주는 개념도.

이것은 튜너가 설정된 곳에 가까운 강한 양의 주파수를 취득하는 아날로그 라디오와 유사하다. 중앙의 주파수에서 더 멀어지게 될 수록 (완벽히 사라지게 될때까지) 점증적으로 더 약해지고 왜곡되게 된다. 마지막 모세포의 감도가 0으로 떨어지게되고 들을 수 없게 되는 시점 이전인 상한 (하한) 가청 주파수 한계점이 있다.

샘플링 주파수와 가청 스펙트럼

 여러분이 이러한 말들을 많이 반복적으로 들었을 것으로 확신한다: 인간의 가청 범위는 20Hz에서 20kHz에 걸쳐있다. 연구자들이 어떻게 이런 특정 숫자를 지정하게 되었는지 아는 것은 중요한 문제이다.

 먼저, 우리는 청음 그룹에 대해서 모든 소리 파장대에 걸쳐서 '청음의 절대적인 한계점'을 측정한다. 이러한 작업은 건강한 귀에 이상적인 환경에서 측정이 이루어져 어떤 주어진 주파수에 대한 인간의 귀가 임지 할 수 있는 매우 미세한 소리를 표현하는 그래프를 우리에게 제공한다. 울림을 막아주는 주변환경, 정교하게 교정된 재생 장치, 그리고 엄격한 통계 분석은 쉬운 부분이다. 귀와 청각의 집중은 둘다 빨리 피로해 진다. 그래서 실험은 청음자가 피로하지 않은 상태에서 이루어져야만 한다. 이러한 점은 많은 휴식과 실험 중단이 이루어진다는 것을 의미한다. 실험은 측정 방법에 따라서 몇 시간에서 몇 일에 걸쳐 어떤 곳에서 이루어진다.

 그리고 나서 우리는 다른 극한점인 '고통 한계점'에 대한 자료를 수집한다. 이러한 작업은 소리의 크기가 귀의 물리적, 신경계적인 체계가 가해진 소리에 의해서 완벽히 압도되지 않을 만큼 그러나 물리적인 고통은 경험할 만큼 높아지게 되는 지점이다. 이러한 자료의 수집은 약간 사기성이 있다. 여러분들은 어떤 사람도 이 과정에서 청음을 통해 영구적인 피해를 입게 되기 원치 않는다.



위의 그림: 16kHz 이상의 주파수에 해당하는 현대적인 음원을 포함한 Flectcher와 Munson (1933)에 의해서 유도된 근사적인 등음량 곡선(equal loudness curve). 절대적인 청음 한계치와 고통 곡선의 한계치는 빨간색으로 표시되어 있다.  이후의 연구자들은 Phon 단위와 ISO 226 표준 등음량 곡선에서의 한계점과 이러한 측정치를 더 정교화 하였다. 현재의 측정치는 귀는 Fletcher와 Munson의 결과보다 낮은 주파수에 대해 분명히 덜 민감하다고 알려주고 있다.

 인간의 청음 범위의 상한은 청음 그래프의 절대치가 고통 한계치와 만나는 지점이 되도록 정의되어 진다. 이 지점에서 혹은 그 아래에서 소리를 희미하게 나마 인지하기 위해선 동시에 참을 수 없을 정도로 크게되어야만 한다.

 낮은 주파수에서 달팽이관은 저음 반사 모음집(bass reflex cabinet)처럼 동작한다. 달팽이관의 구멍은 사람에 따라서 40Hz에서 65Hz사이의 어떤 주파수에 맞춰진 통로로 작동하는 기저막의 꼭지점의 입구이다. 응답특성은 이 주파수 아래에서 가파르게 떨어지게 된다.

 이와같이, 20Hz에서 20kHz는 일반적인 범위이다. 이것은 철저히 가청 스펙트럼을 포함하고 이건 거의 한세기의 걸친 실험 자료에 의해서 증명되는 주장이다.

유전적 축복과 황금귀들.

 나의 서신에 기초해 보면, 많은 사람들이 듣는 기능에 특수한 축복을 가지고 있는 개개인들이 있다고 믿는다. 정말로 '황금귀'같은 것들이 존재하긴 하는 걸까?

 이러한 문제는 여러 분들이 무엇을 황금귀로 부르는가에 따라 다르다.

 젊고, 건강한 사람의 귀는 늙고 아픈 사람의 귀보다 잘 듣는다. 어떤 사람은 대부분의 사람들이 존재 조차 의식하지 못하는 음악과 소리에서 미묘한 차이를 듣도록 예외적으로 잘 훈련된다. 내가 모든 주류 mp3 인코더(과거에는 그들 모든 것들이 아주 좋지 못했다.)를 인지할 수 있었고 이중-맹검법(double-blind testing)에서 믿을 수 있게 이것을 증명할 수 있었을 때는 1990년 대의 어느 시점이었다.

 건강한 귀는 고도로 훈련된 감별능력을 가지고 있을때, 나는 그 사람을 황금귀라고 불렀다. 그렇기는 하지만, 평균 이하의 청력은 비훈련된 청음자를 뛰어 넘는 미세함을 인지하도록 훈련되어 질 수도 있다. 황금귀는 평균적인 사람들의 물리적 능력 이하의 청력보다 좀 더 훈련되어 있다.

 청력 연구자들은 진짜로 예외적인 청력을 갖는 예를 들자면 대단히 확장된 청력 범위를 갖는 개개인들을 찾아내고, 실험하고 문서화하는데 열의를 가지곤 한다. 일반적인 사람들도 훌룡하고 모든 것을 가지고있다. 그러나 모든 이들은 정말로 호감가는 페이퍼를 위해서 유전적인 놀라움을 찾고 싶어 한다. 우리는 과거의 100년간의 실험동안 그러한 어떤 사람도 찾지 못했다. 그래서 그들은 아마도 존재하지 않는다. 미안하다, 우리는 미래에도 계속해서 찾을 것이다.

스렉트럼 애호가들.

 아마도 여러분들은 내가 써놓은 모든 것에 대해서 의심이 많을 것이다; 이것들은 아마도 대부분의 시장의 광고물과는 반대로 가고있다. 대신, 기존의 오디오 분야에서 다루지 않는 가상적인 Wide Spectrum Video 현상을  한번 생각해 보자.

위의 그림: 가시광 대역의 스펙트럼위에 겹쳐서 표현한 사람의 눈의 시세포층의 근사한 log 스케일의 응답 특성이다. 이러한 감지 기관들은 귀의 모세포들이 소리의 겹쳐지는 파장대역에 응답하도록 조절되어 있듯이, 겹쳐진 스펙트럼 대역에서 빛에 응답한다.

 인간의 눈은 가시광 대역의 스펙트럼으로 알려진 빛의 제한된 범위의 주파수만 볼 수 있다. 이와 같은 현상은 소리의 파장의 가청 스펙트럼에 대해서도 동일하게 적용된다. 귀와 같이, 눈은 서로 다른 대역의 그렇지만 서로 겹쳐지는 파장 대역의 빛을 감지하는 감지 세포(시세포층)를 가지고 있다.

 가시광 스펙트럼은 400THz(강한 빨강색)에서 850THz(강한 보라색)까지 걸쳐있다[3]. 인지력은 양쪽 끝에서 급하게 떨어진다. 이런 대략의 제한지점 밑에서, 미세하게나마 감지하기 위해서 요구되는 빛의 파워는 여러분의 망막을 손상시킬 수 있다. 이와 같이, 이러한 사항들은 가청 스펙트럼의 일반적인 제한치로 해석되는, 젊고, 건강하고, 유전적으로 축복받은 개개인들에게 일반적인 범위이다.

 우리의 가상적인 Wide Spectrum Video 현상에서, 이러한 제한치들이 일반적으로 충분하지 않다고 믿는 열렬한 스펙트럼 애호가 그룹을 생각해 보자. 그들은 비디오가 가시광 스펙트럼 뿐만 아니라, 적외선 및 자외선 대역을 표현한다고 주장하는 것과 같다. 대치하여 비교를 계속해 보면, 이런 확장된 범위는 아직도 불충분하다고 주장하고 이런 비디오는 마이크로파와 X-ray 스펙트럼도 포함할때 꽤나 자연스럽게 느겨진다는 좀더 하드코어하고 자랑스러운 사실과 허구의 뒤범벅이 있다. 황금 눈에게 그들은 다른 점은 낮과 밤이다라고 주장한다.

 물론 이러한 것들은 터무늬 없는 일이다.

 아무도 X-ray나 적외선, 자외선, 그리고 마이크로파를 볼 수 없다. 사람들이 그가 얼마나 많이 할 수 있는지를 믿는지는 중요하지 않다. 망막은 간단한 감지 체계를 가지고 있지 않다.

 여기에 어떤이도 할 수 있는 실험이 있다: Apple사의 IR 리모콘을 가져오라. LED는 근적외선 대역인 약 306THz에 해당하는 980nm의 빛을 방출한다. 이 대역은 가시영역에서 그리 멀지 않은 영역이다. 리모콘을 지하실로 가져가거나, 여러분의 집에서 가장 어두운 방으로 가져가라. 그것도 한밤중에 모든 등들은 다끄고. 여러분의 눈을 어둠에 적응 시키자.


위의 사진: 디지털 카메라를 이용해 Apple사의 IR 리모콘을 찍은 사진. 비록 LED가 아주 밝고 가시광 대역의 빨간색 영역에서 멀지 않은 대역의 주파수를 방출할지라도, 이건 눈에 완벽히 보여지지 않는다.

  여러분들은 버튼을 눌렀을때 Apple사의 리모콘의 LED 플레시를 볼 수 있습니까[4]? 아니라구요? 아주 작은 양 조차도 볼 수 없다구요? 몇개의 다른 IR 리모콘도 시도해 봅시다: 많은 것들이 310-350THz에 해당하는 가시광에 아주 근접한 IR 파장을 사용한다. 여러분들은 그들도 마찬가지로 볼 수 없을 겁니다. 나머지 것들은 가시광 대역의 끝자락의 오른쪽에 해당하는 350-380Thz의 빛을 방출할지도 모르고 어둠에 적용된 눈에 완벽히 어둠에서 드물게 볼 수 있게 될지도 모른다[5]. 그들이 가시광대역 안쪽에 있었다면 모두 눈이 부시고 고통스럽게 밝을 것이다.

 이들 근적외선 LED들은 가시 주파수 한계점 밑에서 거의 20% 지점의 해당하는 가시광 영역의 경계지점의 빛을 방출한다. 192kHz 오디오는 가청한계의 400%에 해당한다. 나는 사과와 오렌지를 비교하는 우를 범하지 않기 위해서, 소리와 가시광의 인지가 양 한계점에서 유사하게 감소된다.

  이들 근적외선 LED들은 가시 주파수 한계점 밑에서 거의 20% 지점의 해당하는 가시광 영역의 경계지점의 빛을 방출한다. 192kHz 오디오는 가청한계의 400%에 해당한다. 나는 사과와 오렌지를 비교하는 우를 범하지 않는다, 소리와 가시광의 감지는 인지 한계지점들에서 유사하게 감소된다.

위험하다고 여겨지는 192kHz의 샘플링 주파수.

 192kHz 디지털 음악 파일들은 아무런 이득도 제공하지 않는다. 그런것들은 전혀 중립적이지 못하다; 실제적인 원음 충실도는 약간 나쁘다. 초음파는 재생되는 동안 골칫거리이다.

 소리 변환기와 파워 증폭기 둘다 왜곡이 없는 것은 아니다. 그리고 왜곡은 저주파와 고주파에서 갑작스럽게 증가하게 되어있다. 동일한 변환기가 가청 재생물에서 초음파를 발생시킨다면, 상호변환 디스토션 제품의 제어되지 않은 방출물이 모든 가청 주파수를 덮어 버리듯이 어떤 비선형은 약간의 초음파를 가청 범위의 영역으로 이동시키게 될 것이다. 파워 증폭기의 비선형성도 동일한 현상을 발생 시킨다. 현상은 매우 미약하지만, 청음 실험은 두 현상을 통하여 (초음파와 가청음) 다 듣을 수 있다는 것을 확인해 준다.

위쪽 그림: 약 0.09%정도의 변하지 않는 총 화음 왜곡(THD)을 갖는 이론적인 증폭기에서의 30kHz와 33kHz의 톤을 상호 변환기로 부터 유도된 디스토션 제품의 표현. 디스토션 제품들은 이 두 톤 보다 낮은 주파수를 포함하는 스펙트럼에 걸쳐서 표현된다. 

듣지 못하는 초음파가 상호 변환 디스토션에서 가청영역 (연한 파란색 영역)에 공헌한다. 일반적으로 초음파를 생산하지 않도록 디자인된 시스템은 20kHz이상에서 더 높은 양의 왜곡을 가지고 있다. 많은 공헌을 상호변환기가 하고 있다. 초음파의 발생을 설명하는 디자인된 주파수 대역의 확장은 가청 스펙트럼내의 노이즈와 왜곡의 발생을 감소시키는 타협안을 요구한다. 어느 쪽이든, 초음파의 불필요한 재생산은 성능을 떨어 뜨린다.

 추가적인 왜곡을 회피하는 몇 가지 방법이 있다:

  1. 여러분이 듣을 수 없는 초음파를 분리해서 독립적으로 재생산하는 전용 초음파 스피커, 증폭기, 그리고 교차형 중간체,등은 그져 여러분이 만드는 소리를 어지럽히지 않는다.
  2. 증폭기와 변환기는 더 넓은 주파수의 재생산을 위해 디자인 되었다, 그래서 초음파는 소리의 상호변환을 잃으키지 않는다. 같은 양의 비용과 복잡성이 주어진다면, 이 추가적인 추파수 범위는 스펙트럼의 가청 대역에서의 어떤 성능 개선의 비용을 발생 시키게 될 것이다.
  3. 스피커와 증폭기는 어쨋든 초음파를 발생시키지 않도록 디자인되어야 한다. 
  4. 시작 단계에서 넓은 대역을 포함하는 인코딩은 하지 말아야 한다. 초음파를 포함한 컨텐츠가 아니라면 여러분은 가청 대역에서 초음파 상호면환 왜곡을 만들거나 만들 수 없다.
 위의 것들은 모두는 같은 효과를 갖는 것들이다. 그러나 4번만이 합리성이 갖는다.

 여러분이 여러분 자신의 시스템의 성능에 대해서 의구심을 가지고 있다면, 다음의 샘플들은 30kHz와 33kHz의 톤을 가지고 있는 24bit/96kHz의 WAV 파일과 FLAC으로 된 긴버전과, 어떤 세가지 톤의 신호음 파일이다. 그리고 일반적인 노래 클립은 완벽히 24kHz에서 46kHz의 초음파 영역이 되기 위해서 24kHz로 주파수 상승이 가해졌다.


  • 상호변환 테스트:

30kHz tone + 33kHz tone (24 bit / 96kHz) [5 second WAV] [30 second FLAC]
26kHz - 48kHz warbling tones (24 bit / 96kHz) [10 second WAV]
26kHz - 96kHz warbling tones (24 bit / 192kHz) [10 second WAV]
Song clip shifted up by 24kHz (24 bit / 96kHz WAV) [10 second WAV
    (original version of above clip) (16 bit / 44.1kHz WAV)

 여러분의 시스템은 실제로 완벽한 96kHz 재생이 가능하다고 가정한다면[6], 위의 파일들은 어떤 들리는 노이즈, 톤, 휘파람소리, 클릭음, 혹은 다른 소리들이 없는 완벽히 무음이어야한다. 여러분이 어떤것을 듣게 된다면, 당신의 시스템은 초음파의 가청대역의 상호변환을 일으키는 비선형성을 가지고 있다고 봐야 한다. 소리를 높일때는 주의를 해야한다; 디지털 혹은 아날로그 음성을 가벼운 음성조차도 구동하는것은 갑자기 아주 큰 상호변환 톤을 일으킬 수도 있다.

 요약해 보자면, 초음파의 상호변환은 주어진 시스템에서 듣을 수 있다는 것은 불확실하다. 추가적인 왜곡은 사소한 것일 수도 있고 주의를 요하는 것일 수도 있다. 어느 쪽이든, 초음파는 전혀 이득 될 것이 없다. 그리고 많은 시스템에서 그것은 듣기에 확실히 고통 스러울 것이다. 고통을 주지 않는 시스템에서는 초음파를 다루는데 필요한 비용과 노력은 줄일 수 있고 대신 가청 범위의 성능을 개선하는데 사용되어 질 수 있다. 

샘플링에 대해 잘못 알려진 오류 그리고 오해

 샘플링 이론은 신호처리에 대한 지식이 없다면 대부분 이해하기 어렵다. 대부분의 사람들이 다른 분야의 지식있는 박사들 조차도 반복적으로 잘못 이해하고 있다는 것은 놀랄일이 아니다. 또한 많은 사람들이 그들이 잘못하고 있다는 것을 인지 조차 못하는 것도 놀랄일이 아니다.

위의 그림: 샘플링된 신호들은 자주 미세하지 못한 계단 모습(빨간색)으로 묘사 된다. 이것은 원래의 신호의 잘못된 추정치를 보여준다. 그러나, 이러한 표현은 수학적으로 정확하고 그 신호는 아날로그로 다시 복원되었을 때 원본 신호(파란색)의 정확히 부드러운 모습을 복구한다.

 대부분의 공통적인 오해는 샘플링은 원칙적으로 미세하지 못하고 손실을 발생한다는 것이다. 샘플링된 신호는 원래의 완벽한 부드러운 신호의 들쑥날쑥한, 심하게 꺽인 계단 모양의 복제혀으로 자주 묘사된다. 이것이 여러분이 샘플링이 어떻게 동작하는지 이해하는 이미지라면, 여러분은 아마도 더 빠른 샘플링 비율과 더 많은 샘플링 비트수를 좋은 것으로 여길 것이다. 아마도 그것은 더 미세한 계단 모양과 좀 더 원본에 가까운 추정치가 될 것이다. 디지털 신호는 무한대에 가까운 샘플링 비율로 갈 수록 원래의 아날로그 신호에 더욱 더 가까워지는 소리가 될 것이다. 

 비슷하게, 많은 디지털 신호처리 지식이 없는 사람들은 다음과 같은 사항을 살펴보게 될 것이다:

 그리고 말하자면 "어허!" 이러한 점은 샘플링된 신호는 더 높은 주파수의 아날로그 신호는 엉망으로 표현하게 된다는 점을 이야기 한다. 혹은, 음성 신호의 주파수가 증가함에 따라, 샘플링 품질은 떨어지고 주파수 응답 특성도 엉망이 된다. 즉 입력 신호에 대단히 민감하게 된다.

 보이는 것은 현혹적인 것이다. 이러한 믿음들은 잘못된 것이다!

2013-04-04에 추가된 내용:
디지털 신호와 계단모습에 관해 내가 가졌든 모든 메일에 덧붙여, 나는 여러분이 내 방식대로의 설명의 단순히 받아 들일 필요 없이 우리의 비디오 Digital Show & Tell에서 진짜 장비의 실제 디지털 원리를 보여준다.

과도한 샘플링

 48KHz 이상의 샘플링 레이트는 고음질의 오디오 데이터와 관계없지만, 몇몇 현재의 디지털 오디오 기술에 내제하는 핵심이다. 과도한 샘플링이 이와 가장 관련된 예제이다[7].

 과도한 샘플링은 단순하고 명징하다. 여러분들은 높은 샘플링 레이트가 (샘플링 레이트의 반에 해당하는) Nyquist 주파수와 우리가 듣을 수 있는 최대 주파수인 20Khz 사이의 더 넓은 대역을 아주 잘 다룰 수 있다는 것을 보여주는 나의 A Digital Media Primer for Geeks을 되돌아 보아라.
이것은 더 단순하게, 자연스럽게, 더 신뢰할 만한 아날로그 anti-aliasing 필터를 구성할 수 있게하고 그에 따라서 더 고음질을 갖도록 해준다. 20KHz와 Nyquist 주파수 사이의 이 추가 대역은 본질적으로 그져 아날로그 필터를 위한 스펙트럼의 여유공간이다.

위의 사진: A Digital Media Primer for Geeks으로 부터의 흰 칠판 그림은 48KHz의 ADC/DAC(왼쪽)과 96KHz의 ADC/DAC를 위해 사용되는 변환대역폭을 묘사한다.
 이것은 단지 한면만 이야기하는 것이다. 디지털 필터는 아날로그 필터를 실제적으로 구현하는 제한사항을 몇 가지 가지고 있기 때문에, 우리는 더 훌룡한 효율성과 디지털적으로 정확성을 가지는 anti-aliasing 과정을 완성할 수 있다. 매우 높은 샘플링 레이트의 순수한 디지털 신호가 디지털 anti-aliasing filter를 통과하는 작업은 전송 대역을 빡빡한 대역에 맞추는데 문제가 없다.
이 확장된 디지털 anti-aliasing filter를 거친후, 여분의 대역에 있는 샘플들은 그져 사라져 버린다. 과도하게 샘플링된 음원을 플레이하는 과정은 이와의 반대의 과정으로 대부분 이루어 진다.

 이것은 우리가 192kHz나 더 높은 샘플링이 갖는 모든 고음질의 이득들을(자연스러운 주파수 응답, 낮은 aliasing) 가지고 44.1KHz나 48kHz등의 낮은 샘플링 주파수를 사용할 수 있고 단점은 전혀 없다 (복원 과정의 왜곡을 일의키는 초음파는 쓸모없는 것이다.). 거의 모든 오늘날의 ADC와 DAC들은 매우 높은 주파수의 과도한 샘플링으로 이루어진다. 몇몇의 사람들은 이것이 순전히 자동적으로 인지하지 못하게 이루어지기 때문에 잃어나는 현상이라는 것을 알고 있다.

 ADC와 DAC는 항상 눈에 보이게 과도한 샘플링을 하지 않는다. 30년전에 어떤 녹음 기기들은 단지 아날로그 필터만을 이용해서 높은 샘플링 주파수로 녹음하였고, 결과물과 mastering은 단순히 그 높은 주파수의 신호를 사용하였다. 디지털 anti-aliasing과 decimation 과정들은 (CD 나 DAT를 위해 낮은 주파수로 다시 샘플링하는 작업) matering의 마지막 단계에서 이루어졌다. 이것이 아마도 96kHz와 192kHz가 전문적인 음악 산업과 관련이 있게 되게된 초기의 이유 중 하나일 것이다.

16bit 대 24bit


.............번역중.................

2013년 10월 10일 목요일

데비안 리눅스 백업하고 복구하기

 얼마전에 32bit  커널을 사용하다가 무슨 이유에서인지 apt-get dist-upgrade를 통해 업데이트 하게 되었을때 커널이 지워져 버리고 먹통이 되어 있던 경우가 있었다.

 지금에 와서 되돌아 생각해 보니 배포본을 wheezy에서 jessie로 변경하면서 뭔가 꼬인것 같다. 더이상 32bit 커널로 되돌아 갈 방법이 없어진 것 같다. testing 버전이 jessie로 바뀌면서 의도적인지 실수인지 32bit 버전의 intel 버전을 찾아 볼 수가 없어졌다.

 그래서 설치된 데이안 패키지를 백업하고 나중에 잘못 되었을 때 복구할 수 있는 방법이 필요하다고 생각해서 찾아 보았다.

 다음은 지금 현재의 데비안 시스템에 설치된 패키지 정보를 백업하는 명령어이다.

# dpkg --get-selections > /backup/installed-software.log

위와 같이 하면 현재 설치된 데비안 패키지 리스트를 installed-software.log에 저장하게 된다.

그리고 혹시라도 시스템이 먹통이 되어 복구가 필요할 경우 데비안 시스템을 최소 선택으로 설치하고 (base system만 설치하고) 이러한 상황이면 무선랜은 동작하지 못할 것이다. 네트워크가 연결된 상태에서 (아마도 유선랜이 연결된 상태에서) 다음과 같이 저장된 데비안 패키지 리스트에서 선택적으로 데비안 시스템을 복구 할 수있다.

# dpkg --set-selections < ./backup/installed-software.log

# apt-get dselect-upgrade

혹은 하나의 명령어로

# aptitude install $(cat ./backup/installed-software.log)

덧 붙여 주의 할 점은 홈 디렉토리에 있는 '.'으로 시작하는 설정 파일들은 따로 백업해야하고 그 이외의 /etc/ 밑에 있는
/etc/X11/xorg.conf,
/etc/default/cpufrequd,
/etc/cpufreqd.conf,
/etc/apt/*
/usr/share/X11/xorg.conf.d/*,
등도 마찬가지로 백업해 두고 복구시 같이 원래의 자리로 복사해 두는 것이 많은 수고를 덜어 줄 것이다.

참조 : https://wiki.debian.org/ListInstalledPackages

내가 생각하는 앞으로의 브랜드 PC의 나아갈 방향

 가끔 Laptop을 사서 사용하거나 남의 브랜드 PC를 손보아 줄때 너무나도 쓸떼없는 프로그램들이 깔려있다는 것을 느낀다. 그렇다고 브랜드 별로 특화되어서 설치된 프로그램에서 특별한 유용성을 찾을 수도 없다. 그나마 유용한 것은 드라이버를 자동으로 업데이트하는 유틸리티 정도이다. 그나마도 제대로 관리되지 않아서 그냥 각 부품별로 최신 버전을 다운로드 받아서 사용하는 것이 더 효과적이다.

 아무튼 이러한 브랜드 PC가 가져야할 브랜드 별로 특화되어 차별성을 가지게 하는 그 무엇이 필요하다. 내가 사용하면서 혹은 남의 컴퓨터를 손보아 주면서 느끼는 점은 사용 목적에 따른 프로그램 추천 리스트 혹은 패키지 관리자의 필요성이다.

 이와 같은 방식의 관리자는 윈도우 자동 업데이트나 리눅스의 패키지 관리자에서 사용하고 있다. 하지만, 이와 같은 관리자들은 대상이 너무 협소하거나(윈도우 업데이트 관리자) 너무 선택 사항이 너무 많아서 어느 것을 사용해야 할지 막막하다(리눅스의 패키지 관리자). 좀더 특화하여 공통의 관심 사항을 가지고 있는 사람의 특성을 반영하여 프로그램 패키지를 관리 할 수 있는 관리자가 필요하다.

 우선 언듯 드는 분류는 사무용, 개임용, 디자이너용, 개발자용으로 특정 사용자들을 상대로 주기적인 프로그램 사용 빈도에 따라 컴퓨터 환경을 설정해서 판매하거나 혹은 믿을 만한 사용자 몇몇을 특정하여 그의 컴퓨터 환경을 그대로 모방하는 방법등이 있을 것이다. 요즘 컴퓨터의 하드웨어에서는 별 특징점을 찾아 보기가 힘들기 때문에 이렇게 해야 점점 줄어 드는 브랜드 PC의 살아 갈길을 찾아 볼 수 가 있을 지 않을까 생각한다.

2013년 10월 4일 금요일

mjpeg-streamer의 작동원리

 앞으로 사용해야 할 일이 발생할 것 같아서 시간도 넉넉하고 해서 mjpeg-streamer를 분석해 보려고 한다. 가장 핵심적인 부분은 MJPEG 방식으로 압축하는 부분이기에 실행파일의 껍데기에 해당하는 mjpeg-streamer.c파일에는 영양가 있는 내용이 없다. 거의 매개변수 해석해서 plugin에 해당하는 *.so 파일을 읽어 들이도록 해서 입력과 출력을 지정하는 일이 대부분이다.

 내가 원하는 MJPEG Encoding에 대한 내용은 plugin에 해당 하는 소스코드가 있는 mjpg-streamer/plugins/input_uvc/input_uvc.c에 있다. 그리고 핵심적인 부분을 살펴보면 encoding 부분에서 jpeglib를 활용하고 있다. 어쩐지 소스가 짧더라 했다.

 그래도 사진 압축 방법인 jpeglib로 어떻게 MJPEG 압축을 수행하게 되는지 알아보는 것이 필요하다. 구체적으로 다음 cam_thread() 함수에 uvc 드라이버를 이용해서 카메라의 영상을 실제적으로 MJPEG으로 압축하는 기능을 수행한다. 우선, 소스 코드는 다음과 같다. 출처는 http://sourceforge.net/p/mjpg-streamer/code/HEAD/tree/mjpg-streamer/plugins/input_uvc/input_uvc.c 이다.

/******************************************************************************
Description.: this thread worker grabs a frame and copies it to the global buffer
Input Value.: unused
Return Value: unused, always NULL
******************************************************************************/
void *cam_thread( void *arg ) {
  /* set cleanup handler to cleanup allocated ressources */
  pthread_cleanup_push(cam_cleanup, NULL);

  while( !pglobal->stop ) {

    /* grab a frame */
    if( uvcGrab(videoIn) < 0 ) {
      IPRINT("Error grabbing frames\n");
      exit(EXIT_FAILURE);
    }
  
    DBG("received frame of size: %d\n", videoIn->buf.bytesused);

    /*
     * Workaround for broken, corrupted frames:
     * Under low light conditions corrupted frames may get captured.
     * The good thing is such frames are quite small compared to the regular pictures.
     * For example a VGA (640x480) webcam picture is normally >= 8kByte large,
     * corrupted frames are smaller.
     */
    if ( videoIn->buf.bytesused < minimum_size ) {
      DBG("dropping too small frame, assuming it as broken\n");
      continue;
    }

    /* copy JPG picture to global buffer */
    pthread_mutex_lock( &pglobal->db );

    /*
     * If capturing in YUV mode convert to JPEG now.
     * This compression requires many CPU cycles, so try to avoid YUV format.
     * Getting JPEGs straight from the webcam, is one of the major advantages of
     * Linux-UVC compatible devices.
     */
    if (videoIn->formatIn == V4L2_PIX_FMT_YUYV) {
      DBG("compressing frame\n");
      pglobal->size = compress_yuyv_to_jpeg(videoIn, pglobal->buf, videoIn->framesizeIn, gquality);
    }
    else {
      DBG("copying frame\n");
      pglobal->size = memcpy_picture(pglobal->buf, videoIn->tmpbuffer, videoIn->buf.bytesused);
    }

#if 0
    /* motion detection can be done just by comparing the picture size, but it is not very accurate!! */
    if ( (prev_size - global->size)*(prev_size - global->size) > 4*1024*1024 ) {
        DBG("motion detected (delta: %d kB)\n", (prev_size - global->size) / 1024);
    }
    prev_size = global->size;
#endif

    /* signal fresh_frame */
    pthread_cond_broadcast(&pglobal->db_update);
    pthread_mutex_unlock( &pglobal->db );

    DBG("waiting for next frame\n");

    /* only use usleep if the fps is below 5, otherwise the overhead is too long */
    if ( videoIn->fps < 5 ) {
      usleep(1000*1000/videoIn->fps);
    }
  }

  DBG("leaving input thread, calling cleanup function now\n");
  pthread_cleanup_pop(1);

  return NULL;
}

 이름에서도 알 수 있듯이 영상을 받아오는 쓰레드이다. 전체적인 흐름은 uvc 드라이버로 부터 영상 프레임이 존재하는지 감시하고 이 영상을 global변수의 버퍼 내로 복사한다. 그리고 복사된 영상의 포멧에 따라서 jpeg 압축을 수행한다. 여기서, uvc 드라이버는 좀 더 검색해 보아야 할 내용이지만 잠시 살펴본 결과 많은 종류의 USB 캠을 구동하게 만들어 주는 드라이버이다. 위의 루틴이 쓰레드이기 때문에 메모리를 관리하기 위해서 필요한 mutex나 broadcast 같은 것들은 코드를 이해하는데 불필요하기 때문에 무시하고 넘어가자.

 아무튼 핵심적인 내용은 compress_yuyv_to_jpeg()에 있다. 이 루틴은 http://sourceforge.net/p/mjpg-streamer/code/HEAD/tree/mjpg-streamer/plugins/input_uvc/jpeg_utils.c 에 있다. 이 루틴을 실행하기 위해선 uvc 드라이버로 부터 넘어 오는 영상 데이터가 V4L2_PIX_FMT_YUYV이어야 한다. 일반적으로 YUV4:2:2라고 일컷는 Bayer filter 형식의 영상일 경우 jpeg 압축을 수행한다.

/******************************************************************************
Description.: yuv2jpeg function is based on compress_yuyv_to_jpeg written by
              Gabriel A. Devenyi.
              It uses the destination manager implemented above to compress
              YUYV data to JPEG. Most other implementations use the
              "jpeg_stdio_dest" from libjpeg, which can not store compressed
              pictures to memory instead of a file.
Input Value.: video structure from v4l2uvc.c/h, destination buffer and buffersize
              the buffer must be large enough, no error/size checking is done!
Return Value: the buffer will contain the compressed data
******************************************************************************/
int compress_yuyv_to_jpeg(struct vdIn *vd, unsigned char *buffer, int size, int quality) {
  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;
  JSAMPROW row_pointer[1];
  unsigned char *line_buffer, *yuyv;
  int z;
  static int written;

  line_buffer = calloc (vd->width * 3, 1);
  yuyv = vd->framebuffer;

  cinfo.err = jpeg_std_error (&jerr);
  jpeg_create_compress (&cinfo);
  /* jpeg_stdio_dest (&cinfo, file); */
  dest_buffer(&cinfo, buffer, size, &written);

  cinfo.image_width = vd->width;
  cinfo.image_height = vd->height;
  cinfo.input_components = 3;
  cinfo.in_color_space = JCS_RGB;

  jpeg_set_defaults (&cinfo);
  jpeg_set_quality (&cinfo, quality, TRUE);

  jpeg_start_compress (&cinfo, TRUE);

  z = 0;
  while (cinfo.next_scanline < vd->height) {
    int x;
    unsigned char *ptr = line_buffer;

    for (x = 0; x < vd->width; x++) {
      int r, g, b;
      int y, u, v;

      if (!z)
        y = yuyv[0] << 8;
      else
        y = yuyv[2] << 8;
      u = yuyv[1] - 128;
      v = yuyv[3] - 128;

      r = (y + (359 * v)) >> 8;
      g = (y - (88 * u) - (183 * v)) >> 8;
      b = (y + (454 * u)) >> 8;

      *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
      *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
      *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);

      if (z++) {
        z = 0;
        yuyv += 4;
      }
    }

    row_pointer[0] = line_buffer;
    jpeg_write_scanlines (&cinfo, row_pointer, 1);
  }

  jpeg_finish_compress (&cinfo);
  jpeg_destroy_compress (&cinfo);

  free (line_buffer);

  return (written);
}


 위의 코드는 기본적으로 libjpeg을 사용한다. 이 라이브러리는 디폴트로 압축된 결과를 기본 입출력에 해당하는 하드 디스크에 파일로 저장한다. 그래서 압축 결과물이 메모리에 담겨 지도록 다음과 같이 설정한다.

  /* jpeg_stdio_dest (&cinfo, file); */
  dest_buffer(&cinfo, buffer, size, &written);

  그리고 YUV 4:2:2 방식의 영상을 RGB로 변환하는 Interpolation 과정을 수행하기 위한 버퍼(line_buffer)를 새로 할당하고, uvc로 부터 날라온 YUV 4:2:2 형식의 영상 버퍼도 지정한다.

  line_buffer = calloc (vd->width * 3, 1);
  yuyv = vd->framebuffer;

 나머지 것들은 libjpeg을 이용할 때 수행하는 표준적인 방법으로 압축을 수행한다.

  while (cinfo.next_scanline < vd->height) {
    int x;
    unsigned char *ptr = line_buffer;

    for (x = 0; x < vd->width; x++) {
      int r, g, b;
      int y, u, v;

      if (!z)
        y = yuyv[0] << 8;
      else
        y = yuyv[2] << 8;
      u = yuyv[1] - 128;
      v = yuyv[3] - 128;

      r = (y + (359 * v)) >> 8;
      g = (y - (88 * u) - (183 * v)) >> 8;
      b = (y + (454 * u)) >> 8;

      *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
      *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
      *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);

      if (z++) {
        z = 0;
        yuyv += 4;
      }
    }

    row_pointer[0] = line_buffer;
    jpeg_write_scanlines (&cinfo, row_pointer, 1);
  }

  jpeg_finish_compress (&cinfo);
  jpeg_destroy_compress (&cinfo);

 대체적인 내가 필요한 부분은 다 파악한 것 같다. 여기 저기서 보고 되는 문제점인 YUV를 RGB로 변환하는 과정이 너무나도 씨피유를 잡아 먹는 다는 점이다. 우선 가지고 테스트 해볼 만한 보드가 없어서 위의 코드에서 inline assembly를 사용한다던가 하는 개선책이 효과적일지 알아 볼 수 없지만, 추후에 이거 너무 느린걸 하면 위의 코드를 건드려 보도록해야 겠다.

 참고로 지금 불티나게 팔리고 있는 BeagleBone Black과 Raspberry Pi에서 VGA 급의 영상은 5fps도 안나온다고 한다. 그리고 그 원인의 대부분은 YUV->RGB 변환에 너무 많은 것이 소요하고 있다는 점이다.

 그리고 하나 더 mjpeg 압축이 어떻게 이루어 지는 명확해 졌다. 그냥 한 프레임씩 jpeg으로 압축하는 게 전부 인것 같다. 좀더 살펴 보아야 하겠지만, 위의 코드로는 그렇게 파악되어 진다.

[번역중] GTK+에서의 화면 관리

출처 : http://www.compsci.hunter.cuny.edu/~sweiss/course_materials/csci493.70/lecture_notes/GTK_drawing.pdf

이 내용은 Prof. Stewart Weiss가 강의 노트를 위의 링크에 올려 놓은 것을 단순히 번역한 것입니다.

배경 지식

GTK에서 어떻게 화면을 그리는 것을 이해하기 위해서,
여러분들은 GTK가 어떻게 widget 들을 그리는지에대한 것을 먼저 이해 해야만 한다.
그 이유는 GTK가 widget을 그리는 방식은 여러분의 그림을 출력하는 응용 프로그램을 어떻게 디자인해야하는데 중요한 역할을 가지고 있기 때문이다.
GTK가 widget들을 어떻게 그리는지를 이해하는 것은 여러분들이 자신만의 자체 widget들을 만들 계획이 있다면 또한 필요하다.

윈도우와 윈도우 갱신

대부분의 윈도우 시스템들은 응용 프로그램의 외형 표현이 윈도우라고 불리우는 화면의 네모난 영역내에 구현되는 개념으로 디자인 되어 있다. Gnome, KDE 혹은 Explorer 같은 윈도우 시스템은 응용 프로그램의 윈도우의 보여지는 내용을 자동적으로 저장하지 않는다. 그 대신 시스템은 필요할때 응용 프로그램 자신에게 자신의 윈도우를 다시 그려야 한다고 요청한다. 예를 들면, 다른 윈도우들 밑에 깔려 있는 어떤 윈도우가 가장 위에 놓이게 된다면, 해당 프로그램은 이전에 가려진 영역을 다시 그려야한다. 윈도우 시스템은 윈도우의 가려진 부분을 다시 그리도록 해당 프로그램에게 요청할때, 그 윈도우를 포함하는 프로그램에게 exposure 라는 이벤트를 보낸다. exposure 이벤트는 자체적으로 다시 그려져야 한다는 것을 알리기 위해 윈도우 시스템이 Widget에게 보내지는 단순한 이벤트이다.

이 단락에서는 윈도우는 자동적인 갱신이 가능한 내모난 영역을 의미하고 고차원의 응용 프로그램을 의미하는 것은 아니다. 화면 갱신은 다시 그려질 필요가 없거나 다른 방식으로 보여져야하는 윈도우의 어떤 부분을 제거하는 작업이다. 이것은 어떤 것들이 다시 그려져야하는 윈도우의 영역인지 판단하는 것이다. 그림 1에서 윈도우 A는 윈도우 C 밑에 있는 윈도우 B 밑에 있다. 사용자가 윈도우 A를 화면의 앞으로 가져오는 작업을 한다면, 다시 그려져야만하는 윈도우 A의 영역은 그림에서 그림자로 되어 있는 A ∩ (B ∪ C)안에 있는 윈도우 A의 부분이다.




대부분의 윈도우 시스템들은 자식 윈도우라고 불리우는 중첩된 윈도우를 지원한다. 고차원의 윈도우는 차례로(?) 자식 윈도우를 포함할지도 모르는 많은 자식윈도우들을 포함할 지도 모른다. 예를 들면, 고차원의 윈도우는 일반적으로 메뉴 바를 위한 윈도우와 문서 영역을 위한 윈도우,  각 스크롤바를 위한 윈도우, 그리고 상태바를 위한 윈도우를 포함할 것이다. 게다가, 클릭이 가능한 버튼 (일반적으로 자체 하위 윈도우를 같는) 같은 사용자 입력을 받는 컨트롤도 포함한다. GTK+는 중첩된 윈도우들에 대한 인지 없이 윈도우 시스템에서 실행되는 것을 가능하다. 이러한 방식은 GDK가 해당 시스템하에서 존재하는 그림 그리기 기능을 제공하기 때문에 문제가 되지 않는다. 그러므로, GTK+에서 중첩된 윈도우는 항상 가능하다.

화면 갱신 반복 작업

일반적으로 화면 갱신 반복 작업은 GTK+가 하부에서 작동하는 윈도우 시스템으로 부터 exposure 이벤트를 받을때 시작된다. 예를 들면 사용자가 윈도우를 다른 윈도우 위로 드레그하게 되었을때가 대표적이다. 이러한 경우 윈도우 시스템은 자체적으로 다시 그릴 필요가 있는 아래에 있는 윈도우에게 알려 주게 될 것이다. 또한, 이러한 화면 갱신 반복 작업은 윈도우 내의 Widget 자체가 다시 그려져야할 필요성이 있을때 시작되어 질 수도 있다. 예를 들면, 사용자가 GtkEntry Widget에 글자를 적어 넣을 때, GtkEntry Widget은 GTK+에게 자신을 다시 그려야 한다는 요구을 접수해 줄 것을 요청한다. 다른 말로, Widget은 윈도우 시스템 자체가 자신을 다시 그리는 event를 보내도록 요청하는 기능을 호출 할 수 있다. 이것은 여러분이 바로 볼 수 있을 만큼 프로그램을 단순화 시킨다.

GdkWindow는 GTK+가 실행되는 상황에서 맨 밑에서 있는 윈도우 시스템의 윈도우를 대표한다. X11 에서 이것은 (X) Window에 해당하고 Win32의 경우 HANDLE에 해당한다. 이것은 이러한 윈도우들을 위한 event를 생성하는 윈도우 시스템이다. 이러한 event 들은 GDK 인터페이스로 전달된다. 이는 이러한 원래의 event들을 GdkEvent 의 형태로 변환하고, GTK 층으로 전달한다. 그리고 그 다음으로 GTK widget 층은 특정 GdkWindow에 해당하는 widget을 검색하고, 그 widget에 연계되는 event 시그널들을 발생한다.

GdkWindow에 있거나 없는 Widget들

모든 단일 widget은 그 자신의 GdkWindow을 가졌다면, 화면 갱신 작업은 당연히 이루어 질 것이다; 밑에서 동작하는 윈도우 시스템은  다시 그려질 필요가 있는 윈도우에 해당하는 GDK에게 통지한다. 그리고 GDK는 특정 GdkWindow를 위한 exposure event를 GTK 층에 전달하고 GTK 층은 그 widget을 위한 exposure event 시그널을 발생 시키게 될 것이다. 그러면 widget의 exposure event 핸들러는 widget을 다시 그리게 될  것이다. 그 밖의 다른 것은 할 것이 없다; 윈도우 시스템은 필요로 하는 각각의 윈도우를 위한 exposure event를 발생하고, 그에 해당하는 각 widget은 자신을 다시 그리게 될 것이다.

그러나, 실제적으로 widget들이 자신의 GdkWindow를 갖지 못하도록 하는 것이 더 효과적이고 편리하기 때문이다. 그러나 대신 상위 widget으로 부터 하나를 공유 한다. 모든 widget은 widget이 자신의 윈도우를 갖는지 않 갖는지를 알리는 GTK_NO_WINDOW를 갖는다. widget이 윈도우를 갖지 않는다면 반드시 생성될때 이 플레그(GTK_NO_WINDOW)를 true로 설정하여 GTK에게 갖고 잊지 않다는 것을 알려야 한다. (GTK+ 2.18 혹은 그 이후의 버전에서 widget은 gtk_widget_set_has_window()의 두 번째 매개변수를 false로 설정하여 호출한다.) 이것은 프로그래머로써 해야하는 것은 아니다. 이것은 widget의 생성시에 이루어져야 하는 widget의 구현자가 해야하는 작업이다. 프로그래머로써 여러분들은 특정 widget이 GdkWindow를 가지고 있는지 아닌지를 GTK_NO_WINDOW의 값을 검사하거나 GTK+-2.18 이후의 버전에서는 gtk_widget_get_has_window() 함수를 호출하여 알아 볼 수 있다. GdkWindow를 갖지 않는 widget들은 no-window widget 혹은 GTK_NO_WINDOW widget으로 불린다.

왜 여러분은 윈도우가 없는 widget을 필요로 하게 될까요? 두 가지 큰 이유가 있다.

  • 어떤 widget들은 부분적으로 그려져야할 지라도 상위 widget의 배경이 보여지기를 원할지도 모른다. 예를 들면 다음과 같은 경우이다. label이 themed texture를 가지고 있는 button위에 위치하고 있을때, 각각의 widget이 윈도우를 가지고 있고 배경을 가지고 있다면, 그들을 둘러싸는 것을 고려하여 보여지는 것이 멈추게 될지 모르기 때문에 label은 나쁘게 보여질 것이다. 

  • GDK 윈도우들의 총 개수를 줄이는 것은 GDK와 GTK 사이의 데이터 교류의 양을 줄인다. 그래서 윈도우가 없는 widget을 구현할 수 있다면 성능은 한층 더 좋아 진다. 

계층적인 그리기

이와 같은 사항을 이해 한다면, 우리는 우리의 관심을 GTK가 exposure event를 받을 때 발생하는 일련의 과정을 이해하는 것으로 돌려 보자. 가장 맨 처음 과정은 GTK가 event를 받은 윈도우에 해당하는 Widget을 찾는 것이다. 이 Widget은 no-window widget일 수 없다. (만약 그렇다면 event를 받을 만한 자신의 윈도우를 아예 갖을 수 없기 때문에 처리할 event라는 것은 존재 하지 않는다.) 이와 같은 Widget들은 배경을 그리는 것을 시작한다. 그런 다음 해당 Widget이 container widget이라면, 윈도우를 갖지 않는 자식 Widget들 각각에게 자신들을 다시 그리도록 이야기 해준다. 이러한 과정은 원래의 Widget의 윈도우가 없는 모든 상속자들에게 반복적으로 적용된다.

이러한 과정은 (GTK_NO_WINDOW가 true로 설정된 Widget들인) 자신의 윈도우들을 갖는 Widget에게 전달되어 지지는 않는다. 이것은  이러한 어떤 Widget들이 다시 그려져야한다면, 윈도우 시스템은 그들에 해당하는 GDK 윈도우에 exposure events를 보내게 될 것이기 때문이다. 이와같이, GTK+의 관점에서 이들에게 exposure를 전달할 필요는 없다.

예를 들자면 (GTK API 문서로 부터)

다음 예제는 온라인상의 Gnome/GTK +의 문서 (https://developer.gnome.org/gtk3/unstable/chap-drawing-model.html에 있는 GTK+ Drawing Model) 로부터 가져왔다. 이것은 어떻게 상위 단계의 윈도우가 윈도우가 없는 자식들만 갖을때 자신을 다시 그리는지를 보여준다. 


  1. 최외각의 두꺼운 네모는 상위 레벨의 GtkWindow이다. 이것은 GTK_NO_WINDOW인 Widget은 아니다. 그러므로, exposure event를 반드시 받게된다. 왜냐하면, GDK로부터 생성되었기 때문이다. 먼저 GtkWindow는 그 자신의 배경을 그리게 될 것이다. 그런다음, 자식 윈도우에게 자신을 다시 그릴 것을 요청한다. 여기서는 2번으로 표시된 점선으로된 네모가 이에 해당한다. 
  2. 점선으로된 네모는 GtkVBox이다. 이것은 GtkWindow의 단독 자식을 형성한다. GtkVBox류는 윈도우를 갖지 못하는 Widget이다. 그들 자체로는 아무것도 그릴 것이 없는 그져 Layout Container이다. 그레서 GtkVBox는 그릴 것이 아무것도 없다. 그러나, 대신 그릴 것을 가지고 있는 자식들에게는 요청을 한다. 그 자식들은 3번과 6번에 해당한다.
  3. 얇은 네모는 GtkFrame이다. 물론 윈도우를 가지고 있지 않고 두 개의 자식들을 가지고 있다; 4번으로 표시된 프레임을 위한 Label과 5번으로 표시된 또 다른 내부의 Label이다. 먼저, 프레임은 그 자신의 중간이 짤린 박스를 그리게 되고, 프레임의 Label과 그 안의 자식들에게 그들 자신을 다시 그리도록 요청한다. 
  4. 프레임의 Label은 자식을 갖지 않는다. 그레서 그져 문자만 그린다. "Frame Label"
  5. 내부의 Label은 자식을 갖지 않는다. 그레서 그져 문자만 그린다. "This is some text inside the frame!"
  6. 점선으로 된 네모는 GtkHBox를 의미한다. 다시 말해서, 이것은 스스로 아무것도 그릴 것이 없지만, 그릴 것을 가지고 있는 그의 자식들에게는 요청을 한다. 그 자식들은 7번과 9번에 해당한다.
  7. 얇은 네모는 8번으로 표신된 자식을 하나만 가지고 있는 GtkButton이다. 먼저, 버튼은 그 자신의 박스를 그리고, 자식들에게 자신을 그릴 것을 요청하게 된다.
  8. 이것은 자식을 가지지 못하는 text label이다. 그레서 그져 문자만 그리면 된다.: "Cancel".
  9. 7번과 유사하게 이것은 10번으로 표시된 자식 하나만 가지고 있는 GtkButton이다. 먼저, 버튼은 박스를 그리고 그려야 할 것을 가지고 있는 자식들에게 요청을 하게된다.
  10. 8번과 유사하게 이것은 자식을 갖지 않는 GtkLabel이다. 그레서 자신의 문자만 그리면 된다: "OK".

위의 과정은 어떻게 Widget이 다시 그려지는지 보여준다. 이러한 그리기작업은 모두 화면에 직접적으로 위치해야 한다면, 다양한 부분들이 다시 그려짐에 따라 깜박거림이 발생하게 될 것이다. 이것은 부분적으로 배경과 장식용 요소같은 많은 영역들이 반복적으로 다시 그려지게 되기 때문이다.
그러므로, GTK는 GDK레벨에서 double buffering이라는 개념을 구현하게된다.

Double Buffering

double buffering에서 두개의 캔퍼스들이 존재한다: 화면에 안보이는 캔퍼스와 화면에 보이는 캔퍼스. 그리는 작업은 안보이는 캔퍼스위에서 발생하고 그러고 나서 화면에 그려지게 된다. 그리고 화면에 있었던 캔퍼스는 다음의 그리기 작업을 위해서 화면에 안보이는 캔퍼스가 된다. GTK에서 이런 double buffering은 Widget들에게 보이지 않는 부분이다; 그들은 일반적으로 그들이 화면에 안보이는 버퍼에 그려지고 있는 알지 못한다. 그들은 그져 그들의 일반적인 그리기 명령을 이슈화 할 뿐이다. 그리고 버퍼는 모든 그리기 작업이 완료되었을때 윈도우 시스템에게 돌려주게 된다.

GDK에서 두 개의 기본적인 함수들이 double-buffering의 작동원리의 핵심을 형성한다.
  • gdk_window_begin_paint_region()
  • gdk_window_end_paint()
첫째 함수는 GdkWindow에세 그리기 작업을 위해서 화면에 보이지 않는 버퍼를 생성할 것을 요청한다. 이러한 시스템에 가해지는 모든 차후의 그리기 과정들은 자동적으로 이 버퍼로 재 지정되게 된다. 두 번째 함수는 보이는 화면에 버퍼를 실질적으로 그리게 되고 버퍼를 해제하고 되돌려 주게된다. 

자동화된 double buffering

이것은 exposure 핸들러에 초반부에 gdk_window_begin_paint_region()을 호출하고 마지막 부분에서 gdk_window_end_paint()를 호출하는 모든 Widget들에게는 적용되지 않는다. 이걸 좀더 쉽게 하기 위해서, 모든 GTK+ Widget들은 


............ 번역중 ......

[추가중] 공돌이의 가내 수공업을 위한 준비

PCB Etching 도구

레이져 프린터 혹은 UV LED 조명, 투명 프린터 용지, 약간의 화학약품, 구멍뚤기위한 탁상용 핸드드릴.

탁상용 핸드 드릴 - 드레멀 사의 핸드드릴이 유용한 것 같다. 일부 몇몇 CNC 머신에 이 드레멀 사의 핸드 드릴을 회전자(spindle)로 사용하기 때문에 나중에 CNC 머신제작 하고자 할때 드레멀 드릴을 회전자로 그대로 사용할 수 있고(http://www.instructables.com/id/Building-a-drawer-slide-CNC-machine-for-under-200/, http://www.jcopro.net/2011/08/01/a-dremel-tool-mount-for-the-zen-toolworks-cnc-router/http://www.lirtex.com/robotics/diy-cnc-machine/) 그 부분을 플라스틱을 녹여 분사해 주는 것으로 바꾸면 충분히 3D 프린터로도 사용할 수 있다. 드레멀 사의 핸드 드릴은 너무나도 많기 때문에 좀 더 살펴봐야 하지만, 아래의 링크를 살펴보면 CNC 라우터의 회전자로 사용하기에 200시리즈는 소리도 너무 크고 스피드도 만족 스럽지 못하다고 4000시리즈를 사라고 추천해 준다. (http://www.jcopro.net/2011/12/02/what-is-the-best-dremel-tool-spindle-for-your-cnc-router/) 아무튼 나중을 위해서 드레멀사의 핸드 드릴을 구매해야 겠다.

UV LED 조명 - 아직 어느 파장으로 해야하는지 알지 못하겠다. 보통은 400nm를 사용하지만, 아직 많이 찾아 보질 못했다. 문제는 어느 정도의 광량이 필요하고 어느 정도의 전력이 요구 되냐 이지만, 회로구성은 MOSFET과 TR그리고 전류 감지용 와트 저항만 있으면 충분히 대용량의 LED도 작동 시킬 수 있기 때문에 크게 걱정되지 않는다. 아래의 사이트는 UV 대역을 투과 시키는 투명한 유리를 시용해서 만든 것 같다. 참고해 볼 만하다.

 http://www.instructables.com/id/DIY-Portable-UV-Exposure-Box-PCB-Making-Gadget/

PCB 에칭용 화학 용품들 -

3D 프린터

현 시점에서 $1000 ~ $2000달러의 금액이면 기본적이 환경을 갖출 수 있다. 소프트웨어는 STL 파일 형식만 지원하면 구동이 가능한것으로 보인다. 현 시점에서 3D 프린터에 대한 관심이 많아서 그런지 너무나도 많은 제품들이 존재 한다. 하지만 살펴본 바에 의하면 아래의 제품이 추가적으로 갖추어야 할 것들이 존재하지만(모터 드라이버, 스핀들, 3D 프린터 액서서리, 등등) 나중에 CNC로도 사용할 수 있고 3D 프린터로도 사용할 수 있고 실용성이 많은 것 같다.

http://www.zentoolworks.com/index.php?cPath=14

혹시 맘이 바뀔지도 모르니깐 다음의 사이트도 저장해 둔다. 대단히 많은 제품에 대해서 보기 쉽게 비교해 놓은 사이트다.

http://www.3ders.org/pricecompare/3dprinters/


납땜 도구

예전의 핫꾸 납땜기가 더 좋은데 지금 나오는 알록달록한 것은 가격도 비싸고 필요하지 않은 기능이 많다.

부품 상자

칩저항, 칩 캐패시터, 자주 사용하는 파워 레귤레이터, 범용 TR 혹은 MOSFET

칩저항 - 4.7KΩ, 10KΩ, 1KΩ, 220Ω, 240Ω, 2KΩ, 330Ω, 100KΩ, 10Ω
칩캐패시터 - 1uF, 47uF, 100uF, 470uF,
파워 레귤레이터 - LM317, LM2575
범용 TR 혹은 MOSFET - 2N3904, 2N3909, 2N222,

... 추가중.