2019年7月28日日曜日

The Keyboard Project

이번 여름에 걸쳐 개인 프로젝트를 제대로 끝까지 해보기 위해서 실현 가능한 목표를 세워 보왔다.
On this summer, I wanted to make something, so I decided to put a realistic goal and finish it completely.
今まで、色んなプロジェクトを行ったものの、ちゃんと最後まで終わらせたものが名館たので、今年の夏には実現可能なものを最後まで作ろうと決めた。

이름하여 수제 키보드 제작하기.
Therefore, I decided to make a handmade keyboard.
それで、キーボードを作ってみる。

하지만 전혀 기본지식이 없기때문에 여러가지 블로그, 레딧, 다른 프로젝트 개요들을 읽어 봤다.
But I did not have any basic knowledge about how to make a keyboard, so I'd read some articles, blog posts and Reddit posts.
作ると決めたは良かったのだが、基礎知識が全くなかったのでいろいろとネットで探してみた。

그러다가 찾은 하나의 블로그 포스트... 정말 필요한 정보들이 자세하게 담겨있었다.
One day, I found a blog post which includes all information that I wanted. Awesome!!! Thank you so much Michał!
このブログを偶然見つけて読んでみたら、本当に必要な情報が全部書いてあったので、やった!って感じ。

그리고 바로 작업시작...
I started making a schematic for the keyboard.
そして、早速作業に取り掛かる。
















회로도는 만들었지만 바로 만들수는 없었다... 왜냐하면 이게 제대로 움직일지 몰랐기때문에...
I made this schematic, but I could not just make a PCB out of this because I was not sure it would work.
回路図は先もって作ったが、動くとの確信がなかった。

과거에 다른 nRF52시리즈 보드를 제작한 경험이 있어서 배운 교훈으로 작은 스케일로 먼저 테스트 해보기로 했다.
I've learned that I always have to make a small scale prototype and test as much as possible from my past project "Making nRF52 bluetooth board." So, I decided to make it on a breadboard.
前のプロジェクトから得た経験から、やっぱり、小さいものからテストしてみるのが一番いいと思ったので、基礎から始めた。

테스트 해보기전에 필요한 스킬들을 체크해봤다. 그중에 SMD 납땜하는 기술은 반드시 필요했기때문에 그것부터 연습하기로 했다.
To make a real keyboard, I definitely needed to solder SMD components, so I decided to practice it first.
ちゃんと使えるキーボードを作りたかったので、SMDパーツをはんだ付けを練習しょうとした。

내가 가지고 있던 SMD파츠 중에 가장 크기도 작고 복잡했던 부품이 바로 APA102 2020 LED 였기때문에 이걸가지고 연습해보기로했다.
I'd bought many of APA102 2020 LEDs for my previous project (but I failed that time), so I decided to practice with them.
前に買っておいたAPA102 2020を使うことにした。一番小さかったし、ピンも六つもあったのでちゃんとした練習になるかと思った。
 

연습 또 연습... 그리고 매일매일 테스트
Practice every day... and testing it.
練習中… そして、毎日、テストをした。

하나씩 땜질 하는건 별로 어렵진 않았다 그래서 좀더 큰걸로 도전...
Soldering one LED was not hard, so I decided to challenge a bit bigger one.
一個のLEDをはんだ付けするのにはすぐ慣れてきた。それで、すぐ次のものに…

LED 16개를 동시에 납땜할수 있는 보드 (글쓴이가 직접 제작) 로 다시 연습... 그리고 테스트
I designed this PCB to solder 16 LEDs at the same time. I was practicing with it over and over.
LED 16個を一緒につけなきゃいかないPCBをデザインして、また練習…

APA102 2020 AVR코드를 테스트해보고 싶었기 때문에 테스트는 Arduino Uno와 Atmega32로 진행했다.
I already made some AVR codes for APA102 2020. I tested this board with Arduino Uno and Atmega32.
APA102のために先もってAVRコードを作っておいたので、Arduino UnoとAtmega32を使ってテストをしてみた。


테스트는 성공적이었고, SMD 납땜도 많이 익숙해졌기 때문에 첫번째 9키 키보드 PCB 제작에 들어갔다.
The LED boards were working perfectly. I also got used to SMD soldering, so I decided to make an actual PCB board for 9key keyboard.
SMDはんだ付けに大分慣れてきて、9キーのキーボード制作を始めた。
회로도는 전에 만들었던거 그대로 사용했고 PCB디자인을 했다.
I made this PCB design from the schematic which I made before.
最初の作っておいた回路図を使ってPCBのデザインをした。
주문했던 PCB가 오고 대충 납땜을 끝낸후의 모습... 키가 삐뚤빼뚤하게 납땜이 됐다 ㅠ 키보드 펌웨어는 QMK를 사용했지만 APA102 LED 라이브러리를 합치는데 펌웨어를 베이스부터 고쳐버렸다 (사실상 LED애니메이션 빼고는 거의 새로 만든수준)
After I got the PCB, I soldered all components. Sadly, I could not align all keys correctly. I used the QMK firmware for it, but I had to add my APA102 library to it. I almost remade from the base of the firmware (Because the APA102 library QMK had was totally useless.)
注文したPCBが来て、SMDパーツを付けた。残念ながら、キーの並びがよくなかった。
ファームウェアはQMKを使ったものの、自作したAPA102ライブラリを使うためにかなりの時間を費やして元から直した。(元々あったライブラリは使えものにならなかったので)

