From aefcce1c99598cc8c1748940a01e2d8aa55caf85 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Tue, 6 Oct 2009 22:07:10 +0000 Subject: [PATCH] ogg/flac support bits and pieces; fix up MIDI note dragging and front-edge trims; BROKEN IN PERCUSSIVE MODE FOR NOW git-svn-id: svn://localhost/ardour2/branches/3.0@5745 d708f5d6-7413-0410-9779-e7cbd77b26cf --- gtk2_ardour/canvas-hit.h | 3 ++- gtk2_ardour/canvas-note-event.cc | 11 -------- gtk2_ardour/canvas-note-event.h | 2 +- gtk2_ardour/canvas-note.cc | 17 ++++++++++++ gtk2_ardour/canvas-note.h | 1 + gtk2_ardour/diamond.cc | 32 +++++++++++++++++----- gtk2_ardour/diamond.h | 11 +++++--- gtk2_ardour/editor.h | 4 +-- gtk2_ardour/editor_drag.cc | 17 +++++++++--- gtk2_ardour/editor_imageframe.cc | 2 -- gtk2_ardour/midi_region_view.cc | 39 ++++++++++++++++----------- gtk2_ardour/midi_region_view.h | 2 +- gtk2_ardour/public_editor.h | 2 +- gtk2_ardour/startup.cc | 14 ++++++++++ gtk2_ardour/startup.h | 1 + gtk2_ardour/wscript | 3 +-- libs/ardour/ardour/sndfile_helpers.h | 2 +- libs/ardour/audiofilesource.cc | 5 ---- libs/ardour/sndfile_helpers.cc | 6 +++++ libs/ardour/utils.cc | 11 +------- libs/ardour/wscript | 29 ++++++++++++++++++++ testfile.flac | Bin 0 -> 494 bytes testfile.ogg | Bin 0 -> 4433 bytes wscript | 1 - 24 files changed, 147 insertions(+), 68 deletions(-) create mode 100644 testfile.flac create mode 100644 testfile.ogg diff --git a/gtk2_ardour/canvas-hit.h b/gtk2_ardour/canvas-hit.h index a467952966..2a94907cd6 100644 --- a/gtk2_ardour/canvas-hit.h +++ b/gtk2_ardour/canvas-hit.h @@ -52,7 +52,8 @@ public: void set_outline_color(uint32_t c) { property_outline_color_rgba() = c; } void set_fill_color(uint32_t c) { property_fill_color_rgba() = c; } - bool on_event(GdkEvent* ev) { return CanvasNoteEvent::on_event(ev); } + bool on_event(GdkEvent* ev); + void move_event(double dx, double dy); }; } // namespace Gnome diff --git a/gtk2_ardour/canvas-note-event.cc b/gtk2_ardour/canvas-note-event.cc index 55e31db4e1..b3c6b49ee0 100644 --- a/gtk2_ardour/canvas-note-event.cc +++ b/gtk2_ardour/canvas-note-event.cc @@ -73,17 +73,6 @@ CanvasNoteEvent::validate () _valid = true; } -void -CanvasNoteEvent::move_event(double dx, double dy) -{ - _item->move(dx, dy); - if (_text) { - _text->hide(); - _text->move(dx, dy); - _text->show(); - } -} - void CanvasNoteEvent::show_velocity() { diff --git a/gtk2_ardour/canvas-note-event.h b/gtk2_ardour/canvas-note-event.h index 6b394e1bb5..a9fa6ccb2d 100644 --- a/gtk2_ardour/canvas-note-event.h +++ b/gtk2_ardour/canvas-note-event.h @@ -73,7 +73,7 @@ public: bool selected() const { return _selected; } void selected(bool yn); - void move_event(double dx, double dy); + virtual void move_event(double dx, double dy) = 0; uint32_t base_color(); diff --git a/gtk2_ardour/canvas-note.cc b/gtk2_ardour/canvas-note.cc index b43514edcc..4686a80df9 100644 --- a/gtk2_ardour/canvas-note.cc +++ b/gtk2_ardour/canvas-note.cc @@ -18,5 +18,22 @@ CanvasNote::on_event(GdkEvent* ev) } } +void +CanvasNote::move_event(double dx, double dy) +{ + property_x1() = property_x1() + dx; + property_y1() = property_y1() + dy; + property_x2() = property_x2() + dx; + property_y2() = property_y2() + dy; + + if (_text) { + _text->hide(); + _text->property_x() = _text->property_x() + dx; + _text->property_y() = _text->property_y() + dy; + _text->show(); + } +} + + } // namespace Gnome } // namespace Canvas diff --git a/gtk2_ardour/canvas-note.h b/gtk2_ardour/canvas-note.h index cd3c166951..b65eae53d6 100644 --- a/gtk2_ardour/canvas-note.h +++ b/gtk2_ardour/canvas-note.h @@ -45,6 +45,7 @@ public: void hide() { SimpleRect::hide(); } bool on_event(GdkEvent* ev); + void move_event(double dx, double dy); CanvasNote (MidiRegionView& region, Group& group, diff --git a/gtk2_ardour/diamond.cc b/gtk2_ardour/diamond.cc index 7d3e66357d..3cc198fb98 100644 --- a/gtk2_ardour/diamond.cc +++ b/gtk2_ardour/diamond.cc @@ -25,17 +25,35 @@ using namespace Gnome::Art; Diamond::Diamond(Group& group, double height) : Polygon(group) { - set_height(height); + points = gnome_canvas_points_new (4); + g_object_set (gobj(), "points", points, NULL); + set_height (height); +} + +Diamond::~Diamond () +{ + gnome_canvas_points_free (points); } void Diamond::set_height(double height) { - Points points; - points.push_back(Art::Point(0, height*2.0)); - points.push_back(Art::Point(height, height)); - points.push_back(Art::Point(0, 0)); - points.push_back(Art::Point(-height, height)); - property_points() = points; + double x1, y1, x2, y2; + + get_bounds (x1, y1, x2, y2); + + points->coords[0] = x1; + points->coords[1] = y1 + height*2.0; + + points->coords[2] = x2 + height; + points->coords[3] = y1 + height; + + points->coords[4] = x1; + points->coords[5] = y2; + + points->coords[6] = x2 -height; + points->coords[7] = y2 + height; + + g_object_set (gobj(), "points", points, NULL); } diff --git a/gtk2_ardour/diamond.h b/gtk2_ardour/diamond.h index 6afa3e3d64..adfb289c6f 100644 --- a/gtk2_ardour/diamond.h +++ b/gtk2_ardour/diamond.h @@ -28,11 +28,16 @@ namespace Gnome { namespace Canvas { -class Diamond : public Polygon { -public: +class Diamond : public Polygon +{ + public: Diamond(Group& group, double height); - + ~Diamond (); + void set_height(double height); + + protected: + GnomeCanvasPoints* points; }; diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 5b68abc7bd..76830f4474 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -195,9 +195,9 @@ class Editor : public PublicEditor void add_imageframe_marker_time_axis(const std::string & track_name, TimeAxisView* marked_track, void*) ; void connect_to_image_compositor() ; void scroll_timeaxis_to_imageframe_item(const TimeAxisViewItem* item) ; -#endif - TimeAxisView* get_named_time_axis(const std::string & name) ; +#endif /* WITH_CMT */ + void foreach_time_axis_view (sigc::slot); void add_to_idle_resize (TimeAxisView*, int32_t); diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 5e4a002e94..5aef521608 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -45,6 +45,7 @@ using namespace PBD; using namespace sigc; using namespace Gtk; using namespace Editing; +using namespace ArdourCanvas; double const ControlPointDrag::_zero_gain_fraction = gain_to_slider_position (dB_to_coefficient (0.0)); @@ -3366,7 +3367,7 @@ MouseZoomDrag::finished (GdkEvent* event, bool movement_occurred) NoteDrag::NoteDrag (Editor* e, ArdourCanvas::Item* i) : Drag (e, i) { - ArdourCanvas::CanvasNote* cnote = dynamic_cast(_item); + CanvasNoteEvent* cnote = dynamic_cast(_item); region = &cnote->region_view(); } @@ -3389,7 +3390,7 @@ NoteDrag::start_grab (GdkEvent* event, Gdk::Cursor *) last_x = region->snap_to_pixel(event_x); last_y = event_y; - ArdourCanvas::CanvasNote* cnote = dynamic_cast(_item); + CanvasNoteEvent* cnote = dynamic_cast(_item); if (!(was_selected = cnote->selected())) { @@ -3447,8 +3448,16 @@ NoteDrag::motion (GdkEvent*, bool) dy = streamview->note_height() * this_delta_note; last_y = last_y + dy; } - - region->move_selection (dx, dy); + + if (dx || dy) { + region->move_selection (dx, dy); + + CanvasNoteEvent* cnote = dynamic_cast(_item); + char buf[4]; + snprintf (buf, sizeof (buf), "%g", (int) cnote->note()->note() + drag_delta_note); + //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note())); + _editor->show_verbose_canvas_cursor_with (buf); + } } void diff --git a/gtk2_ardour/editor_imageframe.cc b/gtk2_ardour/editor_imageframe.cc index a0a400d715..4fee0c7062 100644 --- a/gtk2_ardour/editor_imageframe.cc +++ b/gtk2_ardour/editor_imageframe.cc @@ -63,7 +63,6 @@ Editor::get_named_time_axis(const string & name) } /* */ -#ifdef HAVE_CMT void Editor::add_imageframe_time_axis(const string & track_name, void* src) @@ -1115,4 +1114,3 @@ Editor::handle_new_imageframe_marker_time_axis_view(const string & track_name, T /* */ -#endif /* HAVE_CMT */ diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 14e4199bd6..e7b70d29de 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -1191,12 +1191,10 @@ MidiRegionView::update_note (CanvasNote* ev) const nframes64_t note_end_frames = beats_to_frames(note->end_time()); const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start()); - - const double y1 = midi_stream_view()->note_to_y(note->note()); const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start()); - + ev->property_x1() = x; ev->property_y1() = y1; if (note->length() > 0) { @@ -1237,7 +1235,7 @@ MidiRegionView::update_hit (CanvasHit* ev) const double diamond_size = midi_stream_view()->note_height() / 2.0; const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0); - ev->move(x, y); + ev->move_event (x, y); } /** Add a MIDI note to the view (with length). @@ -1680,24 +1678,22 @@ MidiRegionView::move_selection(double dx, double dy) } void -MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) +MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, int8_t dnote) { - // TODO: This would be faster/nicer with a MoveCommand that doesn't need to copy... - if (_selection.find(ev) == _selection.end()) { - return; - } + assert (!_selection.empty()); - uint8_t lowest_note_in_selection = midi_stream_view()->lowest_note(); - uint8_t highest_note_in_selection = midi_stream_view()->highest_note(); + uint8_t lowest_note_in_selection = 127; + uint8_t highest_note_in_selection = 0; uint8_t highest_note_difference = 0; // find highest and lowest notes first + for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) { uint8_t pitch = (*i)->note()->note(); lowest_note_in_selection = std::min(lowest_note_in_selection, pitch); highest_note_in_selection = std::max(highest_note_in_selection, pitch); } - + /* cerr << "dnote: " << (int) dnote << endl; cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note()) @@ -1707,7 +1703,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) cerr << "selection size: " << _selection.size() << endl; cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl; */ - + // Make sure the note pitch does not exceed the MIDI standard range if (dnote <= 127 && (highest_note_in_selection + dnote > 127)) { highest_note_difference = highest_note_in_selection - 127; @@ -1735,7 +1731,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) uint8_t original_pitch = (*i)->note()->note(); uint8_t new_pitch = original_pitch + dnote - highest_note_difference; - + // keep notes in standard midi range clamp_to_0_127(new_pitch); @@ -1755,7 +1751,7 @@ MidiRegionView::note_dropped(CanvasNoteEvent* ev, double dt, uint8_t dnote) // care about notes being moved beyond the upper/lower bounds on the canvas if (lowest_note_in_selection < midi_stream_view()->lowest_note() || - highest_note_in_selection > midi_stream_view()->highest_note()) { + highest_note_in_selection > midi_stream_view()->highest_note()) { midi_stream_view()->set_note_range(MidiStreamView::ContentsRange); } } @@ -1918,6 +1914,14 @@ MidiRegionView::commit_resizing (bool at_front, double delta_x, bool relative) if (at_front && current_x < canvas_note->note()->end_time()) { diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x); + + double len = canvas_note->note()->time() - current_x; + len += canvas_note->note()->length(); + + if (len > 0) { + /* XXX convert to beats */ + diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len); + } } if (!at_front) { @@ -2263,7 +2267,10 @@ MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev) } PublicEditor& editor (trackview.editor()); - editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note())); + char buf[4]; + snprintf (buf, sizeof (buf), "%d", (int) ev->note()->note()); + //editor.show_verbose_canvas_cursor_with (Evoral::midi_note_name (ev->note()->note())); + editor.show_verbose_canvas_cursor_with (buf); } void diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index 952bd9517a..a2c56a2473 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -192,7 +192,7 @@ class MidiRegionView : public RegionView size_t selection_size() { return _selection.size(); } void move_selection(double dx, double dy); - void note_dropped(ArdourCanvas::CanvasNoteEvent* ev, double d_pixels, uint8_t d_note); + void note_dropped(ArdourCanvas::CanvasNoteEvent* ev, double d_pixels, int8_t d_note); /** Return true iff the note is within the extent of the region. * @param visible will be set to true if the note is within the visible note range, false otherwise. diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h index c396131108..d7df0e8d2f 100644 --- a/gtk2_ardour/public_editor.h +++ b/gtk2_ardour/public_editor.h @@ -269,9 +269,9 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulThingWithGoingAway virtual void add_imageframe_time_axis(const std::string & track_name, void*) = 0; virtual void add_imageframe_marker_time_axis(const std::string & track_name, TimeAxisView* marked_track, void*) = 0; virtual void scroll_timeaxis_to_imageframe_item(const TimeAxisViewItem* item) = 0; + virtual TimeAxisView* get_named_time_axis(const std::string & name) = 0; #endif - virtual TimeAxisView* get_named_time_axis(const std::string & name) = 0; virtual RouteTimeAxisView* get_route_view_by_id (PBD::ID& id) = 0; virtual void get_equivalent_regions (RegionView* rv, std::vector&, ARDOUR::RouteGroup::Property) const = 0; diff --git a/gtk2_ardour/startup.cc b/gtk2_ardour/startup.cc index 370ce1b377..e4b19947a7 100644 --- a/gtk2_ardour/startup.cc +++ b/gtk2_ardour/startup.cc @@ -359,6 +359,8 @@ ArdourStartup::setup_initial_choice_page () centering_vbox->pack_start (ic_new_session_button, false, true); centering_vbox->pack_start (ic_existing_session_button, false, true); + ic_new_session_button.signal_button_press_event().connect(mem_fun(*this, &ArdourStartup::initial_choice_activated), false); + ic_existing_session_button.signal_button_press_event().connect(mem_fun(*this, &ArdourStartup::initial_choice_activated), false); centering_hbox->pack_start (*centering_vbox, true, true); @@ -377,6 +379,18 @@ ArdourStartup::setup_initial_choice_page () set_page_complete (ic_vbox, true); } +bool +ArdourStartup::initial_choice_activated(GdkEventButton *event) +{ + if (event && event->type == GDK_2BUTTON_PRESS && session_page_index != -1) + { + set_current_page(session_page_index); + return true; + } + else + return false; +} + void ArdourStartup::setup_session_page () { diff --git a/gtk2_ardour/startup.h b/gtk2_ardour/startup.h index ed8db3fbe8..b3b1379ea5 100644 --- a/gtk2_ardour/startup.h +++ b/gtk2_ardour/startup.h @@ -89,6 +89,7 @@ class ArdourStartup : public Gtk::Assistant { Gtk::VBox ic_vbox; Gtk::RadioButton ic_new_session_button; Gtk::RadioButton ic_existing_session_button; + bool initial_choice_activated(GdkEventButton *); /* monitoring choices */ diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript index 095fc4b97c..bf53167f5e 100644 --- a/gtk2_ardour/wscript +++ b/gtk2_ardour/wscript @@ -52,6 +52,7 @@ gtk2_ardour_sources = [ 'bundle_manager.cc', 'cairo_widget.cc', 'canvas-flag.cc', + 'canvas-hit.cc', 'canvas-note-event.cc', 'canvas-note.cc', 'canvas-program-change.cc', @@ -79,7 +80,6 @@ gtk2_ardour_sources = [ 'editor_export_audio.cc', 'editor_group_tabs.cc', 'editor_hscroller.cc', - 'editor_imageframe.cc', 'editor_keyboard.cc', 'editor_keys.cc', 'editor_markers.cc', @@ -342,7 +342,6 @@ def build(bld): else: key = "_".join (['FONT',sizename]) fontstyle = " ".join ([basefont,points]) - print key font_subst_dict[key] = fontstyle # RC files diff --git a/libs/ardour/ardour/sndfile_helpers.h b/libs/ardour/ardour/sndfile_helpers.h index 23ece80df2..54190dcae3 100644 --- a/libs/ardour/ardour/sndfile_helpers.h +++ b/libs/ardour/ardour/sndfile_helpers.h @@ -26,7 +26,7 @@ // Use this define when initializing arrarys for use in sndfile_*_format() #define SNDFILE_STR_LENGTH 32 -#define SNDFILE_HEADER_FORMATS 5 +#define SNDFILE_HEADER_FORMATS 7 extern const char * const sndfile_header_formats_strings[SNDFILE_HEADER_FORMATS+1]; extern const char * const sndfile_file_endings_strings[SNDFILE_HEADER_FORMATS+1]; diff --git a/libs/ardour/audiofilesource.cc b/libs/ardour/audiofilesource.cc index 705fbc4eab..fa3a86e44b 100644 --- a/libs/ardour/audiofilesource.cc +++ b/libs/ardour/audiofilesource.cc @@ -349,13 +349,8 @@ AudioFileSource::safe_audio_file_extension(const ustring& file) ".vwe", ".VWE", ".paf", ".PAF", ".voc", ".VOC", -#ifdef HAVE_OGG ".ogg", ".OGG", -#endif /* HAVE_OGG */ -#ifdef HAVE_FLAC ".flac", ".FLAC", -#else -#endif // HAVE_FLAC #ifdef HAVE_COREAUDIO ".mp3", ".MP3", ".aac", ".AAC", diff --git a/libs/ardour/sndfile_helpers.cc b/libs/ardour/sndfile_helpers.cc index 08e1117fbb..56259f0812 100644 --- a/libs/ardour/sndfile_helpers.cc +++ b/libs/ardour/sndfile_helpers.cc @@ -35,6 +35,8 @@ const char * const sndfile_header_formats_strings[SNDFILE_HEADER_FORMATS+1] = { N_("AIFF"), N_("CAF"), N_("W64 (64 bit WAV)"), + N_("FLAC"), + N_("Ogg/Vorbis"), N_("raw (no header)"), 0 }; @@ -44,6 +46,8 @@ const char* const sndfile_file_endings_strings[SNDFILE_HEADER_FORMATS+1] = { N_(".aiff"), N_(".caf"), N_(".w64"), + N_(".flac"), + N_(".ogg"), N_(".raw"), 0 }; @@ -53,6 +57,8 @@ int sndfile_header_formats[SNDFILE_HEADER_FORMATS] = { SF_FORMAT_AIFF, SF_FORMAT_CAF, SF_FORMAT_W64, + SF_FORMAT_FLAC, + SF_FORMAT_OGG, SF_FORMAT_RAW }; diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index c8e6b1145f..1ecaba55e5 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -517,16 +517,7 @@ string_is_affirmative (const std::string& str) { /* to be used only with XML data - not intended to handle user input */ - if (str == "1" || str == "y" || str == "Y") { - return true; - } else { - std::string str_uc; - std::transform(str.begin(), str.end(), str_uc.begin(), ::toupper); - if (str_uc == "YES") { - return true; - } - } - return false; + return str == "1" || str == "y" || str == "Y" || (!g_strncasecmp(str.c_str(), "yes", str.length())); } extern "C" { diff --git a/libs/ardour/wscript b/libs/ardour/wscript index 0f64c32f54..fd71f2e6a7 100644 --- a/libs/ardour/wscript +++ b/libs/ardour/wscript @@ -3,6 +3,8 @@ import autowaf import os import glob import Options +import re +import subprocess from w18n import build_i18n # Version of this package (even if built as a child) @@ -119,6 +121,7 @@ libardour_sources = [ 'midi_track.cc', 'mix.cc', 'mtc_slave.cc', + 'mtdm.cc', 'mute_master.cc', 'named_selection.cc', 'onset_detector.cc', @@ -181,6 +184,20 @@ libardour_sources = [ 'version.cc' ] +def flac_supported(): + cmd = subprocess.Popen ("sndfile-info testfile.flac", + stdout = subprocess.PIPE, + stderr = subprocess.STDOUT, shell = True) + out = cmd.communicate()[0]; + return re.search ('unknown format', out) == None + +def ogg_supported(): + cmd = subprocess.Popen ("sndfile-info testfile.ogg", + stdout = subprocess.PIPE, + stderr = subprocess.STDOUT, shell = True) + out = cmd.communicate()[0]; + return re.search ('unknown format', out) == None + def set_options(opt): autowaf.set_options(opt) @@ -210,12 +227,24 @@ def configure(conf): conf.check(header_name='sys/vfs.h', define_name='HAVE_SYS_VFS_H') conf.check(header_name='wordexp.h', define_name='HAVE_WORDEXP') + if flac_supported(): + conf.define ('HAVE_FLAC', 1) + autowaf.display_msg(conf, 'Checking for FLAC support', True) + else: + autowaf.display_msg(conf, 'Checking for FLAC support', False) + if ogg_supported(): + conf.define ('HAVE_OGG', 1) + autowaf.display_msg(conf, 'Checking for Ogg/Vorbis support', True) + else: + autowaf.display_msg(conf, 'Checking for Ogg/Vorbis Support', False) + conf.write_config_header('libardour-config.h') # Boost headers autowaf.check_header(conf, 'boost/shared_ptr.hpp') autowaf.check_header(conf, 'boost/weak_ptr.hpp') + def build(bld): # Library obj = bld.new_task_gen('cxx', 'shlib') diff --git a/testfile.flac b/testfile.flac new file mode 100644 index 0000000000000000000000000000000000000000..af7a7eca2051b5d4aef51935eef6e94e6dd97eab GIT binary patch literal 494 zcmWNMv5ONy9EHcT(#AtX1PgkBbFaDI z+A9%s`S1N5v}OH#jL_m@dbO7`@F1tS3m<=wSYb+Z5Nkgt+@lRghvre3P|6^&O<#gt z#sL+C7R4-31SAjUk|$8eI8DDcmUde!ur$pe3ubfWnoz_SJz=sS zF^$QP<*Ziz-T_BSzPP#Ox`0TKiHNds=r(kS2gA}JqqX!vC6)>4Oj*Qb!L8H(!QRjb literal 0 HcmV?d00001 diff --git a/testfile.ogg b/testfile.ogg new file mode 100644 index 0000000000000000000000000000000000000000..afc5a7a823e01fca1b673df72143032753177556 GIT binary patch literal 4433 zcmeGge^8T0c0Ul3AVq`-0g*{ClmvrAAb5nsn~x}(L;~dll%U*|A5jq7lSG_yQ)f~I z1PUC6KrtW#A&DkMrgB2Xo0~&hehA7Bk)qd`lWK)p=WaN?n|goUz5Nm>=XKhf>D+(a z*=%;-d;8w*`?34>OFMET1GynrX)p$?$K8m{-M5KQ;^eQj`NcX6aNFI3|DcpQiLVi9 zc;;%sGYJ^&_mH}8jQZCr#ofti3lL-%A1~UTrai(vR-C_|=Fe4dW1^#_(LalpVs?aw zlUOL*wVyytfQVDL!}{sQEQEX!dXq2oX*#SBT8&+T@>Zi9_lBcuT8%ZsoPs*>$V$VN z6mCLj9nuR}4c&{1qm$AjoQMW(r?faV{vwxAo4z1r-TcSv1#jD~+DhJ@6oxZjQ+vQh z5h>?YvMJo;SM#bk(3}m?j`1$GYrP^bb)NQ$8RMQwiFs;iN=!$*6I+Xjk09C&M@ zPc+fzO_Lx3NO0*aHrf8G9#*$#ez=lFW08miU5=K9jF$3~DgLNd#Go}ihQOvIB^uUB z_Ua^~xErsLfhda+tn{<;3ZpLv4{im;wIP4AqGP=zn;#VZZ%%1$q~ z7-}QO1;4u;GaY5*rDr{%1f;MpXF?`8Y!jF>WnBb zDz~sP$uo!!lB!nixbPxG#ahEH^QSg$nORe({uYw%7Y=nMCMqL#XI5s`fs=2kKPdbB zn+?!p?8u#s)68aR=1yuJ3}_C1d`dmw3K(bDJe)6E z6FE3U!lVt4<%V_NMoy=o;Zc0UR)vADHoRWma-_y&Ki~1xZuWX5a*Ap%w$@&(u3f5b z5Y)63*O(lG=40c%&#wPqU5%W*T0T^WoMbJ3P2>!TLv}$l^@&t3oO|3k0}4Hn9Jtm1 z2n{uQ@1xI=bSi3&ikqXNvNCsmf5ZTGPNJHXfMS~w3PR{hi|yWTeEjkor8P-KOvR@m z!-o={ws#+@JKMN*kK!-9jN88J`dg~n|6!$mQRRBYmoKfW!X49!SL|DB|NvUB+3S`Z{jUkBC21_aGy5;GE) zERZr3n@mdJCi(H@J7lHD5ov2=krEY|srvDfe&X2Y#8%|+3jdKG{i#3>o!_m7QC}$-qpq%$G7ny8!2izni zO*mJU8(58D4!B+1Fi}?Se6vPh4D+t{irh1uJuTf zFhn5k5HsP{r9S3n)wN~B4()!++tc}lU>yTDK8)s>eblk*BGDpF|ml0}9$Q%Q2G zg^E!|Sz9O%Ub8`I=UOOA=z;WG;^CLgtgPx;hn_>WS76M)EnVWvl;Xv9ogdH2f%L}C zF2FgC(IAFAIaPV96<`t8LZEAJq7ne+0}00oAh8%J*;nm4$UiOs34utbwrh{2M*C&b z>fVHg7O=TGB0${=gRQ(H0`k_Q28$D7N}O_Ozr4exV{I;211ln?DR@o0Q*h+~=IS8;T!<>@(WI@|Oj4inw zCw!vYcV#Z8pr`-!?m7d8_4I!hW~5?9*cs3>RYZ?`!zt(yFXc4(-Fz9E5e390;0s#| z>+K^s;5aNzU47J?Q8(#<$erUS?G~A?dh4R8_rqI(~Sr=(-ZEBHCv{K&r+r%g8#CxSr zGG(J2II>S8cw2?;t9KA$H zPU)+wix%S(5`RI;cJGlZQ&Qi61rYQtNg{&DbUsG$n8~CUyDkN<5DH!_&TEgnX8NN$ JK@iBx=idvtAA