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 | } |