9키 자체는 문제없이 작동했지만 문제를 발견했다. LED가 한꺼번에 흰색으로 켜지면 순간적으로 전력이 모자라서 키보드 작동이 멈추는것.... 흰색이 아닌 다른색 빛은 문제없었지만 흰색이 들어가는 LED애니메이션이 들어가자 마자 멈춰버렸다.
It was working perfectly until I put the LED animation with white color. To make white color, APA102 had to light up RGB at the same time, so white color consumes the most energy. When I put some LED into white color, the keyboard stopped working. I had to fix this problem.
キーボード自体は問題なく動いたが、LEDが問題を起こした。LEDを白色につけたら。電力が瞬間的に足りなくなってキーボードが止まってしまった。

이 문제에 대한 해답은 우연치 않게 Sparkfun이라는 사이트에서 찾았다. 바로 4.7uF 캐패시터를 LED에 가깝게 붙이면 됐던 것이다. 이 캐패시터가 순간적으로 에너지가 모자른 부분을 방전 시키면서 채워준다는 설명을 읽었다. 바로 새로운 디자인에 반영했다
On the Sparkfun website, I found that 4.7uF capacitor can help that situation, so decided to put a 4.7uF capacitor on the new design.
SparkFunというサイトで見つけた内容で、4.7uFキャパシタを使うと瞬間的に足りなかった電力を補充することができるとのことだった。早速、新しいデザインに追加した。

넘버패드를 풀사이즈로 제작... 모든 숫자키와 +-도 넣었다. 전원부도 강화했다.
It is new number pad. I added 4.7uF capacitor. Hopefully, it could fix the problem.
ナンバーパッドの新しいデザイン。電源の入力のところを直した。

PCB가 오고 바로 납땜을 시작했다. 2u짜리 키도 테스트해보고 이번엔 PCB마운트용 스위치를 사용했기때문에 모든 스위치들이 정렬되어있다.
After I got PCB, I finished all soldering as soon as possible. At this time, I used keys for PCB mounting, so it's aligned perfectly. Also, I tested 2u keys that required some supports. 
PCBが来てすぐ作ってみた。2Uキーも混ざっていたし、前にあったキーの並びの問題もあったし、PCBマウント用のキーを新しくつかった。


문제가 있었던 전원도 4.7uF, 100nF 캐패시터 추가로 테스트에도 고쳐진것으로 보였다. 100nF은 마이크로컨트롤러 가까이 4.7uF은 LED에 가깝게 배치했다.
The power problem was completely resolved by adding 4.7uF and 100nF capacitors. I placed four of 100nF capacitors close to microcontroller and 4.7nF close to APA102 LEDs.
前に問題のあった電源の方も4.7uF, 100nFキャパシタ追加でなおったようだ。

새 보드 테스트와 함께 바로 USB Type-C 커넥터에 대한 테스트도 함께 진행했다. D-, D+, VBUS, GND핀만 이용해서 USB2.0으로 연결도 그냥 가능한듯했다.
While I was testing the new PCB, I was testing the USB Type-C connector. It looked like that I could use D-, D+, VBUS and GND pins just like a regular USB connector.
USB Type-Cコネクターを使いたかったので、さっとくテストしてみた。普通のUSBと同じピンがあったので、それをそのまま使ったら問題なく動いてた。

이 보드가 문제없이 움직이자마자, 84키 키보드(75% 키보드)보드를 바로 디자인했다.
After I tested the number pad, I moved on 84 keyboard.
ナンバーパッドのテストを終えて、すぐ84キーボードのデザインに移った。

키보드 디자인에 상당히 시간이 오래걸렸다. USB포트를 혹시나해서 Micro Type B와 Type C동시에 사용할수 있게 했다. 한쪽이 안되면 다른한쪽으로 이용가능하게했다.
It was taking for a long time to design this board. For just in a case, I placed both USB micro type B and type C connectors.
キーボードデザインにかなりの時間を費やした。USBコネクターを万が一のためにBタイプとCタイプ両方を入れておいた。




