ログイン

2016年3月 弊社ホームページは新しくなりました。 https://thinkridge.com

メインメニュー

携帯公式サイト


携帯電話をもっと便利に
もっと楽しく


史上初の吹奏楽専門着メロサイト


POPで癒しでライトでとんがって気持ちのいい〜オルゴール着メロをあなたに

Magome

クラウドベースの MIDI シーケンサ Magome

音楽制作に興味のある方を対象に、スタンドアロンでも使え、ネットならではの面白さも兼ね備えた音楽制作アプリの提供を目指しています

for 携帯電話

https://thinkridge.com/m/
ケータイはこちらへ

アクセスカウンタ

今日 : 221
昨日 : 199
今週 : 927
今月 : 3600
総計 : 1162708
平均 : 177
飯能 天覧山
最新ニュースリリース
ブログ

2015年11月7日(土曜日)

SNI SSL ってのがけっこう一般的になってきてるみたい

Filed under: - takatsuka @ 16時33分22秒

SNI SSL ってのがけっこう一般的になってきてるみたいで、これはかなり良さげな気がしてます。

SSL っていうと IP に紐づけるもんで、いわゆるレンタルサーバーの1サイトで使うみたいなことは事実上不可。
・・・っていうのは、ちょっと前までの話。今はそんなことなさげです。

なので、HTTP じゃ不安だ(=信用ない)から HTTPS にしたい!ってなったら1つのIPを占有できる専用サーバー(今だとVPSとかも)を使うのが定石だったのが、SNI SSL を使えば、レンタルサーバーでも HTTPS のメリットを受諾できます。

DB が使えて、cron が使えて、perl や PHP が使えるレンタルサーバーは全然珍しくないので、そこに SNI SSL を組み合わせれば、実は、Webシステム案件の多くは、証明書代含めても月々千円程度の環境で行けてしまいます。

さらに、レンタルサーバーであれば、OSのパッチあてとかメールのウイルスフィルタ対応とかは、業者様がやってくれ(ることになって)ます。

そのへんも勘案すると実は、保守費用を別にして月々数千円〜1万円以上を覚悟しないといけないと思っていたことは正しくないかもしれません。
ぜひ考慮したいところです。

とはいえ、もちろん、それでも採用出来ないケースはあります。
業者のサーバーに機密情報を置きたくないとか、
ネイティブアプリをサーバーで動かしたいとか、
WindowsXP(のIE) とかガラケーとかは SNI SSL に非対応みたいなので、もしそれら端末からのアクセスを考慮する必要があるときとか。

なにはともあれ、選択肢や可能性が増えることには違いないので、こういう新技術は上手に使っていくことが必要だって感じたと同時に、付いていけるように勉強しなきゃならないと思った次第です。
頑張ります!・・頑張りたい!・・頑張なくてもいい天賦の才がほしい!・・来世に期待しよう!

ありがとうございました。


2015年8月31日(月曜日)

google maps api 表示されなくなってた

Filed under: - takatsuka @ 11時45分33秒

ふと数日前くらいからだと思うけど、google maps api の kml layer で、デフォルトのマーカー(赤い下向き矢印っぽいやつ)が表示されてなくなってた。カスタムのマーカーは問題なく表示される。
コードは何も変えていないのになんで?と思って調査。

kml の <iconstyle><color>…</color> が存在するだけで、表示が出来なくなってるようだ。
とはいえコレ、google map でエクスポートした google 様が返してくる kml なんだけど・・。

幸い、使っているサイトは、サーバーを介して kml をクライアントに渡す作りなので、サーバー側で小細工が可能。
というわけで暫定対策として、問題のタグを全削除して出力するようにして対策。
マーカーに色がつかないけど、なにも表示されないよりはいいでしょう。

ただ、原因がまったく不明。ご存じの方おられましたらご助言賜り度。


2015年8月9日(日曜日)

VMware Server から ESXi へ

Filed under: - takatsuka @ 12時59分06秒

