الفريق العربي للبرمجةأرشيف المنتديات · 2000 – 2023
نسخة أرشيفية للقراءة فقط — التسجيل والمشاركة مغلقان، والمحتوى محفوظ كما كان.

درس: إنشاء مسجل صوتي في C و C#

استطلاع
بدأه Mohammad Elsheimy في 28 مايو 2010 · 6 رد · 1,899 مشاهدة · في المواضيع والدروس
مشاركة: واتساب X فيسبوك تيليجرام

استطلاع

8 مشارك في التصويت

?? ?? ???? ?? ????? ??????

???0 صوت · 0%
???4 صوت · 50%
?????4 صوت · 50%
#1 صاحب الموضوع

المصدر- مع الدوت نت

المحتويات

محتويات هذا الدرس:

  • المحتويات
  • نظرة خاطفة
  • مقدمة
  • أوامر 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#.

تم تعديل هذه المشاركة بواسطة Mohammad Elsheimy في 28 مايو 2010 في 19:27

6
#2

جميل جدا أخي محمد وشرح ولا أجمل.

جعلت الملفات الخاصة بالمشروع كمرفقات أفضل من الروابط الخارجية.

SMPREC - C.zip

SimpleRec - C#.zip

1
tvquran_6.gif

#3

درس جميل و شرح اجمل بارك الله فيك و زادك علما.

لدى بعض التعليقات

  • تعريفك لـ MCI بأنها الأسهل والأشهر والأقوى ليس دقيق فهى سهلة و مشهوره و لكنها ليست الأقوى على الإطلاق و ذلك لحدود امكانياتها مقارنة بتقنيات اخرى.
  • توجد نسخة من الـ Managed DirectX تتيح لك الوصول لـ DirectSound و DirectMusic.
  • توجد تقنيات اخرى تتيح لك معالجة الأصوات منها XACT و DirectShow و لكنها متاحه فقط للـ Unmanaged Code.
  • طالما أن الـ struct لا يحتوى على انواع نصيه لا داعى من استخدام CharSet = CharSet.Ansi حيث أنها الإفتراضيه.
  • بدلا من اعادة تعريف الـ Window Procedure للـ Form يمكنك عمل فئه خاصه بك و من ثم تقوم بعمل تضمين للـ IMessageFilter و التى تتيح لك الداله PreFilterMessage و من داخلها تستطيع كتابة الكود الذى تريد و لتجعل فئتك فعاله قم بإنشاء نسخه منها و من ثم قم بإستدعاء الداله AddMessageFilter الموجوده بـ Application و قم بتمرير الكائن الذى تم انشائه من فئتك و عندما تنتهى من عملك قم بإستخدام الداله RemoveMessageFilter.
  • كنت قد كتبت فئه من قبل لمعالجة النصوص الـ Native من داخل الدوت نت موجوده هنــا.

و الله ولى التوفيق

2 −1

مدونتي: C++ Tips and Tricks

#4

السلام عليكم ورحمة الله وبركاته

أولا أشكركم كثيرا على مروركم وأشكر الأخ فهد على مساعدته لينا.

وشكرا كتير برضه للأخ محمد علاء، لكن انت قلت تعليقات وأنا عندي ردود لكل تعليق:

  • تقنية MCI تعتمد داخليا على Waveform وأنا لا أنكر أبدا أبدا إن الأقوى في هذا المجال هو Waveform ولكن بالنسبة لحدود الموضوع اللي انا بتكلم فيه من تسجيل ملفات صوتية وتشغيلها بطريقة بسيطة ولكن متطورة جدا تبقى هي الأقوى والأقوى من Waveform. فكلمة "الأقوى" كانت مقصود بها الأقوى بالنسبة لسياق الموضوع.
  • أنا قلت إن DirectX تضيفها مباشرة على برنامجك وما حددتش أي برنامج الـ C واللا الـ C#! بالنسبة للـ C أو الـ C# انت هتضيفها مباشرة (طبعا هتستخدم الـ Managed SDK بالنسبة للـ C#) والكلام ما اداش أي انطباع غير كده!
  • أنا قلت بالنسبة للتقينات قلت "الأشهر" فده مش حصر ولكن انتقاء للأشهر وبالفعل دول أكثر التقنيات اللي بتلاقيلها دعم والـ documentation والمراجع references بتاعتهم كتير. وأيضا بالنسبة للدوت نت فيه 3rd Party Components كتير أشهرها وأقواها NAudio.
  • النصوص Ansi هي بالنسبة للـ C# هي الأساسية بالنسبة للـ C# ولكن الـ CLR اختار Unicode كترميز أساسي للـ Windows NT. فممكن لغة زي VB.NET (بصراحة أنا مش متابع فيها) يكون الـ Unicode هو الأساسي أو حتى أي لغة تانية تكون CLI مثلا C++ او أي لغة تانية (مستقبلا ممكن كمان.) ولا يخفى إن القارئ ربما يفكر في كتابة الكود بلغة تانية، ودي مجرد notation أو ملاحظة بسيطة علشان ياخد باله.
  • دائما أنا أفضل اختيار الطرق الأبسط واللي ليها نفس الكفاءة. فبدل ما أدخل القارئ في متاهات Custom Message Processing أظن إن الكود بتاعي أبسط كتير من الكود اللي حضرتك ذكرته، وفي كل الأحوال شكرا لك.
  • أنا كتبت كتاب صغير في Marshaling هو أفضل ما كتب على الإطلاق في هذا الموضوع (وده بشهادة كتاب كبار،) ضم كل ما تفكر فيه من Primitive/Simple data types، و Strings و Booleans و Handles إلى Compound Data Types و Structures/Classes و Unions إلى Callbacks and Delegates إلى Arrays إلى إلى إلى.....
    ممكن تقرا الكتاب ده وتحمله من هنا:
    http://justlikeamagic.wordpress.com/2010/03/09/marshaling
    وأنا منتظر رأيك فيه