보드가 오고 전후면 바로 조립에 들어갔다. 100nF 캐패시터 사이즈를 줄이는걸 깜빡했지만 문제없이 납땜을 했다. 역시 사이즈가 있다보니까 납땜에 약 2시간 정도가 걸렸다. 제일 오래걸렸던부분이 다이오드 납땜이었다. 사이즈도 작고 극성을 볼려면 다이오드에 그려져있는 바를 봐야했는데 오른쪽 그림과 같이 맨눈으론 판단이 거의 불가능하고 돋보기에 하나하나 확인해봐야됬다.
After I got the PCB, I assemble all parts for backside and front side. It was taking more time than I thought. I made a couple mistakes for the size of 100nF and size of 22 resistors. I changed the actual capacitor to 0805, but I put 1210. It did not really matter, so I just solder it. The hardest part of the soldering was checking a polarity of diodes. It's almost impossible to check it by my naked eyes, so I used a magnifier to check it.
PCBを貰ってから前後のパーツをはんだ付けした。100nFキャパシタのサイズを前のボードのままにしたので、新しく買ったキャパシタと会わなかったが、大した問題ではなかったのでそのまま付けた。一番大変なところはやっぱりダイオードの極性を確かめることだった。ダイオードの表面に小さく書かれていたので、それをいちいち拡大鏡を使って確認しながらはんだ付けをした。

 
앞쪽의 모든 LED를 납땜하고 테스를 한뒤에 키를 땜했다. LED하나가 작동하지 않았기에 다시 때버리고 붙였더니 문제 없이 작동했다.
LED soldering was easy. After I soldered all LED, one of the LED was not working, so I desolder it and resolder with a new LED.
LEDをつけ終えてテストしたが、LED一個がちゃんと動いてくれなくて付け直した。

이후에도 여러가지 고칠부분이 있었다. 다이오드 극성이 잘못되어있다던가 마이크로컨트롤러 다리사이에 브릿지가 되어있는걸 고친다던가.
After I fixed the LED, the board still had some problems such as putting a diode with wrong direction (even though I checked the polarity for all diodes) and some bridged pins on microcontroller.
この後にも何箇所か問題があったので直した。ダイオードの極性を間違ってたり、マイクロコントローラの足の間にブリッジができてたり。


그 모든 문제를 고치고 키보드를 테스트... 또다시 문제 발견. 스페이스키가 키캡 무게 때문에 눌린상태로 있는상황이 생겼다. 기온이 차가우면 차가울수록 문제가 심해지는것도 확인했다.
While I was testing the keyboard, I found another problem. Because I was using a light key (50g for linear inertia), the keycap of space bar pressed the switch without actual pressing by a user. Also, it was getting worse if the air temperature was low.
問題をなおしても、また問題が起きた。スペースバーのキーキャップが長いせいである程度の重さがあったし、軽いキーを使ってたのでキーキャップの重さで勝手に押されることがあった。部屋の温度が低ければ低いほどひどくなった。

그래서 바로 약간 더 무거운키를 사용 하기로 했다. (75g 텍타일)
So, I ordered a slightly steeper switch (75g tactile)
問題が明確だったので、少し重たいキーを使うことにした。
스페이스바 문제는 금방해결 됬고 좋은 교훈을 얻었다.
I could fix it soon, and I gained another knowledge from it.
新しいキーをつけることで、すぐ解決できた。また新しいことを学んだ気がした。
PCB인 상태로 그대로 쓰는건 힘들기 때문에 서포트 해줄 다리를 디자인했다. 사용한 앱은 Shapr3D 정말 큰 도움이 됐다.
I designed a few supports for the keyboard. I used Shapr3D app on my iPAD Pro. It is very useful app. I love it.
PCBのままじゃ使いづらかったので、サポートしてくれるパーツをデザインしてみた。使ったアプリはShapr3D。本当に使いやすいアプリ。

가지고 있던 3D프린터 (Ender3였던 물건)를 사용해 바로 프린트해 보았다. 여러가지 디자인을 만들고 테스트해본후 개선해갔다.
I have a 3D printer (Ender 3, but I replaced its main board and firmware), so I tested several design for it. Obviously, the first design was not working at all, so I redesigned it several times.
元々3Dプリンターを持ってたので、作ったデザインをすぐに試したみた。でも、やはり問題がいろいろあったので、デザインの何回かなおした。

그리고 최종디자인이 나오고 키보드에 끼워서 확인해봤다. 정말 신기할정도로 잘맞았다.
Finally, I designed the support that fits perfectly with my keyboard.
何回か直して行くうちにキーボードにピッタリなものができた。
그렇게 99%완성된 키보드... 지금 이 포스트도 이 키보드로 작성된것이다.(키캡을 풀사이즈용 키보드로 사버려서 몇몇개 키캡이 맞지 않았다... 다시 오더했음)
It is 99% completed keyboard. I bought key caps for full-size keyboard, so some of them were not fitting with this keyboard (I ordered new key caps) This post is written by using this keyboard.
そして、99%完成したキーボード。キーキャップをフルサイズキーボード用で買ったので何個か合わなかった。すぐ新しいものを買った。

