SDL generating sound without wav file

I am trying to Mix_LoadWAV_RW to play sound via SDL_RWops* rw, so not playing back a wav-file. Is the following approach the right one? A buffer from a ofstream is placed in the SDL_RWops rw using SDL_RWFromMem. I need to play the array frequencies. No sound is generated. Also, is buffer empty? Can you review this code?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

template <typename Word>
std::ostream& write_word( std::ostream& outs, Word value, unsigned size = sizeof( Word ) )
{
  for (; size; --size, value >>= 8)
    outs.put( static_cast <char> (value & 0xFF) );
  return outs;
}

Main:
		constexpr double two_pi = 6.283185307179586476925286766559;
		constexpr double max_amplitude = 32760;
		double seconds;

		std::vector<std::ofstream> f;
		size_t file_length;
		std::vector<char> buffer(file_length);
		SDL_RWops* rw;
		for (int h = 0; h < mystack; h++){
		    std::string fileName = "MyTest" + std::to_string(h) + ".wav";
		    f.emplace_back(fileName, ios::binary );
		    seconds = 0.0;
		    for (int j = 1; j < numoflines[h].size()/2+1; j++){
			  seconds += durations[j][h]/2000;
			}
			f[h] << "RIFF----WAVEfmt ";
			write_word( f[h],     16, 4 );  // no extension data
			write_word( f[h],      1, 2 );  // PCM - integer samples
			write_word( f[h],      2, 2 );  // two channels (stereo file)
			write_word( f[h],  44100, 4 );  // samples per second (Hz)
			write_word( f[h], 176400, 4 );  // (Sample Rate * BitsPerSample * Channels) / 8
			write_word( f[h],      4, 2 );  // data block size (size of two integer samples, one for each channel, in bytes)
			write_word( f[h],     16, 2 );  // number of bits per sample (use a multiple of 8)

			size_t data_chunk_pos = f[h].tellp();
			f[h] << "data----";


			double hz        = 44100;
			std::vector<double> value;
			int j = 1;
			int N = hz * seconds;
			for (int n = 0; n < N*(numoflines[h].size()/2-1+1); n++)
			{
				double amplitude = (double)n / N * max_amplitude;
				value = {};
				for (int m = 0; m < 1; m++){
					value.push_back(sin( (two_pi * n * frequencies[j][h][m]) / hz ));
					write_word( f[h], (int)(                 amplitude  * value[m]), 2 );
					//write_word( f[h], (int)((max_amplitude - amplitude) * value[m]), 2 );
				}
				if(n == N*j){
					j++;
				}
			}
sizes above)
			file_length = f[h].tellp();

			f[h].seekp( data_chunk_pos + 4 );
			write_word( f[h], file_length - data_chunk_pos + 8 );

			f[h].seekp( 0 + 4 );
			write_word( f[h], file_length - 8, 4 );

		    std::ostringstream ss;
		    ss << f[h].rdbuf();
		    ifstream mystream((ss.str()).c_str());

			std::vector<char> buffer(file_length);
			std::copy(std::istreambuf_iterator<char>(mystream), std::istreambuf_iterator<char>(), buffer.begin());
			rw = SDL_RWFromMem(&buffer[h], buffer.size());
		}

        Mix_Chunk *wave = NULL;
        SDL_Init(SDL_INIT_EVERYTHING);
        Mix_OpenAudio(44100, 0);
        for(int h = 0, t = hxF; h < mystack; h++, t++){
    	    wave = Mix_LoadWAV_RW(rw, SDL_TRUE);
            Mix_PlayChannelTimed(-1, wave, 0, -1);
    	}
Last edited on
Shouldn't line 71 be
 
rw = SDL_RWFromMem(buffer.data(), buffer.size());
?
Last edited on
Line 71 will overwrite rw so whatever rw pointed to before will be "leaked". When the code reaches line 78 rw will point to the last rwops that you loaded. It will try to load a "WAV" from it and then it will free it (because you pass true as second argument) so next time it reaches this line rw will be a dangling pointer.
Last edited on
Here's an example that generates a 10-second long wave-file containing a chirp signal, saves it to disk, and plays it, using SDL_RWops.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#define SDL_MAIN_HANDLED
#include <SDL2/SDL.h>
#include <SDL2/SDL_mixer.h>

#include <iostream>
#include <cstdint>
#include <cassert>
#include <cmath>

using u8 = unsigned char; // do not use std::uint8_t here
using i16 = std::int16_t;
using i32 = std::int32_t;
using u16 = std::uint16_t;
using u32 = std::uint32_t;
using i64 = std::int64_t;
using f32 = float;

int constexpr wav_header_size = 44;
int constexpr wav_sample_rate = 44100;
int constexpr wav_sample_size_bytes = 2;
// Returns the number of bytes required to store a WAVE file containing n samples. 
[[nodiscard]] constexpr i64 wav_get_size_bytes(int f) noexcept 
{ return wav_header_size + static_cast<i64>(f) * wav_sample_size_bytes; }