تحياتي

وشكرا للجميع، شكرا على دعمكم

smile.gif

2
#5
اقتباس
درس جميل و شرح اجمل بارك الله فيك و زادك علما.
+1
اقتباس
أنا كتبت كتاب صغير في Marshaling هو أفضل ما كتب على الإطلاق في هذا الموضوع
-1
1 −1
#6
Mohammad Elsheimy كتب:

السلام عليكم ورحمة الله وبركاته

أولا أشكركم كثيرا على مروركم وأشكر الأخ فهد على مساعدته لينا.

وشكرا كتير برضه للأخ محمد علاء، لكن انت قلت تعليقات وأنا عندي ردود لكل تعليق:

  • تقنية MCI تعتمد داخليا على Waveform وأنا لا أنكر أبدا أبدا إن الأقوى في هذا المجال هو Waveform ولكن بالنسبة لحدود الموضوع اللي انا بتكلم فيه من تسجيل ملفات صوتية وتشغيلها بطريقة بسيطة ولكن متطورة جدا تبقى هي الأقوى والأقوى من Waveform. فكلمة "الأقوى" كانت مقصود بها الأقوى بالنسبة لسياق الموضوع.
  • أنا قلت إن DirectX تضيفها مباشرة على برنامجك وما حددتش أي برنامج الـ C واللا الـ C#! بالنسبة للـ C أو الـ C# انت هتضيفها مباشرة (طبعا هتستخدم الـ Managed SDK بالنسبة للـ C#) والكلام ما اداش أي انطباع غير كده!
  • أنا قلت بالنسبة للتقينات قلت "الأشهر" فده مش حصر ولكن انتقاء للأشهر وبالفعل دول أكثر التقنيات اللي بتلاقيلها دعم والـ documentation والمراجع references بتاعتهم كتير. وأيضا بالنسبة للدوت نت فيه 3rd Party Components كتير أشهرها وأقواها NAudio.
  • النصوص Ansi هي بالنسبة للـ C# هي الأساسية بالنسبة للـ C# ولكن الـ CLR اختار Unicode كترميز أساسي للـ Windows NT. فممكن لغة زي VB.NET (بصراحة أنا مش متابع فيها) يكون الـ Unicode هو الأساسي أو حتى أي لغة تانية تكون CLI مثلا C++ او أي لغة تانية (مستقبلا ممكن كمان.) ولا يخفى إن القارئ ربما يفكر في كتابة الكود بلغة تانية، ودي مجرد notation أو ملاحظة بسيطة علشان ياخد باله.
  • دائما أنا أفضل اختيار الطرق الأبسط واللي ليها نفس الكفاءة. فبدل ما أدخل القارئ في متاهات Custom Message Processing أظن إن الكود بتاعي أبسط كتير من الكود اللي حضرتك ذكرته، وفي كل الأحوال شكرا لك.
  • أنا كتبت كتاب صغير في Marshaling هو أفضل ما كتب على الإطلاق في هذا الموضوع (وده بشهادة كتاب كبار،) ضم كل ما تفكر فيه من Primitive/Simple data types، و Strings و Booleans و Handles إلى Compound Data Types و Structures/Classes و Unions إلى Callbacks and Delegates إلى Arrays إلى إلى إلى.....
    ممكن تقرا الكتاب ده وتحمله من هنا:
    http://justlikeamagic.wordpress.com/2010/03/09/marshaling
    وأنا منتظر رأيك فيه

تحياتي

وشكرا للجميع، شكرا على دعمكم

smile.gif

عملك رائع جداً الله يعطيك الف عافيه

1
#7

شكرا جزيلا smile.gif

مواضيع مشابهة