This may be a tough one to answer

This is code that I'm working on for a VST audio plugin. Incase people don't know, it's compiled as a .dll and loaded in a host application. WavPlayer is the class that is loaded as the plugin. WavReader is a "helper" class, not sure what to call it.

I keep getting an Access Violation error and can't quite figure out why.
I do not recommend trying to use this code as it may be full of errors, kinda why I'm posting here for help. I've commented the code. A lot of the code can be skipped over as it doesn't pertain the my question. I've removed some code out of WavPlayer.cpp that doesn't pertain to my question.
This thread may also clue into what I'm trying to do. http://www.cplusplus.com/forum/general/76159/

Thanks,

Chris

WavReader.h
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
#include <windows.h> 
#include <iostream>
#pragma comment(lib, "winmm.lib")
#ifndef WAVREADER_H
#define WAVREADER_H

/*
This is the structure that I want to return from CreateBuffer().
*/
struct BUFFER{	
	int passedChannels;
	int passedSamples;	
	short **passedBuffers;
	};

class WavReader{
public:
	BUFFER CreateBuffer();

	WavReader ();
	~WavReader ();	

private:

// Structures
	MMCKINFO ChunkInfo;
	MMCKINFO FormatChunkInfo;
	MMCKINFO DataChunkInfo;
	WAVEFORMATEX waveFmt;
	BUFFER passedData;
	
// Variables
	int readNumChannels;
	int readNumSamples;	

// Pointers
	short** readBuffers;
};
#endif


WavReader.cpp
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
#include <windows.h>
#include <iostream>
#pragma comment(lib, "winmm.lib")
#include "WavReader.h"


/*
Open up a .wav file and read it to a buffer.
*/
BUFFER WavReader::CreateBuffer()
{
	// Open the file for reading
	HMMIO handle = mmioOpen(L"C:\\WINDOWS\\MEDIA\\TADA.wav", 0, MMIO_READ);

	memset(&ChunkInfo,0, sizeof(MMCKINFO));
	
	// Read the necessary information from the open file.
	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);
	
	// Get the number of channels and number of samples.
	readNumChannels = waveFmt.nChannels;
	readNumSamples = DataChunkInfo.cksize / (readNumChannels * waveFmt.wBitsPerSample/8);

	// Allocate memory for the samples per channel.
	readBuffers = new short *[readNumChannels];
	for (int i = 0; i < readNumChannels; i++) readBuffers[i] = new short[readNumSamples];

	// Read samples per channel into the memory allocated and pointed at by readBuffers.
	for (int j = 0; j < readNumSamples; j++) 
		for (int i = 0; i < readNumChannels; i++) 
			mmioRead(handle, (HPSTR)&readBuffers[i][j], sizeof(short));

	// Close the file.		
	mmioClose(handle, 0);	
	
	// Assign data to be returned in the passedData struct.
	passedData.passedChannels = readNumChannels;
	passedData.passedSamples = readNumSamples;
	passedData.passedBuffers = readBuffers;	

	// Return the struct passedData.
	return passedData;
}

/*
Constructor Destructor
*/

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

WavReader::~WavReader()
{	
	for (int i = 0; i < readNumChannels; i++) delete[] readBuffers[i];
	delete[] readBuffers;
	
	// Do something here
}


WavPlayer.h
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
/*
This file contains a lot of information pertaining to creating
a VST audio plugin. Most of this does not pertain to my question.
I will comment and explain the important areas and areas where 
I have questions. You can skip to the bottom of this file.
*/

#include <windows.h> 
#include <iostream>
#include "vstsdk2.4\public.sdk\source\vst2.x\audioeffectx.h"
#include "WavReader.h"
#pragma comment(lib, "winmm.lib")

#ifndef __WavPlayer__
#define __WavPlayer__

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 ();

	//---from AudioEffect-----------------------
	virtual void processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames);

	virtual void setProgram (VstInt32 program);
	virtual void setProgramName (char* name);
	virtual void getProgramName (char* name);
	virtual bool getProgramNameIndexed (VstInt32 category, VstInt32 index, char* text);
	

	virtual void setParameter (VstInt32 index, float value);
	virtual float getParameter (VstInt32 index);
	virtual void getParameterLabel (VstInt32 index, char* label);
	virtual void getParameterDisplay (VstInt32 index, char* text);
	virtual void getParameterName (VstInt32 index, char* text);

	virtual void resume ();

	virtual bool getEffectName (char* name);
	virtual bool getVendorString (char* text);
	virtual bool getProductString (char* text);
	virtual VstInt32 getVendorVersion () { return 1000;}

	virtual VstPlugCategory getPlugCategory () { return kPlugCategEffect;}

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

