المصدر- مع الدوت نت
المحتويات
محتويات هذا الدرس:
- المحتويات
- نظرة خاطفة
- مقدمة
- أوامر MCI
- خيارات الانتظار، الإعلام، والتجربة
- معالجة الأخطاء
- التسجيل Recording
- التجهيز Opening
- بدأ التسجيل Recording
- الإيقاف المؤقت Pausing
- الاستمرار Resuming
- الإيقاف Stopping
- الحالة Status
- الحفظ Saving
- الإنهاء Closing
[*]التشغيل Playing
- التحميل Loading
- بدأ التشغيل Playing
- الإيقاف المؤقت Pausing
- الاستمرار Resuming
- معرفة المكان الحالي
- الانتقال إلى مكان معين Seeking
- الإنهاء Closing
- MCI والدوت نت
- الأمثلة
نظرة خاطفة
درسنا في هذا اليوم يتكلم عن كيفية تسجيل الصوت أو بالأصح إنشاء مسجل صوتي في C و C#. سنبدأ أولا بشرح الأفكار والدوال التي سنحتاجها وكيفية برمجتها في لغة C ثم بعد ذلك سنتبعها بكود الدوت نت و C#.
بالإضافة إلى ذلك، فالدرس لا يتكلم فقط عن كيفية تسجيل الصوت، بل عن كيفية تشغيل الصوت أيضا والتحكم فيه.
أيضا يركز هذا الدرس على تقنية MCI أو Media Control Interface حيث هي الأسهل والأشهر والأقوى.
يمكنك أيضا تحميل مثالين برنامجين تسجيل صوت أحدهما بالـ C والآخر بالـ C#.
مقدمة
هناك العديد من التقنيات أو الطرق التي تستطيع من خلالها التعامل مع الصوتيات أو الـ Multimedia بصفة عامة. ومن أهم هذه التقنيات:
- DirectX:
يوفر DirectX مكتبات جاهزة سهلة الاستخدام للتحكم في الصوتيات والـ Multimedia. ويمكنك إضافة هذه المكتبات إلى مشروعك وبدأ العمل مباشرة. - Waveform:
وهذه التقنية توفر إمكانيات متقدمة جدا للتعامل مع الصوتيات مثل معالجة بيانات الصوت وغيرها. وهذه التقنية متوفرة من خلال دوال الويندوز Windows API. - MCI (Media Control Interface):
واجهة التحكم في الوسائط المتعدد MCI. وهذه التقنية هي الأسهل والأشهر والأقوى، وهي التي نركز عليها في درسنا اليوم.
للأسف لا توفر بيئة الدوت نت طريقة للتعامل مع الصوتيات أبدا أو مع أي من هذه التقنيات المذكورة. ومع ذلك يمكنك الحصول على مكتبات جاهزة للدوت نت من خلال المؤسسات المعروفة والمشهورة التي توفر لك هذه الخدمة.
لهذا، فإننا في درسنا اليوم سنحتاج إلى التعامل مع دوال الويندوز Windows API حيث أن تقنية MCI متوفرة من خلال هذا المصدر من خلال المكتبة WinMM.dll.
بالنسبة للغة C سوف نقوم بالتعامل مع هذه الدوال مباشرة. فقط يجب عليك إضافة ملف المكتبة WinMM.lib إلى مشروعك. أما لغة C# فإننا سوف نحتاج إلى تعريف الدوال Platform Invokation في الكود كي نستطيع من التعامل معها، فإنك لا يمكنك التعامل مع الـ Windows API من خلال الدوت نت مباشرة. وهذا ما سنتعلمه في هذا الدرس.
تقنية MCI أو Media Control Interface هي أحد التقنية عالية الجودة سهلة الاستخدام التي توفر إمكانيات التحكم في جميع أنواع الوسائط المتعددة Multimedia (من صوتيات ومرئيات، ملفات، أو CDs، وما إلى غير ذلك.)
ويمكنك التحكم في تقنية MCI باستخدام طريقتين:
- أوامر Commands:
لكل أمر رمزه الخاص. فهناك أمر خاص بالتسجيل، وأمر خاص بإيقاف التسجيل، وأمر خاص بالحفظ ونحوها. نلاحظ أن هذه الرموز لها أسماؤها الخاصة بها. فمثلا أمر التشغيل له الاسم MCI_PLAY والإيقاف له الإسم MCI_STOP فهذه الأوامر كلها عبارة عن ثوابت Constants. - رسائل نصية String Messages:
لكل أمر اسمه الخاص مثل play و stop و pause وما إلى غير ذلك. وهذه الطريقة هي الأشهر في لغات السكربت Scripting Languages مثل VBScript. ولهذا فإننا سوف نتعرض في هذا الدرس إلى الأوامر الرقمية.
والآن إلى الكود. سوف نتعرف على أهم الدوال وأهم العناصر Objects التي سنحتاجها.
أوامر MCI
بما أننا لدينا طريقتين لإرسال الأوامر، واحدة لإرسال الأوامر الرقمية Commands، والأخرى لإرسال أوامر نصية Messages، فإننا أيضا لدينا دالتين، واحدة لإرسال الأوامر الرقمية وهي mciSendCommand()، والأخرى لإرسال الرسائل النصية وهي mciSendMessage()، وفي درسنا هذا سنتعرض للأولى.
تقوم الدالة mciSendCommand() بإرسال أوامر إلى تقنية أو محرك MCI، وهذه الدالة لها التعريف التالي:
MCIERROR mciSendCommand( MCIDEVICEID IDDevice, UINT uMsg, DWORD fdwCommand, DWORD_PTR dwParam );
تأخذ هذه الدالة أربعة مدخلات:
- IDDevice:
كود كارت الصوت الذي تريد التعامل معه. بالطبع هناك بعض الأجهزة التي لديها أكثر من كارت صوت Sound Card، فيمكنك من خلال هذا المدخل تحديد كارت الصوت الذي تريد التعامل معه، أو تحدد 0 للتعامل مع الكارت الرئيسي الذي يستخدمه الويندوز. - uMsg:
كود أو رقم الأمر الذي تود تنفيذه. سوف تتعرض للأوامر تدريجيا أثناء قراءتك لهذا الدرس. - fdwCommand:
الخيارات Flags الخاصة بهذا الأمر. كل أمر له خياراته الخاصة به، ولكن جميع الأوامر تشترك في خيارات ثلاثة وهي الانتظار Wait، الإعلام Notify، والتجربة Test. سوف نتعرض لهذا الخيارات لاحقا. - dwParam:
المدخلات أو البيانات الخاصة بالأمر.
إذا فكل أمر له اسم، مدخلات أو بيانات، وله خيارات.
تقوم هذه الدالة بإرجاع 0 في حالة النجاح أو رقم الخطأ في حالة الفشل.
خيارات الانتظار، الإعلام، والتجربة
كل أمر له خياراته Flags الخاصة به، ولكن هناك 3 خيارات تنطبق على الجميع:
- الانتظار MCI_WAIT:
فعند إرسال الأمر باستخدام الدالة mciSendCommand() تقوم الدالة بالانتظار حتى يتم تنفيذ الأمر كاملا ثم الرجوع إلى المستخدم، وهذا ما نسميه عملية متزامنة Synchronous Operation. يفضل تنفيذ هذا الوضع أو الاختيار في حالة الأوامر مثل Stop و Pause حيث أنه لا يؤثر على الكود الخاص بك. - الإعلام MCI_NOTIFY:
فعند إرسال الأمر باستخدام الدالة mciSendCommand() تقوم الدالة بالرجوع فورا وينفذ الأمر في الخلفية وتقوم تقنية MCI بإرسال رسالة الويندوز MM_MCINOTIFY إلى البرنامج فور انتهائها وتحتوي هذه الرسالة على نتيجة الأمر هل نجح تنفيذه أم حدث خطأ، وهذا ما نسميه عملية غير متزامنه Asynchronous Operation. يفذل تنفيذ هذا الوع في حالة الأوامر مثل Record و Play حتى لا يقوم برنامج بالتوقف تماما عن العمل حتى انتهاء علمية التسجيل أو انتهاء الملف. - التجربة MCI_TEST:
فعند إرسال الأمر باستخدام الدالة mciSendCommand() تقوم الدالة بالتأكد هل يمكن تنفيذ الأمر أم لا ولا تقوم بتنفيذه. فمثلا عند محاولة التوقف Stop تقوم الدالة بالتأكد هل هناك عملية تسجيل أو تشغيل أو لا، فإذا كانت هناك عملية يمكن التوقف منها تقوم الدالة بإرجاع 0 وإلا فتقوم بإرجاع كود للخطأ. وفي كل الأحوال لا ينفذ الأمر.
معالجة الأخطاء
كل أمر يمكن أن ينفذ بطريقة صحيح ويمكن أن يحدث خطأ أثناء تنفيذه. فإذا نفذ بطريقة صحيحة ترجع الدالة mciSendCommand() قيمة 0، وإلا فترجع رقم الخطأ. فمثلا عند محاولتك تشغيل ملف غير موجود يحدث خطأ وترجع الدالة كود هذا الخطأ والممثل بالثابت MCIERR_FILE_NOT_FOUND.
ومن خلال رقم الخطأ هذا نستطيع معرفة الوصف Description للخطأ عن طريق الدالة mciGetErrorString() والتي لها التعريف التالي:
BOOL mciGetErrorString( DWORD fdwError, LPTSTR lpszErrorText, UINT cchErrorText );
تأخذ الدالة رقم الخطأ وترجع نص الخطأ من خلال المدخل الثاني.
كود الـ C التالي يوضح كيفية إرجاع الوصف لخطأ معين:
TCHAR szBuffer[256]; DWORD dwErr; dwErr = mciSendCommand(/* arguments */); mciGetErrorStringW(dwErr, szBuffer, 256); // cchErrorText = // sizeof(szBuffer) / sizeof(szBuffer[0]);
التسجيل Recording
كي تتم عملية التسجيل بالشكل الصحيح يجب عليك اتباع الخطوات التالية:
- تجهيز Open كارت الصوت للعمل (للإدخال Input.)
- بدأ عملية التسجيل Recording.
- إيقاف Stopping عملية التسجيل عند الانتهاء.
- حفظ Saving ما تم تسجيله في ملف صوتي.
- إنهاء Closing التعامل مع كارت الصوت ومع MCI.
التجهيز Opening
وهي عملية فتح أو تجهيز كارت الصوت للعمل مع برنامجك وتتم عن طريق الأمر MCI_OPEN. وكما تعلم لكل أمر مدخلات وخيارات خاصة به. ومدخلات هذا الأمر هو عنصر Structure من نوع MCI_OPEN_PARMS. وهذا العنصر له التعريف التالي:
typedef struct {
DWORD_PTR dwCallback;
MCIDEVICEID wDeviceID;
LPCSTR lpstrDeviceType;
LPCSTR lpstrElementName;
LPCSTR lpstrAlias;
} MCI_OPEN_PARMS;
ما يهمنا في هذا التركيب هو العنصر الثالث والرابع، lpstrDeviceType و lpstrElementName. فالعنصر lpstrDeviceType يحدد نوع البيانات التي نريد التعامل معها مثلا بيانات صوتية waveaudio أو فيديو.
أما العنصر lpstrElementName فيحوي نصا فارغا في حالة التجهيز لعملية التسجيل أو عنوان ملف في حالة عملية التشغيل Play.
يجب عند استخدام هذا الأمر تحديد الخيارين MCI_OPEN_ELEMENT و MCI_OPEN_TYPE ويفضل بالطبع تحديد وضع الانتظار MCI_WAIT.
عند تنفيذ هذا الأمر ورجوع الدالة mciSendCommand() يكون العنصر dwCallbck يحوي كود كارت الصوت الذي تم استخدامه.
كود الـ C التالي يوضح كيفية فتح كارت الصوت وتجهيزه لعملية التسجيل:
MCI_OPEN_PARMS parmOpen;
WORD wDeviceID;
parmOpen.dwCallback = 0;
parmOpen.wDeviceID = 0; // the default device
parmOpen.lpstrDeviceType = TEXT("waveaudio");
parmOpen.lpstrElementName = TEXT("");
parmOpen.lpstrAlias = 0;
mciSendCommand(0, // the default device
MCI_OPEN,
MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)&parmOpen);
// Keep the device ID for future calls
wDeviceID = parmOpen.wDeviceID;
بدأ التسجيل Recording
بعد فتح الجهاز وتجهيزه لعملية التسجيل يمكنك الآن بدأ التسجيل ويتم ذلك عن طريق الأمر MCI_RECORD والذي يأخذ مدخلات من نوع MCI_RECORD_PARMS والتي لها التعريف التالي:
typedef struct {
DWORD_PTR dwCallback;
DWORD dwFrom;
DWORD dwTo;
} MCI_RECORD_PARMS;
يحوي هذا التركيب Structure العناصر التالية:
- dwCallback:
مقبض النافذة Handle التي سوف ترسل إليها الرسالة MM_MCINOTIFY في حالة استخدام وضع الإعلام MCI_NOTIFY. - dwFrom:
يمكنك من التسجيل من ثانية معينة مثلا 0 وتعني الأولى. في حالة تغيير القيمة من 0 إلى قيمة أخرى استخدم الخيار MCI_FROM. - dwTo:
يمكنك من تحديد زمن انتهاء التسجيل، وهذا الزمن يكون مضروب عدد الثواني في 1000. فمثلا للتسجيل لمدة دقيقة نقوم بتحديد القيمة 0 للعنصر dwFrom والقيمة 60000 في العنصر dwTo. أما إذا أردتها مفتوحة فيمكنك تحديد القيمة 0 في العنصرين. في حالة تغيير القيمة من 0 إلى قيمة أخرى استخدم الخيار MCI_TO.
يفضل استخدام الوضع MCI_NOTIFY في هذا الأمر.
لاحظ كود C التالي والذي يقوم ببدأ عملية التسجيل لفترة مفتوحة:
MCI_RECORD_PARMS parmRec; parmRec.dwCallback = 0; parmRec.dwFrom = 0; parmRec.dwTo = 0; // We do not need a notification message // we will send a Stop command, when // we finish. mciSendCommand(wDeviceID, MCI_RECORD, 0, (DWORD)&parmRec);
وهذا الكود يقوم بالتسجيل لفترة معينة:
MCI_RECORD_PARMS parmRec; parmRec.dwCallback = hWnd; // window handle parmRec.dwFrom = 0; parmRec.dwTo = 30000; // 30 seconds // Notify me when you finish the 30 seconds mciSendCommand(wDeviceID, MCI_RECORD, MCI_NOTIFY | MCI_TO, (DWORD)&parmRec);
الإيقاف المؤقت Pausing
ويتم هذا عن طريق الأمر MCI_PAUSE والذي يأخذ مدخلات من نوع MCI_GENERIC_PARMS:
typedef struct {
DWORD_PTR dwCallback;
} MCI_GENERIC_PARMS;الكود التالي يوضح كيفية عمل إيقاف مؤقت Pause لعملية التسجيل:
MCI_GENERIC_PARMS parmGen; parmGen.dwCallback = 0; mciSendCommand(wDeviceID, MCI_PAUSE, MCI_WAIT, (DWORD)&parmGen);
الاستمرار Resuming
الاستمرار Resuming من عملية التسجيل ويتم عن طريق الأمر MCI_RESUME والذي يأخذ أيضا العنصر MCI_GENERIC_PARMS كمدخلات مثل نظيره MCI_PAUSE.
الإيقاف Stopping
أي أيقاف Stop عملية التسجيل تمام. ويتم ذلك عن طريق الأمر MCI_STOP والذي يأخذ أيضا العنصر MCI_GENERIC_PARMS كمدخلات.
الحالة Status
كيف يمكننا معرفة الزمن الذي تم تسجيله؟ كيف يمكننا معرفة العملية التي يقوم MCI بتنفيذها الآن؟ يتم ذلك عن طريق الأمر MCI_STATUS وهو المسؤول عن الاستعلامات ويأخذ مدخلات من نوع MCI_STATUS_PARMS وهي معرفة كالتالي:
typedef struct {
DWORD_PTR dwCallback;
DWORD dwReturn;
DWORD dwItem;
DWORD dwTrack;
} MCI_STATUS_PARMS;بالطبع العنصر الأول dwCallback معروف وقد تم ذكره. أما العناصر الأخرى فيهمنا منها التالي:
- dwReturn:
القيمة المرتجعة الناتجة من الاستعلام. - dwItem:
عبارة عن البيانات التي نريد الاستعلام عنها.
ويأخذ هذا الأمر خيارات من نوع MCI_WAIT و MCI_STATUS_ITEM.
والبيانات التي يمكن الاستعلام عنها والتي تحدد في dwItem هي:
- MCI_STATUS_LENGTH:
طول الملف الذي تم تسجيله أو الذي تم تشغيله. ترجع هذه القيمة في العنصر dwReturn وهي عبارة عن مضروب عدد الثواني في 1000. - MCI_STATUS_POSITION:
المكان الحالي مثلا المكان الذي وصلنا إليه في عملية التشغيل أو التسجيل. ويكون أيضا مضروب عدد الثواني في 1000 ويرجع في العنصر dwReturn. - MCI_STATUS_MODE:
الوضع الحالي. وترجع إحدى القيم التالية في العنصر dwReturn:- MCI_MODE_NOT_READY
- MCI_MODE_PAUSE
- MCI_MODE_PLAY
- MCI_MODE_STOP
- MCI_MODE_RECORD
- MCI_MODE_SEEK
- MCI_STATUS_READY:
ترجع قيمة TRUE إذا كانت MCI جاهزة للعمل أو لا.
المثال التالي يوضح كيفية معرفة الزمن الذي تم تسجيله:
MCI_STATUS_PARMS parmStatus; parmStatus.dwCallback = 0; parmStatus.dwReturn = 0; parmStatus.dwItem = MCI_STATUS_LENGTH; parmStatus.dwTrack = 0; mciSendCommand(0, MCI_STATUS, MCI_WAIT | MCI_STATUS_ITEM, (DWORD)&parmStatus); // Display the number of seconds // parmStatus.dwReturn / 1000
الحفظ Saving
يتم حفظ ما تم تسجيله عن طريق الأمر MCI_SAVE والذي يأخذ مدخلات من نوع MCI_SAVE_PARMS والتي لها التعريف التالي:
typedef struct {
DWORD_PTR dwCallback;
LPCTSTR lpfilename;
} MCI_SAVE_PARMS;dwCallback تم ذكره. lpFilename هو اسم (عنوان) الملف.
ويجب تحديد الخيار MCI_SAVE_FILE ويفضل استخدام MCI_WAIT أيضا.
الكود التالي يقوم بحفظ ما تم تسجيله:
MCI_SAVE_PARMS parmSave;
parmSave.dwCallback = 0;
parmSave.lpfilename = TEXT("recording.wav");
// save to the current directory
mciSendCommand(wDeviceID, MCI_SAVE,
MCI_WAIT | MCI_SAVE_FILE, (DWORD)&parmSave);الإنهاء Closing
بعد أن انتهى عملك مع MCI ومع كارت الصوت يجب أن تقوم بإنهاء ما بدأته وتفريغ ما تم استغلاله من الذاكرة ويتم ذلك عن طريق الأمر MCI_CLOSE والذي يأخذ مدخلات من النوع MCI_GENERIC_PARMS والذي تم ذكره.
الكود التالي يوضح كيفية عملية الإنهاء:
MCI_GENERIC_PARMS parmsGen; parmsGen.dwCallback = 0; mciSendCommand(wDeviceID, MCI_CLOSE, MCI_WAIT, (DWORD)&parmsGen);
التشغيل Playing
في القسم السابق تعلمنا كيفية تسجيل ملفات صوت وحفظها. والآن نتعلم كيفية تشغيل الملفات الصوتية أيضا باستخدام MCI. وكما كان منهجنا في القسم السابق سيكون منهجنا في هذا القسم مع الفرق أنه تم ذكر الكثير من العناصر والدوال ولهذا لن نذكر شرحها مرة أخرى.
يجب عليك اتباع هذه الخطوات كي تستطيع تشغيل ملف صوتي والتحكم فيه في برنامجك:
- تحميل Loading الملف وتجهيز Opening كارت الصوت للعمل مع برنامجك (للإخراج Output.)
- بدأ التشغيل Playing.
- إنهاء Closing التعامل مع كارت الصوت.
التحميل Loading
بالطبع كي يمكنك تشغيل ملف صوتي يجب عليك تحديد الملف المراد تشغيله وأيضا تجهيز كارت الصوت لتشغيل هذا الملف وهذا يتم عن طريق الأمر MCI_LOAD والذي يأخذ مدخلات من النوع MCI_OPEN_PARMS والتي تم ذكرها.
يتشابه هذا الأمر كثيرا مع أمر MCI_OPEN السابق في عملية التسجيل من حيث المدخلات والخيارات، ولكن الفرق الوحيد أو الإضافة الوحيدة أنك في الأمر MCI_OPEN تقوم بإعطاء نص فارغ للعنصر lpstrElementName، أما في هذا الأمر فإنك تقوم بإعطاء عنوان الملف المراد تشغيله لهذا العنصر.
الكود التالي كود C يوضح كيفية تشغيل ملف:
MCI_OPEN_PARMS parmOpen;
WORD wDeviceID;
parmOpen.dwCallback = 0;
parmOpen.wDeviceID = 0; // the default device
parmOpen.lpstrDeviceType = TEXT("waveaudio");
parmOpen.lpstrElementName = TEXT("recording.wav");
parmOpen.lpstrAlias = 0;
mciSendCommand(0, // the default device
MCI_OPEN,
MCI_WAIT | MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)&parmOpen);
// Keep the device ID for future calls
wDeviceID = parmOpen.wDeviceID;
بدأ التشغيل Playing
بعد تحميل الملف في الذاكرة وتحديده في MCI، تصبح MCI جاهزة للعمل ويمكنك الآن البدأ في تشغيل الملف. ويتم ذلك عن طريق الأمر MCI_PLAY والذي يأخذ عنصر من نوع MCI_PLAY_PARMS وهي مثل العنصر MCI_RECORD_PARMS تماما الخاص بالأمر MCI_RECORD.
وبالطبع يفضل استخدام الخيار MCI_NOTIFY مع هذا الأمر.
الكود التالي يوضح كيفية تشغيل ملف من أوله إلا آخره أو إلى إنهائه يدويا:
MCI_PLAY_PARMS parmPlay; // Play the file // from the start to the end parmRec.dwCallback = 0; parmRec.dwFrom = 0; parmRec.dwTo = 0; // notify me when you finish mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)&parmRec);
أما هذا الكود فيوضح كيفية تشغيل أول ثلاثة دقائق فقط من الملف:
MCI_PLAY_PARMS parmPlay; // play only 3 minutes parmRec.dwCallback = 0; parmRec.dwFrom = 0; // 3 * 60 * 1000 parmRec.dwTo = 180000; // notify me when you finish mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY | MCI_TO, (DWORD)&parmRec);
الإيقاف المؤقت Pausing
وهذا أيضا مثل عملية التسجيل يتم عن طريق الأمر MCI_PAUSE والمدخلات MCI_GENERIC_PARMS.
الاستمرار Resuming
وهذا أيضا مثل سابقه مثل عملية التسجيل يتم عن طريق الأمر MCI_RESUME والمدخلات MCI_GENERIC_PARMS.
معرفة المكان الحالي
ما تعلمناه في القسم السابق من كيفية الاستعلام عن الوضع الذي عليه MCI والطول Length والمكان الحاليCurrent Position للعملية نستطيع تطبيقه على عملية التشغيل Play. ويتم ذلك كما نعرف عن طريق الأمر MCI_STATUS والذي يأخذ المدخلات MCI_STATUS_PARMS.
الكود التالي يوضح كيفية معرفة المكان الحالي Current Position لعملية التشغيل:
MCI_STATUS_PARMS parmStatus; parmStatus.dwCallback = 0; parmStatus.dwReturn = 0; parmStatus.dwItem = MCI_STATUS_POSITION; parmStatus.dwTrack = 0; mciSendCommand(wDeviceID, MCI_STATUS, MCI_WAIT | MCI_STATUS_ITEM, (DWORD)&parmStatus); // Display the current position // parmStatus.dwReturn / 1000
الانتقال إلى مكان معين Seeking
عملية الانتقال إلى مكان معين أو Seeking وتتم عن طريق الأمر MCI_SEEK والذي يأخذ مدخلات من النوع MCI_STATUS_PARMS والتي لها التعريف التالي:
typedef struct {
DWORD_PTR dwCallback;
DWORD dwTo;
} MCI_SEEK_PARMS;
يحوي العنصر dwTo قيمة هي عبارة عن المكان المراد الانتقال إليه مضروب عدد الثواني في 1000.
ويأخذ هذا الأمر الخيار MCI_WAIT والخيار MCI_TO مع أحد الخيارين التاليين:
- MCI_SEEK_TO_END:
تكون القيمة الموجودة في dwTo محسوبة تبعا لنهاية الملف. فمثلا إن كانت القيمة هي دقيقتين أي 120000 فعند تنفيذ هذا الأمر باستخدام هذا الخيار ينتقل المؤشر إلى دقيقتين قبل نهاية الملف، فمثلا إن كان طول الملف 10 دقائق ينتقل المؤشر إلى الدقيقة الثامنة. - MCI_SEEK_TO_START:
تكون القيمة الموجودة في dwTo محسوبة تبعا لبداية الملف. فمثلا إذا كان طول الملف 10 دقائق فننتقل إلى الدقيقة الثانية إذا استخدمنا القيمة 120000.
الكود التالي يوضح كيفية الانتقال إلى بداية الملف:
MCI_SEEK_PARMS parmSeek; parmStatus.dwCallback = 0; parmStatus.dwTo = 0; mciSendCommand(wDeviceID, MCI_SEEK, MCI_WAIT | MCI_SEEK_TO_START, (DWORD)&parmSeek);
وهذا يوضح كيفية الانتقال إلى الدقيقة الثالثة:
MCI_SEEK_PARMS parmSeek; parmStatus.dwCallback = 0; parmStatus.dwTo = 180000; mciSendCommand(wDeviceID, MCI_SEEK, MCI_WAIT | MCI_TO, (DWORD)&parmSeek);
الإنهاء Closing
كما عرفت، يجب عليك دوما إنهاء تشغيل الـ MCI بعد الانتهاء من عملك. ويتم ذلك كما ذكرنا باستخدام الأمر MCI_CLOSE.
MCI والدوت نت
ما ذكرناه سابقا كان عن MCI ولغة C. فكما تعرف لا يمكنك التعامل مع دوال الويندوز Windows API مباشرة من خلال بيئة الدوت نت. ولكن يمكنك ذلك عن طريق تعريف الدوال والعناصر التي تريد استخدامها بتعريف خاص ببيئة الدوت نت يسمى Platform Invokation أو PInvoke.
الكود التالي هو عبارة عن تعاريف جميع ما ذكرناه من دوال وعناصر وخيارات وثوابت وغيرها، تعريفا خاصا بالدوت نت أو بالأصح بالـ C#:
internal static class SafeNativeMethods
{
// Constants
public const string WaveAudio = "waveaudio";
public const uint MM_MCINOTIFY = 0x3B9;
public const uint MCI_NOTIFY_SUCCESSFUL = 0x0001;
public const uint MCI_NOTIFY_SUPERSEDED = 0x0002;
public const uint MCI_NOTIFY_ABORTED = 0x0004;
public const uint MCI_NOTIFY_FAILURE = 0x0008;
public const uint MCI_OPEN = 0x0803;
public const uint MCI_CLOSE = 0x0804;
public const uint MCI_PLAY = 0x0806;
public const uint MCI_SEEK = 0x0807;
public const uint MCI_STOP = 0x0808;
public const uint MCI_PAUSE = 0x0809;
public const uint MCI_RECORD = 0x080F;
public const uint MCI_RESUME = 0x0855;
public const uint MCI_SAVE = 0x0813;
public const uint MCI_LOAD = 0x0850;
public const uint MCI_STATUS = 0x0814;
public const uint MCI_SAVE_FILE = 0x00000100;
public const uint MCI_OPEN_ELEMENT = 0x00000200;
public const uint MCI_OPEN_TYPE = 0x00002000;
public const uint MCI_LOAD_FILE = 0x00000100;
public const uint MCI_STATUS_POSITION = 0x00000002;
public const uint MCI_STATUS_LENGTH = 0x00000001;
public const uint MCI_STATUS_ITEM = 0x00000100;
public const uint MCI_NOTIFY = 0x00000001;
public const uint MCI_WAIT = 0x00000002;
public const uint MCI_FROM = 0x00000004;
public const uint MCI_TO = 0x00000008;
// Structures
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_OPEN_PARMS
{
public IntPtr dwCallback;
public uint wDeviceID;
public IntPtr lpstrDeviceType;
public IntPtr lpstrElementName;
public IntPtr lpstrAlias;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_RECORD_PARMS
{
public IntPtr dwCallback;
public uint dwFrom;
public uint dwTo;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_PLAY_PARMS
{
public IntPtr dwCallback;
public uint dwFrom;
public uint dwTo;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_GENERIC_PARMS
{
public IntPtr dwCallback;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_SEEK_PARMS
{
public IntPtr dwCallback;
public uint dwTo;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_SAVE_PARMS
{
public IntPtr dwCallback;
public IntPtr lpfilename;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct MCI_STATUS_PARMS
{
public IntPtr dwCallback;
public uint dwReturn;
public uint dwItem;
public uint dwTrack;
} ;
// Functions
[DllImport("winmm.dll", CharSet = CharSet.Ansi,
BestFitMapping = true, ThrowOnUnmappableChar = true)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern uint mciSendCommand(
uint mciId,
uint uMsg,
uint dwParam1,
IntPtr dwParam2);
[DllImport("winmm.dll", CharSet = CharSet.Ansi,
BestFitMapping = true, ThrowOnUnmappableChar = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool mciGetErrorString(
uint mcierr,
[MarshalAs(UnmanagedType.LPStr)]
System.Text.StringBuilder pszText,
uint cchText);
}ماذا عن استلام رسالة الويندوز MM_MCINOTIFY عند استخدام الوضع MCI_NOTIFY؟ الكود التالي يوضح هذه الفكرة:
public partial class MainForm : Form
{
. . .
protected override void WndProc(ref Message m)
{
if (m.Msg == SafeNativeMethods.MM_MCINOTIFY)
{
// Handle the message
}
// DO NOT REMOVE the following line
base.WndProc(ref m);
}
}الأمثلة
مع الدرس مثالين، الأول SMPREC مسجل صوتي بالـ C، والآخر SampleRec مسجل صوتي بالـ C#.
