1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
|
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<Item> 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<string> saveToList();
void loadFromList(sequence<string> 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 tqparent;
Upon serialization, the container uses
sequence<string> saveToList();
void loadFromList(sequence<string> 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<string> strseq;
while(getline(infile,line))
strseq.push_back(line);
defaultEnvironment().loadFromList(strseq); /* we'd use "container" here */
}
void EnvironmentView::save()
{
vector<string> *strseq;
strseq = defaultEnvironment().saveToList(); /* we'd use "container" here */
ofstream outfile(DEFAULT_ENV_FILENAME);
for(vector<string>::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<string>& list)
{
/* you need to implement this... */
}
vector<string> *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.
|