// Plugin Parameters
	float fPlay;	
/*
The following variables, pointers and class instances are important to my question
*/	
// Class Objects	
	WavReader wavReader; // Instance of WavReader class.
	
// Variables
	int NumChannels; // Variable to store the number of channels returned from WavReader::CreateBuffer()
	int NumSamples; // Variable to store the number of samples per channel returned from WavReader::CreateBuffer()
	float outputVal; // Calculated value to send to outputs. Output value has to be a float value from -1.0 to 1.0

// Pointers
	short** returnedBuffers; //Pointer pointer to the memory location of the buffers.
};

#endif 


WavPlayer.cpp
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
/*
This file contains a lot of information pertaining to creating
a VST audio plugin. Most of this does not pertain to my question.
I will comment and explain the important areas and areas where 
I have questions.
*/

#include <stdio.h>
#include <string.h>
#include <windows.h> 
#include <iostream>
#include "vstgui\aeffguieditor.h"
#include "WavPlayer.h"
#include "WavReader.h"
#pragma comment(lib, "winmm.lib")
#ifndef __WavPlayer__
#endif


WavPlayerProgram::WavPlayerProgram ()
{
	// default Program Values	
	fPlay = 0;	

	strcpy (name, "Default");
}
/*
The constructor of the VST plugin. This is where variables are initialized.
*/
WavPlayer::WavPlayer (audioMasterCallback audioMaster) : AudioEffectX (audioMaster, kNumPrograms, kNumParams)
{	

	// Here I assign the necessary variables and pointers to the returned struct.
	NumChannels = wavReader.CreateBuffer().passedChannels;
	NumSamples = wavReader.CreateBuffer().passedSamples;	
	returnedBuffers = wavReader.CreateBuffer().passedBuffers;
	// We can skip towards the bottom. The following pertains to VST plugins and
	// has nothing to do with my quesiton.

	canProcessReplacing(1);		
	
	// init    
	programs = new WavPlayerProgram[numPrograms];
	
	fPlay = 0;
	if (programs)
		setProgram (0);
		setNumInputs (0);	
		setNumOutputs (2);	
		setUniqueID ('LSWp');			
		resume ();	
}

WavPlayer::~WavPlayer ()
{
	if (programs)
		delete[] programs;
}

/*
This is where the audio gets processed. In this case I want to send the 
information pointed at by **returnedBuffers to the outputs. I cycle through
each channels samples and convert them to a float with a value between 
-1.0 to 1.0. Then I assign that value and send it to the corresponding output.
*/
void WavPlayer::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{	
	float* out1 = outputs[0];
	float* out2 = outputs[1];
	
	for(int k=0; k<sampleFrames;k++)				
		for (int i = 0; i < NumSamples; i++) 
			for (int j = 0; j < NumChannels; j++)
				{							
					outputVal = (float)returnedBuffers[j][i]/32768.0;
					(*out1++) = outputVal; // If I comment out this line, no errors.
				
					j++;						
					outputVal = (float)returnedBuffers[j][i]/32768.0;	
					(*out2++) = outputVal; // If I comment out this line, no errors.
					/*
					If I include the lines that I commented out I get this error:
					Access violation reading location 0xbc0c8010.
					But I can't figure out why?
					*/
					
				} 		
	
}
//This is the end 
Last edited on
In processReplacing you are assuming NumChannels is an even number. Is that the case?

Are you sure that outputs[0] and outputs[1] has a length of at least sampleFrames * NumSamples * NumChannels / 2?
Last edited on
In this situation there are two channels, Left and Right.

Are you sure that outputs[0] and outputs[1] has a length of at least sampleFrames * NumSamples * NumChannels / 2?


This could very well be where I'm messing up. So the host calls processReplacing and is supposed to run the loop for the amount of samplesFrames. I originally had the loop written like this, but had the same error:

1
2
3
4
5
6
7
8
9
10
11
12
13
	for(int k=0; k<sampleFrames;k++)
               {				
		     for (int i = 0; i < NumSamples; i++) 
		          for (int j = 0; j < NumChannels; j++)
				{							
					outputVal = (float)returnedBuffers[j][i]/32768.0;
					(*out1++) = outputVal;
				
					j++;						
					outputVal = (float)returnedBuffers[j][i]/32768.0;	
					(*out2++) = outputVal;					
				}
              }


I'm sure my loops are part of the issue.
1
2
for (int i = 0; i < NumSamples; i++) 
for (int j = 0; j < NumChannels; j++)


This part of the loop alternates between out1 and out2 channels placing one sample at a time. The sample data is interleaved.
The sample data is read from the file like this:

http://ccrma.stanford.edu/courses/422/projects/WaveFormat/

scroll towards the bottom.


Last edited on
1
2
3
4
	// Read samples per channel into the memory allocated and pointed at by readBuffers.
	for (int j = 0; j < readNumSamples; j++) 
		for (int i = 0; i < readNumChannels ; i++) 
			mmioRead(handle, (HPSTR)&readBuffers[i][j], sizeof(short));


...

Shouldn't it look like this?

1
2
3
4
5
6
7
8
9
10
11
12
	
          
		     for (int i = 0; i < NumSamples; i++) 
		          for (int j = 0; j < NumChannels ; j++)
		          {							
					outputVal = (float)returnedBuffers[j][i]/32768.0;
					(*out1++) = outputVal;
								
					outputVal = (float)returnedBuffers[j][i]/32768.0;	
					(*out2++) = outputVal;					
	                   }
                 


I'm not sure what you mean about sampleFrames? Is sampleFrames, the same thing as number of samples per channel?


EDIT: correction
I think what was happening, is that with j++ in the loop, and the loop condition for number of channels being essentially, in your case, < 2, you would get to the iteration where j == 1, and then your additional j++ inside the loop body would make j == 2, and you would attempt to access returnedBuffers[2][i].

The outer loop seams strange. For each go around of the outer loop, you copy your the entire buffer. So it seams that when your done, out, will contain sampleFrames number of copies of the buffer one after the other?

I' not certain my assessment is accurate, but that's how it seams.
Last edited on
1
2
3
4
5
6
7
8
9
10
for (int i = 0; i < NumSamples; i++) 
	for (int j = 0; j < NumChannels; j++)
		{							
		     outputVal = (float)returnedBuffers[j][i]/32768.0;
		     (*out1++) = outputVal;
				
		     j++;						
		     outputVal = (float)returnedBuffers[j][i]/32768.0;	
		     (*out2++) = outputVal;
		} 	


I have the extra j++ inbetween the outs because I want to output the "Left" channel sample 1 and then the "Right" channel sample 1 and continue that pattern.

I guess you could say returnedBuffers[j][i] is representing returnedBuffers[currentChannel][currentSample]

Not sure if I'm explaining that correctly, or hell if I'm wrong.
I have the extra j++ inbetween the outs because I want to output the "Left" channel sample 1 and then the "Right" channel sample 1 and continue that pattern.

I guess you could say returnedBuffers[j][i] is representing returnedBuffers[currentChannel][currentSample]

Not sure if I'm explaining that correctly, or hell if I'm wrong.


I think you need to change it to this then?

1
2
3
4
5
6
7
for (int i = 0; i < NumSamples; i++) 
		     outputVal = (float)returnedBuffers[0][i]/32768.0;
		     (*out1++) = outputVal;
				 
		     outputVal = (float)returnedBuffers[1][i]/32768.0;	
		     (*out2++) = outputVal;
}


Or something like that.

Can you explain what sampleFrames is? Is it the buffer rate or something like that? Do you need to process that many samples of returnedBuffers, every time this function is called?

I'm just shooting in the dark. If this were the case, maybe you need something like this?

1
2
3
4
5
6
7
8
9
10
11
static int OffSet = 0;   //static local variables retain values between function calls