社内サーバー環境刷新してみました。

これまでは、1台のサーバーPCで、WindowsServer をホストOSにして、VMwareServer1.0 使ってゲストOSを5〜6個動かしておりました。

これはこれでまったく困ってなかったんですが、もう提供すらされてない VMwareServer を使い続けても芸がないかと思い、勉強かねて FreeNAS と ESXi を使った環境に移行してみました。

貧乏性 エコな性分なので、これまでどおり全てを激安サーバー1台で賄うようにして、初期投資と電気代と場所の節約を念頭に置きます。

サーバーPC は FUJITSU MX130 S2。HDD は 1T×2台。ESXi のブート用に8Gの USB メモリ。

  1. ESXi6.0 の ISO イメージをダウンロードして、USBメモリにブート可能な状態として用意。
  2. MX130 S2 に USBメモリを挿し、BIOS で USB ブート可に変更し、ESXi6.0 のインストーラーを起動。
  3. インストール先に、同じUSBメモリを指定しインストール。
    ESXi6.0 は MX130 S2 のデバイスはたぶん全て認識してくれたので、特別なことは何もなくインストール完了。
  4. ESXi 上で FreeNAS9.3 に HDD の 8G くらいを割り当ててインストール。
  5. FreeNAS に、HDD×2 の残りを割り当てる。大体 900G × 2。
  6. FreeNAS 上で、この HDD のミラーのボリュームを作り、iSCSI で共有とする。その際、暗号化をONにして、パスフレーズも設定。
  7. ESXi に戻って、この iSCSI を使ったデータストアを作成。これで、900Gサイズの暗号化されてて RAID1(ミラー)な領域を確保。
  8. これまで稼働させていた各ゲストOSを、VMware vCenter Converer Standalone を使って、上記の領域に持っていって(変換して)完了。

という感じです。

この環境の1番のメリットとしては、FreeNAS のボリュームの中に、各ゲストOSを置くことになるので、特別なハードは不要な上、各ゲストOSに何の細工もナシで、RAID と HDD 暗号化と圧縮の恩恵に預かれるところでしょうか。

HDD 故障に備えた RAID はもちろんですが、昨今の風潮的に、社内サーバーとはいえ泥棒に入られりして HDD を盗まれる可能性も考慮して、暗号化は入れときたいところです。

さらに、暗号化しておけば、HDD を破棄するときの厄介からも解放されます。
データ流出を恐れて HDD にドリルで穴開けるとか、専門業者に高いお金だして消去と廃棄を依頼するとか、かなり手間をかけなければならないところ、何も考えずにそのまま廃棄出来てしまうのはかなりメリットです。

デメリットとしては、FreeNAS がけっこうメモリを食うようなので、そこは奢っとく必要がありますが、ミラーで使う分にはそれほどでもないっぽい気もします。

もうちょっとゴージャスを求めていいんであれば、RAIDZというやつにしてもいいかもです。 HDD が最低3台必要で、メモリも食いますが、すごそうです。

一応、すんなり行かなかったこともあり、ネットで調べたりしたので、備忘録としてあげておきます。情報をあげていらっしゃる方々に感謝です。

  • VPN (ブリッジ) 通信が通らない対策。
    ローカルブリッジ機能を使用するためにはプロミスキャスモードを有効にする必要があるが、ESXi のスイッチがデフォルトで無効になってるので変更する。
    スイッチのセキュリテイタブより「無差別モード」を「承諾」に変更して「OK」をクリック
  • ESXi に WindowsXP,2003 の vSphere Client から接続できない場合の対処
    1. ESXi へ SSH にて接続
      /etc/vmware/rhttpproxy/config.xml を編集

      <vmacore> - <ssl> -

      <cipherList>ALL</cipherList> ←追記

    2. サービスを再起動
      /etc/init.d/rhttpproxy restart
  • VMware vCenter Converer Standalone で変換する際、デフォルトのままだと、元の HDD の設定に関わらず シックプロビジョニング になってしまうので、空き容量に余裕がないときは要設定変更。

