#include "include/smackw32/smackw32.h"
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "harness/hooks.h"
#include "harness/trace.h"
// lib/libsmacker
#include "smacker.h"
#include "../miniaudio/include/miniaudio/miniaudio.h"
extern ma_engine miniaudio_engine; // defined in miniaudio_backend.c
extern int gSound_enabled; // defined in globvars.h
static uint32_t smack_last_frame_time = 0;
static void copy_palette(Smack* smack) {
const unsigned char* pal = smk_get_palette(smack->smk_handle);
memcpy(smack
->Palette
, pal
, 256 * 3);
}
Smack* SmackOpen(const char* name, uint32_t flags, uint32_t extrabuf) {
ma_format audioformat_ma;
ma_paged_audio_buffer_config paged_audio_buffer_config;
ma_data_converter_config data_converter_config;
unsigned char track_mask_smk;
unsigned char channels_smk[7];
unsigned char bitdepth_smk[7];
unsigned long sample_rate_smk[7];
double microsecs_per_frame;
Smack* smack;
double fps;
smk smk_handle = smk_open_file(name, SMK_MODE_MEMORY);
if (smk_handle == NULL) {
return NULL;
}
smack
= malloc(sizeof(Smack
));
// libsmacker doesn't tell us whether the palette is new on each frame or not, so just assume it always is new
smack->NewPalette = 1;
// smk_handle is added to hold a pointer to the underlying libsmacker instance
smack->smk_handle = smk_handle;
smk_info_all(smk_handle, NULL, &smack->Frames, µsecs_per_frame);
fps = 1000000.0 / microsecs_per_frame;
smack->MSPerFrame = (unsigned long) ((1 / fps) * 1000); // Pierre-Marie Baty -- added type cast
smk_info_video(smk_handle, &smack->Width, &smack->Height, NULL);
smk_enable_video(smk_handle, 1);
// initialize video sound if required and present
smack->audio_sample_rate = 0;
smack->audio_frame_size_in_bytes = 0;
smack->audio_paged_buffer = NULL;
smack->audio_paged_buffer_data = NULL;
smack->audio_converter = NULL;
smack->audio_track = NULL;
if (!gSound_enabled) {
goto no_audio;
}
// get info about the audio tracks in this video
smk_info_audio(smk_handle, &track_mask_smk, channels_smk, bitdepth_smk, sample_rate_smk);
if (!(track_mask_smk & SMK_AUDIO_TRACK_0)) {
LOG_INFO("Smacker file does not contain audio");
goto no_audio;
}
smack->audio_sample_rate = sample_rate_smk[0];
switch (bitdepth_smk[0]) {
case 8:
audioformat_ma = ma_format_u8;
smack->audio_frame_size_in_bytes = 1 * channels_smk[0];
break;
case 16:
audioformat_ma = ma_format_s16;
smack->audio_frame_size_in_bytes = 2 * channels_smk[0];
break;
case 24:
audioformat_ma = ma_format_s24;
smack->audio_frame_size_in_bytes = 3 * channels_smk[0];
break;
case 32:
audioformat_ma = ma_format_s32;
smack->audio_frame_size_in_bytes = 4 * channels_smk[0];
break;
default:
LOG_WARN("Smacker audio stream has invalid bit depth: %d", bitdepth_smk[0]);
goto no_audio;
}
// allocate and initialize paged buffer data
smack
->audio_paged_buffer_data
= malloc(sizeof(ma_paged_audio_buffer_data
));
if ((smack->audio_frame_size_in_bytes == 0) || (ma_paged_audio_buffer_data_init(audioformat_ma, channels_smk[0], smack->audio_paged_buffer_data) != MA_SUCCESS)) {
LOG_WARN("Failed to create paged audio buffer data");
goto no_audio;
}
// allocate and initialize paged buffer
smack
->audio_paged_buffer
= malloc(sizeof(ma_paged_audio_buffer
));
paged_audio_buffer_config = ma_paged_audio_buffer_config_init(smack->audio_paged_buffer_data);
if (ma_paged_audio_buffer_init (&paged_audio_buffer_config, smack->audio_paged_buffer) != MA_SUCCESS) {
LOG_WARN("Failed to create paged audio buffer for smacker audio stream");
goto no_audio;
}
// allocate and initialize sound
smack
->audio_track
= malloc(sizeof(ma_sound
));
if (ma_sound_init_from_data_source(&miniaudio_engine, smack->audio_paged_buffer, MA_SOUND_FLAG_NO_PITCH | MA_SOUND_FLAG_NO_SPATIALIZATION, NULL, smack->audio_track) != MA_SUCCESS) {
LOG_WARN("Failed to create sound from data source");
goto no_audio;
}
// allocate and initialize data converter if miniaudio engine and Smack file soundtrack sample rates differ
if (ma_engine_get_sample_rate(&miniaudio_engine) != sample_rate_smk[0]) {
smack
->audio_converter
= malloc(sizeof(ma_data_converter
));
data_converter_config = ma_data_converter_config_init(audioformat_ma, audioformat_ma, channels_smk[0], channels_smk[0], smack->audio_sample_rate, ma_engine_get_sample_rate (&miniaudio_engine));
if (ma_data_converter_init(&data_converter_config, NULL, smack->audio_converter) != MA_SUCCESS) {
LOG_WARN("Failed to create sound data converter");
goto no_audio;
}
}
// tell libsmacker we can process audio now
smk_enable_audio(smk_handle, 0, 1);
// load the first frame and return a handle to the Smack file
if (smk_first(smk_handle) == SMK_ERROR) {
smk_close(smk_handle);
return NULL;
}
copy_palette(smack);
return smack;
no_audio:
// no audio or failed to init audio, cleanup audio stuff
if (smack->audio_converter != NULL) {
free(smack
->audio_converter
);
smack->audio_converter = NULL;
}
if (smack->audio_track != NULL) {
ma_sound_uninit(smack->audio_track);
free(smack
->audio_track
);
smack->audio_track = NULL;
}
if (smack->audio_paged_buffer != NULL) {
ma_paged_audio_buffer_uninit(smack->audio_paged_buffer);
free(smack
->audio_paged_buffer
);
smack->audio_paged_buffer = NULL;
}
if (smack->audio_paged_buffer_data != NULL) {
ma_paged_audio_buffer_data_uninit (smack->audio_paged_buffer_data, NULL);
free(smack
->audio_paged_buffer_data
);
smack->audio_paged_buffer_data = NULL;
}
// load the first frame and return a handle to the Smack file
if (smk_first(smk_handle) == SMK_ERROR) {
smk_close(smk_handle);
return NULL;
}
copy_palette(smack);
return smack;
}
int SmackSoundUseDirectSound(void* dd) {
// TODO: do some miniaudio init
return 0;
}
void SmackToBuffer(Smack* smack, uint32_t left, uint32_t top, uint32_t pitch, uint32_t destheight, void* buf, uint32_t flags) {
unsigned long i; // Pierre-Marie Baty -- fixed type
// minimal implementation
char* char_buf = buf;
const unsigned char* frame = smk_get_video(smack->smk_handle);
for (i = 0; i < smack->Height; i++) {
memcpy(&char_buf
[(i
* pitch
)], &frame
[i
* smack
->Width
], smack
->Width
);
}
}
uint32_t SmackDoFrame(Smack* smack) {
const unsigned char *audio_data;
unsigned long audio_data_size;
ma_paged_audio_buffer_page *newPage;
ma_uint64 current_pos;
ma_uint64 nb_frames_in;
ma_uint64 nb_frames_out;
// process audio if we have some
if (smack->audio_track != NULL) {
audio_data = smk_get_audio (smack->smk_handle, 0);
audio_data_size = smk_get_audio_size (smack->smk_handle, 0);
if ((audio_data == NULL) || (audio_data_size == 0)) {
return 0;
}
if (ma_paged_audio_buffer_get_length_in_pcm_frames (smack->audio_paged_buffer, ¤t_pos) != MA_SUCCESS) {
LOG_WARN("ma_paged_audio_buffer_get_length_in_pcm_frames failed");
return 0;
}
// do we need to convert the sample frequency?
if (smack->audio_converter != NULL) {
nb_frames_in = audio_data_size / smack->audio_frame_size_in_bytes;
nb_frames_out = nb_frames_in * ma_engine_get_sample_rate (&miniaudio_engine) / smack->audio_sample_rate;
if (ma_paged_audio_buffer_data_allocate_page (smack->audio_paged_buffer_data, nb_frames_out, NULL, NULL, &newPage) != MA_SUCCESS) {
LOG_WARN("ma_paged_audio_buffer_data_allocate_page failed");
return 0;
}
else if (ma_data_converter_process_pcm_frames (smack->audio_converter, audio_data, &nb_frames_in, newPage->pAudioData, &nb_frames_out) != MA_SUCCESS) {
LOG_WARN("ma_data_converter_process_pcm_frames failed");
return 0;
}
else if (ma_paged_audio_buffer_data_append_page (smack->audio_paged_buffer_data, newPage) != MA_SUCCESS) {
LOG_WARN("ma_paged_audio_buffer_data_append_page failed");
return 0;
}
} else { // no sampling frequency conversion needed
if (ma_paged_audio_buffer_data_allocate_and_append_page(smack->audio_paged_buffer_data, (ma_uint32) (audio_data_size / (ma_uint64) smack->audio_frame_size_in_bytes), audio_data, NULL) != MA_SUCCESS) {
LOG_WARN ("ma_paged_audio_buffer_data_allocate_and_append_page failed");
return 0;
}
}
if (!ma_sound_is_playing (smack->audio_track))
{
// seek either at start, or where the accumulated value hasn't played yet
if (ma_sound_seek_to_pcm_frame (smack->audio_track, current_pos) != MA_SUCCESS) {
LOG_WARN("ma_sound_seek_to_pcm_frame failed");
}
if (ma_sound_start (smack->audio_track) != MA_SUCCESS) {
LOG_WARN("ma_sound_start failed");
}
}
if (ma_sound_at_end (smack->audio_track)) {
LOG_WARN ("video not playing fast enough: sound starved!");
}
}
return 0;
}
void SmackNextFrame(Smack* smack) {
smk_next(smack->smk_handle);
copy_palette(smack);
}
uint32_t SmackWait(Smack* smack) {
uint32_t now = gHarness_platform.GetTicks();
if (now < smack_last_frame_time + smack->MSPerFrame) {
gHarness_platform.Sleep(1);
return 1;
}
smack_last_frame_time = now;
return 0;
}
void SmackClose(Smack* smack) {
if (smack->audio_converter != NULL) {
ma_data_converter_uninit(smack->audio_converter, NULL);
free(smack
->audio_converter
);
}
if (smack->audio_track != NULL) {
ma_sound_stop(smack->audio_track);
ma_sound_uninit(smack->audio_track);
free(smack
->audio_track
);
}
if (smack->audio_paged_buffer != NULL) {
ma_paged_audio_buffer_uninit(smack->audio_paged_buffer);
free(smack
->audio_paged_buffer
);
}
if (smack->audio_paged_buffer_data != NULL) {
ma_paged_audio_buffer_data_uninit(smack->audio_paged_buffer_data, NULL);
free(smack
->audio_paged_buffer_data
);
}
smk_close(smack->smk_handle);
}