ofxXmlSettingが使いにくかったので、picojson使ってみました。
こんな感じ。落ち着いたらクラスにまとめたい。ofxPicojsonに。
ofxXmlSettingが使いにくかったので、picojson使ってみました。
こんな感じ。落ち着いたらクラスにまとめたい。ofxPicojsonに。
僕のTwitter タイムライン界隈では、Kinectハックの話題でもちきりですが、現在僕は、MIDIを再勉強中。
MIDIって、それ自体は過去の遺産の様な規格なのだけど。パラメーターで表現できる範囲が0~127って、…8bitサウンドにも届かないわ…いつの時代だよっ!! と思うけれど。
MIDIの一番の強みは、「DTMソフトと連携できる」これに尽きる。
以前は、各種MIDIコントローラーの強みもあったけれど、今では、タッチパネル+OSCの方が柔軟だな。
しかし、DTMソフトの連携を考える際の最有力はやはりMIDI。
MIDIとか古すぎて忘れちゃった。といった方は、
詳細MIDI規格 http://www.pluto.dti.ne.jp/~daiki/Midi/Midi.html
が最強。
さて、今回考えていることは、
DTMソフトのタイムライン上でエディットしたMIDIデータを、MIDIファイルに書きだして、音楽をつくるように、oFのパラメーターを制御する。
例えば、Arduinoと連携して、照明を音楽の用にDTMソフトで編集できたら面白い。
(詳しくは知らないけど、照明の現場でつかってるのはMIDI規格みたいなもんらしい。)
兎にも角かくにも、ライブラリを選定。
MIDIの通信系には、openFrameworksで ofxMIDIがよさそう。
http://addons.openframeworks.cc/projects/show/ofxmidiin
で見れるはずなのだけれど、サイトが落ちてるのかな?
http://www.addons.openframeworks.cc/files/090812030923_ofxMidi_002.zip
ソースの直接ダウンロードはこちらから。
Mac + XCode環境の場合は、フレームワークにCoreMidiを追加するのを忘れずに。
その他の環境での諸々は、内部的に使っているRtMIDI*1のサイトに対策が載っている。
クラスは基本的には、ofxMidiIn と ofxMidiOut の2つで事足りる。oFらしいシンプル設計。今回、ProggramChangeを扱いたかったので、
ofxMidiOut.h に
void sendProgramChange(int channel, int id);
ofxMidiOut.cpp に
// -------------------------------------------------------------------------------------- void ofxMidiOut::sendProgramChange(int channel, int id) { message.clear(); message.push_back( MIDI_PROGRAM_CHANGE+(channel-1) ); message.push_back( id ); sendMessage(); }
を追加。
Macの場合、Audio MIDI設定というアプリを開いて、
IAC DriverでのMIDIポートをオンにすることで、内部的なmidi通信が出来るようになる。
さらにネットワークの部分を設定すれば、Mac同士で、MIDIケーブルをひかなくても、LANネットワークでMIDI通信が出来てしまう。
ここまで来れば、岩井俊雄+坂本龍一のメディアアートの教科書に載せるべき作品、
機械仕掛けのピアノのための嬉遊曲
のような作品もoFで可能。
今回は、音楽をつくるように時間軸上で音楽以外のパラメーターを編集したかったので、midiファイルの読み込み、再生の機構が必要。
openFrameworks用のライブラリはなさそうだけれど、c++のライブラリがあった。
構造とか、あまりoF向けではないけれど、オープンソースのライブラリにはいつも感謝。
jdksmidi ( https://github.com/jdkoftinoff/jdksmidi )
未完成かつ、c++も勉強中の身だけれど、oFラッパーを作成してみたので、折角なので公開。
ofxMidiSequencer.openMidiFileで、midiファイルを読み込む、
ofxMidiSequencer.start() でシーケンサー再生開始。
今のところ、1ms精度で作っているので、音楽再生には、若干難有り。
その他、いろいろするには、manageMIDIEventsや、sendMidiMessageをオーバーライドしてね。といった不親切設計。追々直していきます。
ofxMidiSequencer.h
#ifndef OFX_MIDI_SEQUENCER #define OFX_MIDI_SEQUENCER #include "ofMain.h" #include "ofxThread.h" #include "ofxMidi.h" #include "jdksmidi/msg.h" #include "jdksmidi/parser.h" #include "jdksmidi/fileread.h" #include "jdksmidi/fileshow.h" #include "jdksmidi/sequencer.h" #include "jdksmidi/track.h" #include "jdksmidi/multitrack.h" #include "jdksmidi/filereadmultitrack.h" using namespace jdksmidi; class ofxMidiSequencer : protected ofxThread { public: ofxMidiSequencer(); virtual ~ofxMidiSequencer(); // load MIDI file bool openMidiFile(string _filepath); void start(); void stop(); void restart(); void sendMidiMessage(MIDIMessage& msg); void setTempo(float tempo); float getTempo(); protected: ofxMidiOut _midiOut; MIDIClockTime _startedTime; MIDIClockTime _currentTime; MIDIClockTime _nextEventTime; MIDISequencer * _sequencer; MIDIMultiTrack * _tracks; virtual void threadedFunction(); inline void manageMIDIEvents(); private: }; #endif
ofxMidiSequencer.cpp
#include "ofxMidiSequencer.h" using namespace jdksmidi; ofxMidiSequencer::ofxMidiSequencer() { _currentTime = 0; _midiOut.openPort(); _tracks = 0; _sequencer = 0; } ofxMidiSequencer::~ofxMidiSequencer() { _midiOut.sendControlChange(1, 123, 0); _midiOut.closePort(); if(_sequencer) delete _sequencer; if(_tracks) delete _tracks; } bool ofxMidiSequencer::openMidiFile(string _filepath) { // clear data if(_sequencer) delete _sequencer; if (_tracks) delete _tracks; // absolute path string filepath = ofToDataPath(_filepath, true); _tracks = new MIDIMultiTrack(); MIDIFileReadStreamFile rs( filepath.c_str() ); MIDIFileReadMultiTrack track_loader( _tracks ); MIDIFileRead reader( &rs, &track_loader ); // set amount of tracks equal to midifile _tracks->ClearAndResize( reader.ReadNumTracks() ); // load the midifile into the multitrack object if ( !reader.Parse() ) { cout << "\nError parse file " << filepath << endl; return false; } // init sequenser _sequencer = new MIDISequencer( _tracks ); return true; } void ofxMidiSequencer::start() { _startedTime = ofGetSystemTime(); _currentTime = 0; _nextEventTime = 0; startThread(true, false); _sequencer->GoToTimeMs(0.0f); } void ofxMidiSequencer::stop() { _currentTime = 0; _nextEventTime = 0; stopThread(); } void ofxMidiSequencer::restart() { _startedTime = ofGetSystemTime(); _currentTime = 0; _nextEventTime = 0; _sequencer->GoToTimeMs(0.0f); } void ofxMidiSequencer::sendMidiMessage(MIDIMessage& msg) { if(msg.IsNoteOn()) { _midiOut.sendNoteOn(1, msg.GetNote(), msg.GetVelocity()); } else if(msg.IsNoteOff()) { _midiOut.sendNoteOff(1, msg.GetNote(), msg.GetVelocity()); } else if(msg.IsControlChange()) { _midiOut.sendControlChange(1, msg.GetController(), msg.GetControllerValue()); } else if( msg.IsProgramChange()) { _midiOut.sendProgramChange(1, msg.GetPGValue()); } } void ofxMidiSequencer::setTempo(float tempo) { float currentTempo = _sequencer->GetCurrentTempo(); float currentTempoScale = _sequencer->GetCurrentTempoScale(); float tempoScase = tempo*currentTempoScale/currentTempo; cout << "tempoScase" << tempoScase << "\n"; _sequencer->SetCurrentTempoScale(tempoScase); } float ofxMidiSequencer::getTempo() { return _sequencer->GetCurrentTempo(); } #pragma mark - #pragma mark protected methods void ofxMidiSequencer::threadedFunction() { while (isThreadRunning() != 0) { if( lock() ) { manageMIDIEvents(); unlock(); ofSleepMillis(1); } } } void ofxMidiSequencer::manageMIDIEvents() { _currentTime = ofGetSystemTime() - _startedTime; while (uint(_nextEventTime) <= uint(_currentTime)) { int track; MIDITimedBigMessage msg; if( _sequencer->GetNextEvent( &track, &msg ) ){ sendMidiMessage(msg); if( !_sequencer->GetNextEventTime( &_nextEventTime) ){ restart(); break; } } else { break; } } }
まだ、できたものが見せられないのですが、出来たら報告します。
*1:http://www.music.mcgill.ca/~gary/rtmidi/
友人が展示に使いたいとの事で、
3面に3つのPCから映像を同期再生。
QuartzCompozerでOSCとか使えば一瞬だ。と思ったが、OSX10.2のマックがあるらしい。
そこで最近勉強中のopenFrameworksで作ってみようと。でも10.2で動くのかな?まあいいか。
以下、ソース。
マックデフォルトのファイル選択ウィンドウから、ファイルを選べたらいいな、と思ったのだが出来なかったため。いろいろ勝手に決めている。
ムービーファイルは"/Users/Shared/movie.mov"におく。
同期する2つのパソコンのIPアドレスは"192.168.1.20"と"192.168.1.21"。
メインパソコンのスペースバーで再生、停止。他の2つも同期する。
ひどいな。全然OF分からん。でもProcessing(Java)より、quicktimeは安定している。そりゃそうか。もっと勉強します。
testApp.h
#ifndef _TEST_APP #define _TEST_APP #include "ofMain.h" #define OF_ADDON_USING_OFXOSC #include "ofAddons.h" #define HOST0 "localhost" #define HOST1 "192.168.1.20" #define HOST2 "192.168.1.21" #define PORT 3333 #define MOVIE_FILE "/Users/Shared/movie.mov" class testApp : public ofSimpleApp{ public: testApp(); void setup(); void update(); void draw(); void keyPressed (int key); void mouseMoved(int x, int y ); void mouseDragged(int x, int y, int button); void mousePressed(int x, int y, int button); void mouseReleased(); private: ofVideoPlayer movie; ofxOscSender oscS0; ofxOscSender oscS1; ofxOscSender oscS2; ofxOscReceiver oscR; bool bPlaying; bool bMainPC; void play(); void pause(); void allPlay(); void allPause(); }; #endif
testApp.cpp
#include "testApp.h" //-------------------------------------------------------------- testApp::testApp() { } //-------------------------------------------------------------- void testApp::setup(){ ofBackground(0, 0, 0); ofHideCursor(); ofSetFrameRate(60); bPlaying = 0; bMainPC = 0; movie.loadMovie(MOVIE_FILE); movie.setLoopState(0x01); //movie.getDuration(); oscS0.setup(HOST0, PORT); oscS1.setup(HOST1, PORT); oscS2.setup(HOST2, PORT); oscR.setup(PORT); } //-------------------------------------------------------------- void testApp::update(){ //ofBackground(100,100,100); movie.idleMovie(); movie.setLoopState(0x01); //printf("%f \n", ofGetFrameRate()); while( oscR.hasWaitingMessages() ) { ofxOscMessage msg; oscR.getNextMessage(&msg); if(strcmp(msg.getAddress(), "/qtmovie") == 0) { if(strcmp(msg.getArgAsString(0), "play") == 0) { play(); } else if (strcmp(msg.getArgAsString(0), "pause") == 0) { pause(); } } } if(bMainPC) { if(movie.getPosition() >= 1) { allPause(); //pause(); ofSleepMillis(1 * 1000); allPlay(); cout << "looppppDAYO" << "\n"; } } } //-------------------------------------------------------------- void testApp::draw(){ movie.draw(0,0,ofGetScreenWidth(),ofGetScreenHeight()); } //-------------------------------------------------------------- void testApp::keyPressed (int key){ switch (key){ case ' ': bPlaying = !bPlaying; bMainPC = true; if(bPlaying == 1) { allPlay(); } else { allPause(); } break; } } //-------------------------------------------------------------- void testApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void testApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mousePressed(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mouseReleased(){ } //------------------------------------------------------------- void testApp::play() { movie.play(); movie.setPaused(false); bPlaying = true; } //-------------------------------------------------------------- void testApp::pause() { movie.setPosition(0.0); movie.setPaused(true); bPlaying = false; } //-------------------------------------------------------------- void testApp::allPlay() { // send osc message ofxOscMessage msg; msg.setAddress("/qtmovie"); msg.addStringArg("play"); oscS1.sendMessage(msg); oscS2.sendMessage(msg); oscS0.sendMessage(msg); } //-------------------------------------------------------------- void testApp::allPause() { // send osc message ofxOscMessage msg; msg.setAddress("/qtmovie"); msg.addStringArg("pause"); oscS1.sendMessage(msg); oscS2.sendMessage(msg); oscS0.sendMessage(msg); }
main.mm
#include "ofMain.h" #include "testApp.h" //======================================================================== int main( ){ ofSetupOpenGL(720, 480, OF_FULLSCREEN); // this kicks off the running of my app // can be OF_WINDOW or OF_FULLSCREEN // pass in width and height too: //FileOpener * fileOpener = [[FileOpener alloc] init]; //[fileOpener open]; ofRunApp(new testApp()); }