ترجمة آلية تحت المراجعة
مقدار
المصدر الأصليقائمة تنفيذ التمرين
تُحفظ علامات هذه القائمة على هذا الجهاز والمتصفح فقط. أنشئ حسابًا مفعّلًا لحفظ إنجاز التمرين في مسارك.
مقدار

مشكلة بحاجة إلى حل
ملفات WAV هو تنسيق ملف شائع لتمثيل الصوت. تقوم ملفات WAV بتخزين الصوت كسلسلة من "العينات": أرقام تمثل قيمة بعض الإشارات الصوتية في نقطة زمنية معينة. تبدأ ملفات WAV بـ "رأس" بحجم 44 بايت يحتوي على معلومات حول الملف نفسه، بما في ذلك حجم الملف وعدد العينات في الثانية وحجم كل عينة. بعد الرأس، يحتوي ملف WAV على سلسلة من العينات، كل منها عبارة عن عدد صحيح مكون من 2 بايت (16 بت) يمثل الإشارة الصوتية في نقطة زمنية معينة.
إن تغيير حجم كل قيمة نموذجية بواسطة عامل معين له تأثير على تغيير مستوى الصوت. على سبيل المثال، سيكون لضرب كل قيمة عينة في 2.0 تأثير مضاعفة مستوى الصوت الأصلي. وفي الوقت نفسه، فإن ضرب كل عينة في 0.5 سيكون له تأثير خفض الحجم إلى النصف.
في ملف يسمى volume.c في مجلد يسمى volume، اكتب برنامجًا لتعديل مستوى صوت الملف الصوتي.
تجريبي
رمز التوزيع
بالنسبة لهذه المشكلة، ستقوم بتوسيع وظيفة الشيفرة المقدمة لك من قبل موظفي CS50.
قم بتنزيل كود التوزيع
قم بتسجيل الدخول cs50.dev، انقر على نافذتك الطرفية، وقم بالتنفيذ cd بمفرده. يجب أن تجد أن مطالبة النافذة الطرفية الخاصة بك تشبه ما يلي:
$
التنفيذ التالي
wget https://cdn.cs50.net/2026/x/psets/4/volume.zip
من أجل تنزيل ملف ZIP يسمى volume.zip في مساحة الشيفرة الخاصة بك.
ثم نفذ
unzip volume.zip
لإنشاء مجلد يسمى volume. لم تعد بحاجة إلى ملف ZIP، حتى تتمكن من التنفيذ
rm volume.zip
ثم قم بالرد بـ "y" متبوعًا بـ Enter عند المطالبة لإزالة ملف ZIP الذي قمت بتنزيله.
الآن اكتب
cd volume
متبوعًا بـ Enter للانتقال إلى (i.e.، open) هذا الدليل. يجب أن تشبه مطالبتك الآن ما يلي.
volume/ $
إذا كان كل شيء ناجحًا، فيجب عليك التنفيذ
ls
وشاهد الملف المسمى volume.c. تنفيذ code volume.c الملف الذي ستكتب فيه الشيفرة الخاص بك لمجموعة المشكلات هذه. إذا لم يكن الأمر كذلك، قم بإعادة تتبع خطواتك ومعرفة ما إذا كان بإمكانك تحديد الخطأ الذي ارتكبته!
تفاصيل التنفيذ
أكمل تنفيذ volume.c، بحيث يغير مستوى صوت الملف الصوتي بعامل معين.
- يجب أن يقبل البرنامج ثلاث وسائط لسطر الأوامر. الأول هو
input، والذي يمثل اسم الملف الصوتي الأصلي. والثاني هوoutput، والذي يمثل اسم الملف الصوتي الجديد الذي يجب إنشاؤه. والثالث هوfactor، وهو المقدار الذي يجب أن يتم به قياس حجم الملف الصوتي الأصلي.- على سبيل المثال، إذا
factorهو2.0، فيجب على برنامجك مضاعفة حجم الملف الصوتيinputواحفظ الملف الصوتي الذي تم إنشاؤه حديثًا فيهoutput.
- على سبيل المثال، إذا
- يجب أن يقوم برنامجك أولاً بقراءة الرأس من ملف الإدخال وكتابة الرأس في ملف الإخراج.
- يجب أن يقرأ برنامجك بعد ذلك بقية البيانات من ملف WAV، عينة واحدة بحجم 16 بت (2 بايت) في كل مرة. يجب أن يقوم برنامجك بضرب كل عينة في
factorواكتب النموذج الجديد إلى ملف الإخراج.- قد تفترض أن ملف WAV سيستخدم قيم موقعة 16 بت كعينات. من الناحية العملية، يمكن أن تحتوي ملفات WAV على أعداد مختلفة من البتات لكل عينة، ولكننا سنفترض وجود عينات 16 بت لهذه المشكلة.
- برنامجك إذا كان يستخدم
malloc، يجب ألا يتسرب أي ذاكرة.
تلميحات
فهم الشيفرة الموجود volume.c
لاحظ ذلك أولاً
تم إعداد volume.c بالفعل لاستقبال ثلاث وسيطات لسطر الأوامر، input, outputو factor.
-
mainيأخذ كلاً منint,argc، ومجموعة منchar *س (سلاسل!)،argv. - إذا
argc، عدد الوسائط في سطر الأوامر بما في ذلك البرنامج نفسه، لا يساوي 4، سيقوم البرنامج بطباعة الاستخدام الصحيح له والخروج برمز الحالة 1.
int main(int argc, char *argv[])
{
// Check command-line arguments
if (argc != 4)
{
printf("Usage: ./volume input.wav output.wav factor\n");
return 1;
}
// ...
}
التالي، volume.c يستخدم fopen لفتح الملفين المقدمين كوسيطات سطر الأوامر.
- من أفضل الممارسات التحقق مما إذا كانت نتيجة الاتصال
fopenهوNULL. إذا كان الأمر كذلك، فهذا يعني أنه لم يتم العثور على الملف أو أنه لا يمكن فتحه.
// Open files and determine scaling factor
FILE *input = fopen(argv[1], "r");
if (input == NULL)
{
printf("Could not open file.\n");
return 1;
}
FILE *output = fopen(argv[2], "w");
if (output == NULL)
{
printf("Could not open file.\n");
return 1;
}
لاحقًا، يتم إغلاق هذه الملفات fclose. كلما اتصلت fopen، يجب عليك الاتصال لاحقًا fclose!
// Close files
fclose(input);
fclose(output);
قبل إغلاق الملفات، لاحظ أن لدينا بعض المهام.
// TODO: Copy header from input file to output file
// TODO: Read samples from input file and write updated data to output file
من المحتمل أنك ستحتاج إلى معرفة العامل الذي يتم من خلاله قياس مستوى الصوت، ولهذا السبب volume.c يقوم بالفعل بتحويل وسيطة سطر الأوامر الثالثة إلى ملف float لك!
float factor = atof(argv[3]);
انسخ رأس WAV من ملف الإدخال إلى ملف الإخراج
أول مهمة يجب عليك القيام بها هي نسخ رأس ملف WAV من input واكتبها إليه output. أولاً، ستحتاج إلى التعرف على بعض أنواع البيانات الخاصة.
حتى الآن، رأينا عددًا من الأنواع المختلفة في لغة C، بما في ذلك int, bool, char, double, floatو long. ومع ذلك، داخل ملف رأس يسمى stdint.h هي تصريحات عدد من أخرى الأنواع التي تسمح لنا بتحديد الحجم (بالبتات) والإشارة (الموقعة أو غير الموقعة) لعدد صحيح بدقة شديدة. هناك نوعان على وجه الخصوص سيكونان مفيدين لنا عند العمل مع ملفات WAV:
-
uint8_tهو نوع يخزن 8 بت (وبالتالي8!) عدد صحيح غير موقع (i.e.، ليس سالبًا) (وبالتاليuint!). يمكننا التعامل مع كل بايت من رأس ملف WAV على أنه ملف قيمةuint8_t. -
int16_tهو نوع يخزن عددًا صحيحًا بعلامة 16 بت (i.e.، موجب أو سالب). يمكننا التعامل مع كل عينة من الصوت في ملف WAV كملف قيمةint16_t.
من المحتمل أنك ستحتاج إلى إنشاء مصفوفة من البايتات لتخزين البيانات من رأس ملف WAV الذي ستقرأه من ملف الإدخال. باستخدام uint8_t اكتب لتمثيل بايت، يمكنك إنشاء مجموعة من n بايت لرأسك مع بناء الجملة مثل
uint8_t header[n];
استبدال n مع عدد البايتات. يمكنك بعد ذلك استخدام header كوسيطة ل fread أو fwrite للقراءة أو الكتابة من الرأس.
تذكر أن رأس ملف WAV يبلغ طوله دائمًا 44 بايت بالضبط. لاحظ ذلك volume.c يعرّف بالفعل متغيرًا اتصلت به HEADER_SIZE، يساوي عدد البايتات في الرأس.
ما يلي هو تلميح كبير جدًا، ولكن إليك كيفية إنجاز هذه المهام!
// Copy header from input file to output file
uint8_t header[HEADER_SIZE];
fread(header, HEADER_SIZE, 1, input);
fwrite(header, HEADER_SIZE, 1, output);
كتابة البيانات المحدثة لملف الإخراج
مهمتك التالية هي قراءة العينات منها input، وقم بتحديث تلك العينات، ثم قم بكتابة العينات المحدثة إليها output. عند قراءة الملفات، من الشائع إنشاء "مخزن مؤقت" لتخزين البيانات مؤقتًا. هناك، يمكنك تعديل البيانات، وبمجرد أن تصبح جاهزة، يمكنك كتابة بيانات المخزن المؤقت في ملف جديد.
تذكر أنه يمكننا استخدام
اكتب int16_t لتمثيل عينة من ملف WAV. لتخزين عينة صوتية، يمكنك إنشاء متغير مخزن مؤقت بصيغة مثل:
// Create a buffer for a single sample
int16_t buffer;
مع وجود مخزن مؤقت للعينات، يمكنك الآن قراءة البيانات فيه، عينة واحدة في كل مرة. حاول استخدام fread لهذه المهمة! يمكنك استخدام &bufferعنوان buffer، كوسيطة لـ fread أو fwrite للقراءة أو الكتابة من المخزن المؤقت. (تذكر أن
يتم استخدام عامل التشغيل & للحصول على عنوان المتغير.)
// Create a buffer for a single sample
int16_t buffer;
// Read single sample into buffer
fread(&buffer, sizeof(int16_t), 1, input);
الآن، لزيادة (أو تقليل) حجم العينة، ما عليك سوى ضربها بعامل ما.
// Create a buffer for a single sample
int16_t buffer;
// Read single sample into buffer
fread(&buffer, sizeof(int16_t), 1, input);
// Update volume of sample
buffer *= factor;
وأخيرًا، يمكنك كتابة تلك العينة المحدثة إلى output:
// Create a buffer for a single sample
int16_t buffer;
// Read single sample from input into buffer
fread(&buffer, sizeof(int16_t), 1, input);
// Update volume of sample
buffer *= factor;
// Write updated sample to new file
fwrite(&buffer, sizeof(int16_t), 1, output);
هناك مشكلة واحدة فقط: ستحتاج إلى ذلك تابع قراءة عينة في المخزن المؤقت الخاص بك، وتحديث حجمها، وكتابة العينة المحدثة إلى ملف الإخراج بينما لا تزال هناك عينات متبقية للقراءة.
- ولحسن الحظ، وفقًا لوثائقه،
سيُرجع
freadعدد عناصر البيانات التي تمت قراءتها بنجاح. قد تجد هذا مفيدًا للتحقق منه عند وصولك إلى نهاية الملف! - ضع في اعتبارك أنه لا يوجد سبب يمنعك من الاتصال
freadداخل أwhileحلقة مشروطة. يمكنك، على سبيل المثال، إجراء مكالمة إلىfreadمثل ما يلي:while (fread(...)) { }
هذا هو التلميح تمامًا، ولكن راجع ما يلي للحصول على طريقة فعالة لحل هذه المشكلة:
// Create a buffer for a single sample
int16_t buffer;
// Read single sample from input into buffer while there are samples left to read
while (fread(&buffer, sizeof(int16_t), 1, input) != 0)
{
// Update volume of sample
buffer *= factor;
// Write updated sample to new file
fwrite(&buffer, sizeof(int16_t), 1, output);
}
لأن إصدار C الذي تستخدمه يعامل القيم غير الصفرية على أنها true والقيم الصفرية كـ false، يمكنك تبسيط بناء الجملة أعلاه إلى ما يلي:
// Create a buffer for a single sample
int16_t buffer;
// Read single sample from input into buffer while there are samples left to read
while (fread(&buffer, sizeof(int16_t), 1, input))
{
// Update volume of sample
buffer *= factor;
// Write updated sample to new file
fwrite(&buffer, sizeof(int16_t), 1, output);
}
الإرشادات التفصيلية
لست متأكدًا من كيفية الحل؟
كيفية الاختبار
يجب أن يتصرف برنامجك وفقًا للأمثلة أدناه.
$ ./volume input.wav output.wav 2.0
عندما تستمع إلى output.wav (كما هو الحال من خلال النقر مع الضغط على مفتاح التحكم output.wav في متصفح الملفات، اختر تحميل، ثم فتح الملف في مشغل الصوت على جهاز الكمبيوتر الخاص بك)، يجب أن يكون صوته أعلى مرتين من input.wav!
$ ./volume input.wav output.wav 0.5
عندما تستمع إلى output.wav، يجب أن يكون صوته بنصف مستوى الصوت input.wav!
صحة
check50 cs50/problems/2026/x/volume
النمط
style50 volume.c
كيفية الإرسال
في جهازك الطرفي، قم بتنفيذ ما يلي لإرسال عملك، والإجابة على المطالبات التي تظهر أيضًا.
submit50 cs50/problems/2026/x/volume