تعد Neutone SDK أداة للباحثين الذين يمكّنونهم من لف نماذج الصوت الخاصة بهم وتشغيلها في DAW باستخدام البرنامج المساعد النيتي. نحن نقدم وظائف لكل من تحميل النماذج محليًا في البرنامج المساعد والمساهمة بها في القائمة الافتراضية للنماذج المتوفرة لأي شخص يقوم بتشغيل المكون الإضافي. نأمل أن يمكّن هذا الباحثين من تجربة نماذجهم بسهولة في DAW ، ولكن أيضًا تزويد المبدعين بمجموعة من النماذج المثيرة للاهتمام.
Juce هو معيار الصناعة لبناء المكونات الإضافية الصوتية. ولهذا السبب ، هناك حاجة إلى معرفة C ++ لتكون قادرة على إنشاء ملحقات صوتية بسيطة للغاية. ومع ذلك ، من النادر أن يتمتع باحثو AI Audio بخبرة واسعة مع C ++ وأن يكونوا قادرين على بناء مثل هذا البرنامج المساعد. علاوة على ذلك ، إنه استثمار خطير يمكن إنفاقه في تطوير خوارزميات أفضل. تتيح النيوترون إنشاء نماذج باستخدام أدوات مألوفة مثل Pytorch ومع الحد الأدنى من كود بيثون لف هذه النماذج بحيث يمكن تنفيذها بواسطة المكون الإضافي النيتي. يمكن أن يتم تشغيل نموذج وتشغيل داخل DAW في أقل من يوم واحد دون أي حاجة إلى رمز أو معرفة C ++.
يوفر SDK الدعم للتخزين التلقائي للمدخلات والمخرجات إلى النموذج الخاص بك ومعدل العينة على الطيران وتحويل الستريو. إنه يتيح نموذجًا لا يمكن تنفيذه إلا بعدد محدد مسبقًا من العينات لاستخدامه في DAW في أي معدل أخذ العينات وأي حجم مخزن مؤقت. بالإضافة إلى ذلك ، في ضمن أدوات SDK للمعايير والتوصيف تتوفر بسهولة حتى تتمكن من تصحيح واختبار أداء النماذج الخاصة بك بسهولة.
يمكنك تثبيت neutone_sdk باستخدام PIP:
pip install neutone_sdk
يتوفر المكون الإضافي للنيوتون على https://neutone.space. نقدم حاليًا مكونات VST3 و AU التي يمكن استخدامها لتحميل النماذج التي تم إنشاؤها باستخدام SDK. يرجى زيارة الموقع لمزيد من المعلومات.
إذا كنت ترغب فقط في لف نموذج دون أن تمر بوصفًا مفصلاً لما كل ما أعددناه هذه الأمثلة لك.
يوفر SDK وظيفة لالتفاف نماذج Pytorch الحالية بطريقة يمكن أن تجعلها قابلة للتنفيذ ضمن البرنامج المساعد VST. في جوهره ، يقوم المكون الإضافي بإرسال أجزاء من عينات الصوت بمعدل عينة معين كإدخال ويتوقع نفس كمية العينات في الإخراج. يمكن لمستخدم SDK تحديد معدل (معدلات) العينة وحجم (حجم) العازلة التي تؤديها نماذجها على النحو الأمثل. ثم يضمن SDK أن تتلقى التمريرة الأمامية للنموذج صوتًا في واحدة من هذه المجموعات (sample_rate ، buffer_size). تتوفر أربعة مقابض تتيح لمستخدمي البرنامج المساعد التغذية في معلمات إضافية للنموذج في وقت التشغيل. يمكن تمكينها أو تعطيلها حسب الحاجة عبر SDK.
باستخدام وظيفة التصدير المضمنة ، يتم تشغيل سلسلة من الاختبارات تلقائيًا لضمان تتصرف النماذج كما هو متوقع وأن تكون جاهزة للتحميل بواسطة المكون الإضافي.
تتوفر أدوات CLI للمعايير والتوصيف لمزيد من التصحيح واختبار النماذج ملفوفة. من الممكن تحديد سرعة وضيق نموذج ما في مجموعة من مجموعات DAW المشتركة المحاكاة (sample_rate ، buffere_size) بالإضافة إلى ملف تعريف الذاكرة واستخدام وحدة المعالجة المركزية.
نحن نقدم عدة نماذج في دليل الأمثلة. سنذهب من خلال واحدة من أبسط النماذج ، نموذج تشويه ، لتوضيح.
افترض أن لدينا نموذج Pytorch التالي. سيتم تغطية المعلمات في وقت لاحق ، وسوف نركز على المدخلات والمخرجات في الوقت الحالي. افترض أن هذا النموذج يتلقى موتر الشكل (2, buffer_size) كمدخل حيث يكون buffer_size معلمة يمكن تحديدها.
class ClipperModel ( nn . Module ):
def forward ( self , x : Tensor , min_val : float , max_val : float , gain : float ) -> Tensor :
return torch . clip ( x , min = min_val * gain , max = max_val * gain )لتشغيل هذا داخل VST ، أبسط غلاف يمكننا الكتابة هو من خلال التصنيف الفرعي لـ waveformtowaveformbase baseclass.
class ClipperModelWrapper ( WaveformToWaveformBase ):
@ torch . jit . export
def is_input_mono ( self ) -> bool :
return False
@ torch . jit . export
def is_output_mono ( self ) -> bool :
return False
@ torch . jit . export
def get_native_sample_rates ( self ) -> List [ int ]:
return [] # Supports all sample rates
@ torch . jit . export
def get_native_buffer_sizes ( self ) -> List [ int ]:
return [] # Supports all buffer sizes
def do_forward_pass ( self , x : Tensor , params : Dict [ str , Tensor ]) -> Tensor :
# ... Parameter unwrap logic
x = self . model . forward ( x , min_val , max_val , gain )
return x الطريقة التي تقوم بها معظم العمل هي do_forward_pass . في هذه الحالة ، يكون مجرد تمرير بسيط ، لكننا سنستخدمه للتعامل مع المعلمات لاحقًا.
بشكل افتراضي ، يعمل VST كـ stereo-stereo ولكن عندما يكون Mono مطلوبًا للنموذج ، يمكننا استخدام is_input_mono و is_output_mono لإبلاغ SDK وتحويل المدخلات والمخرجات تلقائيًا. إذا تم تبديل is_input_mono ، فسيتم تمرير الموتر المتوسط (1, buffer_size) كمدخل بدلاً من (2, buffer_size) . إذا تم تبديل is_output_mono ، فمن المتوقع أن تقوم do_forward_pass بإرجاع موتر أحادي (الشكل (1, buffer_size) ) الذي سيتم تكراره عبر كلتا القناتين عند إخراج VST. يتم ذلك داخل SDK لتجنب تخصيصات الذاكرة غير الضرورية أثناء كل تمريرة.
يمكن استخدام get_native_sample_rates و get_native_buffer_sizes لتحديد أي معدلات عينة مفضلة أو أحجام عازلة. في معظم الحالات ، من المتوقع أن يكون لها عنصر واحد فقط ولكن يتم توفير مرونة إضافية لنماذج أكثر تعقيدًا. في حالة توفير خيارات متعددة ، ستحاول SDK العثور على أفضل واحد للإعداد الحالي لـ DAW. كلما كان معدل العينة أو حجم المخزن المؤقت مختلفًا عن واحد من DAW ، يتم تشغيل الغلاف تلقائيًا يتحول إلى معدل أخذ العينات الصحيح أو ينفذ قائمة انتظار FIFO لحجم المخزن المؤقت المطلوب أو كليهما. سيؤدي هذا إلى إرضاء عقوبة أداء صغيرة ويضيف قدرًا من التأخير. في حالة توافق النموذج مع أي معدل عينة و/أو حجم المخزن المؤقت ، يمكن ترك هذه القوائم فارغة.
هذا يعني أن Tensor x في طريقة do_forward_pass مضمونة ليكون الشكل (1 if is_input_mono else 2, buffer_size) حيث سيتم اختيار buffer_size في وقت التشغيل من القائمة المتوفرة في طريقة get_native_buffer_sizes . سيكون Tensor x أيضًا في أحد معدلات أخذ العينات من القائمة المقدمة في طريقة get_native_sample_rates .
نحن نقدم وظيفة مساعد save_neutone_model تقوم بحفظ النماذج إلى القرص. بشكل افتراضي ، سيقوم هذا بتحويل النماذج إلى Torchscript وتشغيلها من خلال سلسلة من الشيكات للتأكد من أنه يمكن تحميلها بواسطة المكون الإضافي. يمكن تحميل ملف model.nm الناتج داخل المكون الإضافي باستخدام زر load your own . اقرأ أدناه للحصول على كيفية إرسال النماذج إلى المجموعة الافتراضية المرئية للجميع باستخدام البرنامج المساعد.
بالنسبة للنماذج التي يمكنها استخدام إشارات التكييف ، فإننا نقدم حاليًا أربعة معلمات مقبض قابلة للتكوين. ضمن ClipperModelWrapper المحددة أعلاه ، يمكننا تضمين ما يلي:
class ClipperModelWrapper ( WaveformToWaveformBase ):
...
def get_neutone_parameters ( self ) -> List [ NeutoneParameter ]:
return [ NeutoneParameter ( name = "min" , description = "min clip threshold" , default_value = 0.5 ),
NeutoneParameter ( name = "max" , description = "max clip threshold" , default_value = 1.0 ),
NeutoneParameter ( name = "gain" , description = "scale clip threshold" , default_value = 1.0 )]
def do_forward_pass ( self , x : Tensor , params : Dict [ str , Tensor ]) -> Tensor :
min_val , max_val , gain = params [ "min" ], params [ "max" ], params [ "gain" ]
x = self . model . forward ( x , min_val , max_val , gain )
return x أثناء المرور إلى الأمام ، سيكون متغير params عبارة عن قاموس مثل ما يلي:
{
"min" : torch . Tensor ([ 0.5 ] * buffer_size ),
"max" : torch . Tensor ([ 1.0 ] * buffer_size ),
"gain" : torch . Tensor ([ 1.0 ] * buffer_size )
} يتم تحديد مفاتيح القاموس في وظيفة get_parameters .
ستأخذ المعلمات دائمًا القيم بين 0 و 1 ويمكن استخدام وظيفة do_forward_pass للقيام بأي عملية إزالة ضرورية قبل تشغيل الطريقة الداخلية للأمام للنموذج.
علاوة على ذلك ، تأتي المعلمات المرسلة بواسطة المكون الإضافي في الحبيبية على مستوى العينة. بشكل افتراضي ، نأخذ متوسط كل المخزن المؤقت وإرجاع تعويم واحد (كموتر) ، ولكن يمكن استخدام طريقة aggregate_param لتجاوز طريقة التجميع. راجع ملف تصدير Clipper الكامل للحصول على مثال على الحفاظ على هذه التفاصيل.
ستؤخر بعض النماذج الصوتية الصوت لكمية معينة من العينات. هذا يعتمد على بنية كل نموذج معين. من أجل أن تكون الإشارة الرطبة والجافة التي تمر عبر المكون الإضافي لمحاذاة المستخدمين مطلوبة للإبلاغ عن عدد عينات تأخير نموذجها. يمكن استخدام calc_model_delay_samples لتحديد عدد عينات التأخير. تحتوي نماذج الهذيان في المتوسط على عازلة تأخير واحدة (2048 عينات) والتي يتم توصيلها بشكل ثابت في طريقة calc_model_delay_samples ويمكن رؤيتها في الأمثلة. سيكون للنماذج التي يتم تنفيذها مع التداخل المعدمي تأخير مساوٍ لعدد العينات المستخدمة في عمليات الصياغة المتقاطعة كما هو موضح في غلاف نموذج Demucs أو مثال المرشح الطيفي.
يمكن أن يكون حساب التأخير الذي يضيفه النموذج الخاص بك أمرًا صعبًا ، خاصة وأن هناك مصادر مختلفة للتأخير التي يجب دمجها (على سبيل المثال تأخير القوس ، تأخير الفلتر ، تأخير مخزن المؤقت ، و / أو الشبكات العصبية المدربة على الصوت الجاف والرطب غير المحدد) . يجدر قضاء بعض الوقت الإضافي في اختبار النموذج في DAW الخاص بك للتأكد من الإبلاغ عن التأخير بشكل صحيح.
يمكن أن تكون إضافة عازلة Lookbehind إلى النموذج الخاص بك مفيدة للنماذج التي تتطلب قدرًا معينًا من السياق الإضافي لإخراج نتائج مفيدة. يمكن تمكين المخزن المؤقت Lookbehind بسهولة من خلال الإشارة إلى عدد عينات Lookbehind التي تحتاجها في طريقة get_look_behind_samples . عندما تُرجع هذه الطريقة رقمًا أكبر من الصفر ، فإن طريقة do_forward_pass ستحصل دائمًا على موتر من الشكل (in_n_ch, look_behind_samples + buffer_size) ، ولكن لا يزال يتعين عليهم إرجاع موتر من الشكل (out_n_ch, buffer_size) لأحدث العينات.
نوصي بتجنب استخدام مخزن مؤقت للبحث عندما يكون ذلك ممكنًا لأنه يجعل النموذج أقل كفاءة ويمكن أن يؤدي إلى حسابات ضائعة أثناء كل تمريرة للأمام. إذا كنت تستخدم نموذجًا تلافيفيًا بحتًا ، فحاول تبديل جميع الملاحظات إلى التلوينات المخزنة مؤقتًا بدلاً من ذلك.
من الشائع أن تتصرف نماذج الذكاء الاصطناعي بطرق غير متوقعة عند تقديمها مع مدخلات خارج تلك الموجودة في توزيع التدريب. نحن نقدم سلسلة من المرشحات الشائعة (باس منخفضة ، مرور عالي ، ممر النطاق ، توقف النطاق) في ملف NETONE_SDK/Filters.py. يمكن استخدامها أثناء التمريرة الأمامية لتقييد مجال المدخلات التي تدخل النموذج. يمكن لبعضها أن يحفز كمية صغيرة من التأخير ، تحقق من ملف الأمثلة/example_clipper_prefilter.py للحصول على مثال بسيط على كيفية إعداد مرشح.
يحتوي المكون الإضافي على قائمة افتراضية من النماذج التي تهدف إلى المبدعين الذين يرغبون في الاستفادة منها أثناء عمليتهم الإبداعية. نشجع المستخدمين على تقديم نماذجهم بمجرد أن يكونوا سعداء بالنتائج التي يحصلون عليها حتى يمكن أن يستخدمهم المجتمع ككل. من أجل التقديم ، نطلب بعض البيانات الوصفية الإضافية التي سيتم استخدامها لعرض معلومات حول النموذج الذي يستهدف كل من المبدعين والباحثين الآخرين. سيتم عرض ذلك على كل من موقع Neutone وداخل المكون الإضافي.
تخطي طراز المقص السابق ، إليك مثال أكثر واقعية يعتمد على نموذج Overdrive TCN عشوائي مستوحى من Micro-TCN.
class OverdriveModelWrapper ( WaveformToWaveformBase ):
def get_model_name ( self ) -> str :
return "conv1d-overdrive.random"
def get_model_authors ( self ) -> List [ str ]:
return [ "Nao Tokui" ]
def get_model_short_description ( self ) -> str :
return "Neural distortion/overdrive effect"
def get_model_long_description ( self ) -> str :
return "Neural distortion/overdrive effect through randomly initialized Convolutional Neural Network"
def get_technical_description ( self ) -> str :
return "Random distortion/overdrive effect through randomly initialized Temporal-1D-convolution layers. You'll get different types of distortion by re-initializing the weight or changing the activation function. Based on the idea proposed by Steinmetz et al."
def get_tags ( self ) -> List [ str ]:
return [ "distortion" , "overdrive" ]
def get_model_version ( self ) -> str :
return "1.0.0"
def is_experimental ( self ) -> bool :
return False
def get_technical_links ( self ) -> Dict [ str , str ]:
return {
"Paper" : "https://arxiv.org/abs/2010.04237" ,
"Code" : "https://github.com/csteinmetz1/ronn"
}
def get_citation ( self ) -> str :
return "Steinmetz, C. J., & Reiss, J. D. (2020). Randomized overdrive neural networks. arXiv preprint arXiv:2010.04237."تحقق من وثائق الأساليب داخل Core.py ، وكذلك نموذج Overdrive العشوائي على الموقع الإلكتروني وفي المكون الإضافي لفهم مكان عرض كل حقل.
لتقديم نموذج ، يرجى فتح مشكلة على مستودع GitHub. نحتاج حاليًا إلى ما يلي:
save_neutone_model model.nm يوفر SDK ثلاث أدوات CLI التي يمكن استخدامها لتصحيح النماذج ملفوفة واختبارها.
مثال:
$ python -m neutone_sdk.benchmark benchmark-speed --model_file model.nm
INFO:__main__:Running benchmark for buffer sizes (128, 256, 512, 1024, 2048) and sample rates (48000,). Outliers will be removed from the calculation of mean and std and displayed separately if existing.
INFO:__main__:Sample rate: 48000 | Buffer size: 128 | duration: 0.014±0.002 | 1/RTF: 5.520 | Outliers: [0.008]
INFO:__main__:Sample rate: 48000 | Buffer size: 256 | duration: 0.028±0.003 | 1/RTF: 5.817 | Outliers: []
INFO:__main__:Sample rate: 48000 | Buffer size: 512 | duration: 0.053±0.003 | 1/RTF: 6.024 | Outliers: []
INFO:__main__:Sample rate: 48000 | Buffer size: 1024 | duration: 0.106±0.000 | 1/RTF: 6.056 | Outliers: []
INFO:__main__:Sample rate: 48000 | Buffer size: 2048 | duration: 0.212±0.000 | 1/RTF: 6.035 | Outliers: [0.213]
سيقوم تشغيل المعيار السريع تلقائيًا بتشغيل مدخلات عشوائية من خلال النموذج بمعدل عينة قدره 48000 وأحجام عازلة من (128 ، 256 ، 512 ، 1024 ، 2048) والإبلاغ عن متوسط الوقت المستغرق لتنفيذ الاستدلال لمخزن مؤقت واحد. من هذا ، يتم حساب 1/RTF والذي يمثل مدى أسرع من الوقت الحقيقي الذي يوجد فيه النموذج. مع ارتفاع هذا الرقم ، سيستخدم النموذج موارد أقل داخل DAW. من الضروري أن يكون هذا الرقم أكبر من 1 حتى يتم تنفيذ النموذج في الوقت الفعلي على الجهاز الذي يتم تشغيله.
يتم اختبار معدلات العينة وأحجام المخزن المؤقت ، بالإضافة إلى عدد المرات التي يتم فيها تكرار المعيار داخليًا لحساب المتوسطات ويتوفر عدد مؤشرات الترابط المستخدمة للحساب كمعلمات. قم بتشغيل python -m neutone_sdk.benchmark benchmark-speed --help لمزيد من المعلومات. عند تحديد معدلات العينة المخصصة أو أحجام المخزن المؤقت ، يجب نقل كل فرد إلى CLI بشكل منفصل. على سبيل المثال: --sample_rate 48000 --sample_rate 44100 --buffer_size 32 --buffer_size 64 .
على الرغم من أن معيار السرعة يجب أن يكون سريعًا لأن النماذج مطلوبة بشكل عام لتكون في الوقت الفعلي ، فمن الممكن أن تتعثر إذا كان النموذج بطيئًا للغاية. تأكد من اختيار عدد متدرب من معدلات العينة وأحجام المخزن المؤقت للاختبار.
مثال:
$ python -m neutone_sdk.benchmark benchmark-latency model.nm
INFO:__main__:Native buffer sizes: [2048], Native sample rates: [48000]
INFO:__main__:Model exports/ravemodel/model.nm has the following delays for each sample rate / buffer size combination (lowest delay first):
INFO:__main__:Sample rate: 48000 | Buffer size: 2048 | Total delay: 0 | (Buffering delay: 0 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 1024 | Total delay: 1024 | (Buffering delay: 1024 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 512 | Total delay: 1536 | (Buffering delay: 1536 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 256 | Total delay: 1792 | (Buffering delay: 1792 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 128 | Total delay: 1920 | (Buffering delay: 1920 | Model delay: 0)
INFO:__main__:Sample rate: 48000 | Buffer size: 128 | Total delay: 1920 | (Buffering delay: 1920 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 256 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 512 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 1024 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0)
INFO:__main__:Sample rate: 44100 | Buffer size: 2048 | Total delay: 2048 | (Buffering delay: 2048 | Model delay: 0) سيؤدي تشغيل معيار السرعة تلقائيًا إلى حساب زمن انتقال النموذج في مجموعات من sample_rate=(44100, 48000) و buffer_size=(128, 256, 512, 1024, 2048) . هذا يعطي نظرة عامة عامة على ما سيحدث لإعدادات DAW المشتركة. يتم تقسيم التأخير الكلي إلى تأخير التخزين المؤقت وتأخير النموذج. تم الإبلاغ عن تأخير النموذج من قبل منشئ النموذج في غلاف النموذج كما هو موضح أعلاه. يتم حساب تأخير التخزين المؤقت تلقائيًا بواسطة SDK مع الأخذ في الاعتبار مزيج (sample_rate, buffer_size) المحددة بواسطة الغلاف (الأصليين) والمواصفات المحددة بواسطة DAW في وقت التشغيل. إن تشغيل النموذج في مجموعة (sample_rate, buffer_size) سيؤخر تأخير الحد الأدنى.
على غرار معيار السرعة أعلاه ، يمكن تحديد مجموعات تم اختبارها لـ (sample_rate, buffer_size) من CLI. قم بتشغيل python -m neutone_sdk.benchmark benchmark-latency --help لمزيد من المعلومات.
$ python -m neutone_sdk.benchmark profile --model_file exports/ravemodel/model.nm
INFO:__main__:Profiling model exports/ravemodel/model.nm at sample rate 48000 and buffer size 128
STAGE:2023-09-28 14:34:53 96328:4714960 ActivityProfilerController.cpp:311] Completed Stage: Warm Up
30it [00:00, 37.32it/s]
STAGE:2023-09-28 14:34:54 96328:4714960 ActivityProfilerController.cpp:317] Completed Stage: Collection
STAGE:2023-09-28 14:34:54 96328:4714960 ActivityProfilerController.cpp:321] Completed Stage: Post Processing
INFO:__main__:Displaying Total CPU Time
INFO:__main__:-------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
Name Self CPU % Self CPU CPU total % CPU total CPU time avg CPU Mem Self CPU Mem # of Calls
-------------------------------- ------------ ------------ ------------ ------------ ------------ ------------ ------------ ------------
forward 98.54% 799.982ms 102.06% 828.603ms 26.729ms 0 b -918.17 Kb 31
aten::convolution 0.12% 963.000us 0.95% 7.739ms 175.886us 530.62 Kb -143.50 Kb 44
...
...
Full output removed from GitHub.
ستقوم أداة التنميط بتشغيل النموذج بمعدل عينة قدره 48000 وحجم مخزن مؤقت يبلغ 128 تحت Pytorch Profiler وإخراج سلسلة من الأفكار ، مثل إجمالي وقت وحدة المعالجة المركزية ، واستخدام ذاكرة وحدة المعالجة المركزية (لكل وظيفة) واستخدام ذاكرة وحدة المعالجة المركزية المجمعة (لكل مجموعة من مكالمات الوظائف). يمكن استخدام هذا لتحديد الاختناقات في رمز النموذج الخاص بك (حتى في مكالمة النموذج في مكالمة do_forward_pass ).
على غرار القياس ، يمكن تشغيله في مجموعات مختلفة من معدلات العينة وأحجام المخزن المؤقت وكذلك أعداد مختلفة من المواضيع. قم بتشغيل python -m neutone_sdk.benchmark profile --help لمزيد من المعلومات.
نرحب بأي مساهمات في SDK. يرجى إضافة الأنواع كلما أمكن ذلك واستخدام التنسيق black لقابلية القراءة.
خارطة الطريق الحالية هي:
كان مشروع Audacitorch مصدر إلهام كبير لتطوير SDK. تحقق من ذلك هنا