質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.51%

  • C

    4514questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C言語でMIDI編集

受付中

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 337

 前提・実現したいこと

C言語初心者です。このサイトにて初めて質問させていただきます。
C言語でMIDIファイルの中身の16進数の一部を削除する編集プログラムを作っています。
こちらのサイト(http://torasukenote.blog120.fc2.com/blog-entry-104.html)の
「4.トラックチャンクも読み込もう!」のプログラムに16進数の一部を削除するプログラムを追加したいのですが、上手くいかず困っています。

とりあえず16進数1つでもいいので削除ができればと思っております。
拙い文章ではありますが、ご回答お待ちしております。

 該当のソースコード

ソースコード
#include <stdio.h>
#include <stdlib.h>

typedef struct {
    // トラックチャンクのデータを格納する構造体
    char type[4]; // チャンクタイプを示す文字列を格納。「MTrk」が入るはず。[4byte]
    int size;     // トラックチャンクデータのサイズ [4byte]
    char *data;   // トラックデータ(MIDIイベントの羅列)へのポインタ
} TrackChunk;

int convertEndian(void *input, size_t s){
    // エンディアン変換をおこなう関数
    // stdlib.hをインクルードしてください。
    // 【引数】: void *input...エンディアン変換対象へのポインタ
    // 【引数】: size_t    s...変換対象のバイト数

    int i;   // カウンタ
    char *temp;   // 変換時に用いる一時的配列

    if((temp = (char *)calloc(s, sizeof(char))) == NULL){
        perror("Error: Cannot get memory for temp.");
        return 0;   // 領域確保できず(失敗)
    }

    for(i=0; i<s; i++){   // inputデータをtempに一時保管
        temp[i] = ((char *)input)[i];
    }

    for(i=1; i<=s; i++){   // tempデータを逆方向にしてinputへ代入
        ((char *)input)[i-1] = temp[s-i];
    }

    free(temp);   // 確保した領域を解放

    return 1;   // 正常終了
}

int main(){
    int i, j;   // カウンタ
    FILE *fp;         // ファイルポインタ生成
    int endian; //   エンディアン判定にいろいろ使用(0:BigEndian, 1:LittleEndian)

    // ヘッダチャンク情報
    char  header_chunk_type[4]; // チャンクタイプを示す文字列を格納。「MThd」が入るはず。[4byte]
    int   header_chunk_size;    // ヘッダチャンクデータのサイズ [4byte]
    short smf_format;     // SMFのフォーマットタイプ(0か1か2) [2byte]
    short tracks;         // トラックチャンク総数 [2byte]
    short division;       // 四分音符あたりの分解能(ここではデルタタイム) [2byte]

    // トラックチャンク情報
    TrackChunk *track_chunks;   // トラックチャンク情報を格納する配列のためのポインタ


    // エンディアン判定
    endian = 1;
    if(*(char *)&endian){   // リトルエンディアンなら...
        endian = 1;   // Little Endian
    } else {   // ビッグエンディアンなら...
        endian = 0;   // Big Endian
    }

    // MIDIファイルを開く
    if((fp = fopen("./midi/theme01.mid", "rb")) == NULL){   // バイナリ読み取りモードでファイルを開く
        perror("Error: Cannot open the file.");   // 失敗したらエラーを吐く
        return 0;
    }

    // ヘッダチャンク取得
    fread(header_chunk_type, 1, 4, fp);   // チャンクタイプ
    fread(&header_chunk_size, 4, 1, fp);  // チャンクデータサイズ
    fread(&smf_format, 2, 1, fp);   // SMFフォーマットタイプ
    fread(&tracks, 2, 1, fp);       // トラックチャンク総数
    fread(&division, 2, 1, fp);     // 分解能(デルタタイム)

    // 必要ならエンディアン変換
    if(endian){   // リトルエンディアンなら要変換
        // エンディアン変換処理
        convertEndian(&header_chunk_size, sizeof(header_chunk_size));
        convertEndian(&smf_format, sizeof(smf_format));
        convertEndian(&tracks, sizeof(tracks));
        convertEndian(&division, sizeof(division));
    }

    // 読み取ったヘッダチャンク情報を出力
    printf("# Header ========================\n");
    printf("header_chunk_type : %c%c%c%c\n", header_chunk_type[0],
        header_chunk_type[1], header_chunk_type[2], header_chunk_type[3]);
    printf("header_chunk_size : %d\n", header_chunk_size);
    printf("smf_format : %hd\n", smf_format);
    printf("tracks     : %hd\n", tracks);
    printf("division   : %hd\n", division);


    // トラックチャンク取得
    if((track_chunks = (TrackChunk *)calloc(tracks, sizeof(TrackChunk))) == NULL){   // トラック数に応じて領域確保
        perror("Error: Cannot get memory for track_chunks.");
        return 0;   // 領域確保できず(失敗)
    }
    for(i=0; i<tracks; i++){   // トラック数だけ繰返し
        fread(track_chunks[i].type, 1, 4, fp);   // チャンクタイプ
        fread(&track_chunks[i].size, 4, 1, fp);   // チャンクデータサイズ
          if(endian){   // リトルエンディアンなら要変換
            convertEndian(&track_chunks[i].size, sizeof(track_chunks[i].size));
          }
          if((track_chunks[i].data = (char *)calloc(track_chunks[i].size, sizeof(char))) == NULL){   // データサイズに応じて領域確保
            perror("Error: Cannot get memory for track_chunks[i].data .");
            return 0;   // 領域確保できず(失敗)
          }
        fread(track_chunks[i].data, track_chunks[i].size, sizeof(char), fp);   // データ(MIDIイベントの羅列)
        //fread関数は第一引数には格納先のバッファ、第二引数は読み込むデータ1つのバイト数、
        //第三引数は読み込むデータの個数、第四引数はファイルポインタを指定します。
    }

    // 読み取ったトラックチャンク情報を出力,
    for(i=0; i<tracks; i++){   // トラック数だけ繰返し
        printf("# Track[%02d] =====================\n", i);
        printf("track_chunks[%d].type : %c%c%c%c\n", i, track_chunks[i].type[0], track_chunks[i].type[1], track_chunks[i].type[2], track_chunks[i].type[3]);
        printf("track_chunks[%d].size : %d\n", i, track_chunks[i].size);
        printf("track_chunks[%d].data: \n", i);
        for(j=0; j<track_chunks[i].size; j++){
            printf("%02x ", (unsigned char)track_chunks[i].data[j]);   // 16進表記で出力 02は上位で足りない桁を文字0で埋め、最小2桁で表示する xは整数を16進で出力する
            if(!((j+1)%40)) printf("\n");   // 40バイト出力するたびに改行
            //else if(!((j+1)%10)) printf(": ");   // 10バイト出力するたびに区切る
        }
        printf(" \n");
    }

    return 1;
}

 試したこと

 補足情報(FW/ツールのバージョンなど)

OS:Windows10 使用環境:Atom

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • takabosoft

    2018/09/13 16:01

    何が上手く行っていないのか不明なのと、具体的に何の情報を削除しようとしているんでしょう?

    キャンセル

  • craftbosscoffee

    2018/09/17 16:38

    返信が遅れてしまい申し訳ありません。トラックデータの16進数の音の情報(90 54 31 00 90 56 31 00という音の情報の90 56 31 00 の部分だけを)削除して、音の数を減らしたいと思っております。

    キャンセル

回答 1

0

回答じゃないですが、まずいところを。
>    if((temp = (char *)calloc(s, sizeof(char))) == NULL){
これでは、tempには1文字分しか領域は確保されません

callocではこれでいいんですね。失礼しました

#ってか、エンディアン変換だけならこんなことしなくても。


入れ替える、んじゃなくて、BigEndian対応の読み出しルーチンにすれば。

unsigned char* s = input;
return (unsigned int)s[0]<<24 | s[1]<<16 | s[2]<<8 | s[3];

これだけで済みますぜ

んでもひとつ、
データを削除する、んじゃなくて、もひとつバッファを用意しといて、該当データを抜いてコピーする、という考え方で行くほうがいいかと。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/09/19 14:06

    なるほど...。その考えは思いつきませんでした。
    具体的にどういった方法で該当データを抜き出してコピーすることができますか?

    キャンセル

  • 2018/09/19 14:16

    元データのバッファと、書き込み用データのバッファを用意して(それぞれ1Mbyteとってもびくともしないでしょ)、一括してバッファに読み込み、そのデータを書き込みバッファにコピー&編集して、それが済んだら書き込みバッファの内容をファイルに書き出し。
    とするほうがわかりやすいんでは

    キャンセル

  • 2018/09/19 14:29

    #include <stdio.h>
    #include <stdlib.h>

    typedef struct {
    // トラックチャンクのデータを格納する構造体
    char type[4]; // チャンクタイプを示す文字列を格納。「MTrk」が入るはず。[4byte]
    int size; // トラックチャンクデータのサイズ [4byte]
    char *data; // トラックデータ(MIDIイベントの羅列)へのポインタ
    } TrackChunk;

    int convertEndian(void *input, size_t s){
    // エンディアン変換をおこなう関数
    // stdlib.hをインクルードしてください。
    // 【引数】: void *input...エンディアン変換対象へのポインタ
    // 【引数】: size_t s...変換対象のバイト数

    int i; // カウンタ
    char *temp; // 変換時に用いる一時的配列

    if((temp = (char *)calloc(s, sizeof(char))) == NULL){
    perror("Error: Cannot get memory for temp.");
    return 0; // 領域確保できず(失敗)
    }

    for(i=0; i<s; i++){ // inputデータをtempに一時保管
    temp[i] = ((char *)input)[i];
    }

    for(i=1; i<=s; i++){ // tempデータを逆方向にしてinputへ代入
    ((char *)input)[i-1] = temp[s-i];
    }

    free(temp); // 確保した領域を解放

    return 1; // 正常終了
    }

    int main(){
    int i, j; // カウンタ
    FILE *fp; // ファイルポインタ生成
    int endian; // エンディアン判定にいろいろ使用(0:BigEndian, 1:LittleEndian)

    // ヘッダチャンク情報
    char header_chunk_type[4]; // チャンクタイプを示す文字列を格納。「MThd」が入るはず。[4byte]
    int header_chunk_size; // ヘッダチャンクデータのサイズ [4byte]
    short smf_format; // SMFのフォーマットタイプ(0か1か2) [2byte]
    short tracks; // トラックチャンク総数 [2byte]
    short division; // 四分音符あたりの分解能(ここではデルタタイム) [2byte]

    // トラックチャンク情報
    TrackChunk *track_chunks; // トラックチャンク情報を格納する配列のためのポインタ
    //------------------------------------------------------------------------------------------------------
    TrackChunk *track_chunks1; //コピー用の新配列
    //------------------------------------------------------------------------------------------------------
    // エンディアン判定
    endian = 1;
    if(*(char *)&endian){ // リトルエンディアンなら...
    endian = 1; // Little Endian
    } else { // ビッグエンディアンなら...
    endian = 0; // Big Endian
    }

    // MIDIファイルを開く
    if((fp = fopen("./midi/theme01.mid", "rb")) == NULL){ // バイナリ読み取りモードでファイルを開く
    perror("Error: Cannot open the file."); // 失敗したらエラーを吐く
    return 0;
    }

    // ヘッダチャンク取得
    fread(header_chunk_type, 1, 4, fp); // チャンクタイプ
    fread(&header_chunk_size, 4, 1, fp); // チャンクデータサイズ
    fread(&smf_format, 2, 1, fp); // SMFフォーマットタイプ
    fread(&tracks, 2, 1, fp); // トラックチャンク総数
    fread(&division, 2, 1, fp); // 分解能(デルタタイム)

    // 必要ならエンディアン変換
    if(endian){ // リトルエンディアンなら要変換
    // エンディアン変換処理
    convertEndian(&header_chunk_size, sizeof(header_chunk_size));
    convertEndian(&smf_format, sizeof(smf_format));
    convertEndian(&tracks, sizeof(tracks));
    convertEndian(&division, sizeof(division));
    }

    // 読み取ったヘッダチャンク情報を出力
    printf("# Header ========================\n");
    printf("header_chunk_type : %c%c%c%c\n", header_chunk_type[0],
    header_chunk_type[1], header_chunk_type[2], header_chunk_type[3]);
    printf("header_chunk_size : %d\n", header_chunk_size);
    printf("smf_format : %hd\n", smf_format);
    printf("tracks : %hd\n", tracks);
    printf("division : %hd\n", division);


    // トラックチャンク取得
    if((track_chunks = (TrackChunk *)calloc(tracks, sizeof(TrackChunk))) == NULL){ // トラック数に応じて領域確保
    perror("Error: Cannot get memory for track_chunks.");
    return 0; // 領域確保できず(失敗)
    }
    for(i=0; i<tracks; i++){ // トラック数だけ繰返し
    fread(track_chunks[i].type, 1, 4, fp); // チャンクタイプ
    fread(&track_chunks[i].size, 4, 1, fp); // チャンクデータサイズ
    if(endian){ // リトルエンディアンなら要変換
    convertEndian(&track_chunks[i].size, sizeof(track_chunks[i].size));
    }
    if((track_chunks[i].data = (char *)calloc(track_chunks[i].size, sizeof(char))) == NULL){ // データサイズに応じて領域確保
    perror("Error: Cannot get memory for track_chunks[i].data .");
    return 0; // 領域確保できず(失敗)
    }
    fread(track_chunks[i].data, track_chunks[i].size, sizeof(char), fp); // データ(MIDIイベントの羅列)
    //fread関数は第一引数には格納先のバッファ、第二引数は読み込むデータ1つのバイト数、
    //第三引数は読み込むデータの個数、第四引数はファイルポインタを指定します。
    }
    //*----------------------------------------------------------------------------------------------------


    for(i=0;i<tracks;i++){
    for(j=0;j<track_chunks[i].size;j++){
    (unsigned char)track_chunks1[i].data[j]= (unsigned char)track_chunks[i].data[j];//新配列にコピーする
    }
    }



    //----------------------------*/


    // 読み取ったトラックチャンク情報を出力,
    for(i=0; i<tracks; i++){ // トラック数だけ繰返し
    printf("# Track[%02d] =====================\n", i);
    printf("track_chunks[%d].type : %c%c%c%c\n", i, track_chunks[i].type[0], track_chunks[i].type[1], track_chunks[i].type[2], track_chunks[i].type[3]);
    printf("track_chunks[%d].size : %d\n", i, track_chunks[i].size);
    printf("track_chunks[%d].data: \n", i);
    for(j=0; j<track_chunks[i].size; j++){
    printf("%02x ", (unsigned char)track_chunks[i].data[j]); // 16進表記で出力 02は上位で足りない桁を文字0で埋め、最小2桁で表示する xは整数を16進で出力する
    if(!((j+1)%40)) printf("\n"); // 40バイト出力するたびに改行
    //else if(!((j+1)%10)) printf(": "); // 10バイト出力するたびに区切る
    }
    printf(" \n");
    }
    for(j=0; j<track_chunks[i].size; j++){
    printf("%02x ", (unsigned char)track_chunks1[i].data[j]);

    }
    return 1;
    }

    頂いたコメントを考慮して、まずは新しい配列にfor文でコピーするプログラムを書いてみたのですが、(unsigned char)track_chunks1[i].data[j]= (unsigned char)track_chunks[i].data[j];
    の文のところで
    lvalue required as left operand of assignment
    というエラーが出てしまいました。
    どのように改善すればよろしいでしょうか

    キャンセル

  • 2018/09/19 14:30

    --------で区切ったところが自分で追加したプログラム部分です

    キャンセル

  • 2018/09/24 12:19

    とりあえず目先のエラーに関しては左辺のキャストを消せば大丈夫だと思います。 (それでそのエラーは消えますが、その操作が意味的に正しいこと・バグや間違いがないかどうかは分かりません。)

    キャンセル

同じタグがついた質問を見る

  • C

    4514questions

    C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。