Rev 1 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 1 | pmbaty | 1 | #include "include/smackw32/smackw32.h" | 
| 2 | |||
| 3 | #include <assert.h> | ||
| 4 | #include <stddef.h> | ||
| 5 | #include <stdint.h> | ||
| 6 | #include <stdlib.h> | ||
| 7 | #include <string.h> | ||
| 8 | |||
| 9 | #include "harness/hooks.h" | ||
| 10 | #include "harness/trace.h" | ||
| 11 | // lib/libsmacker | ||
| 12 | #include "smacker.h" | ||
| 13 | |||
| 14 | #include "../miniaudio/include/miniaudio/miniaudio.h" | ||
| 15 | extern ma_engine miniaudio_engine; // defined in miniaudio_backend.c | ||
| 16 | extern int gSound_enabled; // defined in globvars.h | ||
| 17 | |||
| 18 | static uint32_t smack_last_frame_time = 0; | ||
| 19 | |||
| 20 | static void copy_palette(Smack* smack) { | ||
| 21 | const unsigned char* pal = smk_get_palette(smack->smk_handle); | ||
| 22 | memcpy(smack->Palette, pal, 256 * 3); | ||
| 23 | } | ||
| 24 | |||
| 25 | Smack* SmackOpen(const char* name, uint32_t flags, uint32_t extrabuf) { | ||
| 26 |     ma_format audioformat_ma; | ||
| 27 |     ma_paged_audio_buffer_config paged_audio_buffer_config; | ||
| 28 |     ma_data_converter_config data_converter_config; | ||
| 29 | unsigned char track_mask_smk; | ||
| 30 | unsigned char channels_smk[7]; | ||
| 31 | unsigned char bitdepth_smk[7]; | ||
| 32 | unsigned long sample_rate_smk[7]; | ||
| 33 | double microsecs_per_frame; | ||
| 34 | Smack* smack; | ||
| 35 | double fps; | ||
| 36 | |||
| 37 | smk smk_handle = smk_open_file(name, SMK_MODE_MEMORY); | ||
| 38 | if (smk_handle == NULL) { | ||
| 39 | return NULL; | ||
| 40 |     } | ||
| 41 | |||
| 42 | smack = malloc(sizeof(Smack)); | ||
| 43 | |||
| 44 |     // libsmacker doesn't tell us whether the palette is new on each frame or not, so just assume it always is new | ||
| 45 | smack->NewPalette = 1; | ||
| 46 | |||
| 47 |     // smk_handle is added to hold a pointer to the underlying libsmacker instance | ||
| 48 | smack->smk_handle = smk_handle; | ||
| 49 | |||
| 50 | smk_info_all(smk_handle, NULL, &smack->Frames, µsecs_per_frame); | ||
| 51 | fps = 1000000.0 / microsecs_per_frame; | ||
| 52 | smack->MSPerFrame = (unsigned long) ((1 / fps) * 1000); // Pierre-Marie Baty -- added type cast | ||
| 53 | smk_info_video(smk_handle, &smack->Width, &smack->Height, NULL); | ||
| 54 | smk_enable_video(smk_handle, 1); | ||
| 55 | |||
| 56 |     // initialize video sound if required and present | ||
| 57 | smack->audio_sample_rate = 0; | ||
| 58 | smack->audio_frame_size_in_bytes = 0; | ||
| 59 | smack->audio_paged_buffer = NULL; | ||
| 60 | smack->audio_paged_buffer_data = NULL; | ||
| 61 | smack->audio_converter = NULL; | ||
| 62 | smack->audio_track = NULL; | ||
| 63 | if (!gSound_enabled) { | ||
| 8 | pmbaty | 64 | goto no_audio; | 
| 1 | pmbaty | 65 |     } | 
| 66 | |||
| 67 |     // get info about the audio tracks in this video | ||
| 68 | smk_info_audio(smk_handle, &track_mask_smk, channels_smk, bitdepth_smk, sample_rate_smk); | ||
| 69 | if (!(track_mask_smk & SMK_AUDIO_TRACK_0)) { | ||
| 70 | LOG_INFO("Smacker file does not contain audio"); | ||
| 8 | pmbaty | 71 | goto no_audio; | 
| 1 | pmbaty | 72 |     } | 
| 73 | smack->audio_sample_rate = sample_rate_smk[0]; | ||
| 74 | switch (bitdepth_smk[0]) { | ||
| 75 | case 8: | ||
| 76 | audioformat_ma = ma_format_u8; | ||
| 77 | smack->audio_frame_size_in_bytes = 1 * channels_smk[0]; | ||
| 78 | break; | ||
| 79 | case 16: | ||
| 80 | audioformat_ma = ma_format_s16; | ||
| 81 | smack->audio_frame_size_in_bytes = 2 * channels_smk[0]; | ||
| 82 | break; | ||
| 83 | case 24: | ||
| 84 | audioformat_ma = ma_format_s24; | ||
| 85 | smack->audio_frame_size_in_bytes = 3 * channels_smk[0]; | ||
| 86 | break; | ||
| 87 | case 32: | ||
| 88 | audioformat_ma = ma_format_s32; | ||
| 89 | smack->audio_frame_size_in_bytes = 4 * channels_smk[0]; | ||
| 90 | break; | ||
| 91 | default: | ||
| 92 | LOG_WARN("Smacker audio stream has invalid bit depth: %d", bitdepth_smk[0]); | ||
| 8 | pmbaty | 93 | goto no_audio; | 
| 1 | pmbaty | 94 |     } | 
| 95 | |||
| 96 |     // allocate and initialize paged buffer data | ||
| 97 | smack->audio_paged_buffer_data = malloc(sizeof(ma_paged_audio_buffer_data)); | ||
| 98 | |||
| 99 | 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)) { | ||
| 100 | LOG_WARN("Failed to create paged audio buffer data"); | ||
| 8 | pmbaty | 101 | goto no_audio; | 
| 1 | pmbaty | 102 |     } | 
| 103 | |||
| 104 |     // allocate and initialize paged buffer | ||
| 105 | smack->audio_paged_buffer = malloc(sizeof(ma_paged_audio_buffer)); | ||
| 106 | |||
| 107 | paged_audio_buffer_config = ma_paged_audio_buffer_config_init(smack->audio_paged_buffer_data); | ||
| 108 | if (ma_paged_audio_buffer_init (&paged_audio_buffer_config, smack->audio_paged_buffer) != MA_SUCCESS) { | ||
| 109 | LOG_WARN("Failed to create paged audio buffer for smacker audio stream"); | ||
| 8 | pmbaty | 110 | goto no_audio; | 
| 1 | pmbaty | 111 |     } | 
| 112 | |||
| 113 |     // allocate and initialize sound | ||
| 114 | smack->audio_track = malloc(sizeof(ma_sound)); | ||
| 115 | |||
| 116 | 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) { | ||
| 117 | LOG_WARN("Failed to create sound from data source"); | ||
| 8 | pmbaty | 118 | goto no_audio; | 
| 1 | pmbaty | 119 |     } | 
| 120 | |||
| 8 | pmbaty | 121 |     // allocate and initialize data converter if miniaudio engine and Smack file soundtrack sample rates differ | 
| 122 | if (ma_engine_get_sample_rate(&miniaudio_engine) != sample_rate_smk[0]) { | ||
| 123 | smack->audio_converter = malloc(sizeof(ma_data_converter)); | ||
| 1 | pmbaty | 124 | |
| 8 | pmbaty | 125 | 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)); | 
| 126 | if (ma_data_converter_init(&data_converter_config, NULL, smack->audio_converter) != MA_SUCCESS) { | ||
| 127 | LOG_WARN("Failed to create sound data converter"); | ||
| 128 | goto no_audio; | ||
| 129 |         } | ||
| 1 | pmbaty | 130 |     } | 
| 131 | |||
| 132 |     // tell libsmacker we can process audio now | ||
| 133 | smk_enable_audio(smk_handle, 0, 1); | ||
| 8 | pmbaty | 134 | |
| 135 |     // load the first frame and return a handle to the Smack file | ||
| 136 | if (smk_first(smk_handle) == SMK_ERROR) { | ||
| 137 | smk_close(smk_handle); | ||
| 138 | free(smack); | ||
| 139 | return NULL; | ||
| 140 |     } | ||
| 141 | copy_palette(smack); | ||
| 1 | pmbaty | 142 | return smack; | 
| 143 | |||
| 8 | pmbaty | 144 | no_audio: | 
| 145 |     // no audio or failed to init audio, cleanup audio stuff | ||
| 1 | pmbaty | 146 | if (smack->audio_converter != NULL) { | 
| 147 | free(smack->audio_converter); | ||
| 148 | smack->audio_converter = NULL; | ||
| 149 |     } | ||
| 150 | if (smack->audio_track != NULL) { | ||
| 151 | ma_sound_uninit(smack->audio_track); | ||
| 152 | free(smack->audio_track); | ||
| 153 | smack->audio_track = NULL; | ||
| 154 |     } | ||
| 155 | if (smack->audio_paged_buffer != NULL) { | ||
| 156 | ma_paged_audio_buffer_uninit(smack->audio_paged_buffer); | ||
| 157 | free(smack->audio_paged_buffer); | ||
| 158 | smack->audio_paged_buffer = NULL; | ||
| 159 |     } | ||
| 160 | if (smack->audio_paged_buffer_data != NULL) { | ||
| 161 | ma_paged_audio_buffer_data_uninit (smack->audio_paged_buffer_data, NULL); | ||
| 162 | free(smack->audio_paged_buffer_data); | ||
| 163 | smack->audio_paged_buffer_data = NULL; | ||
| 164 |     } | ||
| 8 | pmbaty | 165 | |
| 166 |     // load the first frame and return a handle to the Smack file | ||
| 167 | if (smk_first(smk_handle) == SMK_ERROR) { | ||
| 168 | smk_close(smk_handle); | ||
| 169 | free(smack); | ||
| 170 | return NULL; | ||
| 171 |     } | ||
| 172 | copy_palette(smack); | ||
| 1 | pmbaty | 173 | return smack; | 
| 174 | } | ||
| 175 | |||
| 176 | int SmackSoundUseDirectSound(void* dd) { | ||
| 177 |     // TODO: do some miniaudio init | ||
| 178 | |||
| 179 | return 0; | ||
| 180 | } | ||
| 181 | |||
| 182 | void SmackToBuffer(Smack* smack, uint32_t left, uint32_t top, uint32_t pitch, uint32_t destheight, void* buf, uint32_t flags) { | ||
| 183 | unsigned long i; // Pierre-Marie Baty -- fixed type | ||
| 184 | |||
| 185 |     // minimal implementation | ||
| 186 | assert(left == 0); | ||
| 187 | assert(top == 0); | ||
| 188 | assert(flags == 0); | ||
| 189 | |||
| 190 | char* char_buf = buf; | ||
| 191 | |||
| 192 | const unsigned char* frame = smk_get_video(smack->smk_handle); | ||
| 193 | for (i = 0; i < smack->Height; i++) { | ||
| 194 | memcpy(&char_buf[(i * pitch)], &frame[i * smack->Width], smack->Width); | ||
| 195 |     } | ||
| 196 | } | ||
| 197 | |||
| 198 | uint32_t SmackDoFrame(Smack* smack) { | ||
| 199 | const unsigned char *audio_data; | ||
| 200 | unsigned long audio_data_size; | ||
| 201 | ma_paged_audio_buffer_page *newPage; | ||
| 202 |     ma_uint64 current_pos; | ||
| 203 |     ma_uint64 nb_frames_in; | ||
| 204 |     ma_uint64 nb_frames_out; | ||
| 205 | |||
| 206 |     // process audio if we have some | ||
| 8 | pmbaty | 207 | if (smack->audio_track != NULL) { | 
| 1 | pmbaty | 208 | audio_data = smk_get_audio (smack->smk_handle, 0); | 
| 209 | audio_data_size = smk_get_audio_size (smack->smk_handle, 0); | ||
| 210 | if ((audio_data == NULL) || (audio_data_size == 0)) { | ||
| 211 | return 0; | ||
| 212 |         } | ||
| 213 | |||
| 214 | if (ma_paged_audio_buffer_get_length_in_pcm_frames (smack->audio_paged_buffer, ¤t_pos) != MA_SUCCESS) { | ||
| 215 | LOG_WARN("ma_paged_audio_buffer_get_length_in_pcm_frames failed"); | ||
| 216 | return 0; | ||
| 217 |         } | ||
| 218 | |||
| 8 | pmbaty | 219 |         // do we need to convert the sample frequency? | 
| 220 | if (smack->audio_converter != NULL) { | ||
| 221 | nb_frames_in = audio_data_size / smack->audio_frame_size_in_bytes; | ||
| 222 | nb_frames_out = nb_frames_in * ma_engine_get_sample_rate (&miniaudio_engine) / smack->audio_sample_rate; | ||
| 1 | pmbaty | 223 | |
| 8 | pmbaty | 224 | if (ma_paged_audio_buffer_data_allocate_page (smack->audio_paged_buffer_data, nb_frames_out, NULL, NULL, &newPage) != MA_SUCCESS) { | 
| 225 | LOG_WARN("ma_paged_audio_buffer_data_allocate_page failed"); | ||
| 226 | return 0; | ||
| 227 |             } | ||
| 228 | else if (ma_data_converter_process_pcm_frames (smack->audio_converter, audio_data, &nb_frames_in, newPage->pAudioData, &nb_frames_out) != MA_SUCCESS) { | ||
| 229 | LOG_WARN("ma_data_converter_process_pcm_frames failed"); | ||
| 230 | return 0; | ||
| 231 |             } | ||
| 232 | else if (ma_paged_audio_buffer_data_append_page (smack->audio_paged_buffer_data, newPage) != MA_SUCCESS) { | ||
| 233 | LOG_WARN("ma_paged_audio_buffer_data_append_page failed"); | ||
| 234 | return 0; | ||
| 235 |             } | ||
| 236 | } else { // no sampling frequency conversion needed | ||
| 237 | 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) { | ||
| 238 | LOG_WARN ("ma_paged_audio_buffer_data_allocate_and_append_page failed"); | ||
| 239 | return 0; | ||
| 240 |             } | ||
| 1 | pmbaty | 241 |         } | 
| 242 | |||
| 8 | pmbaty | 243 | if (!ma_sound_is_playing (smack->audio_track)) | 
| 1 | pmbaty | 244 |         { | 
| 8 | pmbaty | 245 |             // seek either at start, or where the accumulated value hasn't played yet | 
| 246 | if (ma_sound_seek_to_pcm_frame (smack->audio_track, current_pos) != MA_SUCCESS) { | ||
| 247 | LOG_WARN("ma_sound_seek_to_pcm_frame failed"); | ||
| 1 | pmbaty | 248 |             } | 
| 8 | pmbaty | 249 | if (ma_sound_start (smack->audio_track) != MA_SUCCESS) { | 
| 250 | LOG_WARN("ma_sound_start failed"); | ||
| 1 | pmbaty | 251 |             } | 
| 252 |         } | ||
| 8 | pmbaty | 253 | if (ma_sound_at_end (smack->audio_track)) { | 
| 254 | LOG_WARN ("video not playing fast enough: sound starved!"); | ||
| 255 |         } | ||
| 1 | pmbaty | 256 |     } | 
| 257 | |||
| 258 | return 0; | ||
| 259 | } | ||
| 260 | |||
| 261 | void SmackNextFrame(Smack* smack) { | ||
| 262 | smk_next(smack->smk_handle); | ||
| 263 | copy_palette(smack); | ||
| 264 | } | ||
| 265 | |||
| 266 | uint32_t SmackWait(Smack* smack) { | ||
| 267 | uint32_t now = gHarness_platform.GetTicks(); | ||
| 268 | if (now < smack_last_frame_time + smack->MSPerFrame) { | ||
| 269 | gHarness_platform.Sleep(1); | ||
| 270 | return 1; | ||
| 271 |     } | ||
| 272 | smack_last_frame_time = now; | ||
| 273 | return 0; | ||
| 274 | } | ||
| 275 | |||
| 276 | void SmackClose(Smack* smack) { | ||
| 277 | if (smack->audio_converter != NULL) { | ||
| 278 | ma_data_converter_uninit(smack->audio_converter, NULL); | ||
| 279 | free(smack->audio_converter); | ||
| 280 |     } | ||
| 281 | if (smack->audio_track != NULL) { | ||
| 282 | ma_sound_stop(smack->audio_track); | ||
| 283 | ma_sound_uninit(smack->audio_track); | ||
| 284 | free(smack->audio_track); | ||
| 285 |     } | ||
| 286 | if (smack->audio_paged_buffer != NULL) { | ||
| 287 | ma_paged_audio_buffer_uninit(smack->audio_paged_buffer); | ||
| 288 | free(smack->audio_paged_buffer); | ||
| 289 |     } | ||
| 290 | if (smack->audio_paged_buffer_data != NULL) { | ||
| 291 | ma_paged_audio_buffer_data_uninit(smack->audio_paged_buffer_data, NULL); | ||
| 292 | free(smack->audio_paged_buffer_data); | ||
| 293 |     } | ||
| 294 | smk_close(smack->smk_handle); | ||
| 295 | free(smack); | ||
| 296 | } |