1

This was solved by changing the buffer from int16_t to int8_t since I was trying to write 8bit audio.

I'm trying to fill a buffer for a mono wave file with two different frequencies but failing at it. I'm using CLion in Ubuntu 18.04.

I know, the buffer size is equal to duration*sample_rate so I'm creating a int16_t vector with that size. I tried filling it with one note first.

for(int i = 0; i < frame_total; i++)
    audio[i] = static_cast<int16_t>(128 + 127 * sin(i));

which generated a nice long beeep. And then I changed it with:

for(int i = 0; i < frame_total; i++)
    audio[i] = static_cast<int16_t>(128 + 127 * sin(i*2));

which generated a higher beeeep, but when trying to do the following:

for(int i = 0; i < frame_total/2; i++)
    audio[i] = static_cast<int16_t>(128 + 127 * sin(i*2));

for(int i = frame_total/2; i < frame_total; i++)
    audio[i] = static_cast<int16_t>(128 + 127 * sin(i));

I expect it to write the higher beep in the first half of the audio, and fill the another fall with the "normal" beep. The *.wav file just plays the first note the entire time.

#define FORMAT_AUDIO 1
#define FORMAT_SIZE 16

struct wave_header{
    // Header
    char      riff[4];
    int32_t   file_size;
    char      wave[4];
    // Format
    char      fmt[4];
    int32_t   format_size;
    int16_t   format_audio;
    int16_t   num_channels;
    int32_t   sample_rate;
    int32_t   byte_rate;
    int16_t   block_align;
    int16_t   bits_per_sample;
    // Data
    char      data[4]
    int32_t   data_size;
};

void write_header(ofstream &music_file ,int16_t bits, int32_t samples, int32_t duration){
    wave_header wav_header{};
    int16_t channels_quantity = 1;

    int32_t total_data = duration * samples * channels_quantity * bits/8;
    int32_t file_data = 4 + 8 + FORMAT_SIZE + 8 + total_data;

    wav_header.riff[0] = 'R';
    wav_header.riff[1] = 'I';
    wav_header.riff[2] = 'F';
    wav_header.riff[3] = 'F';

    wav_header.file_size = file_data;

    wav_header.wave[0] = 'W';
    wav_header.wave[1] = 'A';
    wav_header.wave[2] = 'V';
    wav_header.wave[3] = 'E';

    wav_header.fmt[0] = 'f';
    wav_header.fmt[1] = 'm';
    wav_header.fmt[2] = 't';
    wav_header.fmt[3] = ' ';

    wav_header.format_size = FORMAT_SIZE;
    wav_header.format_audio = FORMAT_AUDIO;
    wav_header.num_channels = channels_quantity;
    wav_header.sample_rate = samples;
    wav_header.byte_rate = samples * channels_quantity * bits/8;
    wav_header.block_align = static_cast<int16_t>(channels_quantity * bits / 8);
    wav_header.bits_per_sample = bits;

    wav_header.data[0] = 'd';
    wav_header.data[1] = 'a';
    wav_header.data[2] = 't';
    wav_header.data[3] = 'a';

    wav_header.data_size = total_data;

    music_file.write((char*)&wav_header, sizeof(wave_header));
}

int main(int argc, char const *argv[]) {
    int16_t bits = 8;
    int32_t samples = 44100;
    int32_t duration = 4;
    ofstream music_file("music.wav", ios::out | ios::binary);
    int32_t  frame_total = samples * duration;
    auto* audio = new int16_t[frame_total];

    for(int i = 0; i < frame_total/2; i++)
        audio[i] = static_cast<int16_t>(128 + 127 * sin(i*2));

    for(int i = frame_total/2; i < frame_total; i++)
        audio[i] = static_cast<int16_t>(128 + 127 * sin(i));

    write_header(music_file, bits, samples, duration);
    music_file.write(reinterpret_cast<char*>(audio),sizeof(int16_t)*frame_total);

    return 0;
}
iLu Anzu
  • 23
  • 6
  • When you write only the first part, do you hear only the /2 time wave? It perhaps has something to do with your header size. Also, take WAVEFORMAT structure from the SDK. – Michael Chourdakis Apr 05 '19 at 15:02
  • It plays only the note I write first, in the full wave file. I can't use another structure, this is a project for univ, and we were told we should use this one. I'm following this [structure](http://soundfile.sapp.org/doc/WaveFormat/) – iLu Anzu Apr 05 '19 at 15:11

2 Answers2

2

There are two major issues with your code.


The first one is that you may be writing an invalid header depending on your compiler settings and environment.

The reason is that the wave_header struct is not packed in memory, and may contain padding between members. Therefore, when you do:

music_file.write((char*)&wav_header, sizeof(wave_header));

It may write something that isn't a valid WAV header. Even if you are lucky enough to get exactly what you wanted, it is a good idea to fix it, because it may change at any moment and surely it isn't portable.


The second issue is that the call to write the actual wave:

music_file.write(reinterpret_cast<char*>(audio),sizeof(char)*frame_total);

Is writing exactly half the amount of data you are expecting. The actual size of the data pointed by audio is sizeof(int16_t) * frame_total.

This explains why you are only hearing the first part of the wave you wrote.

Acorn
  • 24,970
  • 5
  • 40
  • 69
  • I changed it to int16_t but still getting the same output, I'm working on the first issue! – iLu Anzu Apr 05 '19 at 15:15
  • @iLuAnzu Have you verified the file by hexdumping it? Does it contain all the value you expect where you expect them? i.e. check the header and also check the wave and how many values you have in the file. – Acorn Apr 05 '19 at 15:24
  • I just did that, and I'm not getting the expected values at the beginning... I'm following this [website](http://soundfile.sapp.org/doc/WaveFormat/) which shows how may the first 72 bytes of the wave file should be, while it's true I'm getting the values mentioned in the page, I'm also getting "20" hex values between those values, as you said "may contain padding between members.", I've used an [onlinehex editor](https://www.onlinehexeditor.com/) to check the values. But I'm not sure about how to fix it exactly, how do I pack it in memory? – iLu Anzu Apr 05 '19 at 16:59
  • @iLuAnzu You can start reading about padding and packing structs [in this question](https://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct-equal-to-the-sum-of-sizeof-of-each-member). Normally you want to `write` member by member, or use a compiler-specific mechanism to ensure the struct is packed (see the question for some examples). You also probably want to read on endianness, too! – Acorn Apr 05 '19 at 17:04
  • I solved it by changing the buffer from int16_t to int8_t array since it's 8bits audio... (someone mentioned it), my error was assuming the array had to be the same data type of "bits_per_sample" parameter in the header. Don't know why even I got those additional hex values, it stills plays well. Thanks a lot – iLu Anzu Apr 05 '19 at 17:34
0

This was solved by changing the buffer (audio) from int16_t to int8_t since I was trying to write 8bit audio.

iLu Anzu
  • 23
  • 6