From c89b5b096fb477b1cb091b2a166af1505d2ef5cd Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Mon, 5 May 2025 21:02:52 +1000 Subject: [PATCH 1/7] Popping fix plz --- cute_sound.h | 52 +++++++++++++++++++++++++--------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/cute_sound.h b/cute_sound.h index 892fff18..743fb39b 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -2328,16 +2328,16 @@ void cs_mix() int i3 = cs_mm_extract_epi32(index_int, 0); cs__m128 loA = cs_mm_set_ps( - i0 > audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], - i1 > audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], - i2 > audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], - i3 > audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] + i0 >= audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], + i1 >= audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], + i2 >= audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], + i3 >= audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] ); cs__m128 hiA = cs_mm_set_ps( - i0 + 1 > audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], - i1 + 1 > audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], - i2 + 1 > audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], - i3 + 1 > audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] + i0 + 1 >= audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], + i1 + 1 >= audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], + i2 + 1 >= audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], + i3 + 1 >= audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] ); cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); @@ -2361,29 +2361,29 @@ void cs_mix() int i3 = cs_mm_extract_epi32(index_int, 0); cs__m128 loA = cs_mm_set_ps( - i0 > audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], - i1 > audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], - i2 > audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], - i3 > audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] + i0 >= audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], + i1 >= audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], + i2 >= audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], + i3 >= audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] ); cs__m128 hiA = cs_mm_set_ps( - i0 + 1 > audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], - i1 + 1 > audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], - i2 + 1 > audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], - i3 + 1 > audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] + i0 + 1 >= audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], + i1 + 1 >= audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], + i2 + 1 >= audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], + i3 + 1 >= audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] ); cs__m128 loB = cs_mm_set_ps( - i0 > audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cB)[i0], - i1 > audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cB)[i1], - i2 > audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cB)[i2], - i3 > audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cB)[i3] + i0 >= audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cB)[i0], + i1 >= audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cB)[i1], + i2 >= audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cB)[i2], + i3 >= audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cB)[i3] ); cs__m128 hiB = cs_mm_set_ps( - i0 + 1 > audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cB)[i0 + 1], - i1 + 1 > audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cB)[i1 + 1], - i2 + 1 > audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cB)[i2 + 1], - i3 + 1 > audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cB)[i3 + 1] + i0 + 1 >= audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cB)[i0 + 1], + i1 + 1 >= audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cB)[i1 + 1], + i2 + 1 >= audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cB)[i2 + 1], + i3 + 1 >= audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cB)[i3 + 1] ); cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); @@ -2689,9 +2689,7 @@ cs_audio_source_t* cs_read_mem_wav(const void* memory, size_t size, cs_error_t* { int sample_size = *((uint32_t*)(data + 4)); int sample_count = sample_size / (fmt.nChannels * sizeof(uint16_t)); - //to account for interpolation in the pitch shifter, we lie about length - //this fixes random popping at the end of sounds - audio->sample_count = sample_count-1; + audio->sample_count = sample_count; audio->channel_count = fmt.nChannels; int wide_count = (int)CUTE_SOUND_ALIGN(sample_count, 4) / 4; From 8438cc841c8d4ee06b2fe1a2aa0aa6b63c8f82e4 Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Wed, 7 May 2025 16:55:20 +1000 Subject: [PATCH 2/7] Github please overwrite cute sound --- cute_sound.h | 138 +++++++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 71 deletions(-) diff --git a/cute_sound.h b/cute_sound.h index 743fb39b..baecfb3d 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -117,7 +117,7 @@ interface needs and use-cases fluffrabbit 1.11 - scalar SIMD mode and various compiler warning/error fixes Daniel Guzman 2.01 - compilation fixes for clang/llvm on MAC. - Brie 2.06 - Looping sound rollover + Brie 2.06 - Looping sound rollover, seamless resampling ogam x.xx - Lots of bugfixes over time, including support negative pitch renex x.xx - Fixes to popping issues and a crash in the mixer. @@ -2292,15 +2292,17 @@ void cs_mix() cs__m128 vA = cs_mm_set1_ps(vA0); cs__m128 vB = cs_mm_set1_ps(vB0); - int prev_playing_sample_index = playing->sample_index; - int samples_to_read = (int)(samples_needed * playing->pitch); + int samples_to_read = (int)((samples_needed - write_offset) * playing->pitch); if (samples_to_read + playing->sample_index > audio->sample_count) { samples_to_read = audio->sample_count - playing->sample_index; } else if (samples_to_read + playing->sample_index < 0) { - // When pitch shifting is negative, samples_to_read is also negative so that offset needs to - // be accounted for otherwise the sample index cursor gets stuck at sample count. - playing->sample_index = audio->sample_count + samples_to_read + playing->sample_index; + samples_to_read = -playing->sample_index; + // Wrap the index cursor so that we don't get stuck on zero + // This will put us 1 step over the end of the array, thankfully our + // resampler already handles overflows like this! + playing->sample_index = ((playing->sample_index + audio->sample_count - 1) % audio->sample_count) + 1; } + int sample_index_wide = (int)CUTE_SOUND_TRUNC(playing->sample_index, 4) / 4; int samples_to_write = (int)(samples_to_read / playing->pitch); int write_wide = CUTE_SOUND_ALIGN(samples_to_write, 4) / 4; @@ -2310,6 +2312,12 @@ void cs_mix() // Do the actual mixing: Apply volume, load samples into float buffers. if (playing->pitch != 1.0f) { + // To avoid bloating the code I've macro-ed the channel sampling + // In SAMPLE_CLAMP I'm casting the index to unsigned to underflow it, to avoid doing multiple bounds checks + // Although it seems like negative indices don't happen in the first place + #define SAMPLE_MOD(channel, index) ((float*)channel)[(index) % audio->sample_count] + #define SAMPLE_CLAMP(channel, index) (unsigned int)(index) >= audio->sample_count ? 0.f : ((float*)cA)[index] + // Pitch shifting -- We read in samples at a resampled rate (multiply by pitch). These samples // are read in one at a time in scalar mode, but then mixed together via SIMD. cs__m128 pitch = cs_mm_set1_ps(playing->pitch); @@ -2327,18 +2335,13 @@ void cs_mix() int i2 = cs_mm_extract_epi32(index_int, 1); int i3 = cs_mm_extract_epi32(index_int, 0); - cs__m128 loA = cs_mm_set_ps( - i0 >= audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], - i1 >= audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], - i2 >= audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], - i3 >= audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] - ); - cs__m128 hiA = cs_mm_set_ps( - i0 + 1 >= audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], - i1 + 1 >= audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], - i2 + 1 >= audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], - i3 + 1 >= audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] - ); + cs__m128 loA = playing->looped ? + cs_mm_set_ps(SAMPLE_MOD(cA, i0), SAMPLE_MOD(cA, i1), SAMPLE_MOD(cA, i2), SAMPLE_MOD(cA, i3)) : + cs_mm_set_ps(SAMPLE_CLAMP(cA, i0), SAMPLE_CLAMP(cA, i1), SAMPLE_CLAMP(cA, i2), SAMPLE_CLAMP(cA, i3)); + + cs__m128 hiA = playing->looped ? + cs_mm_set_ps(SAMPLE_MOD(cA, i0 + 1), SAMPLE_MOD(cA, i1 + 1), SAMPLE_MOD(cA, i2 + 1), SAMPLE_MOD(cA, i3 + 1)) : + cs_mm_set_ps(SAMPLE_CLAMP(cA, i0 + 1), SAMPLE_CLAMP(cA, i1 + 1), SAMPLE_CLAMP(cA, i2 + 1), SAMPLE_CLAMP(cA, i3 + 1)); cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); cs__m128 B = cs_mm_mul_ps(A, vB); @@ -2360,31 +2363,21 @@ void cs_mix() int i2 = cs_mm_extract_epi32(index_int, 1); int i3 = cs_mm_extract_epi32(index_int, 0); - cs__m128 loA = cs_mm_set_ps( - i0 >= audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cA)[i0], - i1 >= audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cA)[i1], - i2 >= audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cA)[i2], - i3 >= audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cA)[i3] - ); - cs__m128 hiA = cs_mm_set_ps( - i0 + 1 >= audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cA)[i0 + 1], - i1 + 1 >= audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cA)[i1 + 1], - i2 + 1 >= audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cA)[i2 + 1], - i3 + 1 >= audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cA)[i3 + 1] - ); - - cs__m128 loB = cs_mm_set_ps( - i0 >= audio->sample_count ? 0 : i0 < 0 ? audio->sample_count : ((float*)cB)[i0], - i1 >= audio->sample_count ? 0 : i1 < 0 ? audio->sample_count : ((float*)cB)[i1], - i2 >= audio->sample_count ? 0 : i2 < 0 ? audio->sample_count : ((float*)cB)[i2], - i3 >= audio->sample_count ? 0 : i3 < 0 ? audio->sample_count : ((float*)cB)[i3] - ); - cs__m128 hiB = cs_mm_set_ps( - i0 + 1 >= audio->sample_count ? 0 : i0 + 1 < 0 ? audio->sample_count : ((float*)cB)[i0 + 1], - i1 + 1 >= audio->sample_count ? 0 : i1 + 1 < 0 ? audio->sample_count : ((float*)cB)[i1 + 1], - i2 + 1 >= audio->sample_count ? 0 : i2 + 1 < 0 ? audio->sample_count : ((float*)cB)[i2 + 1], - i3 + 1 >= audio->sample_count ? 0 : i3 + 1 < 0 ? audio->sample_count : ((float*)cB)[i3 + 1] - ); + cs__m128 loA = playing->looped ? + cs_mm_set_ps(SAMPLE_MOD(cA, i0), SAMPLE_MOD(cA, i1), SAMPLE_MOD(cA, i2), SAMPLE_MOD(cA, i3)) : + cs_mm_set_ps(SAMPLE_CLAMP(cA, i0), SAMPLE_CLAMP(cA, i1), SAMPLE_CLAMP(cA, i2), SAMPLE_CLAMP(cA, i3)); + + cs__m128 hiA = playing->looped ? + cs_mm_set_ps(SAMPLE_MOD(cA, i0 + 1), SAMPLE_MOD(cA, i1 + 1), SAMPLE_MOD(cA, i2 + 1), SAMPLE_MOD(cA, i3 + 1)) : + cs_mm_set_ps(SAMPLE_CLAMP(cA, i0 + 1), SAMPLE_CLAMP(cA, i1 + 1), SAMPLE_CLAMP(cA, i2 + 1), SAMPLE_CLAMP(cA, i3 + 1)); + + cs__m128 loB = playing->looped ? + cs_mm_set_ps(SAMPLE_MOD(cB, i0), SAMPLE_MOD(cB, i1), SAMPLE_MOD(cB, i2), SAMPLE_MOD(cB, i3)) : + cs_mm_set_ps(SAMPLE_CLAMP(cB, i0), SAMPLE_CLAMP(cB, i1), SAMPLE_CLAMP(cB, i2), SAMPLE_CLAMP(cB, i3)); + + cs__m128 hiB = playing->looped ? + cs_mm_set_ps(SAMPLE_MOD(cB, i0 + 1), SAMPLE_MOD(cB, i1 + 1), SAMPLE_MOD(cB, i2 + 1), SAMPLE_MOD(cB, i3 + 1)) : + cs_mm_set_ps(SAMPLE_CLAMP(cB, i0 + 1), SAMPLE_CLAMP(cB, i1 + 1), SAMPLE_CLAMP(cB, i2 + 1), SAMPLE_CLAMP(cB, i3 + 1)); cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); cs__m128 B = cs_mm_add_ps(loB, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiB, loB))); @@ -2395,6 +2388,9 @@ void cs_mix() floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); } } break; + + #undef SAMPLE_MOD + #undef SAMPLE_CLAMP } } else { // No pitch shifting, just add samples together. @@ -2430,25 +2426,28 @@ void cs_mix() CUTE_SOUND_ASSERT(playing->sample_index <= audio->sample_count); if (playing->pitch < 0) { // When pitch shifting is negative adjust the timing a bit further back from sample count to avoid any clipping. - if (prev_playing_sample_index - playing->sample_index < 0) { + if (playing->sample_index == 0) { if (playing->looped) { - playing->sample_index = audio->sample_count - samples_needed; + // Due to SAMPLE_MOD, we may have already played some of + // the first samples, so when we loop, we skip those + playing->sample_index = audio->sample_count - (4 - (audio->sample_count % 4)); + write_offset += samples_to_write; - samples_needed -= samples_to_write; - CUTE_SOUND_ASSERT(samples_needed >= 0); - if (samples_needed == 0) break; + if (write_offset >= samples_needed) break; goto mix_more; } goto remove; } - } else if (playing->sample_index == audio->sample_count) { + } + else if (playing->sample_index == audio->sample_count) { if (playing->looped) { - playing->sample_index = 0; + // Due to SAMPLE_MOD, we may have already played some of + // the first samples, so when we loop, we skip those + playing->sample_index = (4 - (audio->sample_count % 4)); + write_offset += samples_to_write; - samples_needed -= samples_to_write; - CUTE_SOUND_ASSERT(samples_needed >= 0); - if (samples_needed == 0) break; + if (write_offset >= samples_needed) break; goto mix_more; } @@ -2476,15 +2475,12 @@ void cs_mix() cs_list_remove(playing_node); cs_list_push_front(&s_ctx->free_sounds, playing_node); - cs_hashtableremove(&s_ctx->instance_map, playing->id); + + s_ctx->ctx_insts[playing->id & 1023] = NULL; + s_ctx->ctx_insts_used[playing->id & 1023] = 0; + playing_node = next_node; write_offset = 0; - if (s_ctx->on_finish && !playing->is_music) { - cs_playing_sound_t snd = { playing->id }; - s_ctx->on_finish(snd, s_ctx->on_finish_udata); - } else if (s_ctx->on_music_finish && playing->is_music) { - s_ctx->on_music_finish(s_ctx->on_music_finish_udata); - } continue; } while (playing_node != end_node); } @@ -2603,23 +2599,23 @@ static char* cs_next(char* data) return data + 8 + size; } -static void cs_last_element(cs__m128* a, int i, int j, int16_t* samples, int offset) +static void cs_last_element(cs__m128* a, int i, int j, int m, int16_t* samples, int offset) { switch (offset) { case 1: - a[i] = cs_mm_set_ps(samples[j], 0.0f, 0.0f, 0.0f); + a[i] = cs_mm_set_ps(0.0f, 0.0f, 0.0f, samples[j]); break; case 2: - a[i] = cs_mm_set_ps(samples[j], samples[j + 1], 0.0f, 0.0f); + a[i] = cs_mm_set_ps(0.f, 0.f, samples[j + m], samples[j]); break; case 3: - a[i] = cs_mm_set_ps(samples[j], samples[j + 1], samples[j + 2], 0.0f); + a[i] = cs_mm_set_ps(0.f, samples[j + m*2], samples[j + m], samples[j]); break; case 0: - a[i] = cs_mm_set_ps(samples[j], samples[j + 1], samples[j + 2], samples[j + 3]); + a[i] = cs_mm_set_ps(samples[j + m*3], samples[j + m*2], samples[j + m], samples[j]); break; } } @@ -2705,7 +2701,7 @@ cs_audio_source_t* cs_read_mem_wav(const void* memory, size_t size, cs_error_t* for (int i = 0, j = 0; i < wide_count - 1; ++i, j += 4) { a[i] = cs_mm_set_ps((float)samples[j+3], (float)samples[j+2], (float)samples[j+1], (float)samples[j]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); + cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, 1, samples, wide_offset); } break; case 2: @@ -2716,8 +2712,8 @@ cs_audio_source_t* cs_read_mem_wav(const void* memory, size_t size, cs_error_t* a[i] = cs_mm_set_ps((float)samples[j+6], (float)samples[j+4], (float)samples[j+2], (float)samples[j]); b[i] = cs_mm_set_ps((float)samples[j+7], (float)samples[j+5], (float)samples[j+3], (float)samples[j+1]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); - cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 4, samples, wide_offset); + cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, 2, samples, wide_offset); + cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 1, 2, samples, wide_offset); audio->channels[0] = a; audio->channels[1] = b; } break; @@ -2845,7 +2841,7 @@ cs_audio_source_t* cs_read_mem_ogg(const void* memory, size_t length, cs_error_t for (int i = 0, j = 0; i < wide_count - 1; ++i, j += 4) { a[i] = cs_mm_set_ps((float)samples[j+3], (float)samples[j+2], (float)samples[j+1], (float)samples[j]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); + cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, 1, samples, wide_offset); } break; case 2: @@ -2855,8 +2851,8 @@ cs_audio_source_t* cs_read_mem_ogg(const void* memory, size_t length, cs_error_t a[i] = cs_mm_set_ps((float)samples[j+6], (float)samples[j+4], (float)samples[j+2], (float)samples[j]); b[i] = cs_mm_set_ps((float)samples[j+7], (float)samples[j+5], (float)samples[j+3], (float)samples[j+1]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, samples, wide_offset); - cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 4, samples, wide_offset); + cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, 2, samples, wide_offset); + cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 1, 2, samples, wide_offset); break; default: From 74fc8c0340beac41da7b9e2a75bbbbccac86b306 Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Wed, 7 May 2025 17:09:48 +1000 Subject: [PATCH 3/7] Undo errant change --- cute_sound.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/cute_sound.h b/cute_sound.h index baecfb3d..a1735e96 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -2475,9 +2475,7 @@ void cs_mix() cs_list_remove(playing_node); cs_list_push_front(&s_ctx->free_sounds, playing_node); - - s_ctx->ctx_insts[playing->id & 1023] = NULL; - s_ctx->ctx_insts_used[playing->id & 1023] = 0; + cs_hashtableremove(&s_ctx->instance_map, playing->id); playing_node = next_node; write_offset = 0; From 7c00449fac9a50900f949fe7bce0ae050eb06255 Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Wed, 7 May 2025 17:40:18 +1000 Subject: [PATCH 4/7] Reuploading :P --- cute_sound.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cute_sound.h b/cute_sound.h index a1735e96..cadc5e3d 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -2476,9 +2476,14 @@ void cs_mix() cs_list_remove(playing_node); cs_list_push_front(&s_ctx->free_sounds, playing_node); cs_hashtableremove(&s_ctx->instance_map, playing->id); - playing_node = next_node; write_offset = 0; + if (s_ctx->on_finish && !playing->is_music) { + cs_playing_sound_t snd = { playing->id }; + s_ctx->on_finish(snd, s_ctx->on_finish_udata); + } else if (s_ctx->on_music_finish && playing->is_music) { + s_ctx->on_music_finish(s_ctx->on_music_finish_udata); + } continue; } while (playing_node != end_node); } From 12970cc1aba59efe69675e7f6716ebcbe986999d Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Thu, 8 May 2025 18:58:12 +1000 Subject: [PATCH 5/7] change % to & for consistency --- cute_sound.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cute_sound.h b/cute_sound.h index cadc5e3d..9a2cbfcc 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -2430,7 +2430,7 @@ void cs_mix() if (playing->looped) { // Due to SAMPLE_MOD, we may have already played some of // the first samples, so when we loop, we skip those - playing->sample_index = audio->sample_count - (4 - (audio->sample_count % 4)); + playing->sample_index = audio->sample_count - (4 - (audio->sample_count & 3)); write_offset += samples_to_write; if (write_offset >= samples_needed) break; @@ -2444,7 +2444,7 @@ void cs_mix() if (playing->looped) { // Due to SAMPLE_MOD, we may have already played some of // the first samples, so when we loop, we skip those - playing->sample_index = (4 - (audio->sample_count % 4)); + playing->sample_index = (4 - (audio->sample_count & 3)); write_offset += samples_to_write; if (write_offset >= samples_needed) break; From 31d06bb598dd79b57b52e497d118ab53c844e756 Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Thu, 8 May 2025 22:33:07 +1000 Subject: [PATCH 6/7] Another last_element bug I missed I almost panicked when I started hearing clicks again. --- cute_sound.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cute_sound.h b/cute_sound.h index 9a2cbfcc..fd658574 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -2715,8 +2715,8 @@ cs_audio_source_t* cs_read_mem_wav(const void* memory, size_t size, cs_error_t* a[i] = cs_mm_set_ps((float)samples[j+6], (float)samples[j+4], (float)samples[j+2], (float)samples[j]); b[i] = cs_mm_set_ps((float)samples[j+7], (float)samples[j+5], (float)samples[j+3], (float)samples[j+1]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, 2, samples, wide_offset); - cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 1, 2, samples, wide_offset); + cs_last_element(a, wide_count - 1, (wide_count - 1) * 8, 2, samples, wide_offset); + cs_last_element(b, wide_count - 1, (wide_count - 1) * 8 + 1, 2, samples, wide_offset); audio->channels[0] = a; audio->channels[1] = b; } break; @@ -2854,8 +2854,8 @@ cs_audio_source_t* cs_read_mem_ogg(const void* memory, size_t length, cs_error_t a[i] = cs_mm_set_ps((float)samples[j+6], (float)samples[j+4], (float)samples[j+2], (float)samples[j]); b[i] = cs_mm_set_ps((float)samples[j+7], (float)samples[j+5], (float)samples[j+3], (float)samples[j+1]); } - cs_last_element(a, wide_count - 1, (wide_count - 1) * 4, 2, samples, wide_offset); - cs_last_element(b, wide_count - 1, (wide_count - 1) * 4 + 1, 2, samples, wide_offset); + cs_last_element(a, wide_count - 1, (wide_count - 1) * 8, 2, samples, wide_offset); + cs_last_element(b, wide_count - 1, (wide_count - 1) * 8 + 1, 2, samples, wide_offset); break; default: From c7fe60964fbffed988f79e436cfa25b37081cb8f Mon Sep 17 00:00:00 2001 From: userpc0000 <128202058+userpc0000@users.noreply.github.com> Date: Thu, 29 May 2025 21:13:29 +1000 Subject: [PATCH 7/7] correct sample index overflow At least I hope so --- cute_sound.h | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/cute_sound.h b/cute_sound.h index fd658574..d3eb3a4d 100644 --- a/cute_sound.h +++ b/cute_sound.h @@ -766,6 +766,7 @@ void cs_set_global_user_allocator_context(void* user_allocator_context); #endif // CUTE_SOUND_SCALAR_MODE +#define CUTE_SOUND_LOOP(X, Y) ((X) < 0 ? Y - (((X) + 1) / (Y) * (Y) - (X)) : (X) % (Y)) #define CUTE_SOUND_ALIGN(X, Y) ((((size_t)X) + ((Y) - 1)) & ~((Y) - 1)) #define CUTE_SOUND_TRUNC(X, Y) ((size_t)(X) & ~((Y) - 1)) @@ -2298,13 +2299,12 @@ void cs_mix() } else if (samples_to_read + playing->sample_index < 0) { samples_to_read = -playing->sample_index; // Wrap the index cursor so that we don't get stuck on zero - // This will put us 1 step over the end of the array, thankfully our - // resampler already handles overflows like this! - playing->sample_index = ((playing->sample_index + audio->sample_count - 1) % audio->sample_count) + 1; + if (playing->sample_index == 0) + playing->sample_index = audio->sample_count - 1; } int sample_index_wide = (int)CUTE_SOUND_TRUNC(playing->sample_index, 4) / 4; - int samples_to_write = (int)(samples_to_read / playing->pitch); + int samples_to_write = (int)ceilf(samples_to_read / playing->pitch); int write_wide = CUTE_SOUND_ALIGN(samples_to_write, 4) / 4; int write_offset_wide = (int)CUTE_SOUND_ALIGN(write_offset, 4) / 4; static int written_so_far = 0; @@ -2314,8 +2314,8 @@ void cs_mix() if (playing->pitch != 1.0f) { // To avoid bloating the code I've macro-ed the channel sampling // In SAMPLE_CLAMP I'm casting the index to unsigned to underflow it, to avoid doing multiple bounds checks - // Although it seems like negative indices don't happen in the first place - #define SAMPLE_MOD(channel, index) ((float*)channel)[(index) % audio->sample_count] + // Although it seems like negative indices don't happen on unlooped sounds in the first place + #define SAMPLE_LOOP(channel, index) ((float*)channel)[CUTE_SOUND_LOOP(index, audio->sample_count)] #define SAMPLE_CLAMP(channel, index) (unsigned int)(index) >= audio->sample_count ? 0.f : ((float*)cA)[index] // Pitch shifting -- We read in samples at a resampled rate (multiply by pitch). These samples @@ -2336,11 +2336,11 @@ void cs_mix() int i3 = cs_mm_extract_epi32(index_int, 0); cs__m128 loA = playing->looped ? - cs_mm_set_ps(SAMPLE_MOD(cA, i0), SAMPLE_MOD(cA, i1), SAMPLE_MOD(cA, i2), SAMPLE_MOD(cA, i3)) : + cs_mm_set_ps(SAMPLE_LOOP(cA, i0), SAMPLE_LOOP(cA, i1), SAMPLE_LOOP(cA, i2), SAMPLE_LOOP(cA, i3)) : cs_mm_set_ps(SAMPLE_CLAMP(cA, i0), SAMPLE_CLAMP(cA, i1), SAMPLE_CLAMP(cA, i2), SAMPLE_CLAMP(cA, i3)); cs__m128 hiA = playing->looped ? - cs_mm_set_ps(SAMPLE_MOD(cA, i0 + 1), SAMPLE_MOD(cA, i1 + 1), SAMPLE_MOD(cA, i2 + 1), SAMPLE_MOD(cA, i3 + 1)) : + cs_mm_set_ps(SAMPLE_LOOP(cA, i0 + 1), SAMPLE_LOOP(cA, i1 + 1), SAMPLE_LOOP(cA, i2 + 1), SAMPLE_LOOP(cA, i3 + 1)) : cs_mm_set_ps(SAMPLE_CLAMP(cA, i0 + 1), SAMPLE_CLAMP(cA, i1 + 1), SAMPLE_CLAMP(cA, i2 + 1), SAMPLE_CLAMP(cA, i3 + 1)); cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); @@ -2364,19 +2364,19 @@ void cs_mix() int i3 = cs_mm_extract_epi32(index_int, 0); cs__m128 loA = playing->looped ? - cs_mm_set_ps(SAMPLE_MOD(cA, i0), SAMPLE_MOD(cA, i1), SAMPLE_MOD(cA, i2), SAMPLE_MOD(cA, i3)) : + cs_mm_set_ps(SAMPLE_LOOP(cA, i0), SAMPLE_LOOP(cA, i1), SAMPLE_LOOP(cA, i2), SAMPLE_LOOP(cA, i3)) : cs_mm_set_ps(SAMPLE_CLAMP(cA, i0), SAMPLE_CLAMP(cA, i1), SAMPLE_CLAMP(cA, i2), SAMPLE_CLAMP(cA, i3)); cs__m128 hiA = playing->looped ? - cs_mm_set_ps(SAMPLE_MOD(cA, i0 + 1), SAMPLE_MOD(cA, i1 + 1), SAMPLE_MOD(cA, i2 + 1), SAMPLE_MOD(cA, i3 + 1)) : + cs_mm_set_ps(SAMPLE_LOOP(cA, i0 + 1), SAMPLE_LOOP(cA, i1 + 1), SAMPLE_LOOP(cA, i2 + 1), SAMPLE_LOOP(cA, i3 + 1)) : cs_mm_set_ps(SAMPLE_CLAMP(cA, i0 + 1), SAMPLE_CLAMP(cA, i1 + 1), SAMPLE_CLAMP(cA, i2 + 1), SAMPLE_CLAMP(cA, i3 + 1)); cs__m128 loB = playing->looped ? - cs_mm_set_ps(SAMPLE_MOD(cB, i0), SAMPLE_MOD(cB, i1), SAMPLE_MOD(cB, i2), SAMPLE_MOD(cB, i3)) : + cs_mm_set_ps(SAMPLE_LOOP(cB, i0), SAMPLE_LOOP(cB, i1), SAMPLE_LOOP(cB, i2), SAMPLE_LOOP(cB, i3)) : cs_mm_set_ps(SAMPLE_CLAMP(cB, i0), SAMPLE_CLAMP(cB, i1), SAMPLE_CLAMP(cB, i2), SAMPLE_CLAMP(cB, i3)); cs__m128 hiB = playing->looped ? - cs_mm_set_ps(SAMPLE_MOD(cB, i0 + 1), SAMPLE_MOD(cB, i1 + 1), SAMPLE_MOD(cB, i2 + 1), SAMPLE_MOD(cB, i3 + 1)) : + cs_mm_set_ps(SAMPLE_LOOP(cB, i0 + 1), SAMPLE_LOOP(cB, i1 + 1), SAMPLE_LOOP(cB, i2 + 1), SAMPLE_LOOP(cB, i3 + 1)) : cs_mm_set_ps(SAMPLE_CLAMP(cB, i0 + 1), SAMPLE_CLAMP(cB, i1 + 1), SAMPLE_CLAMP(cB, i2 + 1), SAMPLE_CLAMP(cB, i3 + 1)); cs__m128 A = cs_mm_add_ps(loA, cs_mm_mul_ps(index_frac, cs_mm_sub_ps(hiA, loA))); @@ -2388,10 +2388,10 @@ void cs_mix() floatB[i + write_offset_wide] = cs_mm_add_ps(floatB[i + write_offset_wide], B); } } break; + } - #undef SAMPLE_MOD + #undef SAMPLE_LOOP #undef SAMPLE_CLAMP - } } else { // No pitch shifting, just add samples together. switch (audio->channel_count) { @@ -2422,15 +2422,13 @@ void cs_mix() } // playing list logic + int next_sample = (int)((float)(write_wide * 4) * playing->pitch) + playing->sample_index; playing->sample_index += samples_to_read; CUTE_SOUND_ASSERT(playing->sample_index <= audio->sample_count); if (playing->pitch < 0) { - // When pitch shifting is negative adjust the timing a bit further back from sample count to avoid any clipping. if (playing->sample_index == 0) { if (playing->looped) { - // Due to SAMPLE_MOD, we may have already played some of - // the first samples, so when we loop, we skip those - playing->sample_index = audio->sample_count - (4 - (audio->sample_count & 3)); + playing->sample_index = CUTE_SOUND_LOOP(next_sample, audio->sample_count); write_offset += samples_to_write; if (write_offset >= samples_needed) break; @@ -2442,9 +2440,7 @@ void cs_mix() } else if (playing->sample_index == audio->sample_count) { if (playing->looped) { - // Due to SAMPLE_MOD, we may have already played some of - // the first samples, so when we loop, we skip those - playing->sample_index = (4 - (audio->sample_count & 3)); + playing->sample_index = CUTE_SOUND_LOOP(next_sample, audio->sample_count); write_offset += samples_to_write; if (write_offset >= samples_needed) break;