Skip to content

Commit

Permalink
refactor(DmPerformance): properly implement transition composition
Browse files Browse the repository at this point in the history
  • Loading branch information
lmichaelis committed Apr 30, 2024
1 parent ba06097 commit 8554696
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 39 deletions.
23 changes: 19 additions & 4 deletions src/Common.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ enum {
DmInt_TICKS_PER_QUARTER_NOTE = 768,
};


size_t max_usize(size_t a, size_t b) {
return a >= b ? a : b;
}
Expand All @@ -27,6 +26,22 @@ float clamp_f32(float val, float min, float max) {
return val;
}

DmCommandType Dm_embellishmentToCommand(DmEmbellishmentType embellishment) {
switch (embellishment) {
case DmEmbellishment_NONE:
case DmEmbellishment_GROOVE:
return DmCommand_GROOVE;
case DmEmbellishment_FILL:
return DmCommand_FILL;
case DmEmbellishment_INTRO:
return DmCommand_INTRO;
case DmEmbellishment_BREAK:
return DmCommand_BREAK;
case DmEmbellishment_END:
return DmCommand_END;
}
}

bool DmGuid_equals(DmGuid const* a, DmGuid const* b) {
return memcmp(a->data, b->data, sizeof a->data) == 0;
}
Expand All @@ -51,10 +66,10 @@ uint32_t Dm_getMeasureLength(DmTimeSignature sig) {
}

double Dm_getTicksPerSample(DmTimeSignature time_signature, double beats_per_minute, uint32_t sample_rate) {
uint32_t pulses_per_beat = Dm_getBeatLength(time_signature); // unit: music-time per beat
double beats_per_second = beats_per_minute / 60; // unit: 1 per second
uint32_t pulses_per_beat = Dm_getBeatLength(time_signature); // unit: music-time per beat
double beats_per_second = beats_per_minute / 60; // unit: 1 per second
double pulses_per_second = pulses_per_beat * beats_per_second; // unit: music-time per second
double pulses_per_sample = pulses_per_second / sample_rate; // unit: music-time per sample
double pulses_per_sample = pulses_per_second / sample_rate; // unit: music-time per sample
return pulses_per_sample;
}