이로써 키보드 프로젝트는 완성됐고 계획했던 거의 모든부분을 실현했다. 이 프로젝트를 위해서 약 2개월 반정도가 소비되었지만, 정말 많은것을 배울수 있었다. 학교에서 컴퓨터 엔지니어링을 전공하지만 Lab에선 사실 이론적인 부분을 그냥 확인하는 수준이지 실제로 물건을 만들거나하진 못하지만, 이렇게 직접만들어보면 정말 학교에서 배울수 있는건 한정적이라는것을 느낀다. 애초에 직접 안하면 성이 안풀리는 사람으로써 정말 이번 프로젝트를 진행하는동안 즐거웠고 매일매일 새로운것을 배울수 있었다.
Therefore, I finished this keyboard project. I spent almost two month and half, but I have learned so many things for actual PCB design. Even though I am a computer engineering student, the stuff that I can learn from school is very limited. In a school lab, we were just checking out some theoretical concepts, but we did not have anything to actually make something. I am always thinking that a good engineer should know both theory and how to actually make it. "All knowledge must be with performed." I keep this phrase in my mind. Knowledge without any action is nothing, so I keep trying. I was so glad that I started this project.
これで、プロジェクトを終えることができた。これのために約2か月半を費やした。それでも、このプロジェクトから色んな事を学べたので本当に嬉しかった。 コンピューターエンジニアとして学校で学べるものは本当に限られていたし、自分で直接やってみないと気がすまない性格を持ってるものなので、これを作ってる間は本当に楽しかった。

2015年9月29日火曜日

初期化の大事さ・・・

プログラムを書き始めてもう5~6年以上セミプロレベルのコーディングをやっているんですが、今までやはりエンジン制作やらライブラリ制作やらで、ながーいプログラムっていうよりはもっとルチンとか答えを出す計算式を作るパターンが多かったんです。

で、今現在ゲーム制作をしているのですが、久々のゲームルールを組み込む中で、改めて初期化の大事さに気づきました。最近のデバッグツールが優秀すぎなせいかクリティカルなメモリ管理ルールの違反ではない限り、プログラムが正常かのようにデバッグモードで動き出すのです。

特にデバッグモードでは初期化をしなくても0に塗りつぶしてくれるので(これは前のバージョンのコンパイラでもそうだったんですけど)、初期化し忘れに気が付かず、そのままコードを書いていってしまうのです。そのことを思い出してコードの洗い直しに入ったところクラッシュになりかねないコードを全部修正することに・・・

はぁー未だにこんな初歩的なミスをするなんて・・・

2015年5月22日金曜日

Bufferに注意!

プログラムのことを探すとみんな「バッファーが・・・」とか「このBuffer」にはとかよく使うけど、
そもそもBufferってなんだ?
なんとなく読んでいて「あーメモリーのことを言っているんだね」とか
「何か保存する場所なんだ」って感じがしますね。

バッファーを辞書で探してみると(英語)
a person or thing that prevents incompatible or antagonistic people or things from coming into contact with or harming each other.
・・・・まったくわかりませんね。

日本語辞書でみると
1(鉄道車両などの)緩衝器[装置].
2緩衝物.
3緩衝国.
4【電子計算機】 バッファー.
・・・・これも意味わかりませんね。

まぁー特に大した意味もないので、何となくわかればいいと思います。
みんなそういう意味で使ってるんだって感じで。

で、「注意」とタイトルに書いたんですが、いったいどういうことなのかみてみましょう。
ここではバッファーを「メモリ領域」の意味で使おうと思います。
CとC++にはいろんなメモリコピーの関数(機能)があります。
memcpyとかstrcpyとか・・・それらを使う上で、バグったりしたことはありませんか?
プログラムが止まったり、落ちたり、変な動きをしたり、いろいろとあると思います。
それについて考えてみましょう。

多くのバグはまさにバッファー管理の誤りで起きます。例えば、初期化がされなかったり、保存できる領域を超えたりすることが代表的なものですが、この保存できるところを超えるとどうなるかを詳しくみてみましょう。

バッファーって保存する領域なのにこの領域を超えて保存しようとしたらどうなるのか?
どう思いますか?

実はOS側でこれは「ルール違反」だとみなして、プログラムを強制的に止めます。

たまにプログラムを書いて実行してみたら、すごい警告ポップアップが出て「デバッグ」「再実行」のボタンがあるのをみたことありますか?
この警告ですが、大体メモリルール違反で出る警告です。

