Allocate memory - pointer to array of pointers

I want to create a buffer for each channel of audio. The audio is interleaved uncompressed PCM WAV. So a pointer to an array of pointers should be a great way to go. I'm struggling a bit to understand what's going on in this code.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Create a pointer to an array of pointers.
short** buffers = new short *[NumChannels];

// Allocate memory for each channel with the number of samples.
for (int i = 0; i < NumChannels; i++) buffers[i] = new short[NumSamples];

/* I believe this loop will cycle through each channel and place a sample. 
For example in a stereo .wav: Left = sample, Right = sample, Left = sample,
Right = sample, etc. */
for (int i = 0; i < NumSamples; i ++) 
  for (int j = 0; j < NumChannels; j++)
 
/* This part of the loop reads a sample into each channel in an interleaved
pattern. Problem is I'm not sure if it's correct. For instance should it be
(HPSTR)buffers or (HPSTR)*buffers or (HPSTR)**buffers? The size to read
should be fine because the size of a short is two bytes and in 16 bit audio
each sample is two bytes. */
buffers[j][i] = mmioRead(handle, (HPSTR)*buffers, sizeof(short));


Thanks

Chris


Last edited on
What is HPSTR?

You're understanding of how to allocate a 2D array is correct. Now, buffers[j][i] is of type short. I'm not sure what mmioRead() does to judge what parameter it should take.
Last edited on
Read the documentation of mmioRead: http://msdn.microsoft.com/en-us/library/windows/desktop/dd757334%28v=vs.85%29.aspx

The return value is the number of bytes read so you don't want to store that in the buffer.

The second argument should be a pointer to the sample you want to read so I think it should be
(HPSTR) &buffers[j][i].
Last edited on
I get it!! I Know right where I screwed up. I had:

buffers[j][i] = mmioRead(handle, (HPSTR)*buffers, sizeof(short));

Which is wrong on so many levels. I was assigning the return value of mmioRead which like Peter87 said is the number of bytes read. So the next thing I tried was this:

mmioRead(handle, (HPSTR)&buffers[j][i], sizeof(short));

Which worked wonderfully. Now I'm left with another question. In a different project that I'm working on I allocated some memory, but not a pointer to a pointer of arrays, but just the typical fashion. Then I ran the mmioRead function. Here's the code:

1
2
3
size = DataChunkInfo.cksize;
	buffer = new short[size/sizeof(short)];
	mmioRead(handle, (HPSTR)buffer, size);


My question is why in the first example the '&' was added before buffers[j][i] and in the second example didn't have to add the '&' before buffer.

First example:
Pointer to an array of pointers.
mmioRead(handle, (HPSTR)&buffers[j][i], sizeof(short));

Second example:
Pointer to allocated memory (which is basically an array).
mmioRead(handle, (HPSTR)buffer, size);

Is it because the first example is a multi-dimensional array?
Last edited on
buffers[j][i] is the location you want mmioRead to write to so you give it a pointer to that memory location by using the & operator.

buffer is already a pointer to the memory location you want mmioRead to write to so no need to do anything special.
I think I'm getting it.
So buffers[j] is a pointer because of this statement:
1
2
// Create a pointer to an array of pointers.
short** buffers = new short *[NumChannels];


but buffers[j][i] is not because we wrote the statement like this:
1
2
// Allocate memory for each channel with the number of samples.
for (int i = 0; i < NumChannels; i++) buffers[i] = new short[NumSamples];


Instead if we had written it like this:
1
2
// Allocate memory for each channel with the number of samples.
for (int i = 0; i < NumChannels; i++) *buffers[i] = new short[NumSamples];

Then buffers[j][i] would be a pointer. Or am I way off?

*edit*
Apparently I'm way off. It can't be written like this:
1
2
// Allocate memory for each channel with the number of samples.
for (int i = 0; i < NumChannels; i++) *buffers[i] = new short[NumSamples];

When I tried it I got this for an error:
Error: a value of type "short*" cannot be assigned to an entity of type "short".

Last edited on
I have two classes, a WavReader class and a WavPlayer class. I want to pass a structure from the WavReader class that includes my buffers to the WavPlayer class. The header to my WavReader class is:
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
#include <windows.h> 
#include <iostream>
#pragma comment(lib, "winmm.lib")
using namespace std;
#ifndef WAVREADER_H
#define WAVREADER_H

typedef struct BUFFER{
	int channels;
	int samples;
	short **buffersData;
};


class WavReader{

public:
	WavReader ();
	~WavReader ();	
	BUFFER CreateBuffer();	

private:
	MMCKINFO ChunkInfo;
	MMCKINFO FormatChunkInfo;
	MMCKINFO DataChunkInfo;
	WAVEFORMATEX waveFmt;
	int NumChannels;
	int NumSamples;		
	short** buffers;
};
#endif 


The class definition for my WavReader class is:
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
#include <windows.h>
#include <iostream>
#pragma comment(lib, "winmm.lib")
#include "WavReader.h"
using namespace std;

/////////////////////////////////////////////////////////////////////////////////////
//  Open up a .wav file and read it to a buffer