Expand Down
152 changes: 117 additions & 35 deletions src/Performance.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ void DmPerformance_release(DmPerformance* slf) {
DmMessageQueue_free(&slf->music_queue);
DmSegment_release(slf->segment);
DmStyle_release(slf->style);
DmBand_release(slf->band);
DmSynth_free(&slf->synth);
Dm_free(slf);
}
Expand Down Expand Up @@ -122,7 +123,7 @@ DmResult DmPerformance_playSegment(DmPerformance* slf, DmSegment* sgt, DmTiming
DmMessage msg;
msg.time = 0;
msg.type = DmMessage_SEGMENT;
msg.segment.segment = sgt;
msg.segment.segment = DmSegment_retain(sgt);
msg.segment.loop = 0;

if (mtx_lock(&slf->mod_lock) != thrd_success) {
Expand Down Expand Up @@ -445,9 +446,9 @@ static uint32_t DmPerformance_convertIoTimeRange(uint8_t range) {
}

static void DmPerformance_playPattern(DmPerformance* slf, DmPattern* pttn) {
DmSynth_reset(&slf->synth);
DmMessageQueue_clear(&slf->music_queue);
DmSynth_sendNoteOffEverything(&slf->synth);
DmSynth_reset(&slf->synth);

int variation_lock[256];
for (size_t i = 0; i < 256; ++i) {
Expand Down Expand Up @@ -711,10 +712,12 @@ static void DmPerformance_handleMessage(DmPerformance* slf, DmMessage* msg) {
if (sgt == NULL) {
DmSegment_release(slf->segment);
DmStyle_release(slf->style);
DmBand_release(slf->band);

slf->time = 0;
slf->style = NULL;
slf->segment = NULL;
slf->band = NULL;
break;
}

Expand Down Expand Up @@ -773,6 +776,8 @@ static void DmPerformance_handleMessage(DmPerformance* slf, DmMessage* msg) {
break;
case DmMessage_BAND:
// TODO(lmichaelis): The band in this message might have already been de-allocated!
DmBand_release(slf->band);
slf->band = DmBand_retain(msg->band.band);
DmSynth_sendBandUpdate(&slf->synth, msg->band.band);
Dm_report(DmLogLevel_DEBUG,
"DmPerformance: MESSAGE time=%d msg=band-change band=\"%s\"",
Expand Down Expand Up @@ -949,9 +954,108 @@ DmResult DmPerformance_renderPcm(DmPerformance* slf, void* buf, size_t len, DmRe
return DmResult_SUCCESS;
}

DmResult DmPerformance_playTransition(DmPerformance* slf,
DmSegment* sgt,
DmEmbellishmentType embellishment, DmTiming timing) {
static DmResult DmPerformance_composeTransition(DmPerformance* slf,
DmSegment* to,
DmEmbellishmentType embellishment,
DmSegment** out) {
DmResult rv = DmSegment_create(out);
if (rv != DmResult_SUCCESS) {
return rv;
}

DmSegment* trans = *out;
trans->repeats = 1;
trans->length = 0;
trans->play_start = 0;
trans->loop_start = 0;
trans->loop_end = trans->length;
trans->downloaded = true;
trans->info.unam = "Composed Transition";

// NOTE: We only support transitions of length 1 (measure)

DmMessage msg;
msg.time = 0;

if (embellishment != DmEmbellishment_NONE) {
msg.type = DmMessage_TEMPO;
msg.tempo.tempo = slf->tempo;
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

msg.type = DmMessage_BAND;
msg.band.band = DmBand_retain(slf->band);
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

msg.type = DmMessage_STYLE;
msg.style.style = DmStyle_retain(slf->style);
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

msg.type = DmMessage_CHORD;
msg.chord = slf->chord;
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

if (embellishment == DmEmbellishment_END_AND_INTRO) {
// Complex "extro" plus "intro" transitoon
msg.type = DmMessage_COMMAND;
msg.command.command = DmCommand_END;
msg.command.groove_level = slf->groove;
msg.command.groove_range = slf->groove_range;
msg.command.repeat_mode = DmPatternSelect_NO_REPEAT;
msg.command.beat = 0;
msg.command.measure = 0;
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

trans->length = Dm_getMeasureLength(slf->time_signature);

// TODO(lmichaelis): implement "end-and-intro" transitions
Dm_report(DmLogLevel_WARN, "DmPerformance: Complex END_AND_INTRO transition is not yet supported. Only playing END");
} else {
// Basic "extro"-style transition
msg.type = DmMessage_COMMAND;
msg.command.command = Dm_embellishmentToCommand(embellishment);
msg.command.groove_level = slf->groove;
msg.command.groove_range = slf->groove_range;
msg.command.repeat_mode = DmPatternSelect_NO_REPEAT;
msg.command.beat = 0;
msg.command.measure = 0;
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

trans->length = Dm_getMeasureLength(slf->time_signature);
}
}

msg.type = DmMessage_SEGMENT;
msg.time = trans->length;
msg.segment.segment = DmSegment_retain(to);
msg.segment.loop = 0;
rv = DmMessageList_add(&trans->messages, msg);
if (rv != DmResult_SUCCESS) {
return rv;
}

return DmResult_SUCCESS;
}

DmResult
DmPerformance_playTransition(DmPerformance* slf, DmSegment* sgt, DmEmbellishmentType embellishment, DmTiming timing) {
if (slf == NULL || sgt == NULL) {
return DmResult_INVALID_ARGUMENT;
}
Expand All @@ -966,40 +1070,18 @@ DmResult DmPerformance_playTransition(DmPerformance* slf,
return DmResult_INVALID_ARGUMENT;
}

if (mtx_lock(&slf->mod_lock) != thrd_success) {
return DmResult_INTERNAL_ERROR;
DmSegment* transition = NULL;
DmResult rv = DmPerformance_composeTransition(slf, sgt, embellishment, &transition);
if (rv != DmResult_SUCCESS) {
return rv;
}

DmMessageQueue_clear(&slf->control_queue);

// TODO: This is wrong!
uint32_t start = DmPerformance_getStartTime(slf, timing);
if (embellishment != DmEmbellishment_NONE) {
// TODO: This won't work if there are multiple possible pattern
DmPattern* pattern = DmPerformance_choosePattern(slf, (DmCommandType) embellishment);

if (pattern != NULL) {
DmMessage msg;
msg.type = DmMessage_COMMAND;
msg.command.command = (DmCommandType) embellishment;
msg.command.groove_level = pattern->groove_bottom;
msg.command.groove_range = 0;
msg.command.measure = 0;
msg.command.beat = 0;
msg.command.repeat_mode = DmPatternSelect_RANDOM;

DmMessageQueue_add(&slf->control_queue, &msg, start, DmQueueConflict_REPLACE);

start += Dm_getMeasureLength(pattern->time_signature) * pattern->length_measures;
}
rv = DmPerformance_playSegment(slf, transition, timing);
if (rv != DmResult_SUCCESS) {
return rv;
}

DmMessage msg;
msg.type = DmMessage_SEGMENT;
msg.segment.segment = sgt;
DmMessageQueue_add(&slf->control_queue, &msg, start, DmQueueConflict_REPLACE);

(void) mtx_unlock(&slf->mod_lock);
DmSegment_release(transition);
return DmResult_SUCCESS;
}

Expand Down
2 changes: 2 additions & 0 deletions src/_Internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ struct DmPerformance {
DmSegment* segment;
uint32_t segment_start;
DmStyle* style;
DmBand* band;
DmSynth synth;

uint32_t variation;
Expand All @@ -458,6 +459,7 @@ DMINT void Dm_report(DmLogLevel lvl, char const* fmt, ...);
DMINT size_t max_usize(size_t a, size_t b);
DMINT int32_t max_s32(int32_t a, int32_t b);
DMINT float clamp_f32(float val, float min, float max);
DMINT DmCommandType Dm_embellishmentToCommand(DmEmbellishmentType embellishment);
DMINT bool DmGuid_equals(DmGuid const* a, DmGuid const* b);
DMINT void DmTimeSignature_parse(DmTimeSignature* slf, DmRiff* rif);

Expand Down

0 comments on commit 8554696

Please sign in to comment.