See if you can beat this problem!

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Sure it's all going to be relativly straight forward for you guys, but I'm really struggling with this. I am currently working on a VSTi drum machine and I am finding it a nightmare trying to get the my code to play back a WAV file without crashing my system or producing nothing but a glitching noise. Any help on this problem would be greatfully recieved.

I'll try and explain the bits of code I've used, but please bear it mind I am relativly new to programming so be gentle :)

This part of the code is in processReplacing and reads in the PCM file Snare.Wav (I hope)

Code: Select all

void CDrumMajor::processReplacing(float **inputs, float **outputs, long sampleFrames)
{
	// NEW : we have no inputs so only evaluate the output buffers

    float *out1 = outputs[0];
    float *out2 = outputs[1];
	
	FILE* theFile = fopen("e:\\snare.wav", "r"); // open it
	fseek(theFile, 0, SEEK_END); // 
	size = ftell(theFile) >> 1; // 
	fseek(theFile, 0, SEEK_SET); // 
	short *tempArray = new short[size]; // alloc up some arrays
	float *floatArray = new float[size];
	float *leftArray = new float[size];
	float *rightArray = new float[size];
	int buffercount = 0;
	int samplecount = 0;
	int play = 0;
	// read in the entire file
	fread(tempArray, size << 1, 1, theFile);
	fclose(theFile);
	for(long i = 0; i < size; ++i)
{
// rescale and convert to float
floatArray[i] = (float)tempArray[i] / 32767.f;

//CLAMP(floatArray[i], -1.0f, 1.0f);


}
int count = 0;
for (i = 44 ; i <size; i= i+2)
{
	leftArray[count] = floatArray[i];
	rightArray[count] = floatArray[i+1];
	count++;
}
This part is also in process replacing

Code: Select all

while(--sampleFrames >= 0)
		{
			
			
			VstTimeInfo* myTime = getTimeInfo ( kVstPpqPosValid | kVstTempoValid );
			 MyTempo = myTime->tempo;
			MyNumerator = myTime->timeSigNumerator;
			SamplePos = myTime->samplePos;
			SampleRate =myTime->sampleRate;
			PPQPos = myTime->ppqPos;
			BarStartPos = myTime->barStartPos;

			KickBeatPos = MyNumerator / 16;


// ignore this next bit - not used yet

			for (count = 1; count <17; count++)
			{
				randprob = rand()%99 + 1; 
				KickBeatProb[count] = randprob;
			}




		     for (count = 1; count < 17; count++)
			 {	aBarStartPos[count] = BarStartPos + (KickBeatPos * (count-1));
				
				if ((PPQPos >= aBarStartPos[count] && PPQPos <=(aBarStartPos[count] + 0.2) && (KickBeat[count] == 1) && (play == 0)))
						{
					/*		play = 1;
							samplecount=0;

							
						}
				if (play == 1)
				{	*/
							(*out1++) = leftArray[samplecount];    // replacing
							(*out2++) = rightArray[samplecount];
							samplecount++;
							if ((samplecount * 2) > size) samplecount = 0;
				//	
				}
			 }

			 int size;
This part of the code checks to see if a beat is required (by seeing if the part of BeatKick array = 1 and then sets the sample playing. When the sample count is equal to double the size of the file (i.e. the left and right channels added together) it resets the counter to zero

All of which would be great, except, erm.... well it doesn't work

Any help would mean that I would be able to sleep again at night.

I'll sort you out with some kind of beer related thanks (somehow)

T*

Post

Umm, a wav file is more than just data. You have to scan the header, ensure the encoding is right, count channels, sample rate, bit-depth, etc. Further, the data is likely integer, so you need to turn it into float.

I'd suggest you load the file early, and store the normalized PCM float data. Then read that as needed.

Post

Process and ProcessReplacing are time critical routines. I think you should aim to make as few calculations as possible within these routines (optimize). So, reading wave data from a file on disc is probably not a good idea. This will certainly interrupt these processes.

It might be worthwhile to pre-fetch the wave file and store it in and array (memory). During the Process/ProcessReplacing, clock each sample out of the array according to the index of samlpleframes. Starting point can be defined by e.g. deltaframes.

Good luck.
Image

Post

Did you read the posts I made in your last thread asking about playing .Wav's in a VSTi? I doesn't look like you took any notice of it.

You've still got getTimeInfo() called inside the while loop. This will get called a completely unnecessary number of times. It only needs to be called once, because the structure it returns does not change throughout the duration of your function.

You need to locate the relevant chunks in the file before reading them. I wrote a function that will do this for you.

You'd probably define a structure to facilitate reading the data.

As thockin suggests, load the file early, and convert it to 32bit float format.
Think about what is going on here! processReplacing() is called pretty regularly by the host, and you are reading an entire wave file every time you are called. You might get called every 128 samples, which means that you want to keep your loop efficient. I'm surprised your computer doesn't crash.


here is some code for a console app, that will read the wavefile of your choice (actually its restricted to 16bit stereo wav), and output the first samples (l and r) as floats.

it's a bit hacked, but you can clean it up, and incorporate it into your plug and add further handling etc. This could be done a lot better using a few classes and a bit of thought.

Code: Select all

// testwav.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <fstream.h>

#ifdef _MSC_VER
#include <crtdbg.h>
#define assert		_ASSERTE		/* VC++: Dialog box assert */
#else
#define assert(exp)	
#endif


#define PCM_FORMAT			1U

#define NUM_ELEMENTS(a)		(sizeof(a)/sizeof((a)[0]))

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long uint32;

typedef signed char int8;
typedef signed short int16;
typedef signed long int32;

typedef float SAMPLE;
typedef float PARAMETER;

typedef struct {
  SAMPLE l;
  SAMPLE r;
} SAMPLE_ST_T;

typedef struct {
	uint32		len;
	SAMPLE_ST_T *smp;
} SAMPLE_BUF_T;

typedef struct {
	uint16 AudioFormat;
	uint16 NumChannels;
	uint32 SampleRate;
	uint32 ByteRate;
	uint16 BlockAlign;
	uint16 BitsPerSample;	
} FMT_CHUNK_T;

typedef struct {
	uint16 l;
	uint16 r;
} STEREO_I16_T;

/*
** prototypes
*/

static bool readForwardToChunk(
 char *chunkId,
 ifstream& fileStream
);

static bool
loadwav(
	char			*filename,
	SAMPLE_BUF_T&	buf
);

/*
** MAIN
*/

int main(int argc, char* argv[])
{
	SAMPLE_BUF_T	buf;
	char			filename[255U];

	cout << "Enter wavefile name" << endl;
	cin >> filename;

	buf.len = 0U;
	buf.smp = NULL;

	if (loadwav(filename, buf)) {
		if (buf.len > 0U) {
			printf("left: %.2f\n", buf.smp[0].l);
			printf("right: %.2f\n", buf.smp[0].r);
		}
	}

	if(buf.smp) {
		delete[] buf.smp;
	}

	return 0;
}

/*
** allocates buffer on the heap: callers responsibility to clean it up!!
*/
static bool
loadwav(
	char			*filename,
	SAMPLE_BUF_T	&buf
) {
	uint32			datalen;
	uint32			i;
	FMT_CHUNK_T		fmt;
	uint32			fmt_size;
	STEREO_I16_T	smp;

	ifstream	fileStream(filename, ios::binary);

	assert(buf.smp == NULL);

	if (!fileStream) {
		return false;
	}

	if (readForwardToChunk("fmt ", fileStream)) { 
		/*
		** read format chunk
		*/
		fileStream.read((int8*)&fmt_size, sizeof(fmt_size));

		assert(sizeof(fmt) >= fmt_size);

		fileStream.read((int8*)&fmt, sizeof(fmt));

		// at the moment this will only handle 16bit stero wave

		if (fmt.BlockAlign != (fmt.NumChannels * fmt.BitsPerSample  / 8U)) {
			printf("error in block align"); 
			return false;
		}

		if (!(fmt.AudioFormat	== PCM_FORMAT	&&
			fmt.BitsPerSample	== 16U	&&
			fmt.NumChannels		== 2U
			)
		) {
			printf("unsupported sample type (want stero 16bit pcm)"); 
			return false;
		}

	} 
	else { 
	  printf("bad file"); 
	  fileStream.close(); 
	  return false; 
	} 

	// rewind to beginning of file here?  Not sure if you'd ever need to do this 

	if (readForwardToChunk("data", fileStream)) { 

		/* 
		** read data chunk size 
		*/

		fileStream.read((int8*) &datalen, sizeof(datalen));
		
		/* 
		** read data chunk to buffer, performing 
		** any necessary conversions - e.g. 24bit to 
		** 32bit.  could use a function pointer, that 
		** is assigned when readong out the sample 
		** info 
		*/ 

		assert(sizeof(smp) == fmt.BlockAlign);


		buf.len = datalen / fmt.BlockAlign;
		buf.smp	= new SAMPLE_ST_T[buf.len];

		if (!buf.smp) {
			fileStream.close(); 
			buf.len = 0;
			return false;
		}

		for(i = 0; (i < buf.len) && (fileStream != NULL); i++) {

			fileStream.read((int8*) &smp, sizeof(smp));
			
			buf.smp[i].l = ((SAMPLE) smp.l) / ((SAMPLE) 0x7FFF);
			buf.smp[i].r = ((SAMPLE) smp.r) / ((SAMPLE) 0x7FFF);;
		}

		if (i != datalen / fmt.BlockAlign) {
			printf("warning: unexpected end of file");
			buf.len = i;
		}
	} 
	else { 
	  printf("data segment not found"); 
	  fileStream.close(); 
	  return false; 
	} 

	return true;
}



static bool
readForwardToChunk(
 char *chunkId,
 ifstream& fileStream
) {

  bool foundChunk = false;
  char c;
  int pos = 0;
  int len = strlen(chunkId);

  do {
    fileStream.read(&c, sizeof(char));
    if(c == chunkId[pos]) {
      ++pos;
    }
    else {
      pos = 0;
    }
    if(pos == len) {
      foundChunk = true;
      break;
    }
  } while (!fileStream.eof());

   return foundChunk;
}
Last edited by texture on Fri Feb 20, 2004 8:06 pm, edited 1 time in total.

Post

hope u hadn't used that - there was a bit of a whopper in there - I forgot to scale the floats to 1.0 - -1.0 when converting them! It should be fixed in the above code, but not tested.

Post

Couple of tips:

- As someone already stated, you want to avoid file I/O during your processing code. If you must use file I/O, you are better doing so using Win32 non-cached mode (FILE_FLAG_NO_BUFFERRING). Otherwise you will page excessively.

- The safest way to parse a WAV file is to use the mmioXxx API in Windows. This ensures you are walking through the RIFF chunk hierarchy correctly.

Happy coding!

Post Reply

Return to “DSP and Plugin Development”