じゃーなんでこれをルール違反だとみなすか?

例えば、メモリーにこんな感じでプログラムがあるとします。
Aプログラム「1|2|3|1|3|2」Bプログラム「3|1|2|1|2|3」
Aプログラムの一番後ろのマスから3マスデータを書き込むとします。

3|1|2とか書いたらどうなりますかね?

Aプログラムと全然関係のないBプログラムのデータが書き換えられますね?
それではBプログラムがバグったり、落ちたりする問題が起こります。
これじゃ、Bプログラムを作った人があまりにも可哀そうじゃないですか。
それで、こういうことを禁止にしたんです。
この問題はプログラム同士でも変数同士でも起きます。

理由もなく変数の値が変わったりしたら、この問題の可能性が高いです。

で、なんでこんなことが起きるのか?
変数にちゃんと代入したらいいだろうが!って思いますよねw
実は初心者のプログラムではあんまりこの問題が起きません。
でも初心者から抜け中級者になるころぐらいにこの問題が多く起きます。

例えば、

int a[20];
for(int i = 0; i <= 20; i++){
a[i] = 0;
}

このようなことが多くないでしょうか。で、一生懸命探して直すのに他でバグったりしますね。
実はこのようなバグとかエラーを直すことはすごくいい経験です。
まだ、勉強するときにできるだけ多くのエラー、バグ、クラッシュ、警告に合ってくださいw

変に聞こえるかもしれませんが、これもまた経験です。
プログラムは「かけた時間」ですべてが決まります。
かけた時間つまり多くのプログラムを組んでみると多くのエラーとバグと遭遇するでしょう。
それらを直してみることであなたは確実にレベルアップできます。

実際、会社では一人ですべてのプログラムを書く場合は多くありません。
他人が書いたものを使う場合も多いでしょう。あなたのプログラうムが完璧だとしても、ほかの人のプログラムが問題を起こす場合も多くあります。
その時、まさにこのような経験が力を発揮します。
覚えてください。バグ、エラー等を直すことは絶対無駄にはなりません。

まぁー雑談交じりの内容だったのですが、すごく重要な部分です。次はメモリーについてもっと詳しく書いてみようと思います。

2015年5月20日水曜日

3D変換について

要点だけ抑えてみましょう。3D レンダリング(描画)とは3D っぽくみせることです。
例えば前後に動いている板があなたの前にあるとしましょう。

あなたはどう奥行き感じますか?

奥に行くと小さくなるように見えて、手前に来ると大きくなるように見えるでしょう。
パソコンの中の3D 表現とはまさにそういうことです。

とあるオブジェクトがカメラの前に近づくと大きくみせ、遠ざかると小さくみせるだけです。

え?じゃーどうやって?
この二つの絵をみてみましょう。実はすごくシンプルな考え方なんです。


 



真ん中のモンスターの大きさを変えるだけで、手前に来てるような感じがしますね。
(もちろん比較対象が少なすぎて遠近感があんまり感じられないんですけどw)
で?具体的にどんな変換があったのかみましょうか?

このキャラクターの3Dでの左上座標をみてみましょう。

左はXYZ -0.5, 0.5, 1.0
右はXYZ -0.5, 0.5, 5.0

Z座標が変わっていますね。
でも、これだけじゃよく仕組みがわかりませんね。では四角で囲ってみましょう。
で、それの画面上での左上の点の座標を比較してみましょう。

左のほうはX=243.00 Y=163.00で
右のほうはX=294.00 Y=214.00です。

どうでしょうか?なんかわかりますか?実は3D 描画というのは
「最終的に画面のどの位置にどのサイズで描画するか」を演算する処理を言います。
われわれが見ているパソコンのディスプレイは平面ですね。そこに奥行などありません。
ただ平いだけです。

つまり
3D座標→あらゆる変換用の数学式にかける→画面の2D座標
というシンプルな(変換用の数学式を誘導するのはかなり複雑です)ものです。
Openl GLes系はこの機能がついてませんが、DirectXは親切にすべての機能が揃えてあります。
これをネットで調べてみるとものすごい難しく説明します。(あらゆる数学の式で表現してあるので見るだけで嫌になりますw)

実は3D表現をするだけならその式を全部知る必要はありません。
最近のライブラリは優秀なので、あらかじめデバイスの中に
その機能を備えてあるものがほとんどです。

もちろん、高度なことをやろうとすれば、全部わかる必要はありますが、
「んなことじゃなくてとりあえずやってみたいんだ」って方は
今使っているデバイスでのやり方だけを調べてその関数を使うだけでOKだと思います。

例えば、DirectX9では