constexpr u8* store_u32_be(u8* p, u32 n) noexcept
{
  p[0] = static_cast<u8>((n & 0xff'00'00'00u) >> 030);
  p[1] = static_cast<u8>((n & 0x00'ff'00'00u) >> 020);
  p[2] = static_cast<u8>((n & 0x00'00'ff'00u) >> 010);
  p[3] = static_cast<u8>((n & 0x00'00'00'ffu) >> 000);
  return p + 4;
}

constexpr u8* store_u32_le(u8* p, u32 n) noexcept
{
  p[3] = static_cast<u8>((n & 0xff'00'00'00u) >> 030);
  p[2] = static_cast<u8>((n & 0x00'ff'00'00u) >> 020);
  p[1] = static_cast<u8>((n & 0x00'00'ff'00u) >> 010);
  p[0] = static_cast<u8>((n & 0x00'00'00'ffu) >> 000);
  return p + 4;
}

constexpr u8* store_u16_le(u8* p, u16 n) noexcept
{
  p[1] = static_cast<u8>((n & 0xff'00u) >> 010);
  p[0] = static_cast<u8>((n & 0x00'ffu) >> 000);
  return p + 2;
}

// Writes a WAVE file header into the buffer pointed-to by p.
// The file header describes 1-channel, 44100Hz, signed 16-bit little-
// endian data consisting of n samples.  Returns a pointer one-past-the-end 
// of the header, where the sample data is expected to begin.
// 
// See 
//   http://soundfile.sapp.org/doc/WaveFormat/ 
// for format information.
constexpr u8* wav_synthesize_header(u8* p, int n)
{
  [[maybe_unused]] u8* const p_original = p; 

  // Size of data in bytes without metadata:
  i64 const data_chunk_size = static_cast<i64>(n) * wav_sample_size_bytes;
  assert(data_chunk_size > 0);
  assert(data_chunk_size <= std::numeric_limits<u32>::max());

  // Size of data & almost all metadata
  i64 const riff_chunk_size = data_chunk_size - 8 + wav_header_size;
  assert(riff_chunk_size > 0);
  assert(riff_chunk_size <= std::numeric_limits<u32>::max());

  // RIFF chunk
  p = store_u32_be(p, 0x52494646u);      // 4cc magic number "RIFF"
  p = store_u32_le(p, static_cast<u32>(riff_chunk_size)); 
  //    RIFF->WAVE
  p =   store_u32_be(p, 0x57415645);     // 4cc magic number "WAVE"
  //      RIFF->WAVE->fmt chunk
  p =     store_u32_be(p, 0x666d7420);   // 4cc magic number "fmt "
  p =     store_u32_le(p, 16);           // 16 bytes remaining in this chunk
  p =     store_u16_le(p, 1);            // LPCM
  p =     store_u16_le(p, 1);            // 1 channel
  p =     store_u32_le(p, wav_sample_rate);
  p =     store_u32_le(p, wav_sample_rate * wav_sample_size_bytes);
  p =     store_u16_le(p, wav_sample_size_bytes); // frame alignment in bytes
  p =     store_u16_le(p, 16);           // 16 bits per sample
  //      RIFF->WAVE->data chunk
  p =     store_u32_be(p, 0x64617461);   // 4cc magic number "data"
  p =     store_u32_le(p, static_cast<u32>(data_chunk_size));

  assert(p - p_original == wav_header_size);

  return p;
}

f32 constexpr two_pi = 6.283185307179586476925286766559f;

u32 constexpr wav_samples = wav_sample_rate * 10; // 10 seconds
i64 constexpr wav_buffer_size_bytes = wav_get_size_bytes(wav_samples);

static_assert(wav_buffer_size_bytes > 0);
static_assert(wav_buffer_size_bytes <= std::numeric_limits<u32>::max());

u8 wav_buffer[wav_buffer_size_bytes];

[[nodiscard]] constexpr f32 interpolate(f32 t, f32 a, f32 b) { return a + (b - a) * t; }
[[nodiscard]] constexpr i16 discretize(f32 t) { return static_cast<i16>(t * 32767.0f); }

[[noreturn]] void die() { std::abort(); }

int main()
{
  // Write a WAVE file header into wav_buffer, and return a pointer
  // just past-the-end of the header (where the samples should go).
  u8* data = wav_synthesize_header(wav_buffer, wav_samples);

  f32 theta = 0.f;
  f32 omega = 0.f;

  f32 volume = 0.05f; // keep the volume low
  f32 constexpr frequency_start = 55.0f;
  f32 constexpr frequency_finish = 880.0f;

  for (int i = 0; i < wav_samples; ++i)
  {
    data = store_u16_le(data, discretize(volume * std::sin(theta))); 
    omega = interpolate(static_cast<f32>(i) / wav_samples,
      two_pi * frequency_start  / wav_sample_rate, 
      two_pi * frequency_finish / wav_sample_rate);
    theta = std::fmod(theta + omega, two_pi);
  }

  SDL_SetMainReady();
  if (SDL_Init(SDL_INIT_EVERYTHING) < 0) die();

  // Write the synthesized audio to disk:
  SDL_RWops* file_stream = SDL_RWFromFile("output.wav", "w"); 
  SDL_RWwrite(file_stream, wav_buffer, 1, wav_buffer_size_bytes);
  SDL_RWclose(file_stream);

  // Play the synthesized audio.
  if (Mix_OpenAudio(wav_sample_rate, AUDIO_S16LSB, 1, 4096) < 0) die();
  SDL_RWops* wav_buffer_stream = SDL_RWFromMem(wav_buffer, wav_buffer_size_bytes);

  if (wav_buffer_stream == nullptr) die();
  Mix_Chunk* chunk = Mix_LoadWAV_RW(wav_buffer_stream, 0);
  if (chunk == nullptr) die();
  Mix_PlayChannel(-1, chunk, 0);
  
  // Wait for the sound to stop playing.
  while (Mix_Playing(-1)) SDL_Delay(100); 

  // Free resources in reverse order of their acquisition
  Mix_FreeChunk(chunk);
  SDL_RWclose(wav_buffer_stream);
  Mix_CloseAudio();
  SDL_Quit();

  return 0;
}

Last edited on
Topic archived. No new replies allowed.