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 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468
|
* when releasing do:
* make check
* cd po;intltool-update -M
* make dist
* wget http://www.gnu.org/licenses/lgpl-2.1.txt; mv lgpl-2.1.txt COPYING;
* test infrastructure
* add a stub song-io saver
* add a stub processor machine
* have a means to make a copy of log-files for passing tests
* this could help to check 'what changed'
* we'd need to also do xml output from check to run the log compare for
failed tests
* use https://github.com/martinpitt/umockdev for btic tests
* review tests
* ensure we follow Arrange/Act/assert or Given/When/Then style
http://en.wikipedia.org/wiki/Behavior_Driven_Development
* check that all public api is used in tests
nm --defined-only .libs/libbuzztrax-core.a | grep " T " | cut -d' ' -f3 | grep -v "marshal" | sort >/tmp/m1.txt;
nm --undefined-only .libs/libbtcore-check.a | grep " U bt" | cut -c20- | sort | uniq >/tmp/m2.txt;
comm -2 -3 /tmp/m{1,2}.txt
grep ");" src/lib/core/core.h | cut -d'(' -f1 | cut -d' ' -f2 | cut -d'*' -f2
find src/lib/core -name "*.h" -exec grep ");" {} \; | cut -d'(' -f1 | cut -d' ' -f2 | cut -d'*' -f2
* check that we use loop-tests
find tests/lib/ -name "*.c" -exec egrep -Hn "(for|while)\s*\(" {} \; | egrep -v "(:\s*\*|://)"
* quality checks
for file in src/lib/core/*.c; do gtkdoc-depscan -v --book gstreamer-1.0:1.2.0 $file; done
* sparse
make CC=cgcc
* build with clang
export CC=/usr/bin/clang; export CXX=/usr/bin/clang++; ./autoregen.sh
* generate a dependency matrix
* 'make ctags' creates tags files per directory containing symbol locations
* 'ctags -x --recurse src tests >xref' creates a single xref table containing
all symbol locations
* 'find . -name "*.o" -exec nm --undefined-only {} \; | grep " bt_"'
creates a list of externally called entries
= feature =
* controller api
- we might want to allow to attach a controller to a parameter and control
the controllers parameters in the pattern instead, e.g.:
- user attaches LFO to Filter-CutOff, in the pattern he can now edit:
center-value, depth, shape, freq (controlable values of the control-source)
- user attaches an Arpeggiator to a Note property
- we setup extra controller-functions to scale and shift the value (or
introduce a special binding for that)
- we need a controlbinding-router so that on pattern basis we can select which
controlsource is used (pattern, lfo, arpeggiator)
- song format
- we store it per pattern before the data
- ui
- we need some extra buttons in the parameter header to select between
{manual,automation}
* OSC support
* settings
- the OSC port for listening
- add song to path (/<song>/...)
* namespace
- ../<machine>/set_{wire,global,voice}_param
- ../<machine>/set_{mute,solo,bypass}
- ../<machine>/<pattern>/set_{wire,global,voice}_event
- ../{play,stop,pause}
* ui (if osc enabled)
- show the osc path both in machine and pattern view
- eventually make it editable (ensure unique)
- store with song
* we can also expose osc as a 'device' in the btic
- it will support the learn-iface
- learning will echo all received controls
- one can then name it and store
- it would be nice to somehow bundle learned controls as a 'device'
- then we can have presets per app
- we could have a "new osc device" menu item in the control menu
and put the learn item under the new device
* nice client for android: control
http://charlie-roberts.com/Control/?page_id=19
* we can push custom surfaces via osc
http://charlie-roberts.com/Control/?page_id=32
* can be discovered via avahi:
> avahi-browse -a
+ wlan0 IPv4 Control_75846 _osc._udp local
+ wlan0 IPv4 Control_66257 _osc._udp local
* undo/redo/journaling
- links
http://www.buzztrax.org/index.php/Undo,_redo
http://www.buzztrax.org/index.php/Song_autosaving
- we do that on the ui level (also the journalling and the replay of it)
- singleton journal object
- each undo/redo capabale object registeres with its name there
- on replay we can take the prefix og the entry and dispatch
- on journalling we can probably add the prefix
- it is a stack of journal entries
- classes that implement unod/redo implement the change_logger_interface()
and usually have a BtChangeLog reference in their instance struct
- pending parts that need undo/redo
- machine view
- machine/wire parameters
- wavetable view
- info view
- order of serialisation and destruction
- see main-page-machines.c:bt_main_page_machines_delete_machine()
- use g_signal_connect_after() for the '-removed' handler that are
destructive (search for '-removed",G_CALLBACK') can help
- g_object_add_weak_ref is not useful as the object is already gone
- example: bt_setup_remove_machine
- what we want:
machine data {
track data
pattern data 0
...
pattern data n
}
- what happens:
emit "removed" signal
pre:
main-page-machines:on_machine_removed: beg log undo/redo + machine-data
main-page-sequence:on_machine_removed: track data + remove track
default: bt_setup_on_machine_removed ?
foreach pattern do: bt_machine_remove_pattern
emit "removed" signal
pre:
main-page-patterns:on_pattern_removed: beg log undo/redo + pattern data
main-page-sequence:on_pattern_removed: no track -> skip
default: bt_machine_on_pattern_removed ?
-
after:
end log undo/redo
remove & unref
after:
end log undo/redo
remove & unref
- generic phases:
1) owner:before_xxx_removed: beg log undo/redo + xxx data
2) others:on_xxx_removed: log other object
3) children: emit 'removed'
4) owner: after_xxx_removed: end log undo/redo
5) remove & unref
- one problem is that objects that gets removed have children and are part of
other objects:
- machine has patterns and
- sequence has tracks that reference machines
- it is probably easier to have explicit api in the participating UI object
- we should split functions like:
void bt_main_page_machines_delete_machine (const BtMainPageMachines * self, BtMachine * machine)
void bt_main_page_machines_delete_wire (const BtMainPageMachines * self, BtWire * wire)
to serialize and to actually delete afterwards, this way we can trigger
the deletion of the top-level after serialisation is done
- examples:
bt_main_page_machines_delete_machine(self, machine) {
bt_main_page_machines_serialize_machine(self, machine) {
// 1) serialize own data (properties)
// 2) serialize data from object that have a ref to this machine by
g_signal_emit(BtMainPageMachines::machine-delete-event, machine);
// bt_main_page_sequence_on_machine_delete_event() -> bt_main_page_sequence_delete_track()
// 3) serialize all children
foreach(pattern) { bt_main_page_patterns_serialize(self,pattern); }
}
bt_setup_remove_machine(setup, machine);
}
bt_main_page_machines_delete_wire(self, wire) {
bt_main_page_machines_serialize_wire(self, wire) {
// 1) serialize own data (properties)
// 2) serialize data from object that have a ref to this wire by
g_signal_emit(BtMainPageMachines::wire-delete-event, wire);
// 3) serialize all children
foreach(pattern) { bt_main_page_patterns_serialize_value_group(self,pattern,wire_value_group); }
}
bt_setup_remove_wire(setup, wire);
}
bt_main_page_patterns_delete_pattern() {
bt_main_page_patterns_serialize_pattern(self, pattern) {
// 1) serialize own data (properties, value-groups)
foreach(value-group) bt_main_page_patterns_serialize_value_group(self, pattern, value-group)
// 2) serialize data from object that have a ref to this pattern by
g_signal_emit(BtMainPagePatterns::pattern-delete-event, pattern);
// bt_main_page_sequence_on_pattern_delete_event() -> serialize tracks data for this pattern
// 3) serialize all children
// - no children -
}
bt_machine_remove_pattern(machine, pattern);
}
bt_main_page_sequence_delete_track(self, sequence, track_id) {
// no refs, no children
}
bt_main_page_waves_delete_wave() {
bt_main_page_waves_serialize_wave(self, wave) {
// 1) serialize own data (properties)
// 2) serialize data from object that have a ref to this wave by
// TODO: we probably don't want this, as we won't clear wave-refs in
// pattern when e.g. loading a new wave into an existing slot
g_signal_emit(BtMainPagePatterns::wave-delete-event, wave);
// 3) serialize all children
// - no children -
}
bt_wavetable_remove_wave(wavetable, wave);
}
* Hide libxml2 use behind a PersistenceIO interface in a XMLPersistenceIO
implementation. The API would also allow to change the implementation to use
e.g. json or a binary representation instead. We need an API like:
PersistenceIONode: opaque handle
// for saving
PersistenceIONode *bt_persistence_io_new_node(PersistenceIONode *parent, gchar *name);
gboolean bt_persistence_io_set_value(PersistenceIONode *parent, gchar *value);
gboolean bt_persistence_io_set_attribute(PersistenceIONode *parent, gchar *name, gchar *value);
// a varargs function that takes (gchar *name, GType type, <type> value)
// triplets, terminated by NULL
gboolean bt_persistence_io_set_attributes(PersistenceIONode *parent,
gchar *name, ...);
// collection api ...
gboolean bt_persistence_io_set_attributes_from_hash_table(PersistenceIONode *parent, GHashTable *hash_table);
// for loading
GList *bt_persistence_io_get_nodes(PersistenceIONode *parent);
gchar *bt_persistence_io_get_value(PersistenceIONode *parent);
gchar *bt_persistence_io_get_attribute(PersistenceIONode *parent, gchar *name);
// a varargs function that takes (gchar *name, GType type, <type *> value)
// triplets, terminated by NULL
gboolean bt_persistence_io_get_attributes(PersistenceIONode *parent,
gchar *name, ...);
* CPU load per machine
- as we always plug the tee-elements on each machine and each wire starts with
a queue we could do CPU load per thread
- sources start their own thread
- effects and the sink live in the thread of one of the incoming wires
- build a thread_id<->machine mapping
- a hackish way would be to run a pad_probe before the machine and
send a message to the bus with the thread-id<->machine association
- a better way would be to use gst_message_parse_stream_status
see src/lib/core/song.c::on_song_stream_status()
- ideally we show it on the machine view
- extra dialog from tools menu?
- enable/disable from the view menu
- buzztrax-cmd could have a profile option (for play/encode) and print a summary
at the end
* eventually merge machine-rename dialog with machine properties
- we now have inplace rename in the sequence headers as a quick alternative
* control machines (no in/out)
- color=gray
it is never connected and thus, does not need a desaturated shade
- scheduling
- from a property notifies on sink-bin
- need ts
- run a GstTask and sync to the clock
- need to implement tempo iface
- need to hande seeks (to know direction :/)
- we can use that for midi-out
- global params: midi-device (enum)
- voice params: midi-channel, note, velocity, controller1, control-value1, ...
- subclass the control-machines from GstElement
- no pads (and therefore no _loop() or _chain() function
- we see them in the registry
* change presets via controller
* for midi we can expose program change message
* although our presents don't have stable ordering
* a rel-range control? (prev/next)
* we need a way in the ui to attach such a controller to the preset pane
* play machines via midi (interaction controller)
- we have machines with one or more note-params
- sometimes only one is a trigger though
cd bml/tests/testmachine
grep -H PT_NOTE *.txt.* | sort | uniq -c | sort -g
- we have machines with one or more value trigger parames (drum machines)
- we need to have the idle_loop playing when having keyboard assignments
- gui workflow:
- v1: start from the key-controllers
- a toolbar on the bottom
- show/hide in the view menu
- left a combobox to select the device (keyboard, midi, ...)
- next the piano key graphics
- next two comboboxes for key assignment and velocity assignment
- velocity assignment is empty, when selecting machine+key,
it will show possible velocity targets
- for drum-machines, we select the parameter with the key and put the
velecity into the respective parameter
- mark a key-range, assign to machine/note-param (with key offset)
- select a key-range, unassign
- move key-ranges
- v2: start from pattern column headings
- have bind/unbind like in machine window
- define split'ed devices elsewhere
- v3: add controller to context menu on machine-canvas
- how to easily assign a series of key to a series of triggers in one machine
- have a trigger group (and assign to white keys only)
- this would manage both key-number and velocity as we'd only bind a series
of abs-value-controls (velocity) to the drum-machine triggers
- how to assign velocity to the machines velocity param
- it works for monophonic machines
- poly key-press events
- the current code in machine.c (_poly_control_) would increment the
voice_ct on each control event
- we would need to set both key and velocity though
- we also need to ensure to set velocity first and then the key
- each controller has a separate voice_ct
- when assinging a controller to a voice-param, should we always poly-bind
it?
- it would be nice to have a pc-keys interaction controller for playing notes
- we can register this from the ui (it would be part of "ui/edit"
- the machine view would start-stop it on focus (if bound)
- eventually we could use it in pattern-view too and implicitely bind it
if "play-notes" is active
* text parameters in machines
* for a:
* text to speech machine we'd like to have strings in the pattern
* cmd-out machine we'd like to send a dbus command, run a script etc.
* like we have a wavetable, we could have a text table
* a list of text snipped (more than 256!)
* each snippet has a label and a text block (multiline)
* in a text to speech machine we can use text-table index (gint 16 bit),
text offset (gint 16 bit char offset), length (unset, to the end) and
direction (forward, backward)
* in a command out machine, we use the text table index (gint 16 bit) and
ev. script parameters (passed as $1, $2, ...)
* wave table
* we can just keep the buffers from fakesink/appsink in a list and consolidate
on eos
* record audio
* need device selection on the source
* record from song
* need some special setup on sink-bin/encodebin
* load raw wave on song::playing==FALSE
* one framerate for animations
* we have a couple of things that we update in intervals:
* 10fps: main-page-waves.c: preview_update_id=g_timeout_add_full(G_PRIORITY_HIGH,1000/10,on_preview_playback_update,(gpointer)self,NULL);
* 10fps: main-toolbar.c: playback_update_id=g_timeout_add_full(G_PRIORITY_HIGH,1000/10,on_song_playback_update,(gpointer)song,NULL);
* 10fps: machine.c: g_object_set(PART_*LEVEL,"interval",(GstClockTime)(0.1*GST_SECOND),...
* 10fps: signal-analysis-dialog: g_object_set(ANALYZER_*, "interval",(GstClockTime)(0.1*GST_SECOND),...
* we should have one #define somewhere for the UPDATE_INTERVAL
* ideally we ensure that the framerate is an integer multiple of it
* it would be nice to avoid a setting, but for slow machines it might be worth it
(or for fast machine, people could pick a higher rate)
* we could use configure and have the define in config.h
* pattern and main-page-patterns still contain bits of duplicated code
* review the pattern api and check if we should use the groups directly
(run ./api-cleanup.sh to get a report of where the api is used)
* create & add is a bad idea
* machines and wires are automatically added to the setup
* patterns are automatically added to the machine
* this causes trouble for pattern_copy (which we work-around now)
* change tempo while playing
* we would like to ensure that playback continues at the same position, just
at different speed
* when changing the tempo, we'd need to take a snapshot of the playback pos as
time and ticks
* embedded lua (see design/lua) or js (see design/{gjs,seed})
* tools menu
* pattern editor
* register scripts that provide commands to manipulate the patterns
* the app does undo/redo, but comparing the before after state
* script will needs to provide a name
* if we have a keybinding editor, we can associate the function with the key
there
* we could also have an eval entry in the editor, that would fill a selection
with result of cell(tick) = map(f(tick)). tick could either be the absolute
tick or 0.0 ... 1,0 and map will project the function return from 0.0 ... 1.0
to the value vange of the selected property
* interaction controller ?
* do we need to store some of them with the song?
* machine UIs
* instead of adding more metadata to the paramspecs we could also use
GtkBuilder xml file. This way machines can ship a customized layout.
* if not present in the filesystem we generate them on
the fly, maybe also expose this as a standalone tool
* the xml file only covers the parameter boxes
* single window UI
* like we have main-window.c, add a extra-window.c, where the extra window
only has a main-pages widget
* on each tab we have a context menu to move it to the 'next'/'prev' window
* each page is only part of one window
* the settings store how many extra windows we have and for each page in which
one it is docked
* when we close an extra window, we move the pages to the previous window
* the edit-application stores the list of the windows that are open
* see design/gui/multiscreen.c, when saving the monitor positions
gdk_screen_get_monitor_at_point()
* physical remotes
* physical remotes to control synths
* each synth could generate the required arduino sketch + instructions for the
hw
* arduino/esp2866
* https://www.adafruit.com/products/1085: 4ch @ 16bit: 15$
* https://www.adafruit.com/products/1083: 4ch @ 12bit: 10$
* https://www.adafruit.com/products/856: 8ch @ 10bit: 3.75$
* Arduino Uno: 6ch @ 10bit (mine)
* Arduino Micro: 12ch @ 10bit
* Teensy 3.1: 21ch @ 10bit (12ch on the headers)
* controls
* trigger button
https://www.arduino.cc/en/tutorial/button
* toggle button
https://www.arduino.cc/en/Tutorial/Switch
* poti
https://www.arduino.cc/en/Tutorial/AnalogInput
* many buttons on 1 ADC pin
http://www.instructables.com/id/How-to-access-5-buttons-through-1-Arduino-input/?ALLSTEPS
useful also for rotary switches like e.g.
https://www.sparkfun.com/products/10064?_ga=1.108337922.1878275335.1438803671
http://www.conrad.de/ce/de/Search.html?search=Encoder+5+V%2FDC+0.01+A+Schaltpositionen+16&searchType=mainSearchBar
e.g. for mode-selectors
* send osc events
http://trippylighting.com/2014/10/12/touchosc-arduino-tutorial/
* internet exchange for ic-profiles
* store ic-profiles also on files.buzztrax.org/ic-profiles
* when plugging a ic-device, check if we find a profile online
* from the profile-settings, add a button to submit a profile
* ideally upload through a the mailing-list/wordpress/...
* if we upload to files.buzztrax.org, upload to a staging area, where the uploads
are limited by ip/size/... and files are checked immediately after upload
for the file-type
* if we upload to wordpress, we'd need to do OAuth2 to avoid storing the
password in the settings
* maybe do something similar for presets
|