Anklang-0.3.0.dev797+g4e3241f3 anklang-0.3.0.dev797+g4e3241f3
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
prjtests.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include <ase/project.hh>
3#include <ase/clip.hh>
4#include <ase/track.hh>
5#include <ase/testing.hh>
6
7namespace { // Anon
8using namespace Ase;
9
10static void
11project_creation()
12{
13 ProjectImplP project = ProjectImpl::create ("TestProject");
14 TASSERT (project);
15 project->_activate();
16 TASSERT (project->name() == "TestProject");
17
18 const double initial_bpm = project->bpm();
19 TASSERT (initial_bpm >= 10.0 && initial_bpm <= 999.0);
20 uint64_t bpm_notifications = 0;
21 auto bpm_connection = project->on_event ("notify:bpm", [&bpm_notifications] (const Event &event) { bpm_notifications++; });
22
23 uint64_t name_notifications = 0;
24 auto name_connection = project->on_event ("notify:name", [&name_notifications] (const Event &event) { name_notifications++; });
25
26 uint64_t volume_notifications = 0;
27 auto volume_connection = project->on_event ("notify:master_volume", [&volume_notifications] (const Event &event) { volume_notifications++; });
28
29 const double initial_vol = project->master_volume();
30 TASSERT (initial_vol >= -100.0 && initial_vol <= 20.0);
31
32 // Test BPM notify/undo/redo
33 project->bpm (130.0);
34 TASSERT (std::abs (project->bpm() - 130.0) < 0.001);
35 uint64_t last_bpm_notifications = bpm_notifications;
36 project->bpm (123.0);
37 TASSERT (std::abs (project->bpm() - 123.0) < 0.001);
38 TASSERT (bpm_notifications > last_bpm_notifications);
39
40 // Test undo
41 TASSERT (project->can_undo());
42 TASSERT (!project->can_redo());
43 last_bpm_notifications = bpm_notifications;
44 project->undo();
45 TASSERT (!project->can_undo());
46 TASSERT (project->can_redo());
47 TASSERT (std::abs (project->bpm() - initial_bpm) < 0.001);
48 TASSERT (bpm_notifications > last_bpm_notifications);
49 // Test redo
50 last_bpm_notifications = bpm_notifications;
51 project->redo();
52 TASSERT (project->can_undo());
53 TASSERT (!project->can_redo());
54 TASSERT (std::abs (project->bpm() - 123.0) < 0.001);
55 TASSERT (bpm_notifications > last_bpm_notifications);
56
57 project->undo();
58 TASSERT (!project->can_undo());
59 TASSERT (project->can_redo());
60 TASSERT (std::abs (project->bpm() - initial_bpm) < 0.001);
61
62 // Test name notify/undo/redo
63 const String initial_name = project->name();
64 TASSERT (initial_name == "TestProject");
65 uint64_t last_name_notifications = name_notifications;
66 project->name ("NewName");
67 TASSERT (project->name() == "NewName");
68 TASSERT (name_notifications > last_name_notifications);
69
70 TASSERT (project->can_undo());
71 last_name_notifications = name_notifications;
72 project->undo();
73 TASSERT (project->name() == initial_name);
74 TASSERT (project->can_redo());
75 TASSERT (name_notifications > last_name_notifications);
76
77 last_name_notifications = name_notifications;
78 project->redo();
79 TASSERT (project->name() == "NewName");
80 TASSERT (name_notifications > last_name_notifications);
81
82 // Test master_volume notify/undo/redo
83 uint64_t last_volume_notifications = volume_notifications;
84 project->master_volume (-6.0);
85 TASSERT (std::abs (project->master_volume() - (-6.0)) < 0.01);
86 TASSERT (volume_notifications > last_volume_notifications);
87
88 TASSERT (project->can_undo());
89 last_volume_notifications = volume_notifications;
90 project->undo();
91 TASSERT (std::abs (project->master_volume() - initial_vol) < 0.01);
92 TASSERT (project->can_redo());
93 TASSERT (volume_notifications > last_volume_notifications);
94
95 last_volume_notifications = volume_notifications;
96 project->redo();
97 TASSERT (std::abs (project->master_volume() - (-6.0)) < 0.01);
98 TASSERT (volume_notifications > last_volume_notifications);
99
100 project->_deactivate();
101 project->discard();
102
103 // Test create second project
104 ProjectImplP project2 = ProjectImpl::create ("TestProject2");
105 project2->name ("foo");
106 TASSERT (project2->name() == "foo");
107 project2->name ("bar");
108 TASSERT (project2->name() == "bar");
109 project2->discard();
110}
111TEST_ADD (project_creation);
112
113static void
114project_length()
115{
116 ProjectImplP project = ProjectImpl::create ("LengthTest");
117 TASSERT (project);
118 project->_activate();
119
120 double len = project->length();
121 TASSERT (len >= 0.0);
122
123 project->_deactivate();
124 project->discard();
125}
126TEST_ADD (project_length);
127
128static void
129project_track_management()
130{
131 ProjectImplP project = ProjectImpl::create ("TrackTest");
132 TASSERT (project);
133 project->_activate();
134
135 TrackS tracks = project->all_tracks();
136 const size_t initial_count = tracks.size();
137 TASSERT (initial_count >= 1);
138
139 TrackP new_track = project->create_track();
140 TASSERT (new_track);
141
142 tracks = project->all_tracks();
143 TASSERT (tracks.size() == initial_count + 1);
144
145 project->_deactivate();
146 project->discard();
147}
148TEST_ADD (project_track_management);
149
150static void
151project_playback_state()
152{
153 ProjectImplP project = ProjectImpl::create ("PlaybackTest");
154 TASSERT (project);
155 project->_activate();
156
157 // Initially not playing
158 TASSERT (!project->is_playing());
159
160 project->_deactivate();
161 project->discard();
162}
163TEST_ADD (project_playback_state);
164
165static void
166track_mute_solo()
167{
168 ProjectImplP project = ProjectImpl::create ("TrackMuteSoloTest");
169 TASSERT (project);
170 project->_activate();
171
172 TrackP track = project->create_track();
173 TASSERT (track);
174
175 // Test initial mute/solo state
176 TASSERT (!track->is_muted());
177 TASSERT (!track->is_solo());
178
179 // Test mute notifications
180 uint64_t muted_notifications = 0;
181 auto muted_connection = track->on_event ("notify:muted", [&muted_notifications] (const Event &event) { muted_notifications++; });
182
183 uint64_t solo_notifications = 0;
184 auto solo_connection = track->on_event ("notify:solo", [&solo_notifications] (const Event &event) { solo_notifications++; });
185
186 // Test mute with notification
187 uint64_t last_muted_notifications = muted_notifications;
188 track->set_muted (true);
189 TASSERT (track->is_muted());
190 TASSERT (muted_notifications > last_muted_notifications);
191
192 // Reset mute
193 last_muted_notifications = muted_notifications;
194 track->set_muted (false);
195 TASSERT (!track->is_muted());
196 TASSERT (muted_notifications > last_muted_notifications);
197
198 // Test solo with notification
199 uint64_t last_solo_notifications = solo_notifications;
200 track->set_solo (true);
201 TASSERT (track->is_solo());
202 TASSERT (solo_notifications > last_solo_notifications);
203
204 // Reset solo
205 last_solo_notifications = solo_notifications;
206 track->set_solo (false);
207 TASSERT (!track->is_solo());
208 TASSERT (solo_notifications > last_solo_notifications);
209
210 project->_deactivate();
211 project->discard();
212}
213TEST_ADD (track_mute_solo);
214
215static void
216track_undo_redo()
217{
218 ProjectImplP project = ProjectImpl::create ("TrackUndoRedoTest");
219 TASSERT (project);
220 project->_activate();
221
222 TrackP track = project->create_track();
223 TASSERT (track);
224
225 // Test track volume/pan notifications
226 uint64_t volume_notifications = 0;
227 auto volume_connection = track->on_event ("notify:volume", [&volume_notifications] (const Event &event) { volume_notifications++; });
228
229 uint64_t pan_notifications = 0;
230 auto pan_connection = track->on_event ("notify:pan", [&pan_notifications] (const Event &event) { pan_notifications++; });
231
232 // Change volume
233 uint64_t last_volume_notifications = volume_notifications;
234 track->volume (-6.0);
235 TASSERT (std::abs (track->volume() - (-6.0)) < 0.01);
236 TASSERT (volume_notifications > last_volume_notifications);
237
238 // Change volume back
239 last_volume_notifications = volume_notifications;
240 track->volume (0.0);
241 TASSERT (std::abs (track->volume()) < 0.01);
242 TASSERT (volume_notifications > last_volume_notifications);
243
244 // Change pan
245 uint64_t last_pan_notifications = pan_notifications;
246 track->pan (0.5);
247 TASSERT (std::abs (track->pan() - 0.5) < 0.01);
248 TASSERT (pan_notifications > last_pan_notifications);
249
250 // Change pan back
251 last_pan_notifications = pan_notifications;
252 track->pan (-0.5);
253 TASSERT (std::abs (track->pan() - (-0.5)) < 0.01);
254 TASSERT (pan_notifications > last_pan_notifications);
255
256 project->_deactivate();
257 project->discard();
258}
259TEST_ADD (track_undo_redo);
260
261static void
262track_volume_pan()
263{
264 ProjectImplP project = ProjectImpl::create ("TrackVolumePanTest");
265 TASSERT (project);
266 project->_activate();
267
268 TrackP track = project->create_track();
269 TASSERT (track);
270
271 // Test initial volume
272 double initial_vol = track->volume();
273 TASSERT (initial_vol >= -100.0 && initial_vol <= 20.0);
274
275 // Test volume notifications
276 uint64_t volume_notifications = 0;
277 auto volume_connection = track->on_event ("notify:volume", [&volume_notifications] (const Event &event) { volume_notifications++; });
278
279 uint64_t pan_notifications = 0;
280 auto pan_connection = track->on_event ("notify:pan", [&pan_notifications] (const Event &event) { pan_notifications++; });
281
282 // Test setting volume with notification
283 uint64_t last_volume_notifications = volume_notifications;
284 track->volume (-6.0);
285 TASSERT (std::abs (track->volume() - (-6.0)) < 0.01);
286 TASSERT (volume_notifications > last_volume_notifications);
287
288 // Reset volume
289 last_volume_notifications = volume_notifications;
290 track->volume (0.0);
291 TASSERT (std::abs (track->volume()) < 0.01);
292 TASSERT (volume_notifications > last_volume_notifications);
293
294 // Test pan
295 double initial_pan = track->pan();
296 TASSERT (initial_pan >= -1.0 && initial_pan <= 1.0);
297
298 // Test setting pan with notification
299 uint64_t last_pan_notifications = pan_notifications;
300 track->pan (0.5);
301 TASSERT (std::abs (track->pan() - 0.5) < 0.01);
302 TASSERT (pan_notifications > last_pan_notifications);
303
304 // Reset pan
305 last_pan_notifications = pan_notifications;
306 track->pan (-0.5);
307 TASSERT (std::abs (track->pan() - (-0.5)) < 0.01);
308 TASSERT (pan_notifications > last_pan_notifications);
309
310 project->_deactivate();
311 project->discard();
312}
313TEST_ADD (track_volume_pan);
314
315static void
316track_name()
317{
318 ProjectImplP project = ProjectImpl::create ("TrackNameTest");
319 TASSERT (project);
320 project->_activate();
321
322 TrackP track = project->create_track();
323 TASSERT (track);
324
325 // Set and get track name
326 track->name ("MyTrack");
327 TASSERT (track->name() == "MyTrack");
328
329 track->name ("AnotherName");
330 TASSERT (track->name() == "AnotherName");
331
332 project->_deactivate();
333 project->discard();
334}
335TEST_ADD (track_name);
336
337static void
338clip_creation()
339{
340 ProjectImplP project = ProjectImpl::create ("ClipTest");
341 TASSERT (project);
342 project->_activate();
343
344 TrackP track = project->create_track();
345 TASSERT (track);
346
347 TrackImplP trackimpl = std::dynamic_pointer_cast<TrackImpl> (track);
348 TASSERT (trackimpl);
349
350 ClipImplP clip = trackimpl->create_midi_clip ("TestClip", 0.0, 4.0);
351 TASSERT (clip);
352
353 project->_deactivate();
354 project->discard();
355}
356TEST_ADD (clip_creation);
357
358static void
359clip_notes()
360{
361 ProjectImplP project = ProjectImpl::create ("ClipNotesTest");
362 TASSERT (project);
363 project->_activate();
364
365 TrackP track = project->create_track();
366 TASSERT (track);
367
368 TrackImplP trackimpl = std::dynamic_pointer_cast<TrackImpl> (track);
369 TASSERT (trackimpl);
370
371 ClipImplP clip = trackimpl->create_midi_clip ("NotesClip", 0.0, 4.0);
372 TASSERT (clip);
373
374 ClipNoteS notes = clip->list_all_notes();
375 TASSERT (notes.empty());
376
377 ClipNote note;
378 note.id = -1;
379 note.key = 60;
380 note.channel = 0;
381 note.tick = 0;
382 note.duration = 960;
383 note.velocity = 0.8f;
384
385 ClipNoteS batch;
386 batch.push_back (note);
387 clip->change_batch (batch, "Add Note");
388
389 notes = clip->list_all_notes();
390 TASSERT (notes.size() == 1);
391 TASSERT (notes[0].key == 60);
392
393 project->_deactivate();
394 project->discard();
395}
396TEST_ADD (clip_notes);
397
398static void
399clip_range()
400{
401 ProjectImplP project = ProjectImpl::create ("ClipRangeTest");
402 TASSERT (project);
403 project->_activate();
404
405 TrackP track = project->create_track();
406 TASSERT (track);
407
408 TrackImplP trackimpl = std::dynamic_pointer_cast<TrackImpl> (track);
409 TASSERT (trackimpl);
410
411 ClipImplP clip = trackimpl->create_midi_clip ("RangeClip", 0.0, 4.0);
412 TASSERT (clip);
413
414 int64 start = clip->start_tick();
415 int64 stop = clip->stop_tick();
416 TASSERT (start >= 0);
417 TASSERT (stop > start);
418
419 clip->assign_range (start + 960, stop + 960);
420 TASSERT (clip->start_tick() == start + 960);
421
422 project->_deactivate();
423 project->discard();
424}
425TEST_ADD (clip_range);
426
427static void
428clip_mute_volume_pan()
429{
430 ProjectImplP project = ProjectImpl::create ("ClipMuteVolPanTest");
431 TASSERT (project);
432 project->_activate();
433
434 TrackP track = project->create_track();
435 TASSERT (track);
436
437 TrackImplP trackimpl = std::dynamic_pointer_cast<TrackImpl> (track);
438 TASSERT (trackimpl);
439
440 // Test MIDI clip
441 ClipImplP mclip = trackimpl->create_midi_clip ("MidiClip", 0.0, 4.0);
442 TASSERT (mclip);
443
444 // Test initial state
445 TASSERT (!mclip->is_muted());
446
447 // Test mute notifications
448 uint64_t muted_notifications = 0;
449 auto muted_connection = mclip->on_event ("notify:muted", [&muted_notifications] (const Event &event) { muted_notifications++; });
450
451 // Test mute with notification
452 uint64_t last_muted_notifications = muted_notifications;
453 mclip->set_muted (true);
454 TASSERT (mclip->is_muted());
455 TASSERT (muted_notifications > last_muted_notifications);
456
457 // Reset mute
458 last_muted_notifications = muted_notifications;
459 mclip->set_muted (false);
460 TASSERT (!mclip->is_muted());
461 TASSERT (muted_notifications > last_muted_notifications);
462
463 // Test volume notifications
464 uint64_t volume_notifications = 0;
465 auto volume_connection = mclip->on_event ("notify:volume", [&volume_notifications] (const Event &event) { volume_notifications++; });
466
467 // Test setting volume with notification
468 uint64_t last_volume_notifications = volume_notifications;
469 mclip->volume (-6.0);
470 TASSERT (std::abs (mclip->volume() - (-6.0)) < 0.01);
471 TASSERT (volume_notifications > last_volume_notifications);
472
473 // Reset volume
474 last_volume_notifications = volume_notifications;
475 mclip->volume (0.0);
476 TASSERT (std::abs (mclip->volume()) < 0.01);
477 TASSERT (volume_notifications > last_volume_notifications);
478
479 // Test audio clip (pan is only available on audio clips)
480 ClipImplP aclip = trackimpl->create_audio_clip ("AudioClip", 0.0, 4.0);
481 TASSERT (aclip);
482
483 // Test pan notifications for audio clip
484 uint64_t pan_notifications = 0;
485 auto pan_connection = aclip->on_event ("notify:pan", [&pan_notifications] (const Event &event) { pan_notifications++; });
486
487 // Initial pan should be 0
488 TASSERT (std::abs (aclip->pan()) < 0.01);
489
490 // Test setting pan with notification
491 uint64_t last_pan_notifications = pan_notifications;
492 aclip->pan (0.5);
493 TASSERT (std::abs (aclip->pan() - 0.5) < 0.01);
494 TASSERT (pan_notifications > last_pan_notifications);
495
496 // Reset pan
497 last_pan_notifications = pan_notifications;
498 aclip->pan (-0.5);
499 TASSERT (std::abs (aclip->pan() - (-0.5)) < 0.01);
500 TASSERT (pan_notifications > last_pan_notifications);
501
502 // Test audio clip volume
503 uint64_t audio_volume_notifications = 0;
504 auto audio_volume_connection = aclip->on_event ("notify:volume", [&audio_volume_notifications] (const Event &event) { audio_volume_notifications++; });
505
506 last_volume_notifications = audio_volume_notifications;
507 aclip->volume (-3.0);
508 TASSERT (std::abs (aclip->volume() - (-3.0)) < 0.01);
509 TASSERT (audio_volume_notifications > last_volume_notifications);
510
511 project->_deactivate();
512 project->discard();
513}
514TEST_ADD (clip_mute_volume_pan);
515
516static void
517clip_undo_redo()
518{
519 ProjectImplP project = ProjectImpl::create ("ClipUndoRedoTest");
520 TASSERT (project);
521 project->_activate();
522
523 TrackP track = project->create_track();
524 TASSERT (track);
525
526 TrackImplP trackimpl = std::dynamic_pointer_cast<TrackImpl> (track);
527 TASSERT (trackimpl);
528
529 // Test MIDI clip mute undo/redo
530 ClipImplP mclip = trackimpl->create_midi_clip ("UndoMidiClip", 0.0, 4.0);
531 TASSERT (mclip);
532
533 uint64_t muted_notifications = 0;
534 auto muted_connection = mclip->on_event ("notify:muted", [&muted_notifications] (const Event &event) { muted_notifications++; });
535
536 // Set muted
537 mclip->set_muted (true);
538 TASSERT (mclip->is_muted());
539 uint64_t last_muted_notifications = muted_notifications;
540
541 // Undo mute
542 TASSERT (project->can_undo());
543 project->undo();
544 TASSERT (!mclip->is_muted());
545
546 // Redo mute
547 TASSERT (project->can_redo());
548 project->redo();
549 TASSERT (mclip->is_muted());
550
551 // Test MIDI clip volume undo/redo
552 uint64_t volume_notifications = 0;
553 auto volume_connection = mclip->on_event ("notify:volume", [&volume_notifications] (const Event &event) { volume_notifications++; });
554
555 // Set volume
556 double initial_vol = mclip->volume();
557 uint64_t last_volume_notifications = volume_notifications;
558 mclip->volume (-12.0);
559 TASSERT (std::abs (mclip->volume() - (-12.0)) < 0.01);
560
561 // Undo volume
562 TASSERT (project->can_undo());
563 project->undo();
564 TASSERT (std::abs (mclip->volume() - initial_vol) < 0.01);
565
566 // Redo volume
567 TASSERT (project->can_redo());
568 project->redo();
569 TASSERT (std::abs (mclip->volume() - (-12.0)) < 0.01);
570
571 // Test audio clip pan undo/redo
572 ClipImplP aclip = trackimpl->create_audio_clip ("UndoAudioClip", 0.0, 4.0);
573 TASSERT (aclip);
574
575 uint64_t pan_notifications = 0;
576 auto pan_connection = aclip->on_event ("notify:pan", [&pan_notifications] (const Event &event) { pan_notifications++; });
577
578 // Set pan
579 double initial_pan = aclip->pan();
580 uint64_t last_pan_notifications = pan_notifications;
581 aclip->pan (0.8);
582 TASSERT (std::abs (aclip->pan() - 0.8) < 0.01);
583
584 // Undo pan
585 TASSERT (project->can_undo());
586 project->undo();
587 TASSERT (std::abs (aclip->pan() - initial_pan) < 0.01);
588
589 // Redo pan
590 TASSERT (project->can_redo());
591 project->redo();
592 TASSERT (std::abs (aclip->pan() - 0.8) < 0.01);
593
594 // Test MIDI clip notes undo/redo
595 ClipNoteS batch;
596 ClipNote note;
597 note.id = -1;
598 note.key = 60;
599 note.channel = 0;
600 note.tick = 0;
601 note.duration = 960;
602 note.velocity = 0.8f;
603 batch.push_back (note);
604
605 uint64_t notes_notifications = 0;
606 auto notes_connection = mclip->on_event ("notify:notes", [&notes_notifications] (const Event &event) { notes_notifications++; });
607
608 // Add notes
609 uint64_t last_notes_notifications = notes_notifications;
610 mclip->change_batch (batch, "Add Note");
611 TASSERT (mclip->list_all_notes().size() == 1);
612
613 // Undo notes
614 TASSERT (project->can_undo());
615 project->undo();
616 TASSERT (mclip->list_all_notes().empty());
617
618 // Redo notes
619 TASSERT (project->can_redo());
620 project->redo();
621 TASSERT (mclip->list_all_notes().size() == 1);
622
623 // Test MIDI clip range undo/redo
624 int64 initial_start = mclip->start_tick();
625 int64 initial_stop = mclip->stop_tick();
626
627 // Assign new range
628 uint64_t range_notifications = 0;
629 auto range_connection = mclip->on_event ("notify:start_tick", [&range_notifications] (const Event &event) { range_notifications++; });
630
631 uint64_t last_range_notifications = range_notifications;
632 mclip->assign_range (initial_start + 960, initial_stop + 960);
633 TASSERT (mclip->start_tick() == initial_start + 960);
634 TASSERT (range_notifications > last_range_notifications);
635
636 // Undo range
637 TASSERT (project->can_undo());
638 project->undo();
639 TASSERT (mclip->start_tick() == initial_start);
640 TASSERT (mclip->stop_tick() == initial_stop);
641
642 // Redo range
643 TASSERT (project->can_redo());
644 project->redo();
645 TASSERT (mclip->start_tick() == initial_start + 960);
646 TASSERT (mclip->stop_tick() == initial_stop + 960);
647
648 project->_deactivate();
649 project->discard();
650}
651TEST_ADD (clip_undo_redo);
652
653} // Anon
The Anklang C++ API namespace.
Definition api.hh:9
int64_t int64
A 64-bit unsigned integer, use PRI*64 in format strings.
Definition cxxaux.hh:29
typedef uint64_t
Part specific note event representation.
Definition api.hh:232
int64 tick
UI selection flag.
Definition api.hh:237
float velocity
Duration in number of ticks.
Definition api.hh:239
int64 duration
Position in ticks.
Definition api.hh:238
int8 channel
ID, > 0.
Definition api.hh:234
int8 key
MIDI Channel.
Definition api.hh:235
Structure for callback based notifications.
Definition value.hh:113
#define TASSERT(cond)
Unconditional test assertion, enters breakpoint if not fullfilled.
Definition testing.hh:24
#define TEST_ADD(fun)
Register a function to run as part of the unit test suite.
Definition testing.hh:31