// インクルードディレクトリ // /wxWidgets/include // /wxWidgets/release/lib/vc_x64_dll/mswu // ライブラリディレクトリ // /wxWidgets/release/lib/vc_x64_dll // プリプロセッサ // __WXMSW__ // WXUSINGDLL #if defined(_DEBUG) #pragma comment(lib,"wxbase32ud.lib") #pragma comment(lib,"wxbase32ud_net.lib") #pragma comment(lib,"wxbase32ud_xml.lib") #pragma comment(lib,"wxmsw32ud_adv.lib") #pragma comment(lib,"wxmsw32ud_aui.lib") #pragma comment(lib,"wxmsw32ud_core.lib") #pragma comment(lib,"wxmsw32ud_gl.lib") #pragma comment(lib,"wxmsw32ud_html.lib") #pragma comment(lib,"wxmsw32ud_media.lib") #pragma comment(lib,"wxmsw32ud_propgrid.lib") #pragma comment(lib,"wxmsw32ud_qa.lib") #pragma comment(lib,"wxmsw32ud_ribbon.lib") #pragma comment(lib,"wxmsw32ud_richtext.lib") #pragma comment(lib,"wxmsw32ud_stc.lib") #pragma comment(lib,"wxmsw32ud_webview.lib") #pragma comment(lib,"wxmsw32ud_xrc.lib") #else #pragma comment(lib,"wxbase32u.lib") #pragma comment(lib,"wxbase32u_net.lib") #pragma comment(lib,"wxbase32u_xml.lib") #pragma comment(lib,"wxmsw32u_adv.lib") #pragma comment(lib,"wxmsw32u_aui.lib") #pragma comment(lib,"wxmsw32u_core.lib") #pragma comment(lib,"wxmsw32u_gl.lib") #pragma comment(lib,"wxmsw32u_html.lib") #pragma comment(lib,"wxmsw32u_media.lib") #pragma comment(lib,"wxmsw32u_propgrid.lib") #pragma comment(lib,"wxmsw32u_qa.lib") #pragma comment(lib,"wxmsw32u_ribbon.lib") #pragma comment(lib,"wxmsw32u_richtext.lib") #pragma comment(lib,"wxmsw32u_stc.lib") #pragma comment(lib,"wxmsw32u_webview.lib") #pragma comment(lib,"wxmsw32u_xrc.lib") #endif #include <wx/wxprec.h> #include <wx/glcanvas.h> #include <wx/frame.h> #ifndef WX_PRECOMP #include <wx/wx.h> #endif #include <GL/gl.h> #pragma comment(lib, "opengl32.lib")
class MyGLCanvas : public wxGLCanvas { public: MyGLCanvas(wxFrame* parent, int* attribList = NULL); void Render(wxPaintEvent& evt); private: wxGLContext* m_context; // OpenGL context }; MyGLCanvas::MyGLCanvas(wxFrame* parent, int* attribList) : wxGLCanvas(parent, wxID_ANY, attribList) { m_context = new wxGLContext(this); // windowsでちらつき防止 SetBackgroundStyle(wxBG_STYLE_CUSTOM); } void MyGLCanvas::Render(wxPaintEvent& evt) { if (!IsShown()) return; int w, h; GetClientSize(&w, &h); glViewport(0, 0, w, h); wxGLCanvas::SetCurrent(*m_context); wxPaintDC(this); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glBegin(GL_TRIANGLES); glColor3d(1.0, 0.0, 0.0); glVertex3f(0.0, 1.0, 0.0); glColor3d(0.0, 1.0, 0.0); glVertex3f(-1.0, -1.0, 0.0); glColor3d(0.0, 0.0, 1.0); glVertex3f(1.0, -1.0, 0.0); glEnd(); glFlush(); SwapBuffers(); }
class MyFrame : public wxFrame { public: MyFrame(const wxString& title, int xpos, int ypos, int width, int height); ~MyFrame(); void OnSize(wxSizeEvent& event); MyGLCanvas* m_canvas = NULL; }; MyFrame::MyFrame(const wxString& title, int xpos, int ypos, int width, int height) : wxFrame((wxFrame*)NULL, -1, title, wxPoint(xpos, ypos), wxSize(width, height)) { int args[] = { WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0 }; m_canvas = new MyGLCanvas(this, args); m_canvas->Bind(wxEVT_PAINT, &MyGLCanvas::Render, m_canvas); Bind(wxEVT_SIZE, &MyFrame::OnSize, this); } void MyFrame::OnSize(wxSizeEvent& event) { m_canvas->SetSize(event.GetSize()); } MyFrame::~MyFrame() { delete m_canvas; }
class MyApp : public wxApp { public: bool OnInit(); }; bool MyApp::OnInit() { MyFrame* frame = new MyFrame(wxT("wxWidgets OpenGL"), 50, 50, 400, 400); frame->Show(); return true; } IMPLEMENT_APP(MyApp)
SQLite3は公式が.libを配布していない。代わりに.dllと.defが提供されているので、そこからlibコマンドで作成する。
まず windows用の、win64のdllをダウンロードする。
https://www.sqlite.org/download.html
展開すると、sqlite3.def , sqlite3.dllが生成される。
x64 Native Tools Command Prompt を起動し、 lib コマンドを実行する。
>lib /DEF:sqlite3.def /OUT:sqlite3.lib /MACHINE:x64
sqlite3.libが生成される。
C++からSQLite3を使用してみる。
まずソースコードをダウンロードするが、.hと.cの構成になっており、プロジェクトに組み込んでビルドするので、CMakeやlibのlinkなどはしない。
まず、以下からSource Codeをダウンロード。
https://www.sqlite.org/download.html
展開して、sqlite3.cをプロジェクトに追加。以下のコードを実行すると、カレントディレクトリにwebsites.dbが作成される。
#include "sqlite3.h" #include <iostream> int main() { int result; sqlite3* database; result = sqlite3_open("websites.db", &database);// ファイルを開く if (result) { std::cerr << "失敗 " << sqlite3_errmsg(database) << std::endl; return(0); } const char* sql_query; sql_query = "CREATE TABLE WEBSITES(" "SITE_NAME TEXT NOT NULL," "URL TEXT NOT NULL," "ACCESS_COUNT INT NOT NULL);" ; // SQL実行 char* errormsg = 0; result = sqlite3_exec(database, sql_query, 0, 0, &errormsg); if (result != SQLITE_OK) { std::cerr << "失敗: " << errormsg << std::endl; sqlite3_free(errormsg); } sqlite3_close(database);// ファイルを閉じる }
#include "sqlite3.h" #include <iostream> #pragma warning(disable:4996)
// データを追加
void insert(sqlite3* database, const char* site_name, const char* url, int access_count) { char* errormsg = 0; char query[1024]; sprintf(query, "INSERT INTO WEBSITES VALUES('%s', '%s', %d);", site_name, url, access_count); int result = sqlite3_exec(database, query, 0, 0, &errormsg); if (result != SQLITE_OK) { std::cerr << "失敗: " << errormsg << std::endl; sqlite3_free(errormsg); } }
////////////////////////////////////////////////// ////////////////////////////////////////////////// //////////////////////////////////////////////////
// sqlite3_execで、抽出したデータ一軒ごとにこのコールバック関数が呼び出される int callback_per_row(void* NotUsed, int argc, char** argv, char** azColName) { // 一軒分のデータを表示する // 今回は、SITE_NAME , URL , ACCESS_COUNT の三つのカラムがあるので、argc==3 となる for (int i = 0; i < argc; i++) { const char* column_name = azColName[i]; const char* value = argv[i]; // 値は NULL があり得るので、その場合は NULL と表示する if (value != nullptr) { std::cout << column_name << " == " << value << std::endl; } else { std::cout << column_name << " == NULL" << std::endl; } } std::cout << "\n"; return 0; }
// WHERE SITE_NAME で SELECT する void getitem(sqlite3* database ,const char* sitename) { char* errormsg = nullptr; // SQLクエリ char sql_query[1024]; sprintf(sql_query,"SELECT * FROM WEBSITES WHERE SITE_NAME = '%s';",sitename); // SQLクエリ実行 int result = sqlite3_exec(database, sql_query, callback_per_row, 0, &errormsg); if (result != SQLITE_OK) { std::cerr << "失敗: " << errormsg << std::endl; sqlite3_free(errormsg); } }
////////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// int main() { int result; sqlite3* database; result = sqlite3_open("websites.db", &database); if (result) { std::cerr << "失敗 " << sqlite3_errmsg(database) << std::endl; return(0); } const char* sql_query; sql_query = "CREATE TABLE WEBSITES(" "SITE_NAME TEXT NOT NULL," "URL TEXT NOT NULL," "ACCESS_COUNT INT NOT NULL);" ; // SQL実行 char* errormsg = nullptr; result = sqlite3_exec(database, sql_query, 0, 0, &errormsg); if (result != SQLITE_OK) { std::cerr << "失敗: " << errormsg << std::endl; sqlite3_free(errormsg); } insert(database, "Google", "http://www.google.com", 0); insert(database, "Yahoo!", "http://www.yahoo.co.jp", 5); insert(database, "Amazon", "http://www.amazon.co.jp", 10); insert(database, "Facebook", "http://www.facebook.com", 15); insert(database, "Twitter", "http://www.twitter.com", 20); getitem(database,"Amazon"); sqlite3_close(database); }
某C#ベースのゲームエンジンが炎上しているので、少しUEを使えるようになっておきたいので基本的な所を勉強したいと考えている。スタティックメッシュにテクスチャを与えるところからやってみたい。
コンテンツブラウザにテクスチャファイルをドラッグ&ドロップして読み込む。
なお、テクスチャデータが1チャンネル16bitだと色が正常に出ないらしいので8bitを用意する。
作成したマテリアルをダブルクリックして、ノードエディタにテクスチャをドラッグして取り込み、ノードを接続する
最後に、画面左上付近にある適用ボタンを押す。
1チャンネルが16bitの画像を読み込むと色が淡くなる。画像を確認して、色が変わっているなら変換する必要がある。
Ubuntuを入れて使っていたUSB接続のSSDドライブをほかの用途に使いたい。パーティションを切りなおさなければいけないので、windowsからDISKPARTを使った。DISKPARTはFDISKの後継コマンドらしい。
コマンドプロンプトあるいは名前を指定して実行から、diskpart と入力するとコマンドラインツールが起動する。
① list disk で接続しているドライブをすべて表示
② select disk (ディスク番号) を使って、おそらく操作対象であろう物の番号を指定してアクティブにする。
③ detail disk を実行し、現在アクティブなドライブレターやボリュームラベルなどを確認して、確かに操作対象であることを確認する
④ list partition を使ってこのディスクのパーティションの状態を確認。こうやってしっかり自分が正しいディスクを操作しようとしていることを確認する。
* なお、select disk した後でlist diskすると、現在アクティブなドライブの左側に*がつく。
① select partition (パーティション番号) を使い、現在アクティブなディスクの、操作したいパーティションを選択する。
* この状態で list partitionすると、現在アクティブなパーティションの左側に*がついて見える。
② パーティションを削除するには、delete partition または delete partition override を使用。保護されたパーティションなど、delete partition overrideでなければ削除できないものがある。
* 拡張パーティションは空でないと削除できない。
ので、先に中の論理パーティションを削除してから拡張パーティションを削除する。
① create partition primary により、未割り当て領域いっぱいにプライマリパーティションを作成する。いっぱいにしたくない場合はscreate partition primary size=〇〇 とサイズを指定する。(単位はMB)
② format fs=ntfs により、フォーマットする。恐ろしく時間がかかる。本当に恐ろしく時間がかかるので、format fs=ntfs quick と指定することを強く強く推奨する。一度開始すると中断する方法がないので、quickをつけることを積極的に検討するべきだ。
③ assign letter = (ドライブレター) によりドライブレターを与える。この作業をしないとエクスプローラなどでドライブを認識できない。
use std::rc::Rc; use std::cell::RefCell;
#[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } }
fn main(){ let value = Rc::new( RefCell::new( Color::new(0.3,0.2,0.1) ) ); let a = Rc::clone(&value); // ポインタを複製 let b = Rc::clone(&value); // ポインタを複製 // borrow_mut()でアクセス // 構造体を上書きする *a.borrow_mut() = Color::new(0.0,0.0,0.0); println!("a: {:?}", a.borrow()); // 構造体の要素を上書きする b.borrow_mut().r=1.0; println!("b: {:?}", b.borrow()); // borrow()では書き込みできない // b.borrow().g = 1.0; }
これもNoneと組み合わせてnullptrのようなものを表現できる。
この時、Rc::strong_countを使うと、有効なポインタの個数を取得できる。
use std::borrow::Borrow; use std::rc::Rc; use std::cell::RefCell; #[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } } fn main(){ let value = Rc::new( RefCell::new( Color::new(0.3,0.2,0.1) ) ); let mut ref_a = Some(Rc::clone(&value)); // ポインタを複製 let ref_b = Some(Rc::clone(&value)); // ポインタを複製 // 参照の個数を取得 // value , ref_a , ref_b の三つなので3 println!("pointer count: {}",Rc::strong_count(&value)); // 3 ref_a = None; // ref_a を無効にする // ref_a が None なので value , ref_b の二つで2 println!("pointer count: {}",Rc::strong_count(&value)); // 2
if let Some(aa) = ref_a{ *aa.borrow_mut() = Color::new(0.0,0.0,0.0); } else{ println!("ref_a is invalid"); }
if let Some(bb) = ref_b{ *bb.borrow_mut() = Color::new(1.0,1.0,1.0); } else{ println!("ref_b is invalid"); }
// ref_bで書き換えた値が得られる println!("{:?}",value); }
Rc::newでSharedポインタを作り、Rc::downgrade()関数でWeakポインタを作る。
問題は有効性の確認のところで、upgrade()関数がOption<Rc<T>>を返すので、upgrade()の戻り値がSomeを持っている。Someが有効かどうかをチェックするため、if letでパターンマッチする。
use std::rc::{Rc, Weak};
#[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } }
fn main() { let weak; { // Sharedポインタ let shared = Rc::new(Color::new(0.3,0.5,0.0)); // Weakポインタ作成 weak = Rc::downgrade(&shared); // 所有権を持っているのがsharedなので、ここでsharedは破棄される } // weak.Some が有効なら、変数valueに値が入るので表示する if let Some(value) = weak.upgrade() { // weakが指しているデータを表示 println!("{:?}", value); } else { println!("weakは無効"); } }
Weakポインタの個数を取得できる。weak_count()を呼び出す。
fn main() { let weak; { // Sharedポインタ let shared = Rc::new(Color::new(0.3,0.5,0.0)); // Weakポインタ作成 weak = Rc::downgrade(&shared); // Weakポインタをコピーする let weak2 = weak.clone(); println!("{}",weak.weak_count()); // 有効なWeakの数を得る。2 になる // 所有権を持っているのがsharedなので、ここでsharedは破棄される } println!("{}",weak.weak_count()); // 有効なWeakの数を得る。 0 になる }
Rustでは、変数に対する=式で変数の所有権が移動してしまう。従って、安易に=代入するとmoveが発生してしまいコンパイルできなくなる。
#[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } } fn main() { let color = Color::new(0.5,0.2,0.0); let ref1 = color; let ref2 = color; // ref1にcolorが入っているのでcolorは空。 println!("{:?}",color); }
これを避けるには参照(&T)を使うか、Rcという参照カウンタを持つスマートポインタを使う。C++でいうところのshared_ptr。
ここで重要な違いだが、
・Rc ... それぞれが所有権を持つ。すべてのRcが破棄された時が変数(の値)の寿命
・&T ... 参照は所有権を持たないので、有効範囲が参照元の変数の寿命に依存する。
Rc::new()でポインタを作成し、clone()で参照を複製する。
use std::rc::Rc; #[derive(Debug)] struct AB{ a:i32, b:i32, } ////////////////////////////////////////////////// fn main( ){ let ref3; { // Sharedポインタ作成 let myref=Rc::new(AB{a:5,b:10}); { // ポインタを複製。値の複製ではない let ab=myref.clone(); println!("{:?}",ab); // 参照カウント println!("Reference Count ab: {}",Rc::strong_count(&ab)); } // ref3は所有権を持つので、 // myrefのスコープが切れても値は破棄されない ref3 = myref.clone(); } println!("Reference Count ref3: {}",Rc::strong_count(&ref3)); // myrefは無効 // ref3 は有効 let k = ref3.a; println!("{}",k); }
Rustにはnullptrがない。Rcが無効であることを表現できないので、Optionと組み合わせる。
Optionは値があればSomeの中に入っている。値がなければNoneを持つ。
use std::{rc::Rc}; #[derive(Debug)] struct Color{ r:f32, g:f32, b:f32 } impl Color{ fn new(R:f32,G:f32,B:f32)->Color{ return Color{r:R,g:G,b:B}; } } ////////////////////////////////////////////////// fn main( ){ let my_color = Color::new(0.5, 0.4, 0.3); // nullptrができないのでOptionと組み合わせる let mut data: Option<Rc<Color>> = None; // Optionなので、Option::Someに値を入れる data = Some(Rc::new(my_color)); if let Some(color) = data{// dataが有効ならcolorを定義して代入 println!("{:?}",color); } else{ println!("nullptr"); } }
Rc::newにメモリ管理を移管するのは簡単だが、その逆はできないらしい。即ち、一度Rcで管理すると決めたら、もう自分で管理できなくなる。
use std::rc::Rc; #[derive(Debug)] struct AB{ a:i32, b:i32, } ////////////////////////////////////////////////// fn main( ){ let ab = AB{a:5,b:10}; let myref=Rc::new(ab);// abからmyrefにmoveするので、以後 ab は使えない println!("{:?}",myref);// ここで ab を使おうとするとビルドエラー }
#include <iostream> #ifdef _DEBUG #pragma comment(lib, "tiffd.lib") #else #pragma comment(lib, "tiff.lib") #endif #include <tiffio.h> #include <vector> #include <cassert> #pragma warning(disable:4996) int main() { // 保存する画像のサイズ std::uint32_t width = 2; std::uint32_t height = 4; // 画像を作成 std::vector<std::uint8_t> mydata(width * height * 3); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { int pos = y * width + x; mydata[pos * 3 + 0] = float(pos) / (width*height) * 255; mydata[pos * 3 + 1] = 0; mydata[pos * 3 + 2] = 0; } } // tiffファイルの書き込み開始 TIFF* out = TIFFOpen("D:\\Data\\test.tif", "w"); TIFFSetField(out, TIFFTAG_IMAGEWIDTH, width); TIFFSetField(out, TIFFTAG_IMAGELENGTH, height); TIFFSetField(out, TIFFTAG_SAMPLESPERPIXEL, 3); // RGB color TIFFSetField(out, TIFFTAG_BITSPERSAMPLE, 8); // Per channel TIFFSetField(out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT /*ORIENTATION_BOTRIGHT*/); TIFFSetField(out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);//色の格納方法 RGBRGBRGBかRRRGGGBBBか等 TIFFSetField(out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);//ピクセルデータの解釈方法 tsize_t scanlinebytes = 3 * width;// mydataのスキャンライン計算 TIFFSetField(out, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(out, scanlinebytes )); for (std::uint32_t row = 0; row < height; row++) { // row 行目の先頭アドレスを取得 uint8_t* ptr = &mydata[0] + row * scanlinebytes; if (TIFFWriteScanline(out, ptr, row, 0) < 0) // 書き込み break; } TIFFClose(out); return 0; }
CMakeはかなり楽。
Where is the source code: D:/myDevelop/mydev/libtiff/libtiff-master
Where to build the binaries: D:/myDevelop/mydev/libtiff/libtiff-sln
CMAKE_INSTALL_PREFIX: D:/myDevelop/mydev/libtiff/libtiff-install
slnを開いて、ALL_BUILD → INSTALL 。
せっかくなのでTIFFTAG_PHOTOMETRIC や TIFFTAG_PLANARCONFIG を取得する例を書いているが使っていないので消していい。
#include <iostream> #ifdef _DEBUG #pragma comment(lib, "tiffd.lib") #else #pragma comment(lib, "tiff.lib") #endif #include <tiffio.h> #include <vector> #include <cassert> #pragma warning(disable:4996) void pnmP3_Write(const char* const fname, const int width, const int height, const unsigned char* const p); int main() { // tiffファイルの読み込み TIFF* tif = TIFFOpen("D:\\Data\\test.tif", "r"); if (tif == nullptr) { std::cout << "Error: Cannot open tiff file" << std::endl; return -1; } // tifデータの情報を取得 std::uint32_t width, height; std::uint16_t bits_per_sample, samples_per_pixel; std::uint16_t photometric; std::uint16_t planar_config; std::uint16_t sample_format; TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &width); // 画像の幅 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &height); // 画像の高さ TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bits_per_sample); // 1画素あたりのビット数 TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &samples_per_pixel); // 1画素あたりのサンプル数 TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric); // 画像のフォーマット TIFFGetField(tif, TIFFTAG_PLANARCONFIG, &planar_config); // 画像の格納形式 TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sample_format); // 画像のデータフォーマット // 8bitのみ対応 assert(bits_per_sample == 8); // RGBの場合はAは不透明に設定される assert(samples_per_pixel == 3 || samples_per_pixel == 4); size_t image_size = width * height; // 画像を読み込むためのバッファを作成 std::uint32_t* buffer = (std::uint32_t*)_TIFFmalloc(image_size * sizeof(std::uint32_t)); // 画像を保存するためのバッファを作成 std::vector<std::uint8_t> mydata(image_size * 3); // RGBの3チャンネルだけ使用 // 画像を読み込む if (TIFFReadRGBAImage(tif, width, height, buffer, 0)) { for (size_t i = 0; i < image_size; i++) { mydata[i * 3 + 0] = TIFFGetR(buffer[i]); mydata[i * 3 + 1] = TIFFGetG(buffer[i]); mydata[i * 3 + 2] = TIFFGetB(buffer[i]); } } _TIFFfree(buffer); // tiffオブジェクトを解放 TIFFClose(tif); pnmP3_Write("test.ppm", width, height, mydata.data()); std::cout << "Hello World!\n"; } ////////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] vmax 全てのRGBの中の最大値 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname,const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, 255); size_t channelsize = 3; size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * channelsize + 0], p[k * channelsize + 1], p[k * channelsize + 2]); k++; } fprintf(fp, "\n"); } fclose(fp); }