D3DXMATRIX World;
D3DXMATRIX Offset;
D3DXMATRIX View;
D3DXMATRIX Persp;
D3DXMatrixIdentity( &World ); //とりあえず、World行列をリセット

D3DXMatrixTranslation( &Offset, 描画したい位置);
D3DXMatrixMultiply( &World, &World, &Offset ); //描画したい位置をセット
D3DXMatrixLookAtLH(
                  &View,
                  &D3DXVECTOR3(カメラ位置),
                  &D3DXVECTOR3(0, 0, 0),
                  &D3DXVECTOR3(カメラが見ている方向)
                  );//カメラをセット
D3DXMatrixPerspectiveFovLH( &Persp, D3DXToRadian(視野角), 画面の横サイズ/画面の縦サイズ, 1.0f, 10000.0f);//視野角をセット

//描画デバイスにオブジェクトの描画位置を教える
描画デバイス->SetTransform(D3DTS_WORLD, &World);
//カメラ情報を教える。
描画デバイス->SetTransform(D3DTS_VIEW, &View);
//視野角情報を教える
描画デバイス->SetTransform(D3DTS_PROJECTION, &Persp);

描画式

上のやつで描画されます。Open GL自体にはD3DXMatrixTranslationとか
D3DXMatrixMultiplyとかD3DXMatrixLookAtLHとかD3DXMatrixPerspectiveFovLHとか
こういう便利な関数がないのですが、使っているデバイス(例えばiOS, Android)の
デバイスライブラリでこの機能があると思いますので、似たような名前か似たような
機能がありましたらそれを上のものに置き換えたら3D描画ができます。
SetTransformとかはデバイス依存の関数なのでこれも変えないといけませんねw

2013年4月7日日曜日

自作OpenALライブラリー!

自作OpenALライブラリを作りました。

OpenALライブラリの構造
1. AL_Attacthment デバイスとコンテキスト生成。
2. AL_Sound スタティックサウンド再生。
3. AL_Stream ストリームサウンド再生。

そして、ロード部
LOAD_WAVEクラス

機能動作はこのような流れで行きます。

Load_Waveクラスはバッファ生成やデータの読み込みを手伝ってくれます、
Load_Wave::fmt_headerと Load_Wave::SOUND_DATAを使いデータバッファを確保し、サウンドデータ形式や実データを読みこみます。

このデータを
AL_Sound型やAL_Stream型に流し込むことによって再生されます。

AL_Streamはまだラッピングが足りなくてむき出しのところや荒いところがただありますが、このように構成されています。

ヘッダー

#pragma once
#include "LOAD_WAV.h"
#include "AL_Attacthment.h"

class AL_Stream
{
private:
 unsigned int m_Buffer_size;
 unsigned int m_Buffer_cnt;

 ALuint* m_Buffers;
 ALuint m_Source;
 LOAD_WAV::WAVEFILE m_file;
 LOAD_WAV::fmt_header m_format_data;
 LOAD_WAV::SOUND_DATA m_Sound_data;
 
protected:
 static void Set_SoundBuffer(ALuint buffer, LOAD_WAV::SOUND_DATA data, unsigned int data_size, const LOAD_WAV::fmt_header* format_data);

public:
 AL_Stream(void) : m_Buffer_cnt(0), m_Buffer_size(0), m_Buffers(0), m_Source(0), m_file(0){}
 ~AL_Stream(void){};

 void Create_SoundBuffer(unsigned int cnt, unsigned int size);
 void Release(void);
 void Rewind_file(void);

 void Play(void);
 void Set_Wave_file(LOAD_WAV::WAVEFILE file, const LOAD_WAV::fmt_header* fmt);
};




実装部
#include "AL_Stream.h"


void AL_Stream::Set_SoundBuffer(ALuint buffer, LOAD_WAV::SOUND_DATA data, unsigned int data_size, const LOAD_WAV::fmt_header* format_data)
{
 ALenum sound_format = NULL;

 if(format_data->m_Channels == 1){
  sound_format = AL_FORMAT_MONO8 + format_data->m_BitsPersample / 16;
 }else if(format_data->m_Channels == 2){
  sound_format = AL_FORMAT_STEREO8 + format_data->m_BitsPersample / 16;
 }

 alBufferData(buffer, sound_format, data, data_size, format_data->m_SamplerPersec);
}

void AL_Stream::Create_SoundBuffer(unsigned int cnt, unsigned int size)
{
 m_Buffer_cnt = cnt;
 m_Buffer_size = size;

 m_Buffers = (ALuint*)malloc(sizeof(ALuint)*m_Buffer_cnt);
 m_Sound_data = LOAD_WAV::Create_SoundBuffer(m_Buffer_size);

 alGenBuffers(m_Buffer_cnt, m_Buffers);
 alGenSources(1, &m_Source);

 
}