BUFFER WavReader::CreateBuffer()
{
	HMMIO handle = mmioOpen(L"C:\\WINDOWS\\MEDIA\\TADA.wav", 0, MMIO_READ);

	memset(&ChunkInfo,0, sizeof(MMCKINFO));

	mmioDescend(handle, &ChunkInfo, 0, MMIO_FINDRIFF);

	FormatChunkInfo.ckid = mmioStringToFOURCC(L"fmt ", 0);
	mmioDescend(handle, &FormatChunkInfo, &ChunkInfo, MMIO_FINDCHUNK);
	mmioRead(handle, (char*)&waveFmt, sizeof(WAVEFORMATEX));

	mmioAscend(handle, &FormatChunkInfo, 0);
	DataChunkInfo.ckid = mmioStringToFOURCC(L"data", 0);
	mmioDescend(handle, &DataChunkInfo, &ChunkInfo, MMIO_FINDCHUNK);		

	NumChannels = waveFmt.nChannels;
	NumSamples = DataChunkInfo.cksize / (NumChannels * waveFmt.wBitsPerSample/8);	

// Allocate memory for the buffers.
	buffers = new short *[NumChannels];
	for (int i = 0; i < NumChannels; i++) buffers[i] = new short[NumSamples];

// Read data into the buffers.
	for (int i = 0; i < NumSamples; i ++) 
		for (int j = 0; j < NumChannels; j++) 
			mmioRead(handle, (HPSTR)&buffers[j][i], sizeof(short));

	mmioClose(handle, 0);	

// Create a structure to return and fill it.
	BUFFER bufferStruct;
	bufferStruct.channels = NumChannels;
	bufferStruct.samples = NumSamples;
// Not sure what to do here to pass my buffers to my WavPlayer class.
	bufferStruct.buffersData = ?????????
	return bufferStruct;
}
//////////////////////////////////////////////////////////////////////////////////////
// Constructor &  Destructor

WavReader::WavReader()
{
	// Do something here
}

WavReader::~WavReader()
{
	// Delete the buffer
}


The header for my WavPlayer class:
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
#include <windows.h> 
#include <iostream>
#include "WavReader.h"
#pragma comment(lib, "winmm.lib")
using namespace std;

#ifndef __WavPlayer__
#define __WavPlayer__


#include "vstsdk2.4\public.sdk\source\vst2.x\audioeffectx.h"

enum
{
	// Global
	kNumPrograms = 10,
	// Parameters Tags
	kPlay = 0,		
	kNumParams
};

class WavPlayer;
//------------------------------------------------------------------------
class WavPlayerProgram
{
friend class WavPlayer;
public:
	WavPlayerProgram ();
	~WavPlayerProgram () {}
private:
	float fPlay;	
	char name[24];
};
//------------------------------------------------------------------------
class WavPlayer : public AudioEffectX
{
public:
	WavPlayer (audioMasterCallback audioMaster);
	~WavPlayer ();

	virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);

protected:   
	WavPlayerProgram* programs;
	float parameters[kNumParams];

	float fPlay;
	MMCKINFO ChunkInfo;
	MMCKINFO FormatChunkInfo;
	MMCKINFO DataChunkInfo;
	WAVEFORMATEX waveFmt;	
	WavReader wavReader; 	
	short** returnedBuffers;	
	int NumChannels;
	int NumSamples;
};
#endif 


The class definition for the WavPlayer class:
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
#include <stdio.h>
#include <string.h>
#include <windows.h> 
#include <iostream>
#pragma comment(lib, "winmm.lib")
using namespace std;
#ifndef __WavPlayer__
#endif
#include "vstgui\aeffguieditor.h"
#include "WavPlayer.h"
#include "WavReader.h"
//-----------------------------------------------------------------------------
WavPlayerProgram::WavPlayerProgram ()
{
	// default Program Values	
	fPlay = 0;	

	strcpy (name, "Default");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
// The constructor of the VST plugin. I was told to intialize any variables here.
WavPlayer::WavPlayer (audioMasterCallback audioMaster) : AudioEffectX (audioMaster, kNumPrograms, kNumParams)
{	


	NumChannels = wavReader.CreateBuffer().channels;
	NumSamples = wavReader.CreateBuffer().samples;

// I need to access my buffers here.
        returnedBuffers = wavReader.CreateBuffer().buffersData;	
	  
	programs = new WavPlayerProgram[numPrograms];
	
	fPlay = 0;
	if (programs)
		setProgram (0);
		setNumInputs (0);	
		setNumOutputs (2);	
		setUniqueID ('LSWp');			
		resume ();	
}
//------------------------------------------------------------------------
WavPlayer::~WavPlayer ()
{
	if (programs)
		delete[] programs;
}

//---------------------------------------------------------------------------
void WavPlayer::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{	
	float* out1 = outputs[0];
	float* out2 = outputs[1];

// I cycle through my buffers and assign the values to the outs (*out1++).
	if (fPlay > 0)
	{		
		for (int i = 0; i < NumSamples; i ++) 
			for (int j = 0; j < NumChannels; j++)
			{						
				(*out1++) = (float)returnedBuffers[j][i]/32768.0;
				j++;
				(*out2++) = (float)returnedBuffers[j][i]/32768.0;				
			}		
	}
}


I know this may seem crazy, keep in mind I'm still trying to figure this out. There could very well be and most likely is a better way to accomplish this. At least this is helping me get a better grasp of pointers and memory allocation.

So if anyone can offer up some guidance that would be great.

Thanks
Your code is very hard to understand, I like to keep things simple.

WAVE PCM audio format is a very simple audio format, basically it is a header followed by uncrompressed audio data. From the header you get number of channels, bitrate, sample rate, etc

For a 16 bit stereo wave file, once you properly parse the header, you need to read every 16 bits (2 bytes) for left channel, read next 2 bytes to decode right channel and so on until end of data (total length you get previously from the header).

So you just need to copy the data to your buffers to decode left and right channels independently.


You could use fopen()/fread() / ifstream or memory mapped file to read the file in binary mode, not necessarily mmio* windows functions if you want a portable implementation.
Last edited on
Topic archived. No new replies allowed.