From 4c7832a975f9786da873edecb6f917c5b8f98a06 Mon Sep 17 00:00:00 2001 From: Tobias Doerffel Date: Thu, 22 Sep 2005 13:49:52 +0000 Subject: [PATCH] Initial revision git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@3 0778d3d1-df1d-0410-868b-ea421aaaa00d --- AUTHORS | 12 + COPYING | 340 +++++++ ChangeLog | 1124 ++++++++++++++++++++++ INSTALL | 182 ++++ Makefile.am | 304 ++++++ README | 97 ++ TODO | 40 + acinclude.m4 | 266 ++++++ buildtools/bin2res.cpp | 210 +++++ configure.in | 496 ++++++++++ include/about_dialog.h | 69 ++ include/arp_and_chords_tab_widget.h | 138 +++ include/audio_alsa.h | 102 ++ include/audio_device.h | 143 +++ include/audio_dummy.h | 80 ++ include/audio_file_device.h | 94 ++ include/audio_file_ogg.h | 102 ++ include/audio_file_wave.h | 90 ++ include/audio_jack.h | 122 +++ include/audio_oss.h | 79 ++ include/audio_sample_recorder.h | 74 ++ include/audio_sdl.h | 110 +++ include/basic_filters.h | 258 +++++ include/bb_editor.h | 119 +++ include/bb_track.h | 150 +++ include/buffer_allocator.h | 78 ++ include/clipboard.h | 59 ++ include/config_mgr.h | 187 ++++ include/debug.h | 41 + include/effect_board.h | 31 + include/embed.h | 51 + include/endian_handling.h | 59 ++ include/envelope_and_lfo_widget.h | 174 ++++ include/envelope_tab_widget.h | 107 +++ include/export.h | 61 ++ include/export_project_dialog.h | 111 +++ include/group_box.h | 85 ++ include/interpolation.h | 93 ++ include/kmultitabbar.h | 320 +++++++ include/knob.h | 191 ++++ include/lcd_spinbox.h | 84 ++ include/led_checkbox.h | 67 ++ include/midi.h | 107 +++ include/midi_alsa_raw.h | 107 +++ include/midi_dummy.h | 77 ++ include/midi_mapper.h | 112 +++ include/midi_oss.h | 79 ++ include/midi_time.h | 122 +++ include/mixer.h | 341 +++++++ include/mmp.h | 109 +++ include/name_label.h | 75 ++ include/note.h | 167 ++++ include/note_play_handle.h | 180 ++++ include/nstate_button.h | 91 ++ include/oscillator.h | 211 +++++ include/panning.h | 35 + include/pattern.h | 216 +++++ include/pch.h | 69 ++ include/piano_roll.h | 237 +++++ include/piano_widget.h | 104 ++ include/pixmap_button.h | 78 ++ include/play_handle.h | 76 ++ include/preset_preview_play_handle.h | 60 ++ include/project_notes.h | 98 ++ include/qt3support.h | 177 ++++ include/rename_dialog.h | 70 ++ include/sample_buffer.h | 168 ++++ include/sample_play_handle.h | 66 ++ include/sample_track.h | 151 +++ include/setup_dialog.h | 113 +++ include/side_bar.h | 106 +++ include/side_bar_widget.h | 88 ++ include/song_editor.h | 342 +++++++ include/spc_bg_hndl_widget.h | 135 +++ include/surround_area.h | 91 ++ include/tab_bar.h | 102 ++ include/tab_button.h | 75 ++ include/tab_widget.h | 74 ++ include/templates.h | 102 ++ include/text_float.h | 66 ++ include/timeline.h | 160 ++++ include/tooltip.h | 42 + include/track.h | 358 +++++++ include/track_container.h | 119 +++ include/types.h | 48 + include/visualization_widget.h | 81 ++ include/volume.h | 33 + lmms.spec.in | 122 +++ resources/add_bb_track.png | Bin 0 -> 2149 bytes resources/add_sample_track.png | Bin 0 -> 683 bytes resources/apply.png | Bin 0 -> 768 bytes resources/arp_down_off.png | Bin 0 -> 430 bytes resources/arp_down_on.png | Bin 0 -> 670 bytes resources/arp_random_off.png | Bin 0 -> 506 bytes resources/arp_random_on.png | Bin 0 -> 870 bytes resources/arp_up_and_down_off.png | Bin 0 -> 457 bytes resources/arp_up_and_down_on.png | Bin 0 -> 714 bytes resources/arp_up_off.png | Bin 0 -> 399 bytes resources/arp_up_on.png | Bin 0 -> 622 bytes resources/autoscroll_off.png | Bin 0 -> 320 bytes resources/autoscroll_on.png | Bin 0 -> 322 bytes resources/back_to_start.png | Bin 0 -> 344 bytes resources/back_to_zero.png | Bin 0 -> 336 bytes resources/background_artwork.png | Bin 0 -> 15440 bytes resources/bb_track.png | Bin 0 -> 1997 bytes resources/black_key.png | Bin 0 -> 479 bytes resources/black_key_pressed.png | Bin 0 -> 459 bytes resources/cancel.png | Bin 0 -> 882 bytes resources/clock.png | Bin 0 -> 874 bytes resources/colorize.png | Bin 0 -> 1857 bytes resources/edit_copy.png | Bin 0 -> 589 bytes resources/edit_cut.png | Bin 0 -> 1195 bytes resources/edit_erase.png | Bin 0 -> 1265 bytes resources/edit_paste.png | Bin 0 -> 880 bytes resources/edit_redo.png | Bin 0 -> 738 bytes resources/edit_undo.png | Bin 0 -> 682 bytes resources/effect_board.png | Bin 0 -> 1400 bytes resources/empty.png | Bin 0 -> 168 bytes resources/envelope_graph.png | Bin 0 -> 3688 bytes resources/exit.png | Bin 0 -> 1397 bytes resources/exp_wave_active.png | Bin 0 -> 545 bytes resources/exp_wave_inactive.png | Bin 0 -> 573 bytes resources/folder.png | Bin 0 -> 557 bytes resources/folder_locked.png | Bin 0 -> 797 bytes resources/folder_opened.png | Bin 0 -> 616 bytes resources/freeze.png | Bin 0 -> 2238 bytes resources/frozen.png | Bin 0 -> 425 bytes resources/groupbox_led_bg.png | Bin 0 -> 187 bytes resources/help.png | Bin 0 -> 1185 bytes resources/home.png | Bin 0 -> 1616 bytes resources/hourglass.png | Bin 0 -> 4157 bytes resources/keep_stop_position.png | Bin 0 -> 319 bytes resources/knob01.png | Bin 0 -> 1640 bytes resources/knob02.png | Bin 0 -> 1792 bytes resources/knob03.png | Bin 0 -> 894 bytes resources/led_green.png | Bin 0 -> 934 bytes resources/led_off.png | Bin 0 -> 909 bytes resources/led_yellow.png | Bin 0 -> 913 bytes resources/lfo_graph.png | Bin 0 -> 4065 bytes resources/loop_point.png | Bin 0 -> 366 bytes resources/loop_points_off.png | Bin 0 -> 346 bytes resources/loop_points_on.png | Bin 0 -> 350 bytes resources/master_pitch.png | Bin 0 -> 951 bytes resources/master_volume.png | Bin 0 -> 667 bytes resources/moog_saw_wave_active.png | Bin 0 -> 538 bytes resources/moog_saw_wave_inactive.png | Bin 0 -> 587 bytes resources/mute_off.png | Bin 0 -> 1022 bytes resources/mute_on.png | Bin 0 -> 1062 bytes resources/output_graph.png | Bin 0 -> 4412 bytes resources/pause.png | Bin 0 -> 1462 bytes resources/piano.png | Bin 0 -> 838 bytes resources/play.png | Bin 0 -> 1653 bytes resources/playpos_marker.png | Bin 0 -> 516 bytes resources/pr_black_key.png | Bin 0 -> 669 bytes resources/pr_white_key_big.png | Bin 0 -> 504 bytes resources/pr_white_key_small.png | Bin 0 -> 358 bytes resources/preset_file.png | Bin 0 -> 1522 bytes resources/project_export.png | Bin 0 -> 1000 bytes resources/project_file.png | Bin 0 -> 1649 bytes resources/project_new.png | Bin 0 -> 1144 bytes resources/project_notes.png | Bin 0 -> 1325 bytes resources/project_open.png | Bin 0 -> 1039 bytes resources/project_save.png | Bin 0 -> 820 bytes resources/project_saveas.png | Bin 0 -> 1135 bytes resources/record.png | Bin 0 -> 1630 bytes resources/reload.png | Bin 0 -> 1156 bytes resources/rename.png | Bin 0 -> 1075 bytes resources/root.png | Bin 0 -> 1817 bytes resources/sample_track.png | Bin 0 -> 271 bytes resources/saw_wave_active.png | Bin 0 -> 517 bytes resources/saw_wave_inactive.png | Bin 0 -> 530 bytes resources/setup_audio.png | Bin 0 -> 6587 bytes resources/setup_general.png | Bin 0 -> 6440 bytes resources/setup_midi.png | Bin 0 -> 3743 bytes resources/sin_wave_active.png | Bin 0 -> 543 bytes resources/sin_wave_inactive.png | Bin 0 -> 557 bytes resources/songeditor.png | Bin 0 -> 1052 bytes resources/sound_file.png | Bin 0 -> 1893 bytes resources/square_wave_active.png | Bin 0 -> 504 bytes resources/square_wave_inactive.png | Bin 0 -> 495 bytes resources/step_btn_off.png | Bin 0 -> 389 bytes resources/step_btn_off_light.png | Bin 0 -> 336 bytes resources/step_btn_on.png | Bin 0 -> 302 bytes resources/stop.png | Bin 0 -> 1660 bytes resources/surround_area.png | Bin 0 -> 3608 bytes resources/text_block.png | Bin 0 -> 174 bytes resources/text_bold.png | Bin 0 -> 733 bytes resources/text_center.png | Bin 0 -> 177 bytes resources/text_italic.png | Bin 0 -> 659 bytes resources/text_left.png | Bin 0 -> 176 bytes resources/text_right.png | Bin 0 -> 177 bytes resources/text_under.png | Bin 0 -> 724 bytes resources/timeline.png | Bin 0 -> 518 bytes resources/toolbar_bg.png | Bin 0 -> 156 bytes resources/triangle_wave_active.png | Bin 0 -> 555 bytes resources/triangle_wave_inactive.png | Bin 0 -> 580 bytes resources/unfreeze.png | Bin 0 -> 1511 bytes resources/unknown_file.png | Bin 0 -> 457 bytes resources/usr_wave_active.png | Bin 0 -> 518 bytes resources/usr_wave_inactive.png | Bin 0 -> 509 bytes resources/whatsthis.png | Bin 0 -> 985 bytes resources/white_key.png | Bin 0 -> 242 bytes resources/white_key_pressed.png | Bin 0 -> 442 bytes resources/white_noise_wave_active.png | Bin 0 -> 560 bytes resources/white_noise_wave_inactive.png | Bin 0 -> 541 bytes resources/wizard.png | Bin 0 -> 1460 bytes src/audio/audio_alsa.cpp | 439 +++++++++ src/audio/audio_device.cpp | 290 ++++++ src/audio/audio_file_device.cpp | 103 ++ src/audio/audio_file_ogg.cpp | 267 ++++++ src/audio/audio_file_wave.cpp | 118 +++ src/audio/audio_jack.cpp | 379 ++++++++ src/audio/audio_oss.cpp | 329 +++++++ src/audio/audio_sample_recorder.cpp | 112 +++ src/audio/audio_sdl.cpp | 203 ++++ src/lib/buffer_allocator.cpp | 197 ++++ src/lib/clipboard.cpp | 58 ++ src/lib/embed.cpp | 123 +++ src/lib/mmp.cpp | 273 ++++++ src/lib/oscillator.cpp | 259 +++++ src/lib/sample_buffer.cpp | 1014 ++++++++++++++++++++ src/midi/midi_alsa_raw.cpp | 233 +++++ src/midi/midi_mapper.cpp | 222 +++++ src/midi/midi_oss.cpp | 164 ++++ src/tracks/bb_track.cpp | 509 ++++++++++ src/tracks/pattern.cpp | 1027 ++++++++++++++++++++ src/tracks/sample_track.cpp | 395 ++++++++ src/widgets/group_box.cpp | 234 +++++ src/widgets/kmultitabbar.cpp | 1148 +++++++++++++++++++++++ src/widgets/knob.cpp | 812 ++++++++++++++++ src/widgets/lcd_spinbox.cpp | 176 ++++ src/widgets/led_checkbox.cpp | 130 +++ src/widgets/nstate_button.cpp | 143 +++ src/widgets/pixmap_button.cpp | 175 ++++ src/widgets/project_notes.cpp | 571 +++++++++++ src/widgets/rename_dialog.cpp | 89 ++ src/widgets/side_bar_widget.cpp | 123 +++ src/widgets/tab_bar.cpp | 260 +++++ src/widgets/tab_widget.cpp | 177 ++++ src/widgets/text_float.cpp | 152 +++ src/widgets/tooltip.cpp | 59 ++ src/widgets/visualization_widget.cpp | 221 +++++ 242 files changed, 23424 insertions(+) create mode 100644 AUTHORS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 INSTALL create mode 100644 Makefile.am create mode 100644 README create mode 100644 TODO create mode 100644 acinclude.m4 create mode 100644 buildtools/bin2res.cpp create mode 100644 configure.in create mode 100644 include/about_dialog.h create mode 100644 include/arp_and_chords_tab_widget.h create mode 100644 include/audio_alsa.h create mode 100644 include/audio_device.h create mode 100644 include/audio_dummy.h create mode 100644 include/audio_file_device.h create mode 100644 include/audio_file_ogg.h create mode 100644 include/audio_file_wave.h create mode 100644 include/audio_jack.h create mode 100644 include/audio_oss.h create mode 100644 include/audio_sample_recorder.h create mode 100644 include/audio_sdl.h create mode 100644 include/basic_filters.h create mode 100644 include/bb_editor.h create mode 100644 include/bb_track.h create mode 100644 include/buffer_allocator.h create mode 100644 include/clipboard.h create mode 100644 include/config_mgr.h create mode 100644 include/debug.h create mode 100644 include/effect_board.h create mode 100644 include/embed.h create mode 100644 include/endian_handling.h create mode 100644 include/envelope_and_lfo_widget.h create mode 100644 include/envelope_tab_widget.h create mode 100644 include/export.h create mode 100644 include/export_project_dialog.h create mode 100644 include/group_box.h create mode 100644 include/interpolation.h create mode 100644 include/kmultitabbar.h create mode 100644 include/knob.h create mode 100644 include/lcd_spinbox.h create mode 100644 include/led_checkbox.h create mode 100644 include/midi.h create mode 100644 include/midi_alsa_raw.h create mode 100644 include/midi_dummy.h create mode 100644 include/midi_mapper.h create mode 100644 include/midi_oss.h create mode 100644 include/midi_time.h create mode 100644 include/mixer.h create mode 100644 include/mmp.h create mode 100644 include/name_label.h create mode 100644 include/note.h create mode 100644 include/note_play_handle.h create mode 100644 include/nstate_button.h create mode 100644 include/oscillator.h create mode 100644 include/panning.h create mode 100644 include/pattern.h create mode 100644 include/pch.h create mode 100644 include/piano_roll.h create mode 100644 include/piano_widget.h create mode 100644 include/pixmap_button.h create mode 100644 include/play_handle.h create mode 100644 include/preset_preview_play_handle.h create mode 100644 include/project_notes.h create mode 100644 include/qt3support.h create mode 100644 include/rename_dialog.h create mode 100644 include/sample_buffer.h create mode 100644 include/sample_play_handle.h create mode 100644 include/sample_track.h create mode 100644 include/setup_dialog.h create mode 100644 include/side_bar.h create mode 100644 include/side_bar_widget.h create mode 100644 include/song_editor.h create mode 100644 include/spc_bg_hndl_widget.h create mode 100644 include/surround_area.h create mode 100644 include/tab_bar.h create mode 100644 include/tab_button.h create mode 100644 include/tab_widget.h create mode 100644 include/templates.h create mode 100644 include/text_float.h create mode 100644 include/timeline.h create mode 100644 include/tooltip.h create mode 100644 include/track.h create mode 100644 include/track_container.h create mode 100644 include/types.h create mode 100644 include/visualization_widget.h create mode 100644 include/volume.h create mode 100644 lmms.spec.in create mode 100644 resources/add_bb_track.png create mode 100644 resources/add_sample_track.png create mode 100644 resources/apply.png create mode 100644 resources/arp_down_off.png create mode 100644 resources/arp_down_on.png create mode 100644 resources/arp_random_off.png create mode 100644 resources/arp_random_on.png create mode 100644 resources/arp_up_and_down_off.png create mode 100644 resources/arp_up_and_down_on.png create mode 100644 resources/arp_up_off.png create mode 100644 resources/arp_up_on.png create mode 100644 resources/autoscroll_off.png create mode 100644 resources/autoscroll_on.png create mode 100644 resources/back_to_start.png create mode 100644 resources/back_to_zero.png create mode 100644 resources/background_artwork.png create mode 100644 resources/bb_track.png create mode 100644 resources/black_key.png create mode 100644 resources/black_key_pressed.png create mode 100644 resources/cancel.png create mode 100644 resources/clock.png create mode 100644 resources/colorize.png create mode 100644 resources/edit_copy.png create mode 100644 resources/edit_cut.png create mode 100644 resources/edit_erase.png create mode 100644 resources/edit_paste.png create mode 100644 resources/edit_redo.png create mode 100644 resources/edit_undo.png create mode 100644 resources/effect_board.png create mode 100644 resources/empty.png create mode 100644 resources/envelope_graph.png create mode 100644 resources/exit.png create mode 100644 resources/exp_wave_active.png create mode 100644 resources/exp_wave_inactive.png create mode 100644 resources/folder.png create mode 100644 resources/folder_locked.png create mode 100644 resources/folder_opened.png create mode 100644 resources/freeze.png create mode 100644 resources/frozen.png create mode 100644 resources/groupbox_led_bg.png create mode 100644 resources/help.png create mode 100644 resources/home.png create mode 100644 resources/hourglass.png create mode 100644 resources/keep_stop_position.png create mode 100644 resources/knob01.png create mode 100644 resources/knob02.png create mode 100644 resources/knob03.png create mode 100644 resources/led_green.png create mode 100644 resources/led_off.png create mode 100644 resources/led_yellow.png create mode 100644 resources/lfo_graph.png create mode 100644 resources/loop_point.png create mode 100644 resources/loop_points_off.png create mode 100644 resources/loop_points_on.png create mode 100644 resources/master_pitch.png create mode 100644 resources/master_volume.png create mode 100644 resources/moog_saw_wave_active.png create mode 100644 resources/moog_saw_wave_inactive.png create mode 100644 resources/mute_off.png create mode 100644 resources/mute_on.png create mode 100644 resources/output_graph.png create mode 100644 resources/pause.png create mode 100644 resources/piano.png create mode 100644 resources/play.png create mode 100644 resources/playpos_marker.png create mode 100644 resources/pr_black_key.png create mode 100644 resources/pr_white_key_big.png create mode 100644 resources/pr_white_key_small.png create mode 100644 resources/preset_file.png create mode 100644 resources/project_export.png create mode 100644 resources/project_file.png create mode 100644 resources/project_new.png create mode 100644 resources/project_notes.png create mode 100644 resources/project_open.png create mode 100644 resources/project_save.png create mode 100644 resources/project_saveas.png create mode 100644 resources/record.png create mode 100644 resources/reload.png create mode 100644 resources/rename.png create mode 100644 resources/root.png create mode 100644 resources/sample_track.png create mode 100644 resources/saw_wave_active.png create mode 100644 resources/saw_wave_inactive.png create mode 100644 resources/setup_audio.png create mode 100644 resources/setup_general.png create mode 100644 resources/setup_midi.png create mode 100644 resources/sin_wave_active.png create mode 100644 resources/sin_wave_inactive.png create mode 100644 resources/songeditor.png create mode 100644 resources/sound_file.png create mode 100644 resources/square_wave_active.png create mode 100644 resources/square_wave_inactive.png create mode 100644 resources/step_btn_off.png create mode 100644 resources/step_btn_off_light.png create mode 100644 resources/step_btn_on.png create mode 100644 resources/stop.png create mode 100644 resources/surround_area.png create mode 100644 resources/text_block.png create mode 100644 resources/text_bold.png create mode 100644 resources/text_center.png create mode 100644 resources/text_italic.png create mode 100644 resources/text_left.png create mode 100644 resources/text_right.png create mode 100644 resources/text_under.png create mode 100644 resources/timeline.png create mode 100644 resources/toolbar_bg.png create mode 100644 resources/triangle_wave_active.png create mode 100644 resources/triangle_wave_inactive.png create mode 100644 resources/unfreeze.png create mode 100644 resources/unknown_file.png create mode 100644 resources/usr_wave_active.png create mode 100644 resources/usr_wave_inactive.png create mode 100644 resources/whatsthis.png create mode 100644 resources/white_key.png create mode 100644 resources/white_key_pressed.png create mode 100644 resources/white_noise_wave_active.png create mode 100644 resources/white_noise_wave_inactive.png create mode 100644 resources/wizard.png create mode 100644 src/audio/audio_alsa.cpp create mode 100644 src/audio/audio_device.cpp create mode 100644 src/audio/audio_file_device.cpp create mode 100644 src/audio/audio_file_ogg.cpp create mode 100644 src/audio/audio_file_wave.cpp create mode 100644 src/audio/audio_jack.cpp create mode 100644 src/audio/audio_oss.cpp create mode 100644 src/audio/audio_sample_recorder.cpp create mode 100644 src/audio/audio_sdl.cpp create mode 100644 src/lib/buffer_allocator.cpp create mode 100644 src/lib/clipboard.cpp create mode 100644 src/lib/embed.cpp create mode 100644 src/lib/mmp.cpp create mode 100644 src/lib/oscillator.cpp create mode 100644 src/lib/sample_buffer.cpp create mode 100644 src/midi/midi_alsa_raw.cpp create mode 100644 src/midi/midi_mapper.cpp create mode 100644 src/midi/midi_oss.cpp create mode 100644 src/tracks/bb_track.cpp create mode 100644 src/tracks/pattern.cpp create mode 100644 src/tracks/sample_track.cpp create mode 100644 src/widgets/group_box.cpp create mode 100644 src/widgets/kmultitabbar.cpp create mode 100644 src/widgets/knob.cpp create mode 100644 src/widgets/lcd_spinbox.cpp create mode 100644 src/widgets/led_checkbox.cpp create mode 100644 src/widgets/nstate_button.cpp create mode 100644 src/widgets/pixmap_button.cpp create mode 100644 src/widgets/project_notes.cpp create mode 100644 src/widgets/rename_dialog.cpp create mode 100644 src/widgets/side_bar_widget.cpp create mode 100644 src/widgets/tab_bar.cpp create mode 100644 src/widgets/tab_widget.cpp create mode 100644 src/widgets/text_float.cpp create mode 100644 src/widgets/tooltip.cpp create mode 100644 src/widgets/visualization_widget.cpp diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000000..ea0fce5d04 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,12 @@ +Tobias Doerffel + +Maintainer, main-development, artwork etc. + + +Sebastian Tilsch + +recording of many samples + + +...may be there're more here in the future... + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000000..eeb586b392 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000000..108326a143 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,1124 @@ +2005-09-21 Tobias Doerffel + + * configure.in: + made 0.1.1 release + +2005-09-20 Tobias Doerffel + + * include/about_dialog.h: + * src/core/about_dialog.cpp: + no need for passing parent when creating about-dialog + + * src/core/song_editor.cpp: + - different usage of question-box in songEditor::mayChangeProject() + because of bugs with several qt-versions... + - when exporting song, do use baseName( TRUE ) instead of baseName() + for determining base-name of rendered file + + * src/core/lmms_main_win.cpp: + - display filename before app-name in window-title + - only use basename of filename in window-title + - added message referring to LMMS-homepage when clicking on + Help/Help + + * src/core/export_song_dialog.cpp: + use lmmsMainWin::resetWindowTitle() for resetting window-title + after export is done + + * src/widgets/knob.cpp: + do not add tooltips to knobs - clicking on knob has quite the same + effect so tooltips are just annoying + + * src/core/browser.cpp: + when matching file-name, always convert them to lower case for also + handling files like foo.WAV + + * src/core/config_mgr.cpp: + added setValue(...)-call for saving working-directory after setting + up everything - fixes bug with empty browsers after fresh personal + installation of LMMS + +2005-09-19 Tobias Doerffel + + * configure.in: + made 0.1.0 release + +2005-09-18 Tobias Doerffel + + * src/tracks/bb_track.cpp: + revised paintEvent()-method -> bb-TCO's are now painted with a gradient! + + * src/core/song_editor.cpp: + - call bbEditor::stop before playing to make sure play-button in + bb-editor is properly reset + - call realignTracks() with parameter TRUE in wheelEvent(...) + - some hacks for making pause-button work... + + * src/core/track_container.cpp: + added parameter _complete_update to trackContainer::realignTracks() + to be able to hide and show all tracks which is neccessary e.g. + after having changed pixels-per-tact-property -> zooming in song-editor + works now well + + * src/core/setup_dialog.cpp: + added message-box telling the user to restart LMMS + + * include/midi_alsa_raw.h: + * include/midi_device.h: + * include/midi_oss.h: + * include/midi_dummy.h: + * include/mixer.h: + * include/setup_dialog.h: + * src/core/mixer.cpp: + * src/core/setup_dialog.cpp: + * src/midi/midi_alsa_raw.cpp: + * src/midi/midi_device.cpp: + * src/midi/midi_oss.cpp: + made it possible to select and setup MIDI-device + + * include/knob.h: + * src/widgets/knob.cpp: + - many cleanups, coding style improvements etc. + - added new behaviour for controlling knob -> move cursor up/down for + changing value + - replaced usage of status-bar by text-float-widget + + * include/text_float.h: + * src/widgets/text_float.cpp: + took text-float from Rosegarden + + * include/config_mgr.h: + * src/core/config_mgr.cpp: + - fixed bug in configManager::setValue(): new value-pair was always + pushed back, even if it already existed, so result of value() didn't + change until restart + - moved widget-creation-code from ctor to new method createWidgets() + which fixes problems with unlocalized strings in wizard because + embed::loadTranslation() calls configManager::inst()->localeDir() + for determining path to localization files - this call created all + wizard-widgets before actual localization was loaded... + +2005-09-17 Tobias Doerffel + + * src/core/setup_dialog.cpp: + added switches for disabling tooltips and turning on classical knob- + behaviour + + * include/led_checkbox.h: + * src/widget/led_checkbox.cpp: + added LED-check-box + +2005-09-16 Tobias Doerffel + + * src/core/mixer.cpp: + clear output-buffers in ctor before using them -> avoids awful noises + at startup + + * src/core/mixer.cpp: + * src/lib/sample_buffer.cpp: + * src/tracks/channel_track.cpp: + * src/tracks/sample_track.cpp: + use given frame-parameter in methods playing sample-buffers instead + of mixer::framesPerAudioBuffer() -> no clicks at tact-border because + of odd sample-frame-count + + * src/core/lmms_main_win.cpp: + call presetPreviewPlayHandle::cleanUp() in dtor -> fixes segfault which + which occured at exit if you previewed a channel during session + + * include/preset_preview_play_handle.h: + * src/core/preset_preview_play_handle.cpp: + added cleanUp()-method which deletes blind track-container containing + preset-preview-channel + + * src/soundgenerators/triple_oscillator.cpp: + - unified much code by defining setChecked when compiling with Qt 3 + - also set file-name of user-defined-wave as tool-tip when loading + settings + + * most files in src/: + use new tooltip-system - removes qt3/qt4-compat-code + + * include/tooltip.h: + * src/widget/tooltip.cpp: + added tooltip-namespace with method "add(...)" for allowing transparent + usage of tooltips, so there's no difference to code using tooltips + whether it is compiled with Qt 3 or Qt 4 + +2005-09-14 Tobias Doerffel + + * include/mixer.h: + * include/note_play_handle.h: + * include/play_handle.h: + * src/core/mixer.cpp: + * src/core/note_play_handle.cpp: + * src/tracks/channel_track.cpp: + added play-handle-invalidating-feature making it possible to + invalidate notes of which the objects they depend on do not exist + anymore or things like that. for example channel-track-dtor + invalidates all note-play-handles linked with according channel + -> no segfault when deleting channel-track + + * src/core/track_container.cpp: + pause mixer before removing track -> no segfault + + * src/lib/mmp.cpp: + initialize m_type in mmp::multimediaProject( projectTypes ) + + * src/lib/buffer_allocator.cpp: + added mutex for better protection against parallel accesses from + several threads - fixes some segfaults + + * src/core/lmms_main_win.cpp: + improved ~lmmsMainWin() which now first instructs mixer to quit it's + thread for avoiding spurious usages of objects to be destroyed + afterwards -> no segfault at the end + + * src/core/export_song_dialog.cpp: + * src/tracks/pattern.cpp: + cleaned up exporting/freezing mechanisms by using mixer's new methods + play() and pause() + + * include/mixer.h: + * src/core/mixer.cpp: + cleaned up mixer-thread syncing stuff by reorganizing mutexes and + replacing m_waitMutex by m_safetySyncMutex as well as according methods + lockWaitMutex() and unlockWaitMutex() by play() and pause() + + * src/core/setup_dialog.cpp: + tell config-manager to save it's config-file when setupDialog is + destroyed + +2005-09-13 Stephane Thomas + + * locale/fr.ts: + added French translation + +2005-09-13 Tobias Doerffel + + * src/core/lmms_main_win.cpp: + - show setup-dialog if LMMS is started first time or mixer uses dummy- + audio-driver + - added setup-dialog-entry to settings-menu + + * src/lib/mmp.cpp: + if saving preset, check whether file-name contains suffix ".cs.xml" + and add it if neccessary + + * include/midi_alsa_raw.h: + * src/midi/midi_alsa_raw.cpp: + * src/midi/midi_device.cpp: + - changed everything (including file-names) from midiAlsa to midiALSARaw + - added terminate()-call in dtor for terminating thread properly + + * include/tab_bar.h: + * src/widget/tab_bar.cpp: + - added flag for addTab(...)-method indicating whether to use given + text as caption or just as tooltip + - reduced #ifdef QT4-macros by defining some names when compiling + with Qt3 + + * include/setup_dialog.h: + * src/core/setup_dialog.cpp: + added help-button in audio-setup-tab and improved geometry-stuff + + * include/audio_alsa.h: + * include/audio_oss.h: + * src/audio/audio_alsa.cpp: + * src/audio/audio_oss.cpp: + added probeDevice()-method which eases determining device-name in + either actual audio-class as well as in it's setup-widget + +2005-09-11 Tobias Doerffel + + * src/core/mixer.cpp: + use new audio-settings-technology for opening audio-device + + * include/setup_dialog.h: + * src/core/setup_dialog.cpp: + added setup-dialog for general-, audio- and MIDI-settings + + * include/audio_alsa.h: + * include/audio_device.h: + * include/audio_dummy.h: + * include/audio_jack.h: + * include/audio_oss.h: + * include/audio_sdl.h: + * src/audio/audio_alsa.cpp: + * src/audio/audio_device.cpp: + * src/audio/audio_jack.cpp: + * src/audio/audio_oss.cpp: + * src/audio/audio_sdl.cpp: + added setup-widget-technology making it possible to have audio-device- + specific setup-widgets which can be used in setup-dialog etc. + + * include/lcd_spinbox.h: + * src/widgets/lcd_spinbox.cpp: + added stepping-functionality + + * include/tab_widget.h: + * src/widgets/tab_widget.cpp: + pass caption to constructor instead of using fixed text + + * include/tab_bar.h: + * src/widgets/tab_bar.cpp: + - added direction-parameter in constructor + - added exclusive-property + - addTab(...) returns now pointer to tab-button which gives caller + possibility to modify the button + + * src/core/mixer.cpp: + save frames-per-audio-buffer-value in ctor if not set yet instead + of saving it in dtor + + * src/lib/embed.cpp: + do not return null-pixmap if pixmap not found + +2005-09-10 Tobias Doerffel + + * resources/setup_audio.png: + * resources/setup_general.png: + * resources/setup_midi.png: + added icons to be used in setup-dialog + + * src/audio/audio_alsa.cpp: + * src/audio/audio_sample_recorder.cpp: + use auto-cleaning pointer in createSampleBuffer(...) (fixes memory-leak + in ALSA-driver) + + * include/buffer_allocator.h: + added simple class for auto-cleaning pointers + + * src/core/mixer.cpp: + - added delete-calls in mixer::tryAudioDevices() + - init s_instanceOfMe-member in dtor, otherwise LMMS hangs up + + * locale/: + * artwork/: + moved translations and some artwork-stuff into locale- and artwork- + directory which is going to be installed into + LMMS-data-dir (/usr/share/lmms etc.) and shrinks executable + + * src/lib/embed.cpp: + added possibility to have resources in external files instead of being + linked into executable + + * configure.in: + - use macro AS_HELP_STRING for formatting help-strings + - removed --with-latency option as it is not needed anymore + +2005-09-06 Alireza + + * resources/ir.ts: + added Persian translation + +2005-09-04 Tobias Doerffel + + * src/audio/audio_oss.cpp: + - read device-name from config-file before evaluating environment- + variables etc. + - fixed bugs with device-probing (after dev-file was searched, there + was no open(...)-call again so it always failed if the first open(...) + didn't work + + * src/audio/audio_alsa.cpp: + read device-name from config-file before evaluating environment- + variables or using "default" + + * src/core/lmms_main_win.cpp: + call bufferAllocator::cleanUp( 0 ) for destroying all allocated buffers + + * include/buffer_allocator.h: + * src/lib/buffer_allocator.cpp: + - added clean-up-technique which removes least used buffers if + buffer-size exceeds limits (which depends on mixer's buffer-size) + - added field to bufDesc indicating usage of this buf - used by + alloc-method for determining most used, free and matching buffer + + * include/lcd_spinbox.h: + * src/widgets/lcd_spinbox.cpp: + made lcdSpinBox controlable by scroll-wheel + + * src/widgets/knob.cpp: + do not center knob if width of label is greater than actual knob + + * src/audio/audio_sample_recorder.cpp: + use bufferAllocator instead of new[]/delete[] + + * src/core/mixer.cpp: + - read value for frames-per-audio-buffer out of config-file and save + it at exit + - free buffer at exit + + * include/config_mgr.h: + * src/core/config_mgr.cpp: + added value() and setValue()-method which offers all components of LMMS + an easy way for loading/saving their settings at startup/exit + +2005-09-02 Mario Izquierdo +2005-09-02 Johnny Saenz + + * resources/es.ts: + added Spanish translation + +2005-09-02 Tobias Doerffel + + * most files: + finished changes began on 2005-08-29 + + * include/buffer_allocator.h: + * src/lib/buffer_allocator.cpp: + - only return aligned pointers + - small changes in API + + * resources/de.ts: + completed German translation + +2005-08-29 Tobias Doerffel + + * most files: + begin to use new bufferAllocator instead of old buffer-types and + use mixer-method for determining current number of frames per + audio-buffer + + * include/mixer.h: + types audioBuffer and surroundAudioBuffer don't exist anymore as well + as the constant FRAMES_PER_AUDIO_BUFFER - use + mixer::inst()->framesPerAudioBuffer() instead + +2005-08-28 Tobias Doerffel + + * include/song_editor.h: + * src/core/song_editor.cpp: + replaced QSpinBox by lcdSpinBox for BPM-spinbox + + * include/tab_bar.h: + * src/widgets/tab_bar.cpp: + removed function-parameter _font_size for tabBar::addTab(...) + + * most files: + - replaced QFont::setPointSize()-calls by according pointSize()-calls + which makes LMMS usable at any DPI-resolution + - changes for Qt4-compatibility + + * include/misc.h: + * include/template.h: + moved misc.h to template.h and added template-function pointSize + which sets a DPI-independent size for a given font + + * src/tracks/channel_track.cpp: + use labeling-feature of LCD-spinbox and removed old QLabel and + layouting-stuff + + * include/lcd_spinbox.h: + * src/widgets/lcd_spinbox.cpp: + - fixed bug: when moving mouse up, value was not updated properly + - added labeling-feature + - added valueChanged()-signal + + * src/core/arp_and_chords_tab_widget.cpp: + set m_arpDirection correct when loading older settings + +2005-08-27 Tobias Doerffel + + * include/channel_track.h: + * src/tracks/channel_track.cpp: + use new LCD-spinbox for effect-spinbox + + * include/lcd_spinbox.h: + * src/widgets/lcd_spinbox.cpp: + added LCD-spinbox, an improved QLCDNumber with ability of user-input + + * src/core/main.cpp: + changed highlight- and text-color to dark white instead of glaring green + for many people didn't like it... + + * include/audio_jack.h: + * src/audio/audio_jack.cpp: + - read and observe buf-size from JACK, so that writeBufferToDev() can + wait accordingly to it - now JACK-transport also works if buf-sizes + of LMMS and JACK are different + - also free buffers in destructor and not just throw them out of list + +2005-08-26 Tobias Doerffel + + * include/audio_jack.h: + * src/audio/audio_jack.cpp: + - also work with older versions which do not have jack_client_open() + - fixed bugs + - use bufferAllocator instead of new[] and delete[] in time-critical + functions + + * include/buffer_allocator.h: + * src/lib/buffer_allocator.cpp: + added bufferAllocator which is an optimized internal memory-manager + +2005-08-24 Tobias Doerffel + + * include/audio_jack.h: + * src/audio/audio_jack.cpp: + beginning of support for JACK-transport + + * src/audio/audio_sdl.cpp: + fixed memory leak - m_outputBuffer was not deleted in destructor + + * src/core/arp_and_chords_tab_widget.cpp: + completed usage of switchable groupboxes by saving groupbox-states + and evaluating them in processNote() + +2005-08-22 Tobias Doerffel + + * resources/afp_artwork.png: + small improvements for better usability and internationalization + + * src/soundgenerators/audio_file_processor.cpp: + - cleanups and small improvements concerning painting + - added missing tooltips + - moved open-sample-button right to filename-display-bar + + * src/widgets/pixmap_button.cpp: + changed default-pixmaps to led_yellow.png and led_off.png + + * resources/pattern_bg.png: + resized from 2x43 to 2x25 + + * src/core/name_label.cpp: + decreased default-font + + * include/track.h: + decreased size of track-settings-widget from 256 to 224 + + * include/channel_track.h: + * src/tracks/channel_track.cpp: + - use labeling-feature of knob for labeling volume-knob + - use smaller surroundArea in track-settings-widget and smaller + channel-button with smaller font + + * src/core/surround_area.cpp: + made surroundArea working with different widget-sizes than default size + + * resources/background_artwork.png: + made bg-image more pale + + * include/bb_editor.h: + * src/core/bb_editor.cpp: + changed painting-behaviour and made button-bg-gfx's obsolete by using + specialBgHandlingWidget + + * include/groupbox.h: + * src/widgets/groupbox.cpp: + improved groupbox by adding animation and more + + * src/core/envelope_and_lfo_widget.cpp: + * src/core/envelope_tab_widget.cpp: + - use knobs new labeling-feature + - use new tab-widget for target-widgets + + * include/tab_widget.h: + * src/widgets/tab_widget.cpp: + added own tab-widget which looks much cooler than Qt's one + + * src/core/arp_and_chords_tab_widget.cpp: + - using now new groupbox-widget and reorganized knob/switch-positions + - revised arpeggio-direction-buttons + - use knobs new labeling-feature + - removed obsolete headers + + * include/knob.h: + * src/widgets/knob.cpp: + added labeling-feature for automatically labeling knobs with given + text + + +2005-08-21 Tobias Doerffel + + * src/widgets/side_bar_widget.cpp: + * src/tracks/channel_track.cpp: + * src/core/config_mgr.cpp: + * src/core/piano_roll.cpp: + * src/core/arp_and_chords_tab_widget.cpp: + * src/core/main.cpp: + * src/core/browser.cpp: + replaced painting-device-dependent method setPixelSize by setPointSize + + * src/core/envelope_and_lfo_widget.cpp: + now using new group-box which looks much cooler and is more usable + + * include/groupbox.h: + * src/widgets/groupbox.cpp: + added own groupbox-widget with state-LED + + * src/widgets/pixmap_button.cpp: + delete m_bgPixmap in destructor + + * include/spc_bg_hndl_widget.h: + - made member m_backgroundPixmap to normal QPixmap instead of pointer to + it which also fixes a memory leak as dtor didn't delete the pixmap + - also use erase-pixmap of widget if available + + * include/oscillator.h: + - made shape-functions using phase()-function for determining phase of + sample + - made oscillator::phase() using modff instead of floorf-math which + makes the whole thing up to 20% faster + + * src/core/about_dialog.cpp: + added URL of project-page of LMMS + + * src/core/song_editor.cpp: + create new project if file given in cmd-line is not read-/usable + + * include/export_project_dialog.h: + * include/song_editor.h: + * src/core/main.cpp: + * src/core/song_editor.cpp: + added --render command-line-switch to render file and quit as soon as + job is done - very good for benchmarks + + * src/core/main.cpp: + added --version command-line-switch + +2005-08-20 Tobias Doerffel + + * src/core/lmms_main_win.cpp: + - extended filter for sample-browser + - added home- and root-browser for being able to load songs, presets, + samples etc. from somewhere else than within lmms-working-directory + +2005-07-27 Tobias Doerffel + + * src/core/lmms_main_win.cpp: + * src/core/track_container.cpp: + * src/core/track.cpp: + by completing destructors and fixing some other small bugs there's + finally no seg-fault when quitting app! (the bug annoying for months...) + + * include/track_container.h: + all public-slots were made to normal public and FASTCALLed functions + since they aren't connected to a signal anywhere and it just blows up + qt-moc-code + + * include/sample_track.h: + made method sampleTCO::changeLength(...) FASTCALLed since it's virtual + and method in base-class is also FASTCALLed - eliminates crash when + resizing sample + + * include/track.h: + * src/core/track.cpp: + removed unsused method setTrackWidgetHeight( int ) + +2005-07-26 Pedro Lopez-Cabanillas + + * src/core/browser.cpp: + * src/core/config_mgr.cpp: + * src/core/lmms_main_win.cpp: + * src/core/main.cpp: + * src/core/song_editor.cpp: + * src/lib/mmp.cpp: + * src/tracks/pattern.cpp: + * src/widgets/project_notes.cpp: + small changes for compatibility with Qt 3.1 + + * src/midi/midi_alsa.cpp: + * src/midi/midi_oss.cpp: + added MIDIDEV-environment variable for being able to choose a MIDI + device different from the default one + +2005-07-24 Tobias Doerffel + + * include/midi_mapper.h: + * src/midi/midi_mapper.cpp: + added MIDI-mapper to be used in combination with midiDevice's and/or + MIDI-Out-Plugin which can read MIDI-maps added before + + * midi-maps/*map: + added MIDI-maps from KMid + + * include/midi_dummy.h: + * src/midi/midi_device.cpp: + added dummy MIDI-driver for not crashing if no MIDI-device is available + + * src/core/piano_roll.cpp: + - go to start of pattern when pressing home-key + - added support for zooming + + * src/core/song_editor.cpp: + - fixed bug with loop-points by setting current-frame-var to zero to + force reset of it + - added auto-scrolling ability + - fixed bug which caused horizontal scrollbar to have an undefined + max-value after starting up LMMS + - go to start of pattern when pressing home-key + +2005-07-23 Alexey Kouznetsov + + * resources/ru.ts: + added Russian translation + +2005-07-23 Tobias Doerffel + + * src/core/piano_roll.cpp: + scroll back when stopping and play-position changes to invisible area + + * include/time_line.h: + * src/core/song_editor.cpp: + * src/core/time_line.cpp: + added control-buttons to time-line making it possible to control + things like auto-scrolling, looping and play-position after stop + + * src/midi/midi_device.cpp: + limit volume between 0 and 127 when sending out note-on-event + +2005-07-21 Tobias Doerffel + + * include/nstate_button.h: + * src/widgets/nstate_button.cpp: + added widget nStateButton which is a button able to have n different + states with according pixmaps and tooltips + + * resources/back_to_zero.png: + * resources/keep_stop_position.png: + * resources/back_to_start.png: + * resources/loop_points_off.png: + * resources/loop_points_on.png: + * resources/autoscroll_off.png: + * resources/autoscroll_on.png: + added pixmaps for buttons to be used in time-line as soon as possible + + * lmms-0.1.0rc1.tar.bz2: + release of Release Candidate 1 of LMMS 0.1.0 + +2005-07-20 Tobias Doerffel + + * src/midi/midi_file.cpp: + - proper handling of note-on-events which velocity 0 -> treat like + note-off-events makes it now possible to import a much larger amount + of MIDI-files + - added progress-dialog when importing MIDI-file + + * src/core/song_editor.cpp: + horizontal scrollbar is now placed some pixels above lower edge so that + it doesn't take action when resizing song-editor-window at lower edge + + * src/core/track_container.cpp: + added progress-dialog for loading project and according + qApp->processEvents()-call -> LMMS now isn't blocked anymore while + loading project + + * include/knob.h: + * src/widgets/knob.cpp: + fixed bugs caused by changes made before for scroll-wheel-support + +2005-07-19 Tobias Doerffel + + * include/knob.h: + * src/widgets/knob.cpp: + added better scroll-wheel support which increases/decreases knob-value + by 20th of range (e.g 0.05 if knob is ranged between 0 and 1) + +2005-07-17 Tobias Doerffel + + * projects/: + added "covers"-dir and added "Preludium and Fuge A-Minor" by J.S.Bach + to it as first covered project ;-) + + * src/core/note_play_handle.cpp: + - fixed bad bug in notePlayHandle::play(): instead of directly being + deleted, sub-notes were given to mixer to be removed when they played + completely but since these sub-notes are not known to mixer, it + couldn't delete them + - always call channelTrack::deleteNotePluginData() in + noteplayhandle-dtor even if pointer to note-plugin-data is NULL so + that all plugins are notified when note is done (midiOut-plugin + needs that for sending note-off-events!) + + * include/midi_out.h: + * src/soundgenerators/midi_out.cpp: + new plugin midiOut which sends all notes on this channel out to + external midi-device this is especially cool for playing arpeggio for + key pressed on external midi-device!! + + * src/soundgenerators/plucked_string_synth.cpp: + removed unneccesary inclusion of qpainter-header + + * include/midi_file.h: + * src/midi/midi_file.cpp: + * src/core/lmms_main_win.cpp: + * src/core/song_editor.cpp: + added support for importing MIDI-file (very experimental!!) + + * include/midi.h: + * include/midi_device.h: + moved general MIDI-stuff from midi_device.h to midi.h + + * src/audio/audio_file_device.cpp: + added missing arg()-call to QMessageBox::critical()-call in + audioFileDevice-ctor + +2005-07-16 Tobias Doerffel + + * src/core/config_mgr.cpp: + added shortcuts to navigation-buttons + +2005-07-14 Tobias Doerffel + + * src/core/piano_roll.cpp: + only play notes while editing if we're not in play-mode + + * include/song_editor.h: + * src/core/song_editor.cpp: + when exporting always render one additional tact at the end so that + we don't cut off notes with delay etc. + +2005-07-12 Tobias Doerffel + + - successful compilation with GCC 4.0.1 ;-) + + * src/core/arp_and_chords_tab_widget.cpp: + fixed bug responsible for playing base-notes of chords all time + if any arpeggio selected was selected + +2005-07-11 Tobias Doerffel + + * resources/source/wizard_files.xcf: + * resources/wizard_files.png: + updated project-file-icon in image + + * src/core/config_mgr.cpp: + - ask user whether to re-run setup-wizard if config-file indicates + different version of LMMS + - removed redundant assignment in configManager::loadConfigFile() + - added additional layout for having space at the left and right side + - increased font-size of title + + * Makefile.am: + install LMMS-icon into LMMS-data-dir + + * lmms-0.1.0beta.tar.bz2: + made beta-release of 0.1.0 and announced it on kde-apps.org + +2005-07-10 Tobias Doerffel + + * presets/TripleOscillator/: + added cool Church Organ preset and improved Xylophon preset + + * resources/de.ts: + updated German translations and cleaned up all obsolete translations + and context which shrinks the file by 50K... + + * samples/instruments/: + removed vibraphone01.ogg because of poor quality + + * src/core/piano_roll.cpp: + - do not set what's-this-texts as tooltips for play/stop-button + - added tooltip and what's-this-text to record-button + - display message in piano-roll how to open a pattern if none is opened + - always set window-title, even if no pattern is opened + + * src/core/mixer.cpp: + using iterators in second loop in mixer::run() leads to crashes, so + an optimized version of the old code is used now again... + + * src/core/lmms_main_win.cpp: + added tool-buttons for new project, open/save project etc. and added + popup-menu to new-button containing projects in template-directory + + * include/song_editor.h: + * src/core/song_editor.cpp: + - changed remaining method-names containing "[Ss]ong" to "[Pp]roject" + - toggle channel-button after adding channel-track + + * include/export_project_dialog.h: + * src/core/export_project_dialog.cpp: + renamed from export_song_dialog.ext to export_project_dialog.ext + +2005-07-09 Tobias Doerffel + + * all files: + now all source-code-files but some single exceptions do not have more + than 80 characters per line and also were made to conform one + programming-style + + * include/sgs: + wrote sane soundgenerator-plugin-scanner using bash-scripting which + replaces old, dirty and exaggerated c++-program and is also fully + portable + + * include/lmms_main_win.h: + * include/song_editor.h: + * src/core/lmms_main_win.cpp: + * src/core/song_editor.cpp: + renamed methods newFile, openFile etc. to createNewProject, openProject + etc. + + * resources/source/: + - renamed filenew.png, fileopen.png etc. to project_new.png etc. + - replaced song_file.png by project_file.png which has a new file-icon + with LMMS-logo inside + - replaced preset_file.png by new graphic where the star is inside + of a file-icon + + * resources/source/lmms_logo.svg: + made logo Inkscape 0.41 compatible (had problems with gradients since + logo originally had been made with Inkscape 0.39) + + * src/widgets/project_notes.cpp: + removed unneccessary name-strings for QActions + + * resources/project_notes.png: + replaced 16x16-icon with 22x22-icon to provide big enough icon for + toolbar which needs 22x22-icons + + * src/core/bb_editor.cpp: + * src/core/song_editor.cpp: + * src/tracks/bb_track.cpp: + do not crash if either there're no bb-tracks but bb-editor has tracks + or there're bb-tracks but bb-editor contains no tracks + + * src/core/mixer.cpp: + * src/core/note_play_handle.cpp: + * src/core/track_container.cpp: + * src/tracks/pattern.cpp: + rewrote loops using vectors to use iterators instead of indices etc. + which makes it all a bit smaller and faster since we do not always have + to access the current element using the []-operator + +2005-07-08 Tobias Doerffel + + * src/core/envelope_tab_widget.cpp: + * src/lib/sample_buffer.cpp: + * src/widgets/knob.cpp: + small fixes for making compilable with qt4 + + * configure.in: + * projects/: + added directories "tutorials" and "recorded_loops" + +2005-07-07 Tobias Doerffel + + * include/bb_track.h: + added FASTCALL to moveLocation(...) and changeLength(...) which fixes + crash-bug + + * src/core/song_editor.cpp: + do not confirm overwriting file if new project was just saved + + * samples/: + added a lot of new samples from wikisource and others + +2005-07-06 Tobias Doerffel + + * include/project_notes.h: + * src/core/song_editor.cpp: + * src/widgets/project_notes.cpp: + clear project-notes when clearing song (new/open file etc.) + + * src/tracks/pattern.cpp: + ask user when he tries to freeze a pattern of a muted channel whether he + wants to continue since freezing a pattern of a muted channel makes no + sense (you're rendering silence!) + + * include/piano_roll: + * src/core/piano_roll.cpp: + - added possibility to edit the volume of notes + - removed all bool-variables indicating whether an action is being + performed at the moment and introduced m_action, indicating + one of the actions defined in (also new) enum editActions + - replaced m_startTone and m_startOctave with one variable m_startKey, + which makes a lot of code unneccessary + - rewrote loops using note-vectors to use iterators instead of + indices and vector::size() + - renamed m_evolutionHeight to m_notesEditHeight + - replaced QMouseEvent::pos().[xy]()-calls with QMouseEvent::[xy]() + - pianoRoll::getKey() does now take y-position (int) as parameter + instead of QPoint since it only needs y-position + - use new setKey()/key()-member of note everywhere where it is possible + + * include/note.h: + added setKey()- and key()-member which setting/getting absolute key + of note + +2005-07-05 Tobias Doerffel + + * configure.in: + * projects/: + categorized projects in "cool_songs", "demos", "misc" and "templates" + so that we don't annoy the user with a lot projects in his/her project- + root - have to fill up "templates" with project-templates, e.g. + basic drum-kit, accoustic drum-kit etc. + + * most files: + added FASTCALL-attributes to functions taking at least one parameter + + * include/audio_file_processor.h: + * include/plucked_string_synth.h: + * include/triple_oscillator.h: + moved deleteNotePluginData()-method into according cpp-files which + makes including note_play_handle.h obsolete + + * include/audio_file_processor.h: + * include/sample_buffer.h: + * src/lib/sample_buffer.cpp: + * src/soundgenerators/audio_file_processor.cpp: + added support for saving sample-rate-conversion-state in m_pluginData + of a note which fixes bug making clicks in sound when playing a sample + simultanously at different pitches + + * configure.in: + check whether compiler knows floorf and powf and if not, define them + to floor/pow + +2005-07-04 Tobias Doerffel + + * most files: + made LMMS compiling (and working) with Qt 3.0, GCC 2.95 and other old + libs (compiled it under Knoppix 3.1) + + * include/misc.h: + changed names of abs, min, max, limit to myAbs, myMin etc. for not + conflicting with functions of STL with the same name + + * configure.in: + * src/lib/sample_buffer.cpp: + added support for versions of libsndfile < 1.0.11 since API has changed + in libsndfile 1.0.11 + +2005-07-03 Tobias Doerffel + + * resources/control_env_amount_active.png: + * resources/control_env_amount_inactive.png: + * include/envelope_and_lfo_widget.h: + * src/core/envelope_and_lfo_widget.cpp: + it's now possible to control the amount of an envelope with the + according LFO by toggling new button (otherwise the level of the LFO is + added to the level of the envelope as it always has been) + + * src/core/envelope_and_lfo_widget.cpp: + now you can toggle the amount of an envelope/LFO between 0 and 1 by + clicking on according graph + + * all files: + updated mail-address in header + + * src/audio/audio_device.cpp: + always use SURROUND_CHANNELS as channel-count for libsrc since + resampling is done on surround-buffers - fixes bug which was + responsible for producing useless noise when exporting file in + high-quality-mode + + * src/core/export_song_dialog.cpp: + made "high-quality-mode"-switch working (disabled for some reason + in 0.0.9...) + + * src/core/mixer.cpp: + when restoring audio-device, also restore quality-level + + * include/bb_editor.h: + * src/core/bb_editor.cpp: + cleaned up a lot because there was still much code from times when + the track-technology didn't exist and which isn't now used any more + + * src/audio/audio_alsa.cpp: + added support for 48 KHz soundcards + +2005-07-02 Tobias Doerffel + + * configure.in: + * src/lib/sample_buffer.cpp: + made it possible to compile LMMS without SDL_sound, reorganized + sample-decoding and added libsndfile- and libvorbis-support + + * include/midi_time.h: + added operators "=", "+=", "-=" and implemented copy-ctor + + * include/piano_roll.h: + made currentPattern() a const method and added validPattern() + + * src/tracks/pattern.cpp: + optimized addNote() function which uses m_note.push_back() for notes + beyond last note + + * src/core/piano_roll.cpp: + * src/tracks/pattern.cpp: + when pattern is destroyed it checks whether it is currently opened in + piano-roll and if so pattern lets piano-roll set its pointer to current + pattern to NULL. piano-roll does now always check whether current + pattern is not NULL. in this case it denies any actions. fixes bug which + led to crash when having piano-roll open while deleting pattern (e.g. + creating new file, opening file etc.) + +2005-07-01 Tobias Doerffel + + * all files: + replaced "location" in names of functions/variables by "position" + for more clear and unified names + + * include/piano_roll.h: + * include/timeline.h: + * src/core/piano_roll.cpp: + * src/core/timeline.cpp: + added support for autoscroll in piano-roll when playing/recording + pattern + +2005-06-30 Tobias Doerffel + + * include/channel_track.h: + * include/midi_device.h + * include/mixer.h: + * include/pattern.h: + * src/core/piano_roll.cpp: + * src/midi/midi_device.cpp: + * src/tracks/channel_track.cpp: + added support for recording notes from MIDI-device (e.g. MIDI-keyboard + or virtual channel-piano) in a pattern using the piano-roll + + * include/pattern.h + * src/core/song_editor.cpp: + fixed bug which led to crash: when pattern was played in non-looping + mode song-editor always called pattern::finishFreeze() even if there + actually was no pattern-freezing-process -> added isFreezing() which + is called by song-editor for checking whether pattern is being freezed + before calling finishFreeze() + + * src/core/lmms_main_win.cpp: + * src/lib/sample_buffer.cpp: + when showing open-file-dialog, use QFileDialog::selectedFiles()[0] + instead of QFileDialog::selectedFile() because selectedFile() returns + incorrect file-name if user clicked on the file and then on + "Open"-button instead of double-clicking file + + * src/audio/audio_oss.cpp: + * src/soundgenerators/triple_oscillator.cpp: + * src/tracks/channel_track.cpp: + added #include for solving problems with undeclared + functions getenv() and rand() on some distributions (e.g Slackware) + + * src/core/track.cpp: + * src/soundgenerators/triple_oscillator.cpp: + * src/tracks/channel_track.cpp: + added missing #include "debug.h" for solving problems with undeclared + assert() because of not included assert.h + +2005-06-29 Tobias Doerffel + + * configure.in: + * include/audio_sdl.h: + * src/lib/sample_buffer.cpp: + if SDL/SDL.h does not exist, search for it in SDL10, SDL11 etc. and + include according headers in source-files (solves problems on FreeBSD) + + * README: + updated everything a bit... + +2005-06-28 Tobias Doerffel + + * include/envelope_tab_widget.h: + * src/core/envelope_tab_widget.cpp: + use button instead of knob for turning filter on/off + +2005-06-27 Tobias Doerffel + + * samples/effects/Makefile.am: + don't install effect-samples into directory of basses-samples + + diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000000..b42a17ac46 --- /dev/null +++ b/INSTALL @@ -0,0 +1,182 @@ +Basic Installation +================== + + These are generic installation instructions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, a file +`config.cache' that saves the results of its tests to speed up +reconfiguring, and a file `config.log' containing compiler output +(useful mainly for debugging `configure'). + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If at some point `config.cache' +contains results you don't want to keep, you may remove or edit it. + + The file `configure.in' is used to create `configure' by a program +called `autoconf'. You only need `configure.in' if you want to change +it or regenerate `configure' using a newer version of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. If you're + using `csh' on an old version of System V, you might need to type + `sh ./configure' instead to prevent `csh' from trying to execute + `configure' itself. + + Running `configure' takes awhile. While running, it prints some + messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. You can give `configure' +initial values for variables by setting them in the environment. Using +a Bourne-compatible shell, you can do that on the command line like +this: + CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure + +Or on systems that have the `env' program, you can do it like this: + env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you must use a version of `make' that +supports the `VPATH' variable, such as GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + If you have to use a `make' that does not supports the `VPATH' +variable, you have to compile the package for one architecture at a time +in the source code directory. After you have installed the package for +one architecture, use `make distclean' before reconfiguring for another +architecture. + +Installation Names +================== + + By default, `make install' will install the package's files in +`/usr/local/bin', `/usr/local/man', etc. You can specify an +installation prefix other than `/usr/local' by giving `configure' the +option `--prefix=PATH'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +give `configure' the option `--exec-prefix=PATH', the package will use +PATH as the prefix for installing programs and libraries. +Documentation and other data files will still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=PATH' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + + There may be some features `configure' can not figure out +automatically, but needs to determine by the type of host the package +will run on. Usually `configure' can figure that out, but if it prints +a message saying it can not guess the host type, give it the +`--host=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name with three fields: + CPU-COMPANY-SYSTEM + +See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the host type. + + If you are building compiler tools for cross-compiling, you can also +use the `--target=TYPE' option to select the type of system they will +produce code for and the `--build=TYPE' option to select the type of +system on which you are compiling the package. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Operation Controls +================== + + `configure' recognizes the following options to control how it +operates. + +`--cache-file=FILE' + Use and save the results of the tests in FILE instead of + `./config.cache'. Set FILE to `/dev/null' to disable caching, for + debugging `configure'. + +`--help' + Print a summary of the options to `configure', and exit. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--version' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`configure' also accepts some other, not widely useful, options. diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..240109e332 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,304 @@ +AUTOMAKE_OPTIONS = foreign 1.4 + +dist-hook: + rm -rf `find $(distdir) -name \*.moc` + + +install-exec-hook: + cd $(DESTDIR)$(bindir) ; \ + strip lmms + + +if HAVE_RPM +$(PACKAGE)-$(VERSION).tar.gz: dist + +# Rule to build RPM distribution package +rpm: $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE).spec + cp $(PACKAGE)-$(VERSION).tar.gz @RPMSOURCEDIR@ + rpmbuild -ba $(PACKAGE).spec +endif + + +noinst_PROGRAMS= bin2res +bin2res_SOURCES = buildtools/bin2res.cpp + + +INCLUDES = -I$(srcdir)/include -I. +bin_PROGRAMS = lmms + + + +AM_CXXFLAGS := $(AM_CXXFLAGS) $(QT_CXXFLAGS) + + +%.moc: $(srcdir)/include/%.h + $(MOC) -o $@ $< + +%.ts: + $(LUPDATE) $(lmms_SOURCES) -ts locale/$@ + +%.qm: %.ts + $(LRELEASE) $< + + + +lmms_MOC = \ + ./about_dialog.moc \ + ./arp_and_chords_tab_widget.moc \ + ./audio_file_processor.moc \ + ./bb_editor.moc \ + ./bb_track.moc \ + ./browser.moc \ + ./channel_track.moc \ + ./config_mgr.moc \ + ./crystal_button.moc \ + ./envelope_and_lfo_widget.moc \ + ./envelope_tab_widget.moc \ + ./export_project_dialog.moc \ + ./kmultitabbar.moc \ + ./group_box.moc \ + ./knob.moc \ + ./lcd_spinbox.moc \ + ./lmms_main_win.moc \ + ./midi_out.moc \ + ./mixer.moc \ + ./name_label.moc \ + ./nstate_button.moc \ + ./pattern.moc \ + ./piano_roll.moc \ + ./piano_widget.moc \ + ./pixmap_button.moc \ + ./plucked_string_synth.moc \ + ./project_notes.moc \ + ./rename_dialog.moc \ + ./sample_buffer.moc \ + ./sample_track.moc \ + ./setup_dialog.moc \ + ./side_bar.moc \ + ./side_bar_widget.moc \ + ./song_editor.moc \ + ./sound_generator.moc \ + ./surround_area.moc \ + ./tab_bar.moc \ + ./tab_button.moc \ + ./tab_widget.moc \ + ./timeline.moc \ + ./track_container.moc \ + ./track.moc \ + ./triple_oscillator.moc \ + ./visualization_widget.moc + + +BUILT_SOURCES = $(lmms_MOC) + +#lmms_TRANSLATIONS = $(wildcard $(srcdir)/locale/*.qm $(srcdir)/locale/*.ts) + +lmms_EMBEDDED_RESOURCES = $(wildcard $(srcdir)/resources/*png AUTHORS COPYING) + +./embedded_resources.h: $(lmms_EMBEDDED_RESOURCES) bin2res + $(top_builddir)/bin2res $(lmms_EMBEDDED_RESOURCES) > $@ + +./embed.o: ./embedded_resources.h + + +lmms_SOURCES = \ + $(srcdir)/src/audio/audio_alsa.cpp \ + $(srcdir)/src/audio/audio_device.cpp \ + $(srcdir)/src/audio/audio_file_device.cpp \ + $(srcdir)/src/audio/audio_file_ogg.cpp \ + $(srcdir)/src/audio/audio_file_wave.cpp \ + $(srcdir)/src/audio/audio_jack.cpp \ + $(srcdir)/src/audio/audio_oss.cpp \ + $(srcdir)/src/audio/audio_sample_recorder.cpp \ + $(srcdir)/src/audio/audio_sdl.cpp \ + $(srcdir)/src/core/about_dialog.cpp \ + $(srcdir)/src/core/arp_and_chords_tab_widget.cpp \ + $(srcdir)/src/core/bb_editor.cpp \ + $(srcdir)/src/core/browser.cpp \ + $(srcdir)/src/core/config_mgr.cpp \ + $(srcdir)/src/core/envelope_and_lfo_widget.cpp \ + $(srcdir)/src/core/envelope_tab_widget.cpp \ + $(srcdir)/src/core/export_project_dialog.cpp \ + $(srcdir)/src/core/lmms_main_win.cpp \ + $(srcdir)/src/core/main.cpp \ + $(srcdir)/src/core/mixer.cpp \ + $(srcdir)/src/core/name_label.cpp \ + $(srcdir)/src/core/note.cpp \ + $(srcdir)/src/core/note_play_handle.cpp \ + $(srcdir)/src/core/piano_roll.cpp \ + $(srcdir)/src/core/piano_widget.cpp \ + $(srcdir)/src/core/plugin_management.cpp \ + $(srcdir)/src/core/preset_preview_play_handle.cpp \ + $(srcdir)/src/core/sample_play_handle.cpp \ + $(srcdir)/src/core/setup_dialog.cpp \ + $(srcdir)/src/core/song_editor.cpp \ + $(srcdir)/src/core/sound_generator.cpp \ + $(srcdir)/src/core/track.cpp \ + $(srcdir)/src/core/track_container.cpp \ + $(srcdir)/src/core/surround_area.cpp \ + $(srcdir)/src/core/timeline.cpp \ + $(srcdir)/src/lib/buffer_allocator.cpp \ + $(srcdir)/src/lib/clipboard.cpp \ + $(srcdir)/src/lib/embed.cpp \ + $(srcdir)/src/lib/mmp.cpp \ + $(srcdir)/src/lib/oscillator.cpp \ + $(srcdir)/src/lib/sample_buffer.cpp \ + $(srcdir)/src/midi/midi_alsa_raw.cpp \ + $(srcdir)/src/midi/midi_device.cpp \ + $(srcdir)/src/midi/midi_file.cpp \ + $(srcdir)/src/midi/midi_mapper.cpp \ + $(srcdir)/src/midi/midi_oss.cpp \ + $(srcdir)/src/soundgenerators/audio_file_processor.cpp \ + $(srcdir)/src/soundgenerators/midi_out.cpp \ + $(srcdir)/src/soundgenerators/plucked_string_synth.cpp \ + $(srcdir)/src/soundgenerators/triple_oscillator.cpp \ + $(srcdir)/src/tracks/bb_track.cpp \ + $(srcdir)/src/tracks/channel_track.cpp \ + $(srcdir)/src/tracks/pattern.cpp \ + $(srcdir)/src/tracks/sample_track.cpp \ + $(srcdir)/src/widgets/crystal_button.cpp \ + $(srcdir)/src/widgets/group_box.cpp \ + $(srcdir)/src/widgets/kmultitabbar.cpp \ + $(srcdir)/src/widgets/knob.cpp \ + $(srcdir)/src/widgets/lcd_spinbox.cpp \ + $(srcdir)/src/widgets/led_checkbox.cpp \ + $(srcdir)/src/widgets/nstate_button.cpp \ + $(srcdir)/src/widgets/pixmap_button.cpp \ + $(srcdir)/src/widgets/project_notes.cpp \ + $(srcdir)/src/widgets/rename_dialog.cpp \ + $(srcdir)/src/widgets/side_bar_widget.cpp \ + $(srcdir)/src/widgets/tab_bar.cpp \ + $(srcdir)/src/widgets/tab_widget.cpp \ + $(srcdir)/src/widgets/text_float.cpp \ + $(srcdir)/src/widgets/tooltip.cpp \ + $(srcdir)/src/widgets/visualization_widget.cpp \ + $(srcdir)/include/pch.h \ + $(srcdir)/include/debug.h \ + $(srcdir)/include/lmms_main_win.h \ + $(srcdir)/include/audio_alsa.h \ + $(srcdir)/include/audio_device.h \ + $(srcdir)/include/audio_dummy.h \ + $(srcdir)/include/audio_file_device.h \ + $(srcdir)/include/audio_file_ogg.h \ + $(srcdir)/include/audio_file_wave.h \ + $(srcdir)/include/audio_jack.h \ + $(srcdir)/include/audio_oss.h \ + $(srcdir)/include/audio_sample_recorder.h \ + $(srcdir)/include/audio_sdl.h \ + $(srcdir)/include/interpolation.h \ + $(srcdir)/include/mixer.h \ + $(srcdir)/include/pattern.h \ + $(srcdir)/include/channel_track.h \ + $(srcdir)/include/note.h \ + $(srcdir)/include/volume.h \ + $(srcdir)/include/panning.h \ + $(srcdir)/include/song_editor.h \ + $(srcdir)/include/sound_generator.h \ + $(srcdir)/include/audio_file_processor.h \ + $(srcdir)/include/midi_time.h \ + $(srcdir)/include/bb_editor.h \ + $(srcdir)/include/piano_widget.h \ + $(srcdir)/include/effect_board.h \ + $(srcdir)/include/pixmap_button.h \ + $(srcdir)/include/triple_oscillator.h \ + $(srcdir)/include/settings.h \ + $(srcdir)/include/plucked_string_synth.h \ + $(srcdir)/include/rename_dialog.h \ + $(srcdir)/include/export_project_dialog.h \ + $(srcdir)/include/note_play_handle.h \ + $(srcdir)/include/piano_roll.h \ + $(srcdir)/include/basic_filters.h \ + $(srcdir)/include/envelope_tab_widget.h \ + $(srcdir)/include/envelope_and_lfo_widget.h \ + $(srcdir)/include/about_dialog.h \ + $(srcdir)/include/oscillator.h \ + $(srcdir)/include/crystal_button.h \ + $(srcdir)/include/arp_and_chords_tab_widget.h \ + $(srcdir)/include/export.h \ + $(srcdir)/include/group_box.h \ + $(srcdir)/include/tab_widget.h \ + $(srcdir)/include/knob.h \ + $(srcdir)/include/browser.h \ + $(srcdir)/include/templates.h \ + $(srcdir)/include/surround_area.h \ + $(srcdir)/include/kmultitabbar.h \ + $(srcdir)/include/side_bar.h \ + $(srcdir)/include/side_bar_widget.h \ + $(srcdir)/include/track.h \ + $(srcdir)/include/track_container.h \ + $(srcdir)/include/bb_track.h \ + $(srcdir)/include/sample_track.h \ + $(srcdir)/include/sample_buffer.h \ + $(srcdir)/include/name_label.h \ + $(srcdir)/include/play_handle.h \ + $(srcdir)/include/mmp.h \ + $(srcdir)/include/midi_device.h \ + $(srcdir)/include/midi_file.h \ + $(srcdir)/include/midi_alsa_raw.h \ + $(srcdir)/include/midi_oss.h \ + $(srcdir)/include/clipboard.h \ + $(srcdir)/include/types.h \ + $(srcdir)/include/qt3support.h \ + $(srcdir)/include/embed.h \ + $(srcdir)/include/timeline.h \ + $(srcdir)/include/config_mgr.h \ + $(srcdir)/include/spc_bg_hndl_widget.h \ + $(srcdir)/include/tab_bar.h \ + $(srcdir)/include/tab_button.h \ + $(srcdir)/include/project_notes.h \ + $(srcdir)/include/visualization_widget.h \ + $(srcdir)/include/endian_handling.h \ + $(srcdir)/include/preset_preview_play_handle.h \ + $(srcdir)/include/sample_play_handle.h \ + $(srcdir)/include/midi.h \ + $(srcdir)/include/midi_out.h \ + $(srcdir)/include/nstate_button.h \ + $(srcdir)/include/midi_dummy.h \ + $(srcdir)/include/midi_mapper.h \ + $(srcdir)/include/buffer_allocator.h \ + $(srcdir)/include/lcd_spinbox.h \ + $(srcdir)/include/tooltip.h \ + $(srcdir)/include/led_checkbox.h \ + $(srcdir)/include/text_float.h \ + $(srcdir)/include/setup_dialog.h + + +EXTRA_DIST = \ + $(lmms_EMBEDDED_RESOURCES) \ + $(srcdir)/include/sgs + + +CLEANFILES = $(lmms_MOC) ./embedded_resources.h + +if HAVE_LIBSDL +LIB_SDL_LDADD = -lSDL +endif + +if HAVE_LIBSDL_SOUND +LIB_SDL_SOUND_LDADD = -lSDL_sound +endif + +if HAVE_LIBASOUND +LIB_ASOUND_LDADD = -lasound +endif + +if HAVE_LIBJACK +LIB_JACK_LDADD = -ljack +endif + +if HAVE_LIBVORBIS +LIB_VORBIS_LDADD = -lvorbis -lvorbisenc -lvorbisfile +endif + +if HAVE_LIBSRC +LIB_SRC_LDADD = -lsamplerate +endif + +if HAVE_LIBSF +LIB_SF_LDADD = -lsndfile +endif + +lmms_LDADD = $(QT_LDADD) $(LIB_SDL_LDADD) $(LIB_ASOUND_LDADD) $(LIB_JACK_LDADD) $(LIB_SDL_SOUND_LDADD) $(LIB_VORBIS_LDADD) $(LIB_SRC_LDADD) $(LIB_SF_LDADD) + +SUBDIRS = artwork locale midi-maps presets projects samples + diff --git a/README b/README new file mode 100644 index 0000000000..bed7ce9ac4 --- /dev/null +++ b/README @@ -0,0 +1,97 @@ +Linux MultiMedia Studio 0.1.0 +============================= + +Copyright (c) 2004-2005 by Tobias Doerffel + + +The whole program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 2 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more +details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., 59 Temple +Place - Suite 330, Boston, MA 02111-1307, USA + + + +What is LMMS?? +-------------- + +LMMS aims to be a free alternative to popular (but commercial and closed- +source) programs like FruityLoops, Cubase and Logic giving you the ability of +producing music with your computer by creating/synthesizing sounds, arranging +samples, playing live with keyboard and much more... + +LMMS combines the features of a tracker-/sequencer-program (pattern-/channel-/ +sample-/song-/effect-management) and those of powerful synthesizers and +samplers in a modern, user-friendly and easy to use graphical user-interface. + +LMMS is still in heavy development, so with this version please don't expect a +complete, ready and bugfree program!! + + + +Requirements +------------ + +The most important requirement is for sure a fast computer, so don't try to get +LMMS working on a pentium one with 60 MHz... ;-) So you should have at least +500 MHz, but for really enjoying LMMS less than 1 GHz makes no sense... +(LMMS is currently developed on a PIII 450 MHz...!!) + +Required libraries are: + +- Qt 3.2 or higher (tested up to 4.0.0) with devel-files +- SDL_sound (tested with 0.1.5 & 1.0.1) with devel-files + +Optional, but strongly recommended: +- libvorbis with devel-files +- libalsa with devel-files +- SDL with devel-files +- libsamplerate with devel-files + +For compiling you should have an up to date GCC with g++. +LMMS has been (successfully) tested under Debian Sarge 3.1, Fedora Core 2-4, +and SuSE Linux 9.0-9.3 with Qt 3.[23].x and Qt 4.0.0. +It was compiled using GCC 3.3.x, GCC 3.4.x and GCC 4.0.0. + +If you have problems with compiling or running LMMS, find any bug or have +suggestions and so on, please feel free to e-mail me (for mail-address see +below)! + + + + +Help developing LMMS +-------------------- + +If you are interested in LMMS, it's programming, artwork, testing, writing +demo-songs, (and improving this f**** README...) or something like that, +you're welcome to participate on the development of LMMS! + +Just e-mail me: tobydox@users.sourceforge.net + +The project-homepage of LMMS and mailingslists can be found at +http://lmms.sourceforge.net + + +Before coding a new big feature, please always post your idea and suggestions +about your feature and about the actual implementation to the +LMMS-devel-mailinglist (lmms-devel@lists.sourceforge.net) and wait for replies +or email me! Maybe there're different ideas, improvements, hints or maybe your +feature is not welcome/needed at the moment (but for sure this will be very +seldom). + +If you coded your feature, make sure, that it is running properly with the +newest available version of LMMS and that it also runs with different +configurations (e.g. different latency, disabled surround-support, missing +package(s) etc.). Important is also, that you comment your +source so that other people can fix bugs or improve your feature! + + diff --git a/TODO b/TODO new file mode 100644 index 0000000000..043e3f619e --- /dev/null +++ b/TODO @@ -0,0 +1,40 @@ +- make usable with Qt4 +- make LMMS an ALSA-sequencer-client +- adchannel-toolbutton -> popup-menu with available soundgenerator-plugins +- tempo-based arpeggio +- pre-listen when opening sample with QFileDialog +- level-meters in output-graph and channel-track +- panning-editing in piano-roll +- use midi-maps +- process program-/channel-change-events from MIDI-files +- setup MIDI-channel and -program in MIDI-Out +- speed up painting of sampleTCO +- save window-positions, -states and -sizes in files +- solve problems with different keyboard-layouts when playing channel-track with pc-keyboard +- balance env+lfo +- autosave every 1 minute +- audioDummy: always wait until the stuff normally would have been written (=simulate blocking IO) +- plucked-string-synth: knob for metallic -> use noise as wave-shape +- finish qt4-port and make LMMS usable when compiling with Qt4 +- rewrite export-project-dialog using layout-mechanism +- dynamic pitch-change +- make piano-roll use the global clipboard?? +- add languages: + - Dutch + - Italian + - ...any other welcome + + + +Things to be done anytime in the future +- effect-board -> live-fx from input +- event-system +- chord-editor? +- WAVE/OGG/MP3-Import -> FFT-analysis -> write notes +- FLP-Import +- classical note-edit-window -> also ability of printing and maybe later scanning & recognition of notes +- add FLAC as export-format? +- support of LADSPA for all plugins +- better commented source... +- optimize, optimize, optimize..... + diff --git a/acinclude.m4 b/acinclude.m4 new file mode 100644 index 0000000000..051df50520 --- /dev/null +++ b/acinclude.m4 @@ -0,0 +1,266 @@ +# Check for Qt compiler flags, linker flags, and binary packages +AC_DEFUN([gw_CHECK_QT], +[ +AC_REQUIRE([AC_PROG_CXX]) +AC_REQUIRE([AC_PATH_X]) + +AC_MSG_CHECKING([QTDIR]) +AC_ARG_WITH([qtdir], [ --with-qtdir=DIR Qt installation directory [default=$QTDIR]], QTDIR=$withval) +# Check that QTDIR is defined or that --with-qtdir given +if test x"$QTDIR" = x ; then + # some usual Qt-locations + QT_SEARCH="/usr /usr/lib/qt /usr/lib/qt3 /usr/lib/qt31 /usr/lib/qt32 /usr/lib/qt33 /usr/lib/qt-3.0 /usr/lib/qt-3.1 /usr/lib/qt-3.2 /usr/lib/qt-3.3 usr/local/qt /usr/local/qt3 /usr/local/qt31/ /usr/local/qt32 /usr/local/qt33 /usr/X11R6 /usr/local/Trolltech/Qt-4.0.0" +else + QT_SEARCH=$QTDIR + QTDIR="" +fi +for i in $QT_SEARCH ; do + QT_INCLUDE_SEARCH="include include/qt include/qt3 include/Qt" + for j in $QT_INCLUDE_SEARCH ; do + if test -f $i/$j/qglobal.h -a x$QTDIR = x ; then + QTDIR=$i + QT_INCLUDES=$i/$j + fi + done +done +if test x"$QTDIR" = x ; then + AC_MSG_ERROR([*** QTDIR must be defined, or --with-qtdir option given]) +fi +AC_MSG_RESULT([$QTDIR]) + +# Change backslashes in QTDIR to forward slashes to prevent escaping +# problems later on in the build process, mainly for Cygwin build +# environment using MSVC as the compiler +# TODO: Use sed instead of perl +QTDIR=`echo $QTDIR | perl -p -e 's/\\\\/\\//g'` + +AC_MSG_CHECKING([Qt includes]) +# Check where includes are located +if test x"$QT_INCLUDES" = x ; then + AC_MSG_ERROR([*** could not find Qt-includes! Make sure you have the Qt-devel-files installed!]) +fi +AC_MSG_RESULT([$QT_INCLUDES]) + +# Figure out which version of Qt we are using +AC_MSG_CHECKING([Qt version]) +QT_VER=`grep 'define.*QT_VERSION_STR\W' $QT_INCLUDES/qglobal.h | perl -p -e 's/\D//g'` +case "${QT_VER}" in + 2*) + AC_MSG_ERROR([*** Qt 2 is not supported by iTALC. Please upgrade to Qt3!]) + ;; + 3*) + QT_MAJOR="3" + ;; + 4*) + QT_MAJOR="4" + ;; + *) + AC_MSG_ERROR([*** Don't know how to handle this Qt major version]) + ;; +esac +AC_MSG_RESULT([$QT_VER ($QT_MAJOR)]) + +# Check that moc is in path +AC_CHECK_PROG(MOC, moc, $QTDIR/bin/moc,,$QTDIR/bin/) +if test x$MOC = x ; then + AC_CHECK_PROG(MOC, moc-qt3, $QTDIR/bin/moc-qt3,,$QTDIR/bin/) + if test x$MOC = x ; then + AC_MSG_ERROR([*** not found! Make sure you have Qt-devel-tools installed!]) + fi +fi + +# uic is the Qt user interface compiler +AC_CHECK_PROG(UIC, uic, $QTDIR/bin/uic,,$QTDIR/bin/) +if test x$UIC = x ; then + AC_MSG_WARN([*** not found! It's currently not needed but should be part of a proper Qt-devel-tools-installation!]) +fi + +# qembed is the Qt data embedding utility. +#AC_CHECK_PROG(QEMBED, qembed, $QTDIR/bin/qembed,,$QTDIR/bin/) +#if test x$QEMBED = x ; then +# AC_MSG_ERROR([*** not found! Make sure you have Qt-devel-tools/Qt-extensions installed! On some distributions (e.g. SuSE) the package, containing qembed is also called qt3-extensions.]) +#fi + +# lupdate is the Qt translation-update utility. +AC_CHECK_PROG(LUPDATE, lupdate, $QTDIR/bin/lupdate,,$QTDIR/bin/) +if test x$LUPDATE = x ; then + AC_MSG_WARN([*** not found! It's not needed just for compiling but should be part of a proper Qt-devel-tools-installation!]) +fi + +# lrelease is the Qt translation-release utility. +AC_CHECK_PROG(LRELEASE, lrelease, $QTDIR/bin/lrelease,,$QTDIR/bin/) +if test x$LRELEASE = x ; then + AC_MSG_WARN([*** not found! It's not needed just for compiling but should be part of a proper Qt-devel-tools-installation!]) +fi + +# Calculate Qt include path +QT_CXXFLAGS="-I$QT_INCLUDES" +if test "$QT_MAJOR" = "4" ; then + QT_CXXFLAGS="$QT_CXXFLAGS -I$QTDIR/include" +fi + + +QT_IS_EMBEDDED="no" +# On unix, figure out if we're doing a static or dynamic link +case "${host}" in + *-cygwin) + AC_DEFINE_UNQUOTED(WIN32, "", Defined if on Win32 platform) + if test -f "$QTDIR/lib/qt.lib" ; then + QT_LIB="qt.lib" + QT_IS_STATIC="yes" + QT_IS_MT="no" + elif test -f "$QTDIR/lib/qt-mt.lib" ; then + QT_LIB="qt-mt.lib" + QT_IS_STATIC="yes" + QT_IS_MT="yes" + elif test -f "$QTDIR/lib/qt$QT_VER.lib" ; then + QT_LIB="qt$QT_VER.lib" + QT_IS_STATIC="no" + QT_IS_MT="no" + elif test -f "$QTDIR/lib/qt-mt$QT_VER.lib" ; then + QT_LIB="qt-mt$QT_VER.lib" + QT_IS_STATIC="no" + QT_IS_MT="yes" + fi + ;; + + *) + QT_IS_STATIC=`ls $QTDIR/lib/*.a 2> /dev/null` + if test "x$QT_IS_STATIC" = x; then + QT_IS_STATIC="no" + else + QT_IS_STATIC="yes" + fi + if test x$QT_IS_STATIC = xno ; then + QT_IS_DYNAMIC=`ls $QTDIR/lib/*.so 2> /dev/null` + if test "x$QT_IS_DYNAMIC" = x; then + AC_MSG_ERROR([*** Couldn't find any Qt libraries]) + fi + fi + if test "$QT_MAJOR" = "4" ; then + QT_IS_MT="yes" + QT_LIB="-lQtCore -lQtGui -lQtXml -lQt3Support" + else + if test "x`ls $QTDIR/lib/libqt-mt.* 2> /dev/null`" != x ; then + QT_LIB="-lqt-mt" + QT_IS_MT="yes" + elif test "x`ls $QTDIR/lib/libqt.* 2> /dev/null`" != x ; then + QT_LIB="-lqt" + QT_IS_MT="no" + elif test "x`ls $QTDIR/lib/libqte.* 2> /dev/null`" != x ; then + QT_LIB="-lqte" + QT_IS_MT="no" + QT_IS_EMBEDDED="yes" + elif test "x`ls $QTDIR/lib/libqte-mt.* 2> /dev/null`" != x ; then + QT_LIB="-lqte-mt" + QT_IS_MT="yes" + QT_IS_EMBEDDED="yes" + fi + fi + ;; +esac +AC_MSG_CHECKING([if Qt is static]) +AC_MSG_RESULT([$QT_IS_STATIC]) +AC_MSG_CHECKING([if Qt is multithreaded]) +if test "$QT_IS_MT" = "no"; then + AC_MSG_ERROR([*** your Qt is not multithreaded. That's bad, because multithreading is required for compiling... Please install Qt-mt!]) +fi +AC_MSG_RESULT([$QT_IS_MT]) +AC_MSG_CHECKING([if Qt is embedded]) +AC_MSG_RESULT([$QT_IS_EMBEDDED]) + +QT_GUILINK="" +QASSISTANTCLIENT_LDADD="-lqassistantclient" +QT_LIBS="$QT_LIB" + +case "${host}" in + *irix*) + QT_LIBS="$QT_LIB" + if test $QT_IS_STATIC = yes ; then + QT_LIBS="$QT_LIBS -L$x_libraries -lXext -lX11 -lm -lSM -lICE" + fi + ;; + + *linux*) + QT_LIBS="$QT_LIB" + if test $QT_IS_STATIC = yes && test $QT_IS_EMBEDDED = no; then + QT_LIBS="$QT_LIBS -L$x_libraries -lXext -lX11 -lm -lSM -lICE -ldl -ljpeg" + fi + ;; + + + *osf*) + # Digital Unix (aka DGUX aka Tru64) + QT_LIBS="$QT_LIB" + if test $QT_IS_STATIC = yes ; then + QT_LIBS="$QT_LIBS -L$x_libraries -lXext -lX11 -lm -lSM -lICE" + fi + ;; + + *solaris*) + QT_LIBS="$QT_LIB" + if test $QT_IS_STATIC = yes ; then + QT_LIBS="$QT_LIBS -L$x_libraries -lXext -lX11 -lm -lSM -lICE -lresolv -lsocket -lnsl" + fi + ;; + + + *win*) + # linker flag to suppress console when linking a GUI app on Win32 + QT_GUILINK="/subsystem:windows" + + if test $QT_MAJOR = "3" ; then + if test $QT_IS_MT = yes ; then + QT_LIBS="/nodefaultlib:libcmt" + else + QT_LIBS="/nodefaultlib:libc" + fi + fi + + if test $QT_IS_STATIC = yes ; then + QT_LIBS="$QT_LIBS $QT_LIB kernel32.lib user32.lib gdi32.lib comdlg32.lib ole32.lib shell32.lib imm32.lib advapi32.lib wsock32.lib winspool.lib winmm.lib netapi32.lib" + if test $QT_MAJOR = "3" ; then + QT_LIBS="$QT_LIBS qtmain.lib" + fi + else + QT_LIBS="$QT_LIBS $QT_LIB" + if test $QT_MAJOR = "3" ; then + QT_CXXFLAGS="$QT_CXXFLAGS -DQT_DLL" + QT_LIBS="$QT_LIBS qtmain.lib qui.lib user32.lib netapi32.lib" + fi + fi + QASSISTANTCLIENT_LDADD="qassistantclient.lib" + ;; + +esac + + +if test x"$QT_IS_EMBEDDED" = "xyes" ; then + QT_CXXFLAGS="-DQWS $QT_CXXFLAGS" +fi + +if test x"$QT_IS_MT" = "xyes" ; then + QT_CXXFLAGS="$QT_CXXFLAGS -D_REENTRANT -DQT_THREAD_SUPPORT" + QT_LIBS="$QT_LIBS -lpthread" +fi + +QT_LDADD="-L$QTDIR/lib $QT_LIBS" + +#if test x$QT_IS_STATIC = xyes ; then +# OLDLIBS="$LIBS" +# LIBS="$QT_LDADD" +# AC_CHECK_LIB(Xft, XftFontOpen, QT_LDADD="$QT_LDADD -lXft") +# LIBS="$LIBS" +#fi + +AC_MSG_CHECKING([QT_CXXFLAGS]) +AC_MSG_RESULT([$QT_CXXFLAGS]) +AC_MSG_CHECKING([QT_LDADD]) +AC_MSG_RESULT([$QT_LDADD]) + +AC_SUBST(QT_CXXFLAGS) +AC_SUBST(QT_LDADD) +AC_SUBST(QT_GUILINK) +AC_SUBST(QASSISTANTCLIENT_LDADD) + +]) diff --git a/buildtools/bin2res.cpp b/buildtools/bin2res.cpp new file mode 100644 index 0000000000..9680c699cc --- /dev/null +++ b/buildtools/bin2res.cpp @@ -0,0 +1,210 @@ +/* + * bin2res.cpp - generate embedded resources from binary data (based on qembed) + * + * Copyright (c) 2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include +#include +#include +#include + +static void embedData( const char * _input, int _size, std::ostream & _output ); +static std::string convertFileNameToCIdentifier( const std::string & _s ); + + +struct embed +{ + unsigned int size; + std::string name; + std::string cname; +} ; + +typedef std::vector stringlist; + +const int MAX_FILE_SIZE = 256*256*256; // = 16 MB + + +int main( int argc, char * * argv ) +{ + if( argc < 2 ) + { + std::cerr << "Usage:" << std::endl << "\t" << argv[0] << + " files" << std::endl; + return( 1 ); + } + + std::cout << "// Generated by bin2res" << std::endl; + + std::vector embedded_data; + + std::cout << "#ifndef _EMBEDDED_RESOURCES_H" << std::endl; + std::cout << "#define _EMBEDDED_RESOURCES_H" << std::endl; + + stringlist files; + for( int i = 1; i < argc; ++i ) + { + files.push_back( argv[i] ); + } + for( stringlist::iterator it = files.begin(); it != files.end(); ++it ) + { + std::ifstream f( it->c_str() ); + if( f.fail() ) + { + std::cerr << "Cannot open file " << *it << + ", ignoring it" << std::endl; + continue; + } + f.seekg( 0, std::ios::end ); + int fsize = f.tellg(); + f.seekg( 0 ); + if( fsize == 0 || fsize > MAX_FILE_SIZE ) + { + std::cerr << "File " << *it << " has zero size or is " + "too large to be processed with bin2res." << + std::endl; + } + char * data = new char[fsize]; + f.read( data, fsize ); + embed * e = new embed; + e->size = fsize; + if( it->rfind( '/' ) != std::string::npos ) + { + e->name = std::string( it->c_str() + + it->rfind( '/' ) + 1 ); + } + else + { + e->name = *it; + } + e->cname = convertFileNameToCIdentifier( e->name ); + embedded_data.push_back( e ); + std::string s; + std::cout << "static const unsigned char " << e->cname << + "_data[] = {"; + embedData( data, fsize, std::cout ); + std::cout << std::endl << "};" << std::endl << std::endl; + delete[] data; + } + + if( embedded_data.size() > 0 ) + { + std::cout << "static const unsigned char dummy_data[] =" + "{ 0x00 };" << std::endl << std::endl; + embed * dummy = new embed; + dummy->size = 1; + dummy->name = "dummy"; + dummy->cname = convertFileNameToCIdentifier( "dummy" ); + embedded_data.push_back( dummy ); + + std::cout << "#include " << std::endl; + std::cout << "static struct embedDesc" << std::endl + << "{" << std::endl + << " int size;" << std::endl + << " const unsigned char * data;" << + std::endl + << " const char * name;" << std::endl + << "} embed_vec[] = {" << std::endl; + while( embedded_data.size() > 0 ) + { + embed * e = embedded_data[0]; + std::cout << " { " << e->size << ", " << e->cname << + "_data, " << "\"" << e->name << + "\" }," << std::endl; + delete e; + embedded_data.erase( embedded_data.begin() ); + } + std::cout << " { 0, 0, 0 }" << std::endl << "};" << std::endl + << std::endl + << "static const embedDesc & findEmbeddedData( " + "const char * _name )" << std::endl + << "{" << std::endl + << " for( int i = 0; embed_vec[i].data; " + "i++ )" << std::endl + << " {" << std::endl + << " if( strcmp( embed_vec[i].name, " + "_name ) == 0 )" << std::endl + << " {" << std::endl + << " return( " + "embed_vec[i] );" << std::endl + << " }" << std::endl + << " }" << std::endl +/* << " printf( \"warning: embedded resource " + "%s not found!\\n\", _name );" + << std::endl*/ + << " return( findEmbeddedData( " + "\"dummy\" ) );" << std::endl + << "}" << std::endl << std::endl; + } + std::cout << "#endif" << std::endl; + return( 0 ); +} + + + + +std::string convertFileNameToCIdentifier( const std::string & _s ) +{ + std::string r = _s; + int len = r.length(); + if ( len > 0 && !isalpha( (char)r[0] ) ) + { + r[0] = '_'; + } + for ( int i = 1; i < len; i++ ) + { + if ( !isalnum( (char)r[i] ) ) + { + r[i] = '_'; + } + } + return( r ); +} + + + + +void embedData( const char * _input, int _nbytes, std::ostream & _output ) +{ + static const char hexdigits[] = "0123456789abcdef"; + std::string s; + for( int i = 0; i < _nbytes; i++ ) + { + if( ( i%14 ) == 0 ) + { + s += "\n "; + _output << s; + s = ""; + } + unsigned int v = _input[i]; + s += "0x"; + s += hexdigits[(v >> 4) & 15]; + s += hexdigits[v & 15]; + if( i < _nbytes-1 ) + { + s += ','; + } + } + if ( s.length() ) + { + _output << s; + } +} + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000000..2069236c88 --- /dev/null +++ b/configure.in @@ -0,0 +1,496 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.50) +AC_INIT(lmms, 0.1.1, tobydox@users.sourceforge.net) +#AC_INIT(acinclude.m4) +AM_INIT_AUTOMAKE(lmms, 0.1.1) + +AM_CONFIG_HEADER(config.h) + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC +AC_PROG_LN_S + + +AC_PATH_XTRA +gw_CHECK_QT + + +# checks for header files. +AC_HEADER_STDC +AC_HEADER_SYS_WAIT +AC_CHECK_HEADERS([fcntl.h memory.h string.h sys/ioctl.h unistd.h stdlib.h]) + +# checks for typedefs, structures, and compiler characteristics. +AC_C_CONST +AC_C_INLINE +AC_TYPE_SIZE_T +AC_HEADER_TIME +AC_STRUCT_TM +AC_C_VOLATILE +AC_C_BIGENDIAN + +# checks for library functions. +AC_FUNC_ALLOCA +AC_PROG_GCC_TRADITIONAL +AC_FUNC_MALLOC +AC_FUNC_MEMCMP +AC_TYPE_SIGNAL + +AC_CHECK_FUNCS([usleep]) + + +# search for include-path of SDL (just for supporting the FreeBSD-guys.... ;-) +for i in SDL SDL10 SDL11 SDL12 ; do + FOUND="" + AC_CHECK_HEADER($i/SDL.h, FOUND="true") + if test "$FOUND" = "true" ; then + SDL_INC_PATH="$i" + break + fi +done + + +AH_TEMPLATE(SDL_SDL_H, [Define to location of SDL.h]) +AH_TEMPLATE(SDL_SDL_AUDIO_H, [Define to location of SD_audio.h]) +AH_TEMPLATE(SDL_SDL_SOUND_H, [Define to location of SDL_sound.h]) + +OLD_LIBS="$LIBS" +LIBS="$LIBS -lpthread" + +# check for SDL-lib +AC_ARG_WITH(sdl, + AS_HELP_STRING([--without-sdl], + [disable support for SDL-audio-output]), , + [ with_sdlaudio=yes ]) +AH_TEMPLATE(HAVE_SDL_SDL_AUDIO_H, [Define to 1 if you have the $SDL_INC_PATH/SDL_audio.h header file.]) +if test "x$with_sdlaudio" = "xyes" -a ! -z "$SDL_INC_PATH"; then + AC_CHECK_HEADER($SDL_INC_PATH/SDL_audio.h, HAVE_SDL_SDL_AUDIO_H="true") + AC_CHECK_LIB([SDL], [SDL_OpenAudio], HAVE_LIBSDL="true", HAVE_SDL_SDL_AUDIO_H="") +fi +if test ! -z "$HAVE_SDL_SDL_AUDIO_H" ; then + AC_DEFINE_UNQUOTED(SDL_SDL_H, <$SDL_INC_PATH/SDL.h>) + AC_DEFINE_UNQUOTED(SDL_SDL_AUDIO_H, <$SDL_INC_PATH/SDL_audio.h>) + AC_DEFINE(HAVE_SDL_SDL_AUDIO_H) +fi +AM_CONDITIONAL(HAVE_LIBSDL, test ! -z "$HAVE_SDL_SDL_AUDIO_H") + + +# check for SDL_sound-lib +AC_ARG_WITH(sdlsound, + AS_HELP_STRING([--without-sdlsound], + [disable support for reading samples via SDL_sound]), , + [ with_sdlsound=yes ]) +AH_TEMPLATE(HAVE_SDL_SDL_SOUND_H, [Define to 1 if you have the $SDL_INCL_PATH/SDL_sound.h header file.]) +if test "x$with_sdlsound" = "xyes" -a ! -z "$SDL_INC_PATH"; then + AC_CHECK_HEADER($SDL_INC_PATH/SDL_sound.h, HAVE_SDL_SDL_SOUND_H="true") + AC_CHECK_LIB([SDL_sound], [Sound_Init], HAVE_LIBSDL_SOUND="true", HAVE_SDL_SDL_SOUND_H="") +fi +if test ! -z "$HAVE_SDL_SDL_SOUND_H" ; then + AC_DEFINE_UNQUOTED(SDL_SDL_SOUND_H, <$SDL_INC_PATH/SDL_sound.h>) + AC_DEFINE(HAVE_SDL_SDL_SOUND_H) +fi + +AM_CONDITIONAL(HAVE_LIBSDL_SOUND, test ! -z "$HAVE_LIBSDL_SOUND") + +LIBS="$OLD_LIBS" + + +# check for ALSA-lib +AC_ARG_WITH(asound, + AS_HELP_STRING([--without-asound], + [disable support for ALSA-sound-output]), , + [ with_asound=yes ]) +AH_TEMPLATE(HAVE_ALSA_ASOUNDLIB_H, [Define to 1 if you have the alsa/asoundlib.h header file.]) +if test "x$with_asound" = "xyes" ; then + AC_CHECK_HEADER(alsa/asoundlib.h, HAVE_ALSA_ASOUNDLIB_H="true") + AC_CHECK_LIB([asound], [snd_pcm_open], HAVE_LIBASOUND="true", HAVE_ALSA_ASOUNDLIB_H="") +fi +if test ! -z "$HAVE_ALSA_ASOUNDLIB_H" ; then + AC_DEFINE(HAVE_ALSA_ASOUNDLIB_H) +fi +AM_CONDITIONAL(HAVE_LIBASOUND, test ! -z "$HAVE_ALSA_ASOUNDLIB_H") + + +# check for JACK +AC_ARG_WITH(jack, + AS_HELP_STRING([--without-jack], + [disable support for JACK-transport]), , + [ with_jack=yes ]) +AH_TEMPLATE(HAVE_JACK_JACK_H, [Define to 1 if you have the jack/jack.h header file.]) +AH_TEMPLATE(OLD_JACK, [Define to 1 if your JACK-version doesnt know about jack_client_open() etc.]) +if test "x$with_jack" = "xyes" ; then + AC_CHECK_HEADER(jack/jack.h, HAVE_JACK_JACK_H="true") + AC_CHECK_LIB([jack], [jack_client_new], HAVE_LIBJACK="true", HAVE_JACK_JACK_H="") + AC_CHECK_LIB([jack], [jack_client_open], JACK_CLIENT_OPEN="true") + if test -z "$JACK_CLIENT_OPEN" ; then + AC_DEFINE(OLD_JACK) + fi +fi +if test ! -z "$HAVE_JACK_JACK_H" ; then + AC_DEFINE(HAVE_JACK_JACK_H) +fi +AM_CONDITIONAL(HAVE_LIBJACK, test ! -z "$HAVE_JACK_JACK_H") + + +# check for vorbis-lib +AC_ARG_WITH(vorbis, + AS_HELP_STRING([--without-vorbis], + [disable support for exporting song to OGG-files]), , + [ with_vorbis=yes ]) +AH_TEMPLATE(HAVE_VORBIS_CODEC_H, [Define to 1 if you have the vorbis/codec.h header file.]) +AH_TEMPLATE(HAVE_VORBIS_VORBISENC_H, [Define to 1 if you have the vorbis/vorbisenc.h header file.]) +AH_TEMPLATE(HAVE_VORBIS_VORBISFILE_H, [Define to 1 if you have the vorbis/vorbisfile.h header file.]) +if test "x$with_vorbis" = "xyes" ; then + OGG_SUPPORT="true" + AC_CHECK_HEADER(vorbis/codec.h, HAVE_VORBIS_CODEC_H="true",OGG_SUPPORT="") + AC_CHECK_HEADER(vorbis/vorbisenc.h, HAVE_VORBIS_VORBISENC_H="true",OGG_SUPPORT="") + AC_CHECK_HEADER(vorbis/vorbisfile.h, HAVE_VORBIS_VORBISFILE_H="true",OGG_SUPPORT="") + AC_CHECK_LIB([vorbis], [vorbis_analysis], HAVE_LIBVORBIS="true", OGG_SUPPORT="") +fi +if test ! -z "$OGG_SUPPORT" ; then + AC_DEFINE(HAVE_VORBIS_CODEC_H) + AC_DEFINE(HAVE_VORBIS_VORBISENC_H) + AC_DEFINE(HAVE_VORBIS_VORBISFILE_H) +fi +AM_CONDITIONAL(HAVE_LIBVORBIS, test ! -z "$OGG_SUPPORT") + + +# check for libsamplerate +AC_ARG_WITH(src, + AS_HELP_STRING([--without-libsrc], + [disable support for resampling via libsamplerate]), , + [ with_libsrc=yes ]) +AH_TEMPLATE(HAVE_SAMPLERATE_H, [Define to 1 if you have the samplerate.h header file.]) +if test "x$with_libsrc" = "xyes" ; then + AC_CHECK_HEADER(samplerate.h, HAVE_SAMPLERATE_H="true") + AC_CHECK_LIB([samplerate], [src_simple], HAVE_LIBSRC="true", HAVE_SAMPLERATE_H="") +fi +if test ! -z "$HAVE_SAMPLERATE_H" ; then + AC_DEFINE(HAVE_SAMPLERATE_H) +fi +AM_CONDITIONAL(HAVE_LIBSRC, test ! -z "$HAVE_SAMPLERATE_H") + + +# libsndfile-stuff +AC_ARG_WITH(src, + AS_HELP_STRING([--without-libsf], + [disable support for importing files via libsndfile]), , + [ with_libsf=yes ]) +AH_TEMPLATE(HAVE_SNDFILE_H, [Define to 1 if you have the sndfile.h header file.]) +if test "x$with_libsf" = "xyes" ; then + AC_CHECK_HEADER(sndfile.h, HAVE_SNDFILE_H="true") + AC_CHECK_LIB([sndfile], [sf_command], HAVE_LIBSF="true", HAVE_SNDFILE_H="") + AC_MSG_CHECKING([version of libsndfile]) + AH_TEMPLATE(OLD_SNDFILE, [Define to 1 if libsndfile < 1.0.11]) + OLD_LIBS="$LIBS" + LIBS="$LIBS -lsndfile" + AC_RUN_IFELSE([ #include + int main( void ) + { + SF_INFO si; + ( void )sf_open( "", SFM_READ, &si ); + return 0; + }], + AC_MSG_RESULT([>= 1.0.11]), + AC_MSG_RESULT([< 1.0.11]) + AC_DEFINE(OLD_SNDFILE) + ) + LIBS="$OLD_LIBS" +fi +if test ! -z "$HAVE_SNDFILE_H" ; then + AC_DEFINE(HAVE_SNDFILE_H) +fi +AM_CONDITIONAL(HAVE_LIBSF, test ! -z "$HAVE_SNDFILE_H") + +AC_ARG_WITH(nosmpdecs, + AS_HELP_STRING([--without-sample-decoders], + [force compiling LMMS even if libraries for sample-decoding were found]), + [ with_smpdecs=yes ]) + +if test -z "$HAVE_SND_FILE" -a -z "$HAVE_SDL_SDL_SOUND_H" -a -z "$OGG_SUPPORT" -a ! -z "$with_smpdecs"; then + AC_MSG_ERROR([*** neither libsndfile nor SDL_sound nor libvorbis (or according devel-files) were found which would make LMMS unable to load any samples, so please install at least one of the packages and try again! Use --without-sample-decoders to force compiling LMMS.]) +fi + + + +# check whether to disable surround-support +AC_MSG_CHECKING([whether to disable surround-support]) +AH_TEMPLATE(DISABLE_SURROUND, [Define if you want to disable surround-support in LMMS.]) +AC_ARG_ENABLE([surround], + AS_HELP_STRING([--disable-surround], + [compile LMMS without surround-support]), + DISABLE_SURROUND="yes") +if test "x$DISABLE_SURROUND" = "xyes" ; then + AC_MSG_RESULT(yes) + AC_DEFINE(DISABLE_SURROUND) +else + AC_MSG_RESULT(no) +fi + + +# check whether to enable high-quality sinc-resampling +AC_MSG_CHECKING([whether to enable high quality sinc-resampling]) +AH_TEMPLATE(HQ_SINC, [Define if you want to enable high quality sinc-resampling.]) +AC_ARG_ENABLE([hqsinc], + AS_HELP_STRING([--enable-hqsinc], + [enable high quality sinc-resampling]), HQ_SINC="yes") +if test "x$HQ_SINC" = "xyes" ; then + AC_MSG_RESULT(yes) + AC_DEFINE(HQ_SINC) +else + AC_MSG_RESULT(no) +fi + + +# check whether compiler of current platform supports attribute regparm(x) +AC_MSG_CHECKING([whether we can use function-attribute regparm(x) on current platform]) +AH_TEMPLATE(FASTCALL, [Define to any attribute, speeding up function-calls]) +AC_RUN_IFELSE([ int __attribute__((regparm(3))) foo( int a, int b, int c ) + { return( a=b=c ); } + int __attribute__((regparm(3))) main( void ) + { return( foo(2,1,0) ); }], + AC_MSG_RESULT(yes) + AC_DEFINE(FASTCALL, [__attribute__((regparm(3)))]), + AC_MSG_RESULT(no) + AC_DEFINE_UNQUOTED(FASTCALL,[]) + ) + + +AC_MSG_CHECKING([whether floorf is known by compiler]) +AH_TEMPLATE(floorf, [Define to floor if floorf is not known]) +AC_TRY_LINK([#include ], [ floorf ], + AC_MSG_RESULT(yes), + AC_MSG_RESULT([no - will use floor instead]) + AC_DEFINE(floorf, floor) + ) + +AC_MSG_CHECKING([whether powf is known by compiler]) +AH_TEMPLATE(powf, [Define to pow if powf is not known]) +AC_TRY_LINK([#include ], [ powf ], + AC_MSG_RESULT(yes), + AC_MSG_RESULT([no - will use pow instead]) + AC_DEFINE(powf, pow) + ) + +AC_MSG_CHECKING([whether fabsf is known by compiler]) +AH_TEMPLATE(fabsf, [Define to pow if fabsf is not known]) +AC_TRY_LINK([#include ], [ fabsf ], + AC_MSG_RESULT(yes), + AC_MSG_RESULT([no - will use fabs instead]) + AC_DEFINE(fabsf, fabs) + ) + + +# check whether to enable debugging-code +AC_MSG_CHECKING([whether to enable debugging-code]) +AH_TEMPLATE(LMMS_DEBUG, [Define if you want to disable debbuging-code in LMMS.]) +AC_ARG_ENABLE([debug], + AS_HELP_STRING([--enable-debug], + [compile LMMS with additional debugging support]), DEBUG="yes") +if test "x$DEBUG" = "xyes" ; then + AC_MSG_RESULT(yes) + AC_DEFINE(LMMS_DEBUG) +else + AC_MSG_RESULT(no) +fi + + +# check for rpm SOURCES path +echo -n "checking for rpm sources path... " +RPMSOURCEDIR="NOT-FOUND" +for directory in packages OpenLinux redhat RedHat rpm RPM "" ; do + if test -d /usr/src/${directory}/SOURCES ; then + RPMSOURCEDIR="/usr/src/${directory}/SOURCES/" + fi +done +echo "$RPMSOURCEDIR" +AM_CONDITIONAL(HAVE_RPM, test "$RPMSOURCEDIR" != "NOT-FOUND") +AC_SUBST(RPMSOURCEDIR) + +lmmsdatadir="$datadir/$PACKAGE" +AC_SUBST(lmmsdatadir) + + +CXXFLAGS="$CXXFLAGS -g0" +if test $CXX == "g++" ; then + CXXFLAGS="$CXXFLAGS -ansi -Wall -fno-exceptions" +fi + + +AC_CONFIG_FILES([Makefile + artwork/Makefile + locale/Makefile + midi-maps/Makefile + presets/Makefile + presets/AudioFileProcessor/Makefile + presets/MIDI-Out/Makefile + presets/PluckedStringSynth/Makefile + presets/TripleOscillator/Makefile + projects/Makefile + projects/cool_songs/Makefile + projects/covers/Makefile + projects/demos/Makefile + projects/misc/Makefile + projects/recorded_loops/Makefile + projects/templates/Makefile + projects/tutorials/Makefile + samples/Makefile + samples/basses/Makefile + samples/bassloopes/Makefile + samples/beats/Makefile + samples/drums/Makefile + samples/effects/Makefile + samples/instruments/Makefile + samples/latin/Makefile + samples/misc/Makefile + samples/shapes/Makefile + samples/stringsnpads/Makefile + lmms.spec]) +AC_OUTPUT + +with_warnings="false" + +echo +echo + + +if test -z "$OGG_SUPPORT" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have libvorbis installed and/or libvorbis-development-" + echo " = package is missing. This will disable support for exporting songs to" + echo " = OGG-files! If also SDL_sound is missing, you'll not be able to use the " + echo " = samples coming with LMMS since libsndfile currently has no OGG-support!" + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + +if test -z "$HAVE_ALSA_ASOUNDLIB_H" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have ALSA-library installed and/or ALSA-lib-development-" + echo " = package is missing. This will disable support for sound-output via ALSA!" + echo " = If you use ALSA-driver for your soundcard there will be no sound-output!" + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + +if test -z "$HAVE_JACK_JACK_H" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have JACK-library installed and/or JACK-lib-development-" + echo " = package is missing. This will disable support for JACK-transport." + echo " = This is no problem but you'll miss the great possibilities of interacting" + echo " = with other sound-applications in realtime!" + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + +if test -z "$HAVE_SDL_SDL_AUDIO_H" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have SDL-library installed and/or SDL-lib-development-" + echo " = package is missing. This will disable support for sound-output via SDL, " + echo " = which is important, if you're using sound-daemons (arts, ESD, JACK etc.)" + echo " = When compiling without SDL, you'll always have to kill your sound-daemon" + echo " = before using LMMS...!" + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + + +if test -z "$HAVE_SDL_SDL_SOUND_H" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have SDL_sound-library installed and/or SDL_sound-" + echo " = development-package is missing. This means that you can only load samples" + echo " = within LMMS if you have libsndfile and libvorbis installed!" + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + + +if test -z "$HAVE_SNDFILE_H" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have libsndfile installed and/or libsndfile-development-" + echo " = package is missing. This is not that fatal as long as you have SDL_sound" + echo " = installed. Otherwise it's strongly recommended to install libsndfile and" + echo " = libvorbis for being able to use samples within LMMS." + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + + +if test -z "$HAVE_SAMPLERATE_H" ; then + echo " ========================" + echo " === LMMS - WARNING =======================================================" + echo " ========================" + echo " =" + echo " = You don't seem to have libsamplerate installed and/or libsamplerate-" + echo " = development-package is missing. This will disable resampling with" + echo " = libsamplerate and results in worse quality! Currently LMMS will also" + echo " = crash more often, because internal sample-rate-converter has some bugs..." + echo " = Consider installing the missing packages for using the full power of LMMS." + echo " =" + with_warnings="true" +fi + + + + +echo +echo +echo " ============================" +echo " === LMMS - INFORMATION ===================================================" +echo " ============================" +echo " =" +if test "$with_warnings" = "false" ; then + echo " = LMMS has been configured successfully. Now you can build it by typing 'make'." + echo " = After the compilation-process has finished, you can install LMMS by" + echo " = typing 'make install'." +else + echo " = LMMS has been configured, but there were one or more warnings you should" + echo " = not ignore. First try to solve the mentioned problems before" + echo " = proceeding. Otherwise don't complain, if LMMS doesn't run the way" + echo " = you might expect..." +fi +echo " =" +echo " = If there're problems while compiling LMMS, please send a mail to " +echo " = tobydox@users.sourceforge.net!" +echo " =" +echo + diff --git a/include/about_dialog.h b/include/about_dialog.h new file mode 100644 index 0000000000..5a9051d075 --- /dev/null +++ b/include/about_dialog.h @@ -0,0 +1,69 @@ +/* + * about_dialog.h - declaration of class aboutDialog + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _ABOUT_DIALOG_H +#define _ABOUT_DIALOG_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +class QPushButton; +class QLabel; +class QTabWidget; + + +class aboutDialog : public QDialog +{ + Q_OBJECT +public: + aboutDialog( void ); + ~aboutDialog(); + + +protected: + void keyPressEvent( QKeyEvent * _ke ); + void resizeEvent( QResizeEvent * _re ); + + +private: + QPushButton * m_okBtn; + QLabel * m_iconLbl; + QLabel * m_appNameLbl; + QTabWidget * m_aboutTabs; + +} ; + + +#endif + diff --git a/include/arp_and_chords_tab_widget.h b/include/arp_and_chords_tab_widget.h new file mode 100644 index 0000000000..a431e07373 --- /dev/null +++ b/include/arp_and_chords_tab_widget.h @@ -0,0 +1,138 @@ +/* + * arp_and_chords_tab_widget.h - declaration of class arpAndChordWidget which + * provides code for using arpeggio and chords + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _ARP_AND_CHORDS_TAB_WIDGET_H +#define _ARP_AND_CHORDS_TAB_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "settings.h" +#include "types.h" + + +class channelTrack; +class groupBox; +class knob; +class pixmapButton; +class QComboBox; +class QPixmap; +class notePlayHandle; + + +const int MAX_CHORD_POLYPHONY = 10; + + +class arpAndChordsTabWidget : public QWidget, public settings +{ + Q_OBJECT +public: + arpAndChordsTabWidget( channelTrack * _channel_track, + QWidget * _parent ); + ~arpAndChordsTabWidget(); + + static struct chord + { + const QString name; + Sint8 interval[MAX_CHORD_POLYPHONY]; + } s_chords[]; + + void FASTCALL processNote( notePlayHandle * _n ); + static inline int getChordSize( chord & _c ) + { + int idx = 0; + while( _c.interval[idx] != -1 ) + { + ++idx; + } + return( idx ); + } + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "arpandchords" ); + } + + + +protected slots: +// void arpOffToggled( bool ); + void arpUpToggled( bool ); + void arpDownToggled( bool ); + void arpUpAndDownToggled( bool ); + void arpRandomToggled( bool ); + + +private: + + enum arpDirections + { + OFF, + UP, + DOWN, + UP_AND_DOWN, + RANDOM + } m_arpDirection; + + channelTrack * m_channelTrack; + + // chord-stuff + groupBox * m_chordsGroupBox; + QComboBox * m_chordsComboBox; + knob * m_chordRangeKnob; + + // arpeggio-stuff + groupBox * m_arpGroupBox; + QComboBox * m_arpComboBox; + knob * m_arpRangeKnob; + knob * m_arpTimeKnob; + knob * m_arpGateKnob; + QLabel * m_arpDirectionLbl; + //pixmapButton * m_arpOffBtn; + pixmapButton * m_arpUpBtn; + pixmapButton * m_arpDownBtn; + pixmapButton * m_arpUpAndDownBtn; + pixmapButton * m_arpRandomBtn; + +} ; + +#endif diff --git a/include/audio_alsa.h b/include/audio_alsa.h new file mode 100644 index 0000000000..80b5f927b5 --- /dev/null +++ b/include/audio_alsa.h @@ -0,0 +1,102 @@ +/* + * audio_alsa.h - device-class that implements ALSA-PCM-output + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_ALSA_H +#define _AUDIO_ALSA_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_ALSA_ASOUNDLIB_H + +#ifndef ALSA_SUPPORT +#define ALSA_SUPPORT +#endif + +#include + +#include "audio_device.h" + + +class lcdSpinBox; +class QLineEdit; + + +class audioALSA : public audioDevice +{ +public: + audioALSA( Uint32 _sample_rate, bool & _success_ful ); + ~audioALSA(); + + inline static QString name( void ) + { + return( setupWidget::tr( "ALSA (Advanced Linux Sound " + "Architecture)" ) ); + } + + static QString probeDevice( void ); + + + class setupWidget : public audioDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + virtual void saveSettings( void ); + + private: + QLineEdit * m_device; + lcdSpinBox * m_channels; + + } ; + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + int FASTCALL setHWParams( Uint32 _sample_rate, Uint32 _channels, + snd_pcm_access_t _access ); + int setSWParams( void ); + int FASTCALL handleError( int _err ); + + + snd_pcm_t * m_handle; + + snd_pcm_uframes_t m_bufferSize; + snd_pcm_uframes_t m_periodSize; + + snd_pcm_hw_params_t * m_hwParams; + snd_pcm_sw_params_t * m_swParams; + + bool m_littleEndian; + +} ; + +#endif + +#endif diff --git a/include/audio_device.h b/include/audio_device.h new file mode 100644 index 0000000000..28f612e079 --- /dev/null +++ b/include/audio_device.h @@ -0,0 +1,143 @@ +/* + * audio_device.h - base-class for audio-devices, used by LMMS-mixer + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_DEVICE_H +#define _AUDIO_DEVICE_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SAMPLERATE_H +#include +#endif + + +#include "mixer.h" +#include "tab_widget.h" + + +class audioDevice +{ +public: + audioDevice( Uint32 _sample_rate, Uint8 _channels ); + virtual ~audioDevice(); + + inline void lock( void ) + { + m_devMutex.lock(); + } + + inline void unlock( void ) + { + m_devMutex.unlock(); + } + + void FASTCALL writeBuffer( surroundSampleFrame * _ab, Uint32 _frames, + Uint32 _src_sample_rate, + float _master_output ); + + inline Uint32 sampleRate( void ) const + { + return( m_sampleRate ); + } + + Uint8 channels( void ) const + { + return( m_channels ); + } + + + + class setupWidget : public tabWidget + { + public: + setupWidget( const QString & _caption, QWidget * _parent ) : + tabWidget( tabWidget::tr( "Settings for %1" ).arg( + _caption ), _parent ) + { + } + + virtual ~setupWidget() + { + } + + virtual void saveSettings( void ) = 0; + + } ; + + + +protected: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ) = 0; + + // convert a given audio-buffer to a buffer in signed 16-bit samples + // returns num of bytes in outbuf + int FASTCALL convertToS16( surroundSampleFrame * _ab, Uint32 _frames, + float _master_output, + outputSampleType * _output_buffer, + bool _convert_endian = FALSE ); + + // clear given signed-int-16-buffer + void FASTCALL clearS16Buffer( outputSampleType * _outbuf, + Uint32 _frames ); + + // resample given buffer from samplerate _src_src to samplerate _dst_src + void FASTCALL resample( surroundSampleFrame * _src, Uint32 _frames, + surroundSampleFrame * _dst, + Uint32 _src_sr, Uint32 _dst_sr ); + inline void setSampleRate( Uint32 _new_sr ) + { + m_sampleRate = _new_sr; + } + + +private: + Uint32 m_sampleRate; + Uint8 m_channels; + QMutex m_devMutex; + +#ifdef HAVE_SAMPLERATE_H + SRC_DATA m_srcData; + SRC_STATE * m_srcState; +#endif + +} ; + + +#endif diff --git a/include/audio_dummy.h b/include/audio_dummy.h new file mode 100644 index 0000000000..0b7ff86f59 --- /dev/null +++ b/include/audio_dummy.h @@ -0,0 +1,80 @@ +/* + * audio_dummy.h - dummy-audio-device + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_DUMMY_H +#define _AUDIO_DUMMY_H + +#include "audio_device.h" + + +class audioDummy : public audioDevice +{ +public: + audioDummy( Uint32 _sample_rate, bool & _success_ful ) : + audioDevice( _sample_rate, DEFAULT_CHANNELS ) + { + _success_ful = TRUE; + } + + virtual ~audioDummy() + { + } + + inline static QString name( void ) + { + return( setupWidget::tr( "Dummy (no sound output)" ) ); + } + + + class setupWidget : public audioDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ) : + audioDevice::setupWidget( audioDummy::name(), _parent ) + { + } + + virtual ~setupWidget() + { + } + + virtual void saveSettings( void ) + { + } + + } ; + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame *, + Uint32 /*_frames*/, float ) + { + //usleep( (Uint32)( _frames * 1000.0f * 1000.0f / + // DEFAULT_SAMPLE_RATE ) ); + } + +} ; + + +#endif diff --git a/include/audio_file_device.h b/include/audio_file_device.h new file mode 100644 index 0000000000..a958322509 --- /dev/null +++ b/include/audio_file_device.h @@ -0,0 +1,94 @@ +/* + * audio_file_device.h - base-class for audio-device-classes which write + * their output into a file + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _AUDIO_FILE_DEVICE_H +#define _AUDIO_FILE_DEVICE_H + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "audio_device.h" + + + +class audioFileDevice : public audioDevice +{ +public: + audioFileDevice( Uint32 _sample_rate, Uint8 _channels, + const QString & _file, bool _use_vbr, + Uint16 _nom_bitrate, Uint16 _min_bitrate, + Uint16 _max_bitrate ); + virtual ~audioFileDevice(); + + +protected: + int FASTCALL writeData( const void * _data, int _len ); + void seekToBegin( void ); + + inline bool useVBR( void ) const + { + return( m_useVbr ); + } + inline Uint16 nominalBitrate( void ) const + { + return( m_nomBitrate ); + } + inline Uint16 minBitrate( void ) const + { + return( m_minBitrate ); + } + inline Uint16 maxBitrate( void ) const + { + return( m_maxBitrate ); + } + inline bool outputFileOpened( void ) const + { + return( m_outputFile.isOpen() ); + } + + +private: + QFile m_outputFile; + + bool m_useVbr; + + Uint16 m_nomBitrate; + Uint16 m_minBitrate; + Uint16 m_maxBitrate; + +} ; + + +#endif diff --git a/include/audio_file_ogg.h b/include/audio_file_ogg.h new file mode 100644 index 0000000000..2a1ae256c2 --- /dev/null +++ b/include/audio_file_ogg.h @@ -0,0 +1,102 @@ +/* + * audio_file_ogg.h - Audio-device which encodes wave-stream and writes it + * into an OGG-file. This is used for song-export. + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_FILE_OGG_H +#define _AUDIO_FILE_OGG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_VORBIS_CODEC_H + +#include + +#include "audio_file_device.h" + + + +class audioFileOgg : public audioFileDevice +{ +public: + audioFileOgg( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful, const QString & _file, + bool _use_vbr, Uint16 _nom_bitrate, + Uint16 _min_bitrate, Uint16 _max_bitrate ); + ~audioFileOgg(); + + static audioDevice * getInst( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful, + const QString & _file, + bool _use_vbr, + Uint16 _nom_bitrate, + Uint16 _min_bitrate, + Uint16 _max_bitrate ) + { + return( new audioFileOgg( _sample_rate, _channels, _success_ful, + _file, _use_vbr, _nom_bitrate, + _min_bitrate, _max_bitrate ) ); + } + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + bool startEncoding( void ); + void finishEncoding( void ); + inline int writePage( void ); + + + + int m_channels; + long m_rate; + + // Various bitrate/quality options + int m_managed; + int m_bitrate; + int m_minBitrate; + int m_maxBitrate; + + unsigned int m_serialNo; + + vorbis_comment * m_comments; + + // encoding setup - init by init_ogg_encoding + ogg_stream_state m_os; + ogg_page m_og; + ogg_packet m_op; + + vorbis_dsp_state m_vd; + vorbis_block m_vb; + vorbis_info m_vi; + +} ; + + +#endif + +#endif diff --git a/include/audio_file_wave.h b/include/audio_file_wave.h new file mode 100644 index 0000000000..c8e95a033a --- /dev/null +++ b/include/audio_file_wave.h @@ -0,0 +1,90 @@ +/* + * audio_file_wave.h - Audio-device which encodes wave-stream and writes it + * into an WAVE-file. This is used for song-export. + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_FILE_WAVE_H +#define _AUDIO_FILE_WAVE_H + + +#include "audio_file_device.h" + + + +class audioFileWave : public audioFileDevice +{ +public: + audioFileWave( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful, const QString & _file, + bool _use_vbr, Uint16 _nom_bitrate, + Uint16 _min_bitrate, Uint16 _max_bitrate ); + virtual ~audioFileWave(); + + static audioDevice * getInst( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful, + const QString & _file, bool _use_vbr, + Uint16 _nom_bitrate, + Uint16 _min_bitrate, + Uint16 _max_bitrate ) + { + return( new audioFileWave( _sample_rate, _channels, + _success_ful, _file, _use_vbr, + _nom_bitrate, _min_bitrate, + _max_bitrate ) ); + } + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + bool startEncoding( void ); + void finishEncoding( void ); + + + int m_bytesWritten; + + struct waveFileHeader + { + char riff_id[4]; // "RIFF" + Uint32 total_bytes; // total filesize-8 + char wave_fmt_str[8]; // "WAVEfmt" + Uint32 bitrate_1; // bitrate, e.g. 16 + Uint16 uncompressed; // 1 if PCM + Uint16 channels; // 1 = mono 2 = stereo + Uint32 sample_rate; // sample-rate e.g. 44100 + Uint32 bytes_per_second; // sample-rate*channels* + // (bitrate/8) + Uint16 block_alignment; // channels*(bitrate/8) + Uint16 bitrate_2; // bitrate, e.g. 16 + char data_chunk_id[4]; // "data" + Uint32 data_bytes; // total size of sample-data + } m_waveFileHeader; + + //outputSampleType * m_outputBuffer; + +} ; + + +#endif diff --git a/include/audio_jack.h b/include/audio_jack.h new file mode 100644 index 0000000000..df7eaa9431 --- /dev/null +++ b/include/audio_jack.h @@ -0,0 +1,122 @@ +/* + * audio_jack.h - support for JACK-transport + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_JACK_H +#define _AUDIO_JACK_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_JACK_JACK_H + +#define JACK_SUPPORT + +#include + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "audio_device.h" +#include "tab_widget.h" + + +class QLineEdit; +class lcdSpinBox; + + +class audioJACK : public audioDevice +{ +public: + audioJACK( Uint32 _sample_rate, bool & _success_ful ); + ~audioJACK(); + + inline static QString name( void ) + { + return( setupWidget::tr( "JACK (Jack Audio Connection Kit)" ) ); + } + + + class setupWidget : public audioDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + virtual void saveSettings( void ); + + private: + QLineEdit * m_clientName; + lcdSpinBox * m_channels; + + } ; + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + void clearBuffer( void ); + + static int processCallback( jack_nframes_t _nframes, void * _udata ); + static int bufSizeCallback( jack_nframes_t _nframes, void * _udata ); + + + jack_client_t * m_client; + vvector m_outputPorts; + struct bufset + { + sampleType * buf; + Uint32 frames; + } ; + + vlist > m_bufferSets; + Uint32 m_framesDoneInCurBuf; + volatile Uint32 m_frameSync; + + Uint32 m_jackBufSize; + + QMutex m_bufMutex; + +} ; + +#endif + +#endif diff --git a/include/audio_oss.h b/include/audio_oss.h new file mode 100644 index 0000000000..f15a3bde10 --- /dev/null +++ b/include/audio_oss.h @@ -0,0 +1,79 @@ +/* + * audio_oss.h - device-class that implements OSS-PCM-output + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_OSS_H +#define _AUDIO_OSS_H + +#include "audio_device.h" +#include "tab_widget.h" + +#define OSS_SUPPORT + + +class lcdSpinBox; +class QLineEdit; + + +class audioOSS : public audioDevice +{ +public: + audioOSS( Uint32 _sample_rate, bool & _success_ful ); + ~audioOSS(); + + inline static QString name( void ) + { + return( setupWidget::tr( "OSS (Open Sound System)" ) ); + } + + static QString probeDevice( void ); + + + class setupWidget : public audioDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + virtual void saveSettings( void ); + + private: + QLineEdit * m_device; + lcdSpinBox * m_channels; + + } ; + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + int m_audioFD; + + bool m_convertEndian; + +} ; + + +#endif diff --git a/include/audio_sample_recorder.h b/include/audio_sample_recorder.h new file mode 100644 index 0000000000..8bbfc47d0a --- /dev/null +++ b/include/audio_sample_recorder.h @@ -0,0 +1,74 @@ +/* + * audio_sample_recorder.h - device-class that implements recording + * surround-audio-buffers into RAM, maybe later + * also harddisk + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_SAMPLE_RECORDER_H +#define _AUDIO_SAMPLE_RECORDER_H + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "audio_device.h" + + +class sampleBuffer; + + +class audioSampleRecorder : public audioDevice +{ +public: + audioSampleRecorder( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful ); + ~audioSampleRecorder(); + + Uint32 framesRecorded( void ) const; + void FASTCALL createSampleBuffer( sampleBuffer * * _sample_buf ) const; + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + typedef vvector > bufferVector; + bufferVector m_buffers; + +} ; + + +#endif diff --git a/include/audio_sdl.h b/include/audio_sdl.h new file mode 100644 index 0000000000..1f86702524 --- /dev/null +++ b/include/audio_sdl.h @@ -0,0 +1,110 @@ +/* + * audio_sdl.h - device-class that performs PCM-output via SDL + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _AUDIO_SDL_H +#define _AUDIO_SDL_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SDL_SDL_AUDIO_H + +#ifndef SDL_AUDIO_SUPPORT +#define SDL_AUDIO_SUPPORT +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include SDL_SDL_H +#include SDL_SDL_AUDIO_H + + +#include "audio_device.h" +#include "tab_widget.h" + + +class QLineEdit; + + +class audioSDL : public audioDevice +{ +public: + audioSDL( Uint32 _sample_rate, bool & _success_ful ); + ~audioSDL(); + + inline static QString name( void ) + { + return( setupWidget::tr( "SDL (Simple DirectMedia Layer)" ) ); + } + + + class setupWidget : public audioDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + virtual void saveSettings( void ); + + private: + QLineEdit * m_device; + + } ; + + +private: + virtual void FASTCALL writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ); + + void clearBuffer( void ); + + static void sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ); + + SDL_AudioSpec m_audioHandle; + + outputSampleType * m_buffer; + + QMutex m_bufMutex; + QMutex m_callbackMutex; + + bool m_convertEndian; + +} ; + +#endif + +#endif diff --git a/include/basic_filters.h b/include/basic_filters.h new file mode 100644 index 0000000000..d9da2e7905 --- /dev/null +++ b/include/basic_filters.h @@ -0,0 +1,258 @@ +/* + * basic_filters.h - simple but powerful filter-class with most used filters + * + * original file by ??? + * modified and enhanced by Tobias Doerffel, 2004 + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _BASIC_FILTERS_H +#define _BASIC_FILTERS_H + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +#include + +#include "types.h" +#include "mixer.h" +#include "templates.h" + + +template +class basicFilters +{ +public: + + enum filterTypes + { + LOWPASS, + HIPASS, + BANDPASS_CSG, + BANDPASS_CZPG, + NOTCH, + ALLPASS, + MOOG, + DOUBLE_LOWPASS, + DOUBLE_MOOG + } ; + + inline basicFilters( const float _sampleRate ) : + m_b0a0( 0.0f ), + m_b1a0( 0.0f ), + m_b2a0( 0.0f ), + m_a1a0( 0.0f ), + m_a2a0( 0.0f ), + m_sampleRate( _sampleRate ), + m_subFilter( NULL ) + { + // reset in/out history + for( Uint8 _chnl = 0; _chnl < CHANNELS; ++_chnl ) + { + // reset in/out history for simple filters + m_ou1[_chnl] = m_ou2[_chnl] = m_in1[_chnl] = + m_in2[_chnl] = 0.0f; + // reset in/out historey for moog-filter + m_y1[_chnl] = m_y2[_chnl] = m_y3[_chnl] = m_y4[_chnl] = + m_oldx[_chnl] = m_oldy1[_chnl] = + m_oldy2[_chnl] = m_oldy3[_chnl] = 0.0f; + } + } + + inline ~basicFilters() + { + delete m_subFilter; + } + + inline sampleType update( sampleType _in0, Uint8 _chnl ) + { + sampleType out; + if( m_type != MOOG ) + { + // filter + out = m_b0a0*_in0 + m_b1a0*m_in1[_chnl] + + m_b2a0*m_in2[_chnl] - m_a1a0*m_ou1[_chnl] - + m_a2a0*m_ou2[_chnl]; + + // push in/out buffers + m_in2[_chnl] = m_in1[_chnl]; + m_in1[_chnl] = _in0; + m_ou2[_chnl] = m_ou1[_chnl]; + + m_ou1[_chnl] = out; + } + else + { + sampleType x = _in0 - m_r*m_y4[_chnl]; + + // Four cascaded onepole filters (bilinear transform) + m_y1[_chnl] = x*m_p + m_oldx[_chnl]*m_p - + m_k*m_y1[_chnl]; + m_y2[_chnl] = m_y1[_chnl]*m_p+m_oldy1[_chnl]*m_p - + m_k*m_y2[_chnl]; + m_y3[_chnl] = m_y2[_chnl]*m_p+m_oldy2[_chnl]*m_p - + m_k*m_y3[_chnl]; + m_y4[_chnl] = m_y3[_chnl]*m_p+m_oldy3[_chnl]*m_p - + m_k*m_y4[_chnl]; + + m_oldx[_chnl] = x; + m_oldy1[_chnl] = m_y1[_chnl]; + m_oldy2[_chnl] = m_y2[_chnl]; + m_oldy3[_chnl] = m_y3[_chnl]; + out = m_y4[_chnl] - m_y4[_chnl] * m_y4[_chnl] * + m_y4[_chnl] * ( 1.0f/6.0f ); + } + if( m_subFilter != NULL ) + { + return( m_subFilter->update( out, _chnl ) ); + } + // Clipper band limited sigmoid + return( out ); + } + + + inline void calcFilterCoeffs( const filterTypes _type, float _freq, + const float _q + /*, const bool _q_is_bandwidth = FALSE*/ ) + { + // temp coef vars + m_type = _type; + _freq = tMax( _freq, 0.01f );// limit freq for not getting + // bad noise out of the filter... + + if( m_type == MOOG || m_type == DOUBLE_MOOG ) + { + const float f = 2 * _freq / m_sampleRate; // [0 - 1] + m_k = 3.6f*f - 1.6f*f*f - 1; // (Empirical tunning) + m_p = (m_k+1)*0.5f; + m_r = _q*powf( M_E, ( ( 1-m_p ) * 1.386249f ) ); + if( m_type == DOUBLE_MOOG ) + { + if( m_subFilter == NULL ) + { + m_subFilter = + new basicFilters( + m_sampleRate ); + } + m_subFilter->calcFilterCoeffs( MOOG, _freq, + _q ); + } + } + else + { + // other filters + const float omega = 2.0f * M_PI * _freq / + m_sampleRate; + const float tsin = sinf( omega ); + const float tcos = cosf( omega ); + //float alpha; + + //if (q_is_bandwidth) + //alpha = tsin*sinhf(logf(2.0f)/2.0f*q*omega/tsin); + //else + const float alpha = tsin / ( 2.0f*_q ); + + const float a0 = 1.0f / ( 1.0f+alpha ); + + if( m_type == LOWPASS || m_type == DOUBLE_LOWPASS ) + { + m_b0a0 = ((1.0f-tcos)/2.0f)*a0; + m_b1a0 = (1.0f-tcos)*a0; + m_b2a0 = m_b0a0;//((1.0f-tcos)/2.0f)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + if( m_type == DOUBLE_LOWPASS ) + { + if( m_subFilter == NULL ) + { + m_subFilter = + new basicFilters( m_sampleRate ); + } + m_subFilter->calcFilterCoeffs( LOWPASS, + _freq, + _q ); + } + } + else if( m_type == HIPASS ) + { + m_b0a0 = ((1.0f+tcos)/2.0f)*a0; + m_b1a0 = (-1.0f-tcos)*a0; + m_b2a0 = m_b0a0;//((1.0f+tcos)/2.0f)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + } + else if( m_type == BANDPASS_CSG ) + { + m_b0a0 = (tsin/2.0f)*a0; + m_b1a0 = 0.0f; + m_b2a0 = (-tsin/2.0f)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + } + else if( m_type == BANDPASS_CZPG ) + { + m_b0a0 = alpha*a0; + m_b1a0 = 0.0f; + m_b2a0 = (-alpha)*a0; + m_a1a0 = (-2.0f*tcos)*a0; + } + else if( m_type == NOTCH ) + { + m_b0a0 = a0; + m_b1a0 = (-2.0f*tcos)*a0; + m_b2a0 = a0; + m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0; + } + else if( m_type == ALLPASS ) + { + m_b0a0 = (1.0f-alpha)*a0; + m_b1a0 = (-2.0f*tcos)*a0; + m_b2a0 = 1.0;//(1.0f+alpha)*a0; + m_a1a0 = m_b1a0;//(-2.0f*tcos)*a0; + //m_a2a0 = m_b0a0;//(1.0f-alpha)*a0; + } + m_a2a0 = (1.0f-alpha)*a0; + } + } + + +private: + // filter coeffs + float m_b0a0, m_b1a0, m_b2a0, m_a1a0, m_a2a0; + + // coeffs for moog-filter + float m_r, m_p, m_k; + + typedef sampleType frame[CHANNELS]; + + // in/out history + frame m_ou1, m_ou2, m_in1, m_in2; + + // in/out history for moog-filter + frame m_y1, m_y2, m_y3, m_y4, m_oldx, m_oldy1, m_oldy2, m_oldy3; + + filterTypes m_type; + + float m_sampleRate; + basicFilters * m_subFilter; +} ; + + +#endif diff --git a/include/bb_editor.h b/include/bb_editor.h new file mode 100644 index 0000000000..eea12f814e --- /dev/null +++ b/include/bb_editor.h @@ -0,0 +1,119 @@ +/* + * bb_editor.h - declaration of class bbEditor, a basic-component of LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _BB_EDITOR_H +#define _BB_EDITOR_H + +#include "qt3support.h" + + +#include "track_container.h" +#include "lmms_main_win.h" + + +class pixmapButton; +class songEditor; +class QPixmap; + + +class bbEditor : public trackContainer +{ + Q_OBJECT +public: + static inline bbEditor * inst( void ) + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new bbEditor(); + } + return( s_instanceOfMe ); + } + + virtual bool FASTCALL play( midiTime _start, Uint32 _start_frame, + Uint32 _frames, Uint32 _frame_base, + Sint16 _tco_num = -1 ); + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + virtual void updateAfterTrackAdd( void ); + + inline virtual QString nodeName( void ) const + { + return( "bbeditor" ); + } + + virtual inline bool fixedTCOs( void ) const + { + return( TRUE ); + } + + csize currentBB( void ) const; + void FASTCALL setCurrentBB( csize _bb ); + tact FASTCALL lengthOfBB( csize _bb ); + inline tact lengthOfCurrentBB( void ) + { + return( lengthOfBB( currentBB() ) ); + } + void FASTCALL removeBB( csize _bb ); + csize numOfBBs( void ) const; + + void FASTCALL swapBB( csize _bb1, csize _bb2 ); + + +protected: + void closeEvent( QCloseEvent * _ce ); + void keyPressEvent (QKeyEvent * _ke); + void wheelEvent( QWheelEvent * _we ); + void resizeEvent( QResizeEvent * _re ); + void updateBackground( void ); + + +protected slots: + void play( void ); + void stop( void ); + + +private: + bbEditor(); + //bbEditor( const bbEditor & ); + ~bbEditor(); + + void FASTCALL createTCOsForBB( csize _bb ); + + + static bbEditor * s_instanceOfMe; + static QPixmap * s_titleArtwork; + + pixmapButton * m_playButton; + pixmapButton * m_stopButton; + + + friend class songEditor; + friend lmmsMainWin::~lmmsMainWin(); + +} ; + + +#endif diff --git a/include/bb_track.h b/include/bb_track.h new file mode 100644 index 0000000000..b0436ca5ac --- /dev/null +++ b/include/bb_track.h @@ -0,0 +1,150 @@ +/* + * bb_track.h - class bbTrack, a wrapper for using bbEditor + * (which is a singleton-class) as track + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _BB_TRACK_H +#define _BB_TRACK_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "track.h" + +class nameLabel; +class trackContainer; + + +class bbTCO : public trackContentObject +{ + Q_OBJECT +public: + bbTCO( track * _track, const QColor & _c = QColor() ); + virtual ~bbTCO(); + + virtual void FASTCALL movePosition( const midiTime & _pos ); + virtual void FASTCALL changeLength( const midiTime & _length ); + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "bbtco" ); + } + + const QColor & color( void ) const + { + return( m_color ); + } + + +protected slots: + void openInBBEditor( bool _c ); + void openInBBEditor( void ); + void resetName( void ); + void changeName( void ); + void changeColor( void ); + + +protected: + void paintEvent( QPaintEvent * ); + void mouseDoubleClickEvent( QMouseEvent * _me ); + virtual void constructContextMenu( QMenu * ); + + +private: + QString m_name; + QColor m_color; + +} ; + + + +class bbTrack : public QObject, public track +{ + Q_OBJECT +public: + bbTrack( trackContainer * _tc ); + virtual ~bbTrack(); + + virtual trackTypes trackType( void ) const; + virtual bool FASTCALL play( const midiTime & _start, + Uint32 _start_frame, Uint32 _frames, + Uint32 _frame_base, + Sint16 _tco_num = -1 ); + virtual trackContentObject * FASTCALL createTCO( const midiTime & + _pos ); + + virtual void FASTCALL saveTrackSpecificSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadTrackSpecificSettings( const QDomElement & + _this ); + + static bbTrack * FASTCALL findBBTrack( csize _bb_num ); + static csize FASTCALL numOfBBTrack( track * _track ); + static void FASTCALL swapBBTracks( track * _track1, track * _track2 ); + + inline nameLabel * trackLabel( void ) + { + return( m_trackLabel ); + } + + +public slots: + void clickedTrackLabel( void ); + + +protected: + inline virtual QString nodeName( void ) const + { + return( "bbtrack" ); + } + + +private: + nameLabel * m_trackLabel; + + struct bbInfoStruct + { + csize num; + QString name; + } ; + static QMap s_bbNums; + +} ; + + +#endif diff --git a/include/buffer_allocator.h b/include/buffer_allocator.h new file mode 100644 index 0000000000..24f84c1699 --- /dev/null +++ b/include/buffer_allocator.h @@ -0,0 +1,78 @@ +/* + * buffer_allocator.h - namespace bufferAllocator providing routines for own + * optimized memory-management for audio-buffers + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _BUFFER_ALLOCATOR_H +#define _BUFFER_ALLOCATOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "types.h" + + + + +namespace bufferAllocator +{ + void * FASTCALL allocBytes( Uint32 _bytes ); + + template + inline T * FASTCALL alloc( Uint32 _n ) + { + return( (T *) allocBytes( sizeof( T ) * _n ) ); + } + + void FASTCALL free( void * _buf ); + + void FASTCALL cleanUp( Uint16 _level ); + + + // simple class for automatically freeing buffer in complex functions + template + class autoCleaner + { + public: + autoCleaner( T * _ptr ) : + m_ptr( _ptr ) + { + } + ~autoCleaner() + { + bufferAllocator::free( m_ptr ); + } + inline const T * ptr( void ) const + { + return( m_ptr ); + } + + private: + T * m_ptr; + } ; + +} ; + + +#endif diff --git a/include/clipboard.h b/include/clipboard.h new file mode 100644 index 0000000000..01f269b5ce --- /dev/null +++ b/include/clipboard.h @@ -0,0 +1,59 @@ +/* + * clipboard.h - the clipboard for patterns, notes etc. + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _CLIPBOARD_H +#define _CLIPBOARD_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +class settings; + +namespace clipboard +{ + typedef QMap map; + extern map content; + + void FASTCALL copy( settings * _settings_object ); + const QDomElement * FASTCALL getContent( const QString & _node_name ); +} ; + +#endif diff --git a/include/config_mgr.h b/include/config_mgr.h new file mode 100644 index 0000000000..0143aac286 --- /dev/null +++ b/include/config_mgr.h @@ -0,0 +1,187 @@ +/* + * config_mgr.h - class configManager, a class for managing LMMS-configuration + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _CONFIG_MGR_H +#define _CONFIG_MGR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include "lmms_main_win.h" + + +class QLineEdit; +class QLabel; +class QRadioButton; +class QHBoxLayout; +class QVBoxLayout; +class QFrame; + + +const QString PROJECTS_PATH = "projects/"; +const QString PRESETS_PATH = "presets/"; +const QString SAMPLES_PATH = "samples/"; +const QString ARTWORK_PATH = "artwork/"; +const QString LOCALE_PATH = "locale/"; + + +class configManager : public QDialog +{ + Q_OBJECT +public: + static inline configManager * inst( void ) + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new configManager(); + } + return( s_instanceOfMe ); + } + + const QString & lmmsWorkingDir( void ) const + { + return( m_lmmsWorkingDir ); + } + QString projectsDir( void ) const + { + return( m_lmmsWorkingDir + PROJECTS_PATH ); + } + QString presetsDir( void ) const + { + return( m_lmmsWorkingDir + PRESETS_PATH ); + } + QString samplesDir( void ) const + { + return( m_lmmsWorkingDir + SAMPLES_PATH ); + } + QString artworkDir( void ) const + { + return( m_lmmsDataDir + ARTWORK_PATH ); + } + QString localeDir( void ) const + { + return( m_lmmsDataDir + LOCALE_PATH ); + } + + const QString & value( const QString & _class, + const QString & _attribute ) const; + void setValue( const QString & _class, const QString & _attribute, + const QString & _value ); + + bool loadConfigFile( void ); + void saveConfigFile( void ); + + +protected slots: + void openWorkingDir( void ); + void setWorkingDir( const QString & _wd ); + + virtual void accept( void ); + + void backButtonClicked( void ); + void nextButtonClicked( void ); + void switchPage( csize _pg ); + void switchPage( QWidget * _pg ); + + +private: + static configManager * s_instanceOfMe; + + configManager( void ); + configManager( const configManager & _c ); + ~configManager(); + + void createWidgets( void ); + + + void FASTCALL addPage( QWidget * _w, const QString & _title ); + + static void processFilesRecursively( const QString & _src_dir, + const QString & _dst_dir, + void( * _proc_func )( const QString & _src, const QString & + _dst ) ); + + + const QString m_lmmsRcFile; + QString m_lmmsWorkingDir; + QString m_lmmsDataDir; + + typedef vvector > stringPairVector; + typedef QMap settingsMap; + settingsMap m_settings; + + + QWidget * m_pageIntro; + QWidget * m_pageWorkingDir; + QWidget * m_pageFiles; + + QRadioButton * m_samplesCopyRB; + QRadioButton * m_presetsCopyRB; + QRadioButton * m_projectsCopyRB; + + QLineEdit * m_wdLineEdit; + + // wizard stuff + vlist > m_pages; + csize m_currentPage; + QFrame * m_hbar; + QWidget * m_contentWidget; + QLabel * m_title; + QPushButton * m_cancelButton; + QPushButton * m_backButton; + QPushButton * m_nextButton; + QPushButton * m_finishButton; + QHBoxLayout * m_buttonLayout; + QHBoxLayout * m_mainLayout; + QVBoxLayout * m_contentLayout; + + + // lmmsMainWin-dtor is allowed to destroy us... + friend lmmsMainWin::~lmmsMainWin(); + +} ; + +#endif diff --git a/include/debug.h b/include/debug.h new file mode 100644 index 0000000000..0e3c3559e7 --- /dev/null +++ b/include/debug.h @@ -0,0 +1,41 @@ +/* + * debug.h - header file to be included for debugging purposes + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _DEBUG_H +#define _DEBUG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +// set whether debug-stuff (like messages on the console, asserts and other +// additional range-checkings) should be compiled + +#ifdef LMMS_DEBUG +#include +#include +#endif + + +#endif diff --git a/include/effect_board.h b/include/effect_board.h new file mode 100644 index 0000000000..d78dfb12d1 --- /dev/null +++ b/include/effect_board.h @@ -0,0 +1,31 @@ +/* + * effect_board.h - stuff for effect-board + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _EFFECT_BOARD_H +#define _EFFECT_BOARD_H + +const int MIN_EFFECT_CHANNEL = 0; +const int MAX_EFFECT_CHANNEL = 63; +const int DEFAULT_EFFECT_CHANNEL = (MIN_EFFECT_CHANNEL); + +#endif diff --git a/include/embed.h b/include/embed.h new file mode 100644 index 0000000000..314c8ccd4b --- /dev/null +++ b/include/embed.h @@ -0,0 +1,51 @@ +/* + * embed.h - misc. stuff for using embedded data (resources linked into binary) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _EMBED_H +#define _EMBED_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + +namespace embed +{ + +QPixmap getIconPixmap( const char * _name, int _w = -1, int _h = -1 ); +QString getText( const char * _name ); +void loadTranslation( const QString & _tname ); + +} + +#endif diff --git a/include/endian_handling.h b/include/endian_handling.h new file mode 100644 index 0000000000..8254e9db49 --- /dev/null +++ b/include/endian_handling.h @@ -0,0 +1,59 @@ +/* + * endian.h - handle endianess-problems + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _ENDIAN_HANDLING_H +#define _ENDIAN_HANDLING_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "types.h" + + +inline bool isLittleEndian( void ) +{ +#ifdef WORDS_BIGENDIAN + return( FALSE ); +#else + return( TRUE ); +#endif +} + + +inline Sint16 swap16IfBE( Sint16 _i ) +{ + return( isLittleEndian() ? _i : ( ( _i & 0xFF ) << 8) | ( ( _i >> 8 ) & 0xFF ) ); +} + + +inline Sint32 swap32IfBE( Sint32 _i ) +{ + return( isLittleEndian() ? _i : ( ( _i & 0xff000000 ) >> 24 ) | + ( ( _i & 0x00ff0000 ) >> 8 ) | + ( ( _i & 0x0000ff00 ) << 8 ) | + ( ( _i & 0x000000ff ) << 24 ) ); +} + +#endif diff --git a/include/envelope_and_lfo_widget.h b/include/envelope_and_lfo_widget.h new file mode 100644 index 0000000000..b9760d9c19 --- /dev/null +++ b/include/envelope_and_lfo_widget.h @@ -0,0 +1,174 @@ +/* + * envelope_and_lfo_widget.h - declaration of class envelopeAndLFOWidget which + * is used by envelope/lfo/filter-tab of + * channel-window + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _ENVELOPE_AND_LFO_WIDGET_H +#define _ENVELOPE_AND_LFO_WIDGET_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "settings.h" +#include "types.h" +#include "spc_bg_hndl_widget.h" + + +class envelopeTabWidget; +class pixmapButton; +class knob; +class QPaintEvent; +class QPixmap; + + + +class envelopeAndLFOWidget : public QWidget, public settings, + public specialBgHandlingWidget +{ + Q_OBJECT +public: + envelopeAndLFOWidget( float _value_for_zero_amount, QWidget * _parent ); + ~envelopeAndLFOWidget(); + + static inline float expKnobVal( float val ) + { + return( ( ( val < 0 ) ? -1 : 1 ) * val*val ); + } + static void triggerLFO( void ); + static void resetLFO( void ); + + float FASTCALL level( Uint32 _frame, Uint32 _release_begin, + Uint32 _frame_offset ) const; + + inline bool used( void ) const + { + return( m_used ); + } + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "el" ); + } + + +public slots: + void updateSampleVars( void ); + + +protected: + void paintEvent( QPaintEvent * _pe ); + void mousePressEvent( QMouseEvent * _me ); + + float FASTCALL lfoLevel( Uint32 _frame, Uint32 _frame_offset ) const; + + +protected slots: + void updateAfterKnobChange( float ); + void lfoSinWaveCh( bool ); + void lfoTriangleWaveCh( bool ); + void lfoSawWaveCh( bool ); + void lfoSquareWaveCh( bool ); + void x100Toggled( bool ); + + +private: + static Uint32 s_lfoFrame; + + bool m_used; + + // envelope-stuff + knob * m_predelayKnob; + knob * m_attackKnob; + knob * m_holdKnob; + knob * m_decayKnob; + knob * m_sustainKnob; + knob * m_releaseKnob; + knob * m_amountKnob; + static QPixmap * s_envGraph; + + float m_sustainLevel; + float m_amount; + float m_valueForZeroAmount; + float m_amountAdd; + Uint32 m_pahdFrames; + Uint32 m_rFrames; + float * m_pahdEnv; + float * m_rEnv; + + // LFO-stuff + knob * m_lfoPredelayKnob; + knob * m_lfoAttackKnob; + knob * m_lfoSpeedKnob; + knob * m_lfoAmountKnob; + pixmapButton * m_sinLfoBtn; + pixmapButton * m_triangleLfoBtn; + pixmapButton * m_sawLfoBtn; + pixmapButton * m_sqrLfoBtn; + static QPixmap * s_lfoGraph; + + pixmapButton * m_x100Btn; + pixmapButton * m_controlEnvAmountBtn; + + Uint32 m_lfoPredelayFrames; + Uint32 m_lfoAttackFrames; + Uint32 m_lfoOscillationFrames; + float m_lfoAmount; + bool m_lfoAmountIsZero; + float * m_lfoShapeData; + + enum lfoShapes + { + SIN, + TRIANGLE, + SAW, + SQUARE + } m_lfoShape; + + volatile bool m_busy; + + + + friend class envelopeTabWidget; + +} ; + +#endif diff --git a/include/envelope_tab_widget.h b/include/envelope_tab_widget.h new file mode 100644 index 0000000000..d7276b3cfe --- /dev/null +++ b/include/envelope_tab_widget.h @@ -0,0 +1,107 @@ +/* + * envelope_tab_widget.h - declaration of class envelopeTabWidget which + * provides UI- and DSP-code for using envelopes, LFOs + * and a filter + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _ENVELOPE_TAB_WIDGET_H +#define _ENVELOPE_TAB_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "settings.h" +#include "basic_filters.h" +#include "envelope_and_lfo_widget.h" + + +class QComboBox; +class groupBox; +class tabWidget; +class QLabel; +class knob; +class pixmapButton; +class channelTrack; +class notePlayHandle; + + +class envelopeTabWidget : public QWidget, public settings +{ + Q_OBJECT +public: + envelopeTabWidget( channelTrack * _channel_track, QWidget * parent ); + ~envelopeTabWidget(); + + void FASTCALL processAudioBuffer( sampleFrame * _ab, Uint32 _frames, + notePlayHandle * _n ); + + enum targets + { + VOLUME, +/* PANNING, + PITCH,*/ + CUT, + RES, + TARGET_COUNT + } ; + + Uint32 envFrames( void ); + Uint32 releaseFrames( void ); + + float FASTCALL volumeLevel( notePlayHandle * _n, Uint32 _frame ); + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "eldata" ); + } + + +private: + channelTrack * m_channelTrack; + + tabWidget * m_targetsTabWidget; + envelopeAndLFOWidget * m_envLFOWidgets[TARGET_COUNT]; + + // filter-stuff + groupBox * m_filterGroupBox; + QComboBox * m_filterComboBox; + knob * m_filterCutKnob; + knob * m_filterResKnob; + +} ; + +#endif diff --git a/include/export.h b/include/export.h new file mode 100644 index 0000000000..dc98310134 --- /dev/null +++ b/include/export.h @@ -0,0 +1,61 @@ +/* + * export.h - header which is needed for song-export + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _EXPORT_H +#define _EXPORT_H + +#include "types.h" + +class audioDevice; + + +typedef audioDevice * ( * getDeviceInst)( Uint32 _sample_rate, + Uint32 _channels, + bool & _success_ful, + const QString & _file, + bool _use_vbr, + Uint16 _nom_bitrate, + Uint16 _min_bitrate, + Uint16 _max_bitrate ); + + +enum fileTypes +{ + WAVE_FILE, + OGG_FILE, + NULL_FILE = 0xFF +} ; + + +struct fileEncodeDevice +{ + fileTypes m_fileType; + const char * m_description; + const char * m_extension; + getDeviceInst m_getDevInst; +} ; + +extern fileEncodeDevice fileEncodeDevices[]; + + +#endif diff --git a/include/export_project_dialog.h b/include/export_project_dialog.h new file mode 100644 index 0000000000..0789b5a943 --- /dev/null +++ b/include/export_project_dialog.h @@ -0,0 +1,111 @@ +/* + * export_project_dialog.h - declaration of class exportProjectDialog which is + * responsible for exporting project + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _EXPORT_PROJECT_DIALOG_H +#define _EXPORT_PROJECT_DIALOG_H + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "export.h" + + +class QLabel; +class QPushButton; +class QComboBox; +class QProgressBar; +class QCheckBox; +class pixmapButton; + + +class exportProjectDialog : public QDialog +{ + Q_OBJECT +public: + exportProjectDialog( const QString & _file_name, QWidget * _parent ); + ~exportProjectDialog(); + void FASTCALL updateProgressBar( int _new_val ); + + +public slots: + void exportBtnClicked( void ); + + +protected: + void keyPressEvent( QKeyEvent * _ke ); + void closeEvent( QCloseEvent * _ce ); + + +private slots: + void changedType( const QString & ); + void cancelBtnClicked( void ); + void redrawProgressBar( void ); + + +private: + QString m_fileName; + QLabel * m_typeLbl; + QComboBox * m_typeCombo; + QLabel * m_kbpsLbl; + QComboBox * m_kbpsCombo; + QCheckBox * m_vbrCb; + QCheckBox * m_hqmCb; + QLabel * m_hourglassLbl; + QPushButton * m_exportBtn; + QPushButton * m_cancelBtn; + QProgressBar * m_exportProgressBar; + fileTypes m_fileType; + bool m_deleteFile; + int m_oldProgressVal; + int m_progressVal; + + QTimer * m_progressBarUpdateTimer; + + static Sint16 s_availableBitrates[]; + + void finishProjectExport( void ); + void abortProjectExport( void ); + + static fileTypes FASTCALL getFileTypeFromExtension( const QString & + _ext ); + +} ; + +#endif diff --git a/include/group_box.h b/include/group_box.h new file mode 100644 index 0000000000..565701a2af --- /dev/null +++ b/include/group_box.h @@ -0,0 +1,85 @@ +/* + * group_box.h - LMMS-groupbox + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _GROUP_BOX_H +#define _GROUP_BOX_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include "pixmap_button.h" + +class QPixmap; + + +class groupBox : public QWidget +{ + Q_OBJECT +public: + groupBox( const QString & _caption, QWidget * _parent ); + ~groupBox(); + + bool isActive( void ) const + { +#ifdef QT4 + return( m_led->isChecked() ); +#else + return( m_led->isOn() ); +#endif + } + + +public slots: + void setState( bool _on, bool _anim = FALSE ); + void animate( void ); + + +protected: + virtual void resizeEvent( QResizeEvent * _re ); + virtual void mousePressEvent( QMouseEvent * _me ); + + +private: + void updatePixmap( void ); + + static QPixmap * s_ledBg; + + pixmapButton * m_led; + QString m_caption; + + int m_origHeight; + bool m_animating; + +} ; + +#endif diff --git a/include/interpolation.h b/include/interpolation.h new file mode 100644 index 0000000000..dfce10a0ad --- /dev/null +++ b/include/interpolation.h @@ -0,0 +1,93 @@ +/* + * interpolation.h - fast implementations of several interpolation-algorithms + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _INTERPOLATION_H +#define _INTERPOLATION_H + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif + +#include + + +inline float hermiteInterpolate( float x0, float x1, float x2, float x3, + float frac_pos ) +{ + const float frsq = frac_pos*frac_pos; + const float frsq2 = 2*frsq; + return( ( (x2-x0) *0.5f ) * ( frac_pos * (frsq+1) -frsq2 ) + + ( frsq2*frac_pos - 3*frsq ) * ( x1-x2 ) + + frsq2 * (frac_pos-1) * ( ( x3-x1 ) * 0.25f ) + x1 ); + +/* + const float frsq = frac_pos*frac_pos; + //const float frsq2 = 2*frsq; + frac_pos *= 0.5; + const float frcu = frsq*frac_pos; + return ( + + (frcu - frsq + frac_pos) * ((x2 - x0)) + + + (4*frcu - 3*frsq) * (x1 - x2) + //frsq*(2*frac_pos-3) * (x1 - x2) + + + (frcu - 0.5*frsq)*((x3 - x1)) + + + x1 + + ); +*/ +} + + + +inline float cubicInterpolate( float v0, float v1, float v2, float v3, float x ) +{ + float frsq = x*x; + float frcu = frsq*v0; + float t1 = v3 + 3*v1; + + return( v1 + 0.5f * frcu + x * ( v2 - frcu * ( 1.0f/6.0f ) - + t1 * ( 1.0f/6.0f ) - v0 / 3.0f ) + frsq * x * ( t1 * + ( 1.0f/6.0f ) - 0.5f * v2 ) + frsq * ( 0.5f * v2 - v1 ) ); +} + + + +inline float cosinusInterpolate( float v0, float v1, float x ) +{ + float f = cosf( x * ( M_PI*0.5f ) ); + return( v0*f + v1*( 1.0f-f ) ); +} + + + +inline float linearInterpolate( float v0, float v1, float x ) +{ + return( v0*( 1.0f-x ) + v1*x ); +} + + +#endif diff --git a/include/kmultitabbar.h b/include/kmultitabbar.h new file mode 100644 index 0000000000..070f9924c4 --- /dev/null +++ b/include/kmultitabbar.h @@ -0,0 +1,320 @@ +/*************************************************************************** + kmultitabbar.h - description + ------------------- + begin : 2001 + copyright : (C) 2001,2002,2003 by Joseph Wenninger + ***************************************************************************/ + +/*************************************************************************** + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + ***************************************************************************/ + +#ifndef _KMultitabbar_h_ +#define _KMultitabbar_h_ + +#include "qt3support.h" + +class KMultiTabBarButton; +class KMultiTabBarTab; + +#ifdef QT4 + +#include +#include +//#include +#include +#include +#include +#include +#include + +typedef QList buttonList; +typedef QList tabList; + +#else + +#include +#include +#include +#include +#include +#include +#include + +typedef QPtrList buttonList; +typedef QPtrList tabList; + +#endif + + +class QPixmap; +class QPainter; +class QFrame; + +class KMultiTabBarPrivate; +class KMultiTabBarTabPrivate; +class KMultiTabBarButtonPrivate; +class KMultiTabBarInternal; + +/** + * A Widget for horizontal and vertical tabs. + * It is possible to add normal buttons to the top/left + * The handling if only one tab at a time or multiple tabs + * should be raisable is left to the "user". + *@author Joseph Wenninger + */ +class KMultiTabBar: public QWidget +{ + Q_OBJECT +public: + //enum KMultiTabBarMode{ Horizontal, Vertical }; + enum KMultiTabBarPosition{DockLeft, DockRight, DockTop, DockBottom}; + + /** + * VSNET == Visual Studio .Net like (only show the text of active tabs + * KDEV3 == Kdevelop 3 like (always show the text) + * KONQSBC == konqy's classic sidebar style (unthemed), this one is disabled + * at the moment, but will be renabled soon too + */ + enum KMultiTabBarStyle{VSNET=0, KDEV3=1, KONQSBC=2, KDEV3ICON=3,STYLELAST=0xffff}; + + KMultiTabBar(Qt::Orientation o,QWidget *parent=0); + virtual ~KMultiTabBar(); + + /** + * append a new button to the button area. The button can later on be accessed with button(ID) + * eg for connecting signals to it + * @param pic a pixmap for the button + * @param id an arbitraty ID value. It will be emitted in the clicked signal for identifying the button + * if more than one button is connected to a signals. + * @param popup A popup menu which should be displayed if the button is clicked + * @param not_used_yet will be used for a popup text in the future + */ + int appendButton(const QPixmap &pic,int id=-1,QMenu* popup=0,const QString& not_used_yet=QString::null); + /** + * remove a button with the given ID + */ + void removeButton(int id); + /** + * append a new tab to the tab area. It can be accessed lateron with tabb(id); + * @param pic a bitmap for the tab + * @param id an arbitrary ID which can be used later on to identify the tab + * @param text if a mode with text is used it will be the tab text, otherwise a mouse over hint + */ + int appendTab(const QPixmap &pic,int id=-1,const QString& text=QString::null); + /** + * remove a tab with a given ID + */ + void removeTab(int id); + /** + * set a tab to "raised" + * @param id The ID of the tab to manipulate + * @param state true == activated/raised, false == not active + */ + void setTab(int id ,bool state); + /** + * return the state of a tab, identified by it's ID + */ + bool isTabRaised(int id) const; + /** + * get a pointer to a button within the button area identified by its ID + */ + class KMultiTabBarButton *button(int id) const; + + /** + * get a pointer to a tab within the tab area, identiifed by its ID + */ + class KMultiTabBarTab *tab(int id) const; + /** + * set the real position of the widget. + * @param pos if the mode is horizontal, only use top, bottom, if it is vertical use left or right + */ + void setPosition(KMultiTabBarPosition pos); + /** + * set the display style of the tabs + */ + void setStyle(KMultiTabBarStyle style); + /** + * be carefull, don't delete tabs yourself and don't delete the list itself + */ + tabList * tabs(); + /** + * be carefull, don't delete buttons yourself and don't delete the list itself + */ + buttonList * buttons(); + + /** + * might vanish, not sure yet + */ + void showActiveTabTexts(bool show=true); +protected: + friend class KMultiTabBarButton; + virtual void fontChange( const QFont& ); + void updateSeparator(); +private: + class KMultiTabBarInternal *m_internal; + QBoxLayout *m_l; + QFrame *m_btnTabSep; + buttonList m_buttons; + KMultiTabBarPosition m_position; + KMultiTabBarPrivate *d; +}; + +/** + * This class should never be created except with the appendButton call of KMultiTabBar + */ +class KMultiTabBarButton: public QPushButton +{ + Q_OBJECT +public: + KMultiTabBarButton(const QPixmap& pic,const QString&, QMenu *popup, + int id,QWidget *parent, KMultiTabBar::KMultiTabBarPosition pos, KMultiTabBar::KMultiTabBarStyle style); + KMultiTabBarButton(const QString&, QMenu *popup, + int id,QWidget *parent, KMultiTabBar::KMultiTabBarPosition pos, KMultiTabBar::KMultiTabBarStyle style); + virtual ~KMultiTabBarButton(); + int id() const; + +public slots: + /** + * this is used internaly, but can be used by the user, if (s)he wants to + * It the according call of KMultiTabBar is invoked though this modifications will be overwritten + */ + void setPosition(KMultiTabBar::KMultiTabBarPosition); + /** + * this is used internaly, but can be used by the user, if (s)he wants to + * It the according call of KMultiTabBar is invoked though this modifications will be overwritten + */ + void setStyle(KMultiTabBar::KMultiTabBarStyle); + + /** + * modify the text of the button + */ + void setText(const QString &); + + QSize sizeHint() const; + +protected: + KMultiTabBar::KMultiTabBarPosition m_position; + KMultiTabBar::KMultiTabBarStyle m_style; + QString m_text; + virtual void hideEvent( class QHideEvent*); + virtual void showEvent( class QShowEvent*); +private: + int m_id; + KMultiTabBarButtonPrivate *d; +signals: + /** + * this is emitted if the button is clicked + * @param id the ID identifying the button + */ + void clicked(int id); +protected slots: + virtual void slotClicked(); +}; + +/** + * This class should never be created except with the appendTab call of KMultiTabBar + */ +class KMultiTabBarTab: public KMultiTabBarButton +{ + Q_OBJECT +public: + KMultiTabBarTab(const QPixmap& pic,const QString&,int id,QWidget *parent, + KMultiTabBar::KMultiTabBarPosition pos,KMultiTabBar::KMultiTabBarStyle style); + virtual ~KMultiTabBarTab(); + /** + * set the active state of the tab + * @param state true==active false==not active + */ + void setState(bool state); + /** + * choose if the text should always be displayed + * this is only used in classic mode if at all + */ + void showActiveTabText(bool show); + void resize(){ setSize( neededSize() ); } +#ifndef QT4 + virtual inline const QColorGroup & palette( void ) const + { + return( colorGroup() ); + } +#endif +private: + bool m_showActiveTabText; + int m_expandedSize; + KMultiTabBarTabPrivate *d; +protected: + friend class KMultiTabBarInternal; + void setSize(int); + int neededSize(); + void updateState(); + virtual void drawButton(QPainter *); + virtual void drawButtonLabel(QPainter *); + void drawButtonStyled(QPainter *); + void drawButtonClassic(QPainter *); +protected slots: + virtual void slotClicked(); + void setTabsPosition(KMultiTabBar::KMultiTabBarPosition); + +public slots: + virtual void setIcon(const QString&); + virtual void setIcon(const QPixmap&); +}; + + + +class KMultiTabBarInternal: public Q3ScrollView +{ + Q_OBJECT +public: + KMultiTabBarInternal(QWidget *parent,Qt::Orientation o); + int appendTab(const QPixmap &,int=-1,const QString& =QString::null); + KMultiTabBarTab *tab(int) const; + void removeTab(int); + void setPosition(enum KMultiTabBar::KMultiTabBarPosition pos); + void setStyle(enum KMultiTabBar::KMultiTabBarStyle style); + void showActiveTabTexts(bool show); + tabList * tabs(){return &m_tabs;} +#ifndef QT4 + virtual inline const QColorGroup & palette( void ) const + { + return( colorGroup() ); + } +#endif +private: + friend class KMultiTabBar; + QWidget *box; + QBoxLayout *mainLayout; + tabList m_tabs; + enum KMultiTabBar::KMultiTabBarPosition m_position; + bool m_showActiveTabTexts; + enum KMultiTabBar::KMultiTabBarStyle m_style; + int m_expandedTabSize; + int m_lines; +protected: + virtual void drawContents ( QPainter *, int, int, int, int); + + /** + * [contentsM|m]ousePressEvent are reimplemented from QScrollView + * in order to ignore all mouseEvents on the viewport, so that the + * parent can handle them. + */ + virtual void contentsMousePressEvent(QMouseEvent *); + virtual void mousePressEvent(QMouseEvent *); + virtual void resizeEvent(QResizeEvent *); +}; + +#endif diff --git a/include/knob.h b/include/knob.h new file mode 100644 index 0000000000..feff4061cf --- /dev/null +++ b/include/knob.h @@ -0,0 +1,191 @@ +/* + * knob.h - powerful knob-widget + * + * This file is based on the knob-widget of the Qwt Widget Library by Josef Wilgen + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _KNOB_H +#define _KNOB_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "templates.h" + + +class QPixmap; +class textFloat; + + +enum knobTypes +{ + knobDark_28, knobBright_26, knobSmall_17 +} ; + + + +class knob : public QWidget +{ + Q_OBJECT +public: + knob( int _knob_num, QWidget * _parent, const QString & _name ); + virtual ~knob(); + + enum ScrollMode + { + ScrNone, + ScrMouse, + ScrTimer, + ScrDirect, + ScrPage + } ; + + void setTracking( bool _enable ); + void setHintText( const QString & _txt_before, + const QString & _txt_after ); + void setLabel( const QString & _txt ); + + void setTotalAngle( float _angle ); + + void setRange( float _vmin, float _vmax, float _vstep = 0.0, + int _pagesize = 1 ); + + inline float value( void ) const + { + return( m_value ); + } + + void setStep( float ); + + inline float maxValue( void ) const + { + return( m_maxValue ); + } + inline float minValue( void ) const + { + return( m_minValue ); + } + + inline void incPages( int _n_pages ) + { + setNewValue( m_value + float( _n_pages ) * m_pageSize, 1 ); + } + + + +public slots: + void setValue( float _val, bool _is_init_value = FALSE ); + void fitValue( float _val ); + void incValue( int _steps ); + void reset( void ); + void copyValue( void ); + void pasteValue( void ); + void connectToMidiDevice( void ); + void displayHelp( void ); + + +signals: + void valueChanged( float value ); + void sliderPressed( void ); + void sliderReleased( void ); + void sliderMoved( float value ); + + +protected: + virtual void paintEvent( QPaintEvent * _me ); + virtual void resizeEvent( QResizeEvent * _me ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void wheelEvent( QWheelEvent * _me ); + virtual void contextMenuEvent( QContextMenuEvent * _me ); + + void drawKnob( QPainter * _p ); + void setPosition( const QPoint & _p ); + + +private: + void layoutKnob( bool _update = TRUE ); + float getValue( const QPoint & _p ); + void getScrollMode( const QPoint & _p, int & _scroll_mode, + int & _direction ); + void recalcAngle( void ); + + void valueChange( void ); + void rangeChange( void ); + + + void buttonReleased( void ); + + void setNewValue( float x, int align = 0 ); + + + static float s_copiedValue; + static textFloat * s_textFloat; + + + int m_knobWidth; + int m_scrollMode; + float m_mouseOffset; + int m_direction; + int m_tracking; + QPoint m_origMousePos; + + + float m_angle; + float m_oldAngle; + float m_totalAngle; + float m_nTurns; + + QPixmap * m_knobPixmap; + int m_knobNum; + QString m_hintTextBeforeValue; + QString m_hintTextAfterValue; + QString m_label; + + + float m_minValue; + float m_maxValue; + float m_step; + float m_pageSize; + float m_value; + float m_exactValue; + float m_exactPrevValue; + float m_prevValue; + float m_initValue; + +} ; + +#endif diff --git a/include/lcd_spinbox.h b/include/lcd_spinbox.h new file mode 100644 index 0000000000..1a92975e0d --- /dev/null +++ b/include/lcd_spinbox.h @@ -0,0 +1,84 @@ +/* + * lcd_spinbox.h - class lcdSpinBox, an improved QLCDNumber + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _LCD_SPINBOX_H +#define _LCD_SPINBOX_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + + +class lcdSpinBox : public QWidget +{ + Q_OBJECT +public: + lcdSpinBox( int _min, int _max, int _num_digits, QWidget * _parent ); + virtual ~lcdSpinBox(); + + void setStep( int _step ); + + inline int value( void ) const + { + return( m_number->intValue() ); + } + + void setValue( int _value ); + void setLabel( const QString & _txt ); + + +protected: + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void wheelEvent( QWheelEvent * _we ); + + +private: + int m_value; + int m_minValue; + int m_maxValue; + int m_step; + + QLCDNumber * m_number; + QLabel * m_label; + + QPoint m_origMousePos; + + +signals: + void valueChanged( int ); + +} ; + +#endif diff --git a/include/led_checkbox.h b/include/led_checkbox.h new file mode 100644 index 0000000000..53711a436c --- /dev/null +++ b/include/led_checkbox.h @@ -0,0 +1,67 @@ +/* + * led_checkbox.h - class ledCheckBox, an improved QCheckBox + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _LED_CHECKBOX_H +#define _LED_CHECKBOX_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +class QPixmap; + + +class ledCheckBox : public QCheckBox +{ +public: + enum ledColors + { + YELLOW, GREEN, TOTAL_COLORS + } ; + + ledCheckBox( const QString & _txt, QWidget * _parent, + ledColors _color = YELLOW ); + virtual ~ledCheckBox(); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + + +private: + QPixmap * m_ledOnPixmap; + QPixmap * m_ledOffPixmap; + +} ; + +#endif diff --git a/include/midi.h b/include/midi.h new file mode 100644 index 0000000000..5c2ccee314 --- /dev/null +++ b/include/midi.h @@ -0,0 +1,107 @@ +/* + * midi.h - constants, structs etc. concerning MIDI + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIDI_H +#define _MIDI_H + + +#include "types.h" + + + +enum midiEventTypes +{ + // messages + NOTE_OFF = 0x80, + NOTE_ON = 0x90, + KEY_PRESSURE = 0xA0, + CONTROL_CHANGE = 0xB0, + PROGRAM_CHANGE = 0xC0, + CHANNEL_PRESSURE = 0xD0, + PITCH_BEND = 0xE0, + // system exclusive + MIDI_SYSEX = 0xF0, + // system common - never in midi files + MIDI_TIME_CODE = 0xF1, + MIDI_SONG_POSITION = 0xF2, + MIDI_SONG_SELECT = 0xF3, + MIDI_TUNE_REQUEST = 0xF6, + MIDI_EOX = 0xF7, + // system real-time - never in midi files + MIDI_SYNC = 0xF8, + MIDI_TICK = 0xF9, + MIDI_START = 0xFA, + MIDI_CONTINUE = 0xFB, + MIDI_STOP = 0xFC, + MIDI_ACTIVE_SENSING = 0xFE, + MIDI_SYSTEM_RESET = 0xFF, + // meta event - for midi files only + MIDI_META_EVENT = 0xFF +} ; + + + +struct midiEvent +{ + midiEvent( midiEventTypes _type = MIDI_ACTIVE_SENSING, + Uint8 _channel = 0, + Uint16 _param1 = 0, + Uint16 _param2 = 0 ) : + m_type( _type ), + m_channel( _channel ), + m_sysExData( NULL ) + { + m_data.m_param[0] = _param1; + m_data.m_param[1] = _param2; + } + midiEvent( midiEventTypes _type, char * _sysex_data, int _data_len ) : + m_type( _type ), + m_channel( 0 ), + m_sysExData( _sysex_data ) + { + m_data.m_sysExDataLen = _data_len; + } + inline Uint16 key( void ) const + { + return( m_data.m_param[0] ); + } + inline Uint32 velocity( void ) const + { + return( m_data.m_param[1] ); + } + + midiEventTypes m_type; // MIDI event type + Uint8 m_channel; // MIDI channel + union + { + Uint16 m_param[2]; // first/second parameter (key/velocity) + int m_sysExDataLen; // len of m_sysExData + } m_data; + + char * m_sysExData; + +} ; + + +#endif diff --git a/include/midi_alsa_raw.h b/include/midi_alsa_raw.h new file mode 100644 index 0000000000..988268df1e --- /dev/null +++ b/include/midi_alsa_raw.h @@ -0,0 +1,107 @@ +/* + * midi_alsa_raw.h - midi-device-driver for RawMIDI via ALSA + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIDI_ALSA_RAW_H +#define _MIDI_ALSA_RAW_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_ALSA_ASOUNDLIB_H + +#ifndef ALSA_SUPPORT +#define ALSA_SUPPORT +#endif + +#include + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "midi_device.h" + + +struct pollfd; +class QLineEdit; + + +class midiALSARaw : public midiDevice, public QThread +{ +public: + midiALSARaw( channelTrack * _ct = NULL ); + ~midiALSARaw(); + + static QString probeDevice( void ); + + + inline static QString name( void ) + { + return( setupWidget::tr( "ALSA Raw-MIDI (Advanced Linux Sound " + "Architecture)" ) ); + } + + + class setupWidget : public midiDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + virtual void saveSettings( void ); + + private: + QLineEdit * m_device; + + } ; + + +protected: + virtual void FASTCALL sendByte( Uint8 _c ); + virtual void FASTCALL run( void ); + + +private: + snd_rawmidi_t * m_input, * * m_inputp; + snd_rawmidi_t * m_output, * * m_outputp; + int m_npfds; + pollfd * m_pfds; + + volatile bool m_quit; + +} ; + +#endif + +#endif diff --git a/include/midi_dummy.h b/include/midi_dummy.h new file mode 100644 index 0000000000..e8276bc7ae --- /dev/null +++ b/include/midi_dummy.h @@ -0,0 +1,77 @@ +/* + * midi_dummy.h - dummy MIDI-driver + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIDI_DUMMY_H +#define _MIDI_DUMMY_H + + +#include "midi_device.h" +#include "tab_widget.h" + + +class midiDummy : public midiDevice +{ +public: + midiDummy() : + midiDevice() + { + } + ~midiDummy() + { + } + + inline static QString name( void ) + { + return( setupWidget::tr( "Dummy (no MIDI support)" ) ); + } + + + class setupWidget : public midiDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ) : + midiDevice::setupWidget( midiDummy::name(), _parent ) + { + } + + virtual ~setupWidget() + { + } + + virtual void saveSettings( void ) + { + } + + } ; + + +protected: + virtual void FASTCALL sendByte( Uint8 ) + { + } + +} ; + + +#endif diff --git a/include/midi_mapper.h b/include/midi_mapper.h new file mode 100644 index 0000000000..ab595acd7e --- /dev/null +++ b/include/midi_mapper.h @@ -0,0 +1,112 @@ +/* + * midi_mapper.h - MIDI-mapper for any midiDevice + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIDI_MAPPER_H +#define _MIDI_MAPPER_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "types.h" + + +const Uint8 MIDI_PROGRAMS = 128; +const Uint8 MIDI_KEYS = 128; +const Uint8 MIDI_CHANNELS = 16; + +class midiMapper +{ +public: + midiMapper( const QString & _map ); + ~midiMapper(); + + inline const QString & programName( Uint8 _program ) const + { + return( m_patchMap[_program].second ); + } + + inline Uint8 mappedProgramNumber( Uint8 _program ) const + { + return( m_patchMap[_program].first ); + } + + inline const QString & drumsetKeyName( Uint8 _key ) const + { + return( m_drumsetKeyMap[_key].second ); + } + inline Uint8 keyForDrumName( const QString & _name ) const + { + for( Uint8 i = 0; i < MIDI_KEYS; ++i ) + { + if( m_drumsetKeyMap[i].second == _name ) + { + return( i ); + } + } + return( 0 ); + } + + inline Uint8 drumsetChannel( void ) const + { + return( m_drumsetChannel ); + } + + inline Uint8 drumsetPatch( void ) const + { + return( m_drumsetPatch ); + } + + inline Uint8 mappedChannel( Uint8 _channel ) const + { + return( m_channelMap[_channel] ); + } + + +private: + void readPatchMap( QFile & _f ); + void readDrumsetKeyMap( QFile & _f ); + void readChannelMap( QFile & _f ); + + QPair m_patchMap[MIDI_PROGRAMS]; + QPair m_drumsetKeyMap[MIDI_KEYS]; + Uint8 m_channelMap[MIDI_CHANNELS]; + Uint8 m_drumsetChannel; + Uint8 m_drumsetPatch; + +} ; + + +#endif diff --git a/include/midi_oss.h b/include/midi_oss.h new file mode 100644 index 0000000000..c08b93fa9e --- /dev/null +++ b/include/midi_oss.h @@ -0,0 +1,79 @@ +/* + * midi_oss.h - OSS-driver for MIDI-port + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIDI_OSS_H +#define _MIDI_OSS_H + +#include +#include + +#include "midi_device.h" + + +class QLineEdit; + + +class midiOSS : public midiDevice, public QThread +{ +public: + midiOSS( channelTrack * _ct = NULL ); + ~midiOSS(); + + static QString probeDevice( void ); + + + inline static QString name( void ) + { + return( setupWidget::tr( "OSS Raw-MIDI (Open Sound System)" ) ); + } + + + class setupWidget : public midiDevice::setupWidget + { + public: + setupWidget( QWidget * _parent ); + virtual ~setupWidget(); + + virtual void saveSettings( void ); + + private: + QLineEdit * m_device; + + } ; + + +protected: + virtual void FASTCALL sendByte( Uint8 _c ); + virtual void FASTCALL run( void ); + + +private: + QFile m_midiDev; + + volatile bool m_quit; + +} ; + + +#endif diff --git a/include/midi_time.h b/include/midi_time.h new file mode 100644 index 0000000000..74abfba1f0 --- /dev/null +++ b/include/midi_time.h @@ -0,0 +1,122 @@ +/* + * midi_time.h - declaration of class midiTime which provides data-type for + * position- and length-variables + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIDI_TIME_H +#define _MIDI_TIME_H + +#include "types.h" + + +class midiTime +{ +public: + inline midiTime( tact _tact, tact64th _tact_64th ) : + m_tact( _tact ), + m_tact64th( _tact_64th ) + { + } + + inline midiTime( Sint32 _abs = 0 ) : + m_tact( _abs / 64 ), + m_tact64th( _abs % 64 ) + { + } + + inline midiTime( const midiTime & _t ) + { + *this = _t; + } + + inline midiTime & operator=( const midiTime & _t ) + { + m_tact = _t.m_tact; + m_tact64th = _t.m_tact64th; + return( *this ); + } + + inline midiTime & operator+=( const midiTime & _t ) + { + return( *this = static_cast( *this ) + + static_cast( _t ) ); + } + + inline midiTime & operator-=( const midiTime & _t ) + { + return( *this = static_cast( *this ) - + static_cast( _t ) ); + } + + inline void setTact( tact _t ) + { + m_tact = _t; + } + + inline tact getTact( void ) const + { + return( m_tact ); + } + + inline void setTact64th( tact64th _t ) + { + m_tact64th = _t; + } + + inline tact64th getTact64th( void ) const + { + return( m_tact64th ); + } + + // converts time-class in an absolute value, useful for calculations, + // comparisons and so on... + inline operator Sint32( void ) const + { + return( static_cast( m_tact ) * 64 + + static_cast( m_tact64th ) ); + } + + // calculate number of frame that are needed this time + inline Uint32 frames( float _frames_per_tact ) const + { + if( m_tact >= 0 ) + { + return( static_cast( m_tact * + _frames_per_tact + + m_tact64th * + _frames_per_tact / + 64.0f ) ); + } + return( 0 ); + } + + +private: + tact m_tact; + tact64th m_tact64th; + +} ; + + +#endif + diff --git a/include/mixer.h b/include/mixer.h new file mode 100644 index 0000000000..eceda1ef74 --- /dev/null +++ b/include/mixer.h @@ -0,0 +1,341 @@ +/* + * mixer.h - audio-device-independent mixer for LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MIXER_H +#define _MIXER_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include "types.h" +#include "volume.h" +#include "panning.h" +#include "note.h" +#include "play_handle.h" + + +class audioDevice; +class midiDevice; +class lmmsMainWin; + + +const int DEFAULT_BUFFER_SIZE = 512; + +const Uint16 MAX_SAMPLE_PACKETS = 256; // how many parallel audio-samples- + // buffers shall be maximal exist and + // mixed together? + +const Uint8 DEFAULT_CHANNELS = 2; + +const Uint8 SURROUND_CHANNELS = +#ifndef DISABLE_SURROUND + 4; +#else + 2; +#endif + +const Uint8 QUALITY_LEVELS = 2; +const Uint32 DEFAULT_QUALITY_LEVEL = 0; +const Uint32 HIGH_QUALITY_LEVEL = DEFAULT_QUALITY_LEVEL+1; +extern Uint32 SAMPLE_RATES[QUALITY_LEVELS]; +const Uint32 DEFAULT_SAMPLE_RATE = 44100;//SAMPLE_RATES[DEFAULT_QUALITY_LEVEL]; + + +typedef sampleType sampleFrame[DEFAULT_CHANNELS]; +typedef sampleType surroundSampleFrame[SURROUND_CHANNELS]; + +typedef struct +{ + float vol[SURROUND_CHANNELS]; +} volumeVector; + + +const Uint32 BYTES_PER_SAMPLE = sizeof( sampleType ); +const Uint32 BYTES_PER_FRAME = sizeof( sampleFrame ); +const Uint32 BYTES_PER_SURROUND_FRAME = sizeof( surroundSampleFrame ); +const Uint32 BYTES_PER_OUTPUT_SAMPLE = sizeof( outputSampleType ); + +const float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f; + + +const float BASE_FREQ = 440.0f; +const tones BASE_TONE = A; +const octaves BASE_OCTAVE = OCTAVE_4; + + + +class mixer : +#ifndef QT4 + public QObject, +#endif + public QThread +{ + Q_OBJECT +public: + static inline mixer * inst( void ) + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new mixer(); + } + return( s_instanceOfMe ); + } + + + void FASTCALL addBuffer( sampleFrame * _buf, Uint32 _frames, + Uint32 _framesAhead, + volumeVector & _volumeVector ); + inline Uint32 framesPerAudioBuffer( void ) const + { + return( m_framesPerAudioBuffer ); + } + + + inline bool highQuality( void ) const + { + return( m_qualityLevel > DEFAULT_QUALITY_LEVEL ); + } + + + inline const surroundSampleFrame * currentAudioBuffer( void ) const + { + return( m_curBuf ); + } + + // audio-device-stuff + inline const QString & audioDevName( void ) const + { + return( m_audioDevName ); + } + + void FASTCALL setAudioDevice( audioDevice * _dev, bool _hq ); + void restoreAudioDevice( void ); + + + // MIDI-device-stuff + inline const QString & midiDevName( void ) const + { + return( m_midiDevName ); + } + + inline midiDevice * getMIDIDevice( void ) + { + return( m_midiDev ); + } + + + inline void addPlayHandle( playHandle * _ph ) + { + m_playHandles.push_back( _ph ); + } + + inline void removePlayHandle( playHandle * _ph ) + { + m_playHandlesToRemove.push_back( _ph ); + } + + void FASTCALL checkValidityOfPlayHandles( void ); + + + inline int sampleRate( void ) + { + return( SAMPLE_RATES[m_qualityLevel] ); + } + + + inline float masterOutput( void ) const + { + return( m_masterOutput ); + } + + inline void setMasterOutput( float _mo ) + { + m_masterOutput = _mo; + } + + + static inline sampleType clip( sampleType _s ) + { + if( _s > 1.0f ) + { + return( 1.0f ); + } + else if( _s < -1.0f ) + { + return( -1.0f ); + } + return( _s ); + } + + + inline void pause( void ) + { + m_safetySyncMutex.lock(); + } + + inline void play( void ) + { + m_safetySyncMutex.unlock(); + } + + + void clear( void ); + + + void FASTCALL clearAudioBuffer( sampleFrame * _ab, Uint32 _frames ); +#ifndef DISABLE_SURROUND + void FASTCALL clearAudioBuffer( surroundSampleFrame * _ab, + Uint32 _frames ); +#endif + + inline bool haveNoRunningNotes( void ) const + { + return( m_playHandles.size() == 0 ); + } + + +public slots: + void setHighQuality( bool _hq_on = FALSE ); + + +signals: + void sampleRateChanged( void ); + void nextAudioBuffer( const surroundSampleFrame *, Uint32 _frames ); + + +private: + struct samplePacket + { + surroundSampleFrame * m_buffer; // actual buffer for + // wave-data + Uint32 m_frames; + Uint32 m_framesDone; + Uint32 m_framesAhead; // number of frames, the buffer + // should be mixed ahead + volume m_vol; + panning m_pan; + enum samplePacketStates + { + READY, FILLING, UNUSED + } m_state; + } ; + + + static mixer * s_instanceOfMe; + + mixer(); + ~mixer(); + + void quitThread( void ); + + + // we don't allow to create mixer by using copy-ctor + mixer( const mixer & ) : +#ifndef QT4 + QObject(), +#endif + QThread(), + m_curBuf( m_buffer1 ), + m_nextBuf( m_buffer2 ) + { + } + + virtual void run( void ); + + + void FASTCALL mixSamplePacket( samplePacket * _sp ); + + + audioDevice * tryAudioDevices( void ); + midiDevice * tryMIDIDevices( void ); + + + + sampleFrame * m_silence; +#ifndef DISABLE_SURROUND + surroundSampleFrame * m_surroundSilence;// cool, silence in surround ;-) +#endif + + + samplePacket m_samplePackets[MAX_SAMPLE_PACKETS]; + + Uint32 m_framesPerAudioBuffer; + + surroundSampleFrame * m_buffer1; + surroundSampleFrame * m_buffer2; + + surroundSampleFrame * m_curBuf; + surroundSampleFrame * m_nextBuf; + + bool m_discardCurBuf; + + + playHandleVector m_playHandles; + playHandleVector m_playHandlesToRemove; + + Uint8 m_qualityLevel; + volatile float m_masterOutput; + + volatile bool m_quit; + + + + audioDevice * m_audioDev; + audioDevice * m_oldAudioDev; + QString m_audioDevName; + + + midiDevice * m_midiDev; + QString m_midiDevName; + + + QMutex m_safetySyncMutex; + QMutex m_devMutex; + + + friend class lmmsMainWin; + +} ; + + +#endif diff --git a/include/mmp.h b/include/mmp.h new file mode 100644 index 0000000000..5490a8ab5a --- /dev/null +++ b/include/mmp.h @@ -0,0 +1,109 @@ +/* + * mmp.h - class for reading and writing multimedia-project-files + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _MMP_H +#define _MMP_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "types.h" + + +class multimediaProject : public QDomDocument +{ +public: + enum projectTypes + { + UNKNOWN, + SONG_PROJECT, + CHANNEL_SETTINGS, + EFFECT_SETTINGS, + VIDEO_PROJECT, // will come later... + BURN_PROJECT, // will come later... + PLAYLIST, // will come later... + PROJ_TYPE_COUNT + } ; + + + multimediaProject( const QString & _in_file_name ); + multimediaProject( projectTypes _project_type ); + ~multimediaProject(); + + bool FASTCALL writeFile( const QString & _fn, + bool _overwrite_check = TRUE ); + + inline QDomElement & content( void ) + { + return( m_content ); + } + inline QDomElement & head( void ) + { + return( m_head ); + } + + inline projectTypes type( void ) const + { + return( m_type ); + } + + static projectTypes FASTCALL typeOfFile( const QString & _fn ); + + +private: + static projectTypes FASTCALL type( const QString & _type_name ); + static QString FASTCALL typeName( projectTypes _project_type ); + + struct typeDescStruct + { + projectTypes m_type; + QString m_name; + } ; + static typeDescStruct s_types[PROJ_TYPE_COUNT]; + + QDomElement m_content; + QDomElement m_head; + projectTypes m_type; + +} ; + + +const Uint8 MMP_MAJOR_VERSION = 1; +const Uint8 MMP_MINOR_VERSION = 0; +const QString MMP_VERSION_STRING = QString::number( MMP_MAJOR_VERSION ) + "." + + QString::number( MMP_MINOR_VERSION ); + + +#endif + diff --git a/include/name_label.h b/include/name_label.h new file mode 100644 index 0000000000..c396e76110 --- /dev/null +++ b/include/name_label.h @@ -0,0 +1,75 @@ +/* + * name_label.h - class nameLabel, a label which is renamable by + * double-clicking it + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _NAME_LABEL_H +#define _NAME_LABEL_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +class nameLabel : public QLabel +{ + Q_OBJECT +public: + nameLabel( const QString & _initial_name, QWidget * _parent, + const QPixmap & _pm = QPixmap() ); + ~nameLabel(); + const QPixmap * pixmap( void ) const; + + +public slots: + void setPixmap( const QPixmap & _pm ); + void rename( void ); + + +signals: + void nameChanged( const QString & _new_name ); + void clicked( void ); + + +protected: + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseDoubleClickEvent( QMouseEvent * _me ); + virtual void paintEvent( QPaintEvent * _pe ); + + +private: + QPixmap m_pm; + +} ; + +#endif diff --git a/include/note.h b/include/note.h new file mode 100644 index 0000000000..f3293988e3 --- /dev/null +++ b/include/note.h @@ -0,0 +1,167 @@ +/* + * note.h - declaration of class note which contains all informations about a + * note + definitions of several constants and enums + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _NOTE_H +#define _NOTE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include "volume.h" +#include "panning.h" +#include "midi_time.h" +#include "settings.h" + +enum tones +{ + C = 0, + CIS = 1, DES = 1, + D = 2, + DIS = 3, ES = 3, + E = 4, FES = 4, + F = 5, + FIS = 6, GES = 6, + G = 7, + GIS = 8, AS = 8, + A = 9, + AIS = 10, B = 10, + H = 11 +} ; + + +enum octaves +{ + OCTAVE_0, + OCTAVE_1, + OCTAVE_2, + OCTAVE_3, + OCTAVE_4, // default + OCTAVE_5, + OCTAVE_6, + OCTAVE_7, + OCTAVE_8 +} ; + +const octaves DEFAULT_OCTAVE = OCTAVE_4; +const octaves MIN_OCTAVE = OCTAVE_0; +const octaves MAX_OCTAVE = OCTAVE_8; + +const int WHITE_KEYS_PER_OCTAVE = 7; +const int BLACK_KEYS_PER_OCTAVE = 5; +const int NOTES_PER_OCTAVE = WHITE_KEYS_PER_OCTAVE + BLACK_KEYS_PER_OCTAVE; +const int OCTAVES = 9; + + + +class note : public settings +{ +public: + note( const midiTime & _length = 0, const midiTime & _pos = 0, + tones _tone = A, octaves _octave = DEFAULT_OCTAVE, + volume _volume = DEFAULT_VOLUME, + panning _panning = DEFAULT_PANNING ) FASTCALL; + ~note(); + void FASTCALL setLength( const midiTime & _length ); + void FASTCALL setPos( const midiTime & _pos ); + void FASTCALL setTone( tones _tone = C ); + void FASTCALL setOctave( octaves _octave = DEFAULT_OCTAVE ); + void FASTCALL setKey( int _key ); + void FASTCALL setVolume( volume _volume = DEFAULT_VOLUME ); + void FASTCALL setPanning( panning _panning = DEFAULT_PANNING ); + inline midiTime endPos( void ) const + { + return( m_pos + m_length); + } + inline const midiTime & length( void ) const + { + return( m_length ); + } + inline const midiTime & pos( void ) const + { + return( m_pos ); + } + inline midiTime pos( midiTime _base_pos ) const + { + return( m_pos - _base_pos ); + } + inline tones tone( void ) const + { + return( m_tone ); + } + inline octaves octave( void ) const + { + return( m_octave ); + } + inline int key( void ) const + { + return( m_octave * NOTES_PER_OCTAVE + m_tone ); + } + inline volume getVolume( void ) const + { + return( m_volume ); + } + inline panning getPanning( void ) const + { + return( m_panning ); + } + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "note" ); + } + + +private: + tones m_tone; + octaves m_octave; + volume m_volume; + panning m_panning; + midiTime m_length; + midiTime m_pos; + +} ; + + +typedef vvector noteVector; + + +#endif + diff --git a/include/note_play_handle.h b/include/note_play_handle.h new file mode 100644 index 0000000000..3bd5e0ccbc --- /dev/null +++ b/include/note_play_handle.h @@ -0,0 +1,180 @@ +/* + * note_play_handle.h - declaration of class notePlayHandle which is needed + * by LMMS-Play-Engine + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _NOTE_PLAY_HANDLE_H +#define _NOTE_PLAY_HANDLE_H + +#include + +#include "play_handle.h" +#include "basic_filters.h" +#include "note.h" + + +class channelTrack; +class notePlayHandle; + +typedef vvector notePlayHandleVector; + + +class notePlayHandle : public playHandle, public note +{ +public: + void * m_pluginData; + basicFilters<> * m_filter; + + notePlayHandle( channelTrack * _chnl_trk, Uint32 _frames_ahead, + Uint32 _frames, note * n, + bool _arp_note = FALSE ) FASTCALL; + ~notePlayHandle(); + + + virtual void play( void ); + + virtual inline bool done( void ) const + { + return( ( m_released && m_framesBeforeRelease == 0 && + m_releaseFramesDone >= m_releaseFramesToDo ) || + m_channelTrack == NULL ); + } + + virtual void checkValidity( void ); + + + void FASTCALL noteOff( Uint32 _s = 0 ); + + inline Uint32 framesBeforeRelease( void ) const + { + return( m_framesBeforeRelease ); + } + + inline Uint32 releaseFramesDone( void ) const + { + return( m_releaseFramesDone ); + } + + Uint32 actualReleaseFramesToDo( void ) const; + + // returns how many samples this note is aligned ahead, i.e. + // at which position it is inserted in the according buffer + inline Uint32 framesAhead( void ) const + { + return ( m_framesAhead ); + } + + // returns total numbers of frames to play + inline Uint32 frames( void ) const + { + return( m_frames ); + } + + void setFrames( Uint32 _frames ); + + // returns whether note was released + inline bool released( void ) const + { + return( m_released ); + } + + // returns total numbers of played frames + inline Uint32 totalFramesPlayed( void ) const + { + return( m_totalFramesPlayed ); + } + + // returns volume-level at frame _frame (envelope/LFO) + float FASTCALL volumeLevel( Uint32 _frame ); + + // adds note-play-handle _n as subnote + inline void addSubNote( notePlayHandle * _n ) + { + m_subNotes.push_back( _n ); + _n->m_baseNote = FALSE; + // if there was an arp-note added and this note is a base-note + // we set arp-note-flag for indicating that this note is an + // arpeggio-base-note + m_arpNote = _n->arpNote() && baseNote(); + } + + // returns channel-track this note-play-handle plays + inline channelTrack * getChannelTrack( void ) + { + return( m_channelTrack ); + } + + // returns whether note is a base-note, e.g. is not part of an arpeggio + // or a chord + inline bool baseNote( void ) const + { + return( m_baseNote ); + } + + // returns whether note is part of an arpeggio + inline bool arpNote( void ) const + { + return( m_arpNote ); + } + + // returns whether note is base-note for arpeggio + inline bool arpBaseNote( void ) const + { + return( baseNote() && arpNote() ); + } + + inline bool muted( void ) const + { + return( m_muted ); + } + + void mute( void ); + + +private: + channelTrack * m_channelTrack; // needed for calling + // channelTrack::playNote + Uint32 m_frames; // total frames to play + Uint32 m_framesAhead; // numbers of frames ahead in buffer + // to mix in + Uint32 m_totalFramesPlayed; // total frame-counter - used for + // figuring out whether a whole note + // has been played + Uint32 m_framesBeforeRelease; // number of frames after which note + // is released + Uint32 m_releaseFramesToDo; // total numbers of frames to be + // played after release + Uint32 m_releaseFramesDone; // number of frames done after + // release of note + notePlayHandleVector m_subNotes;// used for chords and arpeggios + bool m_released; // indicates whether note is released + bool m_baseNote; // indicates whether note is a + // base-note (i.e. no sub-note) + bool m_arpNote; // indicates whether note is part of + // an arpeggio (either base-note or + // sub-note) + bool m_muted; // indicates whether note is muted + +} ; + +#endif diff --git a/include/nstate_button.h b/include/nstate_button.h new file mode 100644 index 0000000000..68cd7bd3b9 --- /dev/null +++ b/include/nstate_button.h @@ -0,0 +1,91 @@ +/* + * nstate_button.h - declaration of class nStateButton + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _NSTATE_BUTTON_H +#define _NSTATE_BUTTON_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +class nStateButton : public QWidget +{ + Q_OBJECT +public: + nStateButton( QWidget * _parent ); + virtual ~nStateButton(); + void addState( const QPixmap & _pixmap, const QString & _tooltip = "" ); + + inline void setGeneralToolTip( const QString & _tooltip ) + { + m_generalToolTip = _tooltip; + } + + inline int state( void ) const + { + return( m_curState ); + } + + +public slots: + void changeState( int _n ); + + +signals: + void stateChanged( int _n ); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + virtual void mousePressEvent( QMouseEvent * _me ); + + +private: + vvector > m_states; + QString m_generalToolTip; + + int m_curState; + +} ; + +#endif diff --git a/include/oscillator.h b/include/oscillator.h new file mode 100644 index 0000000000..a5ead6a4f8 --- /dev/null +++ b/include/oscillator.h @@ -0,0 +1,211 @@ +/* + * oscillator.h - header-file for oscillator.cpp, a powerful oscillator-class + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _OSCILLATOR_H +#define _OSCILLATOR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "mixer.h" +#include "interpolation.h" + + +// fwd-decl because we need it for the typedef below... +class oscillator; + +typedef void ( oscillator:: * oscFuncPtr ) + ( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ); + + + +class oscillator +{ +public: + enum waveShapes + { + SIN_WAVE, + TRIANGLE_WAVE, + SAW_WAVE, + SQUARE_WAVE, + MOOG_SAW_WAVE, + EXP_WAVE, + WHITE_NOISE_WAVE, + USER_DEF_WAVE + } ; + + enum modulationAlgos + { + FREQ_MODULATION, AMP_MODULATION, MIX, SYNC + } ; + + oscillator( modulationAlgos _modulation_algo, float _freq, + Sint16 _phase_offset, float _volume_factor, + oscillator * _m_subOsc ); + inline virtual ~oscillator() + { + delete m_subOsc; + } + inline void setUserWave( const sampleFrame * _data, Uint32 _frames ) + { + m_userWaveData = _data; + m_userWaveFrames = _frames; + } + inline void update( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ) + { + ( this->*m_callUpdate )( _ab, _frames, _chnl ); + } + inline void setNewFreq( float _new_freq ) + { + // save current state - we need it later for restoring same + // phase (otherwise we'll get clicks in the audio-stream) + const float v = m_sample * m_oscCoeff; + m_freq = _new_freq; + recalcOscCoeff( phase( v ) ); + } + + static oscillator * FASTCALL createNewOsc( waveShapes _wave_shape, + modulationAlgos _modulation_algo, float _freq, + Sint16 _phase_offset, float _volume_factor, + oscillator * _m_subOsc = NULL ); + + inline bool syncOk( void ) + { + const float v1 = m_sample * m_oscCoeff; + const float v2 = ++m_sample * m_oscCoeff; + // check whether v2 is in next period + return( floorf( v2 ) > floorf( v1 ) ); + } + static inline float phase( float _sample ) + { + float t; + return( modff( _sample, &t ) ); + //return( _sample - floorf( _sample ) ); + } + // now follow the wave-shape-routines... + static inline sampleType sinSample( float _sample ) + { + return( sinf( _sample * static_cast( 2.0f * M_PI + ) ) ); + } + + static inline sampleType triangleSample( float _sample ) + { + const float ph = phase( _sample ); + if( ph <= 0.25f ) + { + return( ph * 4.0f ); + } + else if( ph <= 0.75f ) + { + return( 2.0f - ph * 4.0f ); + } + return( ph * 4.0f - 4.0f ); + } + + static inline sampleType sawSample( float _sample ) + { + return( -1.0f + phase( _sample ) * 2.0f ); + } + + static inline sampleType squareSample( float _sample ) + { + return( ( phase( _sample ) > 0.5f ) ? -1.0f : 1.0f ); + } + + static inline sampleType moogSawSample( float _sample ) + { + const float ph= phase( _sample ); + if( ph < 0.5f ) + { + return( -1.0f + ph * 4.0f ); + } + return( 1.0f - 2.0f * ph ); + } + + static inline sampleType expSample( float _sample ) + { + float ph = phase( _sample ); + if( ph > 0.5f ) + { + ph = 1.0f - ph; + } + return( -1.0f + 8.0f * ph * ph ); + } + + static inline sampleType noiseSample( float ) + { + return( 1.0f - 2.0f * ( ( float )rand() * ( 1.0f / + RAND_MAX ) ) ); + } + inline sampleType userWaveSample( float _sample ) + { + const float frame = phase( _sample ) * m_userWaveFrames; + const Uint32 f1 = static_cast( frame ); + const Uint32 f2 = ( f1 + 1 ) % m_userWaveFrames; + return( linearInterpolate( m_userWaveData[f1][0], + m_userWaveData[f2][0], + frame - floorf( frame ) ) ); + } + + +protected: + float m_freq; + float m_volumeFactor; + Sint16 m_phaseOffset; + oscillator * m_subOsc; + Uint32 m_sample; + float m_oscCoeff; + sampleFrame const * m_userWaveData; + Uint32 m_userWaveFrames; + oscFuncPtr m_callUpdate; + + + virtual void FASTCALL updateNoSub( sampleFrame * _ab, Uint32 _frames, + Uint8 _chnl ) = 0; + virtual void FASTCALL updateFM( sampleFrame * _ab, Uint32 _frames, + Uint8 _chnl ) = 0; + virtual void FASTCALL updateAM( sampleFrame * _ab, Uint32 _frames, + Uint8 _chnl ) = 0; + virtual void FASTCALL updateMix( sampleFrame * _ab, Uint32 _frames, + Uint8 _chnl ) = 0; + virtual void FASTCALL updateSync( sampleFrame * _ab, Uint32 _frames, + Uint8 _chnl ) = 0; + + inline void sync( void ) + { + m_sample = 0; + } + void FASTCALL recalcOscCoeff( const float _additional_phase_offset = + 0.0 ); + +} ; + + +#endif diff --git a/include/panning.h b/include/panning.h new file mode 100644 index 0000000000..6fdd7b1214 --- /dev/null +++ b/include/panning.h @@ -0,0 +1,35 @@ +/* + * panning.h - declaration of some constants and types, concerning the + * panning of a note + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _PANNING_H +#define _PANNING_H + +#include "types.h" + +const panning PANNING_LEFT = ( 0 - 100); +const panning PANNING_RIGHT = ( 0 + 100); +const panning PANNING_CENTER = 0; +const panning DEFAULT_PANNING = PANNING_CENTER; + +#endif diff --git a/include/pattern.h b/include/pattern.h new file mode 100644 index 0000000000..15851671e1 --- /dev/null +++ b/include/pattern.h @@ -0,0 +1,216 @@ +/* + * pattern.h - declaration of class pattern, which contains all informations + * about a pattern + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PATTERN_H +#define _PATTERN_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include "note.h" +#include "track.h" +#include "mixer.h" + + +class channelTrack; +class sampleBuffer; +class audioSampleRecorder; +class QTimer; +class QProgressBar; +class QPushButton; +class QPixmap; +class patternFreezeStatusDialog; + + + +const int MAX_BEATS_PER_TACT = 16; +const int MAIN_BEATS_PER_TACT = 4; + + +class pattern : public trackContentObject +{ + Q_OBJECT +public: + enum patternTypes + { + BEAT_PATTERN, MELODY_PATTERN/*, EVENT_PATTERN*/ + } ; + + pattern( channelTrack * _channel_track ); + pattern( const pattern & _pat_to_copy ) FASTCALL; + virtual ~pattern(); + + virtual void FASTCALL movePosition( const midiTime & _pos ); + + + virtual midiTime length( void ) const; + note * FASTCALL addNote( const note & _new_note ); + void FASTCALL removeNote( const note * _note_to_del ); + note * FASTCALL rearrangeNote( const note * _note_to_proc ); + void clearNotes( void ); + + inline noteVector & notes( void ) + { + return( m_notes ); + } + + inline patternTypes type( void ) const + { + return( m_patternType ); + } + void FASTCALL setType( patternTypes _new_pattern_type ); + inline const QString & name( void ) const + { + return( m_name ); + } + inline void setName( const QString & _name ) + { + m_name = _name; + update(); + } + inline channelTrack * getChannelTrack( void ) + { + return( m_channelTrack ); + } + + // functions which are part of freezing-feature + inline bool frozen( void ) const + { + return( m_frozenPattern != NULL ); + } + void FASTCALL playFrozenData( sampleFrame * _ab, Uint32 _start_frame, + Uint32 _frames ); + inline bool isFreezing( void ) const + { + return( m_freezeRecorder != NULL ); + } + void finishFreeze( void ); + + + note * FASTCALL noteAt( int _note_num ); + void FASTCALL setNoteAt( int _note_num, note _new_note ); + void checkType( void ); + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "pattern" ); + } + + +protected slots: + void openInPianoRoll( bool _c ); + void openInPianoRoll( void ); + void clear( void ); + void resetName( void ); + void changeName( void ); + void freeze( void ); + void unfreeze( void ); + void updateFreezeStatusDialog( void ); + void abortFreeze( void ); + + +protected: + void paintEvent( QPaintEvent * _pe ); + void mousePressEvent( QMouseEvent * _me ); + void mouseDoubleClickEvent( QMouseEvent * _me ); + virtual void constructContextMenu( QMenu * ); + + void ensureBeatNotes( void ); + + +private: + static QPixmap * s_patternBg; + static QPixmap * s_stepBtnOn; + static QPixmap * s_stepBtnOff; + static QPixmap * s_stepBtnOffLight; + static QPixmap * s_frozen; + + static void initPixmaps( void ); + + + channelTrack * m_channelTrack; + patternTypes m_patternType; + QString m_name; + noteVector m_notes; + + QMutex m_frozenPatternMutex; + sampleBuffer * m_frozenPattern; + audioSampleRecorder * m_freezeRecorder; + patternFreezeStatusDialog * m_freezeStatusDialog; + QTimer * m_freezeStatusUpdateTimer; +} ; + + + + +class patternFreezeStatusDialog : public QDialog +{ + Q_OBJECT +public: + patternFreezeStatusDialog(); + ~patternFreezeStatusDialog(); + + void FASTCALL setProgress( int _p ); + + +protected: + void closeEvent( QCloseEvent * _ce ); + + +protected slots: + void cancelBtnClicked( void ); + + +private: + QProgressBar * m_progressBar; + QPushButton * m_cancelBtn; + + +signals: + void aborted( void ); + +} ; + + +#endif diff --git a/include/pch.h b/include/pch.h new file mode 100644 index 0000000000..f44e92218b --- /dev/null +++ b/include/pch.h @@ -0,0 +1,69 @@ +// Qt-headers +#include + +#if QT_VERSION >= 0x040000 + +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + + + +// C++ STL / C-stdlib +#include +#include +#include +#include +#include +#include + diff --git a/include/piano_roll.h b/include/piano_roll.h new file mode 100644 index 0000000000..e2c0b70cec --- /dev/null +++ b/include/piano_roll.h @@ -0,0 +1,237 @@ +/* + * piano_roll.h - declaration of class pianoRoll which is a window where you + * can set and edit notes in an easy way + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PIANO_ROLL_H +#define _PIANO_ROLL_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + +#include "types.h" +#include "note.h" + + +class QScrollBar; +class QPainter; +class QPixmap; + +class crystalButton; +class pattern; +class notePlayHandle; +class pixmapButton; +class timeLine; + + + +class pianoRoll : public QWidget +{ + Q_OBJECT +public: + static inline pianoRoll * inst( void ) + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new pianoRoll(); + } + return( s_instanceOfMe ); + } + + void FASTCALL setCurrentPattern( pattern * _new_pattern ); + inline const pattern * currentPattern( void ) const + { + return( m_pattern ); + } + inline bool validPattern( void ) const + { + return( m_pattern != NULL ); + } + + +protected: + void closeEvent( QCloseEvent * _ce ); + void paintEvent( QPaintEvent * _pe ); + void resizeEvent( QResizeEvent * _re ); + void mousePressEvent( QMouseEvent * _me ); + void mouseReleaseEvent( QMouseEvent * _me ); + void mouseMoveEvent( QMouseEvent * _me ); + void keyPressEvent( QKeyEvent * _ke ); + void keyReleaseEvent( QKeyEvent * _ke ); + void wheelEvent( QWheelEvent * _we ); + + int FASTCALL getKey( int _y ); + inline void drawNoteRect( QPainter & _p, Uint16 _x, Uint16 _y, + Sint16 _width, bool _is_selected ); + void removeSelection( void ); + void selectAll( void ); + void FASTCALL getSelectedNotes( noteVector & _selected_notes ); + + +protected slots: + void play( void ); + void record( void ); + void stop( void ); + + void recordNote( const note & _n ); + + void horScrolled( int _new_pos ); + void verScrolled( int _new_pos ); + + void drawButtonToggled( bool = FALSE ); + void eraseButtonToggled( bool = FALSE ); + void selectButtonToggled( bool = FALSE ); + void moveButtonToggled( bool = FALSE ); + + void copySelectedNotes( void ); + void cutSelectedNotes( void ); + void pasteNotes( void ); + void deleteSelectedNotes( void ); + + void updatePosition( const midiTime & _t ); + + +private: + + enum editModes + { + DRAW, + ERASE, + SELECT, + MOVE + } ; + + enum actions + { + NONE, + MOVE_NOTE, + RESIZE_NOTE, + SELECT_NOTES, + MOVE_SELECTION, + CHANGE_NOTE_VOLUME, + CHANGE_NOTE_PANNING + } ; + + enum pianoRollKeyTypes + { + PR_WHITE_KEY_SMALL, + PR_WHITE_KEY_BIG, + PR_BLACK_KEY + } ; + + + static pianoRoll * s_instanceOfMe; + + static QPixmap * s_whiteKeyBigPm; + static QPixmap * s_whiteKeySmallPm; + static QPixmap * s_artwork1; + static QPixmap * s_artwork2; + static QPixmap * s_blackKeyPm; + static QPixmap * s_toolDraw; + static QPixmap * s_toolErase; + static QPixmap * s_toolSelect; + static QPixmap * s_toolMove; + + static pianoRollKeyTypes prKeyOrder[]; + + + pixmapButton * m_playButton; + pixmapButton * m_recordButton; + pixmapButton * m_stopButton; + + crystalButton * m_drawButton; + crystalButton * m_eraseButton; + crystalButton * m_selectButton; + crystalButton * m_moveButton; + + crystalButton * m_cutButton; + crystalButton * m_copyButton; + crystalButton * m_pasteButton; + + pattern * m_pattern; + QScrollBar * m_leftRightScroll; + QScrollBar * m_topBottomScroll; + + midiTime m_currentPosition; + bool m_recording; + + note * m_currentNote; + actions m_action; + + Uint32 m_selectStartTact64th; + int m_selectedTact64th; + int m_selectStartKey; + int m_selectedKeys; + + int m_moveStartKey; + int m_moveStartTact64th; + int m_moveXOffset; + + int m_notesEditHeight; + int m_ppt; + int m_totalKeysToScroll; + + midiTime m_lenOfNewNotes; + + bool m_shiftPressed; + bool m_controlPressed; + + int m_startKey; // first key when drawing + + int m_lastKey; + + noteVector m_notesToCopy; + noteVector m_selNotesForMove; + + + editModes m_editMode; + + + timeLine * m_timeLine; + bool m_scrollBack; + + + pianoRoll( void ); + pianoRoll( const pianoRoll & ); + ~pianoRoll(); + + +signals: + void positionChanged( const midiTime & ); + +} ; + + +#endif + diff --git a/include/piano_widget.h b/include/piano_widget.h new file mode 100644 index 0000000000..fd113aa3cf --- /dev/null +++ b/include/piano_widget.h @@ -0,0 +1,104 @@ +/* + * piano_widget.h - declaration of class pianoWidget, a widget which provides + * an interactive piano/keyboard-widget + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PIANO_WIDGET_H +#define _PIANO_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "note.h" + + +class channelTrack; +class notePlayHandle; + + +enum keyTypes +{ + WHITE_KEY, + BLACK_KEY +} ; + + +class pianoWidget : public QWidget +{ + Q_OBJECT +public: + pianoWidget( channelTrack * _channel_track ); + ~pianoWidget(); + + +protected: + void paintEvent( QPaintEvent * ); + void mousePressEvent( QMouseEvent * me ); + void mouseReleaseEvent( QMouseEvent * me ); + void mouseMoveEvent( QMouseEvent * me ); + void keyPressEvent( QKeyEvent * ke ); + void keyReleaseEvent( QKeyEvent * ke ); + void focusInEvent( QFocusEvent * _fe ); + void focusOutEvent( QFocusEvent * _fe ); + + +private: + int FASTCALL getKeyFromMouse( const QPoint & _p ); + int FASTCALL getKeyFromKeyboard( int _k ) const; + int FASTCALL getKeyX( int _key_num ); + + static QPixmap * s_whiteKeyPm; + static QPixmap * s_blackKeyPm; + static QPixmap * s_whiteKeyPressedPm; + static QPixmap * s_blackKeyPressedPm; + + QScrollBar * m_pianoScroll; + channelTrack * m_channelTrack; + tones m_startTone; // first key when drawing + octaves m_startOctave; + + int m_lastKey; + + +private slots: + void pianoScrolled( int _new_pos ); + +} ; + + +#endif + diff --git a/include/pixmap_button.h b/include/pixmap_button.h new file mode 100644 index 0000000000..6376b780c6 --- /dev/null +++ b/include/pixmap_button.h @@ -0,0 +1,78 @@ +/* + * pixmap_button.h - declaration of class pixmapButton + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PIXMAP_BUTTON_H +#define _PIXMAP_BUTTON_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + + +class pixmapButton : public QPushButton +{ + Q_OBJECT +public: + pixmapButton( QWidget * _parent ); + virtual ~pixmapButton(); + virtual void FASTCALL setActiveGraphic( const QPixmap & _pm ); + virtual void FASTCALL setInactiveGraphic( const QPixmap & _pm, + bool _update = TRUE ); + void FASTCALL setBgGraphic( const QPixmap & _pm ); + + +signals: + void doubleClicked( void ); + void clickedRight( void ); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseDoubleClickEvent( QMouseEvent * _me ); + + +private: + QPixmap * m_activePixmap; + QPixmap * m_inactivePixmap; + QPixmap * m_bgPixmap; + +} ; + +#endif diff --git a/include/play_handle.h b/include/play_handle.h new file mode 100644 index 0000000000..cc96636bfa --- /dev/null +++ b/include/play_handle.h @@ -0,0 +1,76 @@ +/* + * play_handle.h - base-class playHandle which is needed by + * LMMS-Play-Engine + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PLAY_HANDLE_H +#define _PLAY_HANDLE_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +class playHandle +{ +public: + inline playHandle( void ) + { + } + virtual inline ~playHandle() + { + } + virtual void play( void ) = 0; + virtual bool done( void ) const = 0; + + // play-handles can invalidate themselves if an object they depend on + // is going to be deleted or things like that - every of those objects + // has to call mixer::inst()->checkValidityOfPlayHandles() in it's dtor + // and set flag before, so LMMS doesn't crash because these play-handles + // would continue using pointers to deleted objects... + virtual void checkValidity( void ) + { + } + + +private: + +} ; + + +typedef vvector playHandleVector; + + +#endif diff --git a/include/preset_preview_play_handle.h b/include/preset_preview_play_handle.h new file mode 100644 index 0000000000..f263524216 --- /dev/null +++ b/include/preset_preview_play_handle.h @@ -0,0 +1,60 @@ +/* + * preset_preview_play_handle.h - play-handle for playing a short preview-sound + * of a preset + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PRESET_PREVIEW_PLAY_HANDLE_H +#define _PRESET_PREVIEW_PLAY_HANDLE_H + +#include "play_handle.h" + + +class channelTrack; +class notePlayHandle; +class QMutex; + + +class presetPreviewPlayHandle : public playHandle +{ +public: + presetPreviewPlayHandle( const QString & _preset_file ); + virtual ~presetPreviewPlayHandle(); + + static void cleanUp( void ); + + + virtual void play( void ); + virtual bool done( void ) const; + + +private: + static channelTrack * s_globalChannelTrack; + static notePlayHandle * s_globalPreviewNote; + static QMutex * s_globalDataMutex; + + notePlayHandle * m_previewNote; + +} ; + + +#endif diff --git a/include/project_notes.h b/include/project_notes.h new file mode 100644 index 0000000000..5c9bdb312b --- /dev/null +++ b/include/project_notes.h @@ -0,0 +1,98 @@ +/* + * project_notes.h - header for project-notes-editor + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _PROJECT_NOTES_H +#define _PROJECT_NOTES_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include "settings.h" + + +class QAction; +class QComboBox; +class QTextEdit; + + +class projectNotes : public QMainWindow, public settings +{ + Q_OBJECT +public: + projectNotes(); + void clear( void ); + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "projectnotes" ); + } + + +protected: + void setupActions( void ); + + +private slots: + void textBold( void ); + void textUnderline( void ); + void textItalic( void ); + void textFamily( const QString & _f ); + void textSize( const QString & _p ); + void textColor( void ); + void textAlign( QAction * _a ); + + void fontChanged( const QFont & _f ); + void colorChanged( const QColor & _c ); + void alignmentChanged( int _a ); + + +private: + QTextEdit * m_edit; + QAction * m_actionTextBold, + * m_actionTextUnderline, + * m_actionTextItalic, + * m_actionTextColor, + * m_actionAlignLeft, + * m_actionAlignCenter, + * m_actionAlignRight, + * m_actionAlignJustify; + QComboBox * m_comboFont; + QComboBox * m_comboSize; + +} ; + + +#endif diff --git a/include/qt3support.h b/include/qt3support.h new file mode 100644 index 0000000000..f0846ff0d1 --- /dev/null +++ b/include/qt3support.h @@ -0,0 +1,177 @@ +/* + * qt3support.h - layer for supporting Qt3 + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _QT3SUPPORT_H +#define _QT3SUPPORT_H + +// don't know why following line is neccessary, it's a bug in qt-headers - +// otherwise LMMS sometimes fails to compile +//#include + +#include +//#include + +#if QT_VERSION >= 0x040000 +#ifndef QT4 +#define QT4 +#endif +#endif + + +#ifdef QT4 + + +class QColorGroup; +#include + + +typedef int csize; +#define QListViewItem Q3ListViewItem +#define vvector QVector +#define vlist QList + + +#else + + +#define vvector QValueVector +#define vlist QValueList + +#define QMenu QPopupMenu +#define QAbstractButton QButton +#define QScrollArea QScrollView + +// QWidget +#define setWindowTitle setCaption +#define setWindowIcon setIcon +#define isExplicitlyHidden isHidden +#define accessibleName name +#define ensurePolished constPolish + + +// QMenu/QPopupMenu +#define addAction insertItem +//#define addSeparator insertSeparator + + +// QFile/QIODevice +#define seek at + + +// QFileDialog +#define setFileMode setMode +#define setDirectory setDir +#define selectFile setSelection + + +// QThread +#define isRunning running + + +// QScrollView/QScrollArea +#define setHorizontalScrollBarPolicy setHScrollBarMode + + +// QScrollBar +#define setMaximum setMaxValue +#define setMinimum setMinValue + + +// QAbstractButton/QButton +#define setCheckable setToggleButton + + +// QInputEvent/QKeyEvent +#define modifiers state + + +// QButtonGroup +#define addButton insert + + +// QProgressBar +#define setTextVisible setPercentageVisible + + +// QFileInfo +//#define completeSuffix extension +//#define suffix() extension( FALSE ) + + +// QComboBox +#define addItem insertItem +//#define currentIndex currentItem +//#define setCurrentIndex setCurrentItem + + +// QString +#define toLower lower + + +// QTextEdit +#define setLineWrapMode setWordWrap +#define setPlainText setText + + +// QSlider +#define setTickPosition setTickmarks + + +// QStatusBar/QSplashScreen +#define showMessage message +#define clearMessage clear + + +// QDir +#define NoFilter DefaultFilter +#define homePath homeDirPath +#define rootPath rootDirPath +//#define absolutePath absPath + + +// QToolButton +#define setMenu setPopup + + +// QPixmap +#define transformed xForm + + +#define Q3ListView QListView +#define Q3ListViewItem QListViewItem +#define Q3ScrollView QScrollView + +#define QMatrix QWMatrix +#define QIcon QIconSet + +#define ShiftModifier ShiftButton +#define ControlModifier ControlButton + +typedef unsigned int csize; + + +#endif + + +#endif diff --git a/include/rename_dialog.h b/include/rename_dialog.h new file mode 100644 index 0000000000..6e5860f8b5 --- /dev/null +++ b/include/rename_dialog.h @@ -0,0 +1,70 @@ +/* + * rename_dialog.h - declaration of class renameDialog, a simple dialog for + * changing the content of a string + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _RENAME_DIALOG_H +#define _RENAME_DIALOG_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +class QLineEdit; + + +class renameDialog : public QDialog +{ + Q_OBJECT +public: + renameDialog( QString & _string ); + ~renameDialog(); + + +protected: + void keyPressEvent( QKeyEvent * _ke ); + + +protected slots: + void textChanged( const QString & _new_string ); + + +private: + QString & m_stringToEdit; + QString m_originalString; + QLineEdit * m_stringLE; + +} ; + + +#endif + diff --git a/include/sample_buffer.h b/include/sample_buffer.h new file mode 100644 index 0000000000..f3bd38ea30 --- /dev/null +++ b/include/sample_buffer.h @@ -0,0 +1,168 @@ +/* + * sample_buffer.h - container-class sampleBuffer + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SAMPLE_BUFFER_H +#define _SAMPLE_BUFFER_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_SAMPLERATE_H +#include +#endif + + +#include "mixer.h" +#include "types.h" + + +class QPainter; + + +class sampleBuffer : public QObject +{ + Q_OBJECT +public: + enum drawMethods + { + LINE_CONNECT, + DOTS + } ; + + sampleBuffer( const QString & _audio_file = "" ); + sampleBuffer( const sampleFrame * _data, Uint32 _frames ); + + ~sampleBuffer(); + + bool FASTCALL play( sampleFrame * _ab, Uint32 _start_frame, + Uint32 _frames = + mixer::inst()->framesPerAudioBuffer(), + float _freq = BASE_FREQ, bool _looped = FALSE, + void * * _resampling_data = NULL ); + void FASTCALL drawWaves( QPainter & _p, QRect _dr, + drawMethods _dm = LINE_CONNECT ); + inline const QString & audioFile( void ) const + { + return( m_audioFile ); + } + inline Uint32 startFrame( void ) const + { + return( m_startFrame ); + } + inline Uint32 endFrame( void ) const + { + return( m_endFrame ); + } + inline Uint32 frames( void ) const + { + return( m_frames ); + } + inline float amplification( void ) const + { + return( m_amplification ); + } + inline bool reversed( void ) const + { + return( m_reversed ); + } + inline const sampleFrame * data( void ) const + { + return( m_data ); + } + + void FASTCALL deleteResamplingData( void * * _ptr ); + + QString openAudioFile( void ) const; + + + +public slots: + void setAudioFile( const QString & _audio_file ); + void setStartFrame( Uint32 _s ); + void setEndFrame( Uint32 _e ); + void setAmplification( float _a ); + void setReversed( bool _on ); + + +private: + void FASTCALL update( bool _keep_settings = FALSE ); + +#ifdef SDL_SDL_SOUND_H + Uint32 FASTCALL decodeSampleSDL( const char * _f, Sint16 * & _buf, + Uint8 & _channels ); +#endif +#ifdef HAVE_SNDFILE_H + Uint32 FASTCALL decodeSampleSF( const char * _f, Sint16 * & _buf, + Uint8 & _channels ); +#endif +#ifdef HAVE_VORBIS_VORBISFILE_H + Uint32 FASTCALL decodeSampleOGG( const char * _f, Sint16 * & _buf, + Uint8 & _channels ); +#endif + + QString m_audioFile; + sampleFrame * m_origData; + Uint32 m_origFrames; + sampleFrame * m_data; + Uint32 m_frames; + Uint32 m_startFrame; + Uint32 m_endFrame; + float m_amplification; + bool m_reversed; + QMutex m_dataMutex; + +#ifdef HAVE_SAMPLERATE_H + void initResampling( void ); + void quitResampling( void ); + SRC_STATE * createResamplingContext( void ); + void FASTCALL destroyResamplingContext( SRC_STATE * _context ); + + SRC_DATA m_srcData; + SRC_STATE * m_srcState; +#endif + + +signals: + void sampleUpdated( void ); + +} ; + + +#endif diff --git a/include/sample_play_handle.h b/include/sample_play_handle.h new file mode 100644 index 0000000000..2df0c434f7 --- /dev/null +++ b/include/sample_play_handle.h @@ -0,0 +1,66 @@ +/* + * sample_play_handle.h - play-handle for playing a sample + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SAMPLE_PLAY_HANDLE_H +#define _SAMPLE_PLAY_HANDLE_H + +#include "play_handle.h" +#include "types.h" + + +class sampleBuffer; + + +class samplePlayHandle : public playHandle +{ +public: + samplePlayHandle( const QString & _sample_file ); + samplePlayHandle( sampleBuffer * _sample_buffer ); + virtual ~samplePlayHandle(); + + virtual void play( void ); + virtual bool done( void ) const; + + Uint32 totalFrames( void ) const; + inline Uint32 framesDone( void ) const + { + return( m_frame ); + } + void setDoneMayReturnTrue( bool _enable ) + { + m_doneMayReturnTrue = _enable; + } + + +private: + sampleBuffer * m_sampleBuffer; + const bool m_ownSampleBuffer; + bool m_doneMayReturnTrue; + + Uint32 m_frame; + +} ; + + +#endif diff --git a/include/sample_track.h b/include/sample_track.h new file mode 100644 index 0000000000..5953e4e0bd --- /dev/null +++ b/include/sample_track.h @@ -0,0 +1,151 @@ +/* + * sample_track.h - class sampleTrack, a track which provides arrangement of + * samples + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SAMPLE_TRACK_H +#define _SAMPLE_TRACK_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "track.h" +#include "sample_buffer.h" + + +class nameLabel; +class QLabel; +//class sampleTCOSettingsDialog; + + +class sampleTCO : public trackContentObject +{ + Q_OBJECT +public: + sampleTCO( track * _track ); + virtual ~sampleTCO(); + + virtual void FASTCALL changeLength( const midiTime & _length ); + void FASTCALL play( sampleFrame * _ab, Uint32 _start_frame, + Uint32 _frames ); + const QString & sampleFile( void ) const; + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "sampletco" ); + } + + +public slots: + void setSampleFile( const QString & _sf ); + void updateLength( int = 0 ); + + +protected: + void paintEvent( QPaintEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + midiTime getSampleLength( void ) const; + + +private: + sampleBuffer m_sampleBuffer; + + + //friend class sampleTCOSettingsDialog; + +} ; + + + +/* +class sampleTCOSettingsDialog : public QDialog +{ + Q_OBJECT +public: + sampleTCOSettingsDialog( sampleTCO * _stco ); + ~sampleTCOSettingsDialog(); + + +protected slots: + void openSampleFile( void ); + void setSampleFile( const QString & _f ); + + +private: + sampleTCO * m_sampleTCO; + QLabel * m_fileLbl; + +} ; +*/ + + + +class sampleTrack : public QObject, public track +{ + Q_OBJECT +public: + sampleTrack( trackContainer * _tc ); + virtual ~sampleTrack(); + + virtual trackTypes trackType( void ) const; + virtual bool FASTCALL play( const midiTime & _start, + Uint32 _start_frame, Uint32 _frames, + Uint32 _frame_base, + Sint16 _tco_num = -1 ); + virtual trackContentObject * FASTCALL createTCO( const midiTime & + _pos ); + + + virtual void FASTCALL saveTrackSpecificSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadTrackSpecificSettings( const QDomElement & + _this ); + + +public slots: + virtual QString nodeName( void ) const + { + return( "sampletrack" ); + } + + +private: + nameLabel * m_trackLabel; + +} ; + + +#endif diff --git a/include/setup_dialog.h b/include/setup_dialog.h new file mode 100644 index 0000000000..ff9e03b010 --- /dev/null +++ b/include/setup_dialog.h @@ -0,0 +1,113 @@ +/* + * setup_dialog.h - dialog for setting up LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SETUP_DIALOG_H +#define _SETUP_DIALOG_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + +#include "audio_device.h" +#include "midi_device.h" + + +class QComboBox; +class QLabel; +class QSlider; + +class tabBar; + + +class setupDialog : public QDialog +{ + Q_OBJECT +public: + enum configTabs + { + GENERAL_SETTINGS, AUDIO_SETTINGS, MIDI_SETTINGS + } ; + + setupDialog( configTabs _tab_to_open = GENERAL_SETTINGS ); + ~setupDialog(); + + +protected slots: + virtual void accept( void ); + + +private slots: + // general settings widget + void setBufferSize( int _value ); + void resetBufSize( void ); + void displayBufSizeHelp( void ); + + // audio settings widget + void audioInterfaceChanged( const QString & _driver ); + void displayAudioHelp( void ); + + // MIDI settings widget + void midiInterfaceChanged( const QString & _driver ); + void displayMIDIHelp( void ); + + + void toggleToolTips( bool _disabled ); + void toggleKnobUsability( bool _classical ); + + +private: + tabBar * m_tabBar; + + QSlider * m_bufSizeSlider; + QLabel * m_bufSizeLbl; + int m_bufferSize; + + bool m_disableToolTips; + bool m_classicalKnobUsability; + + + typedef QMap aswMap; + typedef QMap mswMap; + + QComboBox * m_audioInterfaces; + aswMap m_audioIfaceSetupWidgets; + + QComboBox * m_midiInterfaces; + mswMap m_midiIfaceSetupWidgets; + + +} ; + + +#endif diff --git a/include/side_bar.h b/include/side_bar.h new file mode 100644 index 0000000000..0f0f427995 --- /dev/null +++ b/include/side_bar.h @@ -0,0 +1,106 @@ +/* + * side_bar.h - code for side-bar in LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SIDE_BAR_H +#define _SIDE_BAR_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "kmultitabbar.h" +#include "side_bar_widget.h" + + +class sideBar : public KMultiTabBar +{ + Q_OBJECT +public: + sideBar( Qt::Orientation _o, QWidget * _parent ) : + KMultiTabBar( _o, _parent ) + { + } + virtual ~sideBar() + { + } + + inline int appendTab( sideBarWidget * _sbw, int _id ) + { + int ret = KMultiTabBar::appendTab( _sbw->icon(), _id, + _sbw->title() ); + m_widgets[_id] = _sbw; + _sbw->hide(); + _sbw->setMinimumWidth( 200 ); + connect( tab( _id ), SIGNAL( clicked( int ) ), this, + SLOT( tabClicked( int ) ) ); + return( ret ); + } + + +private slots: + inline void tabClicked( int _id ) + { + // disable all other tabbar-buttons + QMap::Iterator it; + for( it = m_widgets.begin(); it != m_widgets.end(); ++it ) + { + if( it.key() != _id/* && isTabRaised(it.key()) == TRUE*/ ) + { + setTab( it.key(), FALSE ); + } + if( m_widgets[it.key()] != NULL ) + { + m_widgets[it.key()]->hide(); + } + } + if( m_widgets[_id] != NULL ) + { + if( isTabRaised( _id ) ) + { + m_widgets[_id]->show(); + } + else + { + m_widgets[_id]->hide(); + } + } + } + + +private: + QMap m_widgets; + +} ; + + +#endif diff --git a/include/side_bar_widget.h b/include/side_bar_widget.h new file mode 100644 index 0000000000..11032a954c --- /dev/null +++ b/include/side_bar_widget.h @@ -0,0 +1,88 @@ +/* + * side_bar_widget.h - base-class for all side-bar-widgets + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SIDE_BAR_WIDGET_H +#define _SIDE_BAR_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +class sideBarWidget : public QWidget +{ + Q_OBJECT +public: + sideBarWidget( const QString & _title, const QPixmap & _icon, + QWidget * _parent ); + ~sideBarWidget(); + inline const QPixmap & icon( void ) const + { + return( m_icon ); + } + inline const QString & title( void ) const + { + return( m_title ); + } + + +protected: + void paintEvent( QPaintEvent * _pe ); + void resizeEvent( QResizeEvent * _re ); + inline void contextMenuEvent( QContextMenuEvent * ) + { + } + + inline QWidget * contentParent( void ) + { + return( m_contents ); + } + inline void addContentWidget( QWidget * _w ) + { + m_layout->addWidget( _w ); + } + + +private: + QWidget * m_contents; + QVBoxLayout * m_layout; + QString m_title; + QPixmap m_icon; + +} ; + + +#endif diff --git a/include/song_editor.h b/include/song_editor.h new file mode 100644 index 0000000000..c70f62a6a3 --- /dev/null +++ b/include/song_editor.h @@ -0,0 +1,342 @@ +/* + * song_editor.h - declaration of class songEditor, a window where you can + * setup your songs + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SONG_EDITOR_H +#define _SONG_EDITOR_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "lmms_main_win.h" +#include "track_container.h" +#include "types.h" + + +class QLabel; +class QPixmap; +class QPushButton; +class QScrollBar; +class QSlider; +class QToolButton; +class exportProjectDialog; +class lcdSpinBox; +class pattern; +class projectNotes; +class timeLine; +class visualizationWidget; + + + +const int MIN_BPM = 10; +const int DEFAULT_BPM = 140; +const int MAX_BPM = 1000; +const Uint16 MAX_SONG_LENGTH = 9999; + + + +class songEditor : public trackContainer +{ + Q_OBJECT +public: + enum playModes + { + PLAY_SONG, + PLAY_TRACK, + PLAY_BB, + PLAY_PATTERN, + PLAY_MODE_CNT + } ; + + + class playPos : public midiTime + { + public: + playPos( Sint32 _abs = 0 ) : + midiTime( _abs ), + m_timeLine( NULL ), + m_timeLineUpdate( TRUE ), + m_currentFrame( 0 ) + { + } + inline void setCurrentFrame( Uint32 _f ) + { + m_currentFrame = _f; + } + inline Uint32 currentFrame( void ) const + { + return( m_currentFrame ); + } + timeLine * m_timeLine; + bool m_timeLineUpdate; + + private: + Uint32 m_currentFrame; + } ; + + + + static inline songEditor * inst( void ) + { + if( s_instanceOfMe == NULL ) + { + s_instanceOfMe = new songEditor(); + } + return( s_instanceOfMe ); + } + + void processNextBuffer( void ); + + + inline bool paused( void ) const + { + return( m_paused ); + } + + inline bool playing( void ) const + { + return( m_playing && m_exporting == FALSE ); + } + + inline bool exporting( void ) const + { + return( m_exporting ); + } + + inline bool exportDone( void ) const + { + return( m_exporting == TRUE && + m_playPos[PLAY_SONG].getTact() >= lengthInTacts() + 1 ); + } + inline void setExportProjectDialog( exportProjectDialog * _epd ) + { + m_epd = _epd; + } + inline playModes playMode( void ) const + { + return( m_playMode ); + } + inline playPos & getPlayPos( playModes _pm ) + { + return( m_playPos[_pm] ); + } + + // every function that replaces current file (e.g. creates new file, + // opens another file...) has to call this before and may only process + // if this function returns true + bool mayChangeProject( void ); + + + float framesPerTact( void ) const; + + // file management + void createNewProject( void ); + void FASTCALL createNewProjectFromTemplate( const QString & _template ); + void FASTCALL loadProject( const QString & _file_name ); + bool saveProject( void ); + bool FASTCALL saveProjectAs( const QString & _file_name ); + inline const QString & projectFileName( void ) const + { + return( m_fileName ); + } + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + inline virtual QString nodeName( void ) const + { + return( "songeditor" ); + } + + virtual inline bool fixedTCOs( void ) const + { + return( FALSE ); + } + + int masterPitch( void ) const; + + projectNotes * getProjectNotesWindow( void ) + { + return( m_projectNotes ); + } + + +public slots: + void play( void ); + void stop( void ); + void playTrack( track * _trackToPlay ); + void playBB( void ); + void playPattern( pattern * _patternToPlay, bool _loop = TRUE ); + void pause( void ); + void resumeFromPause( void ); + + void importProject( void ); + void exportProject( void ); + + void startExport( void ); + void stopExport( void ); + // set BPM (beats per minute) + void setBPM( int _new_bpm = DEFAULT_BPM ); + + inline void setModified( void ) + { + m_modified = TRUE; + } + + +protected: + void closeEvent( QCloseEvent * _ce ); + void resizeEvent( QResizeEvent * _re ); + void keyPressEvent( QKeyEvent * _ke ); + void wheelEvent( QWheelEvent * _we ); + void paintEvent( QPaintEvent * _pe ); + + +protected slots: + void insertTact( void ); + void removeTact( void ); + void addChannelTrack( void ); + void addBBTrack( void ); + void addSampleTrack( void ); + void scrolled( int _new_pos ); + void updateTimeLinePosition( void ); + + void masterVolumeChanged( int _new_val ); + void masterVolumePressed( void ); + void masterVolumeMoved( int _new_val ); + void masterVolumeReleased( void ); + void masterPitchChanged( int _new_val ); + void masterPitchPressed( void ); + void masterPitchMoved( int _new_val ); + void masterPitchReleased( void ); + void toggleHQMode( void ); + + void updatePosition( const midiTime & _t ); + + +private: + songEditor(); + songEditor( const songEditor & ); + virtual ~songEditor(); + + void clearProject( void ); + + + inline tact currentTact( void ) const + { + return( m_playPos[m_playMode].getTact() ); + } + + midiTime length( void ) const; + tact lengthInTacts( void ) const; + inline tact64th currentTact64th( void ) const + { + return( m_playPos[m_playMode].getTact64th() ); + } + void FASTCALL setPlayPos( tact _tact_num, tact64th _t_64th, playModes + _play_mode ); + + + + static songEditor * s_instanceOfMe; + + QScrollBar * m_leftRightScroll; + + QToolButton * m_playButton; + QToolButton * m_stopButton; + lcdSpinBox * m_bpmSpinBox; + + QSlider * m_masterVolumeSlider; + QSlider * m_masterPitchSlider; + + visualizationWidget * m_masterOutputGraph; + + + QToolButton * m_addChannelTrackButton; + QToolButton * m_addBBTrackButton; + QToolButton * m_addSampleTrackButton; + QToolButton * m_insertTactButton; + QToolButton * m_removeTactButton; + + + + QString m_fileName; + QString m_oldFileName; + bool m_modified; + + volatile bool m_exporting; + volatile bool m_playing; + volatile bool m_paused; + + playModes m_playMode; + playPos m_playPos[PLAY_MODE_CNT]; + + track * m_trackToPlay; + pattern * m_patternToPlay; + bool m_loopPattern; + + bool m_scrollBack; + + exportProjectDialog * m_epd; + + + projectNotes * m_projectNotes; + + + + enum ACTIONS + { + ACT_STOP_PLAY, ACT_PLAY_SONG, ACT_PLAY_TRACK, ACT_PLAY_BB, + ACT_PLAY_PATTERN, ACT_PAUSE, ACT_RESUME_FROM_PAUSE + } ; + vvector m_actions; + + void doActions( void ); + + + bool m_shiftPressed; + bool m_controlPressed; + + + + friend lmmsMainWin::~lmmsMainWin(); + + + +signals: + void bpmChanged( int _new_bpm ); + +} ; + + +#endif diff --git a/include/spc_bg_hndl_widget.h b/include/spc_bg_hndl_widget.h new file mode 100644 index 0000000000..901a3f0fba --- /dev/null +++ b/include/spc_bg_hndl_widget.h @@ -0,0 +1,135 @@ +/* + * spc_bg_hndl_widget.h - class specialBgHandlingWidget + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SPC_BG_HNDL_WIDGET_H +#define _SPC_BG_HNDL_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include + +#endif + + +class specialBgHandlingWidget +{ +public: + specialBgHandlingWidget( const QPixmap & _pm ) : + m_backgroundPixmap( _pm ), + m_backgroundColor( QColor( 0, 0, 0 ) ) + { + } + + specialBgHandlingWidget( const QColor & _c ) : + m_backgroundPixmap(), + m_backgroundColor( _c ) + { + } + + ~specialBgHandlingWidget() + { + } + + const QPixmap & backgroundPixmap( void ) const + { + return( m_backgroundPixmap ); + } + const QColor & backgroundColor( void ) const + { + return( m_backgroundColor ); + } + + static QPixmap getBackground( const QWidget * _w ) + { + QPixmap pm( _w->size() ); + const QWidget * pw = _w->parentWidget(); + if( dynamic_cast( pw ) ) + { + const specialBgHandlingWidget * s = dynamic_cast< + const specialBgHandlingWidget *>( pw ); + if( s->backgroundPixmap().isNull() == FALSE ) + { +#ifdef QT4 + QPainter p( &pm ); + p.drawPixmap( 0, 0, s->backgroundPixmap(), + _w->x(), _w->y(), _w->width(), + _w->height() ); +#else + bitBlt( &pm, 0, 0, &s->backgroundPixmap(), + _w->x(), _w->y(), _w->width(), + _w->height() ); +#endif + } + else + { + pm.fill( s->backgroundColor() ); + } + } + else + { +#ifdef QT4 + QPainter p( &pm ); + // TODO: fix that for background-pixmaps, because + // drawing is started at the top left edge even + // if this widget isn't posated there + p.fillRect( _w->rect(), pw->palette().brush( + pw->backgroundRole() ) ); +#else + const QPixmap * pbp = pw->paletteBackgroundPixmap(); + if( pbp == NULL ) + { + pbp = pw->erasePixmap(); + } + if( pbp ) + { + bitBlt( &pm, 0, 0, pbp, _w->x(), _w->y(), + _w->width(), _w->height() ); + } + else + { + pm.fill( pw->paletteBackgroundColor() ); + } +#endif + } + return( pm ); + } + + + +private: + QPixmap m_backgroundPixmap; + QColor m_backgroundColor; + +} ; + +#endif diff --git a/include/surround_area.h b/include/surround_area.h new file mode 100644 index 0000000000..23dd9cdb31 --- /dev/null +++ b/include/surround_area.h @@ -0,0 +1,91 @@ +/* + * surround_area.h - class surroundArea which provides widget for setting + * position of a channel + calculation of volume for each + * speaker + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _SURROUND_AREA_H +#define _SURROUND_AREA_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "types.h" +#include "mixer.h" +#include "templates.h" + + +class QPixmap; + +const int SURROUND_AREA_SIZE = 1024; + + +class surroundArea : public QWidget +{ + Q_OBJECT +public: + surroundArea( QWidget * _parent = NULL ); + virtual ~surroundArea(); + volumeVector getVolumeVector( float _v_scale = 0.0f ) const; + inline const QPoint & value( void ) const + { + return( m_sndSrcPos ); + } + void FASTCALL setValue( const QPoint & _p ); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + + +signals: + void valueChanged( const QPoint & _p ); + + +private: + float FASTCALL getVolume( const QPoint & _speaker_pos, + float _v_scale ) const; + + QPoint m_sndSrcPos; + + static const QPoint s_defaultSpeakerPositions[SURROUND_CHANNELS]; + static QPixmap * s_backgroundArtwork; + +} ; + + +#endif + diff --git a/include/tab_bar.h b/include/tab_bar.h new file mode 100644 index 0000000000..e960965003 --- /dev/null +++ b/include/tab_bar.h @@ -0,0 +1,102 @@ +/* + * tab_bar.h - class tabBar + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TAB_BAR_H +#define _TAB_BAR_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + + +class tabButton; + + +class tabBar : public QWidget +{ + Q_OBJECT +public: + tabBar( QWidget * _parent, + QBoxLayout::Direction _dir = QBoxLayout::LeftToRight ); + virtual ~tabBar(); + + tabButton * FASTCALL addTab( QWidget * _w, const QString & _text, + int _id, bool _add_stretch = FALSE, + bool _text_is_tooltip = FALSE ); + void FASTCALL removeTab( int _id ); + + inline void setExclusive( bool _on ) + { + m_exclusive = _on; + } + + int activeTab( void ); + + +public slots: + void setActiveTab( int _id ); + + +protected: + bool FASTCALL tabState( int _id ); + void FASTCALL setTabState( int _id, bool _checked ); + bool allHidden( void ); + + +protected slots: + void hideAll( int _exception = -1 ); + void tabClicked( int _id ); + + +private: + QMap > m_tabs; + QBoxLayout * m_layout; + bool m_exclusive; + + +signals: + void allWidgetsHidden( void ); + void widgetShown( void ); + +} ; + + +#endif diff --git a/include/tab_button.h b/include/tab_button.h new file mode 100644 index 0000000000..abda20ec96 --- /dev/null +++ b/include/tab_button.h @@ -0,0 +1,75 @@ +/* + * tab_button.h - declaration of class tabButton + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TAB_BUTTON_H +#define _TAB_BUTTON_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + + +class tabButton : public QPushButton +{ + Q_OBJECT +public: + tabButton( const QString & _text, int _id, QWidget * _parent ) : + QPushButton( _text, _parent ), + m_id( _id ) + { + setCheckable( TRUE ); + connect( this, SIGNAL( clicked() ), this, + SLOT( slotClicked() ) ); + } + virtual ~tabButton() + { + } + + +signals: + void clicked( int ); + + +protected slots: + void slotClicked( void ) + { + emit clicked( m_id ); + } + + +private: + int m_id; + +} ; + +#endif diff --git a/include/tab_widget.h b/include/tab_widget.h new file mode 100644 index 0000000000..72f4b8153c --- /dev/null +++ b/include/tab_widget.h @@ -0,0 +1,74 @@ +/* + * tab_widget.h - LMMS-tabwidget + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TAB_WIDGET_H +#define _TAB_WIDGET_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +class tabWidget : public QWidget +{ + Q_OBJECT +public: + tabWidget( const QString & _caption, QWidget * _parent ); + ~tabWidget(); + + void addTab( QWidget * _w, const QString & _name ); + + +protected: + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void paintEvent( QPaintEvent * _pe ); + virtual void resizeEvent( QResizeEvent * _re ); + + +private: + struct widgetDesc + { + QWidget * w; // ptr to widget + QString name; // name for widget + int nwidth; // width of name when painting + } ; + typedef vvector widgetStack; + + widgetStack m_widgets; + int m_curWidget; + QString m_caption; + +} ; + +#endif diff --git a/include/templates.h b/include/templates.h new file mode 100644 index 0000000000..4553886719 --- /dev/null +++ b/include/templates.h @@ -0,0 +1,102 @@ +/* + * templates.h - miscellanous templates and algorithms + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TEMPLATES_H +#define _TEMPLATES_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +template +inline T tAbs( const T & x ) +{ + return( x < static_cast( 0 ) ? -x : x ); +} + + + + +template +inline T tMin( const T & x1, const T & x2 ) +{ + if( x1 < x2 ) + { + return( x1 ); + } + return( x2 ); +} + + + + +template +inline T tMax( const T & x1, const T & x2 ) +{ + if( x1 > x2 ) + { + return( x1 ); + } + return( x2 ); +} + + + + +template +inline T tLimit( const T & x, const T & x1, const T & x2 ) +{ + return( tMin( tMax( x, tMin( x1, x2 ) ), + tMax( x1, x2 ) ) ); + +} + + +template +inline QFont pointSize( QFont _f ) +{ + const int DPI = 96; +#ifdef QT4 + _f.setPointSizeF( SIZE * DPI / QX11Info::appDpiX() ); +#else + _f.setPointSizeFloat( SIZE * DPI / QPaintDevice::x11AppDpiX() ); +#endif + return( _f ); +} + + +#endif diff --git a/include/text_float.h b/include/text_float.h new file mode 100644 index 0000000000..5263dda5f2 --- /dev/null +++ b/include/text_float.h @@ -0,0 +1,66 @@ +/* + * text_float.h - class textFloat, a floating text-label + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TEXT_FLOAT +#define _TEXT_FLOAT + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +class textFloat : public QWidget +{ +public: + textFloat( QWidget * _parent ); + virtual ~textFloat() + { + } + + void setText( const QString & _text ); + + void reparent( QWidget * _new_parent ); + + void setVisibilityTimeOut( int _msecs ); + + +protected: + virtual void paintEvent( QPaintEvent * _me ); + + +private: + QString m_text; + +} ; + + +#endif diff --git a/include/timeline.h b/include/timeline.h new file mode 100644 index 0000000000..b9d0dd1cf2 --- /dev/null +++ b/include/timeline.h @@ -0,0 +1,160 @@ +/* + * timeline.h - class timeLine, representing a time-line with position marker + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TIMELINE_H +#define _TIMELINE_H + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "song_editor.h" + + +class QPixmap; +class nStateButton; + + +class timeLine : public QWidget +{ + Q_OBJECT +public: + timeLine( int _xoff, int _yoff, float _ppt, songEditor::playPos & _pos, + const midiTime & _begin, QWidget * _parent ); + ~timeLine(); + + inline songEditor::playPos & pos( void ) + { + return( m_pos ); + } + + enum behaviourAtStopStates + { + BACK_TO_ZERO, BACK_TO_START, KEEP_STOP_POSITION + } ; + + + behaviourAtStopStates behaviourAtStop( void ) const; + + bool loopPointsEnabled( void ) const; + inline const midiTime & loopBegin( void ) const + { + return( ( m_loopPos[0] < m_loopPos[1] ) ? + m_loopPos[0] : m_loopPos[1] ); + } + inline const midiTime & loopEnd( void ) const + { + return( ( m_loopPos[0] > m_loopPos[1] ) ? + m_loopPos[0] : m_loopPos[1] ); + } + + inline void savePos( const midiTime & _pos ) + { + m_savedPos = _pos; + } + inline const midiTime & savedPos( void ) const + { + return( m_savedPos ); + } + + inline void setPixelsPerTact( float _ppt ) + { + m_ppt = _ppt; + update(); + } + + +public slots: + void updatePosition( const midiTime & = 0 ); + void toggleLoopPoints( int _n ); + + +protected: + virtual void paintEvent( QPaintEvent * _pe ); + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + + +private: + inline int markerX( const midiTime & _t ) const + { + return( m_xOffset + static_cast( ( _t - m_begin ) * + m_ppt / 64.0f ) ); + } + + + static QPixmap * s_timeLinePixmap; + static QPixmap * s_posMarkerPixmap; + static QPixmap * s_loopPointPixmap; + + nStateButton * m_autoScroll; + nStateButton * m_loopPoints; + nStateButton * m_behaviourAtStop; + + int m_xOffset; + int m_posMarkerX; + float m_ppt; + songEditor::playPos & m_pos; + const midiTime & m_begin; + midiTime m_loopPos[2]; + + midiTime m_savedPos; + + + enum actions + { + NONE, MOVE_POS_MARKER, MOVE_LOOP_BEGIN, MOVE_LOOP_END + } m_action; + + int m_moveXOff; + + + enum autoScrollStates + { + AUTOSCROLL_ENABLED, AUTOSCROLL_DISABLED + } ; + + enum loopPointStates + { + LOOP_POINTS_DISABLED, LOOP_POINTS_ENABLED + } ; + + +signals: + void positionChanged( const midiTime & _t ); + +} ; + + +#endif diff --git a/include/tooltip.h b/include/tooltip.h new file mode 100644 index 0000000000..7bf2b65a1b --- /dev/null +++ b/include/tooltip.h @@ -0,0 +1,42 @@ +/* + * tooltip.h - namespace toolTip, a tooltip-wrapper for LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TOOLTIP_H +#define _TOOLTIP_H + +#include + +class QWidget; + + +namespace toolTip +{ + + void add( QWidget * _w, const QString & _txt ); + //void remove( QWidget * _w ); + +} ; + + +#endif diff --git a/include/track.h b/include/track.h new file mode 100644 index 0000000000..39e2605b86 --- /dev/null +++ b/include/track.h @@ -0,0 +1,358 @@ +/* + * track.h - declaration of classes concerning tracks -> neccessary for all + * track-like objects (beat/bassline, sample-track...) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TRACK_H +#define _TRACK_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "types.h" +#include "midi_time.h" +#include "settings.h" + + +class track; +class trackContainer; +class trackContentWidget; +class trackWidget; +class pixmapButton; +class QMenu; + +typedef QWidget trackSettingsWidget; +typedef QWidget trackOperationsWidget; + + + +const Uint16 DEFAULT_SETTINGS_WIDGET_WIDTH = 224; +const Uint16 TRACK_OP_WIDTH = 70; +const Uint16 TCO_BORDER_WIDTH = 1; + + +class trackContentObject : public QWidget, public settings +{ + Q_OBJECT +public: + trackContentObject( track * _track ); + trackContentObject( const trackContentObject & _copy ); + virtual ~trackContentObject(); + inline track * getTrack( void ) + { + return( m_track ); + } + inline const midiTime & startPosition( void ) const + { + return( m_startPosition ); + } + inline midiTime endPosition( void ) const + { + return( m_startPosition + m_length ); + } + inline const midiTime & length( void ) const + { + return( m_length ); + } + bool fixedTCOs( void ); + virtual void FASTCALL movePosition( const midiTime & _pos ); + virtual void FASTCALL changeLength( const midiTime & _length ); + + +public slots: + virtual void close( void ); + + +protected: + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void contextMenuEvent( QContextMenuEvent * _cme ); + virtual void constructContextMenu( QMenu * ) + { + } + void setAutoResizeEnabled( bool _e = FALSE ); + float pixelsPerTact( void ); + + +protected slots: + void cut( void ); + void copy( void ); + void paste( void ); + + +private: + track * m_track; + midiTime m_startPosition; + midiTime m_length; + bool m_moving; + bool m_resizing; + bool m_autoResize; + Sint16 m_initialMouseX; + +} ; + + + + +class trackContentWidget : public QWidget +{ + Q_OBJECT +public: + trackContentWidget( trackWidget * _parent ); + virtual ~trackContentWidget(); + + trackContentObject * FASTCALL getTCO( csize _tco_num ); + csize numOfTCOs( void ); + trackContentObject * FASTCALL addTCO( trackContentObject * _tco ); + void FASTCALL removeTCO( csize _tco_num, bool _also_delete = TRUE ); + void FASTCALL removeTCO( trackContentObject * _tco, + bool _also_delete = TRUE ); + void removeAllTCOs( void ); + void FASTCALL swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ); + + inline Uint16 pixelsPerTact( void ) const + { + return( m_pixelsPerTact ); + } + inline void setPixelsPerTact( Uint16 _ppt ) + { + m_pixelsPerTact = _ppt; + } + tact length( void ) const; + + +public slots: + void insertTact( const midiTime & _pos ); + void removeTact( const midiTime & _pos ); + void updateTCOs( void ); + + +protected: + virtual void mousePressEvent( QMouseEvent * _me ); + virtual void mouseMoveEvent( QMouseEvent * _me ); + virtual void mouseReleaseEvent( QMouseEvent * _me ); + virtual void resizeEvent( QResizeEvent * _re ); + + +private: + typedef vvector tcoVector; + + tcoVector m_trackContentObjects; + trackWidget * m_trackWidget; + Uint16 m_pixelsPerTact; + +} ; + + + + +// actual widget shown in trackContainer +class trackWidget : public QWidget +{ + Q_OBJECT +public: + trackWidget( track * _track, QWidget * _parent ); + virtual ~trackWidget(); + + inline const track * getTrack( void ) const + { + return( m_track ); + } + inline track * getTrack( void ) + { + return( m_track ); + } + inline const trackSettingsWidget & getTrackSettingsWidget( void ) const + { + return( m_trackSettingsWidget ); + } + inline const trackContentWidget & getTrackContentWidget( void ) const + { + return( m_trackContentWidget ); + } + inline trackSettingsWidget & getTrackSettingsWidget( void ) + { + return( m_trackSettingsWidget ); + } + inline trackContentWidget & getTrackContentWidget( void ) + { + return( m_trackContentWidget ); + } + bool muted( void ) const; + + +public slots: + void changePosition( const midiTime & _new_pos = -1 ); + void cloneTrack( void ); + void deleteTrack( void ); + void moveTrackUp( void ); + void moveTrackDown( void ); + void setMuted( bool _muted ); + void muteBtnRightClicked( void ); + + +protected: + virtual void resizeEvent( QResizeEvent * _re ); + virtual void paintEvent( QPaintEvent * _pe ); + midiTime FASTCALL endPosition( const midiTime & _pos_start ); + + +private: + track * m_track; + + trackOperationsWidget m_trackOperationsWidget; + trackSettingsWidget m_trackSettingsWidget; + trackContentWidget m_trackContentWidget; + + pixmapButton * m_muteBtn; + +} ; + + + + +// base-class for all tracks +class track : public settings +{ +public: + enum trackTypes + { + CHANNEL_TRACK, + BB_TRACK, + SAMPLE_TRACK, + EVENT_TRACK, + VIDEO_TRACK, + NULL_TRACK, + TOTAL_TRACK_TYPES + } ; + + track( trackContainer * _tc ); + virtual ~track(); + static track * FASTCALL createTrack( trackTypes _tt, + trackContainer * _tc ); + static track * FASTCALL createTrack( const QDomElement & _this, + trackContainer * _tc ); + static track * FASTCALL cloneTrack( track * _track ); + + tact length( void ) const; + + inline bool muted( void ) const + { + return( m_trackWidget->muted() ); + } + + // pure virtual functions + virtual trackTypes trackType( void ) const = 0; + + virtual bool FASTCALL play( const midiTime & _start, + Uint32 _start_frame, + Uint32 _frames, + Uint32 _frame_base, + Sint16 _tco_num = -1 ) = 0; + + + virtual trackContentObject * FASTCALL createTCO( + const midiTime & _pos ) = 0; + + virtual void FASTCALL saveTrackSpecificSettings( QDomDocument & _doc, + QDomElement & _parent ) = 0; + virtual void FASTCALL loadTrackSpecificSettings( + const QDomElement & _this ) = 0; + + + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + + trackContentObject * FASTCALL addTCO( trackContentObject * _tco ); + void FASTCALL removeTCO( csize _tco_num ); + csize numOfTCOs( void ); + trackContentObject * FASTCALL getTCO( csize _tco_num ); + csize FASTCALL getTCONum( trackContentObject * _tco ); + void FASTCALL getTCOsInRange( vlist & _tco_v, + const midiTime & _start, + const midiTime & _end ); + void FASTCALL swapPositionOfTCOs( csize _tco_num1, csize _tco_num2 ); + + inline trackWidget * getTrackWidget( void ) + { + return( m_trackWidget ); + } + inline trackContainer * getTrackContainer( void ) + { + return( m_trackContainer ); + } + inline const trackSettingsWidget * getTrackSettingsWidget( void ) const + { + return( &m_trackWidget->getTrackSettingsWidget() ); + } + inline const trackContentWidget * getTrackContentWidget( void ) const + { + return( &m_trackWidget->getTrackContentWidget() ); + } + inline trackSettingsWidget * getTrackSettingsWidget( void ) + { + return( &m_trackWidget->getTrackSettingsWidget() ); + } + inline trackContentWidget * getTrackContentWidget( void ) + { + return( &m_trackWidget->getTrackContentWidget() ); + } + + +private: + trackContainer * m_trackContainer; + trackWidget * m_trackWidget; + +} ; + + +typedef vvector trackVector; +typedef vvector constTrackVector; + + +#endif diff --git a/include/track_container.h b/include/track_container.h new file mode 100644 index 0000000000..7b221f3451 --- /dev/null +++ b/include/track_container.h @@ -0,0 +1,119 @@ +/* + * track_container.h - base-class for all track-containers like Playlist-Editor, BB-Editor... + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _TRACK_CONTAINER_H +#define _TRACK_CONTAINER_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "track.h" +#include "settings.h" + + + +class trackContainer : public QMainWindow, public settings +{ + Q_OBJECT +public: + trackContainer(); + ~trackContainer(); + inline QWidget * containerWidget( void ) + { + return( m_scrollArea ); + } + virtual void FASTCALL saveSettings( QDomDocument & _doc, + QDomElement & _parent ); + virtual void FASTCALL loadSettings( const QDomElement & _this ); + + inline float pixelsPerTact( void ) const + { + return( m_ppt ); + } + inline const midiTime & currentPosition( void ) const + { + return( m_currentPosition ); + } + virtual bool fixedTCOs( void ) const + { + return( FALSE ); + } + unsigned int FASTCALL countTracks( track::trackTypes _tt = + track::TOTAL_TRACK_TYPES ) const; + + void FASTCALL setMutedOfAllTracks( bool _muted ); + + + virtual void updateAfterTrackAdd( void ); + void FASTCALL setPixelsPerTact( Uint16 _ppt ); + void FASTCALL cloneTrack( track * _track ); + void FASTCALL addTrack( track * _track ); + void FASTCALL removeTrack( track * _track ); + void FASTCALL moveTrackUp( track * _track ); + void FASTCALL moveTrackDown( track * _track ); + void FASTCALL realignTracks( bool _complete_update = FALSE ); + + +protected: + constTrackVector tracks( void ) const; + trackVector tracks( void ); + + virtual void resizeEvent( QResizeEvent * ); + + midiTime m_currentPosition; + + +protected slots: + void updateScrollArea( void ); + + +private: + QScrollArea * m_scrollArea; + typedef vvector trackWidgetVector; + + trackWidgetVector m_trackWidgets; + float m_ppt; + + +signals: + void positionChanged( const midiTime & _pos ); + +} ; + + +#endif diff --git a/include/types.h b/include/types.h new file mode 100644 index 0000000000..796ca2513f --- /dev/null +++ b/include/types.h @@ -0,0 +1,48 @@ +/* + * types.h - typedefs for common types that are used in the whole app + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _TYPES_H +#define _TYPES_H + + +typedef unsigned char Uint8; +typedef signed char Sint8; +typedef unsigned short Uint16; +typedef signed short Sint16; +typedef unsigned int Uint32; +typedef signed int Sint32; + + +typedef Uint32 minute; +typedef Sint8 second; +typedef Sint32 tact; +typedef Sint8 tact64th; +typedef Uint8 volume; +typedef Sint8 panning; + + +typedef float sampleType; +typedef Sint16 outputSampleType; + + +#endif diff --git a/include/visualization_widget.h b/include/visualization_widget.h new file mode 100644 index 0000000000..4367e33c52 --- /dev/null +++ b/include/visualization_widget.h @@ -0,0 +1,81 @@ +/* + * visualization_widget.h - widget for visualization of waves + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifndef _OUTPUT_GRAPH_H +#define _OUTPUT_GRAPH_H + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "mixer.h" + +class QTimer; + + +class visualizationWidget : public QWidget +{ + Q_OBJECT +public: + enum visualizationTypes + { + SIMPLE // add more here + } ; + visualizationWidget( const QPixmap & _bg, QWidget * _parent, + visualizationTypes _vtype = SIMPLE ); + virtual ~visualizationWidget(); + + +protected: + void paintEvent( QPaintEvent * _pe ); + void mousePressEvent( QMouseEvent * _me ); + + +protected slots: + void setAudioBuffer( const surroundSampleFrame * _ab, Uint32 _frames ); + + +private: + QPixmap s_background; + + bool m_enabled; + surroundSampleFrame * m_buffer; + Uint32 m_frames; + + QTimer * m_updateTimer; + +} ; + +#endif diff --git a/include/volume.h b/include/volume.h new file mode 100644 index 0000000000..dc37abd480 --- /dev/null +++ b/include/volume.h @@ -0,0 +1,33 @@ +/* + * volume.h - declaration of some constants and types, concerning the volume of a note + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#ifndef _VOLUME_H +#define _VOLUME_H + +#include "types.h" + +const volume MIN_VOLUME = 0; +const volume MAX_VOLUME = 200; +const volume DEFAULT_VOLUME = 100; + +#endif diff --git a/lmms.spec.in b/lmms.spec.in new file mode 100644 index 0000000000..95b6d71645 --- /dev/null +++ b/lmms.spec.in @@ -0,0 +1,122 @@ +%define _suse %(if [ -f /etc/SuSE-release ] ; then echo 1 ; else echo 0 ; fi) + + +Name: @PACKAGE@ +Version: @VERSION@ +Release: 0 +Summary: powerful sequencer-, synthesizer- and sample-studio for Linux +Summary(de): Leistungsfaehiges Sequenzer-, Synthesizer- und Sample-Studio fuer Linux +License: GPL +URL: http://lmms.sourceforge.net/ +Group: Applications/Multimedia/Sound +Packager: Tobias Doerffel +Provides: lmms = %{version}-%{release} +%if %_suse +BuildRequires: gcc gcc-c++ libstdc++-devel autoconf automake libtool make alsa +BuildRequires: alsa-devel SDL SDL-devel SDL_sound libvorbis libvorbis-devel +BuildRequires: libsamplerate libsamplerate-devel libsndfile libsndfile-devel +BuildRequires: jack-devel +BuildRequires: qt3 qt3-devel qt3-devel-tools +Requires: qt3 alsa SDL_sound libvorbis libsamplerate libsndfile jack +%endif +Requires: lmms-data +Source: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot +Prefix: %{_prefix} + + +%description +LMMS aims to be a free alternative to popular (but commercial and closed- +source) programs like FruityLoops, Cubase and Logic giving you the ability of +producing music with your computer by creating/synthesizing sounds, arranging +samples, playing live with keyboard and much more... + +LMMS combines the features of a tracker-/sequencer-program (pattern-/channel-/ +sample-/song-/effect-management) and those of powerful synthesizers and +samplers in a modern, user-friendly and easy to use graphical user-interface. + +LMMS is still in heavy development, so with this version please don't expect a +complete, ready and bugfree program!! + + +%description -l de +LMMS ist eine freie Alternative zu bekannten (aber kommerziellen und +Closed-Source-) Programmen wie FruityLoops, Cubase und Logic, die Ihnen die +Moeglichkeit geben, mit Ihrem Computer Musik zu produzieren, indem Sie +Klaenge kreieren/synthetisieren, Samples anordnen, mit dem Keyboard live +spielen usw.... + +LMMS kombiniert die Funktionen eines Tracker-/Sequenzer-Programms (Pattern-/ +Kanal-/Sample-/Song-/Effekt-Management) und die von leistungsfaehigen +Synthesizern und Samplern in einer modernen, benutzerfreundlichen und einfach zu +benutzenden grafischen Oberflaeche. + +Derzeit befindet sich LMMS komplett in Entwicklung. Also erwarten Sie bitte mit +dieser Version nicht ein vollstaendiges, fertiges und fehlerfreies Programm!! + + +%package data +Summary: samples, presets, demo-projects and localization-files for LMMS +Summary(de): Samples, Presets, Demo-Projekte und Lokalisierungsdateien fuer LMMS +Group: Applications/Multimedia/Sound +Provides: lmms-data = %{version}-%{release} + +%description data +This package contains platform-independent data and resources for Linux +MultiMedia Studio (LMMS), e.g. samples, presets and demo-projects. + + +%description data -l de +Dieses Paket beinhaltet plattform-unabhaengige Daten und Resourcen fuer Linux +MultiMedia Studio (LMMS), wie z.B. Samples, Presets und Demo-Projekte. + + +%prep +%setup -n %{name}-%{version} + + +%build +CXXFLAGS="$RPM_OPT_FLAGS" ./configure --prefix=%{prefix} + +make + + +%install +make DESTDIR=$RPM_BUILD_ROOT install + +%clean +[ "$RPM_BUILD_ROOT" != "/" ] && rm -rf $RPM_BUILD_ROOT + + +%pre +%post +%preun +%postun + + +%files +%defattr(-,root,root) +%{_bindir}/lmms + + +%files data +%defattr(-,root,root) +%{_datadir}/lmms/ + + + +%changelog +* Tue Sep 20 2005 Tobias Doerffel +- added JACK-dependencies + +* Fri Jul 22 2005 Tobias Doerffel +- added more dependencies for builds under SuSE + +* Sat Jun 25 2005 Tobias Doerffel +- splitted package into lmms and lmms-data +- additional requirements +- updated project-homepage and email-address of packager + +* Thu May 12 2005 Tobias Doerffel +- created lmms.spec.in + diff --git a/resources/add_bb_track.png b/resources/add_bb_track.png new file mode 100644 index 0000000000000000000000000000000000000000..bed9f4a46ee8e7a00548800030b0d5b9774b1110 GIT binary patch literal 2149 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 R4>14$002ovPDHLkV1lbtIgJ1S literal 0 HcmV?d00001 diff --git a/resources/apply.png b/resources/apply.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8e7eafccdc59badee5d4d1b6ccc57ba4f74910 GIT binary patch literal 768 zcmV+b1ONPqP)NOlB}3%D_oL1E(;|=V0cz?_lVVpdzBe z05sj0FG$5F|-7G3;j!Wp-dvXJYts@ejlQP5(bI{0F+= zKhOohFl68aMv6HD1B)3810x4e%L@jEKZ}9te*Ry;P{)vn&0v54VgZvs88-gC^w;69 z{9gtJbs*mg=!-4DU||Q+WAAb=n)E(3a%jp05+ z0g}usAm#^Rb|kSYK)i01Q4RE_{DG#?5gi*cq0&>02=m( zL6KoP@fippfKUsH7+_M12Syk#IC4Nj4G(#6PWlrR7YAk@hp!4L{` yV>C*_BMSfo5F@RsCV&8ly!a}3*@K97Kk>_aLJ&{nl5);9^GgDOwi;{@+N4l}i~=j~Mc=6|0~2IsnCZK?m! Y7uPOv literal 0 HcmV?d00001 diff --git a/resources/arp_down_on.png b/resources/arp_down_on.png new file mode 100644 index 0000000000000000000000000000000000000000..ad5c19b9d04049bca0ddef8fbd375a6a670b5ef4 GIT binary patch literal 670 zcmV;P0%84$P)8cyO@N(e5Ip;ku=fFQg6*r$a z(Am>_-Yp7TLK+p)D5F6uPznLC0zp9F2axqqIg6JN|KYXchfHaCxNT48hRTw{ETT0S zjS4y7D3nt9F(QZ+#0HSF=pZ0AuX%RAyv6uh+2I9)o6bUk#wjqUP+q~Rda#1zd?X`~ z2}qfkYN& z7a={BVCrh`DZJ5}8i3^R%Rtt(7H!$qkaJ;2EmVgg8ir`eGK*n&8XO;z{e9PSLpM$X zzO%es#(U6rQjL9_vw#kPE?i4n0E9rj8yH)Bda(~k=UrKWO~*4cpX23S`+CN#qOmI9 z$T5I{d+)(~i)Z6!_a2s1Z1AV8t6*nG-YOG`nyuU0Mwg(q3>@Ir2&e_{&K*3H8G3T* zSJzolb7k=L`f-uX<04HHB2{lhDvw^c3PgVEzD=~Lz3tW9_xcHu+7BZ2{m)0hZTzDr ziFEEi()?n^);*qF(i=B*|IMRmU0t_}vWgD!R{S6Q0HDC(^Gnc*{r~^~07*qoM6N<$ Ef*lPkL;wH) literal 0 HcmV?d00001 diff --git a/resources/arp_random_off.png b/resources/arp_random_off.png new file mode 100644 index 0000000000000000000000000000000000000000..74391a256b1c797bffb8ad9625eae5d8c487d606 GIT binary patch literal 506 zcmV5}BH9YZ8ejnN^M3Nx9nSjfOfkqwJ0| zzS!0(wd}{!_;FIs%>Eh(eVq+ZU-&lP_rA~Odj%iO zvrjHYA4MmpvD^vYqKMISJ7LX4tvPgZ38QZlcOp~-Dnjf76fpX{<@Zk=dnw203t9SI zgE59yWcYox%B{sUT0!j!MxRARbyS4?U$}(PU*-x2Pkb_2W^BYIEjr{g7E(ZIz-7Ie zWA4u~7ry^(YH9J_A>c!xYB4tDVf1H12TG^ToqU<{u*5eZuM|B>{SE_u58_*fl;==B za)|qzVI`<<=D^HKj2;(mX8N7+GHFM$vEAm)N`^t-;>Q*E>OL$7hLj^Hq#-sIlW&aS z3go9z(YVCunPMT&K*ncjOHj-R0B$^j?;aXvS2SPWZJ@QLlz|&-ntMAb+}sg>85<#w z`+Y-f?7?0P!lVeL;pRH5HKDsxXXY&rDJ9oe72hs}BpUD!g6$u-fl@3z9&Q`7fyYhB z&A&hyL0;~1VX}|W!+mUb3}PIF$OqKgD;cyviS;M={?2Cn|R&fJ6Tr#s(< zv;&@Q!R7+!g^Y^a|x`wRZGabwC^2MnFpPwA!?dN;ptPg{(fj{OUShfzw*H7_k!J*a`TzdqTFj#SmVBs3=<)5x&xgR5BL-77p zP!YtP0&)AiH&~p?Jy&6Pq(Wi92cxMqLUtZMU~6@e?mw$nkoH-ij*#pJfQnq=_EF%p w<#-bYQN(CO(yghmF^7sSA?@2h^yHQQ0y0uidhe4R%>V!Z07*qoM6N<$f>mpa<^TWy literal 0 HcmV?d00001 diff --git a/resources/arp_up_and_down_off.png b/resources/arp_up_and_down_off.png new file mode 100644 index 0000000000000000000000000000000000000000..1909a94aa56f2c8f09e6bbb4ceebd398d881468b GIT binary patch literal 457 zcmV;)0XF`LP)?F)}Ig*{RKfpO+nGx(h!XbeM})a zq(wzxLqrgo*HIo`UEVc_bmzOgFVFMi9i9hlGrMr49w{^Pc1^4u31MPVUD>)NW@qL!P)llF*McssmVvjP8Y#Pgsb2y22g4nD6wu1Rzv_=wfgvPa|!# z;w4YVNY7l=k*cc@&$&wkXaEc}{2r~(0i?GcQZ7qE1WZie>%swq_3jthB_m*AVxV*T z;{=umCf&FZ02706)B|R1fGoF2G7>Jp;ujsS?D%kh(mveO=tBf5lu%0dXO0}yM}NGj zH;4xblUJ?|&)3RjTMlo<+{#DJO^_fWg`0;}MX8HgR{bB%BY}n=9^WsQ?owKP7mAg= z>u>iC7LDx2{*G3u>5{EkBeO2I=~{Z5%D?yrlc93&T+y^s00000NkvXXu0mjfyiK{* literal 0 HcmV?d00001 diff --git a/resources/arp_up_and_down_on.png b/resources/arp_up_and_down_on.png new file mode 100644 index 0000000000000000000000000000000000000000..2170b84bdadb2535b2acaf66f174531ebf253966 GIT binary patch literal 714 zcmV;*0yX`KP)A5@SJn!#)dCvnVXdoCq6Pk)= zLsJ{+f#5_?$;bYEp*nPq=?jb%B|KTz(3vFLYNxSKz!a zaQyHsm&e$B^DB$jMHWLn+UCwK=Yf2+Hk)`gn^gpOM{m3RBgaA=PLn^JAxLWos#<~% z0wKOOy`yGr^A#=KPqLARh2J$yJ;3Sno;6xq1{W#-H3N1BU~>SX33yontR2o_*Zza2 z>ub3BLq1;Zdt;RNjT(j43~)^Z03cO>^&CY?19dsa?o$M$=I@SkOLGx5%Tn+JG6#SF zR032Ikdy&2n_N%?SnNkC9^c+|qGbuA<^hfYr2s?;a#e&}6!7U?w5AAfId!9h{gx3% z&4Q>o5O!xpRFXlfBBZi}YE~Cs1Wq*sK*y*w?adsgVL^BXgxT2vl_{toK`2Qe241B& zL2P;L@pfs1m73ONSiX1=hs^7*M^4pJF;wP@?<`_bv@wb%eo3qS?b z2(Y{bY`=JRe{=f!6_gU?hB-juK}vW(eNq0l@sU#ltrnow3Q(Rxj<3XOZ|_|~GMe5s z%21iIoY|Dgx%LbjjJ&2G0I`+a_wu7V7fR8Y2+b!c{zJTL;`z9DRS-Rk#fts%Pl*hX4Qo literal 0 HcmV?d00001 diff --git a/resources/arp_up_off.png b/resources/arp_up_off.png new file mode 100644 index 0000000000000000000000000000000000000000..ae8ecd653cf3b22537e4bfd43c239b3b581f9a8f GIT binary patch literal 399 zcmV;A0dW3_P)Uq-j^boB9!tlUUh zvJzkIz+;P>QkJ3=p=wV>c>^tA{ zB<5FgKB^cPsP=y@4nlwjCpD#8kp@-nF!7_%0BG@sO&DDtit1JE3L6TSce>jbYP3*X tWAfzc7irBxQ$oA_9T)N7obCR7z)$SSWSTt0>)8MR002ovPDHLkV1o4ZtHS^Q literal 0 HcmV?d00001 diff --git a/resources/arp_up_on.png b/resources/arp_up_on.png new file mode 100644 index 0000000000000000000000000000000000000000..5b4d2d77d85c1e42b0421288ac2ac2b43d86b209 GIT binary patch literal 622 zcmV-!0+IcRP)`0Tjl6@5eUX)J8Q&(@7dlg4mD>3i}u_GOXJQvVtg(is+w^f{HG>?nX>( zW-pQwQV@9~6-5k-=)*q5+0@w9MsCjceNR^r!Z5n3bNO(7oadZ}1OIU~)y57EJ&Shq zjsxDmr5y@8ZyNEjwDjLkOZ~P>eSlJ~Q7b3*Re$&w5t3Nnx=$h!qoy5l*!qNVHjs<5-QyR*w!D!iDpWcZUUO?@TV z)W0nEzS#17LFBNJ%@B&Vm#14#h7TFJ#MxE;EwR@=&%8Wk_I>; zmxpe}_H2oIGnX+gYE{c0$gsG7sv~2bVBXs`o{S+b-wgtV??Rxyl~{`)f;i2 z2Cs^c^@7U*ng)vcdqc~HWfOdPXm-I=)-(rC?Axn5-GPv@2#F$0rUg9(?sW>PzA|Jn zp^y>YX9a0csZ?~`6Y1Eho2iugHt~axmW`!5Fm15DD>%Ih>MsWZ#k{~W=njRKQxLG0 zu#$7O0`3aUZ3ixjBC4vZ0xGDhWNm{jD4K1XZzk?u|MxHV6Y9*?b6}l0T>t<807*qo IM6N<$f{XMUhX4Qo literal 0 HcmV?d00001 diff --git a/resources/autoscroll_off.png b/resources/autoscroll_off.png new file mode 100644 index 0000000000000000000000000000000000000000..3fec304904ab8ce54294a581a3008d49ac9e7540 GIT binary patch literal 320 zcmeAS@N?&q;$mQ6;PUiv2?Eg|%)taC|8^E@11Zh|kH})6*nbda{L;0l6DY`D;_2(k zewAI6lY#s2;eBmDp$DEWjv*DddM7%1F*^#l-q*Kv=lB?Rat({`f!Q2v7vG){+|$${ z&HU)t+(Ty=zc5|?8~f$Uj=(J%UGJU+{|}x1F5dp9y9{%AJHJYD&jF_sdg2Y!+n+P& z9P>HlbuDA*8pFb!cAlOmotT(Tw5Ybr7(NYT2tRwO%g1M*-#zoy@*BUse(L%=u5@AS z_2dgWAHJq>zS(u};VNaG6-nYLH_DoO*RDF9GBs@LLW|dJJgJ{nP0lm#zF(Jj`|IUB zW(O**r$iOi8E#(BkT>gSjQ5ny&udkFZ+WXdf4?r5(%G8)e*gM+-?=rv7qr!D+WrLk OmBG{1&t;ucLK6U)IEc0Y literal 0 HcmV?d00001 diff --git a/resources/autoscroll_on.png b/resources/autoscroll_on.png new file mode 100644 index 0000000000000000000000000000000000000000..b1355c6867e7f8f895e54245aaa95211c07608cd GIT binary patch literal 322 zcmeAS@N?&q;$mQ6;PUiv2?Eg|%)taC|8^E@11Zh|kH})6*nbda{L;0l6DY`D;_2(k zewAI6lR?70_J0IW=#i(3V@SoV-U*Ig%#H%C_w%32C?4T{aWD4W#4}n8IA3t>X-p8f z>-WBxi~nKJjGYbQGv2*lwA@|1tFCL&>WYxRdTYzo|6TffcNe4HI=zWKhD_XwyG4Yh z7`hE7Z<=HprJ3zxlXEUOXww8WJz*Wo6N)~CVhzX3Qd@l(qz;@sUiNkI=ErH3xA%tY zy%4D8nZ;-9_)-gvvb9s?x$=X-`2feZC}6Eq$7p7s`mE-`Clyk5;bP0l+XkKcO`(g literal 0 HcmV?d00001 diff --git a/resources/back_to_start.png b/resources/back_to_start.png new file mode 100644 index 0000000000000000000000000000000000000000..2dc113ee2400f57d8b65ec6db3a35b857b1539da GIT binary patch literal 344 zcmeAS@N?&q;$mQ6;PUiv2?Eg|%)taC|8^E@11Zh|kH})6*nbda{L;0l6DY`D;_2(k zewAI6lZpS^3<+7F&<{@+$B>F!y%YEPIva|(olj5g`!I#4L83vc_{s7v7l%f!8wyPt z3(Xd$ak*RW?rC!0PruSUE*u z>e)0iGbaxw&z!Vm6_yo6jSUK{E8QJm&22baBC_O?ECU~NU&WmMx|y~9KKF>x n_S!(RREdUr_xAsE|I4t@xUes2`=<54FktX>^>bP0l+XkKJT#GC literal 0 HcmV?d00001 diff --git a/resources/back_to_zero.png b/resources/back_to_zero.png new file mode 100644 index 0000000000000000000000000000000000000000..162ee36b40232ff668bb6969ec4ebe27bb4cfed6 GIT binary patch literal 336 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 literal 0 HcmV?d00001 diff --git a/resources/bb_track.png b/resources/bb_track.png new file mode 100644 index 0000000000000000000000000000000000000000..db6a8c913c9b64c79f441e718fc56e43ee45e967 GIT binary patch literal 1997 zcmV;;2Qv7HP){f z!&{hrc!@@*3DRiH&N(ZEOI)rj?VYD?zvU=}TtE~>_@2+Xv(GU&G~nbj_fk|vk|e>l zZIU!aSr#vR^^asu#-qP`5KCALq9MgXeh>e*vwu1e|K}b2BRhB%hAlQb76uT2xF#SK zgspXYrnk4g8)aRhm8Q{XAdn`Glg1AM zGOe+sWI%+G8b1mULST&97b7{q0D&I!#u$)7pm+Dh$KWvtK8#ns?~~6L_DGSZa3E<4 z|GzIjVgD2G%6DDH1^ASYxxqG!h7nq4H%|v_$0c7X-Mj?WRmi5lL9H{5trSf=By|&} zyVF=w((*zwl``F%xlXE`;u{QYr$Z9N)HcTWG|fuqr~k@u zvA>5OMiH*(5yvr(^>VmMlA9Cc zN+qV{43=e+%N3cPnxfGRS*u^A9yJ)s5i(R5W61j+ch1+C$Qce*9e(=ae=wG&I4JEC zq}}FGRXIss7T9giAoDmfTjk!b+{dwd?!k5(j4@~f7=e(I*~uD>c8esA8N~xSjWzn$ z);Mvv#PN9#r6k>Mmp`1pz^rpucCUvT3Ydvo1XfIO+F&mnWz#CNA_881eVOwwzCnMO5V!}AdevSJvF&kf zC(9*HJ$#1h12!xHMj`?bJNKtefeL158;u|2$menhVGssRon2sl;Z}b3 zlONNIl7kZn3Pnn#G5{sl;oP~G`1UuxPT;v1pzH@smL`bfnBFiZ(*|uc#$;rATuDdS zum0;LF0U-}`kNPd_38z*(v+eSLJExDKit&&-+PDX zGuu4z^b@p}dbGP;hI*BJ@CqJ1e)PjB69G;s+8%T9ZI*MpqTOn+& z(WuvHv>I%0b@6?lBe$O5!uS74T#0$=H&5YPK53d^SrV=Fjt*=Bb!d0mROT1a6~lWk zpJSuGMsK~%%1RU8&#}?ftgfvwJ9mT(&{;;3jVR@8-hBH4Tb(w&UXQi4kkKIFmZLVU zFoZTRf-np}Qb$zn*~7?^WW7Cr+6I1ZihN~)(XdCq*Q43&FdVERq=QltTfNFF|2U81 zIRrtTVLV{<@xDWZEDs8zCM1LWOlx#R)>@ zbO0%k|ClZ)5TW`IGSD9gJ zJ3@ISl70tc0AT=wkOI&1F;XEcA7KRu%Ow}sEX+@E>1v&7ZRV5xKsV|#R71SXM<5u9 zbtvD(eJ3BFbLA4vl?HXeuR_Q(rj4YG;%7dTYnD znvXQMyCf9;ukt3kYnvyY{uVWVil2Y}7ew;|p8Wj}IQ`&5ynQ)j>9v2ezP85J<`$j< zl~SHYGo(5>Nf7wNS;oY|ajLf*XSBXdZWIzl5uWEUjALBKVU#3$hQ(4AIvan8VgNLu zv)LxyOkmq!LPPV)o1EL&BB&kZ&NE-*L}?P4CCE{ih1n@K`Z2dx3`0DguP+h3waWT` z|AHQFu_ebsBjt6$xo*$*8)-5B*d2xn%+wE|q@-DDk-7;pjae)y(Z=AqK3-vhLS>5i zqeq#lRtdLaRxe-VO6MI~@)EVi6qeYrTvA+*X1@jdPzmlk^{@%@`A=;s#f_TMTrN-G f-^kzbjivBEL3P8P?}>b}00000NkvXXu0mjf&f~oK literal 0 HcmV?d00001 diff --git a/resources/black_key.png b/resources/black_key.png new file mode 100644 index 0000000000000000000000000000000000000000..c6350945692e1089c7576c4af2727cddf95cd84e GIT binary patch literal 479 zcmV<50U-W~P)_C zX>@2HRA^-&M@dak?_?!z00042Nkl$@^;XrQA$N%4RxBxArW^Zoq zEa2*5d}WcE1+IaX2Sfm)(TF5T$kgxmd3t;RI0N3jtkLau84iaI48xG0a$p?C%w{v! z7>~!h!Np?1>&vri3Z5qnR>lWr_*7%Tsqfg zv&lMn%Y)PDl*we`9D2PTtJNwW9Ys+N0+`R|wA*cWR=_!M0yMtr6u1X2fi;i<@dsaE VeDwvg&<6kj002ovPDHLkV1kbs%x?ey literal 0 HcmV?d00001 diff --git a/resources/black_key_pressed.png b/resources/black_key_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..c58e28ee684d51a0a0e862d556b0f3b2d2347c18 GIT binary patch literal 459 zcmV;+0W|)JP)_C zX>@2HRA^-&M@dak?_?!z0003(NklAs=7>1wZa^H!l(M3vQH>Hb1kgSDL za4Oxp_-pz%{2%^{E-oDcrH*!UFjE)PAgg* z=;ki(7eLp|Cb5fO0QdPl7_=vMt1jWH-O_1B}d&c-XLb`+5elvX`xU8_cm z2*wz+*5#?Y{Q-a-ZA=zObq3|{F$d2&+KBhfhXVrNPrLE^%HgaW$M6?b#edz002ovPDHLkV1f?C B!F>P# literal 0 HcmV?d00001 diff --git a/resources/cancel.png b/resources/cancel.png new file mode 100644 index 0000000000000000000000000000000000000000..49dd0e6abc1537334cb649f3a8817720bec73779 GIT binary patch literal 882 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 literal 0 HcmV?d00001 diff --git a/resources/edit_copy.png b/resources/edit_copy.png new file mode 100644 index 0000000000000000000000000000000000000000..5819ebc944d49c9479afa9003dbe39fccfba0656 GIT binary patch literal 589 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 b*tvfIf7?Faua*u500000NkvXXu0mjfgxEc9 literal 0 HcmV?d00001 diff --git a/resources/edit_paste.png b/resources/edit_paste.png new file mode 100644 index 0000000000000000000000000000000000000000..120793d6a276b3efb3b68305c19b5fbe64eb9b14 GIT binary patch literal 880 zcmV-$1CRWPP)KPds3=Y1r6ZptuZBSAY&G7Bp4-^Z3{P@W*Z{8*bzR$-lIEWtp z0P=x)?v+AZ4g?4wmTy1)@0YQ4G~khyWB9n_KZB*E4#Sftudph%wA5obcIc^rbbunm zm&Z39&UR@#7?)i&#HJP?fLIv#gbc*Y6u_Kcf4_n0hYz3Q460v$e==}Max-v=sWUt| zcf$aeI)DIT`SqWL;r}m)x1H_(G3-6C5pNv0IQ)mG|M{EY*B>UF>Hq?Wad)rne}`my zyoOzUkc?OU%$e&9$;qCK$YKBi#PaRiKZbw5e-KP9`uYlp;P~~Q;m@Cc3}3!{V_;%} zxO(o~?f;Pq2M8dhZ{Pody@n5?17>DMAZ7w%7%(^21{)6J0|XGzV4#bEn(<+*VaUh; zHy9KYCr@65yBtIV1Q5%QpTq?Px}jiKfrH^6gO`^T!^e+b89sgb%5eGe9k3ig0I~f4 z_YcLp|G)ocVE6{afBpjXKz$~P&9#g`nh{LDfB&7~`}ZGUHVgm+5X=AnprH5(Hst@i zuM7;o{}JtC5dHV>e}><`|1z+!Ff;u7hX`|k0Ad0182-jzAZr-@z5PUVq=8)f>(?)cX0Z1mt_1mji3wpaKmf7)2YTh-uir!&3^L>Q?>}Id z!7wOgfLsfTxIcdoWivnk0SgLfCWh}{e=)GL5>5)g zks=qC)R0|?3;+U%rDfYK#u_ix|DwEqa2n`f4-1OF&Ga0R{jid9ATQaT`ql0000H?fKUpa#~{GK$S|LegEzom#h)QuIh;XHP#;CfvU^Jz z7F=7vaP#fW3;+Ky1TrKsJiul!KmZ{Op3Y#vz{IfMRo7J@$~cOFhm8lXS7#lc#jyMG z?hg!q859|^7(SrO0|XGn;0X{z)s)l(SpKsxJb&|?;r-|LU@1;EP6kaSO$IGZEe1&$ zNfgbS_iSdkaqY$dhI|G`bb|o`h>@Wk=zV5}`#`n;5I+D~FcBCW8yJeAUhHL1VEE6F z!0?YDUq)L-K-o(fuKD-R-wbCKo?-a);g=&rIl}>Dg8>4FfuV_EJwr24Yb!$mUca{i zEpA~r&#{r?KMxXQJIVGRsDD1Xa)1D0WT;_y4|Jg;Q1Sv!Ln|2^psEFcKAO)E0;JWT zaxZ~+4p8quhC*a500G2EFhQ0A!~Z|Sc?NzUn;$Bs3&k+77Knc$S@e_P13&;V6Eyf3 z!wUvehKCHl83GtQfqWw%76b(_5>^3X3LyRq#8-hSr{pd00G2IbRInmG+Ya4_!XexQb0Z{lA)J?cpJo}409oI z1rR{YBozP`fKU@?_)VbU5>Ugh0`VRo{tpbO#SAB4N&y0hndH*tGSuaFfQEx23}RFw z!%|?{M*;u=L_UfJCc9{$JEMV_2MYiQAVyl%AOHbGv+@`qfM{kgKmgIqV1NJv02Q;T UDbiTG{{R3007*qoM6N<$f-EE!-2eap literal 0 HcmV?d00001 diff --git a/resources/edit_undo.png b/resources/edit_undo.png new file mode 100644 index 0000000000000000000000000000000000000000..f81746373b1f1a0b3e94ff21f06b589a8b716e2a GIT binary patch literal 682 zcmV;b0#*HqP)Lq9mBDx{jlnX zJ$vfQ_eea{S}$-tsFgk&2x0<613<1QO`%%gc^pXZ^0DTBrgk3hQTn>Kv^O8XF;_Cc zh-}|7nnL|g?}9i5BzJ>-PloXwNe}cz@NYBY!jc1}WdoQ7W@Pua4xovTqrfO|2Z#a& zf*9NL7nWjrfX2Jl5F!fA!DTkg1vjSFaEAvIj zu9eBHya!WH0x_A1%)U!#$5?o%Y|bx^7^t^iH2`$r6)?RW*Z}|kK@J`K6C&o|@lzol QF8}}l07*qoM6N<$f(p$gTmS$7 literal 0 HcmV?d00001 diff --git a/resources/effect_board.png b/resources/effect_board.png new file mode 100644 index 0000000000000000000000000000000000000000..01f1993ceeba0044376e1557a9b57962a26534c1 GIT binary patch literal 1400 zcmV-;1&8{HP))l6$KUXK~Q}2VG8~M5#5UnMMOoIpu<07 z%!(6hSG#H3b?r8{uHD+2X6tQ|rnxsaH}OSb{+W87zI-3fIbS%Ouf((aRP^5_=H(L!Qo#Z{p0o(HdEq3)(8L!IzA$#3Dbne6z~z}`IyJa0JZ-|yc;(X z>zJa#IV7so@F3AZJv->(ChlY%+t8RJ#|sQI$n%u0CGc0sfC{*NpJhM+JhaiuexBwS zrL>k0BOt@LptCC+i0{Vd?;N7~p-jC%=jk#WxOD-{4~ z&z?O(mgS(LD0c`U+8oKjGOd%BW-px{9UWcD6>>Gw=-k2v26&ovq_KFN&v=)&DDV_% z26!74g~L3_GYkWc9E-)e1A)N%ilRIvgt%3fwOai{pe`Pd_xJbr&(F-vOdDSr%k*MV$Nl6P;wvt(85I{d zbCIKBZEbB#Q50M*7lvUlF)=|ln`Og>3JeSk5Rb?4`Fv=aMn0c!iN#{iZrQS>3Xm}P zmK-&p8Bkc}0u};r%U-WnL?V&f!{M-!&1T7DGOVnuP%f8gY;2^vyBotWP!t6r1oidx z;Y1?gnw*>*GD5~0_1u6#mT@NOr3Hx_808B&7K=5Srg>L7oz_g#WNB%M)zwuBg#xB& z5(87(@<5_t0>Cik;x;coS>6rnxAOkGHq<7Srwrn;VY-(yEl}d5t$`wkb5|v7YTCIkrX#|5oJRT34rr~zGg>Bm_L$gEE zg-T(S9~fsnCFU9D1O~7whe9F4=krCX)oNcRlM(Ih?ezBcV%s*BWnozs!C;U?B7v^! z)YaADa=EZ9D?j$#*unXe^FOn|3g;;>hY46fMHv|xF}H8u{zgkn%O**Z2FAz7#n!D` z*}Z!=hGAe?7U6K1Kp=n+0wDyJWl=7di&oaM_yq$f|K4iO)xFE?>})X_jZXOe{;;m= zZI)#z3kwUiWHMQ6Z*Q0Uem|YhW{YAVDIwDKoQOYkH}&M z25un`X1sK_?hjCqy~NYkmHi5fkPxT3V&j%Vppa~dYeb22er|4RUI~M9QEFmIYKlU6 zW=V!ZNJgrHyQgo!`xMnYph7NB7srr_TgeGP0OYbVFjlPy4*;?lJYD@<);T3K0RZ?n BD-i$y literal 0 HcmV?d00001 diff --git a/resources/envelope_graph.png b/resources/envelope_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..dfe7d6cfa73086856c3318aa6175e989c3d9ff8a GIT binary patch literal 3688 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 literal 0 HcmV?d00001 diff --git a/resources/exp_wave_inactive.png b/resources/exp_wave_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..d59dfd120d47ba899c445017e760c861884df43d GIT binary patch literal 573 zcmV-D0>b@?P)_C zX>@2HRA^-&M@dak?_?!z0005BNklIw3KlL_&4bOm$Erl_WJW<-@-BzHbMruFYA_@SN>Dz_X(P z=ytpLd_J4aCK3r<*Z(o4R2;`)7*40t@p!xiKnNkFjN@2H!OWRVhM9#BLW($!Hxz&Y z;QrxWNRdvZO*65#_p(;23I>D8WI{wzh~;V(1i|0wm8-Frabub=7s<(P-ql?r=E#dVQ^dnW1Ue-+yHQAgQ1MV9T-` z#|dVE<2cErrIZ9i0~i3X1wfIKN?KNGcefaZVGzv1Ff0~}DJ!Lj6e&^)fDRy+%h|Tw zYByWW*2&3-&leZ7r>ADKMT-1eKZ@2&TqqQD07vhRs&A_8R=eNtpPzqvczpD~`^0p3 z_%>mh$z<|ZFx%dKp#wPmcv>!(d%fOS|7;%4nJJ3azV8#MULg6DGou58A%p4v<7^I(94gfn zo-rkfX{50wxuv{iU}Omx?c8KtaiUmLaH}vRU zS7=+nykKE?W19ujOm{~%1{Q%E4dL+;66^M-B)pilskc$%!M0NSGXg-J?T*LuI_7&a zUNA6X=TO+d9aq;TvFE>D%RjC!cjXoSvGV+mua}UhV|pp?@Jl}+=kl6#o^_o;2HSTq zu?QUD4q(1l+s@%jCJ0p@-G8#n!T_z_=kY*6!q?|YAd14AQM#b4H}1K}PQ{xRR= zGh)g5?XSW0=s$zhhj-FSK8#EpIR~UyaNnzIKEiTh<^F~1esFI6`8}TH|7U-f3ZMll zQVZ3Zl9(lI5}XetGF<)h-QLC6gD2&HxC%1^NbmA|54|7!p6vAt89BN61-SSg?AKuX z!@!{sFe8(3@e?iPl?l_oYborMQ)qBt+rYF@QNWscqrk*ljvoscJH7)g&`EB{C|i+u sgdyPFA671{28JXSRV^;M1& literal 0 HcmV?d00001 diff --git a/resources/folder_locked.png b/resources/folder_locked.png new file mode 100644 index 0000000000000000000000000000000000000000..307684747fd32a33839dceb44b1e2699bb207aed GIT binary patch literal 797 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 M07*qoM6N<$g41gse*gdg literal 0 HcmV?d00001 diff --git a/resources/frozen.png b/resources/frozen.png new file mode 100644 index 0000000000000000000000000000000000000000..ce4049472fa35cfc2786e21e22a962f59c9694c4 GIT binary patch literal 425 zcmV;a0apHrP)t<9H%UZ6 zRCwBzlOa&UKoCXWb*h4GHL=lPqwAxqqgxBQx|-V5HX2QB&_OQP)Ka^M+k_)zj$vmq zna%yp{{MFmVj-F%TErkT;*kLlF@T%snpxonXo1|`H^3Tz)Lmi>DpFSiHBbOc;B3W( zJtGEQ=LXmUPo(bNzL!85ux|E+U?kKnLu;CHR8eKnLI%(H)Qs z3`HCBqBHDrJgyz0$zY`D3DFMGPehwxoT3S$>pt&IY=I-GyPBldjKf&G121-V1z!7n z9%$Pm+7}j9SR36pph5It_`;+fLK$~PSrE;KDR}MPCx6xH5+IsgQu&X%Q~loCIFyaJIVk6 literal 0 HcmV?d00001 diff --git a/resources/help.png b/resources/help.png new file mode 100644 index 0000000000000000000000000000000000000000..8bd601edf715b424186c1c5ec2e4bd2153c273ab GIT binary patch literal 1185 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 MUHx3vIVCg!0JI~2RR910 literal 0 HcmV?d00001 diff --git a/resources/knob01.png b/resources/knob01.png new file mode 100644 index 0000000000000000000000000000000000000000..d16a87f6cb72bfd57fb1d93a8f24716bb86ac23e GIT binary patch literal 1640 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 i5Ex}P0mh$dxBd$~4!F?o2Vv9z0000(^e3`s;m zRCwBblub+XXcUI8q)Bbk*iZT)DpmzcH(F4n;0OK!!KMFZev%+=#FYpx#DxXHSZS?P zsBJAx+NLo{`^;kIbr{BX&hl_`aX9yVj>K=OR4NHg(-1;1o6Q&wht%tJ=_l=fnwy)O zqF5|)baW)L*{tMw9**Pi_V&i@?Jdn_lUl7N{RoN0Vxm&1aC&+wN~MyN&1R7#3CD35 z4u|x5Jw87_d4GTB>gtO7`+F?Qk^q!nkaD@q$;kU|ANrt`iQ2QB+kWkw{<|211B$ zCt9r*?RJ~v<70BU9P{(@n5M~aIK*{bT-RkV7!U*jilQjQ<8gAi9Dh%REX$~>imvO7 z$77zJo;W)@!!QgKMfqNKx7)?@JQUye@jQ=2B0(~lBoc{W7zXKdnn)yqX__1!9#SY2 zFin$gx65!iWH1;|uh$um$0&_PLwb045WBm(|8if5oLzNvy7} zQmIsk$K&X_P9zec-|usMeNC-aqgt&>|N9Dl)%yCn&@_!uD1;Hq)$07*qoM6N<$f@{N;i~s-t literal 0 HcmV?d00001 diff --git a/resources/led_green.png b/resources/led_green.png new file mode 100644 index 0000000000000000000000000000000000000000..ac978151ee350e8392481e4720166854d98378e2 GIT binary patch literal 934 zcmV;X16lluP)wE8Q?fh@s4B0L>a!X}B5>BKu1Ni*<$EflGh$<0N;=Yx~`h(@` zyN{LMohFx;0YCwOvojaI9UmK+>`y+8+5Bf%Iq(S*!#Y^Hf_h>B>njzQ_D(QDQ@OL% z{^XI`?fhJ~Mqi$KZK&7K$23jF#k*f)Vb2M?$v(wlb__eUL+I*Fu&E{FHopOHC{Wu^ zWg2r)?eHlRY@<*zn~xsFd_REAtC(O%U?Pd1@f8*^3qb|E@D@DY0yqT|p_FTBJ(!Dv zD!SAI;SUVK=>+Cmlb&Je<` z2?Hhu`9Tc22XXD@C4f`-K^IQjZgcQ3qY9M7Xk>qDE7+xlLzx5PhcJ8QDs*l_Z}-Ef z58;;|Z=rN0gjXkzVs&+;QMRhKU!^|@=HF$6kj{qv`*-|TFP0t2n$vF`&z$`7BPd}7 zka*1v>HkVJ622phgk#)T&GJd%Mt+aH|%{{OiDXf7XSbN07*qo IM6N<$g60vYg#Z8m literal 0 HcmV?d00001 diff --git a/resources/led_off.png b/resources/led_off.png new file mode 100644 index 0000000000000000000000000000000000000000..ef8b75b5d6b536c05e120a2063b9b01e0060f171 GIT binary patch literal 909 zcmV;819JR{P)()=>cI`NLaQFVT@ zZM&un4-a=FnxcK0rt!gtS>Aa4HO9uqh~F7!`p0Rq*^kKOerIj9z`=tD2&!r~5Z85m zMak#$P3!i0^VZfD#X^yhkr7hq6xolnoc{VWp66jZHi@{65Q0!py9Y=B$WSO0@f)5} zt!|K?zs%g+95X-9aB6Y_ujZk78r}DGv*Ef(A@MvFyGWi6H~jy($G1F={vT`VkJbsVQHsLI;&&py-g=@%y{t=r6BUZ7ldIDY&kti?t0 z`F!P{g)0+M%9`){=Y_88t-zp^($G7*4(!?8vpO(1_+Ts+-z@}GD-~p_+CmmFLYg(Ko2mGOeXunk?2D=w;Hk5)|Q~EsDW#yS&K#^o9j2$&X-E1954f1 z01yDWt^;&wn%1kT>Z8?awGQ0aUSLwwG%2MtUDy2sI16B!<}G+f+fUko5D*07+t!k% jX{MAC*L5*X6M%mKFlZ{)vH3$M00000NkvXXu0mjf!ZfTu literal 0 HcmV?d00001 diff --git a/resources/led_yellow.png b/resources/led_yellow.png new file mode 100644 index 0000000000000000000000000000000000000000..5b50506d2cb1aeb798d8cc581ec35eee9c79632f GIT binary patch literal 913 zcmV;C18)3@P)YgevQt z{PEGGySnd~n)+z*Zbt!VP9z3$!O#CP^qz+X(z}_S{DiK(imHOIrU>`F%yMCoT<-77 zAHLzgt|Y!#1yFxJolNFB?|qrgj_%*pzmxp@H#{+LicGqPJ-aqZhOct%)+)``FsjO) zQ)hCir^YLP{5fY=MPVhH3>u-mnkKn9_dTPL6UhFLA^s+W4`v-mZgrb9pk zM%;luJ9XC13;vQhIdAzA!=ro;+jp%F5;S> z;yeE>>IV*>=!YrXu}~Br8Nio9=l~*Lm~F4*mn!QelbIX;aI=uc?MBc<1Y0|VC7$N$ z^hG)yf$P~=R=rtoc^ZHki-pDLyfS&_BdOAFt*opvmn9Sgs9FS)nL^Tv)pOYup&=*F@nb+qR!?Y;7$cJMvam?+T|CDd=<>rQa_9Vy&0U znpvt{@f0r??olrN|1f~(#>e03d)OEc1oi#+?a+>BObRKLwe?c7H)uFk!<;Lv+1E!i z?ddb;N>}bH13Wo$;3cu6bx5J^D8j2au8-YT;=Msl_68}2gZ9kX^BWWQefvQk$)qyA nkM8?~0b*gVJTtqt@}T!GWouJ;L5lEH00000NkvXXu0mjf`@^H9 literal 0 HcmV?d00001 diff --git a/resources/lfo_graph.png b/resources/lfo_graph.png new file mode 100644 index 0000000000000000000000000000000000000000..67fb82939ca96799cf8ed8b74400321ac8c45405 GIT binary patch literal 4065 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 BC+h$J literal 0 HcmV?d00001 diff --git a/resources/moog_saw_wave_active.png b/resources/moog_saw_wave_active.png new file mode 100644 index 0000000000000000000000000000000000000000..8c57878ebe6244f9b8f82f6b2d191346ded36111 GIT binary patch literal 538 zcmV+#0_FXQP)_C zX>@2HRA^-&M@dak?_?!z0004zNkl{!@4 zuNjK*BG}OMnsr#piZ!K{xvuLh%N&5)^{sW*7`s2Iuby8WeLX5(6pNy$ZuW|lL`B;1 z_^|`9DN87{lfqj z0f5l_5c6? literal 0 HcmV?d00001 diff --git a/resources/moog_saw_wave_inactive.png b/resources/moog_saw_wave_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..b4d1cd616a72caaaf3379149ad2958ae90da6901 GIT binary patch literal 587 zcmV-R0<`^!P)_C zX>@2HRA^-&M@dak?_?!z0005PNkl(?OyyqrxDCdq=AgKb|z_@duQ(bccF_ebGGO3b?CX-COW&Fx%aNRwRNz+Zx}|e z*DIIHH@4mH_a(`YM~nrUrl|ma-ra$qC$)E`83b%N9F|I@*=#npECoaaMZxawYZZWu z1QhJO*{juRolfWX@)r@26hcHGC;$PQ0zwK+*LHRcBC;$C3Vh!$7K@2^;_>OP0$OKd z0J7Qa;oHMTqw(SF?A!OR^Lux-S{)s|mqI$X4r2jH%IEVcfW*s$VHia8h&504varBZ1&o9%Y{>dO@i7?ETwa2!WUae8tpBH`F}CX-PCNGUnz zUO)9WiiI+KQYFp)2U@ylgXq|C@28x_4;$} Z@(;*Z70__SFJb@y002ovPDHLkV1ho#0)_wp literal 0 HcmV?d00001 diff --git a/resources/mute_off.png b/resources/mute_off.png new file mode 100644 index 0000000000000000000000000000000000000000..58d9e9a048986739af87578b50fc968fb8d79459 GIT binary patch literal 1022 zcmVUuugC?4Yi3bx88vlVFh!TT6XkyaR zr9!d4pth_)jSD2ZxXAA8E}Ml4vof=lVBuSQKRVtN%>GLY1 zePFz+C`z?O2%#CYc({81xs~@{jdp9&dzbGWUe``BF8}~z+cqIYQ54s8ZQD*H5(psx zs6|q9JN?Pb=O0GikpF>Xc6z@&{qnPz`8f7->6+ZjwpaIsg27-o9M*N+ z>-Ew!y}rJl%jHU?QoisY*i;h=Ns~8kzjnNTCBHs?=d-?{`nOJXi#+<`+TGu#J?FTS z6i^RA(P$I^8XFr`RmB)%jFZWvWm%pwGhbML`&?gh);1Q=1=ZZ)rYa z>^Rx)R+fLdSUy|R#v}HhRzN*HJpiz}x>_og5JDJZNs?-7Yh_txfxB>H>iLis^&dq9 z6^B>Z)X>TTg}_4OTOa=jdd!1QfR41Z=@>AvND-W s)e5$4YnqmKJR79Y1pZLH_>}bj3bCVdm8?V9rT_o{07*qoM6N<$f&rW6_W%F@ literal 0 HcmV?d00001 diff --git a/resources/mute_on.png b/resources/mute_on.png new file mode 100644 index 0000000000000000000000000000000000000000..499ebe33dc3968d079ed1bcf90466582c48cf344 GIT binary patch literal 1062 zcmV+>1ljwEP)rMv00YKJL_t(|+J%(OPTN=jg|B^Y>^R67 zJ9ZLBWs)J8Ab!*~FdZTS3kVVm7Db9!b;0b)qPso;579**pfA7zbpcfxF^C@_QPDsl zibBY^N@8$KLVoPTzIPT#-A(6g&hF@2d^*(T<|Y6D06++NczEDAP83D2*9!nJ3`12_ z+qQ$jAo#z~kJW0mXqsMKU7ehqB!pBd6;)LkhFM--&SWyGs+y+x^C7sfu+V5UhKGk2 z7ZC^?E&B*Nep>0AOux?ez2%W9)Ld{*%aY9JkxOva+(fyK7liC=~Mf ze4;3dqA1I9Fc_@WYI}Qo8yg$dYV~JCu~=+=iP4i`1p7-nT*9^2qD|H zd7d{4gXD65eEU|?br=i+ng##>+a4+wQx_MY)oOb@FqurIQmJ~q?smI5j!PsGv$M00 zT#_UaLTuai=JV4mJN@#d`{3ZN+wJ>&fDmt?Fn52Sdiz#_@c!}=`Tc&3vDfRx7_%%Z z2!f_*7-NjFVHkoSIF7@7{~ldiqf@CrEepMW|2IVugl1GV@%l9#kN?Ur<7a2cG)+Q? zEX$7Ln5J1M6d;5E0LO9q{XWK6mgUymT>j(7@phYi{`|%Iy8GQbn~-O3-Y}7feQ;3t z{P}P&K-Fs1FpO|GJQxf*oldD#GEI}FX+lW9-nZbb3;=~H830#MWm#;{WP z-Q&5ltRh8GKW^kPnx@riHI`**nl?={7z_pi0oZOwE-uE(<)hKj#>50+7ytk@7|2&w gbE;aD_C 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 QKL7v#07*qoM6N<$g7V73O#lD@ literal 0 HcmV?d00001 diff --git a/resources/piano.png b/resources/piano.png new file mode 100644 index 0000000000000000000000000000000000000000..3f3a8203ea760773208ca0c3b233d7d2f79f5760 GIT binary patch literal 838 zcmV-M1G)T(P)ElP8=xBvhG)Ja4^ zRCwC0lub(mNf3sg?w-s?h=?H&NIWPSJSd1Dif6&I{uA&1G5>;BQLkduc*v;JlbOkU z_a5AtRd@HWumweT7tPaEZ&$ZCJ3BLbdwcw!c6WD&HJMB}I5 z^PI9Q0dQRxDJ4}^A%s9mNl_FirI1ommL++flVuq)iXuGELOnmrLR}#){(@tu+>Jtu?;ylOzdI6n%lCC?X6)ilRVkjqAFk zX-XW&WLZXCF#qA2+I_~7>TmZzsD!Z2(l`uzOl;o*V%`}?nqwbrC*N)QCC6k2P7ARy24 zwv9eNKknn9#V~lCr@Sn_z z*lqjWeU6)?32hf6hy&lFGR##UU4u@=QZPDp;7!HS2RfXqy00<$l%Ccm+ zTrwC8xV*fiu4~rU*BOt;6h*=2=4K1Ny1HVyTrwJsI6XZjO;a{DHW&_v{dgKgXF?(SlYK}t!#-^Un(QmQSldHPbycApS}e!q`pS)^%-QVP%Wke=ru zrTowTLDMO}#~)y&6jfEVB{!SC>-#Lja*N Q{Qv*}07*qoM6N<$f-4$>+yDRo literal 0 HcmV?d00001 diff --git a/resources/play.png b/resources/play.png new file mode 100644 index 0000000000000000000000000000000000000000..d43c2478acb082baebcb24768f57dd4f0544f01a GIT binary patch literal 1653 zcmV-*28#KKP)kOK~#9!rI$@;TxAr7 zpL6e>NiyxsRE^apt(Gdpnrc%;il7Kq?NZ#h6Bn)&DJZBQE((I+MxjsySAs5dT<9!Z zDE=;DBQ?fCYKUm9O;VemJHt#S^K<9kdtBTvH*+(SHq{ftK=REw@AsZ_-tQb?yLJ)* zEMNz)6Rzw6g*%BmjUsM0t^5nC{F`Nfl(m$;LwBVmmYXY zdVE*kJ^QYnxqPMyR02c}Z-K87z5?cXnfBHIP9ea4l&64af!oFg$8x70Ic4qHu?Im= z6cM>5h=11p;lvLo!e3W@t)k2!d;olgaFG)mOe6ukM+W5?;056BHy?P@d35iihT!PK zwrqB$b`oen!*39TLF7eyC-l4d?|9?v8v*bea2ogsq0aM>M<+h{K51YCI0oD{k{_{$ zv%{2HrG&#iyN`Tdo&jrsnqQ;bE^o<(q6}q+7`bu8nyt?6GrrFvoCjJ-04aunr%{d} zJH+@t z3XJn1b3B9Z@G)Zg`Z&rcf`cMU?WH)Vc63jkJoT`Sh%jIea3FgCv4pZ;=5pgQt+15< z9Dg^381+1aa2%NA6RtSeSimj7y$JdBKqbEvQK(4(DoQhKMsy2gtPHnjZl{zhu^Ozh zTwlfy{cZqGT=Y4F!zjBEmK@UTFiN-291{dSU;3Q;cHKw6)t?l6E1;o`xE*b=$x4>P z`NKH2LwT{xg~kOMK?4+;VUu4fzpU#fD2~yYQG|;QHam^B-)tV6hyg8e$-Bg0dN2XN z4s4oXGx0Y|EOJhcgV}>Dd&^YADl@eiJoRurmy(t`%iS`u?m_s$A%$&Tx76nP`Q7`S zIe(5@`)arS?*1@bxsL%wuLbJ^q=n z1_~DZ1=3cUw4~|Rews8Bb+O)hg0Rck#gXh0W_Ham)0$yrZH16`UCz|b@I~bd{`UVSbi!)@NX3&(8%U#Z z2X%`HCVM>q`0C^OF2zcbLb~&U6tTmL-!KQ{V3M`u$z>bf}CIHf#f%Th@ z*T*ST3rw}9Xgb|>(&23dWg4hCq**g#)I2a`+)E6gGq@V1FaW|ZB$N<0a4FV`6siR# zya}3i6Sb6{ObTsa4wy!%IoO22BEmV~E|kH<^Qv7*ivcK#8@P-&$0@88nDVEh;E{YL zfp37RqMQSkfY9L}${(x%Um}bG4;!MTD;4W9VnsQxP@z~aQm7Z02%?;;t=(zc@ZlTk zzXF!I6ODS{PJAwM-pp`gDEat3*~Ea@LMS06E%CGWGlf=xsbH#i@J*$Hu!`~%FwOpG zVs<*HT>uIQcOX0t^y&e_n}9m*Ke>00000NkvXXu0mjf>Dl#a literal 0 HcmV?d00001 diff --git a/resources/playpos_marker.png b/resources/playpos_marker.png new file mode 100644 index 0000000000000000000000000000000000000000..3985567e9f77670fe60fc8be7593adf74838ab55 GIT binary patch literal 516 zcmV+f0{i`mP) z03bW&9{>OV32;bRa{vGf6951U69E94oEQKA00(qQO+^RQ0~Zt}5b7fNaMgYaf`Xt{6$H`3fp!IE z7&xtF;DJ(128mg|w@I>0(f96|Mf$L$2|An0Jr_Qg-?>-Cj$)FErig~4e74RX&RHq0 z;GY_unzyjFBi#rhf*@$o2;%ME_5d^*EgBS*9!doB!F7LPJaJHMRlnqQelwyXXstZ} zc!g-RcY!M|E-qHxqLQs_)qmCZ6~Nl{TG(uubGEYg#uy_&02CmAdI9@re{D+}+p=k# zG&byvy?bZgU8sKUWGNXkAI)zyY6l@VgrN+4%J&ilJjl@wZQ3+%nsl%O0r6A;1}M#t#np z$;~kIag{MB6K4lufkh^m21Wt@=e~CtgYP7m}D(B2lH`EqDwHs_+QusZ&5YKr#r7u$_X~ z0WXUdB_WJ-HRE&V=ggfuXRHCAxbN~+dw8^hdt_Fx*RgGzFQ2;~WvA1@vMemi!nSQD zlL^!56vuJc@Anrcl2Rh2MAvnM5K%kC;}-gKU5~~HA<#7KWZrx}&(qUWw7wh+1^~3% zZ5oXRW7UD{&h`ec<0BpBgfIwb* z48HH<`#!7HiuHPp5aJoJV~|aofZc9)h~%H-O%Mclp2udhVYAsFgupNi z48u4f6SsJj;~Pk)(*QV*!^5Msmwz+aec#7*U0l~?wOXO;dUTW_fpetd-@2{?Fq_RN z6bj$uzr3NBqI@Zrecz8Fxm+%hw~~~XqI@owe_a@w%_iM$_jV+&iSlx}1Yka&^Wojw z_p;S$QK?iY7K`L^Icl{U)oPU>2%?)DH_!7Tzvp?+u&`*Fh7cms#A9s?_3<25DwXK> z`@i*gG#dT)*Cmt5oW($trwY=LFaZ0@VMJ$S(fK{s{PZ)XBTjI46#JjG9)bkgk|txFH;dQtr=R5 zjNamPb z3zE6ox>f>Vae?JXaOH?g-~v19_6BYQZUka(yFd7hnxo75f6Sv2lK)jg>pxkf0Fnem zL_~UC?}g>}0plBRMK+twK;ZpURaG?+6{5FB?J;w2^Mee5%urU#AOxIqS}ZAmr{}$M zj)jG7$fjvbc<#_^)W+A01-}YHpe)O}u3G}z?RK1{$<^;NbDy-z#@@gTg{uC;;m}^0 u?(QG_>KvEv?9TZbA;>XN7w#Jog7>ty2;_?Z0000FdgVg+)?8LV4i^263Q}Y>8_`iF1B#Zfaf$gL6@8Vo7R>LV0FMhC)b2s)D?y#JZW&7%L{3iW%|*%`R)3_aY*Da>#|(Jp6lVDN;L&m>nxemzpr6J-6x z$P~ElM)q1p1BUj;nm@&b)^jgn-8$p!HHO&lOsiJRRKB}iCIjeE22WQ%mvv4FO#o0` Bh-&}< literal 0 HcmV?d00001 diff --git a/resources/preset_file.png b/resources/preset_file.png new file mode 100644 index 0000000000000000000000000000000000000000..ad4cdfacb08c9d4d2a21537f489114277246fc41 GIT binary patch literal 1522 zcmVozv`YMJfER+f=L24NYnbjSw~1B4JEm>?trj06+>!UXxS zM8k_17Ys%UFUI(WBt!|XQKk!E=m;R-0NF4C*~%On3lzGxTYK;EL+LEtZPyPHPjYh4 z%X823JpboBF9-hTCPfD>m#ft2bncljVZuyRRfqmF22zBDEX&f+(2(MGyHB;Yw!Y)_ zdfT2NT)A@P*Z1z-6VJM}w6ut_va%n5TmVB#H9FQbEoDaW^vP&dRV5S(p{l9`m;vN?o5dp#&n!CkcX#vq zn$;ZJw25Fan3Oi!qxT_pUrwZTv}>9+VoeA^&&iXNuUbW}&}cn#Mwdpf$MVt@AXdWI zwRr7(e_SQIwu;J^X3+jiOH$2~ttCVt5h79ejm8j?C4=0l708{p=1`$fC0HMA7{Rqj`sFZy+RLdT?7(I9o_S~e#P9Q^gk&rN20aA&_wL2l(SeGDDn-0N z1vE**-*ca`72`mtp!R_p!dRL{wbRP!h6ZwTvKi7eR6s>S6B4on8FS|{#pN2ItU*s} z|H;FYfAS*gSkQdd2u?ynAV4a`Xc>e7ABfHfh`|8!w$4NwG!mAg8O8u&kTK7xbRO#B zqp!Y9E`X}4!<${p*U-OlJJTDBQ7R^YuLy(*PyqoUD-3!fV*nwc7|bYRB#cNPWH1VV z=@!?&xz5Ks?_ytEKYYHf31VB#!iBWiY#i9Uh57S;CS!>m9_N5&1v&b&7ME0?bX9=$ z0pa`iIlS#JzHiH6_PWi?o;_RlUKkR^fk{)R^1`mqIk;m7wZ9#wd_z`*LC^w{te@G% z5M%{;cv)k2V;>!kI_kHsp|Y|vVI(vp$^k+M#%5))WZO1=Ixv~Gji0e#l^09lEeHaV z@^3XE1Q3R_bgq1x#=iYVvKOr8t))xJ%gamZhK5w!aPG2YToQubD+h5bmcSnXSsuOu zkwFLy#hf}ih_ikrixw@yWHRXnvr*kp&zJM>c9Slr_O{0Q3?my}TO!A_3t z*~5+X`{{jTrN6(QqN1YVGm{FC$W}QO75u&KL(JXXteHNY?Ck7>1*q9#;g$94Ip11M zxz~%s;UHO@iAO+66bB|&RWY%uYSc<6I-gckGiot9;u40#VJXqGD)t*4>&;_@iKi1n zpePEGBoPXQMk3&LyDz%k?kUrzO(PHpjQTc6W;(q+Ns=&|%``PNF)%RjD3Uy49;bjk zJw1K9&1S0>Lde8K`ecvJRGTcz1cO1jtE=mQs;WnTuYf!8r(^?8!1@edHq)$ literal 0 HcmV?d00001 diff --git a/resources/project_export.png b/resources/project_export.png new file mode 100644 index 0000000000000000000000000000000000000000..e23861bf7086f527d38bdd29bc5e9e874d636d5f GIT binary patch literal 1000 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 literal 0 HcmV?d00001 diff --git a/resources/project_new.png b/resources/project_new.png new file mode 100644 index 0000000000000000000000000000000000000000..ba32d712d4aa176670c0b26eefc7c24a98fbe513 GIT binary patch literal 1144 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 B8h-!) literal 0 HcmV?d00001 diff --git a/resources/record.png b/resources/record.png new file mode 100644 index 0000000000000000000000000000000000000000..367a6dad2adaad131283c0c3061f88474626b907 GIT binary patch literal 1630 zcmV-k2BGk36s5v$e;K_5gEQE-D&1OF0IzIm{znlscDjSCd({IX68FS+%L&wI%B4KnZtbF$GzwL&$(w6s$4I|5x@X( zpaHNYTM9r5NCV;KG+GK@sX{P-1KNNNU|mG=PB{RXBZp##7#z~{g|fg`~5J?WDGu+^Kp9_UiI1=s*|15IrkE)Kf4 zJY?_|E;=u||M{oGkzL>Af$_*1y9C|^`hd(`Wv=A}0Bh}fVUeEpcA-B;Z9&x z%lh-0E_mu$Q@3O(hutjVK;?0)!xF!}4x<3PW_R^S8R(5gT zfwQ|Gf5Nt3c7;^s%39_b82NgK?tkL35ZDjA0K6%Xxj6#im~)~U5xRhlz^eG^UABJN za>*qVl?4GBDk3tc$~*%p^~;t?(-~bR^LyXQi1!asq)g4S`M41TRs;7b+y^XL|NN__ zvYO?-movkh@%C=HI|RlAUIJcL`16K|$N~6?&Q<6T7`FG;(&*q43pZJ4F<=ak z>;DTEi^0?sVKz;}+HN{-yOY|*OAu>M@B|YC)Gl3)i^a+8{+>eOIGRmUNE~Nk=MH2t zi}fZD3u>X)!e7QggN1bt+8r1-6B@|`g@ME9XaYu)G+%N#HEr$Q7b-=gF~rt(Q{TA; z9ZjIg1cf90kW4VcH5B3!c$ALt$;tq_nQXw_ZJWWpGti{yTSCs+97-~8e6AlVuCbO6Y z(pVKbfhiO?silg|O;@$GB7nJ2R>Gk{G@C(U@r5~tA^F3HaA9>d!#JVfgp?Cd_1XxW6pq|R?N!kJb$10iMvG2*k=3<(N<4-oF# zOE^A;U3Ds&%c0o}h2Qp*edbA~-gpHW9>fT5R(PP8a_*@>?sqhT&X={?ai+X{4bPBtG^MK}m1rUbt$H93i zL#l`mU?P&ys~(Kcx{H#KYT<~$I|8QxR{_l$CEnuNR<8Q9KqyCRLZCvd+6B(RZcrcj-u_6vo zgUIq|D%ym|dQ?{e?a^pfymG)u>1c-P5nx&d(*JEZ)z0SsHJr+!vHv$67Xdk`@mvm# cRaFlE0q_AE#r!)%qW}N^07*qoM6N<$f(`QK@&Et; literal 0 HcmV?d00001 diff --git a/resources/reload.png b/resources/reload.png new file mode 100644 index 0000000000000000000000000000000000000000..d65b6d59be6bc15b1771ccf0ab984fbe5c90c329 GIT binary patch literal 1156 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 Hu0mjf2z7lq literal 0 HcmV?d00001 diff --git a/resources/sample_track.png b/resources/sample_track.png new file mode 100644 index 0000000000000000000000000000000000000000..c8eeee88194ee2197a7ceeb37442063a5635f1f9 GIT binary patch literal 271 zcmeAS@N?&q;$mQ6;PUiv2?EjzAk4u8B>zafT@0i+3p^r=f%5-BnDI;3rcR(Bdx@v7 zEBjR@5gs<-4@s+60EO0jx;TbZ+vk+0c+!{t$XVSL~GZS1{?(b1X_f*8R=Op02;V{C%1yx@_1Uzl$YaBd4`Wlngy#3K8 zFM7-i`nJ11==t_KS;;c+-2|6dljpDQT0Swov?IB3{$JmP_J2aYJZ8=5uwu#G`rZQQ ObOujXKbLh*2~7YTO>2Mv literal 0 HcmV?d00001 diff --git a/resources/saw_wave_active.png b/resources/saw_wave_active.png new file mode 100644 index 0000000000000000000000000000000000000000..a48c35f6eb7a5ae4139f8fabff83f26c3fb3810f GIT binary patch literal 517 zcmV+g0{Z=lP)lwoICaa0013yMObu0Z*6U5Zgc=ca%Ew3Wn>_C zX>@2HRA^-&M@dak?_?!z0004eNklngBdM2ryynk@_&%FSYRfT~w2WC$4 z*xql~$LouyMRt~zPs&QGT>*`P1I!G`wRm~4c(VWwKOOo@Up=lwbVo(SFd6FQNxMGY zR4>Y#Uk_C zX>@2HRA^-&M@dak?_?!z0004rNklsmiUs2|0i^F@L}Ak=Bmd)AVz~JIaDgRHYo4C?&0meH$7y{Rp&5sm>~&xW%Hw7?#ww?aJzEsZ`>e=k)gXZG0cw#iCUxET^xy?q(Q%+Szd&XEvMV z^v~TL2w=njBIbE+y20Eh^l=SNZ0YPA4#yWJ#7!Z6g@ zc6ayl_mh)f)0?aD_4|(>h=|ORYVG^}m;JqiuLl5*kB)wxoe3cs zV2r_YwgGH!ZLhsqi=wF4>rJQA%kfwW2?h|1(L|&Q<@E5|p%9|e>74#Jl~RJy^L0oH zIiJs^6aXv$ob!Iae{p^xB&R&gV#UX&r{QoonM^8`3IkB9)n0snMx$Xb%4^=`KLp#& U$qE&|v;Y7A07*qoM6N<$g8$3$4FCWD literal 0 HcmV?d00001 diff --git a/resources/setup_audio.png b/resources/setup_audio.png new file mode 100644 index 0000000000000000000000000000000000000000..705c7fd5f2782f42aea4d73cb3384f4106e7fd52 GIT binary patch literal 6587 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 U0dKXyKN3L3(l*hm(r}6WA4|P-7XSbN literal 0 HcmV?d00001 diff --git a/resources/setup_general.png b/resources/setup_general.png new file mode 100644 index 0000000000000000000000000000000000000000..270de5614b43d22a6211ec1e664107e9610b52fa GIT binary patch literal 6440 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 CJu@u; literal 0 HcmV?d00001 diff --git a/resources/setup_midi.png b/resources/setup_midi.png new file mode 100644 index 0000000000000000000000000000000000000000..3074404440f08700b0a1d99925378c6350a47fc9 GIT binary patch literal 3743 zcmZ8kc{tQv^dC#p81vXeMTW#ATe7{fH)5<~nULL!RL0t%Fvk8`OZHK+B>NsRvYTWl zW5h5>=xuBvvj4vC@2}tUyWi*D``r6|o_o$cpL0LwoOraUKJ*e41Oh=(21pBF*8Zr5cxD?woM{1cRk-uX#3cBqUR z>GKy|5Qswth19VMo>?c_d0p;%))(G7d#&ZYK>{^{N-oFt7g*iSEPfRSVe@B!_DFJS z%38ejG$p{bkH-qHGqaXPfWi!D`}|NY3gKw1o6SGW6C|+O8_)Qv?K-9Ee{H&+oPp~= z^XVlR`EQ2F2i+O)H~o@twk#r8xk6jq7z%GcSkan?Uh>S30RP`%;Y>LC%@HABbwtmo z_)8rcAs(YE+PzroB#zR3Dc-#(4=O-II-5gkr3X7^nxNXn7G3qy(u0K-Wt0mJQ{%X~ z1hQ*K$jR@i<}*167d`y!L@Osd`_B^a|FAT$UOLUNIF>_5b?l{q_zU-owwWhPhA)wD zuB}8w0-lL0dQ*g2&bCI!WvoG(S)tTxRa7Ae6 zdyh5Va#Gt-+i}8N=4*o-^5Hf8l862sJmRx+y@!?B)^GzSCz1WldHpF|$`y?S zag_Sm@lNRJ!D_Uw=%>EE>Dk$}&~NdQht*CUj+NKowE?@EZTpUoo;^F1V~=5ub#bOC zo0ymwo0=*pDd|b16|}VET-8s3qjdcL%F6pNq>N)!Oo(9jFhl5xdk{}yAZjKhuA&GuaIuKrU3d~o~b=3doR3YI^=G1ziDG;Kv!va?tAkHOKP z77M1Ea7~CtN>o(Ty3N<77?<2^({r{jzVap=)^*9lyzyjWSHDbHMQ98U*8Gq0(`OD|Q5qY9f3xY~x} z@L%|g8EPp@zHZVwU4B2VlWz{X4`A6O)^1AivGFCQ8@fk!!K%FLM zVWC}0+I>9-;Rk`YxZ!i{F0J5PsRJq`BdhvgrH4o2qX9 zZGDwgZ7G|@uzDko!sqXTH@Uf!?=sRCOhw;Yo~y2@IbzxK$}26uaHk1-6~dfQn2$)P zL5F4W>k0L_xVy)Y$$EKVd(%CgQ?2XF-9TH)qs-Fr@VYiq$;!ut_W_xD=9RZu*2Ft+ zBovNODBq_ZpYN!vi*UEaSp=6{q9{Tq6i<{Rx57HBGA8MJ>|7jiC~=;l;o&+GX~yX? zgaOIU%e#_<6#ICxN5iiIq?wbOdk2G=q0vqRU{L5HN!GgB{@2wiciZ&AWwl}z1qEF@ zyUTCF_HsmEI|u5&f?t)q!4t!d_I{BCoNDXqb$xu~7JUV6Gyi(JEk5m)$(X9)jYo3n zahps)SeZ{3r2w4d=}a zi$R!c+V+TNm9BLLbvADs8XE3>;D*Mi;*V8(dU`;msT!NJ@s;iKTYdOIu-nyUEMQ$f)mq{X5;TI4op;V~$bg{D!c&xM)>p z14~X$UR33Xqk5jE$AyT;ObRo-%fOQZ+Ax^(Xaxd3kC1PyOAQ z`r6tBUi9NE@p$0Dm6v;Yc~M^T1c8S<6%)$KXFvEAjZlHO*4xWkUhaQ#h_jz^_wq_g zPKJ_5BnyK5W!9LovNEkoD=GEUKTCl5mj|=VF&7Ab_oW<+^DR`=v=`b)E2ygepapL5 z(NV@246Fy9YVcVc{vEwx#8%dWPXRg%Z7_ z8oJ#t3EVh5JbZ2qu$Lc$Dr1FN-dgNwl~DNjt>uhLz4y8~_?D)U(sBqv9EGFJQK|gp z^CMw`1V~pyIh|s+*}Zr6Nnv4Rn7Fm zii(Pn4Ve!(O3jHGUkvxsTfq?`w zMQv&Rzt2ujUTK7678hSTb5>GUA1XG=dZy0IN+>8O$U6WUr{*6*N!~LkVR_lz&DJ9% zB$wqp2L&~7S;gCTYe9!6KH6t+*&h03-O1FJn`}h3f~5uHqW)#Zxlos102JjI5`dE9>T)Hoc*g* zDmBin5e_+Ro9Vnq`*honRBiVl%RR@n?#CH*2P!9LOQYf(T)0FKYcxeO2|qojmi319fPH{&HxGT z>!yA6#}Di#6h-R$>y0JSL;*!ozfdT>V`FGne}9WikV|0TJ@tSU*c<%e^%Ao;cPA9R zXdYN>GE%JT=g&uTQ7X#H$5C-{#u7_L6e25YLZNy(r;VdywUHIb%iH@9CBT_s7!#^_ zHnw>yXXwu6s7J%+`ZY;s4d&I=)sr3vdBN|4avk3@tQ{ADhs6VD*VfKlE|Tjw-VRSn zNohj8L@MOGEiNW&c?LI7DU{vMMJpp~i30zg(+NhfuVHZ=qkD)}ERB_!Nzr6yVqJ?O zr^AKF8mz3WgeQmz$T>4C{TvWNF zdK!qKrOs{tYIF+5f3YU&!;mlElHAdD`wC`}Z~(|mcYt_9`@ICg0E__tcqS$$-k6w} z2m&tU6Or4Qj@IChjS#QxFueQrK95qkzH6Ngl8>BUh^VuXj$KkUlow12HlI-(=KoJ% zfV?1|$%NM)^Q1SumX_aU3V@`+1@X*lvyyV51$H%IC*)zq6Vi5>7+F4Obv1)VNF7}@ z$w!+wWAEM4dfHi@sqo_5N`(97y6f+M1X;dy<~`D|(NHv)RJLf8-9R*satFB)duML- znMKBnYKvimcXkj{F~;PCnQG literal 0 HcmV?d00001 diff --git a/resources/sin_wave_active.png b/resources/sin_wave_active.png new file mode 100644 index 0000000000000000000000000000000000000000..81794ee6c2ebf92e8bffbd6a7e07c27f1491b836 GIT binary patch literal 543 zcmV+)0^t3LP)_C zX>@2HRA^-&M@dak?_?!z0004&Nkl}4sA99=-HqAz_EHdoM1h!QnxoNZ?uGxB z0K?(XTr>0TJP3mJm$qG65m8lD_1-U+%QQ{1EDM3$37p>M>2u$x!JCE0kSA{_HN^`O^@v*=BwhjoYiU_UNS_Kd%)k7=a z&;)yAQPOT&RF~d6=X_E9@wJ}-0kARKum_fNfl}rHDrfi3s$*+DMJ1ZvTudK*>^<*= zK<#x3@HkQAJ9cbP(r#`?RTZ-9nFe~XfOj7GGqu^bji-(IgIRPKAv)WcH=kTxp?-xb z0EwEhLCN|H5ET0^TA`%}MFr7c_cL)=->oMP6NJWY!^DQ(@6^APP hIv5Q8^(Hqi{3nYg$U}4e5Pkpv002ovPDHLkV1nzR=cxby literal 0 HcmV?d00001 diff --git a/resources/sin_wave_inactive.png b/resources/sin_wave_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..abccbbcfb5863a50e46a7981368550b8381549ad GIT binary patch literal 557 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 literal 0 HcmV?d00001 diff --git a/resources/step_btn_off.png b/resources/step_btn_off.png new file mode 100644 index 0000000000000000000000000000000000000000..ef91a2c4ebedf82ffaa9f3b79bee8bdaf7759099 GIT binary patch literal 389 zcmeAS@N?&q;$mQ6;PUiv2?EkQKrFz)1|*dd=dlARwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ETG#Kt9L`YA?g8Bj>J#5JPCIX^cyHLrxhxhOTUBsE2$ zJhLQ2AtWPJ!QIn0;C+f}9#G*cPZ!6Kid&^Yf$N$LcsAcuvEY%vc+b^BNW4b)Ql9a1 z-S^75njNkoT^+KwLwByz&dR+lwqwQd^Ud@3^D{iS$9(n;SO4?(o6nt0jZB)C&cNW< zpedBF?M~)N6!jAuBRIaoX%wcOV|xmWs&K@*3|OX-744~rR91{Qc8o+ywKDVJ-l zv^MmvTg7fJh1SKvmhBo`b&?x?c3Y$coIT*T+woKShW)CQ-J$F}Y4Qw}K?T?AbocCd za4tWv;`=iF&l3(9+$re|-(^U8FM cl^Omp@2shwYg`y~66k9NPgg&ebxsLQ0PmlYA^-pY literal 0 HcmV?d00001 diff --git a/resources/step_btn_off_light.png b/resources/step_btn_off_light.png new file mode 100644 index 0000000000000000000000000000000000000000..d360521cb78c069a74b8b8d858c81017da5b0f50 GIT binary patch literal 336 zcmeAS@N?&q;$mQ6;PUiv2?EkQKrFz)1|*dd=dlARwj^(N7l!{JxM1({$v_d#0*}aI z1_o|H5N51TiJu4*WH0gbb!ETGEG=xPY;yG3Y@m>AiEBiObAE1aYF-J0b5UwyNotBh zd1gt5LP$ocg1e`0!21-{JfOm@o-U3d6}NhW4ssn);Mu%XdXaGGEB(da?zUE0o1FO0 z5wOC+wws+v`naMCv#~JSjqh_)|7UjI-j>KDs(9At^{z+qGRFn^-u2agR8rt@wS5rd z;CA)Yo(ZOWCuNnI9j{(jYKQ!GSw>n)X9 z9TU*Am*LaH36t*UD(-%-`b+2L!&g=Irw*rk78|U8_WO*_=}jhr*%MxUwLLGfZ~Mi% bz90O@1!MXr*NI2~J;31U>gTe~DWM4f>*9b< literal 0 HcmV?d00001 diff --git a/resources/step_btn_on.png b/resources/step_btn_on.png new file mode 100644 index 0000000000000000000000000000000000000000..f0101d0537981a179e66fdfadab51290d643a102 GIT binary patch literal 302 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 P00000NkvXXu0mjf_`6D0 literal 0 HcmV?d00001 diff --git a/resources/text_center.png b/resources/text_center.png new file mode 100644 index 0000000000000000000000000000000000000000..cce40807235daf2d392c5ed3f98e7721a598ed6d GIT binary patch literal 177 zcmeAS@N?&q;$mQ6;PUiv2?EjrAk4@NBvoy5m4FmWx}&cn1H;CC?mvmFKt5Z5Pl)UP z|Nnu^&_kE&fD~g%kYBKanbpj1AWo9Ey9+}HtE>l*!&%@FSP)MzCV_|S*E^l&Yo9;Xs0006eNkl4f26otQgXJ;`c*$QEC3M}ci5eYVeXlJ1o+Qr{sp_Sk-@E7h1cPwUSvhg+V@gDb_bI(1`Br)3WTu<9Px7dD2q(uotK?DLK06`Fd zNHC=z%klVS@4=r>_H%b>c)3&V5v`5g!RZ*0s#u_)=1c&U8T{T6p-{}Z)w(oSJR#C< zEp1skzXlSR0U(JBOQH;c$?;c9_wULnp1Fzp*b1fdYe+(Mp~Sn*O*S?*n)*D?nM@`y zH=vxm?F#9=7J<}Si}pX8fUWIq27^IWoMn_DkhoVQ-i1i&Q)57CnCo?La6rG`C&oyW zNZj!xA#ulK5TI2XrWs}filX53^pusA70RL@ac{fi~X!yz`kt9@{H6k{E8S_)(; zPOJP$M2WI2dAz=kC&_uwGe9y!`7uO0^H|th{(koH=mV21BhRu65faT2 literal 0 HcmV?d00001 diff --git a/resources/text_left.png b/resources/text_left.png new file mode 100644 index 0000000000000000000000000000000000000000..89788e76e82c5be2e2dcd27a4b53e691ebf2673f GIT binary patch literal 176 zcmeAS@N?&q;$mQ6;PUiv2?EjrAk4@NBvoy5m4FmWx}&cn1H;CC?mvmFKt5Z5Pl)UP z|Nnu^&_kE&fD~g%kYBKanbpj1AWo9Ey9+}HtE>l*!&%@FSl*!&%@FSMzCV_|S*E^l&Yo9;Xs0007KNkloDC2wZFKi-I$vA5kfT&cPTYMoQ&CBzwcAj9y0 z!*GBzFdP~0lGf?7!8?aLvbWtcQjPs4o(ggup^KsjtT??{nOk-Qe?AFbPLr05hEc`M z{P_Hb?kg?ta8v)dz3rbGnV*l5 z-ojGLeV$STfA)vl>t~-p!M7MEgyZC|Tixt*xuMz2VJw3MY~bv%!$cFiRmhoWYoDVkZo(Q{ zz3<(q7#Xagb&fIndGGgG=GxqZ2ZQH>teep{1fNdOHVHcBZLVmy!i))UQtdiBA_oKB z`(#&dfa5B+6GjWrw`rb~&)XP8s=?PQ$9m8iFXr3(U;WSZ1*`rr&Pt_D0RR9107*qo IM6N<$f-kt|^8f$< literal 0 HcmV?d00001 diff --git a/resources/toolbar_bg.png b/resources/toolbar_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e8972d15aed5a7582a013d679d2aa43b62bb1e GIT binary patch literal 156 zcmeAS@N?&q;$mQ6;PUiv2?ElLKy1Xp1SExKs@(-roCO|{#S9GG!XV7ZFl&wkP>{XE z)7O>#DjScmAp4A*WBY(YHl8kyAr-fB`*w0M81OI&{ytx`q%*lI#O>Kd%}(K&N6xsa z71`~dp1kMkZ2kD{-91&=R(nlv*UQb?T6ux7gsF7Pp-anV0L@|WboFyt=akR{0ArCb A1poj5 literal 0 HcmV?d00001 diff --git a/resources/triangle_wave_active.png b/resources/triangle_wave_active.png new file mode 100644 index 0000000000000000000000000000000000000000..d2b23e7f96871fa03717e73aa8bfc31f038b5f01 GIT binary patch literal 555 zcmV+`0@VG9P)_C zX>@2HRA^-&M@dak?_?!z0004^NklhVHWvf103Ba3Q+Tr61@*g=k$^s8VSplXTk5beMbRx~NM*&o6im&w>BO0i)5V z(P)Gay!XyIz&U4TW~QoTSuPffJkLEKBBC0r0@X@&x^W5&PS%sctE!5K2;hN>#f7LO zKG8;fwEbVqkr+E(%Ha()3Q+JLopwG?QOG zT14sH)P!)HSV^zf^MKZRe&c+yKj|KHTF+L=ox$q!mM-aJf0F$0&9_Y;y0R`d3yg3W z+_}{vwW^)D@HJrMn?uqs literal 0 HcmV?d00001 diff --git a/resources/triangle_wave_inactive.png b/resources/triangle_wave_inactive.png new file mode 100644 index 0000000000000000000000000000000000000000..c84f8b2d6a0e96372d1169333d634fbc7953ff9e GIT binary patch literal 580 zcmV-K0=xZ*P)_C zX>@2HRA^-&M@dak?_?!z0005INkl8l207gIKA8HdJ21GMMnmS66U}HwE zcm%&f+Pnu3V~GYa025*&Rf7ZyN=UsRJ0Vg`CEE}K*S_}cK-8&Hd)D)9=L7y54nVWn zEEEdaY*vyap6CC#IF7?GT(8%TQy;?cD?N0lw#c^U=)}D}jJ}&@>llb)Gsjll+SKoTQey|Dv^!q(YlyqIU zZR_U83V;*C&-0L5ZSmSq4ufXlC!^?JS4YK=xCLP)|` zk}wA5y54pdG#Uyi{myY-cmRfB7>3d5bT;b>OBiDbV+mu7kiBKuX(@g2<)Yi|3IHgj z^ZC8+`QX^0AHV<(jQPGlf0!Q<@Bo&}$;Q4Iq>2E0U&@-3}$BMWO#o5FawLE zG6RpWxXSO(?;GB~|DX;ukOdeNzwX|Dbo0c?6Z_u0e14dNg_(g0sQc@${|tZs{Rg|s zPEmm2>dS9ng8>4F1wCAU|Mpn>KHq$NK%v9&sMl z*CHZ343hjD45Hls81~)y%JAmfPq2P&Hf9D;VgLbv0D>F*=l4H`|9`)Uu&{9({(Esn zf${Tw8DTXu$v^CZk~}QTl0If~e&YXl)c*ed_wmQKUoT&L_u2Aty*t!%{^^%F00D%QD*gfS zF9ud22_RMgvOyV)85CNO1P-SEg4HlE04@9Xnc>IVHw-d*feeOz7QDbz%**ieF~j4R z!8I2bpFRo@Ku88N!qNv=Bg7D9Xs(AD{ue6u2bhBX0+S~T!{0Bj8Qxsk!0_iEBZGvR zA%mv2ssszeX>EW2V!FkV1e`PE5qdJhdwSo^m^CpZ-1r&g%5)i0t67)U}k1!Ak7Lj z2)UU4kLLZqPzq8{q1i7P~sU(B|rdyP5ANwSd#zz3}o>_ zh5kVb09o`KO8b%9|J2JC#XDS_y^3KKYn~=`11Z8!-qG|8SdSE^#8!w zH?LNo__*oy*Wax`vmPQF3=lwIgIR%L_2=(@c2-7aGf{3PTLCUcc|I;ieonUk9Kgby z3RI!|{>%9P$FKjtzItTzFaVV5jgR3=Hc}n6q}h1o9a>QWZRN6Vp?JQWH}u3s0unFfcI2c)B=-RNP8hvuc&q+lLPm&m1`L zL7qe60;96BGS~dh&d$c(-rkP~8kz4$M@4ZlFm)sz?~}Fv_n7hjzrVl#e}8}ffBnBd zKmWhJzW%>`{XdrO_SJDFS#-!R}2W=@ooliN@= z!NP&T(DCbG(*))hj}-D9uKs7|P!P*uQ`pTS&Xvr}BEY>#*MND#+`gWPPZQV}fP907 z2ip#bDEyY6%XLV>)_|Fjse|=yO3tfz&5C=^Z!*GNpvEX4VB}K^CA}S?_u- zkzx~Y(AnWMQ-X(yfsy4qlSYc;%Xu>*9QrtcE=){IOG`;iOnmU@k&>2Tqe)RwQIp{X zCpDmyW%;{1<=oudefRg*8z*=R-#Em+L~O=P2|J*FxfL2DcJACMymZNuOAVjaEI89X vZ|cR*j2=84k#YO${(k-Y`}<@DCLV?*sV=LkY}H=?BZI-y)z4*}Q$iB}p;fX? literal 0 HcmV?d00001 diff --git a/resources/usr_wave_active.png b/resources/usr_wave_active.png new file mode 100644 index 0000000000000000000000000000000000000000..1c574e5f576634d7beb863bc8cde553a319d8dc8 GIT binary patch literal 518 zcmV+h0{Q)kP)_C zX>@2HRA^-&M@dak?_?!z0004fNklvQD~W}%;t6a`AZ9d?g}^Sm&NAbU```asEJCp2R<}4g`R+Xj zZao6D+wC}x_h0O%Pt##C1TYqUE^K_ie~|o!0W_<=(?y*2sM5tKX}&ueG8m!Z3`0L4OcN z;iGp=7mPc048&hzRsizT{Oqi&I-Z@aN5HQv!$3^HO#aN3>GKqb22uDa=w)3IE~VZn z0+>s*^RjckdG2ez`r}UX)nn`9Q3D_X)T?2bl=ny7tu8RLGc*5oPF$ek1;ehSs(@jt ze)7VpZ&O;DpeEduUII`#tSmiSG6;SL>h<3NVmDj`;9~vatG-g%WGCJ zOf+(S+5^6-s)`vRSHWJh0(bXn&AsMiSM_C zX>@2HRA^-&M@dak?_?!z0004WNklvN4mPJvNB*}KWP17`rq6(1bc~KPIZrAfX5m8m3*=!Dn!)mo! zs(=eTJwN3|9!1gZ?WZ(NYPH&QItqfoaU8E+kK;JYvV-;U+hdxf@2{_&N~PT1ZnuLV z0KVLRy}SFo-|u|icY*J5jDk@Es^;XRJsORG&1UoA=4QM7VGO6IZ(P6@f&ujC=+%5a zpG+pc?>{`;UtC;f*~Wmv0YI!Mikjo&*R$CSST2`WSMQ{-YA|T&bpa6p6cw>0>w3N3 z?{fqHSPuZKR;zx$udw^M6Dz+DFscBRa%x%Qx8pDjKZfD?`I!~5wy#@CF@4o>U3$Qp00(qQO+^RO2?PuX66kg_BLDycXGugs zRCwC8m0wI-bri?H_aD8NTd=gSF&YXAX=N_WLP#887>QZHN*4(R#xgsCJnYT*;=AvV zYU0CQHBCmM7$-`qq(gM-GBpMUP4}m>jl1g~TDbJzE%#6F^NIavU4^p&h|B|Liqrbx{`W=>%m!&nUq>x+jO)j*SwgpQvy*yYxtL=V6 zx!vyf0f+z|{tJ}R3cP;CmHP1PDYpNWt~$Q=L_K-hQ^$84t)4U+!b?9tX#C?YeLm*a zX7$I4ySlpGak*S?E-WmZDVNKud$3D3y2MI$|KXMPuof zf@sQ$DnWbqoC=QPFflP<9~c<;YPa$ojkcxpB<^PjgjNXL7Buvq0FK#AxbnS%+esR7 zm4RUpwtdHKY;4RvGBPsD^ZZEwC@ainwn$;S42UHICQ8O}2ZK*<7`T3if&SO0wJJ9E z1#=vS$;rv~p`oELfESB9M5rPgkBSK(UtzGkreQTffT0M~*65IA9c9@F9cV#DM@Jin zhlj7EQmN~?f)+QMH1I3|hfRa0jezSI0nS8#uTdf7v^bRQ;DNv_%VKJ3s^jCaYac}J zr7p(=Db5->=%5jONI_Oqu=)mW{Vv5*nay`$Xx}~2=A9ou$16(k(xuN%PYYWuJxA+` z-lgAEYrev4WeRL)yQBp{(91sKE$u(SnM?+OK;TzF5J~``sw!TiK3_u?myvx`V+G(m z$gM~wllbZ8j~IH@2T>FuNz#o%q44(qx^5dA8S&NXU$00000NkvXX Hu0mjfzl6v6 literal 0 HcmV?d00001 diff --git a/resources/white_key.png b/resources/white_key.png new file mode 100644 index 0000000000000000000000000000000000000000..b2ae40a0157be0ad1481d7f92e8d7f874f5cad48 GIT binary patch literal 242 zcmeAS@N?&q;$mQ6;PUiv2?EhTY{|g}B>C^`+6ScAlDyqr82*Fcg1yTp14TFsJR*x3 z7`O#Nn6W-3ej-qiy~NYkmHi4cKc6=LwwJ+gfkLt+t`Q~9`MJ5Nc_j?aMX8A;sVNHO znI#ztAsML(?w-B@?^9IsfC|GsT^vIyZoNITkgLH!!0ll<%gsi^2pO@%T(L{oyo(g? zE)8e(=<>gt=3WO{!QkoY=d#Wzp$P!1*ijAu literal 0 HcmV?d00001 diff --git a/resources/white_key_pressed.png b/resources/white_key_pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..4832392f4235a2d1f1b41b7c2a12d5e892a68ff2 GIT binary patch literal 442 zcmV;r0Y(0aP)_C zX>@2HRA^-&M@dak?_?!z0003oNkl0Up-!!ST&<2e5Ry<-SrnkGKVIRik~bpWs|3jjnS;!}aO z#ckW5vA#)>r2>?Ef1)`{(6>rQL2R=H_;ao#3Y*Z`n=>WqU67w5Owhr^_u zI~_C5oy6MMwJb;M3cF=FbAHXfoz}BZhKVt8VES7cD%0pBW?F8GYMg7fmFC<~ zu8bK|huxYZO&cCPS+y+f{n$x6Df?mUTgru}H2?qr literal 0 HcmV?d00001 diff --git a/resources/white_noise_wave_active.png b/resources/white_noise_wave_active.png new file mode 100644 index 0000000000000000000000000000000000000000..253513564eedf2a6558bdaf550b8f28f08fa7b23 GIT binary patch literal 560 zcmV-00?+-4P)_C zX>@2HRA^-&M@dak?_?!z0004}Nkl86!J%0_&3x);6IQGSy)^YiCc?p!)OXIAI-G$GMRaAu8Ty_UGLo-&OLAr zoD1A|1b}|O9|VExx>8CZ1OOofGc&WbcD-Kbd9JmV0E{u3R@3%0%A-8aTfbXw>6UIa zOQ!KShGAn200I!bh%R>)NBc)ByE=Y)?4SAcgVNih!T!KM^(iH-H4zbj@%u4Pc>Zus zDa{_t_$Sxj>(QgpP0Oa&Bmnj6nipK$D(Ype@9D+;#WKh)p8YZkQx;|V zJT17`OZNl-ScqEiW*r6~i~zv2<%AUnFAto};Xj=fs1bjR>H4o$G5`la@;0F*g@aI( zg03j~5K%_qVVJy2Xi3dMxG`}6G(I=fh4M~3%Bc0G6&!}b3TUCexlZA@CmvlA00}?} z)$XfZyJO`O=?XmnG$ULw0Gg9G^xB`=+qOEL4#2g~Ri{+vE`aJzsm@(6S9gs%RgQML y-L3>+tp$K<$ghgQz`sB;*uriD^m@IUyvrXF;L)_ha40YU0000_C zX>@2HRA^-&M@dak?_?!z0004$Nkl2CKl}d$kp65B|ylPbMdr=esL;&@A zT~*a?w_7uQbUK|hO;4v&nx;>kCsq``?*~Efu#QF}UDp*wp)}Dn3c&e%2B2x$m3%N( zMo|Pn0x+FU!!T^MTG#8V6EL-^`rjFn~SI1Z)s&daj=$huf82qEjuI?J*oNtVlH9LKBG>Sggl z2-$4bpBLEv+WNkKbdPZy&*$@%Vmgl_#XtyoTdk5!j+x9$wgA`UpE*WB;PW~{kh8U_s16o zR-g-*fr^Qp7U+`yK>7;9KR#)& zAwY8({xCue0S5^%48OAjl>;UI{eoHuijuz&`aduffByvsI|D!fG5rHt%g({X@S0PF zVfO=8hR4h*46JN?5QBjlfBXbupkaT2Uik6lD}(sFHimzvjx&5?fEfJy4+Fz5ApPqv zga+vYnG6s>OpJ_-4Bvk+Fi0_dV6c&AW>Dn#!0`R&4~8GWK$hMzi@~rXfZ@;PO$_fI zzhO{bR>2U+JClL=X}5{@q(GT22ZFhmL+X0Vy<#o!ey!oX;4%J7<77M!#|2K|N_2y*dnC?DivfB*sp zDL;wPa379JwIJlS@u6+?=Sa9qE!@2iD46Lj?41a(T^j%zz z;r@lk41aF#Vqg#iI`PM529+2NhGgvt42p9T8NOV-$pA_O;Gp;eaq%Cp!T%Wm0*DEe zD*gbYUR*?wK}|uLK}u8*m>QTEetrGIpftaX!8y;Ifkll6sAD0}rYj80|JfO|?U@*Y z*cUKxZ<@sL`4=Qb{DHa}WC;vWN#v;f26^m`1PIuQ&zSmYQ!2Szinfx`M1ByBT73I+jy00Lzq zAm<;$ub57(V~_&hQf$WFWIZt_8dJA0&(c0*K}Re_-jr#=>yv1p~u_504mLeEi42#KQp$ zUj>GD;l*Gt{s+41(Cq6B5>r|kgdH0hmharl!2hp@A;mX=VbaMT4BX1vklY6}`1@Zb zaKQx-KrDZL{bu0(`;&p0PlDn4(+|L8#KFM${yx}C%)r0{DQ0J8X854z%CL0P3I<+h zdxj`GSB7P6!3@9z#gO6}%P{-Ma|UTuWrqJh{xSUd{t@hAfB<6Q<78!4;rY(M_wOZx zg&rrgI0WTSMg~Z!2x9+X_|GE2@YjC=1M|<%3>>U%4B1v27_u@_fhlSuLxzzx!|S(C z8GL;#7!F=!WjM^h$OsTXEbrevW1hSC0x%LjGVt^90bTePS`Pn%q;z0%1X~3!l>ae+ zk|;MfFN2I)97F%aa)z2}E(TpaTZXp&qyOK0W?}sQ>nkfj00EuFlnL~-K2QfVp + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "audio_alsa.h" + + +#ifdef ALSA_SUPPORT + +#include "endian_handling.h" +#include "buffer_allocator.h" +#include "config_mgr.h" +#include "lcd_spinbox.h" +#include "templates.h" + + + +audioALSA::audioALSA( Uint32 _sample_rate, bool & _success_ful ) : + audioDevice( _sample_rate, tLimit( configManager::inst()->value( + "audioalsa", "channels" ).toInt(), + DEFAULT_CHANNELS, SURROUND_CHANNELS ) ), + m_handle( NULL ), + m_hwParams( NULL ), + m_swParams( NULL ), + m_littleEndian( isLittleEndian() ) +{ + _success_ful = FALSE; + + int err; + + if( ( err = snd_pcm_open( &m_handle, +#ifdef QT4 + probeDevice().toAscii().constData(), +#else + probeDevice().ascii(), +#endif + SND_PCM_STREAM_PLAYBACK, + SND_PCM_NONBLOCK ) ) < 0 ) + { + printf( "Playback open error: %s\n", snd_strerror( err ) ); + return; + } + + snd_pcm_hw_params_alloca( &m_hwParams ); + snd_pcm_sw_params_alloca( &m_swParams ); + + if( ( err = setHWParams( _sample_rate, channels(), + SND_PCM_ACCESS_RW_INTERLEAVED ) ) < 0 ) + { + printf( "Setting of hwparams failed: %s\n", + snd_strerror( err ) ); + return; + } + if( ( err = setSWParams() ) < 0 ) + { + printf( "Setting of swparams failed: %s\n", + snd_strerror( err ) ); + return; + } + + _success_ful = TRUE; +} + + + + +audioALSA::~audioALSA() +{ + if( m_handle != NULL ) + { + snd_pcm_close( m_handle ); + } + // the following code doesn't work and leads to a crash... +/* if( m_hwParams != NULL ) + { + snd_pcm_hw_params_free( m_hwParams ); + } + + if( m_swParams != NULL ) + { + snd_pcm_sw_params_free( m_swParams ); + }*/ +} + + + + +QString audioALSA::probeDevice( void ) +{ + QString dev = configManager::inst()->value( "audioalsa", "device" ); + if( dev == "" ) + { + if( getenv( "AUDIODEV" ) != NULL ) + { + return( getenv( "AUDIODEV" ) ); + } + return( "default" ); + } + return( dev ); +} + + + + +int audioALSA::handleError( int _err ) +{ + if( _err == -EPIPE ) + { + // under-run + _err = snd_pcm_prepare( m_handle ); + if( _err < 0 ) + printf( "Can't recovery from underrun, prepare " + "failed: %s\n", snd_strerror( _err ) ); + return ( 0 ); + } + else if( _err == -ESTRPIPE ) + { + while( ( _err = snd_pcm_resume( m_handle ) ) == -EAGAIN ) + { + sleep( 1 ); // wait until the suspend flag + // is released + } + + if( _err < 0 ) + { + _err = snd_pcm_prepare( m_handle ); + if( _err < 0 ) + printf( "Can't recovery from suspend, prepare " + "failed: %s\n", snd_strerror( _err ) ); + } + return ( 0 ); + } + return( _err ); +} + + + + +void audioALSA::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, + float _master_output ) +{ + outputSampleType * outbuf = bufferAllocator::alloc( + _frames * channels() ); + bufferAllocator::autoCleaner<> ac( outbuf ); + + convertToS16( _ab, _frames, _master_output, outbuf, + m_littleEndian != isLittleEndian() ); + + Uint32 frame = 0; + + while( frame < _frames ) + { + int err = snd_pcm_writei( m_handle, outbuf, _frames ); + + if( err == -EAGAIN ) + { + continue; + } + + if( err < 0 ) + { + if( handleError( err ) < 0 ) + { + printf( "Write error: %s\n", + snd_strerror( err ) ); + return; + } + break; // skip this buffer + } + outbuf += err * channels(); + frame += err; + } +} + + + + +int audioALSA::setHWParams( Uint32 _sample_rate, Uint32 _channels, + snd_pcm_access_t _access ) +{ + int err, dir; + + // choose all parameters + if( ( err = snd_pcm_hw_params_any( m_handle, m_hwParams ) ) < 0 ) + { + printf( "Broken configuration for playback: no configurations " + "available: %s\n", snd_strerror( err ) ); + return( err ); + } + + // set the interleaved read/write format + if( ( err = snd_pcm_hw_params_set_access( m_handle, m_hwParams, + _access ) ) < 0 ) + { + printf( "Access type not available for playback: %s\n", + snd_strerror( err ) ); + return( err ); + } + + // set the sample format + if( ( snd_pcm_hw_params_set_format( m_handle, m_hwParams, + SND_PCM_FORMAT_S16_LE ) ) < 0 ) + { + if( ( snd_pcm_hw_params_set_format( m_handle, m_hwParams, + SND_PCM_FORMAT_S16_BE ) ) < 0 ) + { + printf( "Neither little- nor big-endian available for " + "playback: %s\n", snd_strerror( err ) ); + return( err ); + } + m_littleEndian = FALSE; + } + + // set the count of channels + if( ( err = snd_pcm_hw_params_set_channels( m_handle, m_hwParams, + _channels ) ) < 0 ) + { + printf( "Channel count (%i) not available for playbacks: %s\n" + "(Does your soundcard not support surround?)\n", + _channels, snd_strerror( err ) ); + return( err ); + } + + // set the sample rate + if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams, + _sample_rate, 0 ) ) < 0 ) + { + int q = 0; + if( sampleRate() == SAMPLE_RATES[1] ) + { + q = 1; + } + if( sampleRate() == 44100 || sampleRate() == 88200 ) + { + SAMPLE_RATES[0] = 48000; + SAMPLE_RATES[1] = 96000; + } + else + { + SAMPLE_RATES[0] = 44100; + SAMPLE_RATES[1] = 82000; + } + setSampleRate( SAMPLE_RATES[q] ); + if( ( err = snd_pcm_hw_params_set_rate( m_handle, m_hwParams, + SAMPLE_RATES[q], 0 ) ) < 0 ) + { + printf( "Could not set sample rate: %s\n", + snd_strerror( err ) ); + return( err ); + } + } + + m_periodSize = mixer::inst()->framesPerAudioBuffer(); + m_bufferSize = m_periodSize * 8; + dir = 0; + err = snd_pcm_hw_params_set_period_size_near( m_handle, m_hwParams, + &m_periodSize, &dir ); + if( err < 0 ) + { + printf( "Unable to set period size %lu for playback: %s\n", + m_periodSize, snd_strerror( err ) ); + return( err ); + } + dir = 0; + err = snd_pcm_hw_params_get_period_size( m_hwParams, &m_periodSize, + &dir ); + if( err < 0 ) + { + printf( "Unable to get period size for playback: %s\n", + snd_strerror( err ) ); + } + + dir = 0; + err = snd_pcm_hw_params_set_buffer_size_near( m_handle, m_hwParams, + &m_bufferSize ); + if( err < 0 ) + { + printf( "Unable to set buffer size %lu for playback: %s\n", + m_bufferSize, snd_strerror( err ) ); + return ( err ); + } + err = snd_pcm_hw_params_get_buffer_size( m_hwParams, &m_bufferSize ); + + if( 2 * m_periodSize > m_bufferSize ) + { + printf( "buffer to small, could not use\n" ); + return ( err ); + } + + + // write the parameters to device + err = snd_pcm_hw_params( m_handle, m_hwParams ); + if( err < 0 ) + { + printf( "Unable to set hw params for playback: %s\n", + snd_strerror( err ) ); + return ( err ); + } + + return ( 0 ); // all ok +} + + + + +int audioALSA::setSWParams( void ) +{ + int err; + + // get the current swparams + if( ( err = snd_pcm_sw_params_current( m_handle, m_swParams ) ) < 0 ) + { + printf( "Unable to determine current swparams for playback: %s" + "\n", snd_strerror( err ) ); + return( err ); + } + + // start the transfer when a period is full + if( ( err = snd_pcm_sw_params_set_start_threshold( m_handle, + m_swParams, m_periodSize ) ) < 0 ) + { + printf( "Unable to set start threshold mode for playback: %s\n", + snd_strerror( err ) ); + return( err ); + } + + // allow the transfer when at least m_periodSize samples can be + // processed + if( ( err = snd_pcm_sw_params_set_avail_min( m_handle, m_swParams, + m_periodSize ) ) < 0 ) + { + printf( "Unable to set avail min for playback: %s\n", + snd_strerror( err ) ); + return( err ); + } + + // align all transfers to 1 sample + if( ( err = snd_pcm_sw_params_set_xfer_align( m_handle, + m_swParams, 1 ) ) < 0 ) + { + printf( "Unable to set transfer align for playback: %s\n", + snd_strerror( err ) ); + return( err ); + } + + // write the parameters to the playback device + if( ( err = snd_pcm_sw_params( m_handle, m_swParams ) ) < 0 ) + { + printf( "Unable to set sw params for playback: %s\n", + snd_strerror( err ) ); + return( err ); + } + + return( 0 ); // all ok +} + + + + + +audioALSA::setupWidget::setupWidget( QWidget * _parent ) : + audioDevice::setupWidget( audioALSA::name(), _parent ) +{ + m_device = new QLineEdit( audioALSA::probeDevice(), this ); + m_device->setGeometry( 10, 20, 160, 20 ); + + QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); + dev_lbl->setGeometry( 10, 40, 160, 10 ); + + m_channels = new lcdSpinBox( DEFAULT_CHANNELS, SURROUND_CHANNELS, 1, + this ); + m_channels->setStep( 2 ); + m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setValue( configManager::inst()->value( "audioalsa", + "channels" ).toInt() ); + m_channels->move( 180, 20 ); + +} + + + + +audioALSA::setupWidget::~setupWidget() +{ + +} + + + + +void audioALSA::setupWidget::saveSettings( void ) +{ + configManager::inst()->setValue( "audioalsa", "device", + m_device->text() ); + configManager::inst()->setValue( "audioalsa", "channels", + QString::number( m_channels->value() ) ); +} + + +#endif + diff --git a/src/audio/audio_device.cpp b/src/audio/audio_device.cpp new file mode 100644 index 0000000000..2a1769d3cd --- /dev/null +++ b/src/audio/audio_device.cpp @@ -0,0 +1,290 @@ +/* + * audio_device.cpp - base-class for audio-devices used by LMMS-mixer + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + + +#include "audio_device.h" +#include "buffer_allocator.h" +#include "debug.h" + + + +audioDevice::audioDevice( Uint32 _sample_rate, Uint8 _channels ) : + m_sampleRate( _sample_rate ), + m_channels( _channels ) +{ +#ifdef HAVE_SAMPLERATE_H + int error; + if( ( m_srcState = src_new( +#ifdef HQ_SINC + SRC_SINC_BEST_QUALITY, +#else + SRC_SINC_FASTEST, +#endif + SURROUND_CHANNELS, &error ) ) == NULL ) + { + printf( "Error: src_new() failed in audio_device.cpp!\n" ); + } + m_srcData.end_of_input = 0; +#endif +} + + + + +audioDevice::~audioDevice() +{ +#ifdef HAVE_SAMPLERATE_H + src_delete( m_srcState ); +#endif + unlock(); +} + + + + +void audioDevice::writeBuffer( surroundSampleFrame * _ab, Uint32 _frames, + Uint32 _src_sample_rate, float _master_output ) +{ + // make sure, no other thread is accessing device + lock(); + // now were save to access the device + if( _src_sample_rate != m_sampleRate ) + { + surroundSampleFrame * temp = + bufferAllocator::alloc( + _frames * channels() ); + resample( _ab, _frames, temp, _src_sample_rate, m_sampleRate ); + writeBufferToDev( temp, _frames * m_sampleRate / + _src_sample_rate, _master_output ); + bufferAllocator::free( temp ); + } + else + { + writeBufferToDev( _ab, _frames, _master_output ); + } + // release lock + unlock(); +} + + +#ifndef HAVE_SAMPLERATE_H +const Uint8 LP_FILTER_TAPS = 24; +const float LP_FILTER_COEFFS[LP_FILTER_TAPS] = +{ + +0.000511851442, + -0.001446936402, + -0.005058312516, + -0.002347181570, + +0.011236146012, + +0.020351310667, + -0.000479735368, + -0.045333228189 + -0.055186434405, + +0.032962246498, + +0.202439670159, + +0.342350604673, + +0.342350604673, + +0.202439670159, + +0.032962246498, + -0.055186434405, + -0.045333228189 + -0.000479735368, + +0.020351310667, + +0.011236146012, + -0.002347181570, + -0.005058312516, + -0.001446936402, + +0.000511851442 +} ; +#endif + + +void FASTCALL audioDevice::resample( surroundSampleFrame * _src, Uint32 _frames, + surroundSampleFrame * _dst, + Uint32 _src_sr, Uint32 _dst_sr ) +{ +#ifdef HAVE_SAMPLERATE_H + if( m_srcState == NULL ) + { + return; + } + m_srcData.input_frames = _frames; + m_srcData.output_frames = _frames; + m_srcData.data_in = _src[0]; + m_srcData.data_out = _dst[0]; + m_srcData.src_ratio = (float) _dst_sr / _src_sr; + + int error; + if( ( error = src_process( m_srcState, &m_srcData ) ) ) + { + printf( "audioDevice::resample(): error while resampling: %s\n", + src_strerror( error ) ); + } +#else + if( _src_sr == 2*SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] ) + { + // we use a simple N-tap FIR-Filter with + // precalculated/-designed LP-Coeffs + static surroundSampleFrame lp_hist[LP_FILTER_TAPS] = + { +#ifndef DISABLE_SURROUND + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f }, + { 0.0f, 0.0f, 0.0f, 0.0f } +#else + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f }, + { 0.0f, 0.0f } +#endif + } ; + static Uint8 oldest = 0; + + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + lp_hist[oldest][chnl] = _src[frame][chnl]; + if( frame % 2==0 ) + { + _dst[frame/2][chnl] = 0.0f; + for( Uint8 tap = 0; + tap < LP_FILTER_TAPS; ++tap ) + { + _dst[frame / 2][chnl] += +LP_FILTER_COEFFS[tap] * lp_hist[( oldest + tap ) % LP_FILTER_TAPS][chnl]; + } + } + } + oldest = ( oldest + 1 ) % LP_FILTER_TAPS; + } + } + else if( _src_sr == SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] / 2 ) + { + printf( "No resampling for given sample-rates implemented!\n" + "Consider installing libsamplerate and recompile " + "LMMS!\n" ); + } +#endif +} + + + + +int FASTCALL audioDevice::convertToS16( surroundSampleFrame * _ab, + Uint32 _frames, float _master_output, + outputSampleType * _output_buffer, + bool _convert_endian ) +{ + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < channels(); ++chnl ) + { + ( _output_buffer + frame * channels() )[chnl] = + static_cast( + mixer::clip( _ab[frame][chnl] * + _master_output ) * + OUTPUT_SAMPLE_MULTIPLIER ); + } + } + if( _convert_endian ) + { + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < channels(); ++chnl ) + { + Sint8 * ptr = reinterpret_cast( + _output_buffer + + frame * channels() + + chnl ); + *(outputSampleType *)ptr = + ( ( outputSampleType )*ptr << 8 + ) | + ( ( outputSampleType ) * + ( ptr+1 ) ); + } + } + } + return( _frames * channels() * BYTES_PER_OUTPUT_SAMPLE ); +} + + + + +void FASTCALL audioDevice::clearS16Buffer( outputSampleType * _outbuf, + Uint32 _frames ) +{ +#ifdef LMMS_DEBUG + assert( _outbuf != NULL ); +#endif + memset( _outbuf, 0, _frames * channels() * BYTES_PER_OUTPUT_SAMPLE ); +} + diff --git a/src/audio/audio_file_device.cpp b/src/audio/audio_file_device.cpp new file mode 100644 index 0000000000..f13f050684 --- /dev/null +++ b/src/audio/audio_file_device.cpp @@ -0,0 +1,103 @@ +/* + * audio_file_device.cpp - base-class for audio-device-classes which write + * their output into a file + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + + +#include "audio_file_device.h" +#include "export_project_dialog.h" + + +audioFileDevice::audioFileDevice( Uint32 _sample_rate, Uint8 _channels, + const QString & _file, + bool _use_vbr, + Uint16 _nom_bitrate, + Uint16 _min_bitrate, + Uint16 _max_bitrate ) : + audioDevice( _sample_rate, _channels), + m_outputFile( _file ), + m_useVbr( _use_vbr ), + m_nomBitrate( _nom_bitrate ), + m_minBitrate( _min_bitrate ), + m_maxBitrate( _max_bitrate ) +{ +#ifdef QT4 + if( m_outputFile.open( QFile::WriteOnly | QFile::Truncate ) == FALSE ) +#else + if( m_outputFile.open( IO_WriteOnly | IO_Truncate ) == FALSE ) +#endif + { + QMessageBox::critical( NULL, + exportProjectDialog::tr( "Could not open file" ), + exportProjectDialog::tr( "Could not open file %1 " + "for writing.\nPlease make " + "sure you have write-" + "permission to the file and " + "the directory containing the " + "file and try again!" ).arg( + _file ), + QMessageBox::Ok, + QMessageBox::NoButton ); + } +} + + + + +audioFileDevice::~audioFileDevice() +{ + m_outputFile.close(); +} + + + + +int audioFileDevice::writeData( const void * _data, int _len ) +{ +#ifdef QT4 + return( m_outputFile.write( (const char *)_data, _len ) ); +#else + return( m_outputFile.writeBlock( (const char *)_data, _len ) ); +#endif +} + + + + +void audioFileDevice::seekToBegin( void ) +{ + m_outputFile.seek( 0 ); +} + diff --git a/src/audio/audio_file_ogg.cpp b/src/audio/audio_file_ogg.cpp new file mode 100644 index 0000000000..c8704309a6 --- /dev/null +++ b/src/audio/audio_file_ogg.cpp @@ -0,0 +1,267 @@ +/* + * audio_file_ogg.cpp - Audio-device which encodes wave-stream and writes it + * into an OGG-file. This is used for song-export. + * + * This file is based on encode.c from vorbis-tools-source, for more information + * see below. + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +/* OggEnc + ** + ** This program is distributed under the GNU General Public License, version 2. + ** A copy of this license is included with this source. + ** + ** Copyright 2000-2002, Michael Smith + ** + ** Portions from Vorbize, (c) Kenneth Arnold + ** and libvorbis examples, (c) Monty + **/ + + +#include + +#include "audio_file_ogg.h" + +#ifdef HAVE_VORBIS_CODEC_H + + +#include +#include + + +audioFileOgg::audioFileOgg( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful, const QString & _file, + bool _use_vbr, Uint16 _nom_bitrate, + Uint16 _min_bitrate, Uint16 _max_bitrate ) : + audioFileDevice( _sample_rate, _channels, _file, _use_vbr, + _nom_bitrate, _min_bitrate, _max_bitrate ) +{ + _success_ful = startEncoding(); +} + + + + +audioFileOgg::~audioFileOgg() +{ + finishEncoding(); +} + + + + +inline int audioFileOgg::writePage( void ) +{ + int written = writeData( m_og.header, m_og.header_len ); + written += writeData( m_og.body, m_og.body_len ); + return( written ); +} + + + + +bool audioFileOgg::startEncoding( void ) +{ + vorbis_comment vc; + char * comments = "Cool=This song was written with Linux " + "MultiMedia Studio"; + int comment_length = strlen( comments ); + + vc.user_comments = &comments; + vc.comment_lengths = &comment_length; + vc.comments = 1; + vc.vendor = NULL; + + m_channels = channels(); + // vbr enabled? + if( useVBR() == 0 ) + { + m_managed = -1; // we don't want vbr + m_minBitrate = nominalBitrate(); // min for vbr + m_maxBitrate = nominalBitrate(); // max for vbr + } + else + { + m_managed = 0; // let's use vbr + m_minBitrate = minBitrate(); // min for vbr + m_maxBitrate = maxBitrate(); // max for vbr + } + + m_bitrate = nominalBitrate(); // nominal bitrate + m_rate = sampleRate(); // default-samplerate + m_serialNo = 0; // track-num? + m_comments = &vc; // comments for ogg-file + + // Have vorbisenc choose a mode for us + vorbis_info_init( &m_vi ); + + if( vorbis_encode_setup_managed( &m_vi, m_channels, m_rate, + ( m_maxBitrate > 0 )? m_maxBitrate * 1000 : -1, + m_bitrate * 1000, + ( m_minBitrate > 0 )? m_minBitrate * 1000 : -1 ) ) + { + printf( "Mode initialization failed: invalid parameters for " + "bitrate\n" ); + vorbis_info_clear( &m_vi ); + return( FALSE ); + } + + if( m_managed && m_bitrate < 0 ) + { + vorbis_encode_ctl( &m_vi, OV_ECTL_RATEMANAGE_AVG, NULL ); + } + else if( !m_managed ) + { + // Turn off management entirely (if it was turned on). + vorbis_encode_ctl( &m_vi, OV_ECTL_RATEMANAGE_SET, NULL ); + } + + vorbis_encode_setup_init( &m_vi ); + + // Now, set up the analysis engine, stream encoder, and other + // preparation before the encoding begins. + vorbis_analysis_init( &m_vd, &m_vi ); + vorbis_block_init( &m_vd, &m_vb ); + + ogg_stream_init( &m_os, m_serialNo ); + + // Now, build the three header packets and send through to the stream + // output stage (but defer actual file output until the main encode + // loop) + + ogg_packet header_main; + ogg_packet header_comments; + ogg_packet header_codebooks; + int result; + + // Build the packets + vorbis_analysis_headerout( &m_vd, m_comments, &header_main, + &header_comments, &header_codebooks ); + + // And stream them out + ogg_stream_packetin( &m_os, &header_main ); + ogg_stream_packetin( &m_os, &header_comments ); + ogg_stream_packetin( &m_os, &header_codebooks ); + + while( ( result = ogg_stream_flush( &m_os, &m_og ) ) ) + { + if( !result ) + { + break; + } + int ret = writePage(); + if( ret != m_og.header_len + m_og.body_len ) + { + // clean up + finishEncoding(); + return( FALSE ); + } + } + + return( TRUE ); +} + + + + +void FASTCALL audioFileOgg::writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ) +{ + int eos = 0; + + float * * buffer = vorbis_analysis_buffer( &m_vd, _frames * + BYTES_PER_SAMPLE * + channels() ); + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < channels(); ++chnl ) + { + buffer[chnl][frame] = _ab[frame][chnl] * _master_output; + } + } + + vorbis_analysis_wrote( &m_vd, _frames ); + + // While we can get enough data from the library to analyse, + // one block at a time... + while( vorbis_analysis_blockout( &m_vd, &m_vb ) == 1 ) + { + // Do the main analysis, creating a packet + vorbis_analysis( &m_vb, NULL ); + vorbis_bitrate_addblock( &m_vb ); + + while( vorbis_bitrate_flushpacket( &m_vd, &m_op ) ) + { + // Add packet to bitstream + ogg_stream_packetin( &m_os, &m_op ); + + // If we've gone over a page boundary, we can do + // actual output, so do so (for however many pages + // are available) + while( !eos ) + { + int result = ogg_stream_pageout( &m_os, + &m_og ); + if( !result ) + { + break; + } + + int ret = writePage(); + if( ret != m_og.header_len + + m_og.body_len ) + { + printf( "failed writing to " + "outstream\n" ); + return; + } + + if( ogg_page_eos( &m_og ) ) + { + eos = 1; + } + } + } + } +} + + + + +void audioFileOgg::finishEncoding( void ) +{ + // just for flushing buffers... + writeBufferToDev( NULL, 0, 0.0f ); + + // clean up + ogg_stream_clear( &m_os ); + + vorbis_block_clear( &m_vb ); + vorbis_dsp_clear( &m_vd ); + vorbis_info_clear( &m_vi ); +} + + +#endif + diff --git a/src/audio/audio_file_wave.cpp b/src/audio/audio_file_wave.cpp new file mode 100644 index 0000000000..89bebb5461 --- /dev/null +++ b/src/audio/audio_file_wave.cpp @@ -0,0 +1,118 @@ +/* + * audio_file_wave.cpp - Audio-device which encodes wave-stream and writes it + * into a WAVE-file. This is used for song-export. + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + + +#include "audio_file_wave.h" +#include "endian_handling.h" +#include "buffer_allocator.h" + +#include + + +audioFileWave::audioFileWave( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful, const QString & _file, + bool _use_vbr, Uint16 _nom_bitrate, + Uint16 _min_bitrate, Uint16 _max_bitrate ) : + audioFileDevice( _sample_rate, _channels, _file, _use_vbr, + _nom_bitrate, _min_bitrate, _max_bitrate ) +{ + _success_ful = startEncoding(); +} + + + + +audioFileWave::~audioFileWave() +{ + finishEncoding(); +} + + + + +bool audioFileWave::startEncoding( void ) +{ + if( outputFileOpened() == FALSE ) + { + return( FALSE ); + } + + m_bytesWritten = 0; + + memcpy( m_waveFileHeader.riff_id, "RIFF", 4 ); + m_waveFileHeader.total_bytes = swap32IfBE( 0 ); + memcpy( m_waveFileHeader.wave_fmt_str, "WAVEfmt ", 8 ); + m_waveFileHeader.bitrate_1 = + m_waveFileHeader.bitrate_2 = + swap16IfBE( BYTES_PER_OUTPUT_SAMPLE * 8 ); + m_waveFileHeader.uncompressed = swap16IfBE( 1 ); + m_waveFileHeader.channels = swap16IfBE( channels() ); + m_waveFileHeader.sample_rate = swap32IfBE( sampleRate() ); + m_waveFileHeader.bytes_per_second = swap32IfBE( sampleRate() * + BYTES_PER_OUTPUT_SAMPLE * + channels() ); + m_waveFileHeader.block_alignment = swap16IfBE( BYTES_PER_OUTPUT_SAMPLE * + channels() ); + memcpy ( m_waveFileHeader.data_chunk_id, "data", 4 ); + m_waveFileHeader.data_bytes = swap32IfBE( 0 ); + + writeData( &m_waveFileHeader, sizeof( m_waveFileHeader ) ); + + return( TRUE ); +} + + + + +void FASTCALL audioFileWave::writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, + float _master_output ) +{ + outputSampleType * outbuf = bufferAllocator::alloc( + _frames * channels() ); + int bytes = convertToS16( _ab, _frames, _master_output, outbuf, + !isLittleEndian() ); + writeData( outbuf, bytes ); + + bufferAllocator::free( outbuf ); + + m_bytesWritten += bytes; +} + + + + +void audioFileWave::finishEncoding( void ) +{ + seekToBegin(); + + m_waveFileHeader.total_bytes = m_bytesWritten+36; + m_waveFileHeader.data_bytes = m_bytesWritten; + + // write header again, because total-bytes-field and data-bytes-field + // have to be updated... + writeData( &m_waveFileHeader, sizeof( m_waveFileHeader ) ); +} + diff --git a/src/audio/audio_jack.cpp b/src/audio/audio_jack.cpp new file mode 100644 index 0000000000..c5f3630ef1 --- /dev/null +++ b/src/audio/audio_jack.cpp @@ -0,0 +1,379 @@ +/* + * audio_jack.cpp - support for JACK-transport + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + + +#include "audio_jack.h" + +#ifdef HAVE_UNISTD_H +// for usleep +#include +#endif + + +#ifdef JACK_SUPPORT + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + + +#include "debug.h" +#include "templates.h" +#include "buffer_allocator.h" +#include "config_mgr.h" +#include "lcd_spinbox.h" + + +audioJACK::audioJACK( Uint32 _sample_rate, bool & _success_ful ) : + audioDevice( _sample_rate, tLimit( configManager::inst()->value( + "audiojack", "channels" ).toInt(), + DEFAULT_CHANNELS, SURROUND_CHANNELS ) ), + m_framesDoneInCurBuf( 0 ), + m_frameSync( 0 ), + m_jackBufSize( 0 ), + m_bufMutex() +{ + _success_ful = FALSE; + + QString client_name = configManager::inst()->value( "audiojack", + "clientname" ); + if( client_name == "" ) + { + client_name = "lmms"; + } + +#ifndef OLD_JACK + const char * server_name = NULL; + jack_status_t status; + m_client = jack_client_open( client_name. +#ifdef QT4 + toAscii().constData(), +#else + ascii(), +#endif + JackNullOption, &status, + server_name ); + if( m_client == NULL ) + { + printf( "jack_client_open() failed with status %d\n", status ); + if( status & JackServerFailed ) + { + printf( "Could not connect to JACK server.\n" ); + } + return; + } + if( status & JackNameNotUnique ) + { + printf( "there's already a client with name '%s', so unique " + "name '%s' was assigned\n", client_name. +#ifdef QT4 + toAscii().constData(), +#else + ascii(), +#endif + jack_get_client_name( m_client ) ); + } +#else + m_client = jack_client_new( client_name. +#ifdef QT4 + toAscii().constData() +#else + ascii() +#endif + ); +#endif + + // set process-callback + jack_set_process_callback( m_client, processCallback, this ); + + // we need to know about buffer-size changes to know how long to block + // in writeToDev()-method + jack_set_buffer_size_callback( m_client, bufSizeCallback, this ); + + // set shutdown-callback + //jack_on_shutdown( m_client, shutdown, this ); + + m_jackBufSize = jack_get_buffer_size( m_client ); + + + if( jack_get_sample_rate( m_client ) != sampleRate() ) + { + SAMPLE_RATES[0] = jack_get_sample_rate( m_client ); + SAMPLE_RATES[1] = 2 * SAMPLE_RATES[0]; + setSampleRate( SAMPLE_RATES[0] ); + } + + for( Uint8 ch = 0; ch < channels(); ++ch ) + { + QString name = QString( "master_out_" ) + + ( ( ch % 2 ) ? "R" : "L" ) + + QString::number( ch / 2 + 1 ); + m_outputPorts.push_back( jack_port_register( m_client, +#ifdef QT4 + name.toAscii().constData(), +#else + name.ascii(), +#endif + JACK_DEFAULT_AUDIO_TYPE, + JackPortIsOutput, 0 ) ); + if( m_outputPorts.back() == NULL ) + { + printf( "no more JACK-ports available!\n" ); + return; + } + } + + + if( jack_activate( m_client ) ) + { + printf( "cannot activate client\n" ); + return; + } + + const char * * ports = jack_get_ports( m_client, NULL, NULL, + JackPortIsPhysical | + JackPortIsInput ); + if( ports == NULL ) + { + printf( "no physical playback ports. you'll have to do " + "connections at your own!\n" ); + } + else + { + for( Uint8 ch = 0; ch < channels(); ++ch ) + { + if( jack_connect( m_client, jack_port_name( + m_outputPorts[ch] ), + ports[ch] ) ) + { + printf( "cannot connect output ports. you'll " + "have to do connections at your own!\n" + ); + } + } + } + + free( ports ); + + _success_ful = TRUE; +} + + + + +audioJACK::~audioJACK() +{ + jack_deactivate( m_client ); + jack_client_close( m_client ); + + while( m_bufferSets.size() ) + { + while( m_bufferSets.front().size() ) + { + bufferAllocator::free( + m_bufferSets.front().front().buf ); + m_bufferSets.front().erase( + m_bufferSets.front().begin() ); + } + m_bufferSets.erase( m_bufferSets.begin() ); + } +} + + + + +void audioJACK::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, + float _master_output ) +{ + m_bufMutex.lock(); + + vvector bufs; + for( Uint8 chnl = 0; chnl < channels(); ++chnl ) + { + sampleType * buf = bufferAllocator::alloc( + _frames ); + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + buf[frame] = _ab[frame][chnl] * _master_output; + } + bufset b = { buf, _frames } ; + bufs.push_back( b ); + } + m_bufferSets.push_back( bufs ); + m_frameSync += _frames; + + m_bufMutex.unlock(); + + // now wait until data has been collected by processCallback() + while( m_frameSync > m_jackBufSize ) + { +#ifdef HAVE_UNISTD_H +#ifdef HAVE_USLEEP + // just wait and give cpu-time to other processes + usleep( 500 ); +#endif +#endif + } +} + + + + +int audioJACK::processCallback( jack_nframes_t _nframes, void * _udata ) +{ + audioJACK * _this = static_cast( _udata ); + +#ifdef LMMS_DEBUG + assert( _this != NULL ); +#endif + jack_transport_state_t ts = jack_transport_query( _this->m_client, + NULL ); + if( ts != JackTransportRolling ) + { + return( 0 ); + } + + vvector outbufs( _this->channels(), + NULL ); + Uint8 ch = 0; + for( vvector::iterator it = + outbufs.begin(); it != outbufs.end(); ++it, ++ch ) + { + *it = (jack_default_audio_sample_t *) jack_port_get_buffer( + _this->m_outputPorts[ch], _nframes ); + } + + _this->m_bufMutex.lock(); + + jack_nframes_t done = 0; + while( done < _nframes ) + { + if( _this->m_bufferSets.size() == 0 ) + { + break; + } + jack_nframes_t todo = tMin( _nframes - done, + _this->m_bufferSets.front()[0].frames - + _this->m_framesDoneInCurBuf ); + for( Uint8 ch = 0; ch < _this->channels(); ++ch ) + { + memcpy( outbufs[ch] + done, + _this->m_bufferSets.front()[ch].buf + + _this->m_framesDoneInCurBuf, + sizeof( jack_default_audio_sample_t ) * + todo ); + } + _this->m_framesDoneInCurBuf += todo; + if( _this->m_framesDoneInCurBuf >= + _this->m_bufferSets.front()[0].frames ) + { + for( Uint8 ch = 0; ch < _this->channels(); ++ch ) + { + bufferAllocator::free( + _this->m_bufferSets.front()[ch].buf ); + } + _this->m_bufferSets.erase( + _this->m_bufferSets.begin() ); + _this->m_framesDoneInCurBuf = 0; + } + done += todo; + _this->m_frameSync -= todo; + } + _this->m_bufMutex.unlock(); + + return( 0 ); +} + + + + +int audioJACK::bufSizeCallback( jack_nframes_t _nframes, void * _udata ) +{ + audioJACK * _this = static_cast( _udata ); + +#ifdef LMMS_DEBUG + assert( _this != NULL ); +#endif + _this->m_jackBufSize = _nframes; + + return( 0 ); +} + + + + +audioJACK::setupWidget::setupWidget( QWidget * _parent ) : + audioDevice::setupWidget( audioJACK::name(), _parent ) +{ + QString cn = configManager::inst()->value( "audiojack", "clientname" ); + if( cn == "" ) + { + cn = "lmms"; + } + m_clientName = new QLineEdit( cn, this ); + m_clientName->setGeometry( 10, 20, 160, 20 ); + + QLabel * cn_lbl = new QLabel( tr( "CLIENT-NAME" ), this ); + cn_lbl->setFont( pointSize<6>( cn_lbl->font() ) ); + cn_lbl->setGeometry( 10, 40, 160, 10 ); + + m_channels = new lcdSpinBox( DEFAULT_CHANNELS, SURROUND_CHANNELS, 1, + this ); + m_channels->setStep( 2 ); + m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setValue( configManager::inst()->value( "audiojack", + "channels" ).toInt() ); + m_channels->move( 180, 20 ); + +} + + + + +audioJACK::setupWidget::~setupWidget() +{ +} + + + + +void audioJACK::setupWidget::saveSettings( void ) +{ + configManager::inst()->setValue( "audiojack", "clientname", + m_clientName->text() ); + configManager::inst()->setValue( "audiojack", "channels", + QString::number( m_channels->value() ) ); +} + + +#endif diff --git a/src/audio/audio_oss.cpp b/src/audio/audio_oss.cpp new file mode 100644 index 0000000000..60a100e37d --- /dev/null +++ b/src/audio/audio_oss.cpp @@ -0,0 +1,329 @@ +/* + * audio_oss.cpp - device-class that implements OSS-PCM-output + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "audio_oss.h" +#include "buffer_allocator.h" +#include "endian_handling.h" +#include "lcd_spinbox.h" +#include "templates.h" + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_FCNTL_H +#include +#endif +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef OSS_USE_SOUNDCARD_H +/* This is installed on some systems */ +#include +#else +/* This is recommended by OSS */ +#include +#endif + + +#include "config_mgr.h" + + +#ifndef _PATH_DEV_DSP +#ifdef __OpenBSD__ +#define _PATH_DEV_DSP "/dev/audio" +#else +#define _PATH_DEV_DSP "/dev/dsp" +#endif +#endif + + + +audioOSS::audioOSS( Uint32 _sample_rate, bool & _success_ful ) : + audioDevice( _sample_rate, tLimit( configManager::inst()->value( + "audiooss", "channels" ).toInt(), + DEFAULT_CHANNELS, SURROUND_CHANNELS ) ), + m_convertEndian( FALSE ) +{ + _success_ful = FALSE; + + m_audioFD = open( +#ifdef QT4 + probeDevice().toAscii().constData(), +#else + probeDevice().ascii(), +#endif + O_WRONLY, 0 ); + + if( m_audioFD == -1 ) + { + printf( "audioOSS: failed opening audio-device\n" ); + return; + } + + + // Make the file descriptor use blocking writes with fcntl() + if ( fcntl( m_audioFD, F_SETFL, fcntl( m_audioFD, F_GETFL ) & + ~O_NONBLOCK ) < 0 ) + { + printf( "could not set audio blocking mode\n" ); + return; + } + + int frag_spec; + for( frag_spec = 0; static_cast( 0x01 << frag_spec ) < + mixer::inst()->framesPerAudioBuffer() * channels() * + BYTES_PER_OUTPUT_SAMPLE; + ++frag_spec ) + { + } + + frag_spec |= 0x00020000; // two fragments, for low latency + + if ( ioctl( m_audioFD, SNDCTL_DSP_SETFRAGMENT, &frag_spec ) < 0 ) + { + perror( "SNDCTL_DSP_SETFRAGMENT" ); + printf( "Warning: Couldn't set audio fragment size\n" ); + } + + unsigned int value; + // Get a list of supported hardware formats + if ( ioctl( m_audioFD, SNDCTL_DSP_GETFMTS, &value ) < 0 ) + { + perror( "SNDCTL_DSP_GETFMTS" ); + printf( "Couldn't get audio format list\n" ); + return; + } + + // Set the audio format + if( value & AFMT_S16_LE ) + { + value = AFMT_S16_LE; + } + else if( value & AFMT_S16_BE ) + { + value = AFMT_S16_BE; + } + else + { + printf(" Soundcard doesn't support signed 16-bit-data\n"); + } + if ( ioctl( m_audioFD, SNDCTL_DSP_SETFMT, &value ) < 0 ) + { + perror( "SNDCTL_DSP_SETFMT" ); + printf( "Couldn't set audio format\n" ); + return; + } + if( ( isLittleEndian() && ( value == AFMT_S16_BE ) ) || + ( !isLittleEndian() && ( value == AFMT_S16_LE ) ) ) + { + m_convertEndian = TRUE; + } + + // Set the number of channels of output + value = channels(); + if ( ioctl( m_audioFD, SNDCTL_DSP_CHANNELS, &value ) < 0 ) + { + perror( "SNDCTL_DSP_CHANNELS" ); + printf( "Cannot set the number of channels\n" ); + return; + } + if( value != channels() ) + { + printf( "Couldn't set number of channels\n" ); + return; + } + + // Set the DSP frequency + value = sampleRate(); + if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) + { + perror( "SNDCTL_DSP_SPEED" ); + printf( "Couldn't set audio frequency\n" ); + return; + } + if( value != sampleRate() ) + { + //printf( "Soundcard uses different sample-rate than LMMS " + // "does!\n" ); + int q = 0; + if( sampleRate() == SAMPLE_RATES[1] ) + { + q = 1; + } + if( sampleRate() == 44100 || sampleRate() == 88200 ) + { + SAMPLE_RATES[0] = 48000; + SAMPLE_RATES[1] = 96000; + } + else + { + SAMPLE_RATES[0] = 44100; + SAMPLE_RATES[1] = 82000; + } + setSampleRate( SAMPLE_RATES[q] ); + value = sampleRate(); + if ( ioctl( m_audioFD, SNDCTL_DSP_SPEED, &value ) < 0 ) + { + perror( "SNDCTL_DSP_SPEED" ); + printf( "Couldn't set audio frequency\n" ); + return; + } + } + + _success_ful = TRUE; +} + + + + +audioOSS::~audioOSS() +{ + close( m_audioFD ); +} + + + + +QString audioOSS::probeDevice( void ) +{ + QString dev = configManager::inst()->value( "audiooss", "device" ); + if( dev == "" ) + { + char * adev = getenv( "AUDIODEV" ); // Is there a standard + // variable name? + if( adev != NULL ) + { + dev = adev; + } + else + { + dev = _PATH_DEV_DSP; // default device + } + } + + // if the first open fails, look for other devices + if ( QFileInfo( dev ).isWritable() == FALSE ) + { + int instance = -1; + while( 1 ) + { + dev = _PATH_DEV_DSP + QString::number( ++instance ); + if( !QFileInfo( dev ).exists() ) + { + dev = _PATH_DEV_DSP; + break; + } + if( QFileInfo( dev ).isWritable() ) + { + break; + } + } + } + return( dev ); +} + + + + +void audioOSS::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, + float _master_output ) +{ + outputSampleType * outbuf = bufferAllocator::alloc( + _frames * channels() ); + int bytes = convertToS16( _ab, _frames, _master_output, outbuf, + m_convertEndian ); + write( m_audioFD, outbuf, bytes ); + + bufferAllocator::free( outbuf ); +} + + + + +audioOSS::setupWidget::setupWidget( QWidget * _parent ) : + audioDevice::setupWidget( audioOSS::name(), _parent ) +{ + m_device = new QLineEdit( probeDevice(), this ); + m_device->setGeometry( 10, 20, 160, 20 ); + + QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); + dev_lbl->setGeometry( 10, 40, 160, 10 ); + + m_channels = new lcdSpinBox( DEFAULT_CHANNELS, SURROUND_CHANNELS, 1, + this ); + m_channels->setStep( 2 ); + m_channels->setLabel( tr( "CHANNELS" ) ); + m_channels->setValue( configManager::inst()->value( "audiooss", + "channels" ).toInt() ); + m_channels->move( 180, 20 ); + +} + + + + +audioOSS::setupWidget::~setupWidget() +{ + +} + + + + +void audioOSS::setupWidget::saveSettings( void ) +{ + configManager::inst()->setValue( "audiooss", "device", + m_device->text() ); + configManager::inst()->setValue( "audiooss", "channels", + QString::number( m_channels->value() ) ); +} + + diff --git a/src/audio/audio_sample_recorder.cpp b/src/audio/audio_sample_recorder.cpp new file mode 100644 index 0000000000..ee4bd9614b --- /dev/null +++ b/src/audio/audio_sample_recorder.cpp @@ -0,0 +1,112 @@ +/* + * audio_sample_recorder.cpp - device-class that implements recording + * surround-audio-buffers into RAM, maybe later + * also harddisk + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + + +#include "audio_sample_recorder.h" +#include "sample_buffer.h" +#include "buffer_allocator.h" +#include "debug.h" + + + +audioSampleRecorder::audioSampleRecorder( Uint32 _sample_rate, Uint32 _channels, + bool & _success_ful ) : + audioDevice( _sample_rate, _channels ), + m_buffers() +{ + _success_ful = TRUE; +} + + + + +audioSampleRecorder::~audioSampleRecorder() +{ + while( !m_buffers.isEmpty() ) + { + bufferAllocator::free( m_buffers.front().first ); + m_buffers.erase( m_buffers.begin() ); + } +} + + + + +Uint32 audioSampleRecorder::framesRecorded( void ) const +{ + Uint32 frames = 0; + for( bufferVector::const_iterator it = m_buffers.begin(); + it != m_buffers.end(); ++it ) + { + frames += it->second; + } + return( frames ); +} + + + + +void audioSampleRecorder::createSampleBuffer( sampleBuffer * * _sample_buf ) + const +{ + Uint32 frames = framesRecorded(); + // create buffer to store all recorded buffers in + sampleFrame * data = bufferAllocator::alloc( frames ); + // make sure buffer is cleaned up properly at the end... + bufferAllocator::autoCleaner ac( data ); + +#ifdef LMMS_DEBUG + assert( data != NULL ); +#endif + // now copy all buffers into big buffer + for( bufferVector::const_iterator it = m_buffers.begin(); + it != m_buffers.end(); ++it ) + { + memcpy( data, it->first, it->second * sizeof( sampleFrame ) ); + data += it->second; + } + // create according sample-buffer out of big buffer + *_sample_buf = new sampleBuffer( ac.ptr(), frames ); +} + + + + +void audioSampleRecorder::writeBufferToDev( surroundSampleFrame * _ab, + Uint32 _frames, float ) +{ + sampleFrame * buf = bufferAllocator::alloc( _frames ); + for( Uint32 frame = 0; frame < _frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + buf[frame][chnl] = _ab[frame][chnl]; + } + } + m_buffers.push_back( qMakePair( buf, _frames ) ); +} + + diff --git a/src/audio/audio_sdl.cpp b/src/audio/audio_sdl.cpp new file mode 100644 index 0000000000..3e186ddb00 --- /dev/null +++ b/src/audio/audio_sdl.cpp @@ -0,0 +1,203 @@ +/* + * audio_sdl.cpp - device-class that performs PCM-output via SDL + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + + +#include "audio_sdl.h" + +#ifdef SDL_AUDIO_SUPPORT + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + + +#include "buffer_allocator.h" +#include "debug.h" +#include "config_mgr.h" +#include "templates.h" + + + +audioSDL::audioSDL( Uint32 _sample_rate, bool & _success_ful ) : + audioDevice( _sample_rate, DEFAULT_CHANNELS ), + m_buffer( bufferAllocator::alloc( + mixer::inst()->framesPerAudioBuffer() * + channels() ) ), + m_bufMutex(), + m_callbackMutex(), + m_convertEndian( FALSE ) +{ + _success_ful = FALSE; + + // if device is set, we set AUDIODEV-environment-variable, so that + // SDL can evaluate and use it + QString dev = configManager::inst()->value( "audiosdl", "device" ); + if( dev != "" ) + { + putenv( const_cast( ( "AUDIODEV=" + dev ). +#ifdef QT4 + toAscii().constData() ) ); +#else + ascii() ) ); +#endif + } + + + if( SDL_Init( SDL_INIT_AUDIO | SDL_INIT_NOPARACHUTE ) < 0 ) + { + printf( "Couldn't initialize SDL: %s\n", SDL_GetError() ); + return; + } + + m_audioHandle.freq = _sample_rate; + m_audioHandle.format = AUDIO_S16SYS; // we want it in byte-order + // of system, so we don't have + // to convert the buffers + m_audioHandle.channels = channels(); + m_audioHandle.samples = mixer::inst()->framesPerAudioBuffer(); + + m_audioHandle.callback = sdlAudioCallback; + m_audioHandle.userdata = this; + + SDL_AudioSpec actual; + + // open the audio device, forcing the desired format + if( SDL_OpenAudio( &m_audioHandle, &actual ) < 0 ) + { + printf( "Couldn't open SDL-audio: %s\n", SDL_GetError() ); + return; + } + m_convertEndian = ( m_audioHandle.format != actual.format ); + + clearS16Buffer( m_buffer, m_audioHandle.samples ); + + // start playing + SDL_PauseAudio( 0 ); + + _success_ful = TRUE; +} + + + + +audioSDL::~audioSDL() +{ + SDL_PauseAudio( 1 ); + SDL_CloseAudio(); + SDL_Quit(); + + m_bufMutex.lock(); + bufferAllocator::free( m_buffer ); + m_bufMutex.unlock(); +} + + + + + +void audioSDL::writeBufferToDev( surroundSampleFrame * _ab, Uint32 _frames, + float _master_output ) +{ + m_bufMutex.lock(); + convertToS16( _ab, _frames, _master_output, m_buffer, + m_convertEndian ); + m_bufMutex.unlock(); + // before returning make sure, callback was called, so we're synced + // with it (otherwise it could be that (if there's not much to render) + // this function is called several times although we had to wait until + // we can proceed with next audio-output) + m_callbackMutex.lock(); +} + + + + +void audioSDL::sdlAudioCallback( void * _udata, Uint8 * _buf, int _len ) +{ + audioSDL * _this = static_cast( _udata ); + +#ifdef LMMS_DEBUG + assert( _this != NULL ); +#endif + + _this->m_bufMutex.lock(); + + // writeBufferToDev() prepared everything for us, so we just have + // to do a memcpy() :-) + memcpy( _buf, _this->m_buffer, _len ); + + // clear our output buffer, so that we don't output the same noise + // when being called again without that writeBufferToDev() was called + // (e.g. if there's too much to render) + _this->clearS16Buffer( _this->m_buffer, _this->m_audioHandle.samples ); + + _this->m_bufMutex.unlock(); + + // we got our last buffer, so we let writeBufferToDev() return + _this->m_callbackMutex.unlock(); +} + + + + +audioSDL::setupWidget::setupWidget( QWidget * _parent ) : + audioDevice::setupWidget( audioSDL::name(), _parent ) +{ + QString dev = configManager::inst()->value( "audiosdl", "device" ); + m_device = new QLineEdit( dev, this ); + m_device->setGeometry( 10, 20, 160, 20 ); + + QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); + dev_lbl->setGeometry( 10, 40, 160, 10 ); + +} + + + + +audioSDL::setupWidget::~setupWidget() +{ + +} + + + + +void audioSDL::setupWidget::saveSettings( void ) +{ + configManager::inst()->setValue( "audiosdl", "device", + m_device->text() ); +} + + +#endif diff --git a/src/lib/buffer_allocator.cpp b/src/lib/buffer_allocator.cpp new file mode 100644 index 0000000000..97b262bd81 --- /dev/null +++ b/src/lib/buffer_allocator.cpp @@ -0,0 +1,197 @@ +/* + * buffer_allocator.cpp - namespace bufferAllocator providing routines for own + * optimized memory-management for audio-buffers + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include +#include + +#define qSort qHeapSort + +#endif + +#include +#include + +#include "buffer_allocator.h" +#include "templates.h" +#include "mixer.h" +#include "debug.h" + + +struct bufDesc +{ + bool free; + char * origPtr; + void * buf; + Uint32 bytes; + Uint32 timesUsed; + +} ; + + +inline bool operator<( const bufDesc & _bd1, const bufDesc & _bd2 ) +{ + return( _bd1.timesUsed < _bd2.timesUsed ); +} + +#ifdef QT4 + +inline bool operator==( const bufDesc & _bd1, const bufDesc & _bd2 ) +{ + return( memcmp( &_bd1, &_bd2, sizeof( bufDesc ) ) == 0 ); +} + +#else + +inline bool operator!=( const bufDesc & _bd1, const bufDesc & _bd2 ) +{ + return( memcmp( &_bd1, &_bd2, sizeof( bufDesc ) ) != 0 ); +} + +#endif + + +static vlist s_buffers; +typedef vlist::iterator bufIt; + +QMutex s_buffersMutex; + + +const int BUFFER_ALIGN = 16; +const int BUFFER_ALIGN_MASK = BUFFER_ALIGN - 1; + + + +void bufferAllocator::cleanUp( Uint16 _level ) +{ + // first insert all unused bufs into an array + vvector bufsToRemove; + for( bufIt it = s_buffers.begin(); it != s_buffers.end(); ++it ) + { + if( ( *it ).free ) + { + bufsToRemove.push_back( *it ); + } + } + + // sort array by usage of each buffer + // ( operator<(...) compares bufDesc::timesUsed ) + qSort( bufsToRemove ); + + const Uint16 todo = tMin( s_buffers.size() - _level, + bufsToRemove.size() ); + + // now cleanup the first n elements of sorted array + for( Uint16 i = 0; i < todo ; ++i ) + { + delete[] bufsToRemove[i].origPtr; + s_buffers.erase( qFind( s_buffers.begin(), s_buffers.end(), + bufsToRemove[i] ) ); + } +#ifdef LMMS_DEBUG + //printf( "cleaned up %d buffers\n", todo ); +#endif +} + + + + +void bufferAllocator::free( void * _buf ) +{ + s_buffersMutex.lock(); + + // look for buffer + for( bufIt it = s_buffers.begin(); it != s_buffers.end(); ++it ) + { + if( !( *it ).free && ( *it ).buf == _buf ) + { + ++( *it ).timesUsed; + ( *it ).free = TRUE; + break; + } + } + + // do clean-up if neccessary + static Uint16 CLEANUP_LEVEL = static_cast( 512 / ( logf( + mixer::inst()->framesPerAudioBuffer() ) / + logf( 2 ) ) ); + if( s_buffers.size() > CLEANUP_LEVEL ) + { + cleanUp( CLEANUP_LEVEL ); + } + + s_buffersMutex.unlock(); +} + + + + +void * bufferAllocator::allocBytes( Uint32 _bytes ) +{ + QMutexLocker ml( &s_buffersMutex ); + + bufIt free_buf = s_buffers.end(); + + // look whether there's a buffer matching to the one wanted and + // find out the most used one (higher chances for being in CPU-cache) + for( bufIt it = s_buffers.begin(); it != s_buffers.end(); ++it ) + { + if( ( *it ).free && ( *it ).bytes == _bytes ) + { + if( free_buf == s_buffers.end() || + ( *it ).timesUsed > ( *free_buf ).timesUsed ) + { + free_buf = it; + } + } + } + + if( free_buf != s_buffers.end() ) + { + ( *free_buf ).free = FALSE; + return( ( *free_buf ).buf ); + } + + + // nothing so far, so we'll alloc a new (aligned) buf + bufDesc d = { FALSE, new char[_bytes + BUFFER_ALIGN], NULL, _bytes, 0 }; + d.buf = (void *)( (size_t) d.origPtr + ( BUFFER_ALIGN - + ( (size_t) d.origPtr & + BUFFER_ALIGN_MASK ) ) ); + s_buffers.push_back( d ); + return( d.buf ); +} + + + diff --git a/src/lib/clipboard.cpp b/src/lib/clipboard.cpp new file mode 100644 index 0000000000..91e876cee0 --- /dev/null +++ b/src/lib/clipboard.cpp @@ -0,0 +1,58 @@ +/* + * clipboard.cpp - the clipboard for patterns, notes etc. + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "clipboard.h" +#include "settings.h" + + +namespace clipboard +{ + + map content; + + + void copy( settings * _settings_object ) + { + QDomDocument doc; + QDomElement parent = doc.createElement( "clipboard" ); + _settings_object->saveSettings( doc, parent ); + content[_settings_object->nodeName()] = + parent.firstChild().toElement(); + } + + + + + const QDomElement * getContent( const QString & _node_name ) + { + if( content.find( _node_name ) != content.end() ) + { + return( &content[_node_name] ); + } + return( NULL ); + } + +} ; + + diff --git a/src/lib/embed.cpp b/src/lib/embed.cpp new file mode 100644 index 0000000000..eabc07d508 --- /dev/null +++ b/src/lib/embed.cpp @@ -0,0 +1,123 @@ +/* + * embed.cpp - misc stuff for using embedded resources (linked into binary) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "embed.h" +#include "config_mgr.h" + + +namespace embed +{ + +#include "embedded_resources.h" + + +QPixmap getIconPixmap( const char * _name, int _w, int _h ) +{ + if( _w == -1 || _h == -1 ) + { + QString name = QString( _name ) + ".png"; +#ifdef QT4 + const embedDesc & e = findEmbeddedData( + name.toAscii().constData() ); +#else + const embedDesc & e = findEmbeddedData( name.ascii() ); +#endif + // not found? + if( QString( e.name ) != name ) + { + // then look whether icon is in data-dir + QPixmap p( configManager::inst()->artworkDir() + name ); + if( p.isNull() ) + { + p = QPixmap( 1, 1 ); + } + return( p ); + } + QPixmap p; + p.loadFromData( e.data, e.size ); + return( p ); + } +#ifdef QT4 + return( getIconPixmap( _name ).scaled( _w, _h, Qt::IgnoreAspectRatio, + Qt::SmoothTransformation ) ); +#else + return( getIconPixmap( _name ).convertToImage().smoothScale( _w, _h ) ); +#endif +} + + + + +QString getText( const char * _name ) +{ + const embedDesc & e = findEmbeddedData( _name ); + return( QString::fromLatin1( (const char *) e.data, e.size ) ); +} + + + + +void loadTranslation( const QString & _tname ) +{ +#if QT_VERSION >= 0x030100 + QString name = _tname + ".qm"; +#ifdef QT4 + const embedDesc & e = findEmbeddedData( name.toAscii().constData() ); +#else + const embedDesc & e = findEmbeddedData( name.ascii() ); +#endif + QTranslator * t = new QTranslator( 0 ); + // not found? + if( QString( e.name ) != name ) + { + // then look whether translation is in data-dir + t->load( name, configManager::inst()->localeDir() ); + } + else + { + t->load( e.data, (int) e.size ); + } + qApp->installTranslator( t ); +#endif +} + + +} diff --git a/src/lib/mmp.cpp b/src/lib/mmp.cpp new file mode 100644 index 0000000000..64a49b2004 --- /dev/null +++ b/src/lib/mmp.cpp @@ -0,0 +1,273 @@ +/* + * mmp.cpp - implementation of class multimediaProject + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include + +#endif + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include "mmp.h" +#include "song_editor.h" + + + +multimediaProject::typeDescStruct multimediaProject::s_types[multimediaProject::PROJ_TYPE_COUNT] = +{ + { multimediaProject::UNKNOWN, "unknown" }, + { multimediaProject::SONG_PROJECT, "song" }, + { multimediaProject::CHANNEL_SETTINGS, "channelsettings" }, + { multimediaProject::EFFECT_SETTINGS, "effectsettings" }, + { multimediaProject::VIDEO_PROJECT, "video" }, + { multimediaProject::BURN_PROJECT, "burn" }, + { multimediaProject::PLAYLIST, "playlist" } +} ; + + + +multimediaProject::multimediaProject( projectTypes _project_type ) : + QDomDocument( "multimedia-project" ), + m_content(), + m_head(), + m_type( _project_type ) +{ + QDomElement root = createElement( "multimediaproject" ); + root.setAttribute( "version", MMP_VERSION_STRING ); + root.setAttribute( "type", typeName( _project_type ) ); + root.setAttribute( "creator", "Linux MultiMedia Studio (LMMS)" ); + root.setAttribute( "creatorversion", VERSION ); + appendChild( root ); + + m_head = createElement( "head" ); + root.appendChild( m_head ); + + m_content = createElement( typeName( _project_type ) ); + root.appendChild( m_content ); + +} + + + + +multimediaProject::multimediaProject( const QString & _in_file_name ) : + QDomDocument(), + m_content(), + m_head() +{ + QFile in_file( _in_file_name ); +#ifdef QT4 + if( !in_file.open( QIODevice::ReadOnly ) ) +#else + if( !in_file.open( IO_ReadOnly ) ) +#endif + { + QMessageBox::critical( NULL, + songEditor::tr( "Could not open file" ), + songEditor::tr( "Could not open " + "file %1. You probably " + "have no rights to " + "read this file.\n" + "Please make sure you " + "have at least read-" + "access to the file " + "and try again." + ).arg( _in_file_name ) ); + return; + } + + QString error_msg; + int line; + int col; + if( !setContent( &in_file, &error_msg, &line, &col ) ) + { + QMessageBox::critical( NULL, songEditor::tr( "Error in " + "multimedia-project" ), + songEditor::tr( "The multimedia-" + "project %1 seems to " + "contain errors. LMMS " + "will try its best " + "to recover as much as " + "possible data from " + "this file." + ).arg( _in_file_name ) ); + return; + } + in_file.close(); + + + QDomElement root = documentElement(); + m_type = type( root.attribute( "type" ) ); + + QDomNode node = root.firstChild(); + while( !node.isNull() ) + { + if( node.isElement() ) + { + if( node.nodeName() == "head" ) + { + m_head = node.toElement(); + } + else if( node.nodeName() == typeName( m_type ) ) + { + m_content = node.toElement(); + } + } + node = node.nextSibling(); + } +} + + + + +multimediaProject::~multimediaProject() +{ +} + + + + +bool multimediaProject::writeFile( const QString & _fn, bool _overwrite_check ) +{ + QString xml = "\n" + toString( +#if QT_VERSION >= 0x030100 + 2 +#endif + ); + QString fn = _fn; + if( type() == CHANNEL_SETTINGS ) + { + if( fn.section( '.', -2, -1 ) != "cs.xml" ) + { + fn += ".cs.xml"; + } + } + else if( fn.section( '.',-1 ) != "xml" ) + { + fn += ".xml"; + } + + + QFile outfile( fn ); + if( _overwrite_check == TRUE && + outfile.exists() == TRUE && + QMessageBox:: +#if QT_VERSION >= 0x030200 + question +#else + information +#endif + ( NULL, + songEditor::tr( "File already exists" ), + songEditor::tr( "The file %1 already " + "exists.\nDo you want " + "to overwrite it?" + ).arg( fn ), + QMessageBox::Yes, QMessageBox::No ) + == QMessageBox::No ) + { + return( FALSE ); + } + + +#ifdef QT4 + if( !outfile.open( QIODevice::WriteOnly | QIODevice::Truncate ) ) +#else + if( !outfile.open( IO_WriteOnly | IO_Truncate ) ) +#endif + { + QMessageBox::critical( NULL, songEditor::tr( "Could not write " + "file" ), + songEditor::tr( "Could not write file " + "%1. You probably are " + "not permitted to " + "write to this file. " + "Please make sure you " + "have write-access to " + "the file and try " + "again." + ).arg( fn ) ); + return( FALSE ); + } +#ifdef QT4 + outfile.write( xml.toAscii().constData(), xml.length() ); +#else + outfile.writeBlock( xml.ascii(), xml.length() ); +#endif + outfile.close(); + + return( TRUE ); +} + + + + +multimediaProject::projectTypes multimediaProject::typeOfFile( + const QString & _fn ) +{ + multimediaProject m( _fn ); + return( m.type() ); +} + + + + +multimediaProject::projectTypes multimediaProject::type( + const QString & _type_name ) +{ + for( int i = 0; i < PROJ_TYPE_COUNT; ++i ) + { + if( s_types[i].m_name == _type_name ) + { + return( static_cast( + i ) ); + } + } + return( UNKNOWN ); +} + + + + +QString multimediaProject::typeName( projectTypes _project_type ) +{ + if( _project_type >= UNKNOWN && _project_type < PROJ_TYPE_COUNT ) + { + return( s_types[_project_type].m_name ); + } + return( s_types[UNKNOWN].m_name ); +} + diff --git a/src/lib/oscillator.cpp b/src/lib/oscillator.cpp new file mode 100644 index 0000000000..f39c2450c9 --- /dev/null +++ b/src/lib/oscillator.cpp @@ -0,0 +1,259 @@ +/* + * oscillator.cpp - implementation of powerful oscillator-class + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "oscillator.h" + + +const sampleFrame zero_frame = { 0.0f, 0.0f }; + + +oscillator::oscillator( modulationAlgos _modulation_algo, float _freq, + Sint16 _phase_offset, float _volume_factor, + oscillator * _sub_osc ) : + m_freq(_freq), + m_volumeFactor(_volume_factor), + m_phaseOffset(_phase_offset), + m_subOsc(_sub_osc), + m_userWaveData( &zero_frame ), + m_userWaveFrames( 1 ) +{ + + if( m_subOsc != NULL ) + { + switch( _modulation_algo ) + { + case FREQ_MODULATION: + m_callUpdate = &oscillator::updateFM; + break; + + case AMP_MODULATION: + m_callUpdate = &oscillator::updateAM; + break; + + case MIX: + m_callUpdate = &oscillator::updateMix; + break; + case SYNC: + m_callUpdate = &oscillator::updateSync; + break; + } + } + else + { + m_callUpdate = &oscillator::updateNoSub; + } + + recalcOscCoeff(); +} + + + +// if we have no sub-osc, we can't do any modulation... just get our samples +#define defineNoSubUpdateFor(x,getSampleFunction) \ +void x::updateNoSub( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ) \ +{ \ + for( Uint16 frame = 0; frame < _frames; ++frame ) \ + { \ + _ab[frame][_chnl] = getSampleFunction( ++m_sample * \ + m_oscCoeff ) * m_volumeFactor; \ + } \ +} + + +// do fm by using sub-osc as modulator +#define defineFMUpdateFor(x,getSampleFunction) \ +void x::updateFM( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ) \ +{ \ + m_subOsc->update( _ab, _frames, _chnl ); \ + for( Uint16 frame = 0; frame < _frames; ++frame ) \ + { \ + _ab[frame][_chnl] = getSampleFunction( ++m_sample * \ + m_oscCoeff + \ + _ab[frame][_chnl] ) * \ + m_volumeFactor; \ + /* following line is REAL FM */ \ +/* float new_freq = powf( 2.0, _ab[frame][_chnl] ); \ + _ab[frame][_chnl] = getSampleFunction( ++m_sample*((m_freq * \ + new_freq )/mixer::inst()->sampleRate() )) * m_volumeFactor; \ + _ab[frame][_chnl] = getSampleFunction( ++m_sample*(m_oscCoeff *\ + _ab[frame][_chnl] )) * m_volumeFactor;*/ \ + } \ +} + + +// do am by using sub-osc as modulator +#define defineAMUpdateFor(x,getSampleFunction) \ +void x::updateAM( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ) \ +{ \ + m_subOsc->update( _ab, _frames, _chnl ); \ + for( Uint16 frame = 0; frame < _frames; ++frame ) \ + { \ + _ab[frame][_chnl] *= getSampleFunction( ++m_sample * \ + m_oscCoeff ) * m_volumeFactor; \ + } \ +} + + +// do mix by using sub-osc as mix-sample +#define defineMixUpdateFor(x,getSampleFunction) \ +void x::updateMix( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ) \ +{ \ + m_subOsc->update( _ab, _frames, _chnl ); \ + for( Uint16 frame = 0; frame < _frames; ++frame ) \ + { \ + _ab[frame][_chnl] += getSampleFunction( ++m_sample * \ + m_oscCoeff ) * m_volumeFactor; \ + } \ +} + + +// sync with sub-osc (every time sub-osc starts new period, we also start new +// period) +#define defineSyncUpdateFor(x,getSampleFunction) \ +void x::updateSync( sampleFrame * _ab, Uint32 _frames, Uint8 _chnl ) \ +{ \ + for( Uint16 frame = 0; frame < _frames ; ++frame ) \ + { \ + if( m_subOsc->syncOk() ) \ + { \ + sync(); \ + } \ + _ab[frame][_chnl] = getSampleFunction( ++m_sample * \ + m_oscCoeff ) * m_volumeFactor; \ + } \ +} + + + +#define generateOscillatorCodeFor(x,getSampleFunction); \ +class x : public oscillator \ +{ \ +public: \ + x( modulationAlgos modulation_algo, float _freq, Sint16 _phase_offset, \ + float _volume_factor, oscillator * _sub_osc) : \ + oscillator (modulation_algo, _freq, _phase_offset, _volume_factor, \ + _sub_osc ) \ + { \ + } \ + \ +protected: \ + void updateNoSub( sampleFrame * _ab, Uint32 _frames, \ + Uint8 _chnl ); \ + void updateFM( sampleFrame * _ab, Uint32 _frames, \ + Uint8 _chnl ); \ + void updateAM( sampleFrame * _ab, Uint32 _frames, \ + Uint8 _chnl ); \ + void updateMix( sampleFrame * _ab, Uint32 _frames, \ + Uint8 _chnl ); \ + void updateSync( sampleFrame * _ab, Uint32 _frames, \ + Uint8 _chnl ); \ + \ +} ; \ + \ +defineNoSubUpdateFor( x, getSampleFunction ) \ +defineFMUpdateFor( x, getSampleFunction ) \ +defineAMUpdateFor( x, getSampleFunction ) \ +defineMixUpdateFor( x, getSampleFunction ) \ +defineSyncUpdateFor( x, getSampleFunction ) + + +generateOscillatorCodeFor( sinWaveOsc, sinSample ); +generateOscillatorCodeFor( triangleWaveOsc, triangleSample ); +generateOscillatorCodeFor( sawWaveOsc, sawSample ); +generateOscillatorCodeFor( squareWaveOsc, squareSample ); +generateOscillatorCodeFor( moogSawWaveOsc, moogSawSample ); +generateOscillatorCodeFor( expWaveOsc, expSample ); +generateOscillatorCodeFor( noiseWaveOsc, noiseSample ); +generateOscillatorCodeFor( userWaveOsc, userWaveSample ); + + + +oscillator * FASTCALL oscillator::createNewOsc( waveShapes _wave_shape, + modulationAlgos _modulation_algo, + float _freq, Sint16 _phase_offset, + float _volume_factor, + oscillator * _sub_osc ) +{ + switch( _wave_shape ) + { + case SIN_WAVE: + return( new sinWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case TRIANGLE_WAVE: + return( new triangleWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case SAW_WAVE: + return( new sawWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case SQUARE_WAVE: + return( new squareWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case MOOG_SAW_WAVE: + return( new moogSawWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case EXP_WAVE: + return( new expWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case WHITE_NOISE_WAVE: + return( new noiseWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + case USER_DEF_WAVE: + return( new userWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + default: + return( new sinWaveOsc( _modulation_algo, _freq, + _phase_offset, _volume_factor, + _sub_osc ) ); + } + +} + + + + +// should be called every time phase-offset or frequency is changed... +void oscillator::recalcOscCoeff( const float additional_phase_offset ) +{ + m_oscCoeff = m_freq / static_cast( mixer::inst()->sampleRate() ); + + m_sample = static_cast( ( m_phaseOffset * ( 1.0f / 360.0f ) + + additional_phase_offset ) * + ( mixer::inst()->sampleRate() / + m_freq ) ); + // because we pre-increment m_sample in update-function, we should + // decrement it here... (not possible when 0 because it is + // unsigned - overflow!!!) + if( m_sample > 0 ) + { + --m_sample; + } +} diff --git a/src/lib/sample_buffer.cpp b/src/lib/sample_buffer.cpp new file mode 100644 index 0000000000..a9ed579ef0 --- /dev/null +++ b/src/lib/sample_buffer.cpp @@ -0,0 +1,1014 @@ +/* + * sample_buffer.cpp - container-class sampleBuffer + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include + +#if QT_VERSION < 0x030100 +#include +#endif + +#endif + + +#include + +#ifdef SDL_SDL_SOUND_H +#include SDL_SDL_SOUND_H +#endif + +#ifdef HAVE_SNDFILE_H +#include +#endif + +#ifdef HAVE_VORBIS_VORBISFILE_H +#include +#endif + + +#include "sample_buffer.h" +#include "interpolation.h" +#include "paths.h" +#include "templates.h" +#include "config_mgr.h" +#include "endian_handling.h" +#include "debug.h" + + + +sampleBuffer::sampleBuffer( const QString & _audio_file ) : + QObject(), + m_audioFile( _audio_file ), + m_origData( NULL ), + m_origFrames( 0 ), + m_data( NULL ), + m_frames( 0 ), + m_startFrame( 0 ), + m_endFrame( 0 ), + m_amplification( 1.0f ), + m_reversed( FALSE ), + m_dataMutex() +{ +#ifdef SDL_SDL_SOUND_H + // init sound-file-system of SDL + Sound_Init(); +#endif +#ifdef HAVE_SAMPLERATE_H + initResampling(); +#endif + update(); +} + + + + +sampleBuffer::sampleBuffer( const sampleFrame * _data, Uint32 _frames ) : + QObject(), + m_audioFile( "" ), + m_origData( NULL ), + m_origFrames( 0 ), + m_data( NULL ), + m_frames( 0 ), + m_startFrame( 0 ), + m_endFrame( 0 ), + m_amplification( 1.0f ), + m_reversed( FALSE ), + m_dataMutex() +{ + m_origData = new sampleFrame[_frames]; + memcpy( m_origData, _data, _frames*BYTES_PER_FRAME ); + m_origFrames = _frames; +#ifdef SDL_SDL_SOUND_H + // init sound-file-system of SDL + Sound_Init(); +#endif +#ifdef HAVE_SAMPLERATE_H + initResampling(); +#endif + update(); +} + + + + +sampleBuffer::~sampleBuffer() +{ + m_dataMutex.lock(); + delete[] m_origData; + m_origData = NULL; + delete[] m_data; + m_data = NULL; + m_dataMutex.unlock(); + +#ifdef HAVE_SAMPLERATE_H + quitResampling(); +#endif +} + + + + + + +void sampleBuffer::update( bool _keep_settings ) +{ + m_dataMutex.lock(); + + delete[] m_data; + m_data = NULL; + m_frames = 0; + + if( m_audioFile == "" && m_origData != NULL && m_origFrames > 0 ) + { + // TODO: reverse- and amplification-property is not covered + // by following code... + m_data = new sampleFrame[m_origFrames]; + memcpy( m_data, m_origData, m_origFrames * BYTES_PER_FRAME ); + if( _keep_settings == FALSE ) + { + m_frames = m_origFrames; + m_startFrame = 0; + m_endFrame = m_frames-1; + } + } + else if( m_audioFile != "" ) + { + QString file = m_audioFile; + // if there's not an absolute filename, we assume that we made + // it relative before and so we have to add sample-dir to file- + // name + if( file[0] != '/' ) + { + file = configManager::inst()->samplesDir() + file; + } + const char * f = +#ifdef QT4 + file.toAscii().constData(); +#else + file.ascii(); +#endif + Sint16 * buf = NULL; + Uint8 channels; + +#ifdef SDL_SDL_SOUND_H + if( m_frames == 0 ) + { + m_frames = decodeSampleSDL( f, buf, channels ); + } +#endif +#ifdef HAVE_SNDFILE_H + if( m_frames == 0 ) + { + m_frames = decodeSampleSF( f, buf, channels ); + } +#endif +#ifdef HAVE_VORBIS_VORBISFILE_H + if( m_frames == 0 ) + { + m_frames = decodeSampleOGG( f, buf, channels ); + } +#endif + if( m_frames > 0 && buf != NULL ) + { + if( _keep_settings == FALSE ) + { + // update frame-variables + m_startFrame = 0; + m_endFrame = m_frames - 1; + } + + // following code transforms int-samples into + // float-samples and does amplifying & reversing + float fac = m_amplification / 32767.0f; + m_data = new sampleFrame[m_frames]; + + // if reversing is on, we also reverse when + // scaling + if( m_reversed ) + { + for( Uint32 frame = 0; frame < m_frames; + ++frame ) + { + for( Uint8 chnl = 0; + chnl < DEFAULT_CHANNELS; + ++chnl ) + { +const Uint32 idx = ( m_frames - frame ) * channels + ( chnl % channels ); +m_data[frame][chnl] = buf[idx] * fac; + } + } + } + else + { + for( Uint32 frame = 0; frame < m_frames; + ++frame ) + { + for( Uint8 chnl = 0; + chnl < DEFAULT_CHANNELS; + ++chnl ) + { + const Uint32 idx = frame * channels + ( chnl % channels ); + m_data[frame][chnl] = buf[idx] * fac; + } + } + } + + delete[] buf; + + } + else + { + m_data = new sampleFrame[1]; + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + m_data[0][chnl] = 0.0f; + } + m_frames = 1; + } + } + else + { + m_data = new sampleFrame[1]; + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + m_data[0][chnl] = 0.0f; + } + m_frames = 1; + } + + m_dataMutex.unlock(); + + emit sampleUpdated(); +} + + + + +#ifdef SDL_SDL_SOUND_H +Uint32 sampleBuffer::decodeSampleSDL( const char * _f, Sint16 * & _buf, + Uint8 & _channels ) +{ + Sound_AudioInfo STD_AUDIO_INFO = + { + AUDIO_S16SYS, + DEFAULT_CHANNELS, + SAMPLE_RATES[DEFAULT_QUALITY_LEVEL] + } ; + Uint32 frames = 0; + + Sound_Sample * snd_sample = Sound_NewSampleFromFile( _f, + &STD_AUDIO_INFO, 16384 ); + // file not found? + if( snd_sample != NULL ) + { + // let SDL_sound decode our file to requested format + ( void )Sound_DecodeAll( snd_sample ); + _channels = STD_AUDIO_INFO.channels; + frames = snd_sample->buffer_size / ( BYTES_PER_OUTPUT_SAMPLE * + _channels ); + _buf = new Sint16[frames * _channels]; + memcpy( _buf, snd_sample->buffer, snd_sample->buffer_size ); + + Sound_FreeSample( snd_sample ); + } + return( frames ); +} +#endif + + + + +#ifdef HAVE_SNDFILE_H +Uint32 sampleBuffer::decodeSampleSF( const char * _f, Sint16 * & _buf, + Uint8 & _channels ) +{ + SNDFILE * snd_file; + SF_INFO sf_info; + Uint32 frames = 0; +#ifdef OLD_SNDFILE + if( ( snd_file = sf_open_read( _f, &sf_info ) ) != NULL ) + { + frames = sf_info.samples; +#else + if( ( snd_file = sf_open( _f, SFM_READ, &sf_info ) ) != NULL ) + { + frames = sf_info.frames; +#endif + _buf = new Sint16[sf_info.channels * frames]; + frames = sf_read_short( snd_file, _buf, frames ); + _channels = sf_info.channels; + + sf_close( snd_file ); + } + else + { +#ifdef DEBUG_LMMS + printf( "sampleBuffer::decodeSampleSF(): could not load " + "sample %s: %s\n", _f, sf_strerror( NULL ) ); +#endif + } + return( frames ); +} +#endif + + + + +#ifdef HAVE_VORBIS_VORBISFILE_H + +// callback-functions for reading ogg-file + +#ifndef QT4 +#define read readBlock +#define seek at +#define pos at +#endif + +size_t qfileReadCallback( void * _ptr, size_t _size, size_t _n, void * _udata ) +{ + return( static_cast( _udata )->read( (char*)_ptr, + _size * _n ) ); +} + + + + +int qfileSeekCallback( void * _udata, ogg_int64_t _offset, int _whence ) +{ + QFile * f = static_cast( _udata ); + + if( _whence == SEEK_CUR ) + { + f->seek( f->pos() + _offset ); + } + else if( _whence == SEEK_END ) + { + f->seek( f->size() + _offset ); + } + else + { + f->seek( _offset ); + } + return( 0 ); +} + + + + +int qfileCloseCallback( void * _udata ) +{ + delete static_cast( _udata ); + return( 0 ); +} + + + + +long qfileTellCallback( void * _udata ) +{ + return( static_cast( _udata )->pos() ); +} + +#undef read +#undef seek +#undef pos + + + +Uint32 sampleBuffer::decodeSampleOGG( const char * _f, Sint16 * & _buf, + Uint8 & _channels ) +{ + static ov_callbacks callbacks = + { + qfileReadCallback, + qfileSeekCallback, + qfileCloseCallback, + qfileTellCallback + } ; + + OggVorbis_File vf; + + Uint32 frames = 0; + + QFile * f = new QFile( _f ); +#ifdef QT4 + if( f->open( QFile::ReadOnly | QFile::Truncate ) == FALSE ) +#else + if( f->open( IO_ReadOnly | IO_Truncate ) == FALSE ) +#endif + { + delete f; + return( 0 ); + } + + int err = ov_open_callbacks( f, &vf, NULL, 0, callbacks ); + + if( err < 0 ) + { + switch( err ) + { + case OV_EREAD: + printf( "sampleBuffer::decodeSampleOgg(): " + "media read error\n" ); + break; + case OV_ENOTVORBIS: + printf( "sampleBuffer::decodeSampleOgg(): " + "not an Ogg Vorbis file\n" ); + break; + case OV_EVERSION: + printf( "sampleBuffer::decodeSampleOgg(): " + "vorbis version mismatch\n" ); + break; + case OV_EBADHEADER: + printf( "sampleBuffer::decodeSampleOgg(): " + "invalid Vorbis bitstream header\n" ); + break; + case OV_EFAULT: + printf( "sampleBuffer::decodeSampleOgg(): " + "internal logic fault\n" ); + break; + } + delete f; + return( 0 ); + } + + ov_pcm_seek( &vf, 0 ); + + _channels = ov_info( &vf, -1 )->channels; + ogg_int64_t total = ov_pcm_total( &vf, -1 ); + + _buf = new Sint16[total * _channels]; + int bitstream = 0; + long bytes_read = 0; + + do + { + bytes_read = ov_read( &vf, (char *)&_buf[frames * _channels], + ( total - frames ) * _channels * + sizeof( Sint16 ), + isLittleEndian()? 0 : 1, + sizeof( Sint16 ), 1, &bitstream ); + if( bytes_read < 0 ) + { + break; + } + frames += bytes_read / ( _channels * sizeof( Sint16 ) ); + } + while( bytes_read != 0 && bitstream == 0 ); + + ov_clear( &vf ); + + return( frames ); +} +#endif + + + + +#ifdef HAVE_SAMPLERATE_H +void sampleBuffer::initResampling( void ) +{ + m_srcState = createResamplingContext(); + m_srcData.end_of_input = 0; +} + + + + +void sampleBuffer::quitResampling( void ) +{ + destroyResamplingContext( m_srcState ); +} + + + + +SRC_STATE * sampleBuffer::createResamplingContext( void ) +{ + int error; + SRC_STATE * state; + if( ( state = src_new( +#ifdef HQ_SINC + SRC_SINC_MEDIUM_QUALITY, +#else + SRC_ZERO_ORDER_HOLD, +#endif + DEFAULT_CHANNELS, &error ) ) == NULL ) + { + printf( "Error: src_new() failed in sample_buffer.cpp!\n" ); + } + return( state ); +} + + + + +void sampleBuffer::destroyResamplingContext( SRC_STATE * _context ) +{ + src_delete( _context ); +} +#endif + + + + +bool FASTCALL sampleBuffer::play( sampleFrame * _ab, Uint32 _start_frame, + Uint32 _frames, float _freq, + bool _looped, + void * * _resampling_data ) +{ + mixer::inst()->clearAudioBuffer( _ab, _frames ); + + if( m_data == NULL || m_frames == 0 || m_endFrame == 0 || _frames == 0 ) + { + return( FALSE ); + } + + const float freq_factor = 1.0f / (BASE_FREQ / _freq); + const Sint16 freq_diff = static_cast( BASE_FREQ - _freq ); + + Uint32 frames_to_process = _frames; + + // calculate how many frames we have in requested pitch + const Uint32 total_frames_for_current_pitch = static_cast( ( + m_endFrame - m_startFrame ) / + freq_factor ); + if( total_frames_for_current_pitch == 0 ) + { + return( FALSE ); + } + // this holds the number of the first frame to play + const Uint32 play_frame = m_startFrame + ( _start_frame % + total_frames_for_current_pitch ); + + // this holds the number of remaining frames in current loop + Uint32 frames_for_loop = total_frames_for_current_pitch - + ( play_frame - m_startFrame ); + + // do we have frames left?? this is only important when not in + // looping-mode because in looping-mode we loop to start-frame... + if( _start_frame >= total_frames_for_current_pitch && _looped == FALSE ) + { + return( FALSE ); + } + + // make sure, data isn't accessed in any other way (e.g. deleting + // of this buffer...) + m_dataMutex.lock(); + + if( _looped == FALSE && frames_for_loop < frames_to_process ) + { + frames_to_process = frames_for_loop; + } + + // calc pointer of first frame + sampleFrame * start_frame = (sampleFrame *) m_data + + static_cast( play_frame * + freq_factor ); + sampleFrame * loop_start = start_frame; + + // check whether we have to change pitch... + if( freq_diff != 0 ) + { +#ifdef HAVE_SAMPLERATE_H + SRC_STATE * state = m_srcState; + if( _resampling_data != NULL ) + { + if( _start_frame == 0 ) + { + *_resampling_data = createResamplingContext(); + } + state = static_cast( *_resampling_data ); + } + m_srcData.data_in = start_frame[0]; + m_srcData.data_out = _ab[0]; + m_srcData.input_frames = static_cast( frames_for_loop * + freq_factor ); + m_srcData.output_frames = frames_to_process; + m_srcData.src_ratio = 1.0 / freq_factor; + int error; + if( ( error = src_process( state, &m_srcData ) ) ) + { + printf( "sampleBuffer: error while resampling: %s\n", + src_strerror( error ) ); + } +#else + Uint32 src_frame_base = 0; + // check whether we're in high-quality-mode + if( mixer::inst()->highQuality() ) + { + // we are, so let's use cubic interpolation... + for( Uint32 frame = 0; frame < frames_to_process; + ++frame ) + { + // current loop done? + if( _looped && ( frame-src_frame_base ) > + frames_for_loop ) + { + start_frame = loop_start; + src_frame_base = frame; + frames_for_loop = frames_to_process % + total_frames_for_current_pitch; + } + + const float src_frame_idx = frame*freq_factor; + Uint32 frame_num = static_cast( + src_frame_idx) - src_frame_base; + const float frac_pos = src_frame_idx - + static_cast( src_frame_idx ); + + // because of cubic interpolation we have to + // access start_frame[frame_num-1], so make + // sure we don't access data out of + // buffer-array-boundaries + if( frame_num == 0 && play_frame == 0 ) + { + frame_num = 1; + } + for ( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; + ++chnl ) + { + _ab[frame][chnl] = cubicInterpolate( + start_frame[frame_num-1][chnl], + start_frame[frame_num+0][chnl], + start_frame[frame_num+1][chnl], + start_frame[frame_num+2][chnl], + frac_pos ); + } + } + } + else + { + // just normal mode, so we can use linear + // interpolation... + for( Uint32 frame = 0; frame < frames_to_process; + ++frame ) + { + if( _looped && ( frame-src_frame_base ) > + frames_for_loop ) + { + start_frame = loop_start; + src_frame_base = frame; + frames_for_loop = frames_to_process % + total_frames_for_current_pitch; + } + const float src_frame_idx = frame * freq_factor; + const Uint32 frame_num = (Uint32)src_frame_idx - + src_frame_base + 0; + const float frac_pos = src_frame_idx - + (Uint32) src_frame_idx; + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; + ++chnl ) + { + _ab[frame][chnl] = linearInterpolate( + start_frame[frame_num][chnl], + start_frame[frame_num+1][chnl], + frac_pos ); + } + } + } +#endif + } + else + { + // we don't have to pitch, so we just copy the sample-data + // as is into pitched-copy-buffer + if( _looped && frames_for_loop < frames_to_process ) + { + Uint32 total_frames_copied = 0; + while( total_frames_copied < frames_to_process ) + { + memcpy( _ab[total_frames_copied], start_frame, + frames_for_loop * BYTES_PER_FRAME ); + total_frames_copied += frames_for_loop; + + // reset start_frame to start + start_frame = loop_start; + // and calculate frames for next loop + frames_for_loop = frames_to_process % + total_frames_for_current_pitch; + if( frames_for_loop > + frames_to_process-total_frames_copied ) + { + frames_for_loop = frames_to_process - + total_frames_copied; + } + } + } + else + { + memcpy( _ab, start_frame, + frames_to_process * BYTES_PER_FRAME ); + } + } + + m_dataMutex.unlock(); + + return( TRUE ); + +} + + + + +void sampleBuffer::drawWaves( QPainter & _p, QRect _dr, drawMethods _dm ) +{ + _p.setClipRect( _dr ); + _p.setPen (QColor(0x22, 0xFF, 0x44)); +#ifdef QT4 + // TODO: save and restore aa-settings + _p.setRenderHint( QPainter::Antialiasing ); +#endif + const int w = _dr.width(); + const int h = _dr.height(); + + const Uint16 y_base = h/2+_dr.y(); + const float y_space = h/2; + + if( m_data == NULL || m_frames == 0 ) + { + _p.drawLine( _dr.x(), y_base, _dr.x()+w, y_base ); + return; + } + else if( _dm == LINE_CONNECT ) + { +#ifdef QT4 + float old_x = _dr.x(); + float old_y[DEFAULT_CHANNELS] = { y_base, y_base }; + + float fpp = tMax( tMin( m_frames / (float)w, + 20.0f ), 1.0f ); + + for( float frame = 0; frame < m_frames; frame += fpp ) + { + const float x = frame*w / m_frames + _dr.x(); + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + const float y = y_base + + m_data[static_cast( frame )][chnl] * y_space; + _p.drawLine( + QLineF( old_x, old_y[chnl], x, y ) ); + old_y[chnl] = y; + } + old_x = x; + } +#else + int old_x = _dr.x(); + int old_y[DEFAULT_CHANNELS] = { y_base, y_base }; + + Uint32 fpp = tMax( tMin( m_frames / w, 20 ), + 1 ); + + for( Uint32 frame = 0; frame < m_frames; frame += fpp ) + { + const int x = static_cast( frame / + (float) m_frames * w ) + + _dr.x(); + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + const Uint16 y = y_base + + static_cast( m_data[frame][chnl]*y_space ); + _p.drawLine( old_x, old_y[chnl], x, y ); + old_y[chnl] = y; + } + old_x = x; + } +#endif + + } + else if( _dm == DOTS ) + { + for( Uint32 frame = 0; frame < m_frames; ++frame ) + { + const int x = frame*w / m_frames + _dr.x(); + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + _p.drawPoint( x, y_base + + static_cast( + m_data[frame][chnl]*y_space ) ); + } + } + } + _p.setClipping( FALSE ); +} + + + + +QString sampleBuffer::openAudioFile( void ) const +{ +#ifdef QT4 + QFileDialog ofd( NULL, tr( "Open audio file" ) ); +#else + QFileDialog ofd( QString::null, QString::null, NULL, "", TRUE ); + ofd.setWindowTitle( tr( "Open audio file" ) ); +#endif + + QString dir; + if( m_audioFile != "" ) + { + QString f = m_audioFile; + if( QFileInfo( f ).isRelative() ) + { + f = configManager::inst()->samplesDir() + f; + } +#ifdef QT4 + dir = QFileInfo( f ).absolutePath(); +#else + dir = QFileInfo( f ).dirPath( TRUE ); +#endif + } + else + { + dir = configManager::inst()->samplesDir(); + } + // change dir to position of previously opened file + ofd.setDirectory( dir ); + ofd.setFileMode( QFileDialog::ExistingFiles ); + + // set filters +#ifdef QT4 + QStringList types; + types << tr( "All Audio-Files (*.wav *.ogg *.voc *.aif *.aiff *.au " + "*.raw)" ) + << tr( "Wave-Files (*.wav)" ) + << tr( "OGG-Files (*.ogg)" ) + //<< tr( "MP3-Files (*.mp3)" ) + //<< tr( "MIDI-Files (*.mid)" ) + << tr( "VOC-Files (*.voc)" ) + << tr( "AIFF-Files (*.aif *.aiff)" ) + << tr( "AU-Files (*.au)" ) + << tr( "RAW-Files (*.raw)" ) + //<< tr( "FLAC-Files (*.flac)" ) + //<< tr( "MOD-Files (*.mod)" ) + ; + ofd.setFilters( types ); +#else + ofd.addFilter( tr( "All Audio-Files (*.wav *.ogg *.voc *.aif *.aiff " + "*.au *.raw)" ) ); + ofd.addFilter( tr( "Wave-Files (*.wav)" ) ); + ofd.addFilter( tr( "OGG-Files (*.ogg)" ) ); + //ofd.addFilter (tr("MP3-Files (*.mp3)")); + //ofd.addFilter (tr("MIDI-Files (*.mid)")); + ofd.addFilter( tr( "VOC-Files (*.voc)" ) ); + ofd.addFilter( tr( "AIFF-Files (*.aif *.aiff)" ) ); + ofd.addFilter( tr( "AU-Files (*.au)" ) ); + ofd.addFilter( tr( "RAW-Files (*.raw)" ) ); + //ofd.addFilter (tr("FLAC-Files (*.flac)")); + //ofd.addFilter (tr("MOD-Files (*.mod)")); + ofd.setSelectedFilter( tr( "All Audio-Files (*.wav *.ogg *.voc *.aif " + "*.aiff *.au *.raw)" ) ); +#endif + if( m_audioFile != "" ) + { + // select previously opened file + ofd.selectFile( QFileInfo( m_audioFile ).fileName() ); + } + + if ( ofd.exec () == QDialog::Accepted ) + { + if( ofd.selectedFiles().isEmpty() ) + { + return( "" ); + } + QString sf = ofd.selectedFiles()[0]; + if( !QFileInfo( sf ).isRelative() ) + { +#if QT_VERSION >= 0x030100 + sf = sf.replace( configManager::inst()->samplesDir(), + "" ); +#else + sf = sf.replace( QRegExp( + configManager::inst()->samplesDir() ), + "" ); +#endif + } + return( sf ); + } + + return( "" ); +} + + + + +void sampleBuffer::setAudioFile( const QString & _audio_file ) +{ + m_audioFile = _audio_file; + // try to make path of audio-file relative if it's posated + // within LMMS-working-dir + if( !QFileInfo( m_audioFile ).isRelative() ) + { +#if QT_VERSION >= 0x030100 + m_audioFile = m_audioFile.replace( + configManager::inst()->samplesDir(), "" ); +#else + m_audioFile = m_audioFile.replace( + QRegExp( configManager::inst()->samplesDir() ), + "" ); +#endif + } + update(); +} + + + + +void sampleBuffer::setStartFrame( Uint32 _s ) +{ + // don't set this parameter while playing + m_dataMutex.lock(); + m_startFrame = _s; + m_dataMutex.unlock(); +} + + + + +void sampleBuffer::setEndFrame( Uint32 _e ) +{ + // don't set this parameter while playing + m_dataMutex.lock(); + m_endFrame = _e; + m_dataMutex.unlock(); +} + + + + +void sampleBuffer::setAmplification( float _a ) +{ + m_amplification = _a; + update( TRUE ); +} + + + + +void sampleBuffer::setReversed( bool _on ) +{ + m_reversed = _on; + update( TRUE ); +} + + + + +void sampleBuffer::deleteResamplingData( void * * _ptr ) +{ +#ifdef HAVE_SAMPLERATE_H +#ifdef LMMS_DEBUG + assert( _ptr != NULL ); + assert( *_ptr != NULL ); +#endif + destroyResamplingContext( static_cast( *_ptr ) ); +#endif + *_ptr = NULL; +} + + + +#include "sample_buffer.moc" + diff --git a/src/midi/midi_alsa_raw.cpp b/src/midi/midi_alsa_raw.cpp new file mode 100644 index 0000000000..a9062c5910 --- /dev/null +++ b/src/midi/midi_alsa_raw.cpp @@ -0,0 +1,233 @@ +/* + * midi_alsa_raw.cpp - midi-device-driver for RawMIDI via ALSA + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "midi_alsa_raw.h" +#include "config_mgr.h" +#include "templates.h" + + +#ifdef ALSA_SUPPORT + + +midiALSARaw::midiALSARaw( channelTrack * _ct ) : + midiDevice( _ct ), + QThread(), + m_inputp( &m_input ), + m_outputp( &m_output ), + m_quit( FALSE ) +{ + int err; + if( ( err = snd_rawmidi_open( m_inputp, m_outputp, +#ifdef QT4 + probeDevice().toAscii().constData(), +#else + probeDevice().ascii(), +#endif + 0 ) ) < 0 ) + { + printf( "cannot open MIDI-device: %s\n", snd_strerror( err ) ); + return; + } + + snd_rawmidi_read( m_input, NULL, 0 ); + + snd_rawmidi_nonblock( m_input, 1 ); + m_npfds = snd_rawmidi_poll_descriptors_count( m_input ); + m_pfds = new pollfd[m_npfds]; + snd_rawmidi_poll_descriptors( m_input, m_pfds, m_npfds ); + + start( +#if QT_VERSION >= 0x030200 + QThread::LowPriority +#endif + ); +} + + + + +midiALSARaw::~midiALSARaw() +{ + if( running() ) + { + m_quit = TRUE; + wait( 500 ); + terminate(); + + snd_rawmidi_close( m_input ); + snd_rawmidi_close( m_output ); + delete[] m_pfds; + } +} + + + + +QString midiALSARaw::probeDevice( void ) +{ + QString dev = configManager::inst()->value( "midialsa", "device" ); + if( dev == "" ) + { + if( getenv( "MIDIDEV" ) != NULL ) + { + return( getenv( "MIDIDEV" ) ); + } + return( "default" ); + } + return( dev ); +} + + + + +void midiALSARaw::sendByte( Uint8 _c ) +{ + snd_rawmidi_write( m_output, &_c, sizeof( _c ) ); +} + + + + +void midiALSARaw::run( void ) +{ + Uint8 buf[128]; + //int cnt = 0; + while( m_quit == FALSE ) + { + msleep( 5 ); // must do that, otherwise this thread takes + // too much CPU-time, even with LowPriority... + int err = poll( m_pfds, m_npfds, 10000 ); + if( err < 0 && errno == EINTR ) + { + printf( "midiALSARaw::run(): Got EINTR while " + "polling. Will stop polling MIDI-events from " + "MIDI-port.\n" ); + break; + } + if( err < 0 ) + { + printf( "poll failed: %s\nWill stop polling " + "MIDI-events from MIDI-port.\n", + strerror( errno ) ); + break; + } + if( err == 0 ) + { + //printf( "there seems to be no active MIDI-device %d\n", ++cnt ); + continue; + } + unsigned short revents; + if( ( err = snd_rawmidi_poll_descriptors_revents( + m_input, m_pfds, m_npfds, &revents ) ) < 0 ) + { + printf( "cannot get poll events: %s\nWill stop polling " + "MIDI-events from MIDI-port.\n", + snd_strerror( errno ) ); + break; + } + if( revents & ( POLLERR | POLLHUP ) ) + { + printf( "POLLERR or POLLHUP\n" ); + break; + } + if( !( revents & POLLIN ) ) + { + continue; + } + err = snd_rawmidi_read( m_input, buf, sizeof( buf ) ); + if( err == -EAGAIN ) + { + continue; + } + if( err < 0 ) + { + printf( "cannot read from port \"%s\": %s\nWill stop " + "polling MIDI-events from MIDI-port.\n", + /*port_name*/"default", snd_strerror( err ) ); + break; + } + if( err == 0 ) + continue; + for( int i = 0; i < err; ++i ) + { + const midiEvent * midi_event = parseData( buf[i] ); + if( midi_event != NULL ) + { + processInEvent( *midi_event ); + } + } + } + +} + + + + +midiALSARaw::setupWidget::setupWidget( QWidget * _parent ) : + midiDevice::setupWidget( midiALSARaw::name(), _parent ) +{ + m_device = new QLineEdit( midiALSARaw::probeDevice(), this ); + m_device->setGeometry( 10, 20, 160, 20 ); + + QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); + dev_lbl->setGeometry( 10, 40, 160, 10 ); +} + + + + +midiALSARaw::setupWidget::~setupWidget() +{ +} + + + + +void midiALSARaw::setupWidget::saveSettings( void ) +{ + configManager::inst()->setValue( "midialsa", "device", + m_device->text() ); +} + + + +#endif + diff --git a/src/midi/midi_mapper.cpp b/src/midi/midi_mapper.cpp new file mode 100644 index 0000000000..46d82466e5 --- /dev/null +++ b/src/midi/midi_mapper.cpp @@ -0,0 +1,222 @@ +/* + * midi_mapper.cpp - MIDI-mapper for any midiDevice + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + + +#include "midi_mapper.h" + +#ifdef QT4 + +#include + +#else + +#include + +#define indexOf find + +#endif + + +midiMapper::midiMapper( const QString & _map ) : + m_drumsetChannel( 0 ), + m_drumsetPatch( 0 ) +{ + // default mappings + for( Uint8 i = 0; i < MIDI_PROGRAMS; ++i ) + { + m_patchMap[i].first = i; + } + for( Uint8 i = 0; i < MIDI_KEYS; ++i ) + { + m_drumsetKeyMap[i].first = i; + } + for( Uint8 i = 0; i < MIDI_CHANNELS; ++i ) + { + m_channelMap[i] = i; + } + QFile map( _map ); +#ifdef QT4 + if( !map.open( QIODevice::ReadOnly ) ) +#else + if( !map.open( IO_ReadOnly ) ) +#endif + { + return; + } + while( !map.atEnd() ) + { + char buf[1024]; + int len = map.readLine( buf, sizeof( buf ) ); + if( len <= 0 ) + { + continue; + } + QString line( buf ); + line.replace( '\n', "" ); + if( line.left( 6 ) == "DEFINE" ) + { + if( line.section( ' ', 1, 1 ) == "PATCHMAP" ) + { + readPatchMap( map ); + } + else if( line.section( ' ', 1, 1 ) == "KEYMAP" && + line.section( ' ', 2, 2 ) == + "\"Drumset\"" ) + { + readDrumsetKeyMap( map ); + } + else if( line.section( ' ', 1, 1 ) == "CHANNELMAP" ) + { + readChannelMap( map ); + } + } + } +} + + + + +midiMapper::~midiMapper() +{ +} + + + + +void midiMapper::readPatchMap( QFile & _f ) +{ + Uint8 prog_idx = 0; + + while( !_f.atEnd() && prog_idx < MIDI_PROGRAMS ) + { + char buf[1024]; + int len = _f.readLine( buf, sizeof( buf ) ); + if( len <= 0 ) + { + continue; + } + QString line( buf ); + line.replace( '\n', "" ); + if( line.left( 3 ) == "END" ) + { + return; + } + if( QString( line ).replace( ' ', "" )[0] == '#' ) + { + continue; + } + m_patchMap[prog_idx].first = line.section( '=', 1, 1 ).toInt(); + m_patchMap[prog_idx].second = line.section( '=', 0, 0 ). + replace( ' ', "" ); + ++prog_idx; + } +} + + + + +void midiMapper::readDrumsetKeyMap( QFile & _f ) +{ + Uint8 key = 0; + while( !_f.atEnd() ) + { + char buf[1024]; + int len = _f.readLine( buf, sizeof( buf ) ); + if( len <= 0 ) + { + continue; + } + QString line( buf ); + line.replace( '\n', "" ); + if( line.left( 3 ) == "END" ) + { + return; + } + if( QString( line ).replace( ' ', "" )[0] == '#' ) + { + continue; + } + if( line[4] != '=' ) + { + m_drumsetKeyMap[key].first = line.section( '=', 1, 1 ). + toInt(); + + m_drumsetKeyMap[key].second = line.mid( 4 ). + section( '=', 0, 0 ). + section( ' ', 1, 1 ). + replace( ' ', "" ); + } + ++key; + } +} + + + + +void midiMapper::readChannelMap( QFile & _f ) +{ + while( !_f.atEnd() ) + { + char buf[1024]; + int len = _f.readLine( buf, sizeof( buf ) ); + if( len <= 0 ) + { + continue; + } + QString line( buf ); + line.replace( '\n', "" ); + if( line.left( 3 ) == "END" ) + { + return; + } + if( QString( line ).replace( ' ', "" )[0] == '#' ) + { + continue; + } + Uint8 ch = line.section( ' ', 0, 0 ).toInt(); + Uint8 mch = line.section( '=', 1, 1 ).mid( 1 ). + section( ' ', 0, 0 ). + toInt(); + if( ch < MIDI_CHANNELS && mch < MIDI_CHANNELS ) + { + m_channelMap[ch] = mch; + if( line.contains( QRegExp( "Keymap *\"Drumset\"" ) ) ) + { + m_drumsetChannel = mch; + int fp = line.indexOf( "ForcePatch" ); + if( fp != -1 ) + { + m_drumsetPatch = line.mid( fp ). + section( ' ', 1, 1 ). + toInt(); + } + } + } + } +} + + + +#undef indexOf + diff --git a/src/midi/midi_oss.cpp b/src/midi/midi_oss.cpp new file mode 100644 index 0000000000..3efb32835d --- /dev/null +++ b/src/midi/midi_oss.cpp @@ -0,0 +1,164 @@ +/* + * midi_oss.cpp - simple midi-device-driver for OSS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "midi_oss.h" +#include "config_mgr.h" +#include "templates.h" + + + +midiOSS::midiOSS( channelTrack * _ct ) : + midiDevice( _ct ), + QThread(), + m_midiDev( probeDevice() ), + m_quit( FALSE ) +{ + // only start thread, if opening of MIDI-device is successful, + // otherwise isRunning()==FALSE indicates error +#ifdef QT4 + if( m_midiDev.open( QIODevice::ReadWrite ) || + m_midiDev.open( QIODevice::ReadOnly ) ) +#else + if( m_midiDev.open( IO_ReadWrite ) || m_midiDev.open( IO_ReadOnly ) ) +#endif + { + start( +#if QT_VERSION >= 0x030200 + QThread::LowPriority +#endif + ); + } +} + + + + +midiOSS::~midiOSS() +{ + m_quit = TRUE; + wait( 500 ); + terminate(); +} + + + + +QString midiOSS::probeDevice( void ) +{ + QString dev = configManager::inst()->value( "midioss", "device" ); + if( dev == "" ) + { + if( getenv( "MIDIDEV" ) != NULL ) + { + return( getenv( "MIDIDEV" ) ); + } + return( "/dev/midi" ); + } + return( dev ); +} + + + + +void midiOSS::sendByte( Uint8 _c ) +{ +#ifdef QT4 + m_midiDev.putChar( _c ); +#else + m_midiDev.putch( _c ); +#endif +} + + + + +void midiOSS::run( void ) +{ + while( m_quit == FALSE ) + { +#ifdef QT4 + char c; + if( !m_midiDev.getChar( &c ) ) + { + continue; + } + const midiEvent * midi_event = parseData( c ); +#else + const midiEvent * midi_event = parseData( m_midiDev.getch() ); +#endif + if( midi_event != NULL ) + { + processInEvent( *midi_event ); + } + } +} + + + + + +midiOSS::setupWidget::setupWidget( QWidget * _parent ) : + midiDevice::setupWidget( midiOSS::name(), _parent ) +{ + m_device = new QLineEdit( midiOSS::probeDevice(), this ); + m_device->setGeometry( 10, 20, 160, 20 ); + + QLabel * dev_lbl = new QLabel( tr( "DEVICE" ), this ); + dev_lbl->setFont( pointSize<6>( dev_lbl->font() ) ); + dev_lbl->setGeometry( 10, 40, 160, 10 ); +} + + + + +midiOSS::setupWidget::~setupWidget() +{ +} + + + + +void midiOSS::setupWidget::saveSettings( void ) +{ + configManager::inst()->setValue( "midioss", "device", + m_device->text() ); +} + + diff --git a/src/tracks/bb_track.cpp b/src/tracks/bb_track.cpp new file mode 100644 index 0000000000..ebfab85ddc --- /dev/null +++ b/src/tracks/bb_track.cpp @@ -0,0 +1,509 @@ +/* + * bb_track.cpp - implementation of class bbTrack + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include +#include + +#endif + + +#include "bb_track.h" +#include "song_editor.h" +#include "bb_editor.h" +#include "templates.h" +#include "name_label.h" +#include "embed.h" +#include "rename_dialog.h" + +#include "bb_track.moc" + + +QMap bbTrack::s_bbNums; + + +bbTCO::bbTCO( track * _track, const QColor & _c ) : + trackContentObject( _track ), + m_name( ( dynamic_cast( _track ) != NULL ) ? + dynamic_cast( _track )->trackLabel()->text() : + QString( "" ) ), + m_color( _c.isValid() ? _c : QColor( 64, 128, 255 ) ) +{ + //setPaletteBackgroundColor( QColor( 64, 128, 255 ) ); +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + tact t = bbEditor::inst()->lengthOfBB( + bbTrack::numOfBBTrack( getTrack() ) ); + if( t > 0 ) + { + changeLength( midiTime( t, 0 ) ); + } +} + + + + +bbTCO::~bbTCO() +{ +} + + + + +void bbTCO::movePosition( const midiTime & _pos ) +{ + // bb-playlist-entries are always aligned on tact-boundaries + trackContentObject::movePosition( midiTime( _pos.getTact(), 0 ) ); +} + + + + +void bbTCO::changeLength( const midiTime & _length ) +{ + // the length of bb-playlist-entries is always a multiple of one tact + trackContentObject::changeLength( midiTime( _length.getTact(), 0 ) ); +} + + + + +void bbTCO::constructContextMenu( QMenu * _cm ) +{ +#ifdef QT4 + QAction * a = new QAction( embed::getIconPixmap( "bb_track" ), + tr( "Open in Beat+Bassline-Editor" ), + _cm ); + _cm->insertAction( _cm->actions()[0], a ); + connect( a, SIGNAL( triggered( bool ) ), this, + SLOT( openInBBEditor( bool ) ) ); +#else + _cm->insertItem( embed::getIconPixmap( "bb_track" ), + tr( "Open in Beat+Bassline-Editor" ), + this, SLOT( openInBBEditor() ), + 0, -1, 0 ); +#endif +#ifdef QT4 + _cm->insertSeparator( _cm->actions()[1] ); +#else + _cm->insertSeparator( 1 ); +#endif +#ifdef QT4 + _cm->addSeparator(); +#else + _cm->insertSeparator(); +#endif + _cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ), + this, SLOT( resetName() ) ); + _cm->addAction( embed::getIconPixmap( "rename" ), tr( "Change name" ), + this, SLOT( changeName() ) ); + _cm->addAction( embed::getIconPixmap( "colorize" ), + tr( "Change color" ), this, SLOT( changeColor() ) ); +} + + + + +void bbTCO::mouseDoubleClickEvent( QMouseEvent * ) +{ + openInBBEditor(); +} + + + + +void bbTCO::paintEvent( QPaintEvent * ) +{ + QColor col = m_color; + if( getTrack()->muted() ) + { + col = QColor( 160, 160, 160 ); + } +#ifdef QT4 + QPainter p( this ); + // TODO: set according brush/pen for gradient! + p.fillRect( rect(), col ); +#else + // create pixmap for whole widget + QPixmap pm( rect().size() ); + // and a painter for it + QPainter p( &pm ); + + // COOL gradient ;-) + for( int y = 0; y < height(); ++y ) + { + p.setPen( col.light( 130 - y * 60 / height() ) ); + p.drawLine( 0, y, width(), y ); + } + //pm.fill( col ); +#endif + + tact t = bbEditor::inst()->lengthOfBB( bbTrack::numOfBBTrack( + getTrack() ) ); + if( length().getTact() > 1 && t > 0 ) + { + for( int x = TCO_BORDER_WIDTH + static_cast( t * + pixelsPerTact() ); x < width(); + x += static_cast( t * pixelsPerTact() ) ) + { + p.setPen( col.light( 80 ) ); + p.drawLine( x, 1, x, 5 ); + p.setPen( col.light( 120 ) ); + p.drawLine( x, height() - 6, x, height() - 2 ); + } + } + + p.setPen( col.dark() ); + p.drawRect( 0, 0, width(), height() ); + + p.setFont( pointSize<7>( p.font() ) ); + p.setPen( QColor( 0, 0, 0 ) ); + p.drawText( 2, QFontMetrics( p.font() ).height() - 1, m_name ); + +#ifndef QT4 + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void bbTCO::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement bbtco_de = _doc.createElement( nodeName() ); + bbtco_de.setAttribute( "name", m_name ); + if( _parent.nodeName() == "clipboard" ) + { + bbtco_de.setAttribute( "pos", QString::number( -1 ) ); + } + else + { + bbtco_de.setAttribute( "pos", + QString::number( startPosition() ) ); + } + bbtco_de.setAttribute( "len", QString::number( length() ) ); + bbtco_de.setAttribute( "color", QString::number( m_color.rgb() ) ); + _parent.appendChild( bbtco_de ); +} + + + + +void bbTCO::loadSettings( const QDomElement & _this ) +{ + m_name = _this.attribute( "name" ); + if( _this.attribute( "pos" ).toInt() >= 0 ) + { + movePosition( _this.attribute( "pos" ).toInt() ); + } + changeLength( _this.attribute( "len" ).toInt() ); + if( _this.attribute( "color" ).toUInt() != 0 ) + { + m_color.setRgb( _this.attribute( "color" ).toUInt() ); + } +} + + + + +void bbTCO::openInBBEditor( bool ) +{ + bbEditor::inst()->setCurrentBB( bbTrack::numOfBBTrack( getTrack() ) ); + bbEditor::inst()->show(); + bbEditor::inst()->setFocus(); +} + + + + +void bbTCO::openInBBEditor( void ) +{ + openInBBEditor( FALSE ); +} + + + + +void bbTCO::resetName( void ) +{ + if( dynamic_cast( getTrack() ) != NULL ) + { + m_name = dynamic_cast( getTrack() )-> + trackLabel()->text(); + } +} + + + + +void bbTCO::changeName( void ) +{ + renameDialog rename_dlg( m_name ); + rename_dlg.exec(); +} + + + + +void bbTCO::changeColor( void ) +{ + QColor _new_color = QColorDialog::getColor( m_color ); + if( _new_color.isValid() && _new_color != m_color ) + { + m_color = _new_color; + songEditor::inst()->setModified(); + update(); + } +} + + + + + +bbTrack::bbTrack( trackContainer * _tc ) + : track( _tc ) +{ + getTrackWidget()->setFixedHeight( 32 ); + + csize bbNum = s_bbNums.size(); + bbInfoStruct bis = { bbNum, "" }; + s_bbNums[this] = bis; + m_trackLabel = new nameLabel( tr( "Beat/Bassline %1" ).arg( bbNum ), + getTrackSettingsWidget(), + embed::getIconPixmap( "bb_track" ) ); + m_trackLabel->setGeometry( 1, 1, DEFAULT_SETTINGS_WIDGET_WIDTH-2, 29 ); + m_trackLabel->show(); + connect( m_trackLabel, SIGNAL( clicked() ), this, + SLOT( clickedTrackLabel() ) ); + bbEditor::inst()->setCurrentBB( bbNum ); + + _tc->updateAfterTrackAdd(); +} + + + + +bbTrack::~bbTrack() +{ + csize bb = s_bbNums[this].num; + bbEditor::inst()->removeBB( bb ); + for( QMap::iterator it = + s_bbNums.begin(); it != s_bbNums.end(); ++it ) + { +#ifdef QT4 + if( it.value().num > bb ) + { + --it.value().num; + } +#else + if( it.data().num > bb ) + { + --it.data().num; + } +#endif + } + s_bbNums.remove( this ); +} + + + + +track::trackTypes bbTrack::trackType( void ) const +{ + return( BB_TRACK ); +} + + + +// play _frames frames of given TCO within starting with _start/_start_frame +bool FASTCALL bbTrack::play( const midiTime & _start, Uint32 _start_frame, + Uint32 _frames, Uint32 _frame_base, + Sint16 _tco_num ) +{ + if( _tco_num >= 0 ) + { + return( bbEditor::inst()->play( _start, _start_frame, _frames, + _frame_base, + s_bbNums[this].num ) ); + } + + vlist tcos; + getTCOsInRange( tcos, _start, _start +static_cast( _frames * + 64 / songEditor::inst()->framesPerTact() ) ); + + if ( tcos.size() == 0 ) + { + return( FALSE ); + } + + midiTime lastPosition; + midiTime lastLen; + for( vlist::iterator it = tcos.begin(); + it != tcos.end(); ++it ) + { + if( ( *it )->startPosition() >= lastPosition ) + { + lastPosition = ( *it )->startPosition(); + lastLen = ( *it )->length(); + } + } + if( _start - lastPosition < lastLen ) + { + return( bbEditor::inst()->play( _start - lastPosition, + _start_frame, _frames, + _frame_base, + s_bbNums[this].num ) ); + } + return( FALSE ); +} + + + + +trackContentObject * bbTrack::createTCO( const midiTime & _pos ) +{ + // if we're creating a new bbTCO, we colorize it according to the + // previous bbTCO, so we have to get all TCOs from 0 to _pos and + // pickup the last and take the color if it + vlist tcos; + getTCOsInRange( tcos, 0, _pos ); + if( tcos.size() > 0 && dynamic_cast( tcos.back() ) != NULL ) + { + return( new bbTCO( this, + dynamic_cast( tcos.back() )->color() ) ); + + } + return( new bbTCO( this ) ); +} + + + + + + +void bbTrack::saveTrackSpecificSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement bbt_de = _doc.createElement( nodeName() ); + bbt_de.setAttribute( "name", m_trackLabel->text() ); +/* bbt_de.setAttribute( "current", s_bbNums[this].num == + bbEditor::inst()->currentBB() );*/ + _parent.appendChild( bbt_de ); + if( s_bbNums[this].num == 0 && + _parent.parentNode().nodeName() != "clone" ) + { + bbEditor::inst()->saveSettings( _doc, bbt_de ); + } +} + + + + +void bbTrack::loadTrackSpecificSettings( const QDomElement & _this ) +{ + m_trackLabel->setText( _this.attribute( "name" ) ); + if( _this.firstChild().isElement() ) + { + bbEditor::inst()->loadSettings( + _this.firstChild().toElement() ); + } +/* doesn't work yet because bbTrack-ctor also sets current bb so if + bb-tracks are created after this function is called, this doesn't + help at all.... + if( _this.attribute( "current" ).toInt() ) + { + bbEditor::inst()->setCurrentBB( s_bbNums[this].num ); + }*/ +} + + + + +// return pointer to bbTrack specified by _bb_num +bbTrack * bbTrack::findBBTrack( csize _bb_num ) +{ + for( QMap::iterator it = + s_bbNums.begin(); + it != s_bbNums.end(); ++it ) + { +#ifdef QT4 + if( it.value().num == _bb_num ) +#else + if( it.data().num == _bb_num ) +#endif + { + return( it.key() ); + } + } + return( NULL ); +} + + + + +csize bbTrack::numOfBBTrack( track * _track ) +{ + if( dynamic_cast( _track ) != NULL ) + { + return( s_bbNums[dynamic_cast( _track )].num ); + } + return( 0 ); +} + + + + +void bbTrack::swapBBTracks( track * _track1, track * _track2 ) +{ + bbTrack * t1 = dynamic_cast( _track1 ); + bbTrack * t2 = dynamic_cast( _track2 ); + if( t1 != NULL && t2 != NULL ) + { + qSwap( s_bbNums[t1].num, s_bbNums[t2].num ); + bbEditor::inst()->swapBB( s_bbNums[t1].num, s_bbNums[t2].num ); + bbEditor::inst()->setCurrentBB( s_bbNums[t2].num ); + } +} + + + + +void bbTrack::clickedTrackLabel( void ) +{ + bbEditor::inst()->setCurrentBB( s_bbNums[this].num ); + bbEditor::inst()->show(); +} diff --git a/src/tracks/pattern.cpp b/src/tracks/pattern.cpp new file mode 100644 index 0000000000..c228da7580 --- /dev/null +++ b/src/tracks/pattern.cpp @@ -0,0 +1,1027 @@ +/* + * pattern.cpp - implementation of class pattern which holds notes + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include + +#endif + + +#include "pattern.h" +#include "channel_track.h" +#include "templates.h" +#include "embed.h" +#include "piano_roll.h" +#include "track_container.h" +#include "rename_dialog.h" +#include "sample_buffer.h" +#include "audio_sample_recorder.h" +#include "song_editor.h" +#include "tooltip.h" + + +QPixmap * pattern::s_patternBg = NULL; +QPixmap * pattern::s_stepBtnOn = NULL; +QPixmap * pattern::s_stepBtnOff = NULL; +QPixmap * pattern::s_stepBtnOffLight = NULL; +QPixmap * pattern::s_frozen = NULL; + + + + +pattern::pattern ( channelTrack * _channel_track ) : + trackContentObject( _channel_track ), + m_channelTrack( _channel_track ), + m_patternType( BEAT_PATTERN ), + m_name( _channel_track->name() ), + m_frozenPatternMutex(), + m_frozenPattern( NULL ), + m_freezeRecorder( NULL ), + m_freezeStatusDialog( NULL ), + m_freezeStatusUpdateTimer( NULL ) +{ + initPixmaps(); + + setFixedHeight( s_patternBg->height() + 4 ); + +#ifndef QT4 + // set background-mode for flicker-free redraw + setBackgroundMode( Qt::NoBackground ); +#endif + + if( m_patternType == BEAT_PATTERN ) + { + for( int i = 0; i < MAX_BEATS_PER_TACT; ++i ) + { + m_notes.push_back( new note( midiTime( 0 ), + midiTime( i*4 ) ) ); + } + } + + changeLength( length() ); + + setAutoResizeEnabled( FALSE ); + + toolTip::add( this, + tr( "double-click to open this pattern in piano-roll" ) ); +} + + + + +pattern::pattern( const pattern & _pat_to_copy ) : + trackContentObject( _pat_to_copy.m_channelTrack ), + m_channelTrack( _pat_to_copy.m_channelTrack ), + m_patternType( _pat_to_copy.m_patternType ), + m_name( "" ), + m_frozenPatternMutex(), + m_frozenPattern( NULL ) +{ + initPixmaps(); + + for( noteVector::const_iterator it = _pat_to_copy.m_notes.begin(); + it != _pat_to_copy.m_notes.end(); ++it ) + { + m_notes.push_back( new note( **it ) ); + } + + setFixedHeight( s_patternBg->height() + 4 ); + +#ifndef QT4 + // set background-mode for flicker-free redraw + setBackgroundMode( Qt::NoBackground ); +#endif + + changeLength( length() ); + setAutoResizeEnabled( FALSE ); +} + + + + +pattern::~pattern() +{ + if( pianoRoll::inst()->currentPattern() == this ) + { + pianoRoll::inst()->setCurrentPattern( NULL ); + } + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + delete *it; + } + + m_notes.clear(); + + m_frozenPatternMutex.lock(); + delete m_frozenPattern; + m_frozenPatternMutex.unlock(); +} + + + + +void pattern::initPixmaps( void ) +{ + if( s_patternBg == NULL ) + { + s_patternBg = new QPixmap( embed::getIconPixmap( + "pattern_bg" ) ); + } + if( s_stepBtnOn == NULL ) + { + s_stepBtnOn = new QPixmap( embed::getIconPixmap( + "step_btn_on" ) ); + } + if( s_stepBtnOff == NULL ) + { + s_stepBtnOff = new QPixmap( embed::getIconPixmap( + "step_btn_off" ) ); + } + if( s_stepBtnOffLight == NULL ) + { + s_stepBtnOffLight = new QPixmap( embed::getIconPixmap( + "step_btn_off_light" ) ); + } + if( s_frozen == NULL ) + { + s_frozen = new QPixmap( embed::getIconPixmap( "frozen" ) ); + } +} + + + + +void pattern::movePosition( const midiTime & _pos ) +{ + // patterns are always aligned on tact-boundaries + trackContentObject::movePosition( midiTime( _pos.getTact(), 0 ) ); +} + + + + +void pattern::constructContextMenu( QMenu * _cm ) +{ +#ifdef QT4 + QAction * a = new QAction( embed::getIconPixmap( "piano" ), + tr( "Open in piano-roll" ), _cm ); + _cm->insertAction( _cm->actions()[0], a ); + connect( a, SIGNAL( triggered( bool ) ), this, + SLOT( openInPianoRoll( bool ) ) ); +#else + _cm->insertItem( embed::getIconPixmap( "piano" ), + tr( "Open in piano-roll" ), + this, SLOT( openInPianoRoll() ), + 0, -1, 0 ); +#endif +#ifdef QT4 + _cm->insertSeparator( _cm->actions()[1] ); +#else + _cm->insertSeparator( 1 ); +#endif + +#ifdef QT4 + _cm->addSeparator(); +#else + _cm->insertSeparator(); +#endif + _cm->addAction( embed::getIconPixmap( "edit_erase" ), + tr( "Clear all notes" ), this, SLOT( clear() ) ); +#ifdef QT4 + _cm->addSeparator(); +#else + _cm->insertSeparator(); +#endif + _cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ), + this, SLOT( resetName() ) ); + _cm->addAction( embed::getIconPixmap( "rename" ), tr( "Change name" ), + this, SLOT( changeName() ) ); +#ifdef QT4 + _cm->addSeparator(); +#else + _cm->insertSeparator(); +#endif + _cm->addAction( embed::getIconPixmap( "freeze" ), + ( m_frozenPattern != NULL )? tr( "Refreeze" ) : tr( "Freeze" ), + this, SLOT( freeze() ) ); + _cm->addAction( embed::getIconPixmap( "unfreeze" ), tr( "Unfreeze" ), + this, SLOT( unfreeze() ) ); +} + + + + +void pattern::ensureBeatNotes( void ) +{ + // make sure, that all step-note exist + for( int i = 0; i < MAX_BEATS_PER_TACT; ++i ) + { + bool found = FALSE; + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + if( ( *it )->pos() == i * 4 && ( *it )->length() <= 0 ) + { + found = TRUE; + break; + } + } + if( found == FALSE ) + { + addNote( note( midiTime( 0 ), midiTime( i * 4 ) ) ); + } + } +} + + + + +void pattern::paintEvent( QPaintEvent * ) +{ + changeLength( length() ); + +#ifdef QT4 + QPainter p( this ); +#else + // create pixmap for whole widget + QPixmap pm( rect().size() ); + + // and a painter for it + QPainter p( &pm ); +#endif + + for( Sint16 x = 2; x < width() - 1; x += 2 ) + { + p.drawPixmap( x, 2, *s_patternBg ); + } + p.setPen( QColor( 57, 69, 74 ) ); + p.drawLine( 0, 0, width(), 0 ); + p.drawLine( 0, 0, 0, height() ); + p.setPen( QColor( 120, 130, 140 ) ); + p.drawLine( 0, height() - 1, width() - 1, height() - 1 ); + p.drawLine( width() - 1, 0, width() - 1, height() - 1 ); + + p.setPen( QColor( 0, 0, 0 ) ); + p.drawRect( 1, 1, width() - 2, height() - 2 ); + + float ppt = pixelsPerTact(); + + if( m_patternType == pattern::MELODY_PATTERN ) + { + Sint16 central_key = 0; + if( m_notes.size() > 0 ) + { + // first determine the central tone so that we can + // display the area where most of the m_notes are + Sint16 total_notes = 0; + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + if( ( *it )->length() > 0 ) + { + central_key += ( *it )->key(); + ++total_notes; + } + } + + if( total_notes > 0 ) + { + central_key = central_key / total_notes; + + Sint16 central_y = s_patternBg->height() / 2; + Sint16 y_base = central_y+TCO_BORDER_WIDTH-1; + + const Sint16 x_base = TCO_BORDER_WIDTH; + + p.setPen( QColor( 0, 0, 0 ) ); + for( tact tact_num = 1; tact_num < + length().getTact(); ++tact_num ) + { + p.drawLine( x_base + static_cast( + ppt*tact_num ), + TCO_BORDER_WIDTH, + x_base + + static_cast( ppt * tact_num ), + height() - 2 * + TCO_BORDER_WIDTH ); + } + if( getTrack()->muted() ) + { + p.setPen( QColor( 160, 160, 160 ) ); + } + else if( m_frozenPattern != NULL ) + { + p.setPen( QColor( 0x00, 0xE0, 0xFF ) ); + } + else + { + p.setPen( QColor( 0xFF, 0xB0, 0x00 ) ); + } + + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + Sint16 y_pos = central_key - + ( *it )->key(); + + if( ( *it )->length() > 0 && + y_pos > -central_y && + y_pos < central_y ) + { + Sint16 x1 = 2 * x_base + + static_cast( ( *it )->pos() * ppt / 64 ); + Sint16 x2 = x1 + + static_cast( ( *it )->length() * ppt / 64 ); + p.drawLine( x1, y_base+y_pos, + x2, + y_base+y_pos ); + + } + } + } + } + } + else if( m_patternType == pattern::BEAT_PATTERN && ppt >= 192 ) + { + QPixmap stepon; + QPixmap stepoff; + QPixmap stepoffl; +#ifdef QT4 + stepon = s_stepBtnOn->scaled( width() / 16, + s_stepBtnOn->height(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation ); + stepoff = s_stepBtnOff->scaled( width() / 16, + s_stepBtnOff->height(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation ); + stepoffl = s_stepBtnOffLight->scaled( width() / 16, + s_stepBtnOffLight->height(), + Qt::IgnoreAspectRatio, + Qt::SmoothTransformation ); +#else + stepon.convertFromImage( s_stepBtnOn->convertToImage().scale( + width() / 16, s_stepBtnOn->height() ) ); + stepoff.convertFromImage( s_stepBtnOff->convertToImage().scale( + width() / 16, s_stepBtnOff->height() ) ); + stepoffl.convertFromImage( s_stepBtnOffLight->convertToImage(). + scale( width() / 16, s_stepBtnOffLight->height() ) ); +#endif + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + Sint16 no = it - m_notes.begin(); + Sint16 x = TCO_BORDER_WIDTH + static_cast( no * + ppt / + m_notes.size() ); + Sint16 y = height() - s_stepBtnOn->height() - 1; + if( ( *it )->length() < 0 ) + { + p.drawPixmap( x, y, stepon ); + } + else if( ( no / 4 ) % 2 ) + { + p.drawPixmap( x, y, stepoff ); + } + else + { + p.drawPixmap( x, y, stepoffl ); + } + } + } + + p.setFont( pointSize<7>( p.font() ) ); + p.setPen( QColor( 32, 240, 32 ) ); + p.drawText( 2, 9, m_name ); + if( m_frozenPattern != NULL ) + { + p.setPen( QColor( 0, 224, 255 ) ); + p.drawRect( 0, 0, width(), height() - 1 ); + p.drawPixmap( 3, height() - s_frozen->height() - 4, *s_frozen ); + } + +#ifndef QT4 + // blit drawn pixmap to actual widget + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void pattern::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() != Qt::LeftButton ) + { + _me->ignore(); + return; + } + + if( m_patternType == pattern::BEAT_PATTERN && pixelsPerTact() >= 192 && + _me->y() > height() - s_stepBtnOn->height() ) + { + note * n = m_notes[( _me->x() - TCO_BORDER_WIDTH ) * 16 / + width() ]; + if( n->length() < 0 ) + { + n->setLength( 0 ); + } + else + { + n->setLength( -64 ); + } + songEditor::inst()->setModified(); + update(); + return; + } + trackContentObject::mousePressEvent( _me ); +} + + + + +void pattern::mouseDoubleClickEvent( QMouseEvent * _me ) +{ + if( _me->button() != Qt::LeftButton ) + { + _me->ignore(); + return; + } + if( m_patternType == pattern::MELODY_PATTERN || + !( m_patternType == pattern::BEAT_PATTERN && + pixelsPerTact() >= 192 && + _me->y() > height() - s_stepBtnOn->height() ) ) + { + openInPianoRoll(); + } +} + + + + +void pattern::openInPianoRoll( void ) +{ + openInPianoRoll( FALSE ); +} + + + + +void pattern::openInPianoRoll( bool ) +{ + pianoRoll::inst()->setCurrentPattern( this ); + pianoRoll::inst()->show(); + pianoRoll::inst()->setFocus(); + return; +} + + + + +void pattern::clear( void ) +{ + clearNotes(); + ensureBeatNotes(); +} + + + + +void pattern::resetName( void ) +{ + m_name = m_channelTrack->name(); +} + + + + +void pattern::changeName( void ) +{ + renameDialog rename_dlg( m_name ); + rename_dlg.exec(); +} + + + + +void pattern::freeze( void ) +{ + if( songEditor::inst()->playing() ) + { + QMessageBox::information( 0, tr( "Cannot freeze pattern" ), + tr( "The pattern currently " + "cannot be freezed " + "because you're in " + "play-mode. Please " + "stop and try again!" ), + QMessageBox::Ok ); + return; + } + if( m_channelTrack->muted() ) + { + if( QMessageBox:: +#if QT_VERSION >= 0x030200 + question +#else + information +#endif + + ( 0, tr( "Channel muted" ), + tr( "The channel this pattern " + "belongs to is " + "currently muted, so " + "freezing makes no " + "sense! Do you still " + "want to continue?" ), + QMessageBox::Yes, + QMessageBox::No | + QMessageBox::Default | + QMessageBox::Escape ) == + QMessageBox::No ) + { + return; + } + } + + // already frozen? + if( m_frozenPattern != NULL ) + { + // then unfreeze, before freezing it again + unfreeze(); + } + + mixer::inst()->pause(); + + bool b; + m_freezeRecorder = new audioSampleRecorder( + mixer::inst()->sampleRate(), + DEFAULT_CHANNELS, b ); + mixer::inst()->setAudioDevice( m_freezeRecorder, + mixer::inst()->highQuality() ); + + songEditor::inst()->playPattern( this, FALSE ); + songEditor::playPos & ppp = songEditor::inst()->getPlayPos( + songEditor::PLAY_PATTERN ); + ppp.setTact( 0 ); + ppp.setTact64th( 0 ); + ppp.setCurrentFrame( 0 ); + ppp.m_timeLineUpdate = FALSE; + m_freezeStatusDialog = new patternFreezeStatusDialog; + connect( m_freezeStatusDialog, SIGNAL( aborted() ), this, + SLOT( abortFreeze() ) ); + + m_freezeStatusUpdateTimer = new QTimer( this ); + connect( m_freezeStatusUpdateTimer, SIGNAL( timeout() ), this, + SLOT( updateFreezeStatusDialog() ) ); + + m_freezeStatusUpdateTimer->start( 50 ); + + m_freezeStatusDialog->show(); + + mixer::inst()->play(); +} + + + + +void pattern::unfreeze( void ) +{ + if( m_frozenPattern != NULL ) + { + m_frozenPatternMutex.lock(); + delete m_frozenPattern; + m_frozenPattern = NULL; + m_frozenPatternMutex.unlock(); + } +} + + + + +void pattern::updateFreezeStatusDialog( void ) +{ + m_freezeStatusDialog->setProgress( songEditor::inst()->getPlayPos( + songEditor::PLAY_PATTERN ) * + 100 / length() ); + m_frozenPatternMutex.lock(); + + // finishFreeze called? + if( m_freezeRecorder == NULL ) + { + // then we're done and destroy the timer and the dialog + delete m_freezeStatusUpdateTimer; + delete m_freezeStatusDialog; + m_freezeStatusUpdateTimer = NULL; + m_freezeStatusDialog = NULL; + } + + m_frozenPatternMutex.unlock(); +} + + + + +void pattern::finishFreeze( void ) +{ + songEditor::inst()->stop(); + + m_frozenPatternMutex.lock(); + + m_freezeRecorder->createSampleBuffer( &m_frozenPattern ); + + mixer::inst()->restoreAudioDevice(); + m_freezeRecorder = NULL; + + songEditor::inst()->getPlayPos( songEditor::PLAY_PATTERN + ).m_timeLineUpdate = TRUE; + + m_frozenPatternMutex.unlock(); +} + + + + +void pattern::abortFreeze( void ) +{ + songEditor::inst()->stop(); + + m_frozenPatternMutex.lock(); + + mixer::inst()->restoreAudioDevice(); + m_freezeRecorder = NULL; + + songEditor::inst()->getPlayPos( songEditor::PLAY_PATTERN + ).m_timeLineUpdate = TRUE; + + m_frozenPatternMutex.unlock(); +} + + + + +void pattern::playFrozenData( sampleFrame * _ab, Uint32 _start_frame, + Uint32 _frames ) +{ + m_frozenPatternMutex.lock(); + if( m_frozenPattern != NULL ) + { + m_frozenPattern->play( _ab, _start_frame, _frames ); + } + m_frozenPatternMutex.unlock(); +} + + + + + +midiTime pattern::length( void ) const +{ + if( m_patternType == BEAT_PATTERN ) + { + // TODO: remove this limitation later by adding + // "Add step to pattern"-option + return( 64 ); + } + + Sint32 max_length = 0; + + for( noteVector::const_iterator it = m_notes.begin(); + it != m_notes.end(); + ++it ) + { + max_length = tMax( max_length, ( *it )->endPos() ); + } + if( max_length % 64 == 0 ) + { + return( midiTime( tMax( max_length, 64 ) ) ); + } + return( midiTime( tMax( midiTime( max_length ).getTact() + 1, 1 ), + 0 ) ); +} + + + + +note * pattern::addNote( const note & _new_note ) +{ + note * new_note = new note( _new_note ); + + if( m_notes.size() == 0 || m_notes.last()->pos() <= new_note->pos() ) + { + m_notes.push_back( new_note ); + } + else + { + // simple algorithm for inserting the note between two + // notes with smaller and greater position + // maybe it could be optimized by starting in the middle and + // going forward or backward but note-inserting isn't that + // time-critical since it is usually not done while playing... + long new_note_abs_time = new_note->pos(); + noteVector::iterator it = m_notes.begin(); + + while( it != m_notes.end() && + ( *it )->pos() < new_note_abs_time ) + { + ++it; + } + + m_notes.insert( it, new_note ); + } + + checkType(); + update(); + changeLength( length() ); + + return( new_note ); +} + + + + +void pattern::removeNote( const note * _note_to_del ) +{ + noteVector::iterator it = m_notes.begin(); + while( it != m_notes.end() ) + { + if( ( *it ) == _note_to_del ) + { + delete *it; + m_notes.erase( it ); + break; + } + ++it; + } + + checkType(); + update(); + changeLength( length() ); +} + + + + +note * pattern::rearrangeNote( const note * _note_to_proc ) +{ + // just rearrange the position of the note by removing it and adding + // a copy of it -> addNote inserts it at the correct position + note copy_of_note( *_note_to_proc ); + removeNote( _note_to_proc ); + + return( addNote( copy_of_note ) ); +} + + + + +void pattern::clearNotes( void ) +{ + for( noteVector::iterator it = m_notes.begin(); it != m_notes.end(); + ++it ) + { + delete *it; + } + + m_notes.clear(); + checkType(); + update(); +} + + + + +void pattern::setType( patternTypes _new_pattern_type ) +{ + if( _new_pattern_type == BEAT_PATTERN || + _new_pattern_type == MELODY_PATTERN ) + { + m_patternType = _new_pattern_type; + } +} + + + + +note * pattern::noteAt( int _note_num ) +{ + if( (csize) _note_num < m_notes.size() ) + { + return( m_notes[_note_num] ); + } + return( NULL ); +} + + + + +void pattern::setNoteAt( int _note_num, note _new_note ) +{ + if( static_cast( _note_num ) < m_notes.size() ) + { + delete m_notes[_note_num]; + m_notes[_note_num] = new note( _new_note ); + checkType(); + update(); + } +} + + + + +void pattern::checkType( void ) +{ + noteVector::iterator it = m_notes.begin(); + while( it != m_notes.end() ) + { + if( ( *it )->length() > 0 ) + { + setType( pattern::MELODY_PATTERN ); + return; + } + ++it; + } + setType( pattern::BEAT_PATTERN ); +} + + + + + +void pattern::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement pattern_de = _doc.createElement( nodeName() ); + pattern_de.setAttribute( "type", QString::number( m_patternType ) ); + pattern_de.setAttribute( "name", m_name ); + if( _parent.nodeName() == "clipboard" ) + { + pattern_de.setAttribute( "pos", QString::number( -1 ) ); + } + else + { + pattern_de.setAttribute( "pos", QString::number( + startPosition() ) ); + } + pattern_de.setAttribute( "len", QString::number( length() ) ); + pattern_de.setAttribute( "frozen", QString::number( + m_frozenPattern != NULL ) ); + _parent.appendChild( pattern_de ); + + // now save settings of all notes + for( noteVector::iterator it = m_notes.begin(); + it != m_notes.end(); ++it ) + { + if( ( *it )->length() ) + { + ( *it )->saveSettings( _doc, pattern_de ); + } + } +} + + + + +void pattern::loadSettings( const QDomElement & _this ) +{ + unfreeze(); + + m_patternType = static_cast( _this.attribute( "type" + ).toInt() ); + m_name = _this.attribute( "name" ); + if( _this.attribute( "pos" ).toInt() >= 0 ) + { + movePosition( _this.attribute( "pos" ).toInt() ); + } + changeLength( midiTime( _this.attribute( "len" ).toInt() ) ); + + clearNotes(); + + QDomNode node = _this.firstChild(); + while( !node.isNull() ) + { + if( node.isElement() ) + { + note * n = new note(); + n->loadSettings( node.toElement() ); + m_notes.push_back( n ); + } + node = node.nextSibling(); + } + ensureBeatNotes(); +/* if( _this.attribute( "frozen" ).toInt() ) + { + freeze(); + }*/ + update(); +} + + + + +patternFreezeStatusDialog::patternFreezeStatusDialog( void ) +{ + setWindowTitle( tr( "Freezing pattern..." ) ); +#if QT_VERSION >= 0x030200 + setModal( TRUE ); +#endif + + m_progressBar = new QProgressBar( this ); + m_progressBar->setGeometry( 10, 10, 200, 24 ); +#ifdef QT4 + m_progressBar->setMaximum( 100 ); +#else + m_progressBar->setTotalSteps( 100 ); +#endif + m_progressBar->setTextVisible( FALSE ); + m_progressBar->show(); + m_cancelBtn = new QPushButton( embed::getIconPixmap( "cancel" ), + tr( "Cancel" ), this ); + m_cancelBtn->setGeometry( 50, 38, 120, 28 ); + connect( m_cancelBtn, SIGNAL( clicked() ), this, + SLOT( cancelBtnClicked() ) ); +} + + + + +patternFreezeStatusDialog::~patternFreezeStatusDialog() +{ +} + + + + + +void patternFreezeStatusDialog::setProgress( int _p ) +{ +#ifdef QT4 + m_progressBar->setValue( _p ); +#else + m_progressBar->setProgress( _p ); +#endif +} + + + + +void patternFreezeStatusDialog::closeEvent( QCloseEvent * _ce ) +{ + _ce->ignore(); + cancelBtnClicked(); +} + + + + +void patternFreezeStatusDialog::cancelBtnClicked( void ) +{ + emit( aborted() ); +} + + + + + +#include "pattern.moc" + diff --git a/src/tracks/sample_track.cpp b/src/tracks/sample_track.cpp new file mode 100644 index 0000000000..54eaa68624 --- /dev/null +++ b/src/tracks/sample_track.cpp @@ -0,0 +1,395 @@ +/* + * sample_track.cpp - implementation of class sampleTrack, a track which + * provides arrangement of samples + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "sample_track.h" +#include "song_editor.h" +#include "name_label.h" +#include "embed.h" +#include "templates.h" +#include "buffer_allocator.h" +#include "tooltip.h" + + + +sampleTCO::sampleTCO( track * _track ) : + trackContentObject( _track ), + m_sampleBuffer() +{ +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + + setSampleFile( "" ); + + // we need to receive bpm-change-events, because then we have to + // change length of this TCO + connect( songEditor::inst(), SIGNAL( bpmChanged( int ) ), this, + SLOT( updateLength( int ) ) ); +} + + + + +sampleTCO::~sampleTCO() +{ +} + + + + +void sampleTCO::changeLength( const midiTime & _length ) +{ + trackContentObject::changeLength( tMax( static_cast( _length ), + 64 ) ); +} + + + + +void FASTCALL sampleTCO::play( sampleFrame * _ab, Uint32 _start_frame, + Uint32 _frames ) +{ + _start_frame = static_cast( tMax( 0.0f, _start_frame - + startPosition() * + songEditor::inst()->framesPerTact() / 64 ) ); + m_sampleBuffer.play( _ab, _start_frame, _frames ); +} + + + + +const QString & sampleTCO::sampleFile( void ) const +{ + return( m_sampleBuffer.audioFile() ); +} + + + + +void sampleTCO::setSampleFile( const QString & _sf ) +{ + m_sampleBuffer.setAudioFile( _sf ); + updateLength(); + update(); + // set tooltip to filename so that user can see what sample this + // sample-tco contains + toolTip::add( this, ( m_sampleBuffer.audioFile() != "" ) ? + m_sampleBuffer.audioFile() : + tr( "double-click to select sample" ) ); +} + + + + +void sampleTCO::updateLength( int ) +{ + changeLength( getSampleLength() ); +} + + + + +void sampleTCO::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); + p.fillRect( rect(), QColor( 0, 64, 255 ) ); +#else + // create pixmap for whole widget + QPixmap pm( rect().size() ); + pm.fill( QColor( 0, 64, 255 ) ); + // and a painter for it + QPainter p( &pm ); +#endif + + if( getTrack()->muted() ) + { + p.setPen( QColor( 160, 160, 160 ) ); + } + else + { + p.setPen( QColor( 0, 0, 128 ) ); + } + p.drawRect( 0, 0, width(), height() ); + m_sampleBuffer.drawWaves( p, QRect( 1, 1, tMax( tMin( width() - 3, + static_cast( getSampleLength() * + pixelsPerTact() / 64 ) ), 1 ), + height() - 4 ) ); +#ifndef QT4 + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +void sampleTCO::mouseDoubleClickEvent( QMouseEvent * ) +{ + QString af = m_sampleBuffer.openAudioFile(); + if( af != "" && af != m_sampleBuffer.audioFile() ) + { + setSampleFile( af ); + songEditor::inst()->setModified(); + } +} + + + + +midiTime sampleTCO::getSampleLength( void ) const +{ + return( static_cast( m_sampleBuffer.frames() / + songEditor::inst()->framesPerTact() * + 64 ) ); +} + + + + +void FASTCALL sampleTCO::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement sampletco_de = _doc.createElement( nodeName() ); + if( _parent.nodeName() == "clipboard" ) + { + sampletco_de.setAttribute( "pos", QString::number( -1 ) ); + } + else + { + sampletco_de.setAttribute( "pos", QString::number( + startPosition() ) ); + } + sampletco_de.setAttribute( "len", QString::number( length() ) ); + sampletco_de.setAttribute( "src", sampleFile() ); + // TODO: start- and end-frame + _parent.appendChild( sampletco_de ); +} + + + + +void FASTCALL sampleTCO::loadSettings( const QDomElement & _this ) +{ + if( _this.attribute( "pos" ).toInt() >= 0 ) + { + movePosition( _this.attribute( "pos" ).toInt() ); + } + changeLength( _this.attribute( "len" ).toInt() ); + setSampleFile( _this.attribute( "src" ) ); +} + + + + + +/* + +sampleTCOSettingsDialog::sampleTCOSettingsDialog( sampleTCO * _stco ) : + QDialog(), + m_sampleTCO( _stco ) +{ + resize( 400, 300 ); + + QVBoxWidget * vb0 = new QVBoxWidget( this ); + vb0->resize( 400, 300 ); + QHBoxWidget * hb00 = new QHBoxWidget( vb0 ); + m_fileLbl = new QLabel( _stco->sampleFile(), hb00 ); + QPushButton * open_file_btn = new QPushButton( + embed::getIconPixmap( "fileopen" ), "", hb00 ); + connect( open_file_btn, SIGNAL( clicked() ), this, + SLOT( openSampleFile() ) ); + + QHBoxWidget * hb01 = new QHBoxWidget( vb0 ); + + QPushButton * ok_btn = new QPushButton( tr( "OK" ), hb01 ); + ok_btn->setGeometry( 10, 0, 100, 32 ); + connect( ok_btn, SIGNAL( clicked() ), this, SLOT( accept() ) ); + + QPushButton * cancel_btn = new QPushButton( tr( "Cancel" ), hb01 ); + cancel_btn->setGeometry( 120, 0, 100, 32 ); + connect( ok_btn, SIGNAL( clicked() ), this, SLOT( reject() ) ); + +} + + + + +sampleTCOSettingsDialog::~sampleTCOSettingsDialog() +{ +} + + + + +void sampleTCOSettingsDialog::openSampleFile( void ) +{ + QString af = m_sampleTCO->m_sampleBuffer.openAudioFile(); + if( af != "" ) + { + setSampleFile( af ); + } +} + + + + +void sampleTCOSettingsDialog::setSampleFile( const QString & _f ) +{ + m_fileLbl->setText( _f ); + m_sampleTCO->setSampleFile( _f ); + songEditor::inst()->setModified(); +} +*/ + + + + + +sampleTrack::sampleTrack( trackContainer * _tc ) + : track( _tc ) +{ + getTrackWidget()->setFixedHeight( 32 ); + + m_trackLabel = new nameLabel( tr( "Sample track" ), + getTrackSettingsWidget(), + embed::getIconPixmap( + "sample_track" ) ); + m_trackLabel->setGeometry( 1, 1, DEFAULT_SETTINGS_WIDGET_WIDTH-2, 29 ); + m_trackLabel->show(); + + _tc->updateAfterTrackAdd(); +} + + + + +sampleTrack::~sampleTrack() +{ +} + + + + +track::trackTypes sampleTrack::trackType( void ) const +{ + return( SAMPLE_TRACK ); +} + + + + +bool FASTCALL sampleTrack::play( const midiTime & _start, Uint32 _start_frame, + Uint32 _frames, Uint32 _frame_base, + Sint16 /*_tco_num*/ ) +{ + vlist tcos; + getTCOsInRange( tcos, _start, _start+static_cast( _frames * 64 / + songEditor::inst()->framesPerTact() ) ); + + if ( tcos.size() == 0 ) + { + return( FALSE ); + } + + sampleFrame * buf = bufferAllocator::alloc( _frames ); + + volumeVector v = { 1.0f, 1.0f +#ifndef DISABLE_SURROUND + , 1.0f, 1.0f +#endif + } ; + float fpt = songEditor::inst()->framesPerTact(); + + for( vlist::iterator it = tcos.begin(); + it != tcos.end(); ++it ) + { + sampleTCO * st = dynamic_cast( *it ); + if( st != NULL ) + { + st->play( buf, _start_frame + + static_cast( _start.getTact() * + fpt ), + _frames ); + mixer::inst()->addBuffer( buf, _frames, _frame_base + + static_cast( + st->startPosition().getTact64th() * + fpt / 64.0f ), v ); + } + } + + bufferAllocator::free( buf ); + + return( TRUE ); +} + + + + +trackContentObject * sampleTrack::createTCO( const midiTime & ) +{ + return( new sampleTCO( this ) ); +} + + + + + +void sampleTrack::saveTrackSpecificSettings( QDomDocument & _doc, + QDomElement & _parent ) +{ + QDomElement st_de = _doc.createElement( nodeName() ); + st_de.setAttribute( "name", m_trackLabel->text() ); + _parent.appendChild( st_de ); +} + + + + +void sampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) +{ + m_trackLabel->setText( _this.attribute( "name" ) ); +} + + + + +#include "sample_track.moc" + diff --git a/src/widgets/group_box.cpp b/src/widgets/group_box.cpp new file mode 100644 index 0000000000..5da124577b --- /dev/null +++ b/src/widgets/group_box.cpp @@ -0,0 +1,234 @@ +/* + * group_box.cpp - groupbox for LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#define setChecked setOn + +#endif + +#include + + +#include "group_box.h" +#include "embed.h" +#include "spc_bg_hndl_widget.h" +#include "templates.h" + + +QPixmap * groupBox::s_ledBg = NULL; + + +groupBox::groupBox( const QString & _caption, QWidget * _parent ) : + QWidget( _parent ), + m_caption( _caption ), + m_origHeight( height() ), + m_animating( FALSE ) +{ + if( s_ledBg == NULL ) + { + s_ledBg = new QPixmap( embed::getIconPixmap( + "groupbox_led_bg" ) ); + } + updatePixmap(); + + m_led = new pixmapButton( this ); + m_led->move( 2, 3 ); + m_led->setActiveGraphic( embed::getIconPixmap( "led_green" ) ); + m_led->setInactiveGraphic( embed::getIconPixmap( "led_off" ) ); + m_led->setBgGraphic( specialBgHandlingWidget::getBackground( m_led ) ); + connect( m_led, SIGNAL( toggled( bool ) ), + this, SLOT( setState( bool ) ) ); +} + + + + +groupBox::~groupBox() +{ +} + + + + +void groupBox::resizeEvent( QResizeEvent * ) +{ + updatePixmap(); + if( m_animating == FALSE ) + { + m_origHeight = height(); + } +} + + + + +void groupBox::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->y() > 1 && _me->y() < 13 ) + { + setState( !isActive(), TRUE ); + } +} + + + + +void groupBox::setState( bool _on, bool _anim ) +{ + m_led->setChecked( _on ); + if( ( _anim == TRUE || ( _on == TRUE && height() < m_origHeight ) ) && + m_animating == FALSE ) + { + m_animating = TRUE; + animate(); + } +} + + + + +void groupBox::animate( void ) +{ + float state = (float)( m_origHeight - height() ) / + (float)( m_origHeight - 19 ); + int dy = static_cast( 3 - 2 * cosf( state * 2 * M_PI ) ); + if( isActive() && height() < m_origHeight ) + { + } + else if( !isActive() && height() > 19 ) + { + dy = -dy; + } + else + { + m_animating = FALSE; + return; + } + resize( width(), height() + dy ); + QTimer::singleShot( 10, this, SLOT( animate() ) ); +#ifdef QT4 + QObjectList ch = parent()->children(); +#else + QObjectList ch = *( parent()->children() ); +#endif + for( csize i = 0; i < ch.count(); ++i ) + { + QWidget * w = dynamic_cast( ch.at( i ) ); + if( w == NULL || w->y() < y() + height() ) + { + continue; + } + w->move( w->x(), w->y() + dy ); + } +#ifdef QT4 + ch = children(); +#else + ch = *children(); +#endif + for( csize i = 0; i < ch.count(); ++i ) + { + QWidget * w = dynamic_cast( ch.at( i ) ); + if( w == NULL || w == m_led ) + { + continue; + } + w->move( w->x(), w->y() + dy ); + if( w->y() < 14) + { + w->hide(); + } + else if( w->isHidden() == TRUE ) + { + w->show(); + } + } +} + + + + +void groupBox::updatePixmap( void ) +{ + QPixmap pm( size() ); + pm.fill( QColor( 96, 96, 96 ) ); + + QPainter p( &pm ); + + // outer rect + p.setPen( QColor( 64, 64, 64 ) ); + p.drawRect( 0, 0, width(), height() ); + + // brighter line at bottom/right + p.setPen( QColor( 160, 160, 160 ) ); + p.drawLine( width() - 1, 0, width() - 1, height() - 1 ); + p.drawLine( 0, height() - 1, width() - 1, height() - 1 ); + + // draw our led-pixmap + p.drawPixmap( 2, 2, *s_ledBg ); + + // draw groupbox-titlebar + p.fillRect( 2, 2, width() - 4, 9, QColor( 30, 45, 60 ) ); + + // draw line below titlebar + p.setPen( QColor( 0, 0, 0 ) ); + p.drawLine( 2 + s_ledBg->width(), 11, width() - 3, 11 ); + + // black inner rect + p.drawRect( 1, 1, width() - 2, height() - 2 ); + + + p.setPen( QColor( 255, 255, 255 ) ); + p.setFont( pointSize<7>( font() ) ); + p.drawText( 22, 10, m_caption ); + +#ifdef QT4 + QPalette pal = palette(); + pal.setBrush( backgroundRole(), QBrush( pm ) ); + pal.setColor( QPalette::Background, QColor( 96, 96, 96 ) ); + setPalette( pal ); +#else + setPaletteBackgroundColor( QColor( 96, 96, 96 ) ); + setErasePixmap( pm ); +#endif +} + + + +#undef setChecked + +#include "group_box.moc" + diff --git a/src/widgets/kmultitabbar.cpp b/src/widgets/kmultitabbar.cpp new file mode 100644 index 0000000000..4b3adba04d --- /dev/null +++ b/src/widgets/kmultitabbar.cpp @@ -0,0 +1,1148 @@ +/*************************************************************************** + kmultitabbar.cpp - description + ------------------- + begin : 2001 + copyright : (C) 2001,2002,2003 by Joseph Wenninger + ***************************************************************************/ + +/*************************************************************************** + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + ***************************************************************************/ + +#include "kmultitabbar.h" + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +//#include "kmultitabbar.moc" +//#include "kmultitabbar_p.h" +//#include "kmultitabbar_p.moc" +#include +#include +#include +#include +#include +#include +#include + +//#define palette colorGroup + +/*#define #palette().shadow().color() #colorGroup().shadow() +#define #palette().background().color() #colorGroup().background() +#define #palette().light().color() #colorGroup().light() +#define #palette().text().color() #colorGroup().text()*/ + +#define isChecked isOn +#define TextShowMnemonic ShowPrefix +#define State SFlags +#define State_None Style_Default +#define State_Enabled Style_Enabled +#define State_On Style_On + +#endif + +#include "templates.h" +#include "tooltip.h" + +#include "kmultitabbar.moc" + +//#include +//#include + + +class KMultiTabBarTabPrivate { +public: + QPixmap pix; +}; + + +KMultiTabBarInternal::KMultiTabBarInternal(QWidget *parent, Qt::Orientation o):Q3ScrollView(parent) +{ + m_expandedTabSize=-1; + m_showActiveTabTexts=false; +#ifndef QT4 + m_tabs.setAutoDelete(true); +#endif + setHScrollBarMode(AlwaysOff); + setVScrollBarMode(AlwaysOff); + if (o==Qt::Vertical) + { + box=new QWidget(viewport()); + mainLayout=new QVBoxLayout(box); +#ifndef QT4 + mainLayout->setAutoAdd(true); +#endif + box->setFixedWidth(24); + setFixedWidth(24); + } + else + { + box=new QWidget(viewport()); + mainLayout=new QHBoxLayout(box); +#ifndef QT4 + mainLayout->setAutoAdd(true); +#endif + box->setFixedHeight(24); + setFixedHeight(24); + } + addChild(box); + setFrameStyle(NoFrame); +#ifndef QT4 + viewport()->setBackgroundMode(Qt::PaletteBackground); +#endif +} + +void KMultiTabBarInternal::setStyle(enum KMultiTabBar::KMultiTabBarStyle style) +{ + m_style=style; + for (csize i=0;isetStyle(m_style); + + if ( (m_style==KMultiTabBar::KDEV3) || + (m_style==KMultiTabBar::KDEV3ICON ) ) { + resizeEvent(0); + } + viewport()->repaint(); +} + +void KMultiTabBarInternal::drawContents ( QPainter * paint, int clipx, int clipy, int clipw, int cliph ) +{ + Q3ScrollView::drawContents (paint , clipx, clipy, clipw, cliph ); + + if (m_position==KMultiTabBar::DockRight) + { + + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,0,0,viewport()->height()); + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(120)); + paint->drawLine(1,0,1,viewport()->height()); + + + } + else + if (m_position==KMultiTabBar::DockLeft) + { + paint->setPen(palette().light() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(23,0,23,viewport()->height()); + paint->drawLine(22,0,22,viewport()->height()); + + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,0,0,viewport()->height()); + } + else + if (m_position==KMultiTabBar::DockBottom) + { + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,0,viewport()->width(),0); + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(120)); + paint->drawLine(0,1,viewport()->width(),1); + } + else + { + paint->setPen(palette().light() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,23,viewport()->width(),23); + paint->drawLine(0,22,viewport()->width(),22); + +/* paint->setPen(palette().shadow().color()); + paint->drawLine(0,0,0,viewport()->height());*/ + + } + + +} + +void KMultiTabBarInternal::contentsMousePressEvent(QMouseEvent *ev) +{ + ev->ignore(); +} + +void KMultiTabBarInternal::mousePressEvent(QMouseEvent *ev) +{ + ev->ignore(); +} + +void KMultiTabBarInternal::resizeEvent(QResizeEvent *ev) { + //kdDebug()<<"KMultiTabBarInternal::resizeEvent"<setGeometry(0,0,width(),height()); + printf("resizevent\n"); + int lines=1; + int space; + int tmp=0; + if ((m_position==KMultiTabBar::DockBottom) || (m_position==KMultiTabBar::DockTop)) + space=width(); + else + space=height(); + + int cnt=0; +//CALCULATE LINES + for (csize i=0;ineededSize(); + if (tmp>space) { + if (cnt>1)i--; + cnt=0; + tmp=0; + lines++; + } + } +//SET SIZE & PLACE + if ((m_position==KMultiTabBar::DockBottom) || (m_position==KMultiTabBar::DockTop)) { + setFixedHeight(lines*24); + box->setFixedHeight(lines*24); + tmp=0; + cnt=0; + m_lines=lines; + lines=0; + for (csize i=0;ineededSize(); + if (tmp>space) { + if (cnt>1) i--; + else + m_tabs.at(i)->move(tmp-m_tabs.at(i)->neededSize(),lines*24); + cnt=0; + tmp=0; + lines++; + } else m_tabs.at(i)->move(tmp-m_tabs.at(i)->neededSize(),lines*24); + } + } + else { + setFixedWidth(lines*24); + box->setFixedWidth(lines*24); + tmp=0; + cnt=0; + m_lines=lines; + lines=0; + for (csize i=0;ineededSize(); + if (tmp>space) { + if (cnt>1) i--; + else + m_tabs.at(i)->move(lines*24,tmp-m_tabs.at(i)->neededSize()); + cnt=0; + tmp=0; + lines++; + } else m_tabs.at(i)->move(lines*24,tmp-m_tabs.at(i)->neededSize()); + } + } + + + //kdDebug()<<"needed lines:"< it(m_tabs);it.hasNext();it.next()){ + if (it.peekNext()->id()==id) return it.peekNext(); + } +#else + for (QPtrListIterator it(m_tabs);it.current();++it){ + if (it.current()->id()==id) return it.current(); + } +#endif + return 0; +} + + +int KMultiTabBarInternal::appendTab(const QPixmap &pic ,int id,const QString& text) +{ + KMultiTabBarTab *tab; + m_tabs.append(tab= new KMultiTabBarTab(pic,text,id,box,m_position,m_style)); + tab->showActiveTabText(m_showActiveTabTexts); + if (m_style==KMultiTabBar::KONQSBC) + { + if (m_expandedTabSizeneededSize()) { + m_expandedTabSize=tab->neededSize(); + for (csize i=0;isetSize(m_expandedTabSize); + + } else tab->setSize(m_expandedTabSize); + } else tab->updateState(); + tab->show(); + return 0; +} + +void KMultiTabBarInternal::removeTab(int id) +{ + for (csize pos=0;posid()==id) + { + delete m_tabs.at(pos); +#ifdef QT4 + m_tabs.removeAt(pos); +#else + m_tabs.remove(pos); +#endif + resizeEvent(0); + break; + } + } +} + +void KMultiTabBarInternal::setPosition(enum KMultiTabBar::KMultiTabBarPosition pos) +{ + m_position=pos; + for (csize i=0;isetTabsPosition(m_position); + viewport()->repaint(); +} + + +KMultiTabBarButton::KMultiTabBarButton(const QPixmap& pic,const QString& text, QMenu *popup, + int id,QWidget *parent,KMultiTabBar::KMultiTabBarPosition pos,KMultiTabBar::KMultiTabBarStyle style) + :QPushButton(QIcon(),text,parent),m_style(style) +{ + setIcon(pic); + setText(text); + m_position=pos; + if (popup) setMenu(popup); + setFlat(true); + setFixedHeight(24); + setFixedWidth(24); + m_id=id; + toolTip::add( this, text ); + connect(this,SIGNAL(clicked()),this,SLOT(slotClicked())); +} + +KMultiTabBarButton::KMultiTabBarButton(const QString& text, QMenu *popup, + int id,QWidget *parent,KMultiTabBar::KMultiTabBarPosition pos,KMultiTabBar::KMultiTabBarStyle style) + :QPushButton(QIcon(),text,parent),m_style(style) +{ + setText(text); + m_position=pos; + if (popup) setMenu(popup); + setFlat(true); + setFixedHeight(24); + setFixedWidth(24); + m_id=id; + toolTip::add(this,text); + connect(this,SIGNAL(clicked()),this,SLOT(slotClicked())); +} + +KMultiTabBarButton::~KMultiTabBarButton() { +} + +int KMultiTabBarButton::id() const{ + return m_id; +} + +void KMultiTabBarButton::setText(const QString& text) +{ + QPushButton::setText(text); + m_text=text; + toolTip::add(this,text); +} + +void KMultiTabBarButton::slotClicked() +{ + emit clicked(m_id); +} + +void KMultiTabBarButton::setPosition(KMultiTabBar::KMultiTabBarPosition pos) +{ + m_position=pos; + repaint(); +} + +void KMultiTabBarButton::setStyle(KMultiTabBar::KMultiTabBarStyle style) +{ + m_style=style; + repaint(); +} + +void KMultiTabBarButton::hideEvent( QHideEvent* he) { + QPushButton::hideEvent(he); + KMultiTabBar *tb=dynamic_cast(parentWidget()); + if (tb) tb->updateSeparator(); +} + +void KMultiTabBarButton::showEvent( QShowEvent* he) { + QPushButton::showEvent(he); + KMultiTabBar *tb=dynamic_cast(parentWidget()); + if (tb) tb->updateSeparator(); +} + + +QSize KMultiTabBarButton::sizeHint() const +{ + ensurePolished(); + + int w = 0, h = 0; + + // calculate contents size... +#ifndef QT_NO_ICONSET +#ifndef QT4 + if ( iconSet() && !iconSet()->isNull() ) { + int iw = iconSet()->pixmap( QIcon::Small, QIcon::Normal ).width() + 4; + int ih = iconSet()->pixmap( QIcon::Small, QIcon::Normal ).height(); + w += iw; + h = tMax( h, ih ); + } +#endif +#endif +#ifdef QT4 + QStyleOptionButton sob; + if ( menu() != 0 ) + w += style()->pixelMetric(QStyle::PM_MenuButtonIndicator, &sob, this); +#else + if( isMenuButton() ) + w += style().pixelMetric(QStyle::PM_MenuButtonIndicator, this); +#endif + +#ifdef QT4 + if ( !icon().isNull() ) { + const QPixmap & pm = icon().pixmap( QSize( 16, 16 ) ); + w += pm.width(); + h += pm.height(); +#else + if ( pixmap() ) { + QPixmap *pm = (QPixmap *)pixmap(); + w += pm->width(); + h += pm->height(); +#endif + } else { + QString s( text() ); + bool empty = s.isEmpty(); + if ( empty ) + s = QString::fromLatin1("XXXX"); + QFontMetrics fm = fontMetrics(); + QSize sz = fm.size( Qt::TextShowMnemonic, s ); + if(!empty || !w) + w += sz.width(); + if(!empty || !h) + h = tMax(h, sz.height()); + } +#ifdef QT4 + return (style()->sizeFromContents(QStyle::CT_ToolButton, &sob, QSize(w, h), this). + expandedTo(QApplication::globalStrut())); +#else + return (style().sizeFromContents(QStyle::CT_ToolButton, this, QSize(w, h)). + expandedTo(QApplication::globalStrut())); +#endif +} + + +KMultiTabBarTab::KMultiTabBarTab(const QPixmap& pic, const QString& text, + int id,QWidget *parent,KMultiTabBar::KMultiTabBarPosition pos, + KMultiTabBar::KMultiTabBarStyle style) + :KMultiTabBarButton(text,0,id,parent,pos,style), + m_showActiveTabText(false) +{ + d=new KMultiTabBarTabPrivate(); + setIcon(pic); + m_expandedSize=24; + setCheckable(true); +} + +KMultiTabBarTab::~KMultiTabBarTab() { + delete d; +} + + +void KMultiTabBarTab::setTabsPosition(KMultiTabBar::KMultiTabBarPosition pos) +{ + if ((pos!=m_position) && ((pos==KMultiTabBar::DockLeft) || (pos==KMultiTabBar::DockRight))) { + if (!d->pix.isNull()) { + QMatrix temp;// (1.0F, 0.0F, 0.0F, -1.0F, 0.0F, 0.0F); + temp.rotate(180); + d->pix=d->pix.transformed(temp); + setIcon(d->pix); + } + } + + setPosition(pos); +// repaint(); +} + +void KMultiTabBarTab::setIcon(const QString& icon) +{ + //QPixmap pic=SmallIcon(icon); + QPixmap pic (icon); + setIcon(pic); +} + +void KMultiTabBarTab::setIcon(const QPixmap& icon) +{ + + if (m_style!=KMultiTabBar::KDEV3) { + if ((m_position==KMultiTabBar::DockLeft) || (m_position==KMultiTabBar::DockRight)) { + QMatrix rotateMatrix; + if (m_position==KMultiTabBar::DockLeft) + rotateMatrix.rotate(90); + else + rotateMatrix.rotate(-90); + QPixmap pic=icon.transformed(rotateMatrix); + d->pix=pic; +#ifdef QT4 + QPushButton::setIcon(pic); +#else + setIconSet(pic); +#endif + } else +#ifdef QT4 + QPushButton::setIcon(icon); +#else + setIconSet(icon); +#endif + } +} + +void KMultiTabBarTab::slotClicked() +{ + updateState(); + KMultiTabBarButton::slotClicked(); +} + +void KMultiTabBarTab::setState(bool b) +{ +#ifdef QT4 + setChecked(b); +#else + setOn(b); +#endif + updateState(); +} + +void KMultiTabBarTab::updateState() +{ + + if (m_style!=KMultiTabBar::KONQSBC) { + if ((m_style==KMultiTabBar::KDEV3) || (m_style==KMultiTabBar::KDEV3ICON) || (isChecked())) { + QPushButton::setText(m_text); + } else { + //kdDebug()<<"KMultiTabBarTab::updateState(): setting text to an empty QString***************"<rotate(-90); + paint->drawPixmap(1-pixmap.width(),0,pixmap); + break; + case KMultiTabBar::DockRight: + paint->rotate(90); + paint->drawPixmap(0,1-pixmap.height(),pixmap); + break; + + default: + paint->drawPixmap(0,0,pixmap); + break; + } +// style().drawControl(QStyle::CE_PushButtonLabel,painter,this, QRect(0,0,pixmap.width(),pixmap.height()), +// colorGroup(),QStyle::Style_Enabled); + + +} + +void KMultiTabBarTab::drawButtonClassic(QPainter *paint) +{ + QPixmap pixmap; + //if ( iconSet()) + // pixmap = iconSet()->pixmap( QIcon::Small, QIcon::Normal ); +#ifdef QT4 + if( !icon().isNull() ) + { + pixmap = icon().pixmap( 16, 16 ); + } +#else + if( iconSet() ) + pixmap = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ); +#endif + + paint->fillRect(0, 0, 24, 24, palette().background() +#ifdef QT4 + .color() +#endif + ); + + if (!isChecked()) + { + + if (m_position==KMultiTabBar::DockRight) + { + paint->fillRect(0,0,21,21,QBrush(palette().background() +#ifdef QT4 + .color() +#endif + )); + + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(150)); + paint->drawLine(0,22,23,22); + + paint->drawPixmap(12-pixmap.width()/2,12-pixmap.height()/2,pixmap); + + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,0,0,23); + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(120)); + paint->drawLine(1,0,1,23); + + } + else + if ((m_position==KMultiTabBar::DockBottom) || (m_position==KMultiTabBar::DockTop)) + { + paint->fillRect(0,1,23,22,QBrush(palette().background() +#ifdef QT4 + .color() +#endif + )); + + paint->drawPixmap(12-pixmap.width()/2,12-pixmap.height()/2,pixmap); + + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(120)); + paint->drawLine(23,0,23,23); + + + paint->setPen(palette().light() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,22,23,22); + paint->drawLine(0,23,23,23); + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,0,23,0); + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(120)); + paint->drawLine(0,1,23,1); + + } + else + { + paint->setPen(palette().background() +#ifdef QT4 + .color() +#endif + .dark(120)); + paint->drawLine(0,23,23,23); + paint->fillRect(0,0,23,21,QBrush(palette().background() +#ifdef QT4 + .color() +#endif + )); + paint->drawPixmap(12-pixmap.width()/2,12-pixmap.height()/2,pixmap); + + paint->setPen(palette().light() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(23,0,23,23); + paint->drawLine(22,0,22,23); + + paint->setPen(palette().shadow() +#ifdef QT4 +.color() +#endif + ); + paint->drawLine(0,0,0,23); + + } + + + } + else + { + if (m_position==KMultiTabBar::DockRight) + { + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,height()-1,23,height()-1); + paint->drawLine(0,height()-2,23,height()-2); + paint->drawLine(23,0,23,height()-1); + paint->drawLine(22,0,22,height()-1); + paint->fillRect(0,0,21,height()-3,QBrush(palette().light() +#ifdef QT4 + .color() +#endif + )); + paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap); + + if (m_showActiveTabText) + { + if (height()<25+4) return; + + QPixmap tpixmap(height()-25-3, width()-2); + QPainter painter(&tpixmap); + + painter.fillRect(0,0,tpixmap.width(),tpixmap.height(),QBrush(palette().light() +#ifdef QT4 + .color() +#endif + )); + + painter.setPen(palette().text() +#ifdef QT4 + .color() +#endif + ); + painter.drawText(0,+width()/2+QFontMetrics(QFont()).height()/2,m_text); + + paint->rotate(90); + //kdDebug()<<"tpixmap.width:"<drawPixmap(25,-tpixmap.height()+1,tpixmap); + } + + } + else + if (m_position==KMultiTabBar::DockTop) + { + paint->fillRect(0,0,width()-1,23,QBrush(palette().light() +#ifdef QT4 + .color() +#endif + )); + paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap); + if (m_showActiveTabText) + { + paint->setPen(palette().text() +#ifdef QT4 + .color() +#endif + ); + paint->drawText(25,height()/2+QFontMetrics(QFont()).height()/2,m_text); + } + } + else + if (m_position==KMultiTabBar::DockBottom) + { + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,23,width()-1,23); + paint->drawLine(0,22,width()-1,22); + paint->fillRect(0,0,width()-1,21,QBrush(palette().light() +#ifdef QT4 + .color() +#endif + )); + paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap); + if (m_showActiveTabText) + { + paint->setPen(palette().text() +#ifdef QT4 + .color() +#endif + ); + paint->drawText(25,height()/2+QFontMetrics(QFont()).height()/2,m_text); + } + + } + else + { + + + paint->setPen(palette().shadow() +#ifdef QT4 + .color() +#endif + ); + paint->drawLine(0,height()-1,23,height()-1); + paint->drawLine(0,height()-2,23,height()-2); + paint->fillRect(0,0,23,height()-3,QBrush(palette().light() +#ifdef QT4 + .color() +#endif + )); + paint->drawPixmap(10-pixmap.width()/2,10-pixmap.height()/2,pixmap); + if (m_showActiveTabText) + { + + if (height()<25+4) return; + + QPixmap tpixmap(height()-25-3, width()-2); + QPainter painter(&tpixmap); + + painter.fillRect(0,0,tpixmap.width(),tpixmap.height(),QBrush(palette().light() +#ifdef QT4 + .color() +#endif + )); + + painter.setPen(palette().text() +#ifdef QT4 + .color() +#endif + ); + painter.drawText(tpixmap.width()-QFontMetrics(QFont()).width(m_text),+width()/2+QFontMetrics(QFont()).height()/2,m_text); + + paint->rotate(-90); + //kdDebug()<<"tpixmap.width:"<drawPixmap(-24-tpixmap.width(),2,tpixmap); + + } + + } + + } +} + + + + + + + +KMultiTabBar::KMultiTabBar( Qt::Orientation o, QWidget *parent):QWidget(parent) +{ +#ifndef QT4 + m_buttons.setAutoDelete(false); +#endif + if (o==Qt::Vertical) + { + m_l=new QVBoxLayout(this); + setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding +#ifndef QT4 + , true +#endif + )); +// setFixedWidth(24); + } + else + { + m_l=new QHBoxLayout(this); + setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed +#ifndef QT4 + , true +#endif + )); +// setFixedHeight(24); + } + m_l->setMargin(0); +#ifndef QT4 + m_l->setAutoAdd(false); +#endif + m_internal=new KMultiTabBarInternal(this,o); + setPosition((o==Qt::Vertical)?KMultiTabBar::DockRight:KMultiTabBar::DockBottom); + setStyle(VSNET); + // setStyle(KDEV3); + //setStyle(KONQSBC); + m_l->insertWidget(0,m_internal); + m_l->insertWidget(0,m_btnTabSep=new QFrame(this)); + m_btnTabSep->setFixedHeight(4); + m_btnTabSep->setFrameStyle(QFrame::Panel | QFrame::Sunken); + m_btnTabSep->setLineWidth(2); + m_btnTabSep->hide(); + + updateGeometry(); +} + +KMultiTabBar::~KMultiTabBar() { +} + +/*int KMultiTabBar::insertButton(QPixmap pic,int id ,const QString&) +{ + (new KToolbarButton(pic,id,m_internal))->show(); + return 0; +}*/ + +int KMultiTabBar::appendButton(const QPixmap &pic ,int id,QMenu *popup,const QString&) +{ + KMultiTabBarButton *btn; + m_buttons.append(btn= new KMultiTabBarButton(pic,QString::null, + popup,id,this,m_position,m_internal->m_style)); + m_l->insertWidget(0,btn); + btn->show(); + m_btnTabSep->show(); + return 0; +} + +void KMultiTabBar::updateSeparator() { + bool hideSep=true; +#ifdef QT4 + for (QListIterator it(m_buttons);it.hasNext();){ + if (it.next()->isVisibleTo(this)) { + hideSep=false; + break; + } + } +#else + for (QPtrListIterator it(m_buttons);it.current();++it){ + if (it.current()->isVisibleTo(this)) { + hideSep=false; + break; + } + } +#endif + if (hideSep) m_btnTabSep->hide(); + else m_btnTabSep->show(); + +} + +int KMultiTabBar::appendTab(const QPixmap &pic ,int id ,const QString& text) +{ + m_internal->appendTab(pic,id,text); + return 0; +} + +KMultiTabBarButton* KMultiTabBar::button(int id) const +{ +#ifdef QT4 + for (QListIterator it(m_buttons);it.hasNext();it.next()){ + if (it.peekNext()->id()==id) return it.peekNext(); + } +#else + for (QPtrListIterator it(m_buttons);it.current();++it){ + if (it.current()->id()==id) return it.current(); + } +#endif + return 0; +} + +KMultiTabBarTab* KMultiTabBar::tab(int id) const +{ + return m_internal->tab(id); +} + + + +void KMultiTabBar::removeButton(int id) +{ + for (csize pos=0;posid()==id) + { + m_buttons.at(pos)->deleteLater(); +#ifdef QT4 + m_buttons.removeAt(pos); +#else + m_buttons.remove(pos); +#endif + //m_buttons.take(pos)->deleteLater(); + break; + } + } + if (m_buttons.count()==0) m_btnTabSep->hide(); +} + +void KMultiTabBar::removeTab(int id) +{ + m_internal->removeTab(id); +} + +void KMultiTabBar::setTab(int id,bool state) +{ + KMultiTabBarTab *ttab=tab(id); + if (ttab) + { + ttab->setState(state); + } +} + +bool KMultiTabBar::isTabRaised(int id) const +{ + KMultiTabBarTab *ttab=tab(id); + if (ttab) + { + return ttab->isChecked(); + } + + return false; +} + + +void KMultiTabBar::showActiveTabTexts(bool show) +{ + m_internal->showActiveTabTexts(show); +} + +void KMultiTabBar::setStyle(KMultiTabBarStyle style) +{ + m_internal->setStyle(style); +} + +void KMultiTabBar::setPosition(KMultiTabBarPosition pos) +{ + m_position=pos; + m_internal->setPosition(pos); + for (csize i=0;isetPosition(pos); +} +void KMultiTabBar::fontChange(const QFont& /* oldFont */) +{ + for (csize i=0;icount();i++) + tabs()->at(i)->resize(); + repaint(); +} + +tabList * KMultiTabBar::tabs() {return m_internal->tabs();} +buttonList * KMultiTabBar::buttons() {return &m_buttons;} + diff --git a/src/widgets/knob.cpp b/src/widgets/knob.cpp new file mode 100644 index 0000000000..0fd057fa22 --- /dev/null +++ b/src/widgets/knob.cpp @@ -0,0 +1,812 @@ +/* + * knob.cpp - powerful knob-widget + * + * This file is based on the knob-widget of the Qwt Widget Library from + * Josef Wilgen + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include +#include + +#define addSeparator insertSeparator + +#endif + +#ifndef __USE_XOPEN +#define __USE_XOPEN +#endif +#include + +#include "knob.h" +#include "song_editor.h" +#include "midi_device.h" +#include "embed.h" +#include "spc_bg_hndl_widget.h" +//#include "tooltip.h" +#include "config_mgr.h" +#include "text_float.h" +#include "mixer.h" + + +const int WHEEL_DELTA = 120; + + +static double MinRelStep = 1.0e-10; +static double DefaultRelStep = 1.0e-2; +static double MinEps = 1.0e-10; + + +float knob::s_copiedValue = 0.0f; +textFloat * knob::s_textFloat = NULL; + + + +knob::knob( int _knob_num, QWidget * _parent, const QString & _name ) : + QWidget( _parent +#ifndef QT4 + , _name.ascii() +#endif + ), + m_scrollMode( ScrNone ), + m_mouseOffset( 0.0f ), + m_tracking( TRUE ), + m_angle( 0.0f ), + m_oldAngle( 0.0f ), + m_nTurns( 0.0f ), + m_knobNum( _knob_num ), + m_hintTextBeforeValue( "" ), + m_hintTextAfterValue( "" ), + m_label( "" ), + m_minValue( 0.0f ), + m_maxValue( 100.0f ), + m_value( 0.0f ), + m_exactValue( 0.0f ), + m_exactPrevValue( 0.0f ), + m_prevValue( 0.0f ), + m_initValue( 0.0f ) +{ + if( s_textFloat == NULL ) + { + s_textFloat = new textFloat( this ); + } + +#ifdef QT4 + setAccessibleName( _name ); +#endif + setRange( 0.0, 100.0, 1.0 ); + +#ifdef QT4 + m_knobPixmap = new QPixmap( embed::getIconPixmap( QString( "knob0" + + QString::number( m_knobNum + 1 ) ).toAscii().constData() ) ); +#else + m_knobPixmap = new QPixmap( embed::getIconPixmap( "knob0" + + QString::number( m_knobNum + 1 ) ) ); +#endif +#ifdef QT4 +// setAttribute( Qt::WA_NoBackground ); +#else + setBackgroundMode( Qt::NoBackground ); +#endif + + m_knobWidth = m_knobPixmap->width(); + + setFixedSize( m_knobPixmap->width(), m_knobPixmap->height() ); + setTotalAngle( 270.0f ); + recalcAngle(); + +} + + + + +// Destructor +knob::~knob() +{ + // make sure pointer to this knob isn't used anymore in active + // midi-device-class + if( mixer::inst()->getMIDIDevice()->pitchBendKnob() == this ) + { + mixer::inst()->getMIDIDevice()->setPitchBendKnob( NULL ); + } +} + + + + +void knob::setHintText( const QString & _txt_before, + const QString & _txt_after ) +{ + m_hintTextBeforeValue = _txt_before; + m_hintTextAfterValue = _txt_after; +/* toolTip::add( this, m_hintTextBeforeValue + QString::number( value() ) + + m_hintTextAfterValue );*/ +} + + + + +void knob::setLabel( const QString & _txt ) +{ + m_label = _txt; + setFixedSize( tMax( m_knobPixmap->width(), + QFontMetrics( pointSize<6>( font() + ) ).width( m_label ) ), + m_knobPixmap->height() + 10 ); + update(); +} + + + + +void knob::setTotalAngle( float _angle ) +{ + if( _angle < 10.0 ) + { + m_totalAngle = 10.0; + } + else + { + m_totalAngle = _angle; + } + + layoutKnob(); +} + + + + +void knob::drawKnob( QPainter * _p ) +{ + _p->drawPixmap( 0, 0, specialBgHandlingWidget::getBackground( this ) ); + + const float radius = m_knobPixmap->width() / 2 - 1; + const float xm = m_knobPixmap->width() / 2;//radius + 1; + const float ym = m_knobPixmap->height() / 2;//radius+1; + + const float rarc = m_angle * M_PI / 180.0; + const float ca = cos( rarc ); + const float sa = -sin( rarc ); + + _p->drawPixmap( static_cast( xm - m_knobPixmap->width() / 2 ), 0, + *m_knobPixmap ); + + _p->setPen( QPen( QColor( 200, 0, 0 ), 2 ) ); + + switch( m_knobNum ) + { + case knobSmall_17: + { + _p->drawLine( (int)( xm-sa ), (int)( ym-ca ), + (int)( xm - sa*radius ), + (int)( ym - ca*radius ) ); + break; + } + case knobBright_26: + { + _p->drawLine( (int)( xm-sa ), (int)( ym-ca ), + (int)( xm - sa*( radius-5 ) ), + (int)( ym - ca*( radius-5 ) ) ); + break; + } + case knobDark_28: + { + const float rb = tMax( ( radius - 10 ) / 3.0, + 0.0 ); + const float re = tMax( ( radius - 4 ), 0.0 ); + _p->drawLine( (int)( xm-sa*rb ) + 1, + (int)( ym - ca*rb ) + 1, + (int)( xm - sa*re ) + 1, + (int)( ym - ca*re ) + 1 ); + break; + } + } +} + + + + +void knob::valueChange( void ) +{ + recalcAngle(); + update(); +/* toolTip::add( this, m_hintTextBeforeValue + QString::number( value() ) + + m_hintTextAfterValue );*/ + if( m_tracking ) + { + emit valueChanged( value() ); + } +} + + + + +float knob::getValue( const QPoint & _p ) +{ + if( configManager::inst()->value( "knobs", "classicalusability" + ).toInt() ) + { + const float dx = float( ( rect().x() + rect().width() / 2 ) - + _p.x() ); + const float dy = float( ( rect().y() + rect().height() / 2 ) - + _p.y() ); + + const float arc = atan2( -dx, dy ) * 180.0 / M_PI; + + float new_value = 0.5 * ( m_minValue + m_maxValue ) + + ( arc + m_nTurns * 360.0 ) * + ( m_maxValue - m_minValue ) / + m_totalAngle; + + const float oneTurn = tAbs( m_maxValue - m_minValue ) * + 360.0 / m_totalAngle; + const float eqValue = value() + m_mouseOffset; + + if( tAbs( new_value - eqValue ) > 0.5 * oneTurn ) + { + if( new_value < eqValue ) + { + new_value += oneTurn; + } + else + { + new_value -= oneTurn; + } + } + return( new_value ); + } + return( ( _p.y() - m_origMousePos.y() ) * m_step ); +} + + + + +void knob::getScrollMode( const QPoint &, int & _scroll_mode, int & _direction ) +{ + _scroll_mode = ScrMouse; + _direction = 0; +} + + + + +void knob::rangeChange() +{ + layoutKnob(); + recalcAngle(); +} + + + + +void knob::resizeEvent( QResizeEvent * ) +{ + layoutKnob( FALSE ); +} + + + + +// Recalculate the slider's geometry and layout based on +// the current rect and fonts. +void knob::layoutKnob( bool _update_geometry ) +{ + if( _update_geometry ) + { + updateGeometry(); + update(); + } +} + + + + +void knob::paintEvent( QPaintEvent * _me ) +{ + // Use double-buffering + QRect ur = _me->rect(); +#ifndef QT4 + if( ur.isValid() ) + { +#endif +#ifdef QT4 + QPainter p( this ); +#else + QPixmap pix( ur.size() ); + pix.fill( this, ur.topLeft() ); + QPainter p( &pix, this ); +#endif + p.translate( -ur.x(), -ur.y() ); + drawKnob( &p ); + if( m_label != "" ) + { + p.setFont( pointSize<6>( p.font() ) ); + p.setPen( QColor( 255, 255, 255 ) ); + p.drawText( width() / 2 - + QFontMetrics( p.font() ).width( m_label ) / 2, + height() - 2, m_label ); + } +#ifndef QT4 + p.end(); + bitBlt( this, ur.topLeft(), &pix ); + } +#endif + +} + + + +void knob::recalcAngle( void ) +{ + m_oldAngle = m_angle; + + // + // calculate the angle corresponding to the value + // + if( m_maxValue == m_minValue ) + { + m_angle = 0; + m_nTurns = 0; + } + else + { + m_angle = ( value() - 0.5 * ( m_minValue + m_maxValue ) ) / + ( m_maxValue - m_minValue ) * m_totalAngle; + m_nTurns = floor( ( m_angle + 180.0 ) / 360.0 ); + m_angle = m_angle - m_nTurns * 360.0; + } +} + + + + +//! Mouse press event handler +void knob::mousePressEvent( QMouseEvent * _me ) +{ + const QPoint & p = _me->pos(); + m_origMousePos = p; + + getScrollMode( p, m_scrollMode, m_direction ); + + switch( m_scrollMode ) + { + case ScrMouse: + m_mouseOffset = getValue( p ) - value(); + emit sliderPressed(); + break; + + default: + m_mouseOffset = 0; + m_direction = 0; + break; + } + + if( _me->button() == Qt::LeftButton ) + { + if( !configManager::inst()->value( "knobs", "classicalusability" + ).toInt() ) + { + QApplication::setOverrideCursor( Qt::BlankCursor ); + } + s_textFloat->reparent( this ); + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + + QPoint( m_knobPixmap->width() + 2, 0 ) ); + s_textFloat->show(); + } +} + + + + +//! Emits a valueChanged() signal if necessary +void knob::buttonReleased( void ) +{ + if( ( !m_tracking ) || ( value() != m_prevValue ) ) + { + emit valueChanged( value() ); + } +} + + + + +//! Mouse Release Event handler +void knob::mouseReleaseEvent( QMouseEvent * _me ) +{ + m_scrollMode = ScrNone; + buttonReleased(); + + switch( m_scrollMode ) + { + case ScrMouse: + setPosition( _me->pos() ); + m_direction = 0; + m_mouseOffset = 0; + emit sliderReleased (); + break; + + case ScrDirect: + setPosition( _me->pos() ); + m_direction = 0; + m_mouseOffset = 0; + break; + + default: + break; + } + + if( !configManager::inst()->value( "knobs", "classicalusability" + ).toInt() ) + { + QApplication::restoreOverrideCursor(); + } + + s_textFloat->hide(); +} + + + + +void knob::setPosition( const QPoint & _p ) +{ + if( configManager::inst()->value( "knobs", "classicalusability" + ).toInt() ) + { + setNewValue( getValue( _p ) - m_mouseOffset, 1 ); + } + else + { + setNewValue( m_value - getValue( _p ), 1 ); + } +} + + + + +void knob::setTracking( bool _enable ) +{ + m_tracking = _enable; +} + + + + +//! Mouse Move Event handler +void knob::mouseMoveEvent( QMouseEvent * _me ) +{ + if( m_scrollMode == ScrMouse ) + { + setPosition( _me->pos() ); + if( value() != m_prevValue ) + { + emit sliderMoved( value() ); + } + if( !configManager::inst()->value( "knobs", "classicalusability" + ).toInt() ) + { + QCursor::setPos( mapToGlobal( m_origMousePos ) ); + } + } + songEditor::inst()->setModified(); + + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + +} + + + +//! Qt wheel event +void knob::wheelEvent( QWheelEvent * _me ) +{ + _me->accept(); + const int inc = _me->delta() / WHEEL_DELTA; + incPages( inc ); + + songEditor::inst()->setModified(); + + s_textFloat->reparent( this ); + s_textFloat->setText( m_hintTextBeforeValue + + QString::number( value() ) + + m_hintTextAfterValue ); + s_textFloat->move( mapTo( topLevelWidget(), QPoint( 0, 0 ) ) + + QPoint( m_knobPixmap->width() + 2, 0 ) ); + s_textFloat->setVisibilityTimeOut( 1000 ); + +/* toolTip::add( this, m_hintTextBeforeValue+QString::number( value() ) + + m_hintTextAfterValue );*/ + + if( value() != m_prevValue ) + { + emit sliderMoved( value() ); + } +} + + + + +void knob::setValue( float _val, bool _is_init_value ) +{ + if( _is_init_value ) + { + m_initValue = _val; + } + + setNewValue( _val, 0 ); +} + + + + +void knob::fitValue( float _val ) +{ + setNewValue( _val, 1 ); +} + + + + +void knob::incValue( int _steps ) +{ + setNewValue( m_value + float( _steps ) * m_step, 1 ); +} + + + + +void knob::setRange( float _vmin, float _vmax, float _vstep, int _page_size ) +{ + int rchg = ( ( m_maxValue != _vmax ) || ( m_minValue != _vmin ) ); + + if( rchg ) + { + m_minValue = _vmin; + m_maxValue = _vmax; + } + + // + // look if the step width has an acceptable + // value or otherwise change it. + // + setStep( _vstep ); + + // + // limit page size + // +/* m_pageSize = tLimit( pageSize, 0, int( tAbs( ( m_maxValue - + m_minValue ) / m_step ) ) ); */ + m_pageSize = tMax( ( m_maxValue - m_minValue ) / 40.0f, _vstep ); + + // + // If the value lies out of the range, it + // will be changed. Note that it will not be adjusted to + // the new step width. + setNewValue( m_value, 0 ); + + // call notifier after the step width has been + // adjusted. + if( rchg ) + { + rangeChange(); + } +} + + + + +void knob::setNewValue( float _x, int _align ) +{ + m_prevValue = m_value; + + m_value = tLimit( _x, m_minValue, m_maxValue ); + + m_exactPrevValue = m_exactValue; + m_exactValue = m_value; + + // align to grid + if( _align ) + { + if( m_step != 0.0 ) + { + m_value = floorf( m_value / m_step ) * m_step; + } + else + { + m_value = m_minValue; + } + + // correct rounding error at the border + if( tAbs( m_value - m_maxValue ) < MinEps * + tAbs( m_step ) ) + { + m_value = m_maxValue; + } + + // correct rounding error if value = 0 + if( tAbs( m_value ) < MinEps * tAbs( m_step ) ) + { + m_value = 0.0; + } + } + + if( m_prevValue != m_value ) + { + valueChange(); + } +} + + + + +void knob::setStep( float _vstep ) +{ + float intv = m_maxValue - m_minValue; + + float newStep; + + if( _vstep == 0.0 ) + { + newStep = intv * DefaultRelStep; + } + else + { + if( ( intv > 0 ) && ( _vstep < 0 ) || ( intv < 0 ) && + ( _vstep > 0 ) ) + { + newStep = -_vstep; + } + else + { + newStep = _vstep; + } + if( tAbs( newStep ) < + tAbs( MinRelStep * intv ) ) + { + newStep = MinRelStep * intv; + } + } + if( newStep != m_step ) + { + m_step = newStep; + } +} + + + + +void knob::contextMenuEvent( QContextMenuEvent * ) +{ + QMenu contextMenu( this ); +#ifdef QT4 + contextMenu.setTitle( accessibleName() ); +#else + QLabel * caption = new QLabel( "" + + QString( accessibleName() ) + "", this ); + caption->setPaletteBackgroundColor( QColor( 0, 0, 192 ) ); + caption->setAlignment( Qt::AlignCenter ); + contextMenu.addAction( caption ); +#endif + contextMenu.addAction( embed::getIconPixmap( "reload" ), + tr( "&Reset (%1%2)" ).arg( m_initValue ).arg( + m_hintTextAfterValue ), + this, SLOT( reset() ) ); + contextMenu.addSeparator(); + contextMenu.addAction( embed::getIconPixmap( "edit_copy" ), + tr( "&Copy value (%1%2)" ).arg( value() ).arg( + m_hintTextAfterValue ), + this, SLOT( copyValue() ) ); + contextMenu.addAction( embed::getIconPixmap( "edit_paste" ), + tr( "&Paste value (%1%2)" + ).arg( s_copiedValue ).arg( + m_hintTextAfterValue ), + this, SLOT( pasteValue() ) ); + contextMenu.addSeparator(); + contextMenu.addAction( tr( "Connect to MIDI-device" ), this, + SLOT( connectToMidiDevice() ) ); + contextMenu.addSeparator(); + contextMenu.addAction( embed::getIconPixmap( "help" ), tr( "&Help" ), + this, SLOT( displayHelp() ) ); + contextMenu.exec( QCursor::pos() ); +} + + + + +void knob::reset( void ) +{ + setValue( m_initValue ); +} + + + + +void knob::copyValue( void ) +{ + s_copiedValue = value(); +} + + + + +void knob::pasteValue( void ) +{ + setValue( s_copiedValue ); +} + + + + +void knob::connectToMidiDevice( void ) +{ + mixer::inst()->getMIDIDevice()->setPitchBendKnob( this ); +} + + + + +void knob::displayHelp( void ) +{ +#ifdef QT4 + QWhatsThis::showText( mapToGlobal( rect().bottomRight() ), + whatsThis() ); +#else + QWhatsThis::display( QWhatsThis::textFor( this ), mapToGlobal( + rect().bottomRight() ) ); +#endif +} + + + + +#include "knob.moc" + + +#ifndef QT4 +#undef addSeparator +#endif diff --git a/src/widgets/lcd_spinbox.cpp b/src/widgets/lcd_spinbox.cpp new file mode 100644 index 0000000000..61ba76704c --- /dev/null +++ b/src/widgets/lcd_spinbox.cpp @@ -0,0 +1,176 @@ +/* + * lcd_spinbox.cpp - class lcdSpinBox, an improved QLCDNumber + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "lcd_spinbox.h" +#include "templates.h" + + + +lcdSpinBox::lcdSpinBox( int _min, int _max, int _num_digits, + QWidget * _parent ) : + QWidget( _parent ), + m_minValue( _min ), + m_maxValue( _max ), + m_step( 1 ), + m_label( NULL ), + m_origMousePos() +{ + m_number = new QLCDNumber( _num_digits, this ); + m_number->setFrameShape( QFrame::Panel ); + m_number->setFrameShadow( QFrame::Sunken ); + m_number->setSegmentStyle( QLCDNumber::Flat ); +#ifdef QT4 + QPalette pal = m_number->palette(); + pal.setColor( QPalette::Background, QColor( 32, 32, 32 ) ); + pal.setColor( QPalette::Foreground, QColor( 255, 180, 0 ) ); + m_number->setPalette( pal ); +#else + m_number->setPaletteBackgroundColor( QColor( 32, 32, 32 ) ); + m_number->setPaletteForegroundColor( QColor( 255, 180, 0 ) ); +#endif + // value is automatically limited to given range + setValue( 0 ); + + m_number->setFixedSize( m_number->sizeHint() * 0.9 ); + setFixedSize( m_number->size() ); +} + + + + +lcdSpinBox::~lcdSpinBox() +{ +} + + + +void lcdSpinBox::setStep( int _step ) +{ + m_step = tMax( _step, 1 ); +} + + + + +void lcdSpinBox::setValue( int _value ) +{ + _value = ( ( tLimit( _value, m_minValue, m_maxValue ) - m_minValue ) / + m_step ) * m_step + m_minValue; + QString s = QString::number( _value ); + while( (int) s.length() < m_number->numDigits() ) + { + s = "0" + s; + } + + m_number->display( s ); +} + + + + +void lcdSpinBox::setLabel( const QString & _txt ) +{ + if( m_label == NULL ) + { + m_label = new QLabel( _txt, this ); + m_label->setFont( pointSize<6>( m_label->font() ) ); + m_label->setGeometry( 0, y() + height(), + QFontMetrics( m_label->font() ).width( _txt ), 7 ); + setFixedSize( tMax( width(), m_label->width() ), + height() + m_label->height() ); + } + else + { + m_label->setText( _txt ); + } +} + + + + +void lcdSpinBox::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton && _me->y() < m_number->height() ) + { + m_origMousePos = _me->globalPos(); + QApplication::setOverrideCursor( Qt::BlankCursor ); + } +} + + + +void lcdSpinBox::mouseMoveEvent( QMouseEvent * _me ) +{ + if( _me->modifiers() == Qt::LeftButton ) + { + int dy = _me->globalY() - m_origMousePos.y(); + if( dy > 1 || dy < -1 ) + { + setValue( value() - dy / 2 * m_step ); + emit valueChanged( value() ); + QCursor::setPos( m_origMousePos ); + } + } +} + + + + +void lcdSpinBox::mouseReleaseEvent( QMouseEvent * _me ) +{ + QCursor::setPos( m_origMousePos ); + QApplication::restoreOverrideCursor(); +} + + + + +void lcdSpinBox::wheelEvent( QWheelEvent * _we ) +{ + setValue( value() + _we->delta() / 120 * m_step ); +} + + + + +#include "lcd_spinbox.moc" + diff --git a/src/widgets/led_checkbox.cpp b/src/widgets/led_checkbox.cpp new file mode 100644 index 0000000000..117eb2ed9c --- /dev/null +++ b/src/widgets/led_checkbox.cpp @@ -0,0 +1,130 @@ +/* + * led_checkbox.cpp - class ledCheckBox, an improved QCheckBox + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include + +#define setChecked setOn +#define isChecked isOn + +#endif + + +#include "led_checkbox.h" +#include "embed.h" +#include "templates.h" +#include "spc_bg_hndl_widget.h" + + +static const QString names[ledCheckBox::TOTAL_COLORS] = +{ + "led_yellow", "led_green" +} ; + + + +ledCheckBox::ledCheckBox( const QString & _text, QWidget * _parent, + ledColors _color ) : + QCheckBox( _text, _parent ) +{ + if( _color >= TOTAL_COLORS || _color < YELLOW ) + { + _color = YELLOW; + } + m_ledOnPixmap = new QPixmap( embed::getIconPixmap( names[_color] +#ifdef QT4 + .toAscii().constData() +#endif + ) ); + m_ledOffPixmap = new QPixmap( embed::getIconPixmap( "led_off" ) ); + +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + + setFont( pointSize<7>( font() ) ); + setFixedSize( m_ledOffPixmap->width() + 6 + + QFontMetrics( font() ).width( text() ), + m_ledOffPixmap->height() ); +} + + + + +ledCheckBox::~ledCheckBox() +{ + delete m_ledOnPixmap; + delete m_ledOffPixmap; +} + + + + +void ledCheckBox::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( rect().size() ); + draw_pm.fill( this, rect().topLeft() ); + + QPainter p( &draw_pm, this ); +#endif + p.drawPixmap( 0, 0, specialBgHandlingWidget::getBackground( this ) ); + +#ifdef QT4 + if( checkState() == Qt::Checked ) +#else + if( state() == On ) +#endif + { + p.drawPixmap( 0, 0, *m_ledOnPixmap ); + } + else + { + p.drawPixmap( 0, 0, *m_ledOffPixmap ); + } + + p.drawText( m_ledOffPixmap->width() + 2, 10, text() ); + +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + +#undef setChecked +#undef isChecked + diff --git a/src/widgets/nstate_button.cpp b/src/widgets/nstate_button.cpp new file mode 100644 index 0000000000..2586214554 --- /dev/null +++ b/src/widgets/nstate_button.cpp @@ -0,0 +1,143 @@ +/* + * nstate_button.cpp - implementation of n-state-button + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include + +#endif + + +#include "nstate_button.h" +#include "embed.h" +#include "tooltip.h" + + + +nStateButton::nStateButton( QWidget * _parent ) : + QWidget( _parent ), + m_generalToolTip( "" ), + m_curState( -1 ) +{ +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif +} + + + + +nStateButton::~nStateButton() +{ + while( m_states.size() ) + { + delete m_states.first().first; + m_states.erase( m_states.begin() ); + } +} + + + + +void nStateButton::addState( const QPixmap & _pm, const QString & _tooltip ) +{ + m_states.push_back( qMakePair( new QPixmap( _pm ), _tooltip ) ); + // first inserted pixmap? + if( m_states.size() == 1 ) + { + // then resize ourself + resize( _pm.width(), _pm.height() ); + // and set state to first pixmap + changeState( 0 ); + } +} + + + + +void nStateButton::changeState( int _n ) +{ + if( _n >= 0 && _n < (int) m_states.size() ) + { + m_curState = _n; + + const QString & _tooltip = + ( m_states[m_curState].second != "" ) ? + m_states[m_curState].second : + m_generalToolTip; + toolTip::add( this, _tooltip ); + + emit stateChanged( m_curState ); + + update(); + } +} + + + + +void nStateButton::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( rect().size() ); + draw_pm.fill( this, rect().topLeft() ); + + QPainter p( &draw_pm, this ); +#endif + + if( m_curState >= 0 && m_curState < (int) m_states.size() ) + { + p.drawPixmap( 0, 0, *m_states[m_curState].first ); + } +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +void nStateButton::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton && m_states.size() ) + { + changeState( ( ++m_curState ) % m_states.size() ); + } +} + + + + +#include "nstate_button.moc" + diff --git a/src/widgets/pixmap_button.cpp b/src/widgets/pixmap_button.cpp new file mode 100644 index 0000000000..f2a6e9abcb --- /dev/null +++ b/src/widgets/pixmap_button.cpp @@ -0,0 +1,175 @@ +/* + * pixmap_button.cpp - implementation of pixmap-button (often used as "themed" + * checkboxes/radiobuttons etc) + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#else + +#include + +#endif + + +#include "pixmap_button.h" +#include "embed.h" + + + +pixmapButton::pixmapButton( QWidget * _parent ) : + QPushButton( _parent ), + m_activePixmap( NULL ), + m_inactivePixmap( NULL ), + m_bgPixmap( NULL ) +{ + setActiveGraphic( embed::getIconPixmap( "led_yellow" ) ); + setInactiveGraphic( embed::getIconPixmap( "led_off" ), FALSE ); + +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + + setCheckable( TRUE ); +} + + + + +pixmapButton::~pixmapButton() +{ + delete m_activePixmap; + delete m_inactivePixmap; + delete m_bgPixmap; +} + + + + +void pixmapButton::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( rect().size() ); + draw_pm.fill( this, rect().topLeft() ); + + QPainter p( &draw_pm, this ); +#endif + + if( m_bgPixmap != NULL ) + { + p.drawPixmap( 0, 0, *m_bgPixmap ); + } + +#ifdef QT4 + if( isChecked() ) +#else + if( isOn() ) +#endif + { + if( m_activePixmap != NULL ) + { + p.drawPixmap( 0, 0, *m_activePixmap ); + } + } + else + { + if( m_inactivePixmap != NULL ) + { + p.drawPixmap( 0, 0, *m_inactivePixmap ); + } + } +#ifndef QT4 + // and blit all the drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + + +void pixmapButton::mousePressEvent( QMouseEvent * _me) +{ + if( _me->button() == Qt::RightButton ) + { + emit( clickedRight() ); + } + else + { + QPushButton::mousePressEvent( _me ); + } +} + + + + +void pixmapButton::mouseDoubleClickEvent( QMouseEvent * ) +{ + emit doubleClicked(); +} + + + + +void pixmapButton::setActiveGraphic( const QPixmap & _pm ) +{ + delete m_activePixmap; + + m_activePixmap = new QPixmap( _pm ); + resize( m_activePixmap->width(), m_activePixmap->height() ); +} + + + + +void pixmapButton::setInactiveGraphic( const QPixmap & _pm, bool _update ) +{ + delete m_inactivePixmap; + + m_inactivePixmap = new QPixmap( _pm ); + if( _update ) + { + update(); + } +} + + + + +void pixmapButton::setBgGraphic( const QPixmap & _pm ) +{ + delete m_bgPixmap; + + m_bgPixmap = new QPixmap( _pm ); +} + + + +#include "pixmap_button.moc" + diff --git a/src/widgets/project_notes.cpp b/src/widgets/project_notes.cpp new file mode 100644 index 0000000000..6f85e87591 --- /dev/null +++ b/src/widgets/project_notes.cpp @@ -0,0 +1,571 @@ +/* + * project_notes.h - header for project-notes-editor + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define isChecked isOn +#define setChecked setOn +#define setFontWeight setBold +#define setFontUnderline setUnderline +#define setFontItalic setItalic +#define setFontFamily setFamily +#define setFontPointSize setPointSize +#define setTextColor setColor +#define textColor color +#define setHtml setText +#define toHtml text + +#endif + + +#include "project_notes.h" +#include "embed.h" +#include "lmms_main_win.h" +#include "song_editor.h" + + + +projectNotes::projectNotes() : + QMainWindow( lmmsMainWin::inst()->workspace() +#ifndef QT4 + , 0, Qt::WStyle_Title +#endif + ) +{ +#ifdef QT4 + lmmsMainWin::inst()->workspace()->addWindow( this ); +#endif + + m_edit = new QTextEdit( this ); +#ifdef QT4 + QPalette pal; + pal.setColor( m_edit->backgroundRole(), QColor( 64, 64, 64 ) ); + m_edit->setPalette( pal ); +#else + m_edit->setTextFormat( RichText ); + m_edit->setPaletteBackgroundColor( QColor( 64, 64, 64 ) ); +#endif + clear(); + + connect( m_edit, SIGNAL( currentFontChanged( const QFont & ) ), + this, SLOT( fontChanged( const QFont & ) ) ); + connect( m_edit, SIGNAL( currentColorChanged( const QColor & ) ), + this, SLOT( colorChanged( const QColor & ) ) ); + connect( m_edit, SIGNAL( currentAlignmentChanged( int ) ), + this, SLOT( alignmentChanged( int ) ) ); + connect( m_edit, SIGNAL( textChanged() ), + songEditor::inst(), SLOT( setModified() ) ); + + setupActions(); + + setCentralWidget( m_edit ); + setWindowTitle( tr( "Project notes" ) ); + setWindowIcon( embed::getIconPixmap( "project_notes" ) ); +} + + + + +void projectNotes::clear( void ) +{ + m_edit->setHtml( tr( "Put down your project notes here." ) ); +} + + + + +void projectNotes::setupActions() +{ + QToolBar * tb = new QToolBar( tr( "Edit Actions" ), this ); + QAction * a; + + // changes between qt3's toolbar-system and this of qt4 are too + // big, so we completely implement two versions +#ifdef QT4 + a = new QAction( embed::getIconPixmap( "edit_undo" ), tr( "&Undo" ), + tb ); + a->setShortcut( tr( "Ctrl+Z" ) ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( undo() ) ); + + a = new QAction( embed::getIconPixmap( "edit_redo" ), tr( "&Redo" ), + tb ); + a->setShortcut( tr( "Ctrl+Y" ) ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( redo() ) ); + + a = new QAction( embed::getIconPixmap( "edit_copy" ), tr( "&Copy" ), + tb ); + a->setShortcut( tr( "Ctrl+C" ) ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( copy() ) ); + + a = new QAction( embed::getIconPixmap( "edit_cut" ), tr( "Cu&t" ), + tb ); + a->setShortcut( tr( "Ctrl+X" ) ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( cut() ) ); + + a = new QAction( embed::getIconPixmap( "edit_paste" ), tr( "&Paste" ), + tb ); + a->setShortcut( tr( "Ctrl+V" ) ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( paste() ) ); + + + tb = new QToolBar( tr( "Format Actions" ), this ); + + m_comboFont = new QComboBox( tb ); + m_comboFont->setEditable( TRUE ); + QFontDatabase db; + m_comboFont->addItems( db.families() ); + connect( m_comboFont, SIGNAL( activated( const QString & ) ), + m_edit, SLOT( setFontFamily( const QString & ) ) ); + m_comboFont->lineEdit()->setText( QApplication::font().family() ); + + m_comboSize = new QComboBox( tb ); + m_comboSize->setEditable( TRUE ); + vlist sizes = db.standardSizes(); + vlist::Iterator it = sizes.begin(); + for ( ; it != sizes.end(); ++it ) + { + m_comboSize->addItem( QString::number( *it ) ); + } + connect( m_comboSize, SIGNAL( activated( const QString & ) ), + this, SLOT( textSize( const QString & ) ) ); + m_comboSize->lineEdit()->setText( QString::number( + QApplication::font().pointSize() ) ); + + m_actionTextBold = new QAction( embed::getIconPixmap( "text_bold" ), + tr( "&Bold" ), tb ); + m_actionTextBold->setShortcut( tr( "Ctrl+B" ) ); + connect( m_actionTextBold, SIGNAL( activated() ), this, + SLOT( textBold() ) ); + + m_actionTextItalic = new QAction( embed::getIconPixmap( "text_italic" ), + tr( "&Italic" ), tb ); + m_actionTextItalic->setShortcut( tr( "Ctrl+I" ) ); + connect( m_actionTextItalic, SIGNAL( activated() ), this, + SLOT( textItalic() ) ); + + m_actionTextUnderline = new QAction( embed::getIconPixmap( + "text_under" ), + tr( "&Underline" ), tb ); + m_actionTextUnderline->setShortcut( tr( "Ctrl+U" ) ); + connect( m_actionTextUnderline, SIGNAL( activated() ), this, + SLOT( textUnderline() ) ); + + + QActionGroup * grp = new QActionGroup( tb ); + connect( grp, SIGNAL( selected( QAction* ) ), this, + SLOT( textAlign( QAction* ) ) ); + + m_actionAlignLeft = new QAction( embed::getIconPixmap( "text_left" ), + tr( "&Left" ), grp ); + m_actionAlignLeft->setShortcut( tr( "Ctrl+L" ) ); + + m_actionAlignCenter = new QAction( embed::getIconPixmap( + "text_center" ), + tr( "C&enter" ), grp ); + m_actionAlignCenter->setShortcut( tr( "Ctrl+E" ) ); + + m_actionAlignRight = new QAction( embed::getIconPixmap( "text_right" ), + tr( "&Right" ), grp ); + m_actionAlignRight->setShortcut( tr( "Ctrl+R" ) ); + + m_actionAlignJustify = new QAction( embed::getIconPixmap( + "text_block" ), + tr( "&Justify" ), grp ); + m_actionAlignJustify->setShortcut( tr( "Ctrl+J" ) ); + + + QPixmap pix( 16, 16 ); + pix.fill( Qt::black ); + m_actionTextColor = new QAction( pix, tr( "&Color..." ), tb ); + connect( m_actionTextColor, SIGNAL( activated() ), this, + SLOT( textColor() ) ); + +#else +#if QT_VERSION >= 0x030100 + a = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "edit_undo" ), tr( "&Undo" ), + CTRL + Key_Z, this ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( undo() ) ); + a->addTo( tb ); + + a = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "edit_redo" ), tr( "&Redo" ), + CTRL + Key_Y, this ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( redo() ) ); + a->addTo( tb ); + + a = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "edit_copy" ), tr( "&Copy" ), + CTRL + Key_C, this ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( copy() ) ); + a->addTo( tb ); + + a = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "edit_cut" ), tr( "Cu&t" ), + CTRL + Key_X, this ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( cut() ) ); + a->addTo( tb ); + + a = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "edit_paste" ), tr( "&Paste" ), + CTRL + Key_V, this ); + connect( a, SIGNAL( activated() ), m_edit, SLOT( paste() ) ); + a->addTo( tb ); + + + tb = new QToolBar( this ); + tb->setLabel( tr( "Format Actions" ) ); + + m_comboFont = new QComboBox( TRUE, tb ); + QFontDatabase db; + m_comboFont->insertStringList( db.families() ); + connect( m_comboFont, SIGNAL( activated( const QString & ) ), + m_edit, SLOT( setFamily( const QString & ) ) ); + m_comboFont->lineEdit()->setText( QApplication::font().family() ); + + m_comboSize = new QComboBox( TRUE, tb ); + vlist sizes = db.standardSizes(); + vlist::Iterator it = sizes.begin(); + for ( ; it != sizes.end(); ++it ) + { + m_comboSize->insertItem( QString::number( *it ) ); + } + connect( m_comboSize, SIGNAL( activated( const QString & ) ), + this, SLOT( textSize( const QString & ) ) ); + m_comboSize->lineEdit()->setText( QString::number( + QApplication::font().pointSize() ) ); + + m_actionTextBold = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "text_bold" ), + tr( "&Bold" ), CTRL + Key_B, this ); + connect( m_actionTextBold, SIGNAL( activated() ), this, + SLOT( textBold() ) ); + m_actionTextBold->addTo( tb ); + m_actionTextBold->setToggleAction( TRUE ); + + m_actionTextItalic = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "text_italic" ), + tr( "&Italic" ), CTRL + Key_I, this ); + connect( m_actionTextItalic, SIGNAL( activated() ), this, + SLOT( textItalic() ) ); + m_actionTextItalic->addTo( tb ); + m_actionTextItalic->setToggleAction( TRUE ); + + m_actionTextUnderline = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( + "text_under" ), + tr( "&Underline" ), + CTRL + Key_U, this ); + connect( m_actionTextUnderline, SIGNAL( activated() ), this, + SLOT( textUnderline() ) ); + m_actionTextUnderline->addTo( tb ); + m_actionTextUnderline->setToggleAction( TRUE ); + + QActionGroup * grp = new QActionGroup( this ); + connect( grp, SIGNAL( selected( QAction* ) ), this, + SLOT( textAlign( QAction* ) ) ); + + m_actionAlignLeft = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "text_left" ), + tr( "&Left" ), CTRL + Key_L, + grp ); + m_actionAlignLeft->setToggleAction( TRUE ); + m_actionAlignCenter = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( + "text_center" ), + tr( "C&enter" ), CTRL + Key_E, + grp ); + m_actionAlignCenter->setToggleAction( TRUE ); + + m_actionAlignRight = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( "text_right" ), + tr( "&Right" ), CTRL + Key_R, + grp ); + m_actionAlignRight->setToggleAction( TRUE ); + + m_actionAlignJustify = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + embed::getIconPixmap( + "text_block" ), + tr( "&Justify" ), CTRL + Key_J, + grp ); + m_actionAlignJustify->setToggleAction( TRUE ); + + grp->addTo( tb ); + + + QPixmap pix( 16, 16 ); + pix.fill( Qt::black ); + m_actionTextColor = new QAction( +#if QT_VERSION < 0x030200 + "", +#endif + pix, tr( "&Color..." ), 0, this ); + connect( m_actionTextColor, SIGNAL( activated() ), this, + SLOT( textColor() ) ); + m_actionTextColor->addTo( tb ); +#endif +#endif +} + + + + +void projectNotes::textBold() +{ + m_edit->setFontWeight( m_actionTextBold->isChecked() ); + songEditor::inst()->setModified(); +} + + + + +void projectNotes::textUnderline() +{ + m_edit->setFontUnderline( m_actionTextUnderline->isChecked() ); + songEditor::inst()->setModified(); +} + + + + +void projectNotes::textItalic() +{ + m_edit->setFontItalic( m_actionTextItalic->isChecked() ); + songEditor::inst()->setModified(); +} + + + + +void projectNotes::textFamily( const QString & _f ) +{ + m_edit->setFontFamily( _f ); + m_edit->viewport()->setFocus(); + songEditor::inst()->setModified(); +} + + + + +void projectNotes::textSize( const QString & _p ) +{ + m_edit->setFontPointSize( _p.toInt() ); + m_edit->viewport()->setFocus(); + songEditor::inst()->setModified(); +} + + + + +void projectNotes::textColor() +{ + QColor col = QColorDialog::getColor( m_edit->textColor(), this ); + if ( !col.isValid() ) + { + return; + } + m_edit->setTextColor( col ); + QPixmap pix( 16, 16 ); + pix.fill( Qt::black ); +#ifdef QT4 + m_actionTextColor->setIcon( pix ); +#else + m_actionTextColor->setIconSet( pix ); +#endif +} + + + + +void projectNotes::textAlign( QAction * _a ) +{ + if( _a == m_actionAlignLeft ) + { + m_edit->setAlignment( Qt::AlignLeft ); + } + else if( _a == m_actionAlignCenter ) + { + m_edit->setAlignment( Qt::AlignHCenter ); + } + else if( _a == m_actionAlignRight ) + { + m_edit->setAlignment( Qt::AlignRight ); + } + else if( _a == m_actionAlignJustify ) + { + m_edit->setAlignment( Qt::AlignJustify ); + } +} + + + + +void projectNotes::fontChanged( const QFont & _f ) +{ + m_comboFont->lineEdit()->setText( _f.family() ); + m_comboSize->lineEdit()->setText( QString::number( _f.pointSize() ) ); + m_actionTextBold->setChecked( _f.bold() ); + m_actionTextItalic->setChecked( _f.italic() ); + m_actionTextUnderline->setChecked( _f.underline() ); + songEditor::inst()->setModified(); +} + + + + +void projectNotes::colorChanged( const QColor & _c ) +{ + QPixmap pix( 16, 16 ); + pix.fill( _c ); +#ifdef QT4 + m_actionTextColor->setIcon( pix ); +#else + m_actionTextColor->setIconSet( pix ); +#endif + songEditor::inst()->setModified(); +} + + + + +void projectNotes::alignmentChanged( int _a ) +{ + if ( +#ifndef QT4 + ( _a == Qt::AlignAuto ) || +#endif + ( _a & Qt::AlignLeft ) ) + { + m_actionAlignLeft->setChecked( TRUE ); + } + else if ( ( _a & Qt::AlignHCenter ) ) + { + m_actionAlignCenter->setChecked( TRUE ); + } + else if ( ( _a & Qt::AlignRight ) ) + { + m_actionAlignRight->setChecked( TRUE ); + } + else if ( ( _a & Qt::AlignJustify ) ) + { + m_actionAlignJustify->setChecked( TRUE ); + } + songEditor::inst()->setModified(); +} + + + + +void projectNotes::saveSettings( QDomDocument & _doc, QDomElement & _parent ) +{ + QDomElement pn_de = _doc.createElement( nodeName() ); + QDomCDATASection ds = _doc.createCDATASection( m_edit->toHtml() ); + pn_de.appendChild( ds ); + _parent.appendChild( pn_de ); +} + + + + +void projectNotes::loadSettings( const QDomElement & _this ) +{ + m_edit->setHtml( _this.text() ); +} + + +#include "project_notes.moc" + + + +#undef isChecked +#undef setChecked +#undef setFontWeight +#undef setFontUnderline +#undef setFontItalic +#undef setFontFamily +#undef setFontPointSize +#undef textColor +#undef setTextColor +#undef setHtml +#undef toHtml + diff --git a/src/widgets/rename_dialog.cpp b/src/widgets/rename_dialog.cpp new file mode 100644 index 0000000000..fb958ccc24 --- /dev/null +++ b/src/widgets/rename_dialog.cpp @@ -0,0 +1,89 @@ +/* + * rename_dialog.cpp - implementation of dialog for renaming something + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include + +#endif + + +#include "rename_dialog.h" + + + +renameDialog::renameDialog( QString & _string ) : + QDialog(), + m_stringToEdit( _string ), + m_originalString( _string ) +{ + setWindowTitle( tr("Rename...") ); + m_stringLE = new QLineEdit( this ); + m_stringLE->setText( _string ); + m_stringLE->setGeometry ( 10, 5, 220, 20 ); + connect( m_stringLE, SIGNAL( textChanged( const QString & ) ), this, + SLOT( textChanged( const QString & ) ) ); + connect( m_stringLE, SIGNAL( returnPressed() ), this, + SLOT( accept() ) ); +} + + + + +renameDialog::~renameDialog() +{ +} + + + + +void renameDialog::keyPressEvent( QKeyEvent * _ke ) +{ + if( _ke->key() == Qt::Key_Escape ) + { + m_stringLE->setText( m_originalString ); + accept(); + } +} + + + + +void renameDialog::textChanged( const QString & _new_string ) +{ + m_stringToEdit = _new_string; +} + + + + +#include "rename_dialog.moc" + diff --git a/src/widgets/side_bar_widget.cpp b/src/widgets/side_bar_widget.cpp new file mode 100644 index 0000000000..c7ac75969a --- /dev/null +++ b/src/widgets/side_bar_widget.cpp @@ -0,0 +1,123 @@ +/* + * side_bar_widget.cpp - implementation of base-widget for side-bar + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "side_bar_widget.h" +#include "types.h" +#include "templates.h" + + + +sideBarWidget::sideBarWidget( const QString & _title, const QPixmap & _icon, + QWidget * _parent ) : + QWidget( _parent ), + m_title( _title ), + m_icon( _icon ) +{ + m_contents = new QWidget( this ); + m_layout = new QVBoxLayout( m_contents ); + m_layout->setSpacing( 5 ); +} + + + + +sideBarWidget::~sideBarWidget() +{ +} + + + + +void sideBarWidget::paintEvent( QPaintEvent * ) +{ + const Uint16 TITLE_FONT_HEIGHT = 16; + +#ifdef QT4 + QPainter p( this ); + p.fillRect( 0, 0, width(), 27, palette().highlight().color() ); +#else + QPixmap draw_pm( rect().size() ); + draw_pm.fill( QApplication::palette().color( QPalette::Normal, + QColorGroup::Background ) ); + + QPainter p( &draw_pm, this ); + p.fillRect( 0, 0, width(), 27, QApplication::palette().color( + QPalette::Normal, + QColorGroup::Highlight ) ); +#endif + + QFont f = p.font(); + f.setBold( TRUE ); + p.setFont( pointSize( f ) ); + +#ifdef QT4 + p.setPen( palette().highlightedText().color() ); +#else + p.setPen( QApplication::palette().color( QPalette::Normal, + QColorGroup::HighlightedText ) ); +#endif + const Uint16 tx = m_icon.width()+4; + const Uint16 ty = 2+TITLE_FONT_HEIGHT; + p.drawText( tx, ty, m_title ); + p.drawLine( tx, ty+4, width()-4, ty+4 ); + + p.drawPixmap( 2, 2, m_icon ); + +#ifndef QT4 + // and blit all drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + +void sideBarWidget::resizeEvent( QResizeEvent * ) +{ + const Uint16 MARGIN = 6; + m_contents->setGeometry( MARGIN, 40 + MARGIN, width() - MARGIN * 2, + height() - MARGIN * 2 - 40 ); +} + + + +#include "side_bar.moc" +#include "side_bar_widget.moc" + diff --git a/src/widgets/tab_bar.cpp b/src/widgets/tab_bar.cpp new file mode 100644 index 0000000000..be1e48b2c2 --- /dev/null +++ b/src/widgets/tab_bar.cpp @@ -0,0 +1,260 @@ +/* + * tab_bar.cpp - implementation of tab-bar + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "tab_bar.h" +#include "tab_button.h" +#include "templates.h" +#include "tooltip.h" + +#ifndef QT4 + +#define value data +#define setChecked setOn +#define isChecked isOn +#define removeWidget remove + +#endif + + + +tabBar::tabBar( QWidget * _parent, QBoxLayout::Direction _dir ) : + QWidget( _parent ), +#ifdef QT4 + m_layout( new QBoxLayout( _dir, this ) ), +#else + m_layout( new QBoxLayout( this, _dir ) ), +#endif + m_exclusive( FALSE ) +{ +} + + + + +tabBar::~tabBar() +{ +} + + + + +tabButton * tabBar::addTab( QWidget * _w, const QString & _text, int _id, + bool _add_stretch, bool _text_is_tooltip ) +{ + // already tab with id? + if( m_tabs.contains( _id ) ) + { + // then remove it + removeTab( _id ); + } + QString caption = ( _text_is_tooltip ) ? "" : _text; + // create tab-button + tabButton * b = new tabButton( caption, _id, this ); + connect( b, SIGNAL( clicked( int ) ), this, SLOT( tabClicked( int ) ) ); + b->show(); + if( _text_is_tooltip ) + { + toolTip::add( b, _text ); + } + + // small workaround, because QBoxLayout::addWidget(...) doesn't + // work properly, so we first have to remove all tabs from the + // layout and them add them in the correct order + QMap >::iterator it; + for( it = m_tabs.begin(); it != m_tabs.end(); ++it ) + { +#if QT_VERSION >= 0x030100 + m_layout->removeWidget( it.value().first ); +#endif + } + m_tabs.insert( _id, qMakePair( b, _w ) ); + for( it = m_tabs.begin(); it != m_tabs.end(); ++it ) + { + m_layout->addWidget( it.value().first ); + } + + if( _add_stretch ) + { + m_layout->addStretch(); + } + + + // we assume, parent-widget is a widget acting as widget-stack so all + // widgets have the same size and only the one on the top is visible + _w->setFixedSize( _w->parentWidget()->size() ); + + b->setFont( pointSize<7>( b->font() ) ); + + return( b ); +} + + + + +void tabBar::removeTab( int _id ) +{ + // find tab-button and delete it + if( m_tabs.find( _id ) != m_tabs.end() ) + { + delete m_tabs[_id].first; + m_tabs.erase( m_tabs.find( _id ) ); + } +} + + + + +void tabBar::setActiveTab( int _id ) +{ + setTabState( _id, TRUE ); + hideAll( _id ); + if( allHidden() ) + { + emit allWidgetsHidden(); + } + else + { + emit widgetShown(); + } +} + + + + +int tabBar::activeTab( void ) +{ + QMap >::iterator it; + for( it = m_tabs.begin(); it != m_tabs.end(); ++it ) + { + if( tabState( it.key() ) == TRUE ) + { + return( it.key() ); + } + } + return( -1 ); +} + + + + +bool tabBar::tabState( int _id ) +{ + if( m_tabs.find( _id ) == m_tabs.end() ) + { + return( FALSE ); + } + return( m_tabs[_id].first->isChecked() ); +} + + + + +void tabBar::setTabState( int _id, bool _checked ) +{ + if( m_tabs.find( _id ) != m_tabs.end() ) + { + m_tabs[_id].first->setChecked( _checked ); + } +} + + + + +void tabBar::hideAll( int _exception ) +{ + QMap >::iterator it; + for( it = m_tabs.begin(); it != m_tabs.end(); ++it ) + { + if( it.key() != _exception ) + { + setTabState( it.key(), FALSE ); + } + it.value().second->hide(); + } + if( m_tabs.find( _exception ) != m_tabs.end() ) + { + if( tabState( _exception ) ) + { + m_tabs[_exception].second->show(); + } + else + { + m_tabs[_exception].second->hide(); + } + } +} + + + + +void tabBar::tabClicked( int _id ) +{ + if( m_exclusive == TRUE && activeTab() == -1 ) + { + setActiveTab( _id ); + } + else + { + bool all_hidden_before = allHidden(); + // disable tabbar-buttons except the one clicked + hideAll( _id ); + bool now_hidden = allHidden(); + if( all_hidden_before == TRUE && now_hidden == FALSE ) + { + emit widgetShown(); + } + else if( all_hidden_before == FALSE && now_hidden == TRUE ) + { + emit allWidgetsHidden(); + } + } +} + + + + +bool tabBar::allHidden( void ) +{ + QMap >::iterator it; + for( it = m_tabs.begin(); it != m_tabs.end(); ++it ) + { + if( !it.value().second->isHidden() ) + { + return( FALSE ); + } + } + return( TRUE ); +} + + + +#undef value +#undef setChecked +#undef isChecked +#undef removeWidget + + +#include "tab_bar.moc" +#include "tab_button.moc" + diff --git a/src/widgets/tab_widget.cpp b/src/widgets/tab_widget.cpp new file mode 100644 index 0000000000..137f0c984e --- /dev/null +++ b/src/widgets/tab_widget.cpp @@ -0,0 +1,177 @@ +/* + * tabwidget.cpp - tabwidget for LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include + +#else + +#include +#include + +#endif + + +#include "tab_widget.h" +#include "templates.h" + + + +tabWidget::tabWidget( const QString & _caption, QWidget * _parent ) : + QWidget( _parent ), + m_curWidget( 0 ), + m_caption( _caption ) +{ + setFont( pointSize<7>( font() ) ); +#ifdef QT4 + QPalette pal = palette(); + pal.setColor( QPalette::Background, QColor( 96, 96, 96 ) ); + setPalette( pal ); +#else + setPaletteBackgroundColor( QColor( 96, 96, 96 ) ); + //setBackgroundMode( Qt::NoBackground ); +#endif +} + + + + +tabWidget::~tabWidget() +{ +} + + + +void tabWidget::addTab( QWidget * _w, const QString & _name ) +{ + widgetDesc d = { _w, _name, fontMetrics().width( _name ) + 10 } ; + m_widgets.push_back( d ); + // make sure new tab doesn't overlap current widget + m_widgets[m_curWidget].w->raise(); + _w->setFixedSize( width() - 4, height() - 14 ); + _w->move( 2, 12 ); +} + + + + +void tabWidget::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->y() > 1 && _me->y() < 13 ) + { + int cx = 14 + fontMetrics().width( m_caption ); + for( widgetStack::iterator it = m_widgets.begin(); + it != m_widgets.end(); ++it ) + { + if( _me->x() >= cx && _me->x() <= cx + it->nwidth ) + { + it->w->raise(); + m_curWidget = it - m_widgets.begin(); + update(); + return; + } + cx += it->nwidth; + } + } +} + + + + +void tabWidget::resizeEvent( QResizeEvent * ) +{ + for( widgetStack::iterator it = m_widgets.begin(); + it != m_widgets.end(); ++it ) + { + it->w->setFixedSize( width() - 4, height() - 14 ); + } +} + + + + + +void tabWidget::paintEvent( QPaintEvent * _pe ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap pm( size() ); + pm.fill( QColor( 96, 96, 96 ) ); + + QPainter p( &pm ); +#endif + + p.setPen( QColor( 64, 64, 64 ) ); + p.drawRect( 0, 0, width(), height() ); + + p.setPen( QColor( 160, 160, 160 ) ); + p.drawLine( width() - 1, 0, width() - 1, height() - 1 ); + p.drawLine( 0, height() - 1, width() - 1, height() - 1 ); + + p.setPen( QColor( 0, 0, 0 ) ); + p.drawRect( 1, 1, width() - 2, height() - 2 ); + + p.fillRect( 2, 2, width() - 4, 9, QColor( 30, 45, 60 ) ); + p.drawLine( 2, 11, width() - 3, 11 ); + + p.setPen( QColor( 255, 255, 255 ) ); + p.setFont( font() ); + p.drawText( 5, 10, m_caption ); + + int cx = 14 + fontMetrics().width( m_caption ); + + p.setFont( pointSize<6>( p.font() ) ); + + p.setPen( QColor( 160, 160, 160 ) ); + + for( widgetStack::iterator it = m_widgets.begin(); + it != m_widgets.end(); ++it ) + { + if( it - m_widgets.begin() == m_curWidget ) + { + p.setPen( QColor( 32, 48, 64 ) ); + p.fillRect( cx, 2, it->nwidth - 6, 9, + QColor( 160, 160, 160 ) ); + } + p.drawText( cx + 3, 9, it->name ); + p.setPen( QColor( 160, 160, 160 ) ); + cx += it->nwidth; + } + +#ifndef QT4 + bitBlt( this, rect().topLeft(), &pm ); +#endif +} + + + + +#include "tab_widget.moc" + diff --git a/src/widgets/text_float.cpp b/src/widgets/text_float.cpp new file mode 100644 index 0000000000..d7ac19b57a --- /dev/null +++ b/src/widgets/text_float.cpp @@ -0,0 +1,152 @@ +/* + * text_float.cpp - class textFloat, a floating text-label + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include + +#else + +#include +#include +#include + +#define setParent reparent + +#endif + + +#include "text_float.h" +#include "templates.h" + + + +textFloat::textFloat( QWidget * _parent ) : + QWidget( _parent +#ifndef QT4 + , "textFloat", WStyle_Customize | WStyle_NoBorder | WStyle_StaysOnTop +#endif + ), + m_text( "" ) +{ +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + + reparent( parentWidget() ); + resize( 20, 20 ); + hide(); +} + + + + +void textFloat::reparent( QWidget * _new_parent ) +{ + QPoint position = _new_parent->pos(); + + // Get position and reparent to either top level or dialog + // + while( _new_parent->parentWidget() && !_new_parent->isTopLevel() + && +#ifdef QT4 + !_new_parent->windowType() == Qt::Dialog +#else + !_new_parent->isDialog() +#endif + ) + { + _new_parent = _new_parent->parentWidget(); + position += _new_parent->pos(); + } + + // Position this widget to the right of the parent + // + //move(pos + QPoint(parent->width() + 5, 5)); + + QWidget::setParent( _new_parent, +#ifdef QT4 + Qt::FramelessWindowHint | + Qt::WindowStaysOnTopHint ); +#else + WStyle_Customize | WStyle_NoBorder | + WStyle_StaysOnTop, + position + QPoint( 20, 5 ) ); +#endif +} + + + + +void textFloat::setVisibilityTimeOut( int _msecs ) +{ + QTimer::singleShot( _msecs, this, SLOT( hide() ) ); + show(); +} + + + + +void textFloat::paintEvent( QPaintEvent * _pe ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( size() ); + QPainter p( &draw_pm ); +#endif + p.setPen( QColor( 0, 0, 0 ) ); + p.setBrush( QColor( 255, 255, 255 ) ); + + p.setFont( pointSize<8>( p.font() ) ); + + QFontMetrics metrics( p.fontMetrics() ); + QRect textBound = metrics.boundingRect( m_text ); + + resize( textBound.width() + 5, textBound.height() + 5 ); + p.drawRect( rect() ); + + p.setPen( Qt::black ); + p.drawText( 2, 10, m_text ); + +#ifndef QT4 + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +void textFloat::setText( const QString & _text ) +{ + m_text = _text; + repaint(); +} + + +#undef setParent + diff --git a/src/widgets/tooltip.cpp b/src/widgets/tooltip.cpp new file mode 100644 index 0000000000..2adecd3494 --- /dev/null +++ b/src/widgets/tooltip.cpp @@ -0,0 +1,59 @@ +/* + * tooltip.cpp - namespace toolTip, a tooltip-wrapper for LMMS + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include + +#else + +#include + +#endif + +#include "tooltip.h" +#include "config_mgr.h" + + +void toolTip::add( QWidget * _w, const QString & _txt ) +{ + if( !configManager::inst()->value( "tooltips", "disabled" ).toInt() ) + { +#ifdef QT4 + //_w->setToolTip( _txt ); +#else + QToolTip::add( _w, _txt ); +#endif + } +} + + +/* +void toolTip::remove( QWidget * _w ) +{ + QToolTip::remove( _w ); +} +*/ diff --git a/src/widgets/visualization_widget.cpp b/src/widgets/visualization_widget.cpp new file mode 100644 index 0000000000..a3e1ad9d4c --- /dev/null +++ b/src/widgets/visualization_widget.cpp @@ -0,0 +1,221 @@ +/* + * visualization_widget.cpp - widget for visualization of waves + * + * Linux MultiMedia Studio + * Copyright (c) 2004-2005 Tobias Doerffel + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + + +#include "qt3support.h" + +#ifdef QT4 + +#include +#include +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "visualization_widget.h" +#include "embed.h" +#include "buffer_allocator.h" +#include "templates.h" +#include "tooltip.h" + + +const int UPDATE_TIME = 1000 / 20; // 20 fps + + + +visualizationWidget::visualizationWidget( const QPixmap & _bg, QWidget * _p, + visualizationTypes _vtype ) : + QWidget( _p ), + s_background( _bg ), + m_enabled( FALSE ) +{ +#ifndef QT4 + setBackgroundMode( Qt::NoBackground ); +#endif + + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + m_buffer = bufferAllocator::alloc( frames ); + + for( Uint32 frame = 0; frame < frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + m_buffer[frame][chnl] = 0.0f; + } + } + + setFixedSize( s_background.width(), s_background.height() ); + + + m_updateTimer = new QTimer( this ); + connect( m_updateTimer, SIGNAL( timeout() ), this, SLOT( update() ) ); + if( m_enabled ) + { + m_updateTimer->start( UPDATE_TIME ); + } + + connect( mixer::inst(), SIGNAL( nextAudioBuffer( + const surroundSampleFrame *, Uint32 ) ), + this, SLOT( setAudioBuffer( + const surroundSampleFrame *, Uint32 ) ) ); + + toolTip::add( this, tr( "click to enable/disable visualization of " + "master-output" ) ); +} + + + + +visualizationWidget::~visualizationWidget() +{ +} + + + + +void visualizationWidget::setAudioBuffer( const surroundSampleFrame * _ab, + Uint32 _frames ) +{ + if( m_enabled ) + { + memcpy( m_buffer, *_ab, _frames * BYTES_PER_SURROUND_FRAME); + } +} + + + + +void visualizationWidget::paintEvent( QPaintEvent * ) +{ +#ifdef QT4 + QPainter p( this ); +#else + QPixmap draw_pm( rect().size() ); + //draw_pm.fill( this, rect().topLeft() ); + + QPainter p( &draw_pm, this ); +#endif + p.drawPixmap( 0, 0, s_background ); + + if( m_enabled ) + { + float master_output = mixer::inst()->masterOutput(); + Uint16 w = width()-4; + float half_h = -( height() - 6 ) / 3.0 * master_output - 1; + Uint16 x_base = 2; + Uint16 y_base = height()/2 - 1; + Uint16 old_y[DEFAULT_CHANNELS] = { y_base + (int)( + m_buffer[0][0]*half_h ), + y_base + (int)( + m_buffer[0][1]*half_h ) + } ; + + p.setClipRect( 2, 2, w, height()-4 ); + + float max_level = 0.0; + + const Uint32 frames = mixer::inst()->framesPerAudioBuffer(); + + // analyse wave-stream for max-level + for( Uint32 frame = 0; frame < frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < SURROUND_CHANNELS; ++chnl ) + { + if( tAbs( m_buffer[frame][chnl] ) > + max_level ) + { + max_level = tAbs( + m_buffer[frame][chnl] ); + } + } + } + // and set color according to that... + if( max_level * master_output < 0.9 ) + { + p.setPen( QColor( 96, 255, 96 ) ); + } + else if( max_level * master_output < 1.1 ) + { + p.setPen( QColor( 255, 192, 64 ) ); + } + else + { + p.setPen( QColor( 255, 64, 64 ) ); + } + + // now draw all that stuff + for( Uint32 frame = 0; frame < frames; ++frame ) + { + for( Uint8 chnl = 0; chnl < DEFAULT_CHANNELS; ++chnl ) + { + Uint16 cur_y = y_base + + (Uint16)( m_buffer[frame][chnl] * + half_h ); + Uint16 xp = x_base + frame * w / frames; + p.drawLine( xp, old_y[chnl], xp, cur_y ); + old_y[chnl] = cur_y; + } + } + + } + +#ifndef QT4 + // and blit all drawn stuff on the screen... + bitBlt( this, rect().topLeft(), &draw_pm ); +#endif +} + + + + +void visualizationWidget::mousePressEvent( QMouseEvent * _me ) +{ + if( _me->button() == Qt::LeftButton ) + { + m_enabled = !m_enabled; + if( m_enabled ) + { + m_updateTimer->start( UPDATE_TIME ); + } + else + { + m_updateTimer->stop(); + // we have to update (remove last waves), + // because timer doesn't do that anymore + update(); + } + } +} + + + +#include +