Clean up, implement new SPU-ADPCM looping options
This commit is contained in:
parent
4a0d0c55fd
commit
7d537edffb
|
@ -54,7 +54,7 @@ static int find_min_shift(const psx_audio_encoder_channel_state_t *state, int16_
|
||||||
|
|
||||||
int32_t s_min = 0;
|
int32_t s_min = 0;
|
||||||
int32_t s_max = 0;
|
int32_t s_max = 0;
|
||||||
for (int i = 0; i < 28; i++) {
|
for (int i = 0; i < PSX_AUDIO_SPU_SAMPLES_PER_BLOCK; i++) {
|
||||||
int32_t raw_sample = (i >= sample_limit) ? 0 : samples[i * pitch];
|
int32_t raw_sample = (i >= sample_limit) ? 0 : samples[i * pitch];
|
||||||
int32_t previous_values = (k1*prev1 + k2*prev2 + (1<<5))>>6;
|
int32_t previous_values = (k1*prev1 + k2*prev2 + (1<<5))>>6;
|
||||||
int32_t sample = raw_sample - previous_values;
|
int32_t sample = raw_sample - previous_values;
|
||||||
|
@ -87,7 +87,7 @@ static uint8_t attempt_to_encode(psx_audio_encoder_channel_state_t *outstate, co
|
||||||
|
|
||||||
outstate->mse = 0;
|
outstate->mse = 0;
|
||||||
|
|
||||||
for (int i = 0; i < 28; i++) {
|
for (int i = 0; i < PSX_AUDIO_SPU_SAMPLES_PER_BLOCK; i++) {
|
||||||
int32_t sample = ((i >= sample_limit) ? 0 : samples[i * pitch]) + outstate->qerr;
|
int32_t sample = ((i >= sample_limit) ? 0 : samples[i * pitch]) + outstate->qerr;
|
||||||
int32_t previous_values = (k1*outstate->prev1 + k2*outstate->prev2 + (1<<5))>>6;
|
int32_t previous_values = (k1*outstate->prev1 + k2*outstate->prev2 + (1<<5))>>6;
|
||||||
int32_t sample_enc = sample - previous_values;
|
int32_t sample_enc = sample - previous_values;
|
||||||
|
@ -205,25 +205,17 @@ uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int samp
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t psx_audio_spu_get_buffer_size(int sample_count) {
|
uint32_t psx_audio_spu_get_buffer_size(int sample_count) {
|
||||||
return ((sample_count + 27) / 28) << 4;
|
return ((sample_count + PSX_AUDIO_SPU_SAMPLES_PER_BLOCK - 1) / PSX_AUDIO_SPU_SAMPLES_PER_BLOCK) << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings) {
|
uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings) {
|
||||||
return settings.format == PSX_AUDIO_XA_FORMAT_XA ? 2336 : 2352;
|
return settings.format == PSX_AUDIO_XA_FORMAT_XA ? 2336 : 2352;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t psx_audio_spu_get_buffer_size_per_block(void) {
|
|
||||||
return 16;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings) {
|
uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings) {
|
||||||
return (((settings.bits_per_sample == 8) ? 112 : 224) >> (settings.stereo ? 1 : 0)) * 18;
|
return (((settings.bits_per_sample == 8) ? 112 : 224) >> (settings.stereo ? 1 : 0)) * 18;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t psx_audio_spu_get_samples_per_block(void) {
|
|
||||||
return 28;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings) {
|
uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings) {
|
||||||
// 1/2 interleave for 37800 Hz 8-bit stereo at 1x speed
|
// 1/2 interleave for 37800 Hz 8-bit stereo at 1x speed
|
||||||
int interleave = settings.stereo ? 2 : 4;
|
int interleave = settings.stereo ? 2 : 4;
|
||||||
|
@ -307,14 +299,14 @@ int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* sample
|
||||||
}
|
}
|
||||||
|
|
||||||
int psx_audio_spu_encode(psx_audio_encoder_channel_state_t *state, int16_t* samples, int sample_count, int pitch, uint8_t *output) {
|
int psx_audio_spu_encode(psx_audio_encoder_channel_state_t *state, int16_t* samples, int sample_count, int pitch, uint8_t *output) {
|
||||||
uint8_t prebuf[28];
|
uint8_t prebuf[PSX_AUDIO_SPU_SAMPLES_PER_BLOCK];
|
||||||
uint8_t *buffer = output;
|
uint8_t *buffer = output;
|
||||||
|
|
||||||
for (int i = 0; i < sample_count; i += 28, buffer += 16) {
|
for (int i = 0; i < sample_count; i += PSX_AUDIO_SPU_SAMPLES_PER_BLOCK, buffer += PSX_AUDIO_SPU_BLOCK_SIZE) {
|
||||||
buffer[0] = encode(state, samples + i * pitch, sample_count - i, pitch, prebuf, 0, 1, SPU_ADPCM_FILTER_COUNT, SHIFT_RANGE_4BPS);
|
buffer[0] = encode(state, samples + i * pitch, sample_count - i, pitch, prebuf, 0, 1, SPU_ADPCM_FILTER_COUNT, SHIFT_RANGE_4BPS);
|
||||||
buffer[1] = 0;
|
buffer[1] = 0;
|
||||||
|
|
||||||
for (int j = 0; j < 28; j+=2) {
|
for (int j = 0; j < PSX_AUDIO_SPU_SAMPLES_PER_BLOCK; j+=2) {
|
||||||
buffer[2 + (j>>1)] = (prebuf[j] & 0x0F) | (prebuf[j+1] << 4);
|
buffer[2 + (j>>1)] = (prebuf[j] & 0x0F) | (prebuf[j+1] << 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -327,24 +319,24 @@ int psx_audio_spu_encode_simple(int16_t* samples, int sample_count, uint8_t *out
|
||||||
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);
|
||||||
|
|
||||||
if (length >= 32) {
|
if (length >= PSX_AUDIO_SPU_BLOCK_SIZE) {
|
||||||
|
uint8_t *last_block = output + length - PSX_AUDIO_SPU_BLOCK_SIZE;
|
||||||
|
|
||||||
if (loop_start < 0) {
|
if (loop_start < 0) {
|
||||||
//output[1] = PSX_AUDIO_SPU_LOOP_START;
|
last_block[1] |= PSX_AUDIO_SPU_LOOP_END;
|
||||||
output[length - 16 + 1] = PSX_AUDIO_SPU_LOOP_END;
|
|
||||||
|
// Insert trailing looping block
|
||||||
|
memset(output + length, 0, PSX_AUDIO_SPU_BLOCK_SIZE);
|
||||||
|
output[length + 1] = PSX_AUDIO_SPU_LOOP_START | PSX_AUDIO_SPU_LOOP_END;
|
||||||
|
|
||||||
|
length += PSX_AUDIO_SPU_BLOCK_SIZE;
|
||||||
} else {
|
} else {
|
||||||
psx_audio_spu_set_flag_at_sample(output, loop_start, PSX_AUDIO_SPU_LOOP_START);
|
int loop_start_offset = loop_start / PSX_AUDIO_SPU_SAMPLES_PER_BLOCK * PSX_AUDIO_SPU_BLOCK_SIZE;
|
||||||
output[length - 16 + 1] = PSX_AUDIO_SPU_LOOP_REPEAT;
|
|
||||||
|
last_block[1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
|
||||||
|
output[loop_start_offset + 1] |= PSX_AUDIO_SPU_LOOP_START;
|
||||||
}
|
}
|
||||||
} else if (length >= 16) {
|
|
||||||
output[1] = PSX_AUDIO_SPU_LOOP_START | PSX_AUDIO_SPU_LOOP_END;
|
|
||||||
if (loop_start >= 0)
|
|
||||||
output[1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag) {
|
|
||||||
int buffer_pos = (sample_pos / 28) << 4;
|
|
||||||
spu_data[buffer_pos + 1] = flag;
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,8 +28,13 @@ freely, subject to the following restrictions:
|
||||||
|
|
||||||
// audio.c
|
// audio.c
|
||||||
|
|
||||||
#define PSX_AUDIO_XA_FREQ_SINGLE 18900
|
#define PSX_AUDIO_SPU_BLOCK_SIZE 16
|
||||||
#define PSX_AUDIO_XA_FREQ_DOUBLE 37800
|
#define PSX_AUDIO_SPU_SAMPLES_PER_BLOCK 28
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PSX_AUDIO_XA_FREQ_SINGLE = 18900,
|
||||||
|
PSX_AUDIO_XA_FREQ_DOUBLE = 37800
|
||||||
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PSX_AUDIO_XA_FORMAT_XA, // .xa file
|
PSX_AUDIO_XA_FORMAT_XA, // .xa file
|
||||||
|
@ -56,23 +61,22 @@ typedef struct {
|
||||||
psx_audio_encoder_channel_state_t right;
|
psx_audio_encoder_channel_state_t right;
|
||||||
} psx_audio_encoder_state_t;
|
} psx_audio_encoder_state_t;
|
||||||
|
|
||||||
#define PSX_AUDIO_SPU_LOOP_END 1
|
enum {
|
||||||
#define PSX_AUDIO_SPU_LOOP_REPEAT 3
|
PSX_AUDIO_SPU_LOOP_END = 1 << 0,
|
||||||
#define PSX_AUDIO_SPU_LOOP_START 4
|
PSX_AUDIO_SPU_LOOP_REPEAT = 3 << 0,
|
||||||
|
PSX_AUDIO_SPU_LOOP_START = 1 << 2
|
||||||
|
};
|
||||||
|
|
||||||
uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count);
|
uint32_t psx_audio_xa_get_buffer_size(psx_audio_xa_settings_t settings, int sample_count);
|
||||||
uint32_t psx_audio_spu_get_buffer_size(int sample_count);
|
uint32_t psx_audio_spu_get_buffer_size(int sample_count);
|
||||||
uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings);
|
uint32_t psx_audio_xa_get_buffer_size_per_sector(psx_audio_xa_settings_t settings);
|
||||||
uint32_t psx_audio_spu_get_buffer_size_per_block(void);
|
|
||||||
uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings);
|
uint32_t psx_audio_xa_get_samples_per_sector(psx_audio_xa_settings_t settings);
|
||||||
uint32_t psx_audio_spu_get_samples_per_block(void);
|
|
||||||
uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings);
|
uint32_t psx_audio_xa_get_sector_interleave(psx_audio_xa_settings_t settings);
|
||||||
int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output);
|
int psx_audio_xa_encode(psx_audio_xa_settings_t settings, psx_audio_encoder_state_t *state, int16_t* samples, int sample_count, uint8_t *output);
|
||||||
int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output);
|
int psx_audio_xa_encode_simple(psx_audio_xa_settings_t settings, int16_t* samples, int sample_count, uint8_t *output);
|
||||||
int psx_audio_spu_encode(psx_audio_encoder_channel_state_t *state, int16_t* samples, int sample_count, int pitch, uint8_t *output);
|
int psx_audio_spu_encode(psx_audio_encoder_channel_state_t *state, int16_t* samples, int sample_count, int pitch, 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(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);
|
||||||
void psx_audio_spu_set_flag_at_sample(uint8_t* spu_data, int sample_pos, int flag);
|
|
||||||
|
|
||||||
// cdrom.c
|
// cdrom.c
|
||||||
|
|
||||||
|
@ -115,25 +119,29 @@ _Static_assert(sizeof(psx_cdrom_sector_mode2_t) == PSX_CDROM_SECTOR_SIZE, "Inval
|
||||||
|
|
||||||
#define PSX_CDROM_SECTOR_XA_CHANNEL_MASK 0x1F
|
#define PSX_CDROM_SECTOR_XA_CHANNEL_MASK 0x1F
|
||||||
|
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_EOR 0x01
|
enum {
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_VIDEO 0x02
|
PSX_CDROM_SECTOR_XA_SUBMODE_EOR = 1 << 0,
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_AUDIO 0x04
|
PSX_CDROM_SECTOR_XA_SUBMODE_VIDEO = 1 << 1,
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_DATA 0x08
|
PSX_CDROM_SECTOR_XA_SUBMODE_AUDIO = 1 << 2,
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_TRIGGER 0x10
|
PSX_CDROM_SECTOR_XA_SUBMODE_DATA = 1 << 3,
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_FORM2 0x20
|
PSX_CDROM_SECTOR_XA_SUBMODE_TRIGGER = 1 << 4,
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_RT 0x40
|
PSX_CDROM_SECTOR_XA_SUBMODE_FORM2 = 1 << 5,
|
||||||
#define PSX_CDROM_SECTOR_XA_SUBMODE_EOF 0x80
|
PSX_CDROM_SECTOR_XA_SUBMODE_RT = 1 << 6,
|
||||||
|
PSX_CDROM_SECTOR_XA_SUBMODE_EOF = 1 << 7
|
||||||
|
};
|
||||||
|
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_MONO 0x00
|
enum {
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_STEREO 0x01
|
PSX_CDROM_SECTOR_XA_CODING_MONO = 0 << 0,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_CHANNEL_MASK 0x03
|
PSX_CDROM_SECTOR_XA_CODING_STEREO = 1 << 0,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_FREQ_DOUBLE 0x00
|
PSX_CDROM_SECTOR_XA_CODING_CHANNEL_MASK = 3 << 0,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_FREQ_SINGLE 0x04
|
PSX_CDROM_SECTOR_XA_CODING_FREQ_DOUBLE = 0 << 2,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_FREQ_MASK 0x0C
|
PSX_CDROM_SECTOR_XA_CODING_FREQ_SINGLE = 1 << 2,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_BITS_4 0x00
|
PSX_CDROM_SECTOR_XA_CODING_FREQ_MASK = 3 << 2,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_BITS_8 0x10
|
PSX_CDROM_SECTOR_XA_CODING_BITS_4 = 0 << 4,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_BITS_MASK 0x30
|
PSX_CDROM_SECTOR_XA_CODING_BITS_8 = 1 << 4,
|
||||||
#define PSX_CDROM_SECTOR_XA_CODING_EMPHASIS 0x40
|
PSX_CDROM_SECTOR_XA_CODING_BITS_MASK = 3 << 4,
|
||||||
|
PSX_CDROM_SECTOR_XA_CODING_EMPHASIS = 1 << 6
|
||||||
|
};
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
PSX_CDROM_SECTOR_TYPE_MODE1,
|
PSX_CDROM_SECTOR_TYPE_MODE1,
|
||||||
|
|
|
@ -136,7 +136,10 @@ static void write_vag_header(const args_t *args, int size_per_channel, uint8_t *
|
||||||
strncpy((char*)(header + 0x20), &args->output_file[name_offset], 16);
|
strncpy((char*)(header + 0x20), &args->output_file[name_offset], 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
void encode_file_xa(args_t *args, decoder_t *decoder, FILE *output) {
|
// The functions below are some peak spaghetti code I would rewrite if that
|
||||||
|
// didn't also require scrapping the rest of the codebase. -- spicyjpeg
|
||||||
|
|
||||||
|
void encode_file_xa(const args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
|
psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
|
||||||
|
|
||||||
int audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
|
int audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
|
||||||
|
@ -187,26 +190,37 @@ void encode_file_xa(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void encode_file_spu(args_t *args, decoder_t *decoder, FILE *output) {
|
void encode_file_spu(const args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
psx_audio_encoder_channel_state_t audio_state;
|
psx_audio_encoder_channel_state_t audio_state;
|
||||||
memset(&audio_state, 0, sizeof(psx_audio_encoder_channel_state_t));
|
memset(&audio_state, 0, sizeof(psx_audio_encoder_channel_state_t));
|
||||||
|
|
||||||
int audio_samples_per_block = psx_audio_spu_get_samples_per_block();
|
|
||||||
int block_size = psx_audio_spu_get_buffer_size_per_block();
|
|
||||||
int block_count;
|
|
||||||
|
|
||||||
// The header must be written after the data as we don't yet know the
|
// The header must be written after the data as we don't yet know the
|
||||||
// number of audio samples.
|
// number of audio samples.
|
||||||
if (args->format == FORMAT_VAG)
|
if (args->format == FORMAT_VAG)
|
||||||
fseek(output, VAG_HEADER_SIZE, SEEK_SET);
|
fseek(output, VAG_HEADER_SIZE, SEEK_SET);
|
||||||
|
|
||||||
for (block_count = 0; ensure_av_data(decoder, audio_samples_per_block, 0); block_count++) {
|
uint8_t buffer[PSX_AUDIO_SPU_BLOCK_SIZE];
|
||||||
|
int block_count = 0;
|
||||||
|
|
||||||
|
if (!(args->flags & FLAG_SPU_NO_LEADING_DUMMY)) {
|
||||||
|
// Insert leading silent block
|
||||||
|
memset(buffer, 0, PSX_AUDIO_SPU_BLOCK_SIZE);
|
||||||
|
|
||||||
|
fwrite(buffer, PSX_AUDIO_SPU_BLOCK_SIZE, 1, output);
|
||||||
|
block_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int loop_start_block = -1;
|
||||||
|
|
||||||
|
if (args->audio_loop_point >= 0)
|
||||||
|
loop_start_block = (args->audio_loop_point * args->audio_frequency) / (PSX_AUDIO_SPU_SAMPLES_PER_BLOCK * 1000);
|
||||||
|
|
||||||
|
for (; ensure_av_data(decoder, PSX_AUDIO_SPU_SAMPLES_PER_BLOCK, 0); block_count++) {
|
||||||
int samples_length = decoder->audio_sample_count;
|
int samples_length = decoder->audio_sample_count;
|
||||||
|
|
||||||
if (samples_length > audio_samples_per_block)
|
if (samples_length > PSX_AUDIO_SPU_SAMPLES_PER_BLOCK)
|
||||||
samples_length = audio_samples_per_block;
|
samples_length = PSX_AUDIO_SPU_SAMPLES_PER_BLOCK;
|
||||||
|
|
||||||
uint8_t buffer[16];
|
|
||||||
int length = psx_audio_spu_encode(
|
int length = psx_audio_spu_encode(
|
||||||
&audio_state,
|
&audio_state,
|
||||||
decoder->audio_samples,
|
decoder->audio_samples,
|
||||||
|
@ -215,15 +229,10 @@ void encode_file_spu(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
buffer
|
buffer
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: implement proper loop flag support
|
if (block_count == loop_start_block)
|
||||||
if (false)
|
|
||||||
buffer[1] |= PSX_AUDIO_SPU_LOOP_START;
|
buffer[1] |= PSX_AUDIO_SPU_LOOP_START;
|
||||||
if (decoder->end_of_input) {
|
if ((args->flags & FLAG_SPU_LOOP_END) && decoder->end_of_input)
|
||||||
if (args->flags & FLAG_SPU_LOOP_END)
|
|
||||||
buffer[1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
|
buffer[1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
|
||||||
else
|
|
||||||
buffer[1] |= PSX_AUDIO_SPU_LOOP_END;
|
|
||||||
}
|
|
||||||
|
|
||||||
retire_av_data(decoder, samples_length, 0);
|
retire_av_data(decoder, samples_length, 0);
|
||||||
fwrite(buffer, length, 1, output);
|
fwrite(buffer, length, 1, output);
|
||||||
|
@ -235,12 +244,21 @@ void encode_file_spu(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
stderr,
|
stderr,
|
||||||
"\rBlock: %6d | Encoding speed: %5.2fx",
|
"\rBlock: %6d | Encoding speed: %5.2fx",
|
||||||
block_count,
|
block_count,
|
||||||
(double)(block_count * audio_samples_per_block) / (double)(args->audio_frequency * t)
|
(double)(block_count * PSX_AUDIO_SPU_SAMPLES_PER_BLOCK) / (double)(args->audio_frequency * t)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int overflow = (block_count * block_size) % args->alignment;
|
if (!(args->flags & FLAG_SPU_LOOP_END)) {
|
||||||
|
// Insert trailing looping block
|
||||||
|
memset(buffer, 0, PSX_AUDIO_SPU_BLOCK_SIZE);
|
||||||
|
buffer[1] = PSX_AUDIO_SPU_LOOP_START | PSX_AUDIO_SPU_LOOP_END;
|
||||||
|
|
||||||
|
fwrite(buffer, PSX_AUDIO_SPU_BLOCK_SIZE, 1, output);
|
||||||
|
block_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int overflow = (block_count * PSX_AUDIO_SPU_BLOCK_SIZE) % args->alignment;
|
||||||
|
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
for (int i = 0; i < (args->alignment - overflow); i++)
|
for (int i = 0; i < (args->alignment - overflow); i++)
|
||||||
|
@ -248,15 +266,15 @@ void encode_file_spu(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
}
|
}
|
||||||
if (args->format == FORMAT_VAG) {
|
if (args->format == FORMAT_VAG) {
|
||||||
uint8_t header[VAG_HEADER_SIZE];
|
uint8_t header[VAG_HEADER_SIZE];
|
||||||
write_vag_header(args, block_count * block_size, header);
|
write_vag_header(args, block_count * PSX_AUDIO_SPU_BLOCK_SIZE, header);
|
||||||
|
|
||||||
fseek(output, 0, SEEK_SET);
|
fseek(output, 0, SEEK_SET);
|
||||||
fwrite(header, VAG_HEADER_SIZE, 1, output);
|
fwrite(header, VAG_HEADER_SIZE, 1, output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void encode_file_spui(args_t *args, decoder_t *decoder, FILE *output) {
|
void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
int audio_state_size = sizeof(psx_audio_encoder_channel_state_t) * args->audio_channels;
|
int audio_samples_per_chunk = args->audio_interleave / PSX_AUDIO_SPU_BLOCK_SIZE * PSX_AUDIO_SPU_SAMPLES_PER_BLOCK;
|
||||||
|
|
||||||
// NOTE: since the interleaved .vag format is not standardized, some tools
|
// NOTE: since the interleaved .vag format is not standardized, some tools
|
||||||
// (such as vgmstream) will not properly play files with interleave < 2048,
|
// (such as vgmstream) will not properly play files with interleave < 2048,
|
||||||
|
@ -267,38 +285,52 @@ void encode_file_spui(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
int header_size = VAG_HEADER_SIZE + args->alignment - 1;
|
int header_size = VAG_HEADER_SIZE + args->alignment - 1;
|
||||||
header_size -= header_size % args->alignment;
|
header_size -= header_size % args->alignment;
|
||||||
|
|
||||||
int audio_samples_per_block = psx_audio_spu_get_samples_per_block();
|
|
||||||
int block_size = psx_audio_spu_get_buffer_size_per_block();
|
|
||||||
int audio_samples_per_chunk = args->audio_interleave / block_size * audio_samples_per_block;
|
|
||||||
int chunk_count;
|
|
||||||
|
|
||||||
if (args->format == FORMAT_VAGI)
|
if (args->format == FORMAT_VAGI)
|
||||||
fseek(output, header_size, SEEK_SET);
|
fseek(output, header_size, SEEK_SET);
|
||||||
|
|
||||||
|
int audio_state_size = sizeof(psx_audio_encoder_channel_state_t) * args->audio_channels;
|
||||||
psx_audio_encoder_channel_state_t *audio_state = malloc(audio_state_size);
|
psx_audio_encoder_channel_state_t *audio_state = malloc(audio_state_size);
|
||||||
uint8_t *buffer = malloc(buffer_size);
|
|
||||||
memset(audio_state, 0, audio_state_size);
|
memset(audio_state, 0, audio_state_size);
|
||||||
|
|
||||||
for (chunk_count = 0; ensure_av_data(decoder, audio_samples_per_chunk * args->audio_channels, 0); chunk_count++) {
|
uint8_t *buffer = malloc(buffer_size);
|
||||||
|
int chunk_count = 0;
|
||||||
|
|
||||||
|
for (; ensure_av_data(decoder, audio_samples_per_chunk * args->audio_channels, 0); chunk_count++) {
|
||||||
int samples_length = decoder->audio_sample_count / args->audio_channels;
|
int samples_length = decoder->audio_sample_count / args->audio_channels;
|
||||||
if (samples_length > audio_samples_per_chunk) samples_length = audio_samples_per_chunk;
|
int buffer_offset = 0;
|
||||||
|
|
||||||
|
if (samples_length > audio_samples_per_chunk)
|
||||||
|
samples_length = audio_samples_per_chunk;
|
||||||
|
|
||||||
|
// Insert leading silent block
|
||||||
|
if (chunk_count == 0 && !(args->flags & FLAG_SPU_NO_LEADING_DUMMY)) {
|
||||||
|
buffer_offset = PSX_AUDIO_SPU_BLOCK_SIZE;
|
||||||
|
samples_length -= PSX_AUDIO_SPU_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
for (int ch = 0; ch < args->audio_channels; ch++) {
|
for (int ch = 0; ch < args->audio_channels; ch++) {
|
||||||
memset(buffer, 0, buffer_size);
|
memset(buffer, 0, buffer_size);
|
||||||
|
|
||||||
int length = psx_audio_spu_encode(
|
int length = psx_audio_spu_encode(
|
||||||
audio_state + ch,
|
audio_state + ch,
|
||||||
decoder->audio_samples + ch,
|
decoder->audio_samples + ch,
|
||||||
samples_length,
|
samples_length,
|
||||||
args->audio_channels,
|
args->audio_channels,
|
||||||
buffer
|
buffer + buffer_offset
|
||||||
);
|
);
|
||||||
|
|
||||||
if (length) {
|
if (length > 0) {
|
||||||
// TODO: implement proper loop flag support
|
uint8_t *last_block = buffer + length - PSX_AUDIO_SPU_BLOCK_SIZE;
|
||||||
if (args->flags & FLAG_SPU_LOOP_END)
|
|
||||||
buffer[length - block_size + 1] |= PSX_AUDIO_SPU_LOOP_REPEAT;
|
if (args->flags & FLAG_SPU_LOOP_END) {
|
||||||
else if (decoder->end_of_input)
|
last_block[1] = PSX_AUDIO_SPU_LOOP_REPEAT;
|
||||||
buffer[length - block_size + 1] |= PSX_AUDIO_SPU_LOOP_END;
|
} else if (decoder->end_of_input) {
|
||||||
|
// HACK: the trailing block should in theory be appended to
|
||||||
|
// the existing data, but it's easier to just zerofill and
|
||||||
|
// repurpose the last encoded block
|
||||||
|
memset(last_block, 0, PSX_AUDIO_SPU_BLOCK_SIZE);
|
||||||
|
last_block[1] = PSX_AUDIO_SPU_LOOP_START | PSX_AUDIO_SPU_LOOP_END;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fwrite(buffer, buffer_size, 1, output);
|
fwrite(buffer, buffer_size, 1, output);
|
||||||
|
@ -332,10 +364,9 @@ void encode_file_spui(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void encode_file_str(args_t *args, decoder_t *decoder, FILE *output) {
|
void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
|
psx_audio_xa_settings_t xa_settings = args_to_libpsxav_xa_audio(args);
|
||||||
int audio_samples_per_sector;
|
int audio_samples_per_sector;
|
||||||
uint8_t buffer[PSX_CDROM_SECTOR_SIZE];
|
|
||||||
|
|
||||||
int offset, sector_size;
|
int offset, sector_size;
|
||||||
|
|
||||||
|
@ -349,7 +380,8 @@ void encode_file_str(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
|
|
||||||
int interleave;
|
int interleave;
|
||||||
int video_sectors_per_block;
|
int video_sectors_per_block;
|
||||||
if (decoder->state.audio_stream) {
|
|
||||||
|
if (decoder->state.audio_stream != NULL) {
|
||||||
// 1/N audio, (N-1)/N video
|
// 1/N audio, (N-1)/N video
|
||||||
audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
|
audio_samples_per_sector = psx_audio_xa_get_samples_per_sector(xa_settings);
|
||||||
interleave = psx_audio_xa_get_sector_interleave(xa_settings) * args->str_cd_speed;
|
interleave = psx_audio_xa_get_sector_interleave(xa_settings) * args->str_cd_speed;
|
||||||
|
@ -399,16 +431,24 @@ void encode_file_str(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
for (int j = 0; !decoder->end_of_input || encoder.state.frame_data_offset < encoder.state.frame_max_size; j++) {
|
for (int j = 0; !decoder->end_of_input || encoder.state.frame_data_offset < encoder.state.frame_max_size; j++) {
|
||||||
ensure_av_data(decoder, audio_samples_per_sector * args->audio_channels, frames_needed);
|
ensure_av_data(decoder, audio_samples_per_sector * args->audio_channels, frames_needed);
|
||||||
|
|
||||||
if ((j%interleave) < video_sectors_per_block) {
|
uint8_t buffer[PSX_CDROM_SECTOR_SIZE];
|
||||||
// Video sector
|
bool is_video_sector;
|
||||||
|
|
||||||
|
if (args->flags & FLAG_STR_TRAILING_AUDIO)
|
||||||
|
is_video_sector = (j % interleave) < video_sectors_per_block;
|
||||||
|
else
|
||||||
|
is_video_sector = (j % interleave) > 0;
|
||||||
|
|
||||||
|
if (is_video_sector) {
|
||||||
init_sector_buffer_video(args, (psx_cdrom_sector_mode2_t*) buffer, j);
|
init_sector_buffer_video(args, (psx_cdrom_sector_mode2_t*) buffer, j);
|
||||||
|
|
||||||
int frames_used = encode_sector_str(&encoder, args->format, decoder->video_frames, buffer);
|
int frames_used = encode_sector_str(&encoder, args->format, decoder->video_frames, buffer);
|
||||||
retire_av_data(decoder, 0, frames_used);
|
retire_av_data(decoder, 0, frames_used);
|
||||||
} else {
|
} else {
|
||||||
// Audio sector
|
|
||||||
int samples_length = decoder->audio_sample_count / args->audio_channels;
|
int samples_length = decoder->audio_sample_count / args->audio_channels;
|
||||||
if (samples_length > audio_samples_per_sector) samples_length = audio_samples_per_sector;
|
|
||||||
|
if (samples_length > audio_samples_per_sector)
|
||||||
|
samples_length = audio_samples_per_sector;
|
||||||
|
|
||||||
// FIXME: this is an extremely hacky way to handle audio tracks
|
// FIXME: this is an extremely hacky way to handle audio tracks
|
||||||
// shorter than the video track
|
// shorter than the video track
|
||||||
|
@ -438,7 +478,7 @@ void encode_file_str(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4);
|
buffer[0x00E] = ((t%75)%10)|(((t%75)/10)<<4);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((j%interleave) < video_sectors_per_block)
|
if (is_video_sector)
|
||||||
psx_cdrom_calculate_checksums((psx_cdrom_sector_t *)buffer, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1);
|
psx_cdrom_calculate_checksums((psx_cdrom_sector_t *)buffer, PSX_CDROM_SECTOR_TYPE_MODE2_FORM1);
|
||||||
|
|
||||||
fwrite(buffer + offset, sector_size, 1, output);
|
fwrite(buffer + offset, sector_size, 1, output);
|
||||||
|
@ -461,7 +501,11 @@ void encode_file_str(args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
destroy_mdec_encoder(&encoder);
|
destroy_mdec_encoder(&encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void encode_file_sbs(args_t *args, decoder_t *decoder, FILE *output) {
|
void encode_file_strspu(const args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
|
// TODO: implement
|
||||||
|
}
|
||||||
|
|
||||||
|
void encode_file_sbs(const args_t *args, decoder_t *decoder, FILE *output) {
|
||||||
mdec_encoder_t encoder;
|
mdec_encoder_t encoder;
|
||||||
init_mdec_encoder(&encoder, args->video_codec, args->video_width, args->video_height);
|
init_mdec_encoder(&encoder, args->video_codec, args->video_width, args->video_height);
|
||||||
|
|
||||||
|
|
|
@ -32,4 +32,5 @@ void encode_file_xa(const args_t *args, decoder_t *decoder, FILE *output);
|
||||||
void encode_file_spu(const args_t *args, decoder_t *decoder, FILE *output);
|
void encode_file_spu(const args_t *args, decoder_t *decoder, FILE *output);
|
||||||
void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output);
|
void encode_file_spui(const args_t *args, decoder_t *decoder, FILE *output);
|
||||||
void encode_file_str(const args_t *args, decoder_t *decoder, FILE *output);
|
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_sbs(const args_t *args, decoder_t *decoder, FILE *output);
|
void encode_file_sbs(const args_t *args, decoder_t *decoder, FILE *output);
|
||||||
|
|
|
@ -120,7 +120,6 @@ int main(int argc, const char **argv) {
|
||||||
|
|
||||||
case FORMAT_STR:
|
case FORMAT_STR:
|
||||||
case FORMAT_STRCD:
|
case FORMAT_STRCD:
|
||||||
case FORMAT_STRSPU:
|
|
||||||
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)
|
||||||
|
@ -147,6 +146,30 @@ int main(int argc, const char **argv) {
|
||||||
encode_file_str(&args, &decoder, output);
|
encode_file_str(&args, &decoder, output);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case FORMAT_STRSPU:
|
||||||
|
if (!(args.flags & FLAG_QUIET)) {
|
||||||
|
if (decoder.state.audio_stream)
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"Audio format: SPU-ADPCM, %d Hz %d channels, interleave=%d\n",
|
||||||
|
args.audio_frequency,
|
||||||
|
args.audio_channels,
|
||||||
|
args.audio_interleave
|
||||||
|
);
|
||||||
|
|
||||||
|
fprintf(
|
||||||
|
stderr,
|
||||||
|
"Video format: %s, %dx%d, %.2f fps\n",
|
||||||
|
bs_codec_names[args.video_codec],
|
||||||
|
args.video_width,
|
||||||
|
args.video_height,
|
||||||
|
(double)args.str_fps_num / (double)args.str_fps_den
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
encode_file_strspu(&args, &decoder, output);
|
||||||
|
break;
|
||||||
|
|
||||||
case FORMAT_SBS:
|
case FORMAT_SBS:
|
||||||
if (!(args.flags & FLAG_QUIET))
|
if (!(args.flags & FLAG_QUIET))
|
||||||
fprintf(
|
fprintf(
|
||||||
|
|
|
@ -23,6 +23,7 @@ freely, subject to the following restrictions:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
@ -236,14 +237,16 @@ static const uint8_t dct_zagzig_table[8*8] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
#define SF0 0x5a82 // cos(0/16 * pi) * sqrt(2)
|
enum {
|
||||||
#define SF1 0x7d8a // cos(1/16 * pi) * 2
|
SF0 = 0x5a82, // cos(0/16 * pi) * sqrt(2)
|
||||||
#define SF2 0x7641 // cos(2/16 * pi) * 2
|
SF1 = 0x7d8a, // cos(1/16 * pi) * 2
|
||||||
#define SF3 0x6a6d // cos(3/16 * pi) * 2
|
SF2 = 0x7641, // cos(2/16 * pi) * 2
|
||||||
#define SF4 0x5a82 // cos(4/16 * pi) * 2
|
SF3 = 0x6a6d, // cos(3/16 * pi) * 2
|
||||||
#define SF5 0x471c // cos(5/16 * pi) * 2
|
SF4 = 0x5a82, // cos(4/16 * pi) * 2
|
||||||
#define SF6 0x30fb // cos(6/16 * pi) * 2
|
SF5 = 0x471c, // cos(5/16 * pi) * 2
|
||||||
#define SF7 0x18f8 // cos(7/16 * pi) * 2
|
SF6 = 0x30fb, // cos(6/16 * pi) * 2
|
||||||
|
SF7 = 0x18f8 // cos(7/16 * pi) * 2
|
||||||
|
};
|
||||||
|
|
||||||
static const int16_t dct_scale_table[8*8] = {
|
static const int16_t dct_scale_table[8*8] = {
|
||||||
SF0, SF0, SF0, SF0, SF0, SF0, SF0, SF0,
|
SF0, SF0, SF0, SF0, SF0, SF0, SF0, SF0,
|
||||||
|
@ -525,7 +528,9 @@ bool init_mdec_encoder(mdec_encoder_t *encoder, bs_codec_t video_codec, int vide
|
||||||
if (
|
if (
|
||||||
state->dct_context == NULL ||
|
state->dct_context == NULL ||
|
||||||
state->ac_huffman_map == NULL ||
|
state->ac_huffman_map == NULL ||
|
||||||
state->coeff_clamp_map == NULL
|
state->dc_huffman_map == NULL ||
|
||||||
|
state->coeff_clamp_map == NULL ||
|
||||||
|
state->delta_clamp_map == NULL
|
||||||
)
|
)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -536,7 +541,7 @@ bool init_mdec_encoder(mdec_encoder_t *encoder, bs_codec_t video_codec, int vide
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
state->dct_block_lists[i] = malloc(dct_block_size);
|
state->dct_block_lists[i] = malloc(dct_block_size);
|
||||||
|
|
||||||
if (!state->dct_block_lists[i])
|
if (state->dct_block_lists[i] == NULL)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue