Environments: ============= 1. What is an Environment? 2. How do the interfaces look? 3. How do I create/use one? 4. How do I implement items? /* Documentation TODO: add more details: - Guis and aRts (probably seperate document), GuiFactory, GenericGuiFactory,... - the dataDirectory (sections 2,3,4) - how items react on changes of active (section 4) */ 1. What is an Environment? -------------------------- Programs like sequencers often require complex structures with lots of parameters to configure running inside aRts, to create music. For instance for composing a song, you might require - several effects - several synthetic instruments - several audio tracks - a mixer While with artscontrol, the user can setup much of this himself manually, the problem is that this has to be done over and over again. That is, if he saves the song, the settings of his effects, instruments and the mixer will not be saved with it. The main idea of the new interfaces in Arts::Environment is that the sequencer can save the environment required to create a song along with the the song, so that the user will find himself surrounded by the same effects, instruments,... with the same settings again, once he loads the song again. So, conceptually, we can imagine the environment as a "room", where the user works in to create a song. He needs to install the things inside the room he needs. Initially, the room will be empty. Now, the user things: oh, I am going to need this nice 24 channel mixer. *plop* - it appears in the room. Now he thinks I need some sampler which can play my piano. *plop* - it appears in the room. Now he starts working, and adds the "items" he needs. Finally, if he stops working on the song, he can pack all what is in the environment in a little box, and whenever he starts working on the song again, he can start where he left off. He can even take the environment to a friend, and continue working on the song there. Note that there might be other tasks (such as creating a film, playing an mp3 with noatun,...) which will have similar requirements of saving the current state, so the concept of environments is not limited to songs. 2. How do the interfaces look? ------------------------------ There are two main things in the Environment, that is Arts::Environment::Container this interface is where you put all your stuff you need to create a song Arts::Environment::Item this is an item that works inside an environment 2.1 The Container interface: ---------------------------- Initially "Container"s are empty when created. If you create items, you need to tell the container about it. void addItem(Item item); Item createItem(string name); You can create the Item yourself and use addItem afterwards, or you can tell the container to create an Item (by name), and return it to you. Then it will automatically be put into the environment. You can list the items that are currently inside an environment with the attribute readonly attribute sequence items; and remove them with void removeItem(Item item); Finally, the more interesting aspect is that you can save the whole environment to a list of strings, and restore it from there. sequence saveToList(); void loadFromList(sequence strlist); If you load it, all items will be created that were created in the environment you saved. There are two more special items. Here are two remaining problems to explain you the purpose of these (you can skip these explaination if you want get the basic idea first): * the "sample data problem" Consider you have a song where you use these spectacular "boom" sound you just sampled. Now you save it to a list of strings, and take it to a friend. There are two things that could happen up to now: 1. the "boom" sound doesn't get put into that list of strings, so when you play the song on your friends computer it might be missing 2. the "boom" sound gets saved as string list - this would probably be really really inefficient for both, loading and saving So we introduce a attribute string dataDirectory; where song specific data can be saved. The general idea is that items should access data from anywhere on your harddisc. However, if you execute a special operation (such as "pack the environment"), all data used in the environment should get copied into the dataDirectory, and the data should be used from there in the future. The details of this are not quite done yet. * the "outside world object" problem Suppose you use an environment and some items use objects that are not items of the environment. A typical example might be objects which refer to an Arts::StereoEffectStack outside the environment. Such objects could be saved, but they need to know how to restore themselves to "the same" StereoEffectStack again (which will not be restored by the environment). The general idea to solve this is the context. In the context the user can name non-environment objects and say: "well, here is a StereoEffectStack my items will refer to, and it is named 'OutputEffectStack'". Upon serialization, the environment would not care about the OutputEffectStack, and each item which needs to refer to it would only save that it needs to put the items in something called 'OutputEffectStack' again. The same way, up on restore, items could lookup the 'OutputEffectStack' again and insert StereoEffects in there. The details of this are not quite done yet. 2.2 The Item interface: ----------------------- Most functions in the Item interface are not too relevant for users. Upon insertion, the environment uses void setContainer(Container container); to tell the Item in which environment it lives. It also uses setContainer( Container::null()) once the Item gets removed. Which container the Item is in can be seen in the readonly attribute Container parent; Upon serialization, the container uses sequence saveToList(); void loadFromList(sequence strlist); to save or restore the data. Finally, you can see if the item is currently inside a Container with the readonly attribute boolean active; There is some trick here: the problem is that you will probably want to hold references to items (or parts of items) in some situations. But if you do so, and for instance display the item on the GUI, you will still display the item if it was removed from the environment. And you will (by reference counting) prevent it from disappearing. So... if you hold a reference, watch whether the item is still active, using connect(item, "active_changed", ...) and do release the references you hold if it is not. I.e. if you have a mixer window on the screen, and the mixer gets inactive, close it. (See also: change notification documentation). 3. How do I create/use one? --------------------------- First of all, creation. Usually, you will hold your environments on the sound server. So creating will look like: /* lookup sound server (use an existing one, if you have one) */ Arts::SoundServer server = Reference("global:Arts_SoundServer"); if(server.isNull()) /* error handling -> no sound server running */; /* create the object on the server */ Arts::Environment::Container container = Arts::DynamicCast(server.createObject("Arts::Environment::Container")); if(container.isNull()) /* error handling -> environment container could not be created */; Good, now we have an environment. What to do now? We could add a mixer. This would work like Arts::Environment::MixerItem mixer = Arts::DynamicCast(container.createItem("Arts::Environment::MixerItem")); if(mixer.isNull()) /* error handling -> no mixer */; Cool. A mixer. What do we do with that. Hm... setting the channel count would be nice, for instance. mixer.channelCount(8); /* an eight channel mixer */ And finally, we could display a GUI for it, using the KDE gui embedding thing. Arts::GenericGuiFactory guiFactory; Arts::Widget widget = guiFactory.createGui(mixer); if(!widget.isNull()) { KArtsWidget *kartswidget = new KArtsWidget(widget); kartswidget->show(); } else { /* error handling -> no gui available for this item */ } NOTE: for GenericGuiFactory to work, it needs to know what toolkit you are using. For KDE, you need to add the line ObjectManager::the()->provideCapability("kdegui"); somewhere in your application (only do this once). You can try using saveToList or loadFromList on the container, too. A classical piece of code which does this is, copied from artscontrol: void EnvironmentView::load() /* load from file DEFAULT_ENV_FILENAME */ { ifstream infile(DEFAULT_ENV_FILENAME); string line; vector strseq; while(getline(infile,line)) strseq.push_back(line); defaultEnvironment().loadFromList(strseq); /* we'd use "container" here */ } void EnvironmentView::save() { vector *strseq; strseq = defaultEnvironment().saveToList(); /* we'd use "container" here */ ofstream outfile(DEFAULT_ENV_FILENAME); for(vector::iterator i = strseq->begin(); i != strseq->end(); i++) outfile << *i << endl; delete strseq; } /* remark about loading: Of course, after loading the environment, you might want to look at container.items, to be able to display the guis again. To find out if an item is a mixer, you can do Arts::Environment::MixerItem m = Arts::DynamicCast(item); - or if you want to get the type in a generic way, you can use string typename = item._interfaceName(); */ Finally, if we're tired of using our mixer, we can remove it again, using container.removeItem(mixer); 4. How do I implement items? ---------------------------- Basically, you derive an interface from Arts::Environment::Item, like this: // this code is in the .idl file: interface MyItem : Arts::Environment::Item { /* methods/attributes here */ }; and your implementation from Arts::Environment::Item_impl, like this: // this code is in the .cc file: #include "artsmodules.h" #include "env_item_impl.h" class MyItem_impl : virtual public MyItem_skel, virtual public Arts::Environment::Item_impl { public: void loadFromList(const vector& list) { /* you need to implement this... */ } vector *saveToList() { /* ... and that */ } }; REGISTER_IMPLEMENTATION(MyItem_impl); /* register your implementation */ If you want your item to have a Gui, implement a GuiFactory that can create a gui for the item.