この Lab では1~2人のクラスメートと協力することは問題ありませんが、そのグループのすべての受講生が等しく実習に貢献することが求められます。
オーディオファイルの音量を変更するプログラムを記述します。
$ ./volume INPUT.wav OUTPUT.wav 2.0
INPUT.wav
は元のオーディオファイルの名前、OUTPUT.wav
は指定された係数(例えば2.0)でスケーリングされた音量を持つオーディオファイルの名前です。
WAVファイル
WAVファイルは、オーディオを表すための一般的なファイル形式です。WAVファイルには、オーディオがシーケンスの「サンプル」 (特定の時点でのオーディオ信号の値を表す数字) として保存されます。WAVファイルは、ファイルのサイズ、毎秒のサンプル数、各サンプルのサイズなど、ファイル自体に関する情報を含む44バイトの 「ヘッダー」 で始まります。ヘッダーの後に、WAVファイルには一連のサンプルが含まれています。各サンプルは、特定の時点でのオーディオ信号を表す2バイト (16ビット) の整数です。
各サンプル値を指定の係数でスケールすると、オーディオのボリュームが変化します。たとえば、各サンプル値に2.0を掛けると、元のオーディオのボリュームが倍になります。各サンプルを0.5倍にすると、ボリュームは半分になります。
型
これまでのところ、int
、bool
、char
、double
、float
、long
など、Cにはさまざまな型があることを見てきました。stdint.h
と呼ばれるヘッダファイルの中には、整数のサイズ (ビット単位) と符号 (符号付きまたは符号なし) を非常に正確に定義することを可能にする多くの他の型の宣言があります。この実習では、特に2つのタイプが役立ちます。
uint8_t
は8ビットの符号なし (すなわち負でない) 整数を格納する型です。WAVファイルのヘッダの各バイトをuint8_t
値として扱うことができます。int16_t
は16ビット符号付き (正または負の) 整数を格納する型です。WAVファイル内の各オーディオサンプルをint16_t
値として扱うことができます。
始め方
VS Codeを開きます。
ターミナルウィンドウ内をクリックすることから始めて、それからcd
を実行します。
その後プロンプトは次のようになっていることがわかります。
$
ターミナルウィンドウの内側をクリックし、次のように入力します。
wget https://cdn.cs50.net/2021/fall/labs/4/volume.zip
その後にEnterを押すと、volume.zipというZIPがあなたのCodespaceにダウンロードされます。wgetと次のURLの間にあるスペースや、その他の文字を見落とさないように注意してください。
次に
unzip volume.zip
を実行して、volumeというフォルダを作成します。
ZIPファイルは不要になったため、
rm volume.zip
を実行し、プロンプトで “y “に続いてEnterで応答すると、ダウンロードしたZIPファイルが削除されます。
次に
cd volume
の後にEnterを押して、そのディレクトリに移動する(つまり、開く)。これでプロンプトは以下のようになります。
volume/ $
すべて成功した場合は、次のように実行します。
ls
すると、volume.c ファイルと input.wav ファイルが表示されます。
もし問題が発生した場合は、もう一度同じ手順を踏んで、どこが間違っていたかを確認してください
実装の詳細
volume.c
の実装を完了して、サウンドファイルの音量を指定の係数で変更します。
- このプログラムは、3つのコマンドライン引数を受け取ります。
input
は元のオーディオファイルの名前を表し、output
は生成される新しいオーディオファイルの名前を表し、factor
は元のオーディオファイルのボリュームをスケールする量を表します。- たとえば、
factor
が2.0
の場合、プログラムはinput
のオーディオファイルのボリュームを倍にし、新しく生成されたオーディオファイルをoutput
に保存します。
- たとえば、
- プログラムは、最初に入力ファイルからヘッダーを読み取り、出力ファイルに書き込みます。このヘッダの長さは常に正確に44バイトであることを思い出してください。
volume.c
は、ヘッダのバイト数に等しいHEADER_SIZE
という名前の変数をすでに定義していることに注意してください。
- 次に、プログラムは残りのデータを16ビット (2バイト) のサンプルを一度に1つずつWAVファイルから読み込みます。プログラムでは、各サンプルをファクターで乗算し、新しいサンプルを出力ファイルに書き込みます。
- WAVファイルは16ビットの符号付き値をサンプルとして使用すると仮定します。実際には、WAVファイルのサンプルあたりのビット数はさまざまですが、この実習では16ビットのサンプルを想定しています。
malloc
を使用するプログラムは、メモリーをリークしてはいけません。
ウォークスルー
このビデオは、コースがまだコードを書くためにCS50 IDEを使用していたときに記録されたものです。インターフェイスはあなたのCodespaceと異なるように見えますが、2つの環境の動作はほぼ同じであるはずです!
ヒント
- 入力ファイルから読み込むWAVファイルヘッダのデータを格納するために、バイトの配列を作成します。
uint8_t
型を使用してバイトを表すと、次のような構文でヘッダーのn
バイトの配列を作成できます。
uint8_t header[n];
n
はバイト数で置き換えます。その後、header
を引数としてfread
またはfwrite
を使用して、ヘッダーを読み書きできます。
- WAVファイルから読み込んだオーディオサンプルを保存する 「バッファ」 を作成することもできます。
int16_t
型を使用してオーディオサンプルを格納すると、次のような構文で変数bufferを作成できます。
int16_t buffer;
その後、&buffer
を引数としてfread
またはfwrite
を使用して、バッファの読み込みまたは書き込みを行うことができます (変数のアドレスを取得するために&
演算子が使用されることを思い出してください)。
fread
とfwrite
のドキュメントはこちらにあります。- 特に、どちらの関数も以下の引数を受け付けることに注意してください。
ptr
: メモリ内でデータを格納する場所 (ファイルから読み取る場合) またはデータを書き込む場所 (ファイルにデータを書き込む場合) へのポインタ
size
: データ項目のバイト数
nmemb
:読み取りまたは書き込みを行うデータ項目数 (それぞれのバイトsize
)
stream
: 読み書きするファイルポインタ
- ドキュメントによると、
fread
は正常に読み込まれたデータの数を返します。これは、ファイルの最後に達したことを確認する場合に便利です。
- 特に、どちらの関数も以下の引数を受け付けることに注意してください。
解決方法がわかりませんか?
コードのテスト方法
プログラムは、次の例に従って動作する必要があります。
$ ./volume input.wav output.wav 2.0
output.wav
(ファイルブラウザでoutput.wavをcontrolキーを押しながらクリックし、ダウンロードを選択し、コンピュータのオーディオプレーヤーでファイルを開きます。) を再生すると、input.wav
の2倍の音量になります。
$ ./volume input.wav output.wav 0.5
output.wav
を再生すると、input.wav
の半分の音量になります。
check50
を使用して以下を実行し、コードの正確さを評価してください。ただし、コンパイルとテストは必ず自分で行ってください。
check50 cs50/labs/2022/x/volume
以下を実行し、style50
を使用してコードのスタイルを評価します。
style50 volume.c
提出方法
ターミナルで、以下を実行して提出してください。
submit50 cs50/labs/2022/x/volume