for (int i = Offset; (i < Offset + sampleFrames) && (i < NumSamples); i++) 
		     outputVal = (float)returnedBuffers[0][i]/32768.0;
		     (*out1++) = outputVal;
				 
		     outputVal = (float)returnedBuffers[1][i]/32768.0;	
		     (*out2++) = outputVal;
}
OffSet += sampleFrames;
//increment OffSet by sampleFrames each time function is called 
Last edited on
sampleFrames is the buffer size the host application sends to the audio card. So it processes that many samples each time processReplacing is called. Once it's done processing the samples, it passes it along to the soundcard and processReplacing is called again to get more samples to pass to the soundcard. So for (int i = Offset; (i < Offset + sampleFrames) is a way of not overloading the host's buffer.

You very well could be correct about trying to access returnedBuffers[2][i];

I need to read through my code again and really try to think about what's going on with the loop.

These stinking loops can get confusing.

Offset just keeps track of where your at.

1
2
3
4
5
6
7
8
9
10
11
static int OffSet = 0;   //static local variables retain values between function calls

for (int i = Offset; (i < Offset + sampleFrames) && (i < NumSamples); i++) 
		     outputVal = (float)returnedBuffers[0][i]/32768.0;
		     (*out1++) = outputVal;
				 
		     outputVal = (float)returnedBuffers[1][i]/32768.0;	
		     (*out2++) = outputVal;
}
OffSet += sampleFrames;
//increment OffSet by sampleFrames each time function is called  


Say that sampleFrames == 10. The first time the function is called, Offset == 0. You send samples 0 - 9 to the sound card. Then your offset is incremented by 10. The next time the function is called, you send samples 10 - 19, and so on.



Last edited on
Ever have an answer come to you when you least expect it.

It dawned on me that with the current pattern of my loops, I'm trying to dump all the samples (approx. 70,000) of them into a buffer that can only hold approx. 1500 samples..... duh.

Ok, now I just need to get the pattern right.

EDIT: update

That was exactly the problem. I swapped a couple things around and no more crash and I get sound. I've got a couple small things to iron out, once I get it I'll post the solution.
Last edited on
GOT IT!!!

It works perfectly!! I'm so pumped!!

Here's the solution starting at the processReplacing() call.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void WavPlayer::processReplacing (float** inputs, float** outputs, VstInt32 sampleFrames)
{	
	float* out1 = outputs[0];
	float* out2 = outputs[1];
	static int offset = 0;
					
		for (int i = 0; i < sampleFrames; i++) 
			for (int j = 0; j < NumChannels; j++)
				{						
					outputVal = (float)returnedBuffers[j][offset]/32768.0;
					(*out1++) = outputVal;	

					j++;						
					outputVal = (float)returnedBuffers[j][offset]/32768.0;	
					(*out2++) = outputVal;

					offset++;
				
					if (offset > NumSamples) offset = 0;					
				} 	
}


Also, the interleaving is done correctly!! Left channel comes out the left speaker, right channel out of the right. No audio artifacts either.

Now when I look at the solution, it seems so simple.

@iseeplusplus:
When you mentioned the offset, it reminded me of my last plugin where I had used a "sample counter" to keep track of my position in a sequence measured in samples. It's almost exactly the same situation. Thanks so much for helping me get to my solution.

Thanks everyone for all the help. I'm sure as this project moves forward I'll run into other crazy situations, so I'll be back.
Last edited on
I notice how I was wrong about accessing returnedBuffers[2][i].

I'm glad you got it working.

The only thing I wonder about now, is wether it should be,
if (offset >= NumSamples) offset = 0;

Because if offset == NumSamples, you'll access returnedBuffers[j][NumSamples], when your last item should be, returnedBuffers[j][NumSamples - 1]?
Last edited on
Ah yes, because the way I currently have it, it will wait until offset > NumSamples before it sets it to zero and I actually want my last access point to the array to be NumSamples - 1, otherwise I'll be once again trying to access memory that hasn't been allocated. Would it make more sense to have if (offset == NumSamples) offset = 0;? To make sure that I don't go past NumSamples - 1. or is having the >= a better fail safe. Will I ever learn.

@iseeplusplus:
You are a genius, thanks.

EDIT: question
I have used #include <crtdbg.h> and this in my main() function: _CrtSetDbgFlag(_crtDbgFlag | _CRTDBG_LEAK_CHECK_DF); to help me keep track of memory leaks while debugging in my practice programs, but with dealing with a .dll I'm not sure if this will work. Do I place it in the plugin's constructor or destructor? Not sure where to put it, but I'd like to use it, it's really helpful.
Last edited on
Topic archived. No new replies allowed.