View Single Post
Old 09-26-2014, 06:43 PM   #25
Alkamist
Human being with feelings
 
Join Date: Dec 2011
Posts: 402
Default

I've made some progress. First, I went through and made the code for 1 channel. I managed to make it work, but now I'm trying to add arbitrary channel support. I'm getting output, but I'm hearing some crackles when it plays. It also doesn't seem to be repeating the entire sample (I can tell because I'm testing a loop and it comes back in off-time). I made the number of hardware channels a constant and just put in 2 because I'm not sure where to find that information yet. If someone has the time and could look at this and maybe give some advice I would really appreciate it.

My call in ProcessDoubleReplacing:

Code:
const int kNumHardChannels = 2;

void AlkaSampler::ProcessDoubleReplacing(double **inputs, double **outputs, int nFrames)
{
  double sampleOutput = 0;

  double *out[kNumHardChannels];

  for (int channel = 0; channel < kNumHardChannels; ++channel)
  {
    out[channel] = outputs[channel];
  }

  for (int sample = 0; sample < nFrames; ++sample)
  {
    for (int channel = 0; channel < kNumHardChannels; ++channel)
    {
      mAudioSample.getBuffer(sampleOutput, channel, sample, nFrames);
      *out[channel] = sampleOutput * mGain;
      ++out[channel];
    }
  }
}
AudioSample.h

Code:
#ifndef __AUDIOSAMPLE__
#define __AUDIOSAMPLE__

#include <iostream>
#include <fstream>
#include <vector>

class AudioSample
{
public:
  AudioSample();
  ~AudioSample(); 
  void loadWaveFile(char *fname);
  //Getters:
  unsigned int getSampleLength() { return mSubChunk2Size / (mNumChannels * (mBitsPerSample / 8)); }
  void getBuffer(double &sampleOutput, int channel, int sample, int nFrames);
  //Setters:
  void setBuffer();
private:
  std::ifstream myfile;
  std::vector<double> mBuffer;
  unsigned int mPlayhead;
  unsigned int mChunkSize, mSubChunk1Size, mSampleRate,
               mByteRate, mSubChunk2Size;
  unsigned short int mAudioFormat, mNumChannels, 
                     mBlockAlign, mBitsPerSample;
};

#endif /* defined(__AUDIOSAMPLE__) */
AudioSample.cpp

Code:
#include "AudioSample.h"

AudioSample::AudioSample() 
{
  mPlayhead = 0;
  loadWaveFile("c:/Users/Corey/Desktop/asdf1.wav");
}

AudioSample::~AudioSample() {}

void AudioSample::loadWaveFile(char *fname) 
{
  //Open the file in binary input mode.
  myfile.open(fname, std::ios::binary | std::ios::in); 
  
  if (myfile.is_open())
  {
    char buf[5] = {0};

    //Should have 'RIFF'.
    myfile.read(buf, 4);
  
    if (!strcmp(buf, "RIFF")) 
    { 
      //We had 'RIFF', so let's continue.

      //Size of the rest of the chunk following this number.
      myfile.read(buf, 4);
      mChunkSize = *(reinterpret_cast<unsigned int *>(buf));
      
      //Should contain 'WAVE'.
      myfile.read(buf, 4);
      
      if (!strcmp(buf,"WAVE"))
      { 
        //This is probably a wave file since it contained "WAVE".

        //Should contain 'fmt '.
        myfile.read(buf, 4);

        //Size of the rest of the Subchunk following this number.
        myfile.read(buf, 4);
        mSubChunk1Size = *(reinterpret_cast<unsigned int *>(buf));
        
        //PCM = 1, other values mean there is some type of file compression.
        myfile.read(buf, 2);
        mAudioFormat = *(reinterpret_cast<unsigned short *>(buf));

        //1 mono, 2 stereo, etc.
        myfile.read(buf, 2);
        mNumChannels = *(reinterpret_cast<unsigned short *>(buf));

        //The sample rate of the audio.
        myfile.read(buf, 4);
        mSampleRate = *(reinterpret_cast<unsigned int *>(buf));

        //mSampleRate * mNumChannels * mBitsPerSample / 8.
        myfile.read(buf, 4);
        mByteRate = *(reinterpret_cast<unsigned int *>(buf));

        //mNumChannels * mBitsPerSample / 8.
        myfile.read(buf, 2);
        mBlockAlign = *(reinterpret_cast<unsigned short *>(buf));

        //8 bits = 8, 16 bits = 16, 24 bits = 24, etc.
        myfile.read(buf, 2);
        mBitsPerSample = *(reinterpret_cast<unsigned int *>(buf));
        
        //Skip until we find the data chunk.
        myfile.read(buf, 4);
        while (strcmp(buf, "data"))
        {
          unsigned int extraChunkSize = 0;
          myfile.read(buf, 4);
          extraChunkSize = *(reinterpret_cast<unsigned int *>(buf));
          myfile.seekg(extraChunkSize, std::ios::cur);
          myfile.read(buf, 4);
        }

        //Size of the data.
        myfile.read(buf, 4);
        mSubChunk2Size = *(reinterpret_cast<unsigned int *>(buf));
        
        //Read in the data.
        setBuffer();

        //Close the file.
        myfile.close();
      } 
      else 
        std::cerr << "Error: RIFF file but not a wave file" << std::endl;
    } 
    else 
      std::cerr << "Error: not a RIFF file" << std::endl;
  } 
  else
    std::cerr << "Error: Could not open the file!" << std::endl;
} 

void AudioSample::setBuffer()
{
  const unsigned char bytesPerSample = 2;
  char buf[bytesPerSample];
  //1-Dimensional vector to hold all samples.
  mBuffer.resize(getSampleLength() * mNumChannels);
  //Iterate through each channel every sample.
  for (int sample = 0; sample < getSampleLength(); ++sample)
  {
    for (int channel = 0; channel < mNumChannels; ++channel)
    {
      //Read data into the temporary buffer.
      myfile.read(buf, bytesPerSample);
      //Reinterpret cast the temporary buffer into a short int value.
      //This will have to be changed with different bytes per sample,
      //but I'm not sure quite how to do it yet.
      short int outputValue = *(reinterpret_cast<short int *>(buf));
      //Check if the value is positive or negative and scale it between
      //-1 to 1 accordingly. After scaling it, put it in the buffer.
      if (outputValue >= 0)
      {
        mBuffer[sample * mNumChannels + channel] = static_cast<double>(outputValue) / 32767.0;
      }
      else
      {
        mBuffer[sample * mNumChannels + channel] = static_cast<double>(outputValue) / 32768.0;
      }
    }
  }
}

void AudioSample::getBuffer(double &sampleOutput, int channel, int sample, int nFrames)
{
  //Check if the current buffer index that is going to be played 
  //is bigger than the size of the vector. If it is, reset the
  //playhead so the sample repeats.
  if ((sample * mNumChannels + channel) + mPlayhead > getSampleLength() * mNumChannels)
  {
    mPlayhead = 0;
  }
  if (channel < mNumChannels)
  {
    //If we are within a channel that is present in the sample, set
    //the output value to the current buffer sample, which is offset
    //by the playhead.
    sampleOutput = mBuffer[(sample * mNumChannels + channel) + mPlayhead];
    //Increment the playhead by the buffer size every time we hit
    //the end of the buffer.
    if (sample >= nFrames - 1)
    {
      mPlayhead += nFrames;
    }
  }
  else
  {
    //If we make it to a channel that isn't present in the sample,
    //just set the output value to 0.
    sampleOutput = 0;
  }
}
Alkamist is offline   Reply With Quote