これだけ便利でよくできた環境が、たいしてコストかけずに出来てしまうことに感心してしまいますし、優秀なソフトを開発されてる方々に感謝です。

いまどきは、クラウドやら Saas やら VPS やらが充実してますので、社内にサーバーやストレージを置かなきゃいけないようなケースも減ってきているとは思いますが、大量のデータを扱いたいとか、外のサーバーに大事なデータを置いておくのは不安とか、コストの面以外にも、社内に置くメリットはまだあると思います。

一応自分もIT業界の端っこに身を置いてますんで、いろいろな案件に適切な提案をしていけるよう精進しとかんとイカンなと思った次第です。


2015年2月1日(日曜日)

boost::asio で UDPホールパンチング

Filed under: - takatsuka @ 22時21分43秒

NAT 越えの技術は skype が出てきたころには話題になってる気がしますが、自前のソフトに組み込んで使えるような標準的な形に既になってるのかよくわからないってこともあり、興味本位で試してみました。

UDP ホールパンチングによる NAT 越えです。
で、boost::asio の udp の勉強も兼ねてサンプルアプリを作ってみました。

とはいえ、boost::asio の udp については難しいことは何もなく tcp よりシンプルに使えました。有難いことです。開発者の方に感謝です。

さて、UDP ホールパンチングについて。
wikipedia みると NAT の実装にはいろいろあるみたいですが、大ざっぱな分類でいう Port-Restricted cone NAT という環境同士までの P2P 通信は実現出来ました。
Full cone NAT とか Address-Restricted cone NAT の環境は試せてないんですが、Port-Restricted より条件緩いものなので、これらもたぶん問題なさそう。

で、Symmetric NAT って言われる環境はやっぱり無理そうでした。

docomo の LTE回線、純正のSPモード と MVNO (nifty の一番安いヤツ) を試しましたが、これに該当してそうです。
スマホのテザリングで繋げた PC で試したので、Symmetric NAT がプロバイダ側にあるのか、スマホのテザリング機能なのか判断できませんが残念。早々に諦めました。

で、以下は自分自身向けの備忘録もかねて、コードの抜粋と、軽く説明を・・。

  • ヘッダ
namespace RLib
{

    class CUdpSocket         :private boost::noncopyable     {         class CSocket;         const boost::shared_ptr<CSocket>    m_spSocket;     public:         CUdpSocket(asio::io_service &ioService);         ~CUdpSocket();         void Close();         bool Bind(unsigned short nPort,boost::system::error_code &ec=boost::system::error_code());         bool Connect(const std::string &sDomain,const std::string &sPort);         bool SendTo(const boost::asio::ip::udp::endpoint &endPoint,                     const boost::shared_ptr<const vector<char>> &spData,                     boost::system::error_code &ec=boost::system::error_code());         bool Send(const boost::shared_ptr<const vector<char>> &spData,                   boost::system::error_code &ec=boost::system::error_code());

        // データ受信         // ・次の読み込み待ちにする場合には Receive(); をコールすべし。コールしない場合には何もしない。         typedef boost::function<void (CUdpSocket &udpSocket,const boost::system::error_code &ec,                                 const boost::asio::ip::udp::endpoint &endPointRemote,                                 const boost::shared_ptr<const vector<char>> &spReceivedData)> FuncOnReceived;         bool Receive(const FuncOnReceived &funcOnReceived,unsigned short nBufferSize=8192);

    public:         static CString GetTextAddress(const asio::ip::udp::socket::endpoint_type &ep)             {                 return CRString::Format(_T("%s:%d"),CString(ep.address().to_string().c_str()),ep.port());             }     };

}

class CUdpHolepunching {     class CMain;     CMain   &m_main; public:     typedef boost::function<void (const CString &s)> FuncMessage;     CUdpHolepunching(const FuncMessage &funcMessage);     ~CUdpHolepunching();

