diff --git a/include/support.h b/include/support.h index cfa8cf23..6c4bc62a 100644 --- a/include/support.h +++ b/include/support.h @@ -38,15 +38,22 @@ // Works with both \ and / directory delimeters. std::string get_basename(const std::string& filename); -// Unsigned integer division with ceiling -// There is no risk of signed types being used here because the template is unsigned +// Unsigned-only integer division with ceiling template::value>::type, typename T2, class = typename std::enable_if::value>::type> -inline const T1 ceil_divide(const T1 x, const T2 y) noexcept { +inline const T1 ceil_udivide(const T1 x, const T2 y) noexcept { return (x != 0) ? 1 + ((x - 1) / y) : 0; // https://stackoverflow.com/a/2745086 } +// Signed-only integer division with ceiling +template::value>::type, + typename T2, class = typename std::enable_if::value>::type> +inline const T1 ceil_sdivide(const T1 x, const T2 y) noexcept { + return x / y + (((x < 0) ^ (y > 0)) && (x % y)); + // https://stackoverflow.com/a/33790603 +} + // Include a message in assert, similar to static_assert: #define assertm(exp, msg) assert(((void)msg, exp)) // Use (void) to silent unused warnings. diff --git a/src/dos/cdrom_image.cpp b/src/dos/cdrom_image.cpp index dcf687ea..2b0c50e8 100644 --- a/src/dos/cdrom_image.cpp +++ b/src/dos/cdrom_image.cpp @@ -142,7 +142,7 @@ uint32_t CDROM_Interface_Image::BinaryFile::decode(int16_t *buffer, const uint32_t bytes_read = static_cast(file->gcount()); // Return the number of decoded Redbook frames - return ceil_divide(bytes_read, BYTES_PER_REDBOOK_PCM_FRAME); + return ceil_udivide(bytes_read, BYTES_PER_REDBOOK_PCM_FRAME); } CDROM_Interface_Image::AudioFile::AudioFile(const char *filename, bool &error) @@ -201,8 +201,8 @@ bool CDROM_Interface_Image::AudioFile::seek(const uint32_t requested_pos) // Convert the position from a byte offset to time offset, in milliseconds. const uint32_t ms_per_s = 1000; - const uint32_t pos_in_frames = ceil_divide(requested_pos, BYTES_PER_RAW_REDBOOK_FRAME); - const uint32_t pos_in_ms = ceil_divide(pos_in_frames * ms_per_s, REDBOOK_FRAMES_PER_SECOND); + const uint32_t pos_in_frames = ceil_udivide(requested_pos, BYTES_PER_RAW_REDBOOK_FRAME); + const uint32_t pos_in_ms = ceil_udivide(pos_in_frames * ms_per_s, REDBOOK_FRAMES_PER_SECOND); #ifdef DEBUG /** @@ -281,7 +281,7 @@ bool CDROM_Interface_Image::AudioFile::read(uint8_t *buffer, // Setup characteristics about our track and the request const uint8_t channels = getChannels(); const uint8_t bytes_per_frame = channels * REDBOOK_BPS; - const uint32_t requested_frames = ceil_divide(requested_bytes, BYTES_PER_REDBOOK_PCM_FRAME); + const uint32_t requested_frames = ceil_udivide(requested_bytes, BYTES_PER_REDBOOK_PCM_FRAME); uint32_t decoded_bytes = 0; uint32_t decoded_frames = 0; @@ -537,7 +537,7 @@ bool CDROM_Interface_Image::GetAudioSub(unsigned char& attr, const auto track_file = player.trackFile.lock(); if (track_file) { const uint32_t sample_rate = track_file->getRate(); - const uint32_t played_frames = ceil_divide(player.playedTrackFrames + const uint32_t played_frames = ceil_udivide(player.playedTrackFrames * REDBOOK_FRAMES_PER_SECOND, sample_rate); absolute_sector = player.startSector + played_frames; track_iter current_track = GetTrack(absolute_sector); @@ -689,7 +689,7 @@ bool CDROM_Interface_Image::PlayAudioSector(const uint32_t start, uint32_t len) * 64-bit. */ player.playedTrackFrames = 0; - player.totalTrackFrames = ceil_divide(track_rate * player.totalRedbookFrames, + player.totalTrackFrames = ceil_udivide(track_rate * player.totalRedbookFrames, REDBOOK_FRAMES_PER_SECOND); #ifdef DEBUG @@ -818,7 +818,7 @@ bool CDROM_Interface_Image::ReadSectors(PhysPt buffer, "%s after %u sectors (%u bytes)", num, raw ? "raw" : "cooked", sector, success ? "Succeeded" : "Failed", - ceil_divide(bytes_read, sectorSize), bytes_read); + ceil_udivide(bytes_read, sectorSize), bytes_read); #endif return success; } diff --git a/src/libs/decoders/mp3.cpp b/src/libs/decoders/mp3.cpp index 7f8002eb..6251d166 100644 --- a/src/libs/decoders/mp3.cpp +++ b/src/libs/decoders/mp3.cpp @@ -131,7 +131,7 @@ static int32_t MP3_open(Sound_Sample* const sample, const char* const ext) // total_time needs milliseconds internal->total_time = (num_frames != 0) ? - static_cast(ceil_divide(num_frames * 1000u, sample->actual.rate)) + static_cast(ceil_udivide(num_frames * 1000u, sample->actual.rate)) : -1; } } @@ -161,7 +161,7 @@ static Sint32 MP3_seek(Sound_Sample* const sample, const Uint32 ms) Sound_SampleInternal* const internal = static_cast(sample->opaque); mp3_t* p_mp3 = static_cast(internal->decoder_private); const uint64_t sample_rate = sample->actual.rate; - const drmp3_uint64 pcm_frame = ceil_divide(sample_rate * ms, 1000u); + const drmp3_uint64 pcm_frame = ceil_udivide(sample_rate * ms, 1000u); const drmp3_bool32 result = drmp3_seek_to_pcm_frame(p_mp3->p_dr, pcm_frame); return (result == DRMP3_TRUE); } /* MP3_seek */ diff --git a/src/libs/decoders/mp3_seek_table.cpp b/src/libs/decoders/mp3_seek_table.cpp index cf70d69b..9b3ec8c7 100644 --- a/src/libs/decoders/mp3_seek_table.cpp +++ b/src/libs/decoders/mp3_seek_table.cpp @@ -204,7 +204,7 @@ Uint64 generate_new_seek_points(const char* filename, // We also take into account the desired number of "FRAMES_PER_SEEK_POINT", // which is defined above. drmp3_uint32 num_seek_points = static_cast - (ceil_divide(mp3_frame_count, FRAMES_PER_SEEK_POINT)); + (ceil_udivide(mp3_frame_count, FRAMES_PER_SEEK_POINT)); seek_points_vector.resize(num_seek_points); result = drmp3_calculate_seek_points(p_dr, diff --git a/src/libs/decoders/opus.cpp b/src/libs/decoders/opus.cpp index d2da0532..7d8e142b 100644 --- a/src/libs/decoders/opus.cpp +++ b/src/libs/decoders/opus.cpp @@ -257,14 +257,20 @@ static int32_t opus_open(Sound_Sample * sample, const char * ext) const OpusHead* oh = op_head(of, -1); output_opus_info(of, oh); + // Populate track properties sample->actual.rate = OPUS_SAMPLE_RATE; sample->actual.channels = static_cast(oh->channel_count); sample->flags = op_seekable(of) ? SOUND_SAMPLEFLAG_CANSEEK: 0; sample->actual.format = AUDIO_S16SYS; - int64_t pcm_result = op_pcm_total(of, -1); // If positive, holds total PCM samples - internal->total_time = pcm_result == OP_EINVAL ? -1 : // total milliseconds in the stream - static_cast - (ceil_divide(static_cast(pcm_result), OPUS_SAMPLE_RATE_PER_MS)); + + // Populate the track's duration in milliseconds (or -1 if bad) + const auto pcm_result = static_cast(op_pcm_total(of, -1)); + if (pcm_result == OP_EINVAL) + internal->total_time = -1; + else { + constexpr auto frames_per_ms = static_cast(OPUS_SAMPLE_RATE_PER_MS); + internal->total_time = ceil_sdivide(pcm_result, frames_per_ms); + } return rcode; } /* opus_open */ @@ -310,7 +316,7 @@ static uint32_t opus_read(Sound_Sample * sample, void * buffer, uint32_t request } // Finally, we return the number of frames decoded - const uint32_t decoded_frames = ceil_divide(total_decoded_samples, channels); + const uint32_t decoded_frames = ceil_udivide(total_decoded_samples, channels); return decoded_frames; } /* opus_read */