void AL_Stream::Release(void)
{
 alSourceStop(m_Source);
 LOAD_WAV::Release_SoundBuffer(m_Sound_data);
 alDeleteSources(1, &m_Source);
 alDeleteBuffers(m_Buffer_cnt, m_Buffers);
 
 if(m_Buffers != NULL){
  free(m_Buffers);
  m_Buffers = NULL;
 }
}

void AL_Stream::Rewind_file(void)
{
 fseek(m_file, m_format_data.m_Data_Position, SEEK_SET);
}


void AL_Stream::Play(void)
{
 ALint buffer_processed = 0;
 ALint source_stats = 0;
 alGetSourcei(m_Source, AL_BUFFERS_PROCESSED, &buffer_processed);
 alGetSourcei(m_Source, AL_SOURCE_STATE, &source_stats);

 if(source_stats == AL_STOPPED || source_stats == AL_INITIAL){
  alSourcePlay(m_Source);
 }

 while (buffer_processed > 0){
  ALuint unqued_buffer = NULL;
  alSourceUnqueueBuffers(m_Source, 1, &unqued_buffer);
  LOAD_WAV::Get_SoundBuffer(m_file, m_Sound_data, m_Buffer_size);
  AL_Stream::Set_SoundBuffer(unqued_buffer, m_Sound_data, m_Buffer_size, &m_format_data);
  alSourceQueueBuffers(m_Source, 1, &unqued_buffer);
  buffer_processed--;
 }
}

void AL_Stream::Set_Wave_file(LOAD_WAV::WAVEFILE file, const LOAD_WAV::fmt_header* fmt)
{
 m_file = file;
 memcpy(&m_format_data, fmt, sizeof(LOAD_WAV::fmt_header));

 for(unsigned int i = 0; i < m_Buffer_cnt; i ++){
  LOAD_WAV::Get_SoundBuffer(m_file, m_Sound_data, m_Buffer_size);
  AL_Stream::Set_SoundBuffer(m_Buffers[i], m_Sound_data, m_Buffer_size, &m_format_data);
 }

 alSourceQueueBuffers(m_Source, m_Buffer_cnt, m_Buffers);
}


ストリーム再生で意外とてこずる方が多かったので、そのような方たちに参考になれるよう全ソース公開しますw
これが参考になればいいですけどww

かなり荒いソースだし、コピペーだけじゃ使えないですけど、読む方によっては参考になれると思います。


マルチスレッドとパフォーマンス改善の関係性


ここ数日間スレッドを使って遊んでみました。

マルチスレッド化がパフォーマンス改善にどれくらい影響をするのか気になったりして、過去にはWin32 APIのスレッド機能を使ってテストしてみたりしたんですが、
今回はboostをインストールしたので、boost::threadを使い遊んでみました。

多分、皆さんが一番気になることって、マルチスレッドってパフォーマンス改善に繋がるの?そして、どれくらい改善できる?というところだと思います。
その答えは改善できる可能性が高いということです。そして、改善の程度はスレッドに与える仕事によります。

なぜ、可能性という言葉を使ったのか。それは改善できるかもしれないし、改善できないかもしれないということなので使わせてもらいました。
正直なところ、マルチスレッド化ということがパフォーマンス改善に直接繋がるわけではない気がします。
あくまでも、Aという処理をしている間プログラムを止まらせたくないというところでマルチスレッドは使われるわけです。
今やデュアルコアなんか当たり前でクアッドコアやオクタコアまで市販されています。

このコアを分ける流れは昔の単一コアの処理スピードを上げることが限界に達することによってできたのです。各コアの処理スピードは少し落として、そのコアたちを複数個乗せたほうが処理スピードを上げられるという概念でマルチコアCPUが作られ始めたわけです。


ちなみに、昔単一コアの処理スピードが3.0Ghzを超えたとき、CPUを限界までこき使う技術「ハイパースレッド」(仮想マルチコア化技術)が開発されたりしたこともありました。w

マルチコアということが当たり前になり、大量の仕事を分けて処理した方が早いという概念もさらに加速化されることになります。

当たり前な考え方ですが、これを現実にたとえてみるとマルチスレッドとパフォーマンスの関係がさらに理解しやすくなります。

例えば、
手紙に切手を貼る仕事が与えられてこれをこなしていくと考えてみましょう。
5000枚が超える量を二人でわけるとき

Aというメインの人が手紙をとるとき2枚ずつとり、サブのBに分けてあげるとことにすると、この仕事の効率は極端にわるくなります。

逆にAという人は「一人でやったほうが早いわ」と言うでしょう。

では、最初から2500枚ずつ分けると、AとBの負担も減りつつ仕事も速くなる可能性があります。