    void Server(unsigned short nPort);     void Client(const std::string &sDomain,const std::string &sPort);

};

  • .cpp
class CUdpSocket::CSocket
{
public:
    CUdpSocket      *m_pUdpSocket;
    ip::udp::socket m_socket;
public:
    CSocket(CUdpSocket &udpSocket,io_service &ioService)
        :m_pUdpSocket(&udpSocket)
        ,m_socket(ioService)
        {
            m_socket.open(ip::udp::v4());
        }
};

CUdpSocket::CUdpSocket(asio::io_service &ioService) :m_spSocket(new CSocket(*this,ioService)) { }

CUdpSocket::~CUdpSocket() {     m_spSocket->m_pUdpSocket = NULL;        // 破棄されたマーク }

void CUdpSocket::Close() {     m_spSocket->m_socket.close(); }

bool CUdpSocket::Bind(unsigned short nPort,boost::system::error_code &ec) {     return m_spSocket->m_socket.bind( ip::udp::endpoint(ip::udp::v4(),nPort), ec ) == false;    // エラーなら }

bool CUdpSocket::Receive(const FuncOnReceived &funcOnReceived,unsigned short nBufferSize) {     struct F{         static void OnReceived(const boost::system::error_code &ec,size_t bytesReceived,                                boost::shared_ptr<CSocket> spSocket,                                boost::shared_ptr<ip::udp::endpoint> spEndpointRemote,                                boost::shared_ptr<vector<char>> spBuffer,const FuncOnReceived funcOnReceived)         {             if( !spSocket->m_pUdpSocket ) return;                       // 破棄されてる?             if( ec ){   // Error                 if( ec == boost::asio::error::operation_aborted ) return;                 //return; エラーでもコールはする             }

            spBuffer->resize(bytesReceived);             if( funcOnReceived ){                 funcOnReceived( *spSocket->m_pUdpSocket, ec, *spEndpointRemote, spBuffer );             }         }     };

    boost::shared_ptr<ip::udp::endpoint> spEndpoint(new ip::udp::endpoint);     boost::shared_ptr<vector<char>> spBuffer(new vector<char>(nBufferSize));    // 受信バッファ     m_spSocket->m_socket.async_receive_from(         asio::buffer(*spBuffer),         *spEndpoint,         boost::bind(             &F::OnReceived,             boost::asio::placeholders::error,             boost::asio::placeholders::bytes_transferred,             m_spSocket,spEndpoint,spBuffer,funcOnReceived)     );

    return true; }

bool CUdpSocket::SendTo(const boost::asio::ip::udp::endpoint &endPoint,                         const boost::shared_ptr<const vector<char>> &spData,boost::system::error_code &ec) {     if( !spData ){         BOOST_ASSERT(false);         return false;     }     const size_t size = m_spSocket->m_socket.send_to( asio::buffer(*spData), endPoint, 0, ec );     if( !ec ) return size == spData->size();     ATLTRACE( _T("nCUdpSocket Error Send -> %s"), CString(ec.message().c_str()) );     return false; }

bool CUdpSocket::Send(const boost::shared_ptr<const vector<char>> &spData,boost::system::error_code &ec) {     if( !spData ){         BOOST_ASSERT(false);         return false;     }     const size_t size = m_spSocket->m_socket.send( asio::buffer(*spData), 0, ec );     if( !ec ) return size == spData->size();     ATLTRACE( _T("nCUdpSocket Error Send -> %s"), CString(ec.message().c_str()) );     return false; }

////////////////////////////////////////////////

class CUdpHolepunching::CMain     :public CWindowImpl<CMain> { public:     DECLARE_WND_CLASS( _T("CMain") );     BEGIN_MSG_MAP(CMain)         MESSAGE_HANDLER(WM_TIMER, OnTimer)     END_MSG_MAP() public:     LRESULT OnTimer(UINT, WPARAM, LPARAM, BOOL&)         {             m_ioService.poll();             return 0;         } public:     const CRUid                 m_id;     FuncMessage                 m_funcMessage;     io_service                  m_ioService;     CUdpSocket                  m_udpSocket;     auto_ptr<deadline_timer>    m_apTimer;

#pragma pack(push,1)     struct CEndpoint     {         unsigned long   m_nIpv4;         unsigned short  m_nPort;         CEndpoint()             {}         CEndpoint(const ip::udp::endpoint &endPoint)             {                 m_nIpv4 = endPoint.address().to_v4().to_ulong();                 m_nPort = endPoint.port();             }     }; #pragma pack(pop)

    std::map<CRUid,CEndpoint>   m_mapMember;

    void OnReceived(CUdpSocket &udpSocket,const boost::system::error_code &ec,                     const boost::asio::ip::udp::endpoint &endPointRemote,                     const boost::shared_ptr<const vector<char>> &spBuffer)         {             if( ec ){   // Error                 m_funcMessage( CRString::Format(_T("%s : OnRecived Error [%s] %s"),                                CString(m_id.GetText().c_str()), CUdpSocket::GetTextAddress(endPointRemote),                                CString(ec.message().c_str())) );             }else{                 m_funcMessage( CRString::Format(_T("%s : OnRecived [%s]"), CString(m_id.GetText().c_str()),                                CUdpSocket::GetTextAddress(endPointRemote) ) );

                // メンツリスト更新                 vector<char> vBuffer(*spBuffer);                 if( vBuffer.size() >= sizeof(CRUid) ){ // このソケットの送り主を自身のメンツリストにマージ                     CRUid id;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(id),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&id),sizeof(id)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(id) );                     m_mapMember[id] = CEndpoint(endPointRemote);                 }else BOOST_ASSERT(false);                 // 送り主からのメンツリストを自身のメンツリストにマージ                 while( vBuffer.size() >= sizeof(CRUid)+sizeof(CEndpoint) ){                     CRUid id;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(id),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&id),sizeof(id)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(id) );                     CEndpoint ep;                     std::copy( vBuffer.begin(), vBuffer.begin()+sizeof(ep),                                stdext::checked_array_iterator<char*>(reinterpret_cast<char*>(&ep),sizeof(ep)) );                     vBuffer.erase( vBuffer.begin(), vBuffer.begin()+sizeof(ep) );                     if( id != m_id ){                                       // 自分自身は除く                         m_mapMember[id] = ep;                     }                 }

            }

            udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) );    // 次の読み込み待ちを開始         }

public:     CMain(const FuncMessage &funcMessage)         :m_id(CRUid::RandomGenerator())         ,m_funcMessage(funcMessage)         ,m_udpSocket(m_ioService)         ,m_apTimer(new deadline_timer(m_ioService))         {             Create(HWND_MESSAGE,CRect(0,0,0,0),_T(""),NULL);             ::SetTimer( m_hWnd, 0, 1, NULL );              m_apTimer->expires_from_now( boost::posix_time::millisec(0) );          // nTime秒後に             m_apTimer->async_wait( boost::bind( &CMain::OnDeadlineTimer, this, _1 ) );         }

    ~CMain()         {             m_apTimer.reset();             m_udpSocket.Close();             if( m_hWnd ) DestroyWindow();         }

    void OnDeadlineTimer(const boost::system::error_code& error)         {             if( error || error==boost::asio::error::operation_aborted ) return; // チェック

            if( m_apTimer.get() ){                      // 終了していないなら次のタイマーを設定                 m_apTimer->expires_at( m_apTimer->expires_at() + boost::posix_time::millisec(5000) );                 m_apTimer->async_wait( boost::bind( &CMain::OnDeadlineTimer, this, _1 ) );             }

            // メンツリスト送信             boost::shared_ptr<vector<char>> spData(new vector<char>);             std::copy( reinterpret_cast<const char*>(&m_id), reinterpret_cast<const char*>(&m_id)+sizeof(m_id),                        std::back_inserter(*spData) );             for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapMember.begin(); i!=m_mapMember.end(); i++ ){                 const CRUid &id = i->first;                 std::copy( reinterpret_cast<const char*>(&id), reinterpret_cast<const char*>(&id)+sizeof(id),                            std::back_inserter(*spData) );                 const CEndpoint &ep = i->second;                 std::copy( reinterpret_cast<const char*>(&ep), reinterpret_cast<const char*>(&ep)+sizeof(ep),                            std::back_inserter(*spData) );             }

            CString sSendTo;             for(std::map<CRUid,CEndpoint>::const_iterator i=m_mapMember.begin(); i!=m_mapMember.end(); i++ ){                 const CEndpoint &ep = i->second;                 ip::address addr(boost::asio::ip::address_v4(ep.m_nIpv4));                 ip::udp::endpoint endpoint( addr, ep.m_nPort );                 m_udpSocket.SendTo( endpoint, spData );                 sSendTo.Format(_T("%s [%s]"),CString(sSendTo),CUdpSocket::GetTextAddress(endpoint));             }             if( !sSendTo.IsEmpty() ){                 m_funcMessage( CRString::Format(_T("%s : SendTo %s"), CString(m_id.GetText().c_str()), sSendTo ));             }         }

    void Server(unsigned short nPort)         {             boost::system::error_code ec;             if( !m_udpSocket.Bind(nPort,ec) ){                 m_funcMessage( CRString::Format(_T("%s : Bind Error [%s]"), CString(m_id.GetText().c_str()),                                                 CString(ec.message().c_str()) ) );                 return;             }             m_udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) ); // 読み込み待ちを開始         }

    void Client(const std::string &sDomain,const std::string &sPort)         {             boost::shared_ptr<vector<char>> spData(new vector<char>);             std::copy( reinterpret_cast<const char*>(&m_id),                        reinterpret_cast<const char*>(&m_id)+sizeof(m_id), std::back_inserter(*spData) );              ip::udp::resolver resolver(m_ioService);             ip::udp::resolver::query query(ip::udp::v4(), sDomain, sPort );             boost::system::error_code ec;             ip::udp::resolver::iterator iterator = resolver.resolve(query,ec);             if( ec ){                 m_funcMessage( CRString::Format(_T("%s : resolve Error [%s]"),                                               CString(m_id.GetText().c_str()), CString(ec.message().c_str()) ) );                 return;             }             m_udpSocket.SendTo( *iterator,spData );             m_udpSocket.Receive( boost::bind(&CMain::OnReceived,this,_1,_2,_3,_4) );   // 読み込み待ちを開始         }

};

CUdpHolepunching::CUdpHolepunching(const FuncMessage &funcMessage) :m_main(*new CMain(funcMessage)) { }

CUdpHolepunching::~CUdpHolepunching() {     delete &m_main; }

void CUdpHolepunching::Server(unsigned short nPort) {     m_main.m_ioService.post( boost::bind( &CMain::Server, &m_main, nPort ) ); }

void CUdpHolepunching::Client(const std::string &sDomain,const std::string &sPort) {     m_main.m_ioService.post( boost::bind( &CMain::Client, &m_main, sDomain, sPort ) ); }

UDP ホールパンチングの仕組み上、穴をあけずとも UDP が通る環境が一つ必要です。
STUN サーバーと言われるもの。
サンプルアプリでは「サーバー起動」ってすると、UDP ポートを指定して待ち受け状態になります。
(とはいえ、標準化されてるプロトコルを実装してるわけではなく、オレオレ実装です。)

各端末(クライアントPC)は、このサーバーに接続することで、自身の NAT に穴を開けることと同時に、サーバーから各端末のエンドポイント情報を貰います。
あとは、各端末同士がそのエンドポイントに対して送信し合うことで P2P 通信を行う。という寸法です。

サンプルアプリでは、サーバーもクライアントも、すべての相手に対し、キープアライブも兼ねて、5秒毎に各端末のエンドポイント情報を送信し続けます。

というわけで、NAT 越えの P2P 通信自体は出来ることがわかったんですが、実用的にするのはいろいろハードルがあって、
Symmetric NAT 内の端末に対しては、サーバーなりが橋渡しをして通信しないといけない。とか、
UDP すら通さないような環境向けに TCP で橋渡しするようなサーバーを用意する。とか、
UDP を使うので、TCP 並の信頼性とか順序の安定性が必要なら自前実装しなきゃ。とか、
同じ NAT 内の端末同士は、ローカルIPで通信させるなり対策しなきゃ。(たぶん一般的なルーターでは、自分自身が開けたポートに自分自身から接続は出来なさそうなので)。とか。

この程度は世の P2P アプリ(skypeとか)は当然のように実現してるんだと思います。御見それです。

これらも楽しそうなので作ってみたいですが、仕事かなにかで必要になってくれないと手を出しずらいです。大変そうなので。

上記のソース一式はこちらからお願いします。例によって windows 用です。MFC 使ってます。
なにか間違いとかご意見とかればぜひお願いします。

ありがとうございました。


2014年11月3日(月曜日)

移転のご案内

Filed under: - ohashi @ 18時30分12秒

この度、有限会社シンクリッジは千葉市中央区よりさいたま市大宮区へ移転致しました。
今後ともご支援ご鞭撻の程、よろしくお願い申し上げます。

新住所
〒330-0854
さいたま市大宮区桜木町4−929−2桜の木ヨシノ102

電話番号
048-729-7045


2014年9月8日(月曜日)

Magome に MML の機能を入れてみました

Filed under: - takatsuka @ 11時53分43秒

  • クラウドMIDIシーケンサ Magome に MML 打ち込みの機能を入れてみました。
    昨今のシーケンサというかDAWでは、ステップ入力自体があんまりなさそうに思うんですが、グラフィカルで直観的なUIと、テキストでカタカタと打っていく方式を組み合わせると、けっこう便利なんじゃなかろうかと思ってたところ、確かに、テキストで打ち込んだ音符がその場でピアノロールで表示されるっていうのは、気分いいです。
  • ステップ入力といえばレコンポーザが有名だったと思うんですが、実はちゃんと使ったことはないです。自分の場合、打ち込みは BASIC 言語から入って、その流れで MML 打ち込みが普通になって、それから MAC で Studio Vision を使い始めたので、王道のステップ入力というのをよく知りません。
  • ということもあり MML です。MML が ステップ入力 というカテゴリに入るのかわかりませんが、キーボード(鍵盤ではなくPCのキーボード)で打ち込むというのは慣れれば結構ラクだと思うんです。それに、書式の工夫次第で可能性が広がりそうだし。
  • 関数をこさえて呼べばフレーズが勝手に鳴るとか、スケール名を入れると音符が勝手にそのスケールにアジャストされる。とか、サビのキーが高すぎときには、その手前で3度くらいスムーズに下げるような和声学に基づいたモーションを生成してくれる。とか。
  • と、アイデアだけは広がるんですが、実現させて使ってみないとどういうものかわからない。頭の中だけで完成形をイメージできない。ってのが、自分の想像力不足かなと思います。
  • 形にするのが一番大変なとこでもあり、楽しいとこでもあるんで、徐々に進めてこうと思います。ご助言ご意見などあればぜひお願いいたします。


2014年7月24日(木曜日)

magome のサイトを xoops cube で刷新

Filed under: - takatsuka @ 00時21分51秒

magome のサイトを xoops cube を使ったものに刷新しました。

ユーザー登録&管理の仕組みを自前で組むのは手間かかりそうだし、便利なプラグイン(モジュール)がそのまま使えるのはラクだなという理由です。 ラクな分カユいとこに手が届きづらくなるデメリットはきっとあると思うんですが、それを帳消しに出来るほどのメリットがあると思いました。開発者の方々には感謝です。

で、今後もモジュール開発をするであろう主に未来の自分用にメモを残しておこうと思いました。間違いとかあればぜひご指摘頂ければ幸いです。

ノウハウメモ

DB

  • テーブル作成は、"モジュール/sql/mysql.sql” を用意して SQL を書く。それを、xoops_version.php の$modversion[’sqlfile’][’mysql’] = “sql/mysql.sql”; って指定。
    • なので、それを反映させるには、モジュールの再インストールが必要。
    • なお、$modversion[’tables’][index] = “テーブル名” への反映もしないと、モジュールアンインストール時にテーブルを削除してくれない。
    • ここで書くテーブル名は、そのまま採用されるわけではなく、プリフィックスが付加される。なのでDB操作時のSQL文でも、プリフィックスの考慮を忘るべからず。
  • DB操作は、$xoopsDB っていう変数のメンバを使うべし。
  • $xoopsDB->queryF( $query ) は、UPDATE系でコールすべし。それ以外は $xoopsDB->query( $query ) を使うべきっぽい。

preload

  • XOOPS_ROOT 直下の preloadフォルダ と、モジュールの下の preloadフォルダ では動作が違う。全ページに影響させたい場合は XOOPS_ROOT 直下の preloadフォルダ に置くべきっぽい。(が、このあたりは全然把握不足)

REST

  • XMLとかJSONを返すようなwebサービスのページを作りたい場合、そのページの最後の「include XOOPS_ROOT_PATH."/footer.php”;」をコールせずに、自前でechoすれば良いっぽい。(が、お作法的に正しいのかわからない)

表示

条件によって左右のサイドバーとかを非表示にしたい。

  • 各領域の表示をON,OFF出来るような条件文を書いたテーマを自前で用意して対応する。(が、もっとスマートな方法があるのかもしれない。)

テンプレート

  • smartyの書式は「<{○○}>」で括るべし。

トラブったメモ

  • 日本語が表示されない。日本語のlanguageファイルが無視される。
    • UTF8 が標準文字コードっぽいので、languageファイル は 「japanese」ではなく「ja_utf8」になる。(本来は「english」も含めた3種を用意すべき?)
  • テンプレートファイルを編集してもページが変わらない。
    • テンプレートを書き換えたらモジュールのアップデートが必要。もっと手軽な方法があるなら知りたい。
  • モジュールのアンインストールしたとき、作ったテーブルを削除してくれない。
    • xoops_version.php の $modversion[’tables’][index] = “テーブル名” の記述が漏れてるか確認。


2014年7月6日(日曜日)

XOOPS Cube の進化っぷりにビックリ

Filed under: - takatsuka @ 09時30分36秒

新規で会員制サイトをこさえようと思っておったところ、昨今はフリーの CMS (とかその手のもの)が沢山あるので迷っておりました。

WordPress や OpenPNE とかは活発そうだし、Movable Type は有料版もあるだけに品質は良さそうだし。
どれであっても便利っぽいし、カスタマイズすれば本来の用途以外にも使えそうで、何を選択しても間違いではなさそう。

がしかし今回は、自社サイトでも使わせてもらってる XOOPS でやってみようかなと思いました。
諸々の事柄に対して一番ツブシが利きそう。(って抽象的な想像な上に、根拠もそういう印象持っただけって理由なんですが。)

なにはともあれ、自社サイトはもう10年近く前にこさえたものってこともあり、今の XOOPS Cube の進化っぷりに物凄く驚いた次第。

インストールが簡単なのはそのまま、プラグインとかテーマは一覧から選んでインストールできるようになってる、アップデートも通知してくれちゃって。昨今の CMS に全然見劣りしてない。
日本語との相性やプラグイン開発用の情報は問題なさそうだし。

色々な CMS を比較できるほど触ったわけではなく詳しくもないんですが、会員制サイトを、吊るしではない前提でこさえるなら、XOOPS CUBE 良さそうだなって思いました。開発者の方に感謝です。