全年6月6日の投稿[5件]
2025年 この範囲を時系列順で読む この範囲をファイルに出力する
しばらく前に書いた Art-Net を扱うコードです。
今だったらこうするのにってところは多いですが、これをたたき台にしようかなと。
表示の都合で一部の文字を全角にしています。コピペしてお使いになる際はご注意ください。オレオレライブラリーやオレオレヘッダーが必要なのでこのソースだけでは動きませんけど、要点の参考になれば幸いかと。
/* --------------
Art-Net Engine
ae_main.c
-------------- */
// 汎用ライブラリをインクルード
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
//#include <netinet/in.h>
//#include <sys/ipc.h>
// 自作ライブラリをインクルード
#include "ae_main.h"
#include "std_inkey.h"
#include "std_dump.h"
// Main
int main( int argc, char *argv〔〕 ) {
// 汎用変数
// int i ;
// int ret = 0 ;
// 共有メモリ変数
ae_sharemem_t *ae_sharemem ;
int ae_sharememsize = sizeof( ae_sharemem_t ) ;
int ae_sharememID ;
//【開発用】共有メモリ領域の確保 上位モジュールが出来たらそちらで作る
ae_sharememID = shmget( IPC_PRIVATE, ae_sharememsize, SHM_R|SHM_W ) ;
// 共有メモリ定義
ae_sharemem = shmat( ae_sharememID, 0, SHM_R|SHM_W ) ;
// nsec時変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int start_nsec ; // 処理開始nsec時を保存
//【開発用】処理時間変数
unsigned long long int lap_nsec ; // 処理終了nsec時
static unsigned long long int max_nsec = 0 ; // 経過したnsecの最大値
// Art-Net 受信変数
int an_rx ; // 受信socket番号
struct sockaddr_in an_rx_addr ; // 受信先指定構造体
int an_rx_length ; // recvfromで得たデータ長
char an_rx_nic〔〕 = { "eth0" } ; // 受信NICの名称
char an_rx_buf〔 ADMX_BUF_SIZE 〕 ; // 受信バッファ ArtDMXの最大バイト長で設定
socklen_t an_rx_sin_size = ADMX_BUF_SIZE ; // recvfromに渡す受信バッファサイズ
struct sockaddr_in an_rx_from_addr ; // 送信元情報の構造体
artdmx_t artdmx_rx ; // 受信したArtDMXを保存する構造体
// Art-Net 送信変数
int an_tx ; // 送信socket番号
struct sockaddr_in an_tx_addr ; // 送信先指定構造体
char an_tx_nic〔〕 = { "eth1" } ; // 受信NICの名称
int yes = 1 ; // 送信をブロードキャストに設定する際のフラグ変数
artdmx_t artdmx_tx〔 ADMX_TX_UNIVRESES 〕 ; // 送信するArtDMXを保存する構造体配列
char an_tx_buf〔 ADMX_BUF_SIZE 〕 ; // 送信バッファ Art-DMXの最大バイト長で設定
int an_tx_count = 0 ; // 送信ユニバースカウンタ
unsigned long long int an_tx_nsec = 0 ; // 送信nsec時を保存
float an_tx_fps = 29.97 ; // 送信フレームレート
unsigned int an_tx_interval =
( unsigned int )( 1e9 / ADMX_TX_UNIVRESES / an_tx_fps ) ;
// Art-Netの受信ソケットを作成
an_rx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 受信ソケットのインスタンスを作成
setsockopt( an_rx, SOL_SOCKET, SO_BINDTODEVICE, // 受信NICを指定する
&an_rx_nic, sizeof( an_rx_nic ) );
an_rx_addr.sin_family = AF_INET ; // 受信をUDPに設定
an_rx_addr.sin_port = htons( AN_PORT ) ; // 受信ポート番号を設定
an_rx_addr.sin_addr.s_addr = INADDR_ANY ; // 送信元アドレスを無指定にする
bind( an_rx, ( struct sockaddr * ) &an_rx_addr, // 上記をインスタンスに設定する
sizeof( an_rx_addr ) ) ;
// Art-Netの送信ソケットを作成
an_tx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 送信ソケットのインスタンスを作成
an_tx_addr.sin_family = AF_INET ; // 送信をUDPに設定
an_tx_addr.sin_port = htons( AN_PORT ) ; // 送信ポート番号を設定
an_tx_addr.sin_addr.s_addr = inet_addr( "255.255.255.255" ) ; // 送信先アドレスを無指定にする
setsockopt( an_tx, SOL_SOCKET, SO_BINDTODEVICE, // 送信NICを指定する
&an_tx_nic, sizeof( an_tx_nic ) ) ;
setsockopt( an_tx, SOL_SOCKET, SO_BROADCAST, // 送信をブロードキャストに設定
( char * )&yes, sizeof( yes ) ) ;
// std_inkey 用変数
int key_code_length = 0 ;
int key_code_length_st = 0 ;
char key_code〔 32 〕 = { 0x00 } ;
char key_code_st〔 32 〕 = { 0x00 } ;
// std_inkey を開始
set_inkey( SET ) ;
// ***** Art-Netの受送信と表示 *****
printf( "\e〔2J\e〔?25l" ) ; // 画面を消去 カーソル非表示 \e〔?25l カーソル非表示
while( strcmp( key_code, _KEY_F12 ) != 0 ) {
// ------ 開始nsec時を取得 ------
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
start_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// --------- キー入力 ----------
key_code_length = get_inkey( key_code ) ; // キー入力を取得
if( strcmp( key_code, "\x20" ) == 0 ) max_nsec = 0 ; //【開発用】キー入力が〔SP〕なら実行時間計測の最大値をクリア
if( key_code_length > 0 ) { // キー入力を別関数に渡すためのスタック
key_code_length_st = key_code_length ;
memcpy( key_code_st, key_code, sizeof( key_code ) ) ;
}
// --------- 受 信 -----------
memset( an_rx_buf, 0, ADMX_BUF_SIZE ) ; // 受信バッファのクリア
an_rx_length = recvfrom( an_rx, an_rx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // an_rx_from_addrには送信元情報が入る
( struct sockaddr * )&an_rx_from_addr, &an_rx_sin_size ) ;
// 受信がある場合
if( an_rx_length > 0 ) { // an_rx_lengthは受信値のバイト長
// 送信元IPアドレスとポートを抽出し、構造体artdmx_rxに保存
inet_ntop( AF_INET, &an_rx_from_addr.sin_addr, // 受信データから送信元IPアドレスを抽出保存
artdmx_rx.senderip, sizeof( artdmx_rx.senderip ) ) ;
strcpy( artdmx_rx.senderip, inet_ntoa( an_rx_from_addr.sin_addr ) ) ;
artdmx_rx.senderport = ntohs( an_rx_from_addr.sin_port ) ; // 受信データから送信元ポートを抽出保存
artdmx_rx.recv_nsec = start_nsec ; // 受信日時を保存
// 受信したArtDMXをデコード
if( artdmx_decode( an_rx_buf, &artdmx_rx ) >= 0 ) // ArtDMXなら送信スタックに保存
memcpy( &artdmx_tx〔 artdmx_rx.universe 〕, // ArtDMX以外なら読み飛ばし
&artdmx_rx, sizeof( artdmx_t ) ) ;
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
}
// --------- 送 信 -----------
else if( start_nsec - an_tx_nsec > ( unsigned int )( an_tx_interval ) ) {
artdmx_encode( an_tx_buf, &artdmx_tx〔 an_tx_count 〕 ) ; // 送信データをエンコード
sendto( an_tx, an_tx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // 送信実行
( struct sockaddr * )&an_tx_addr, sizeof( an_tx_addr ) ) ;
/*
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
*/
//【開発用】受信データの表示
artdmx_check_disp( &artdmx_tx〔 an_tx_count 〕, // 送信データの仮表示
key_code_st, &key_code_length_st, &lap_nsec, &max_nsec ) ;
// 送信事後処理
an_tx_nsec = start_nsec ; // 送信インターバル・次回用のnsec取得
if( ++an_tx_count > 7 ) an_tx_count = 0 ; // 送信ルートカウンタインクリメント
}
// 待ち時間 無駄にループしても処理負荷が増えるだけなので少しお休み
usleep( 1 ) ;
}
// --------- 終 了 ----------
set_inkey( RESET ) ; // std_inkeyを終了
close( an_rx ) ; // 受信ソケットをクローズ
close( an_tx ) ; // 送信ソケットをクローズ
printf( "\e〔?25h\e〔0m\n" ) ; // コンソール画面を戻す \e〔?25h カーソルの表示
shmdt( ae_sharemem ) ; // 共有メモリの切り離し
//【開発用】上位モジュールが出来たらそちらで処理する
shmctl( ae_sharememID, IPC_RMID, 0) ; // 共有メモリ領域開放
return 0 ;
}
// ArtDMXのデコード関数
int artdmx_decode( char *an_rx_buf, artdmx_t *artdmx_rx ) {
int i ;
char int_st〔 sizeof( int ) 〕 ;
// IDを抽出
for( i = 0; i < 8; i++ ) { // bufの先頭から8バイト
artdmx_rx->id〔 i 〕 = an_rx_buf〔 i 〕 ;
}
// OpCodeを抽出
int_st〔 0 〕 = an_rx_buf〔 8 〕 ;
int_st〔 1 〕 = an_rx_buf〔 9 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->opcode = *( ( int * )&int_st〔 0 〕) ;
if( ! (strcmp( artdmx_rx->id, "Art-Net" ) == 0 // ArtDMXかチェックし、そうでなければエラーを返して終了
&& artdmx_rx->opcode == 0x5000 ) ) {
return -1 ;
}
// ProVerを抽出
int_st〔 0 〕 = an_rx_buf〔 11 〕 ;
int_st〔 1 〕 = an_rx_buf〔 10 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->prover = *( ( int * )&int_st〔 0 〕) ;
// Sequenceを抽出
artdmx_rx->sequence = an_rx_buf〔 12 〕 ;
// Physicalを抽出
artdmx_rx->physical = an_rx_buf〔 13 〕 ;
// Universeを抽出
artdmx_rx->subnet = an_rx_buf〔 14 〕 / 0x10 ;
artdmx_rx->universe = an_rx_buf〔 14 〕 % 0x10 ;
artdmx_rx->net = an_rx_buf〔 15 〕 ;
// Lengthを抽出
int_st〔 0 〕 = an_rx_buf〔 17 〕 ;
int_st〔 1 〕 = an_rx_buf〔 16 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->length = *( ( int * )&int_st〔0〕) ;
// Dataを抽出
for( i = 0; i < artdmx_rx->length; i++ ) {
artdmx_rx->data〔 i 〕 = an_rx_buf〔 i + 18 〕 ;
}
if( artdmx_rx->length < 512 ) {
for( i = artdmx_rx->length; i < 512; i++ ) {
artdmx_rx->data〔 i 〕 = 0x00 ;
}
}
return 0 ;
}
// ArtDMXのエンコード関数
int artdmx_encode( char *an_tx_buf, artdmx_t *artdmx_tx ) {
int i ;
// IDを設定
for( i = 0; i < 8; i++ ) {
an_tx_buf〔 i 〕 = artdmx_tx->id〔 i 〕 ;
}
// OpCodeを設定
an_tx_buf〔 8 〕 = (char)( artdmx_tx->opcode % 0x100 ) ;
an_tx_buf〔 9 〕 = (char)( artdmx_tx->opcode / 0x100 ) ;
// ProVerを設定
an_tx_buf〔 10 〕 = (char)( artdmx_tx->prover / 0x100 ) ;
an_tx_buf〔 11 〕 = (char)( artdmx_tx->prover % 0x100 ) ;
// Sequenceを設定
an_tx_buf〔 12 〕 = (char) artdmx_tx->sequence ;
// Physicalを設定
an_tx_buf〔 13 〕 = (char) artdmx_tx->physical ;
// SubUniを設定
an_tx_buf〔 14 〕 = (char)( artdmx_tx->subnet * 0x100 + artdmx_tx->universe ) ;
// Netを設定
an_tx_buf〔 15 〕 = (char)artdmx_tx->net ;
// Lengthを設定
an_tx_buf〔 16 〕 = (char)( artdmx_tx->length / 0x100 ) ;
an_tx_buf〔 17 〕 = (char)( artdmx_tx->length % 0x100 ) ;
// Dataを設定
memcpy( an_tx_buf + 18, artdmx_tx->data, 512 ) ;
return 0 ;
}
// ArtDMXのチェック用画面表示
int artdmx_check_disp( artdmx_t *artdmx, char *key_code_st, int *key_code_length_st,
unsigned long long int *val1, unsigned long long int *val2 ) {
// 汎用変数
//int i ;
// タイムカウント変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int now_nsec ; // 現在nsecを保存
static unsigned long long int prev_nsec = 0 ; // 過去nsecを保存
// データ管理用
static artdmx_t artdmx_disp ;
// std_dump 用変数
static int first_line = 0 ; // 表示開始行数
// 表示制御変数
struct winsize ws ;
static int col = 20 ;
static int row = 100 ;
static int net = 0 ;
static int subnet = 0 ;
static int universe = 0 ;
// カーソルキー入力
if( *key_code_length_st > 0 ) {
if( strcmp( key_code_st, _KEY_RIGHT ) == 0 ) {
if( ++universe > 7 ) universe = 7 ;
}
if( strcmp( key_code_st, _KEY_LEFT ) == 0 ) {
if( --universe < 0 ) universe = 0 ;
}
if( strcmp( key_code_st, _KEY_UP ) == 0 ) {
first_line-- ;
}
if( strcmp( key_code_st, _KEY_DOWN ) == 0 ) {
first_line++ ;
}
*key_code_length_st = 0 ;
}
// 対象ユニバースのデータをスタック
if( ( artdmx->net == net ) && ( artdmx->subnet == subnet ) && ( artdmx->universe == universe ) ) {
memcpy( &artdmx_disp, artdmx, sizeof( artdmx_t ) ) ;
}
// タイムカウント
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
if( now_nsec - prev_nsec < 5e7) return -1 ; // 経過時間が不足なら何もせずリターン
prev_nsec = now_nsec ; // 過去nsecを現在値としておく
// 画面のテキスト表示サイズを取得
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) ; // 画面サイズを取得 幅=ws.ws_col 高=ws.ws_row
if( row + 4 > ws.ws_row ) row = ws.ws_row - 4 ; // dumpの行数制限
// artdmxを表示
printf( "\e〔0;0H" ) ; // カーソルを左上に
printf( "Sender: %s:%d", artdmx_disp.senderip, artdmx_disp.senderport ) ;
printf( "\e〔28GID: %s", artdmx_disp.id ) ;
printf( "\e〔41GOpCode: 0x%04X", artdmx_disp.opcode ) ;
printf( "\e〔57GProVer: 0x%04X", artdmx_disp.prover ) ;
printf( "\e〔0K\nSequence: 0x%04X", artdmx_disp.sequence ) ;
printf( "\e〔19GPhysical: 0x%04X", artdmx_disp.physical ) ;
printf( "\e〔37GLength: %d", artdmx_disp.length ) ;
printf( "\e〔50G< Net:%d SubNet:%d Universe:%d >\e〔0K\n", artdmx_disp.net, artdmx_disp.subnet, artdmx_disp.universe ) ;
dump_print( artdmx_disp.data, artdmx_disp.length, col, row, &first_line, 0 ) ;
printf( "%8lld /%8lld nsec", *val1, *val2 ) ;
//fflush( stdout ) ;
// 終了
return 0 ;
}
#[Art-Net] #C言語
今だったらこうするのにってところは多いですが、これをたたき台にしようかなと。
表示の都合で一部の文字を全角にしています。コピペしてお使いになる際はご注意ください。オレオレライブラリーやオレオレヘッダーが必要なのでこのソースだけでは動きませんけど、要点の参考になれば幸いかと。
/* --------------
Art-Net Engine
ae_main.c
-------------- */
// 汎用ライブラリをインクルード
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/ioctl.h>
#include <sys/shm.h>
//#include <netinet/in.h>
//#include <sys/ipc.h>
// 自作ライブラリをインクルード
#include "ae_main.h"
#include "std_inkey.h"
#include "std_dump.h"
// Main
int main( int argc, char *argv〔〕 ) {
// 汎用変数
// int i ;
// int ret = 0 ;
// 共有メモリ変数
ae_sharemem_t *ae_sharemem ;
int ae_sharememsize = sizeof( ae_sharemem_t ) ;
int ae_sharememID ;
//【開発用】共有メモリ領域の確保 上位モジュールが出来たらそちらで作る
ae_sharememID = shmget( IPC_PRIVATE, ae_sharememsize, SHM_R|SHM_W ) ;
// 共有メモリ定義
ae_sharemem = shmat( ae_sharememID, 0, SHM_R|SHM_W ) ;
// nsec時変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int start_nsec ; // 処理開始nsec時を保存
//【開発用】処理時間変数
unsigned long long int lap_nsec ; // 処理終了nsec時
static unsigned long long int max_nsec = 0 ; // 経過したnsecの最大値
// Art-Net 受信変数
int an_rx ; // 受信socket番号
struct sockaddr_in an_rx_addr ; // 受信先指定構造体
int an_rx_length ; // recvfromで得たデータ長
char an_rx_nic〔〕 = { "eth0" } ; // 受信NICの名称
char an_rx_buf〔 ADMX_BUF_SIZE 〕 ; // 受信バッファ ArtDMXの最大バイト長で設定
socklen_t an_rx_sin_size = ADMX_BUF_SIZE ; // recvfromに渡す受信バッファサイズ
struct sockaddr_in an_rx_from_addr ; // 送信元情報の構造体
artdmx_t artdmx_rx ; // 受信したArtDMXを保存する構造体
// Art-Net 送信変数
int an_tx ; // 送信socket番号
struct sockaddr_in an_tx_addr ; // 送信先指定構造体
char an_tx_nic〔〕 = { "eth1" } ; // 受信NICの名称
int yes = 1 ; // 送信をブロードキャストに設定する際のフラグ変数
artdmx_t artdmx_tx〔 ADMX_TX_UNIVRESES 〕 ; // 送信するArtDMXを保存する構造体配列
char an_tx_buf〔 ADMX_BUF_SIZE 〕 ; // 送信バッファ Art-DMXの最大バイト長で設定
int an_tx_count = 0 ; // 送信ユニバースカウンタ
unsigned long long int an_tx_nsec = 0 ; // 送信nsec時を保存
float an_tx_fps = 29.97 ; // 送信フレームレート
unsigned int an_tx_interval =
( unsigned int )( 1e9 / ADMX_TX_UNIVRESES / an_tx_fps ) ;
// Art-Netの受信ソケットを作成
an_rx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 受信ソケットのインスタンスを作成
setsockopt( an_rx, SOL_SOCKET, SO_BINDTODEVICE, // 受信NICを指定する
&an_rx_nic, sizeof( an_rx_nic ) );
an_rx_addr.sin_family = AF_INET ; // 受信をUDPに設定
an_rx_addr.sin_port = htons( AN_PORT ) ; // 受信ポート番号を設定
an_rx_addr.sin_addr.s_addr = INADDR_ANY ; // 送信元アドレスを無指定にする
bind( an_rx, ( struct sockaddr * ) &an_rx_addr, // 上記をインスタンスに設定する
sizeof( an_rx_addr ) ) ;
// Art-Netの送信ソケットを作成
an_tx = socket( AF_INET, SOCK_DGRAM, 0 ) ; // 送信ソケットのインスタンスを作成
an_tx_addr.sin_family = AF_INET ; // 送信をUDPに設定
an_tx_addr.sin_port = htons( AN_PORT ) ; // 送信ポート番号を設定
an_tx_addr.sin_addr.s_addr = inet_addr( "255.255.255.255" ) ; // 送信先アドレスを無指定にする
setsockopt( an_tx, SOL_SOCKET, SO_BINDTODEVICE, // 送信NICを指定する
&an_tx_nic, sizeof( an_tx_nic ) ) ;
setsockopt( an_tx, SOL_SOCKET, SO_BROADCAST, // 送信をブロードキャストに設定
( char * )&yes, sizeof( yes ) ) ;
// std_inkey 用変数
int key_code_length = 0 ;
int key_code_length_st = 0 ;
char key_code〔 32 〕 = { 0x00 } ;
char key_code_st〔 32 〕 = { 0x00 } ;
// std_inkey を開始
set_inkey( SET ) ;
// ***** Art-Netの受送信と表示 *****
printf( "\e〔2J\e〔?25l" ) ; // 画面を消去 カーソル非表示 \e〔?25l カーソル非表示
while( strcmp( key_code, _KEY_F12 ) != 0 ) {
// ------ 開始nsec時を取得 ------
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
start_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
// --------- キー入力 ----------
key_code_length = get_inkey( key_code ) ; // キー入力を取得
if( strcmp( key_code, "\x20" ) == 0 ) max_nsec = 0 ; //【開発用】キー入力が〔SP〕なら実行時間計測の最大値をクリア
if( key_code_length > 0 ) { // キー入力を別関数に渡すためのスタック
key_code_length_st = key_code_length ;
memcpy( key_code_st, key_code, sizeof( key_code ) ) ;
}
// --------- 受 信 -----------
memset( an_rx_buf, 0, ADMX_BUF_SIZE ) ; // 受信バッファのクリア
an_rx_length = recvfrom( an_rx, an_rx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // an_rx_from_addrには送信元情報が入る
( struct sockaddr * )&an_rx_from_addr, &an_rx_sin_size ) ;
// 受信がある場合
if( an_rx_length > 0 ) { // an_rx_lengthは受信値のバイト長
// 送信元IPアドレスとポートを抽出し、構造体artdmx_rxに保存
inet_ntop( AF_INET, &an_rx_from_addr.sin_addr, // 受信データから送信元IPアドレスを抽出保存
artdmx_rx.senderip, sizeof( artdmx_rx.senderip ) ) ;
strcpy( artdmx_rx.senderip, inet_ntoa( an_rx_from_addr.sin_addr ) ) ;
artdmx_rx.senderport = ntohs( an_rx_from_addr.sin_port ) ; // 受信データから送信元ポートを抽出保存
artdmx_rx.recv_nsec = start_nsec ; // 受信日時を保存
// 受信したArtDMXをデコード
if( artdmx_decode( an_rx_buf, &artdmx_rx ) >= 0 ) // ArtDMXなら送信スタックに保存
memcpy( &artdmx_tx〔 artdmx_rx.universe 〕, // ArtDMX以外なら読み飛ばし
&artdmx_rx, sizeof( artdmx_t ) ) ;
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
}
// --------- 送 信 -----------
else if( start_nsec - an_tx_nsec > ( unsigned int )( an_tx_interval ) ) {
artdmx_encode( an_tx_buf, &artdmx_tx〔 an_tx_count 〕 ) ; // 送信データをエンコード
sendto( an_tx, an_tx_buf, ADMX_BUF_SIZE, MSG_DONTWAIT, // 送信実行
( struct sockaddr * )&an_tx_addr, sizeof( an_tx_addr ) ) ;
/*
//【開発用】終了nsec時を取得
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
lap_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec - start_nsec ; // 取得値をひとまとめにする(nsec)
if( max_nsec < lap_nsec ) max_nsec = lap_nsec ; // ここまでの最大処理時間を得る
*/
//【開発用】受信データの表示
artdmx_check_disp( &artdmx_tx〔 an_tx_count 〕, // 送信データの仮表示
key_code_st, &key_code_length_st, &lap_nsec, &max_nsec ) ;
// 送信事後処理
an_tx_nsec = start_nsec ; // 送信インターバル・次回用のnsec取得
if( ++an_tx_count > 7 ) an_tx_count = 0 ; // 送信ルートカウンタインクリメント
}
// 待ち時間 無駄にループしても処理負荷が増えるだけなので少しお休み
usleep( 1 ) ;
}
// --------- 終 了 ----------
set_inkey( RESET ) ; // std_inkeyを終了
close( an_rx ) ; // 受信ソケットをクローズ
close( an_tx ) ; // 送信ソケットをクローズ
printf( "\e〔?25h\e〔0m\n" ) ; // コンソール画面を戻す \e〔?25h カーソルの表示
shmdt( ae_sharemem ) ; // 共有メモリの切り離し
//【開発用】上位モジュールが出来たらそちらで処理する
shmctl( ae_sharememID, IPC_RMID, 0) ; // 共有メモリ領域開放
return 0 ;
}
// ArtDMXのデコード関数
int artdmx_decode( char *an_rx_buf, artdmx_t *artdmx_rx ) {
int i ;
char int_st〔 sizeof( int ) 〕 ;
// IDを抽出
for( i = 0; i < 8; i++ ) { // bufの先頭から8バイト
artdmx_rx->id〔 i 〕 = an_rx_buf〔 i 〕 ;
}
// OpCodeを抽出
int_st〔 0 〕 = an_rx_buf〔 8 〕 ;
int_st〔 1 〕 = an_rx_buf〔 9 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->opcode = *( ( int * )&int_st〔 0 〕) ;
if( ! (strcmp( artdmx_rx->id, "Art-Net" ) == 0 // ArtDMXかチェックし、そうでなければエラーを返して終了
&& artdmx_rx->opcode == 0x5000 ) ) {
return -1 ;
}
// ProVerを抽出
int_st〔 0 〕 = an_rx_buf〔 11 〕 ;
int_st〔 1 〕 = an_rx_buf〔 10 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->prover = *( ( int * )&int_st〔 0 〕) ;
// Sequenceを抽出
artdmx_rx->sequence = an_rx_buf〔 12 〕 ;
// Physicalを抽出
artdmx_rx->physical = an_rx_buf〔 13 〕 ;
// Universeを抽出
artdmx_rx->subnet = an_rx_buf〔 14 〕 / 0x10 ;
artdmx_rx->universe = an_rx_buf〔 14 〕 % 0x10 ;
artdmx_rx->net = an_rx_buf〔 15 〕 ;
// Lengthを抽出
int_st〔 0 〕 = an_rx_buf〔 17 〕 ;
int_st〔 1 〕 = an_rx_buf〔 16 〕 ;
int_st〔 2 〕 = 0x00 ;
int_st〔 3 〕 = 0x00 ;
artdmx_rx->length = *( ( int * )&int_st〔0〕) ;
// Dataを抽出
for( i = 0; i < artdmx_rx->length; i++ ) {
artdmx_rx->data〔 i 〕 = an_rx_buf〔 i + 18 〕 ;
}
if( artdmx_rx->length < 512 ) {
for( i = artdmx_rx->length; i < 512; i++ ) {
artdmx_rx->data〔 i 〕 = 0x00 ;
}
}
return 0 ;
}
// ArtDMXのエンコード関数
int artdmx_encode( char *an_tx_buf, artdmx_t *artdmx_tx ) {
int i ;
// IDを設定
for( i = 0; i < 8; i++ ) {
an_tx_buf〔 i 〕 = artdmx_tx->id〔 i 〕 ;
}
// OpCodeを設定
an_tx_buf〔 8 〕 = (char)( artdmx_tx->opcode % 0x100 ) ;
an_tx_buf〔 9 〕 = (char)( artdmx_tx->opcode / 0x100 ) ;
// ProVerを設定
an_tx_buf〔 10 〕 = (char)( artdmx_tx->prover / 0x100 ) ;
an_tx_buf〔 11 〕 = (char)( artdmx_tx->prover % 0x100 ) ;
// Sequenceを設定
an_tx_buf〔 12 〕 = (char) artdmx_tx->sequence ;
// Physicalを設定
an_tx_buf〔 13 〕 = (char) artdmx_tx->physical ;
// SubUniを設定
an_tx_buf〔 14 〕 = (char)( artdmx_tx->subnet * 0x100 + artdmx_tx->universe ) ;
// Netを設定
an_tx_buf〔 15 〕 = (char)artdmx_tx->net ;
// Lengthを設定
an_tx_buf〔 16 〕 = (char)( artdmx_tx->length / 0x100 ) ;
an_tx_buf〔 17 〕 = (char)( artdmx_tx->length % 0x100 ) ;
// Dataを設定
memcpy( an_tx_buf + 18, artdmx_tx->data, 512 ) ;
return 0 ;
}
// ArtDMXのチェック用画面表示
int artdmx_check_disp( artdmx_t *artdmx, char *key_code_st, int *key_code_length_st,
unsigned long long int *val1, unsigned long long int *val2 ) {
// 汎用変数
//int i ;
// タイムカウント変数
struct timespec now ; // 現在POSIX時を得る構造体
unsigned long long int now_nsec ; // 現在nsecを保存
static unsigned long long int prev_nsec = 0 ; // 過去nsecを保存
// データ管理用
static artdmx_t artdmx_disp ;
// std_dump 用変数
static int first_line = 0 ; // 表示開始行数
// 表示制御変数
struct winsize ws ;
static int col = 20 ;
static int row = 100 ;
static int net = 0 ;
static int subnet = 0 ;
static int universe = 0 ;
// カーソルキー入力
if( *key_code_length_st > 0 ) {
if( strcmp( key_code_st, _KEY_RIGHT ) == 0 ) {
if( ++universe > 7 ) universe = 7 ;
}
if( strcmp( key_code_st, _KEY_LEFT ) == 0 ) {
if( --universe < 0 ) universe = 0 ;
}
if( strcmp( key_code_st, _KEY_UP ) == 0 ) {
first_line-- ;
}
if( strcmp( key_code_st, _KEY_DOWN ) == 0 ) {
first_line++ ;
}
*key_code_length_st = 0 ;
}
// 対象ユニバースのデータをスタック
if( ( artdmx->net == net ) && ( artdmx->subnet == subnet ) && ( artdmx->universe == universe ) ) {
memcpy( &artdmx_disp, artdmx, sizeof( artdmx_t ) ) ;
}
// タイムカウント
clock_gettime( CLOCK_REALTIME, &now ) ; // 現在POSIX時間値取得
now_nsec = ( unsigned long long int )now.tv_sec * 1e9 // unsigned long long int型(8バイト長int)変数に
+ now.tv_nsec ; // 取得値をひとまとめにする(nsec)
if( now_nsec - prev_nsec < 5e7) return -1 ; // 経過時間が不足なら何もせずリターン
prev_nsec = now_nsec ; // 過去nsecを現在値としておく
// 画面のテキスト表示サイズを取得
ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) ; // 画面サイズを取得 幅=ws.ws_col 高=ws.ws_row
if( row + 4 > ws.ws_row ) row = ws.ws_row - 4 ; // dumpの行数制限
// artdmxを表示
printf( "\e〔0;0H" ) ; // カーソルを左上に
printf( "Sender: %s:%d", artdmx_disp.senderip, artdmx_disp.senderport ) ;
printf( "\e〔28GID: %s", artdmx_disp.id ) ;
printf( "\e〔41GOpCode: 0x%04X", artdmx_disp.opcode ) ;
printf( "\e〔57GProVer: 0x%04X", artdmx_disp.prover ) ;
printf( "\e〔0K\nSequence: 0x%04X", artdmx_disp.sequence ) ;
printf( "\e〔19GPhysical: 0x%04X", artdmx_disp.physical ) ;
printf( "\e〔37GLength: %d", artdmx_disp.length ) ;
printf( "\e〔50G< Net:%d SubNet:%d Universe:%d >\e〔0K\n", artdmx_disp.net, artdmx_disp.subnet, artdmx_disp.universe ) ;
dump_print( artdmx_disp.data, artdmx_disp.length, col, row, &first_line, 0 ) ;
printf( "%8lld /%8lld nsec", *val1, *val2 ) ;
//fflush( stdout ) ;
// 終了
return 0 ;
}
#[Art-Net] #C言語
2024年 この範囲を時系列順で読む この範囲をファイルに出力する
DA変換は構想だけ進めてしばらくは棚上げかな。ガッツリ取り組めば実働4-5日で作れると思うのですけどね。
ユニット関係は DX1220 を2台入れたラックにスプリッターを実装するのが先です。
頭がとっちらかって何から何をしたらいいのかゴチャゴチャです。本業もデータやら美術やら課題が多すぎ。
挙句の果てには倉庫の棚も作らねばなりません。部下が倉庫の整理を頑張ってくれているのでやりやすいようにしてやらんと。物とレイアウトの都合でありがちな棚ではダメなので作るしかありません。
頭を冷やして課題を整理しましょうかねぇ~。
#器具の製作
ユニット関係は DX1220 を2台入れたラックにスプリッターを実装するのが先です。
頭がとっちらかって何から何をしたらいいのかゴチャゴチャです。本業もデータやら美術やら課題が多すぎ。
挙句の果てには倉庫の棚も作らねばなりません。部下が倉庫の整理を頑張ってくれているのでやりやすいようにしてやらんと。物とレイアウトの都合でありがちな棚ではダメなので作るしかありません。
頭を冷やして課題を整理しましょうかねぇ~。
#器具の製作
2023年 この範囲を時系列順で読む この範囲をファイルに出力する
DI-1MUSEはコンデンサを替えて24時間経過。
音が鈍ったというか、明瞭感が弱くなっています。コンデンサを替える前と大差ない印象。
先行して替えたコンデンサでも24時間経過で一度鈍くなり48時間経過で戻る現象がありました。コンデンサのエージングはそういうものなのかもしれませんが気分は萎えます。
追記
確実ではないのですが、ピンクノイズを当てた直後は音が鈍るものの、通常の音源を数時間当てると音が戻るようです。通常の音源を3時間くらい当てて再チェックしたところ、昨日のイイ感じに戻っていました。
この話だけですとピンクノイズは余計なことに感じますが、ピンクノイズを当てて通常音源を当てると明瞭なだけでなくまろやかさも加わりますので、エージングには良いように思います。
音源100時間、ピンクノイズ100時間、音源100時間の設定で続けています。現在ピンクノイズ30時間。
#音の世界
音が鈍ったというか、明瞭感が弱くなっています。コンデンサを替える前と大差ない印象。
先行して替えたコンデンサでも24時間経過で一度鈍くなり48時間経過で戻る現象がありました。コンデンサのエージングはそういうものなのかもしれませんが気分は萎えます。
追記
確実ではないのですが、ピンクノイズを当てた直後は音が鈍るものの、通常の音源を数時間当てると音が戻るようです。通常の音源を3時間くらい当てて再チェックしたところ、昨日のイイ感じに戻っていました。
この話だけですとピンクノイズは余計なことに感じますが、ピンクノイズを当てて通常音源を当てると明瞭なだけでなくまろやかさも加わりますので、エージングには良いように思います。
音源100時間、ピンクノイズ100時間、音源100時間の設定で続けています。現在ピンクノイズ30時間。
#音の世界
今後はLTCをMTCに変換する方法も考えましょう。
LTCをバイトデータとして受信し、MTCのパケットに書き直して31,250bpsのUARTで送出すればMTCになります。
オレメモです。
PICには「変化割込み」と呼ばれるI/Oピンの入力が変化すると割込みが発生する機能があります。これとタイマーを組み合わせれば波長の計測が可能です。
PICのクロックを32MHzにした場合、LTCの波長は命令ステップ(Fosc/4)換算でビットが1なら1,666から2,083、0なら倍の3,332から4,166です。誤差10%としても1の最大値(2,291)と0の最小値(3,029)は被りませんしグレーゾーンも広いので、波長の判別はfpsの種類に関係なく可能です。fpsやフォーマットの種類はLTCのデータに書かれているので計測した波長から判断する必要はありません。
差動バイフェーズのビットは短い波長が2つ続けば1、長い波長が1つで0です。ビットデータが取れたら80bitのシフトレジスタに入れていきます。短い波長は必ず2回続きますから、長い波長の直前の短い波長が奇数回ならエラーとして仕切り直しです。80bitのシフトレジスタの末尾16bitにシンクワードが認められれば正常なLTCパケットが取得できたことになります。
LTCパケットが取得出来ればMTCパケットを作り、入力されたLTCに基づいたタイミングで31,250bpsに設定したPICのUARTから送出します。後は電気的にMIDIにすればMTCです。
必ず3フレーム遅れますが、欲しいのは絶対値ではなく相対値ですからいいかなと。
プログラムが求めるメモリサイズ次第ですが、8pinの12F1822で作る予定です。
追記
LTCにはfpsフォーマットを記載する領域はありません。訂正します。NDF/DFは記載されます。
fpsフォーマットをデータに記載するのはMTCです。
ですので、LTCの場合は波長からfpsフォーマットを推測します。
#PIC #タイムコード
LTCをバイトデータとして受信し、MTCのパケットに書き直して31,250bpsのUARTで送出すればMTCになります。
オレメモです。
PICには「変化割込み」と呼ばれるI/Oピンの入力が変化すると割込みが発生する機能があります。これとタイマーを組み合わせれば波長の計測が可能です。
PICのクロックを32MHzにした場合、LTCの波長は命令ステップ(Fosc/4)換算でビットが1なら1,666から2,083、0なら倍の3,332から4,166です。誤差10%としても1の最大値(2,291)と0の最小値(3,029)は被りませんしグレーゾーンも広いので、波長の判別はfpsの種類に関係なく可能です。
差動バイフェーズのビットは短い波長が2つ続けば1、長い波長が1つで0です。ビットデータが取れたら80bitのシフトレジスタに入れていきます。短い波長は必ず2回続きますから、長い波長の直前の短い波長が奇数回ならエラーとして仕切り直しです。80bitのシフトレジスタの末尾16bitにシンクワードが認められれば正常なLTCパケットが取得できたことになります。
LTCパケットが取得出来ればMTCパケットを作り、入力されたLTCに基づいたタイミングで31,250bpsに設定したPICのUARTから送出します。後は電気的にMIDIにすればMTCです。
必ず3フレーム遅れますが、欲しいのは絶対値ではなく相対値ですからいいかなと。
プログラムが求めるメモリサイズ次第ですが、8pinの12F1822で作る予定です。
追記
LTCにはfpsフォーマットを記載する領域はありません。訂正します。NDF/DFは記載されます。
fpsフォーマットをデータに記載するのはMTCです。
ですので、LTCの場合は波長からfpsフォーマットを推測します。
#PIC #タイムコード