void YSE::INTERNAL::soundFile::loadStreaming() {

  File file;
  if (IO().getActive()) {
    // will be deleted by AudioFormatReader
    customFileReader * cfr = new customFileReader;
    cfr->create(fileName.c_str());
    streamReader = SOUND::Manager().getReader(cfr);
  }
  else {
    file = File::getCurrentWorkingDirectory().getChildFile(juce::String(fileName));
    streamReader = SOUND::Manager().getReader(file);
  }

  if (streamReader != nullptr) {
    _fileBuffer.setSize(streamReader->numChannels, STREAM_BUFFERSIZE);
    // sample rate adjustment
    _sampleRateAdjustment = static_cast<Flt>(streamReader->sampleRate) / static_cast<Flt>(SAMPLERATE);
    _length = (Int)streamReader->lengthInSamples;
    _buffer = _fileBuffer.getArrayOfReadPointers();
    _channels = _fileBuffer.getNumChannels();
    _streamPos = 0;
    fillStream(false);
    // file is ready for use now
    state = READY;
  }
  else {
    LogImpl().emit(E_FILEREADER, "Unable to read " + file.getFullPathName().toStdString());
    state = INVALID;
  }
}
void YSE::INTERNAL::soundFile::run() {
  if (_streaming) {
    if (source == nullptr) {
      if (IO().getActive()) {
        // will be deleted by AudioFormatReader
        customFileReader * cfr = new customFileReader;
        cfr->create(fileName.c_str());
        streamReader = SOUND::Manager().getReader(cfr);
      }
      else {
        streamReader = SOUND::Manager().getReader(file);
      }
    }
    else {
      streamReader = SOUND::Manager().getReader(source);
    }
    if (streamReader != nullptr) {
      _buffer.setSize(streamReader->numChannels, STREAM_BUFFERSIZE);
      // sample rate adjustment
      _sampleRateAdjustment = static_cast<Flt>(streamReader->sampleRate) / static_cast<Flt>(SAMPLERATE);
      _length = (Int)streamReader->lengthInSamples;
      _streamPos = 0;
      fillStream(false);
      // file is ready for use now
      state = READY;
      return;
    }
    else {
      LogImpl().emit(E_FILEREADER, "Unable to read " + file.getFullPathName().toStdString());
      state = INVALID;
      return;
    }
  }

  // load non streaming sounds in one go
  ScopedPointer<AudioFormatReader> reader;
  if (source == nullptr) {
    if (IO().getActive()) {
      // will be deleted by AudioFormatReader
      customFileReader * cfr = new customFileReader;
      cfr->create(fileName.c_str());
      reader = SOUND::Manager().getReader(cfr);
    }
    else {
      reader = SOUND::Manager().getReader(file);
    }
    
  }
  else {
    reader = SOUND::Manager().getReader(source);
  }
  
  if (reader != nullptr) {
    _buffer.setSize(reader->numChannels, (Int)reader->lengthInSamples);
    reader->read(&_buffer, 0, (Int)reader->lengthInSamples, 0, true, true);
    // sample rate adjustment
    _sampleRateAdjustment = static_cast<Flt>(reader->sampleRate) / static_cast<Flt>(SAMPLERATE);
    _length = _buffer.getNumSamples();
    // file is ready for use now
    state = READY;
    return;
  }
  else {
    LogImpl().emit(E_FILEREADER, "Unable to read " + file.getFullPathName().toStdString());
    state = INVALID;
    return;
  }
}
Bool YSE::INTERNAL::soundFile::read(std::vector<DSP::sample> & filebuffer, Flt& pos, UInt length, Flt speed, Bool loop, SOUND_STATUS & intent, Flt & volume) {
  /** Yes, this function uses goto...
      It is highly optimized for speed and this is the best way I could find
      to ensure good performance. Suggestions are welcome.
  */

  if (state != READY) return false;

  // adjust speed for sample rate
  speed *= _sampleRateAdjustment;

  // don't play streaming sounds backwards
  if (_streaming && speed < 0) speed = 0;

  Flt realPos = 0;
  if (_streaming) {
    realPos = pos;
    while (pos >= STREAM_BUFFERSIZE) {
      pos -= STREAM_BUFFERSIZE;
    }
    realPos -= pos;
  }

  Flt ** ptr2;
  if (!_audioBuffer) ptr2 = _buffer.getArrayOfChannels();
  
  // this is for a smooth fade-in to avoid glitches
  if (intent == SS_WANTSTOPLAY) {
    intent = SS_PLAYING;
    volume = 0.f;
  }

  Flt startPos = pos;
  YSE::SOUND_STATUS startIntent = intent;
  Flt startVolume = volume;
  
  // Now start filling the channels.
  // Most sounds will probably only have one channel. At any rate, it is 
  // assumed that soundFile and soundImplementation have the same amount
  // of channels.
  FOREACH (filebuffer) {
    // these are to reset to starting point in case of multi channel sounds
    pos = startPos;
    volume = startVolume;
    intent = startIntent;

    // this assumes the output and file have the same number of channels
    Flt * out = filebuffer[i].getBuffer();
    Flt * in;
    if (!_audioBuffer)  {
      in = ptr2[i];
    }
    else { // only for audioBuffer sources
      in = _audioBuffer->getChannel(i).getBuffer();
      _length = _audioBuffer->getLength();
    }
    UInt l = length;

  startAgain:
    // evaluate intent and see what course to take
    while (l > 0) {
      
      // if intent is stopped or paused, we can just fill the rest of the 
      // buffer with zeroes. Normally, stopped or paused sounds are not
      // even processed, but we get here when a sounds starts as 'wants_to_pause',
      // fades out to zero and still has to fill the rest of the buffer.
      if (intent == SS_STOPPED || intent == SS_PAUSED) {
        // fill the rest with zero's
        for (; l > 7; l -= 8, out += 8) {
          out[0] = 0.f;     out[1] = 0.f;
          out[2] = 0.f;     out[3] = 0.f;
          out[4] = 0.f;     out[5] = 0.f;
          out[6] = 0.f;     out[7] = 0.f;
        }
        while (l--) *out++ = 0.f;
        goto nextBuffer; // this output channel buffer is full if we get here
      } 

      // Most of the time a sound will just play at full volume. This has nothing
      // to do with the sound volume itself (which will be applied afterwards) but
      // with sounds fading in and out at start, stop and pause (to avoid glitches).
      // Therefore, this is the most important part to optimize for speed.
      else if (intent == SS_PLAYING_FULL_VOLUME) {
      mainLoop:

        // if playing backwards and we're past the beginning of the soundFile in
        // less than 16 steps, move one by one
        if ((speed < 0) && ((pos + speed * 16) < 0)) {
          while (l--) {
            *out++ = in[(UInt)pos];
            pos += speed;
            // check if we're past the beginning and readjust position if so
            if (pos < 0) goto calibrate;
          }
          goto nextBuffer; // this output channel buffer is full if we get here
        }

        // if playing forward and we're past the end of the soundFile in 
        // less than 16 steps, move one by one
        else if ((speed > 0) && ((pos + speed * 16) >= (_streaming ? STREAM_BUFFERSIZE : _length))) {
          while (l--) {
            *out++ = in[(UInt)pos];
            pos += speed;
            // check if we're past the end and readjust position if so
            if (pos >= (_streaming ? STREAM_BUFFERSIZE : _length)) goto calibrate;
          }
          goto nextBuffer; // this output channel buffer is full if we get here
        }

        // at this point, we can be sure our current position allows for 
        // 16 more frames without recalibration
        else {
          // if the output still needs more than 16 frames, add 16 at once
          if (l > 15) {
            out[0] = in[(UInt)pos]; pos += speed;
            out[1] = in[(UInt)pos]; pos += speed;
            out[2] = in[(UInt)pos]; pos += speed;
            out[3] = in[(UInt)pos]; pos += speed;
            out[4] = in[(UInt)pos]; pos += speed;
            out[5] = in[(UInt)pos]; pos += speed;
            out[6] = in[(UInt)pos]; pos += speed;
            out[7] = in[(UInt)pos]; pos += speed;
            out[8] = in[(UInt)pos]; pos += speed;
            out[9] = in[(UInt)pos]; pos += speed;
            out[10] = in[(UInt)pos]; pos += speed;
            out[11] = in[(UInt)pos]; pos += speed;
            out[12] = in[(UInt)pos]; pos += speed;
            out[13] = in[(UInt)pos]; pos += speed;
            out[14] = in[(UInt)pos]; pos += speed;
            out[15] = in[(UInt)pos]; pos += speed;
            l -= 16;
            out += 16;
            // check if we're past the end and readjust position if so
            if (pos < 0 || pos >= (_streaming ? STREAM_BUFFERSIZE : _length)) goto calibrate;
            // since l > 15, the output buffer is not full yet
            goto mainLoop;
          }
          // almost at the end of the output buffer, handle one
          // frame at a time now
          else {
            while (l-- > 0) {
              *out++ = in[(UInt)pos];
              pos += speed;
              // check if we're past the end and readjust position if so
              if (pos < 0 || pos >= (_streaming ? STREAM_BUFFERSIZE : _length)) goto calibrate;
            }
          }
          goto nextBuffer; // this output channel buffer is full if we get here
        }
      }

      // Sound plays, but not at full volume. So we're fading in
      else if (intent == SS_PLAYING) {
        while (l--) {
          *out++ = in[(UInt)pos] * volume;
          pos += speed;
          volume += 0.005f;
          if (pos < 0 || pos >= (_streaming ? STREAM_BUFFERSIZE : _length)) goto calibrate;
          if ((volume >= 1.f)) {
            // full volume is reached. Move to the optimized version
            volume = 1.f;
            intent = SS_PLAYING_FULL_VOLUME;
            goto startAgain;
          }
        }
        goto nextBuffer; // this output channel buffer is full if we get here
      }

      // Still playing, but fading out to pause
      else if (intent == SS_WANTSTOPAUSE) {
        while (l--) {
          *out++ = in[(UInt)pos] * volume;
          pos += speed;
          volume -= 0.005f;
          if (pos < 0 || pos >= (_streaming ? STREAM_BUFFERSIZE : _length)) goto calibrate;
          if ((volume <= 0.f)) {
            // fade out complete, switch intent to paused and restart
            // The rest of the buffer will be filled with zeroes
            volume = 0.f;
            intent = SS_PAUSED;
            goto startAgain;
          }
        }
        goto nextBuffer; // this output channel buffer is full if we get here
      }

      // Still playing, but fading out to stop
      else if (intent == SS_WANTSTOSTOP) {
        while (l--) {
          *out++ = in[(UInt)pos] * volume;
          pos += speed;
          volume -= 0.005f;
          if (pos < 0 || pos >= (_streaming ? STREAM_BUFFERSIZE : _length)) goto calibrate;
          if ((volume <= 0.f)) {
            // fade out complete, switch intent to stopped and restart
            // The rest of the buffer will be filled with zeroes
            volume = 0.f;
            intent = SS_STOPPED;
            goto startAgain;
          }
        }
        goto nextBuffer; // this output channel buffer is full if we get here
      }

calibrate:
      if (_streaming) {
        while (pos >= STREAM_BUFFERSIZE) {
          if(fillStream(loop)) {
            pos -= STREAM_BUFFERSIZE;
            realPos += STREAM_BUFFERSIZE;
            if (realPos >= streamReader->lengthInSamples) {
              realPos -= streamReader->lengthInSamples;
            }
          }
          else {
            pos = 0;
            intent = SS_STOPPED;
            volume = 0.f;
            _streamPos = 0;
            continue;
          }
        }
      }
      else 
      {
        // if we get here, pos is past the end or before the beginning
        // recalibrate position now. We can't simply set it to the end
        // or the beginning because this won't work with speed <> 1
        while (pos < 0) pos += _length; // looping backwards
        if (pos >= _length) {
          if (loop) while (pos >= _length) pos -= _length;
          else {
            // if no loop, fill the rest of the buffer with zero's
            pos = 0;
            intent = SS_STOPPED;
          }
        }
      }
    }

    // This label creates a safe point to start processing the next channel when the 
    // current one is done
    nextBuffer: ;
  }

  if (_streaming) pos += realPos;
  // make sure position is reset to zero if playing has stopped during this read
  if (intent == SS_STOPPED) {
    pos = 0;
    if (_streaming) {
      _streamPos = 0;
      if (fillStream(loop)) {
        pos -= STREAM_BUFFERSIZE;
        realPos += STREAM_BUFFERSIZE;
        if (realPos >= streamReader->lengthInSamples) {
          realPos -= streamReader->lengthInSamples;
        }
      }
      pos += realPos;
    }
  }
  return true;
}