ここでも「可能性」という言葉が出てきました。
なぜなら、Aが仕事を先に終わらせてもBが終わらないとBを待つしかありません。
(Bが極端に切手をはることが苦手という前提ですがw)

このようなことから考えてみると

適度な人数に適度な仕事をわけてやることを前提にマルチスレッドというものはその力を発揮できるということになります。

では、さらにこのマルチスレッドを有効に使う方法は何があるか考えてみましょう。
これも理解しやすくするために現実に例えていきたいと思います。

飯屋があるとします。
この飯屋の店長は料理も早く、そしてうまい。
かつ食器洗いやそのた雑務もはやくこなせる人です。

この店は店長一人ですべてをやればいいのか?
その答えは「仕事の量による」ということになります。

1.客数が少ない場合だと、一人でこなしたほうが良いに決まっている。
2.客数が多い場合はアルバイトを雇って使ったほうがいい。

ということはすぐ思いつきます。

では、2の場合
バイトくんに料理をやらせるとどうなるか?
バイト君がなにか調理師の資格をもってない限り、飯の味は悪くなり、店の評判はおちるでしょう。

つまり、仕事というものは優先順位というものがあるということです。
飯屋というものはご飯の味が第一で、この能力に優れている人間がやらなければいけません。

このことから順位をつけてみると・・・
1.      料理
2.      食器洗い、その他の雑務
(順位付けが適当ですが、気にしないでくださいw)

つまり、一番大事なことは店長がやり、その他の雑務はバイトに任せたほうが効率的には良いはずです。

これをゲームプログラムに当てはめてみましょう。
例えば、
1.ゲームのルールの処理
2.敵がいる場合AIの処理
3.ビジュアルエフェクト等・・・
の順だったら、
メインスレッドは1を処理します。
2は時と場合によって分担しましょう。
3は動きだけの雑な処理なので完全にサブスレッドに預けます。

これをメインスレッドで全部こなすと
どうでもいい3の処理が少し遅延しただけで、1や2の処理が遅延する恐れがあります。
ゲームになんの影響もないオブジェクトの処理で処理落ちをおこすわけなんです。

このようなことは避けたい!

3のような仕事をメインの処理をする前に別スレッドに預けて大事なことはメインでやることで、処理遅延は避けられます。

つまり、マルチスレッドというものは

そのプログラムの設計やシステムに密接にかかわるもので単にマルチスレッド化して良いものではありません。

2013年3月29日金曜日

マージソートのパフォーマンス

void main(void)
{
 //システム時間を計る計測器(クロック数で計算)
 B_Counter* sys_counter = new B_Counter();
 
 //配列を用意する
 int x[MAX_DATA];
 for(int i = 0; i < MAX_DATA; i ++){
  x[i] = (int)rand();
 } 

 //カウンター開始
 sys_counter->Counter_Start();
 
 //ソート開始
 merge_sort(x, 0, MAX_DATA-1);
 
 //カウンター終了
 sys_counter->Counter_End();
 
 //時間出力
 printf("%f \n", sys_counter->Get_Time());


 int c[MAX_DATA];

 for(int i = 0; i < MAX_DATA; i ++){
  c[i] = (int)rand();
 }

 sys_counter->Counter_Start();

 //一番単純なソートシステム
 for(int i = 0; i < MAX_DATA - 1; i ++){
  for(int a = i+1; a < MAX_DATA; a ++){
   if(c[i] > c[a]){
    int data = c[i];
    c[i] = c[a];
    c[a] = data;
   }
  }
 }
 sys_counter->Counter_End();

 printf("\n");
 
 printf("%f \n", sys_counter->Get_Time());
}

上記のようなソースでソートアルゴリズムをテストしてみました。

使っているアルゴリズムは二つで
1.マージソート。
2.アルゴリズムでも何でもない単純比較ソート。

その結果は・・・

・配列を20個用意した場合
マージソート : 0.008979
単純ソート : 0.004489

20個ソートしているのに何でここまで時間がかかるんだよって疑問を持ってる方もいらっしゃると思いますが、
そこんとこはおいといて時間をご覧ください。

なぜかマージソートのほうが時間がかかっています。
この時点では・・・マージソートって効率悪いなーって思ってましたが、配列の個数を増やしてみたら・・・

・配列を200個用意した場合
マージソート : 0.085299
単純ソート : 0.300150

200個ソートした時点でかなりの差が見られました。
明らかにマージソートのほうが速いですね。

・配列を2万個用意した場合
マージソート : 12.141984
単純ソート : 3484.902047

この時点ではもう相手にならないですねw
マージソートの特徴かもしれませんが、ソートする量によって時間も比例しますが、
大量のデータのソートに適してるという結果がみられました!

かなり有意義なスピード比べでしたねーw(知らなかったのは自分だけかもしれませんがw)