Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Setting Master Clock Rate Seems to Break Divider Callbacks #2

Closed
AuzFox opened this issue Dec 28, 2020 · 1 comment
Closed

Setting Master Clock Rate Seems to Break Divider Callbacks #2

AuzFox opened this issue Dec 28, 2020 · 1 comment

Comments

@AuzFox
Copy link

AuzFox commented Dec 28, 2020

Hello! I've been using this library to write my own chiptune tracker, and things have been going pretty well, but I've noticed an issue that occurs when using BKSetPtr() to set the tick rate of the master clock. My thought process was that I could set the tick rate of the master clock to a value calculated from the desired BPM of the song, and use divider callbacks to trigger notes/fx/etc after a given number of ticks. Simply using different divider values at the default clock rate would lead to rounding errors and inaccurate BPM rates, as mentioned in the documentation/examples.

The issue is that changing the period of the master clock seems to cause the divider callbacks to only be executed once and are not called again. I've written a simplified C++ program that illustrates this here:

#include <BlipKit/BlipKit.h>
#include <SDL2/SDL.h>

// BlipKit stuff:
BKCallback bkCallback;
BKContext* context;
BKDivider* divider;
BKTrack* track;

// SDL stuff:
SDL_AudioSpec want, have;
SDL_Window* window;
SDL_AudioDeviceID device;

// program/"song" stuff:
#define NOTEDATA_LEN 16
#define EMPTY_NOTE -3
bool running = true;
int noteDataIndex = 0;
BKInt noteData[NOTEDATA_LEN] = {
  BK_G_3,
  EMPTY_NOTE,
  EMPTY_NOTE,
  EMPTY_NOTE,
  BK_E_3,
  EMPTY_NOTE,
  EMPTY_NOTE,
  EMPTY_NOTE,
  BK_D_3,
  EMPTY_NOTE,
  EMPTY_NOTE,
  EMPTY_NOTE,
  BK_C_3,
  EMPTY_NOTE,
  EMPTY_NOTE,
  EMPTY_NOTE
};

// not called by default:
void setClockRate(double ticksPerSecond) {
  BKTime time = BKTimeFromSeconds(context, 1.0 / ticksPerSecond);
  BKSetPtr(context, BK_CLOCK_PERIOD, &time, sizeof(time));
}

void sdlAudioCallback(void* userdata, Uint8* stream, int len) {
  BKUInt numFrames = len / sizeof(BKFrame) / have.channels;
  BKContextGenerate(context, (BKFrame*) stream, numFrames);
}

BKEnum dividerCallback(BKCallbackInfo* info, void* userdata) {
  BKInt note = noteData[noteDataIndex];

  if (note != EMPTY_NOTE) {
    if (note >= 0) {
      note *= BK_FINT20_UNIT;
    }
    BKSetAttr(track, BK_NOTE, note);
  }

  noteDataIndex++;
  if (noteDataIndex >= NOTEDATA_LEN) {
    noteDataIndex = 0;
  }

  return 0;
}

int main(int argc, char** argv) {
  // init SDL stuff:
  SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO);

  window = SDL_CreateWindow(
    "BlipKit Divider Test",
    SDL_WINDOWPOS_CENTERED,
    SDL_WINDOWPOS_CENTERED,
    400,
    400,
    0
  );

  want.freq = 44100;
  want.format = AUDIO_S16SYS;
  want.channels = 2;
  want.samples = 1024;
  want.callback = sdlAudioCallback;
  want.userdata = nullptr;
  device = SDL_OpenAudioDevice(
    nullptr,
    0,
    &want,
    &have,
    SDL_AUDIO_ALLOW_FREQUENCY_CHANGE | SDL_AUDIO_ALLOW_CHANNELS_CHANGE
  );

  // init BlipKit stuff:
  BKContextAlloc(&context, have.channels, have.freq);

  BKTrackAlloc(&track, BK_SQUARE);
  BKSetAttr(track, BK_MASTER_VOLUME, (1.0 / 6) * BK_MAX_VOLUME);
  BKSetAttr(track, BK_VOLUME, 0.50 * BK_MAX_VOLUME);

  BKTrackAttach(track, context);

  bkCallback.func = dividerCallback;
  bkCallback.userInfo = nullptr;
  BKDividerAlloc(&divider, 6, &bkCallback);

  BKContextAttachDivider(context, divider, BK_CLOCK_TYPE_BEAT);

  SDL_PauseAudioDevice(device, 0);

  // main loop:
  SDL_Event e;
  while (running) {
    while (SDL_PollEvent(&e)) {
      switch (e.type) {
        case SDL_QUIT:
          running = false;
          break;
        default:
          break;
      }
    }
    SDL_Delay(2);
  }

  SDL_CloseAudioDevice(device);
  SDL_DestroyWindow(window);
  SDL_Quit();
  BKDispose(track);
  BKDispose(divider);
  BKDispose(context);

  return 0;
}

When I compile and run the above code as is, everything works as intended and a series of 4 notes are played repeatedly. But when a call to setClockRate() is made any time after initialization, only the first note is played and held indefinitely.

I've even tried calling setClockRate(240.0) to set the rate to the default value, and the same problem still occurred.

Apologies if this is just a case of user error, and I understand this project hasn't seen much activity lately, but any help would be appreciated. :)

@detomon
Copy link
Owner

detomon commented Dec 29, 2020

Hello @AuzFox

You did everything correct.
The issue was, that the clock uses two different version on 32-bit and 64-bit systems. Compiling the library via make did work correctly. But when the header was included directly (#include <BlipKit/BlipKit.h>) it always recognised the system as 32-bit, which interprets the BKTime struct differently.
The latest commit fixes this issue.

Great to see that someone uses my library :)
Feel free to ask any question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants