Disable unimplemented formats, add missing const qualifiers

This commit is contained in:
spicyjpeg 2025-03-08 01:10:42 +01:00
parent 60cbaca2b2
commit 801d70e22e
No known key found for this signature in database
GPG Key ID: 5CC87404C01DF393
8 changed files with 196 additions and 46 deletions

View File

@ -50,7 +50,7 @@ $ psxavenc -t vagi -f 44100 -c 2 -L -i 2048 in.wav out.vag
The output format must be set using the `-t` option. The output format must be set using the `-t` option.
| Format | Audio codec | Audio channels | Video codec | Sector size | | Format | Audio codec | Audio channels | Video codec | Sector size |
| :------- | :------------------- | :------------- | :------------ | :---------- | | :------ | :------------------- | :------------- | :------------ | :---------- |
| `xa` | XA-ADPCM | 1 or 2 | | 2336 bytes | | `xa` | XA-ADPCM | 1 or 2 | | 2336 bytes |
| `xacd` | XA-ADPCM | 1 or 2 | | 2352 bytes | | `xacd` | XA-ADPCM | 1 or 2 | | 2352 bytes |
| `spu` | SPU-ADPCM | 1 | | | | `spu` | SPU-ADPCM | 1 | | |
@ -59,7 +59,6 @@ The output format must be set using the `-t` option.
| `vagi` | SPU-ADPCM | Any | | | | `vagi` | SPU-ADPCM | Any | | |
| `str` | XA-ADPCM (optional) | 1 or 2 | BS v2/v3/v3dc | 2336 bytes | | `str` | XA-ADPCM (optional) | 1 or 2 | BS v2/v3/v3dc | 2336 bytes |
| `strcd` | XA-ADPCM (optional) | 1 or 2 | BS v2/v3/v3dc | 2352 bytes | | `strcd` | XA-ADPCM (optional) | 1 or 2 | BS v2/v3/v3dc | 2352 bytes |
| `strspu` | SPU-ADPCM (optional) | Any | BS v2/v3/v3dc | 2048 bytes |
| `strv` | | | BS v2/v3/v3dc | 2048 bytes | | `strv` | | | BS v2/v3/v3dc | 2048 bytes |
| `sbs` | | | BS v2/v3/v3dc | | | `sbs` | | | BS v2/v3/v3dc | |
@ -81,11 +80,12 @@ Notes:
specified using the `-a` option (2048 bytes by default). Note that `vagi` specified using the `-a` option (2048 bytes by default). Note that `vagi`
files with more than 2 channels and/or alignment other than 2048 bytes are not files with more than 2 channels and/or alignment other than 2048 bytes are not
standardized. standardized.
- The `strspu` format encodes the input file's audio track as a series of custom - ~~The `strspu` format encodes the input file's audio track as a series of~~
.str chunks (type ID `0x0001` by default) holding interleaved SPU-ADPCM data ~~custom .str chunks (type ID `0x0001` by default) holding interleaved~~
in the same format as `spui`, rather than XA-ADPCM. As .str chunks do not ~~SPU-ADPCM data in the same format as `spui`, rather than XA-ADPCM. As .str~~
require custom XA subheaders, a file with standard 2048-byte sectors that does ~~chunks do not require custom XA subheaders, a file with standard 2048-byte~~
not need any special handling will be generated. ~~sectors that does not need any special handling will be generated.~~ *This*
*format has not yet been implemented.*
- The `strv` format disables audio altogether and is equivalent to `strspu` on - The `strv` format disables audio altogether and is equivalent to `strspu` on
an input file with no audio track. an input file with no audio track.
- The `sbs` format (used in some System 573 games) consists of a series of - The `sbs` format (used in some System 573 games) consists of a series of
@ -95,8 +95,8 @@ Notes:
## Supported video codecs ## Supported video codecs
All formats with a video track (`str`, `strcd`, `strspu`, `strv` and `sbs`) can All formats with a video track (`str`, `strcd`, `strv` and `sbs`) can use any of
use any of the codecs listed below. The codec can be set using the `-v` option. the codecs listed below. The codec can be set using the `-v` option.
| Codec | Supported by | Typ. decoder CPU usage | | Codec | Supported by | Typ. decoder CPU usage |
| :------------- | :-------------------- | :--------------------- | | :------------- | :-------------------- | :--------------------- |

View File

@ -36,7 +36,14 @@ freely, subject to the following restrictions:
static const int16_t filter_k1[ADPCM_FILTER_COUNT] = {0, 60, 115, 98, 122}; static const int16_t filter_k1[ADPCM_FILTER_COUNT] = {0, 60, 115, 98, 122};
static const int16_t filter_k2[ADPCM_FILTER_COUNT] = {0, 0, -52, -55, -60}; static const int16_t filter_k2[ADPCM_FILTER_COUNT] = {0, 0, -52, -55, -60};
static int find_min_shift(const psx_audio_encoder_channel_state_t *state, int16_t *samples, int sample_limit, int pitch, int filter, int shift_range) { static int find_min_shift(
const psx_audio_encoder_channel_state_t *state,
const int16_t *samples,
int sample_limit,
int pitch,
int filter,
int shift_range
) {
// Assumption made: // Assumption made:
// //
// There is value in shifting right one step further to allow the nibbles to clip. // There is value in shifting right one step further to allow the nibbles to clip.
@ -71,7 +78,19 @@ static int find_min_shift(const psx_audio_encoder_channel_state_t *state, int16_
return min_shift; return min_shift;
} }
static uint8_t attempt_to_encode(psx_audio_encoder_channel_state_t *outstate, const psx_audio_encoder_channel_state_t *instate, int16_t *samples, int sample_limit, int pitch, uint8_t *data, int data_shift, int data_pitch, int filter, int sample_shift, int shift_range) { static uint8_t attempt_to_encode(
psx_audio_encoder_channel_state_t *outstate,
const psx_audio_encoder_channel_state_t *instate,
const int16_t *samples,
int sample_limit,
int pitch,
uint8_t *data,
int data_shift,
int data_pitch,
int filter,
int sample_shift,
int shift_range
) {
uint8_t sample_mask = 0xFFFF >> shift_range; uint8_t sample_mask = 0xFFFF >> shift_range;
uint8_t nondata_mask = ~(sample_mask << data_shift); uint8_t nondata_mask = ~(sample_mask << data_shift);
@ -120,7 +139,17 @@ static uint8_t attempt_to_encode(psx_audio_encoder_channel_state_t *outstate, co
return hdr; return hdr;
} }
static uint8_t encode(psx_audio_encoder_channel_state_t *state, int16_t *samples, int sample_limit, int pitch, uint8_t *data, int data_shift, int data_pitch, int filter_count, int shift_range) { static uint8_t encode(
psx_audio_encoder_channel_state_t *state,
const int16_t *samples,
int sample_limit,
int pitch,
uint8_t *data,
int data_shift,
int data_pitch,
int filter_count,
int shift_range
) {
psx_audio_encoder_channel_state_t proposed; psx_audio_encoder_channel_state_t proposed;
int64_t best_mse = ((int64_t)1<<(int64_t)50); int64_t best_mse = ((int64_t)1<<(int64_t)50);
int best_filter = 0; int best_filter = 0;
@ -161,7 +190,13 @@ static uint8_t encode(psx_audio_encoder_channel_state_t *state, int16_t *samples
best_filter, best_sample_shift, shift_range); best_filter, best_sample_shift, shift_range);
} }
static void encode_block_xa(int16_t *audio_samples, int audio_samples_limit, uint8_t *data, psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state) { static void encode_block_xa(
const int16_t *audio_samples,
int audio_samples_limit,
uint8_t *data,
psx_audio_xa_settings_t settings,
psx_audio_encoder_state_t *state
) {
if (settings.bits_per_sample == 4) { if (settings.bits_per_sample == 4) {
if (settings.stereo) { if (settings.stereo) {
data[0] = encode(&(state->left), audio_samples, audio_samples_limit, 2, data + 0x10, 0, 4, XA_ADPCM_FILTER_COUNT, SHIFT_RANGE_4BPS); data[0] = encode(&(state->left), audio_samples, audio_samples_limit, 2, data + 0x10, 0, 4, XA_ADPCM_FILTER_COUNT, SHIFT_RANGE_4BPS);
@ -258,7 +293,7 @@ static void psx_audio_xa_encode_init_sector(psx_cdrom_sector_mode2_t *buffer, in
int psx_audio_xa_encode( int psx_audio_xa_encode(
psx_audio_xa_settings_t settings, psx_audio_xa_settings_t settings,
psx_audio_encoder_state_t *state, psx_audio_encoder_state_t *state,
int16_t* samples, const int16_t *samples,
int sample_count, int sample_count,
int lba, int lba,
uint8_t *output uint8_t *output
@ -306,7 +341,7 @@ void psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *out
int psx_audio_xa_encode_simple( int psx_audio_xa_encode_simple(
psx_audio_xa_settings_t settings, psx_audio_xa_settings_t settings,
int16_t* samples, const int16_t *samples,
int sample_count, int sample_count,
int lba, int lba,
uint8_t *output uint8_t *output
@ -320,7 +355,7 @@ int psx_audio_xa_encode_simple(
int psx_audio_spu_encode( int psx_audio_spu_encode(
psx_audio_encoder_channel_state_t *state, psx_audio_encoder_channel_state_t *state,
int16_t* samples, const int16_t *samples,
int sample_count, int sample_count,
int pitch, int pitch,
uint8_t *output uint8_t *output
@ -340,7 +375,7 @@ int psx_audio_spu_encode(
return buffer - output; return buffer - output;
} }
int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start) { int psx_audio_spu_encode_simple(const int16_t *samples, int sample_count, uint8_t *output, int loop_start) {
psx_audio_encoder_channel_state_t state; psx_audio_encoder_channel_state_t state;
memset(&state, 0, sizeof(psx_audio_encoder_channel_state_t)); memset(&state, 0, sizeof(psx_audio_encoder_channel_state_t));
int length = psx_audio_spu_encode(&state, samples, sample_count, 1, output); int length = psx_audio_spu_encode(&state, samples, sample_count, 1, output);

View File

@ -75,26 +75,26 @@ uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings);
int psx_audio_xa_encode( int psx_audio_xa_encode(
psx_audio_xa_settings_t settings, psx_audio_xa_settings_t settings,
psx_audio_encoder_state_t *state, psx_audio_encoder_state_t *state,
int16_t* samples, const int16_t *samples,
int sample_count, int sample_count,
int lba, int lba,
uint8_t *output uint8_t *output
); );
int psx_audio_xa_encode_simple( int psx_audio_xa_encode_simple(
psx_audio_xa_settings_t settings, psx_audio_xa_settings_t settings,
int16_t* samples, const int16_t *samples,
int sample_count, int sample_count,
int lba, int lba,
uint8_t *output uint8_t *output
); );
int psx_audio_spu_encode( int psx_audio_spu_encode(
psx_audio_encoder_channel_state_t *state, psx_audio_encoder_channel_state_t *state,
int16_t* samples, const int16_t *samples,
int sample_count, int sample_count,
int pitch, int pitch,
uint8_t *output uint8_t *output
); );
int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *output, int loop_start); int psx_audio_spu_encode_simple(const int16_t *samples, int sample_count, uint8_t *output, int loop_start);
void psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length); void psx_audio_xa_encode_finalize(psx_audio_xa_settings_t settings, uint8_t *output, int output_length);
// cdrom.c // cdrom.c

View File

@ -125,7 +125,7 @@ static const char *const general_options_help =
" vagi: [A.] .vag SPU-ADPCM interleaved\n" " vagi: [A.] .vag SPU-ADPCM interleaved\n"
" str: [AV] .str video + XA-ADPCM, 2336-byte sectors\n" " str: [AV] .str video + XA-ADPCM, 2336-byte sectors\n"
" strcd: [AV] .str video + XA-ADPCM, 2352-byte sectors\n" " strcd: [AV] .str video + XA-ADPCM, 2352-byte sectors\n"
" strspu: [AV] .str video + SPU-ADPCM, 2048-byte sectors\n" //" strspu: [AV] .str video + SPU-ADPCM, 2048-byte sectors\n"
" strv: [.V] .str video, 2048-byte sectors\n" " strv: [.V] .str video, 2048-byte sectors\n"
" sbs: [.V] .sbs video\n" " sbs: [.V] .sbs video\n"
" -R key=value,... Pass custom options to libswresample (see FFmpeg docs)\n" " -R key=value,... Pass custom options to libswresample (see FFmpeg docs)\n"
@ -498,7 +498,7 @@ static const char *const general_usage =
" psxavenc -t spu|vag [spu-options] <in> <out.vag>\n" " psxavenc -t spu|vag [spu-options] <in> <out.vag>\n"
" psxavenc -t spui|vagi [spui-options] <in> <out.vag>\n" " psxavenc -t spui|vagi [spui-options] <in> <out.vag>\n"
" psxavenc -t str|strcd [xa-options] [bs-options] [str-options] <in> <out.str>\n" " psxavenc -t str|strcd [xa-options] [bs-options] [str-options] <in> <out.str>\n"
" psxavenc -t strspu [spui-options] [bs-options] [str-options] <in> <out.str>\n" //" psxavenc -t strspu [spui-options] [bs-options] [str-options] <in> <out.str>\n"
" psxavenc -t strv [bs-options] [str-options] <in> <out.str>\n" " psxavenc -t strv [bs-options] [str-options] <in> <out.str>\n"
" psxavenc -t sbs [bs-options] [sbs-options] <in> <out.sbs>\n" " psxavenc -t sbs [bs-options] [sbs-options] <in> <out.sbs>\n"
"\n"; "\n";

View File

@ -22,6 +22,7 @@ freely, subject to the following restrictions:
3. This notice may not be removed or altered from any source distribution. 3. This notice may not be removed or altered from any source distribution.
*/ */
#include <assert.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@ -432,7 +433,9 @@ void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
uint8_t sector[PSX_CDROM_SECTOR_SIZE]; uint8_t sector[PSX_CDROM_SECTOR_SIZE];
bool is_video_sector; bool is_video_sector;
if (args->flags & FLAG_STR_TRAILING_AUDIO) if (audio_samples_per_sector == 0)
is_video_sector = true;
else if (args->flags & FLAG_STR_TRAILING_AUDIO)
is_video_sector = (sector_count % interleave) < video_sectors_per_block; is_video_sector = (sector_count % interleave) < video_sectors_per_block;
else else
is_video_sector = (sector_count % interleave) > 0; is_video_sector = (sector_count % interleave) > 0;
@ -497,7 +500,114 @@ void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
} }
void encode_file_strspu(const args_t *args, decoder_t *decoder, FILE *output) { void encode_file_strspu(const args_t *args, decoder_t *decoder, FILE *output) {
// TODO: implement int interleave;
int audio_samples_per_sector;
int video_sectors_per_block;
if (decoder->state.audio_stream != NULL) {
assert(false); // TODO: implement
if (!(args->flags & FLAG_QUIET))
fprintf(
stderr,
"Interleave: %d/%d audio, %d/%d video\n",
interleave - video_sectors_per_block,
interleave,
video_sectors_per_block,
interleave
);
} else {
// 0/1 audio, 1/1 video
interleave = 1;
audio_samples_per_sector = 0;
video_sectors_per_block = 1;
}
mdec_encoder_t encoder;
init_mdec_encoder(&encoder, args->video_codec, args->video_width, args->video_height);
// e.g. 15fps = (150*7/8/15) = 8.75 blocks per frame
encoder.state.frame_block_base_overflow = (75 * args->str_cd_speed) * video_sectors_per_block * args->str_fps_den;
encoder.state.frame_block_overflow_den = interleave * args->str_fps_num;
double frame_size = (double)encoder.state.frame_block_base_overflow / (double)encoder.state.frame_block_overflow_den;
if (!(args->flags & FLAG_QUIET))
fprintf(stderr, "Frame size: %.2f sectors\n", frame_size);
encoder.state.frame_output = malloc(2016 * (int)ceil(frame_size));
encoder.state.frame_index = 0;
encoder.state.frame_data_offset = 0;
encoder.state.frame_max_size = 0;
encoder.state.frame_block_overflow_num = 0;
encoder.state.quant_scale_sum = 0;
// FIXME: this needs an extra frame to prevent A/V desync
int frames_needed = (int) ceil((double)video_sectors_per_block / frame_size);
if (frames_needed < 2)
frames_needed = 2;
int sector_count = 0;
for (; !decoder->end_of_input || encoder.state.frame_data_offset < encoder.state.frame_max_size; sector_count++) {
ensure_av_data(decoder, audio_samples_per_sector * args->audio_channels, frames_needed);
uint8_t sector[2048];
bool is_video_sector;
if (audio_samples_per_sector == 0)
is_video_sector = true;
else if (args->flags & FLAG_STR_TRAILING_AUDIO)
is_video_sector = (sector_count % interleave) < video_sectors_per_block;
else
is_video_sector = (sector_count % interleave) > 0;
if (is_video_sector) {
init_sector_buffer_video(args, sector, sector_count);
int frames_used = encode_sector_str(
&encoder,
args->format,
args->str_video_id,
decoder->video_frames,
sector
);
retire_av_data(decoder, 0, frames_used);
} else {
int samples_length = decoder->audio_sample_count / args->audio_channels;
if (samples_length > audio_samples_per_sector)
samples_length = audio_samples_per_sector;
// FIXME: this is an extremely hacky way to handle audio tracks
// shorter than the video track
if (!samples_length)
video_sectors_per_block++;
assert(false); // TODO: implement
retire_av_data(decoder, samples_length * args->audio_channels, 0);
}
fwrite(sector, 2048, 1, output);
time_t t = get_elapsed_time();
if (!(args->flags & FLAG_HIDE_PROGRESS) && t) {
fprintf(
stderr,
"\rFrame: %4d | LBA: %6d | Avg. q. scale: %5.2f | Encoding speed: %5.2fx",
encoder.state.frame_index,
sector_count,
(double)encoder.state.quant_scale_sum / (double)encoder.state.frame_index,
(double)(encoder.state.frame_index * args->str_fps_den) / (double)(t * args->str_fps_num)
);
}
}
free(encoder.state.frame_output);
destroy_mdec_encoder(&encoder);
} }
void encode_file_sbs(const args_t *args, decoder_t *decoder, FILE *output) { void encode_file_sbs(const args_t *args, decoder_t *decoder, FILE *output) {

View File

@ -146,6 +146,10 @@ int main(int argc, const char **argv) {
break; break;
case FORMAT_STRSPU: case FORMAT_STRSPU:
// TODO: implement and remove this check
fprintf(stderr, "This format is not currently supported\n");
break;
case FORMAT_STRV: case FORMAT_STRV:
if (!(args.flags & FLAG_QUIET)) { if (!(args.flags & FLAG_QUIET)) {
if (decoder.state.audio_stream) if (decoder.state.audio_stream)

View File

@ -577,7 +577,7 @@ void destroy_mdec_encoder(mdec_encoder_t *encoder) {
} }
} }
void encode_frame_bs(mdec_encoder_t *encoder, uint8_t *video_frame) { void encode_frame_bs(mdec_encoder_t *encoder, const uint8_t *video_frame) {
mdec_encoder_state_t *state = &(encoder->state); mdec_encoder_state_t *state = &(encoder->state);
assert(state->dct_context); assert(state->dct_context);
@ -758,15 +758,12 @@ int encode_sector_str(
mdec_encoder_t *encoder, mdec_encoder_t *encoder,
format_t format, format_t format,
uint16_t str_video_id, uint16_t str_video_id,
uint8_t *video_frames, const uint8_t *video_frames,
uint8_t *output uint8_t *output
) { ) {
mdec_encoder_state_t *state = &(encoder->state); mdec_encoder_state_t *state = &(encoder->state);
int last_frame_index = state->frame_index;
int frame_size = encoder->video_width * encoder->video_height * 2; int frame_size = encoder->video_width * encoder->video_height * 2;
int frames_used = 0;
uint8_t header[32];
memset(header, 0, sizeof(header));
while (state->frame_data_offset >= state->frame_max_size) { while (state->frame_data_offset >= state->frame_max_size) {
state->frame_index++; state->frame_index++;
@ -779,8 +776,12 @@ int encode_sector_str(
encode_frame_bs(encoder, video_frames); encode_frame_bs(encoder, video_frames);
video_frames += frame_size; video_frames += frame_size;
frames_used++;
} }
uint8_t header[32];
memset(header, 0, sizeof(header));
// STR version // STR version
header[0x000] = 0x60; header[0x000] = 0x60;
header[0x001] = 0x01; header[0x001] = 0x01;
@ -831,5 +832,5 @@ int encode_sector_str(
memcpy(output + offset + 0x020, state->frame_output + state->frame_data_offset, 2016); memcpy(output + offset + 0x020, state->frame_output + state->frame_data_offset, 2016);
state->frame_data_offset += 2016; state->frame_data_offset += 2016;
return state->frame_index - last_frame_index; return frames_used;
} }

View File

@ -64,11 +64,11 @@ typedef struct {
bool init_mdec_encoder(mdec_encoder_t *encoder, bs_codec_t video_codec, int video_width, int video_height); bool init_mdec_encoder(mdec_encoder_t *encoder, bs_codec_t video_codec, int video_width, int video_height);
void destroy_mdec_encoder(mdec_encoder_t *encoder); void destroy_mdec_encoder(mdec_encoder_t *encoder);
void encode_frame_bs(mdec_encoder_t *encoder, uint8_t *video_frame); void encode_frame_bs(mdec_encoder_t *encoder, const uint8_t *video_frame);
int encode_sector_str( int encode_sector_str(
mdec_encoder_t *encoder, mdec_encoder_t *encoder,
format_t format, format_t format,
uint16_t str_video_id, uint16_t str_video_id,
uint8_t *video_frames, const uint8_t *video_frames,
uint8_t *output uint8_t *output
); );