Krita Source Code Documentation
Loading...
Searching...
No Matches
KisMLTProducerKrita.cpp
Go to the documentation of this file.
1/*
2 * KisMLTProducerKrita
3 * Produces variable-speed audio within a restricted range of frames. Used internally by Krita to drive audio-synced animation playback.
4 * Copyright (C) 2022 Eoin O'Neill <eoinoneill1991@gmail.com>
5 * Copyright (C) 2022 Emmet O'Neill <emmetoneill.pdx@gmail.com>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22#include "KisMLTProducerKrita.h"
23
24#include <framework/mlt.h>
25#include <limits.h>
26#include <math.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31
32#include "kis_assert.h"
33
34#include <framework/mlt_factory.h>
35#include <framework/mlt_frame.h>
36#include <framework/mlt_producer.h>
37#include <framework/mlt_property.h>
38#include <framework/mlt_service.h>
39
40typedef struct
41{
42 mlt_producer producer_internal;
43 int audio_sample_rate = 0;
44 bool force_reset_audio_frequency_for_frames = false; // used for 'count' producer
46
49static int restrict_range(int index, int min, int max)
50{
51 const int span = max - min;
52 return (MAX(index - min, 0) % (span + 1)) + min;
53}
54
55static int is_valid_range(const int frame_start, const int frame_end)
56{
57 const bool NON_NEGATIVE = frame_start >= 0 && frame_end >= 0;
58 const bool NON_INVERTED = frame_end > frame_start;
59
60 return NON_NEGATIVE && NON_INVERTED;
61}
62
63void scale_audio_frequency(mlt_producer producer, mlt_audio audio)
64{
65 mlt_properties props = MLT_PRODUCER_PROPERTIES(producer);
66
67 // Scale the frequency to account for the dynamic speed (normalized).
68 double SPEED = mlt_properties_get_double(props, "speed");
69
71 SPEED = 1.0;
72 }
73
74 audio->frequency = (double) audio->frequency * fabs(SPEED);
75
76 KIS_SAFE_ASSERT_RECOVER(audio->frequency > 0) {
77 audio->frequency = 1;
78 }
79
80 if (SPEED < 0.0) {
81 mlt_audio_reverse(audio);
82 }
83}
84
85static int producer_get_audio(mlt_frame frame,
86 void **buffer,
87 mlt_audio_format *format,
88 int *frequency,
89 int *channels,
90 int *samples)
91{
92 mlt_producer producer = static_cast<mlt_producer>(mlt_frame_pop_audio(frame));
93 private_data *pdata = (private_data *) producer->child;
94
95 struct mlt_audio_s audio;
96
105 *frequency = -1;
106 *samples = -1;
107 }
108
109 mlt_audio_set_values(&audio, *buffer, *frequency, *format, *samples, *channels);
110
111 int error = mlt_frame_get_audio(frame,
112 &audio.data,
113 &audio.format,
114 &audio.frequency,
115 &audio.channels,
116 &audio.samples);
117
118 scale_audio_frequency(producer, &audio);
119 mlt_audio_get_values(&audio, buffer, frequency, format, samples, channels);
120
121 return error;
122}
123
124static int producer_generate_silent_audio(mlt_frame frame,
125 void **buffer,
126 mlt_audio_format *format,
127 int *frequency,
128 int *channels,
129 int *samples)
130{
131 mlt_producer producer = static_cast<mlt_producer>(mlt_frame_pop_audio(frame));
132 private_data *pdata = (private_data *) producer->child;
133
134 {
135 // Get the producer fps
136 double fps = mlt_producer_get_fps(producer);
137
138 if (mlt_properties_get(MLT_FRAME_PROPERTIES(frame), "producer_consumer_fps"))
139 fps = mlt_properties_get_double(MLT_FRAME_PROPERTIES(frame), "producer_consumer_fps");
140
141 mlt_position position = mlt_properties_get_position(MLT_FRAME_PROPERTIES(frame), "_position");
142
143 int size = 0;
144 *channels = *channels <= 0 ? 2 : *channels;
145 *frequency = pdata->audio_sample_rate > 0 ? pdata->audio_sample_rate : 44100;
146 *samples = mlt_audio_calculate_frame_samples(fps, *frequency, position); //NOTE: Audio BUFFER_SIZE
147 *format = *format == mlt_audio_none ? mlt_audio_s16 : *format;
148
149 size = mlt_audio_format_size(*format, *samples, *channels);
150 if (size)
151 *buffer = mlt_pool_alloc(size);
152 else
153 *buffer = NULL;
154
162 mlt_frame_set_audio(frame, *buffer, *format, size, mlt_pool_release);
163 }
164
165 struct mlt_audio_s audio;
166 mlt_audio_set_values(&audio, *buffer, *frequency, *format, *samples, *channels);
167 mlt_audio_silence(&audio, *samples, 0);
168
169 scale_audio_frequency(producer, &audio);
170
171 mlt_audio_get_values(&audio, buffer, frequency, format, samples, channels);
172
173 return 0;
174}
175
176static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index)
177{
178 mlt_properties props = MLT_PRODUCER_PROPERTIES(producer);
179 const int FRAME_START = mlt_properties_get_int(props, "start_frame");
180 const int FRAME_END = mlt_properties_get_int(props, "end_frame");
181 const bool IS_RANGE_LIMITED = mlt_properties_get_int(props, "limit_enabled");
182
183 private_data *pdata = (private_data *) producer->child;
184 const int POSITION = mlt_producer_position(pdata->producer_internal);
185
186 if (IS_RANGE_LIMITED && is_valid_range(FRAME_START, FRAME_END)) {
187 mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(pdata->producer_internal),
188 "_position",
189 restrict_range(POSITION, FRAME_START, FRAME_END));
190 }
191
192 int retval = mlt_service_get_frame((mlt_service) pdata->producer_internal, frame, index);
193
194 if (!mlt_frame_is_test_audio(*frame)) {
195 mlt_frame_push_audio(*frame, producer);
196 mlt_frame_push_audio(*frame, (void*)producer_get_audio);
197 } else {
211 mlt_frame_push_audio(*frame, producer);
212 mlt_frame_push_audio(*frame, (void*)producer_generate_silent_audio);
213
214 mlt_properties properties = MLT_FRAME_PROPERTIES(*frame);
215 mlt_properties_set_int(properties, "test_audio", 0);
216 }
217
218 return retval;
219}
220
221static void producer_property_changed( mlt_service owner, mlt_producer self, mlt_event_data event_data)
222{
223 const char *name = mlt_event_data_to_string(event_data);
224 if (!name) return;
225
231 if (strcmp(name, "_speed") == 0) {
232 const double speed = mlt_producer_get_speed(self);
233 private_data* pdata = (private_data*)self->child;
234 mlt_producer_set_speed(pdata->producer_internal, speed);
235 }
236}
237
238static int producer_seek(mlt_producer producer, mlt_position position)
239{
240 private_data *pdata = (private_data *) producer->child;
241
242 int retval = mlt_producer_seek(pdata->producer_internal, position);
243
247 mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(producer),
248 "_position",
249 position);
250 mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(producer),
251 "_frame",
252 position);
253
254 return retval;
255}
256
257static void producer_close(mlt_producer producer)
258{
259 private_data *pdata = (private_data *) producer->child;
260
261 if (pdata) {
262 mlt_producer_close(pdata->producer_internal);
263 free(pdata);
264 }
265
266 producer->close = NULL;
267 mlt_producer_close(producer);
268 free(producer);
269}
270
273extern "C" void* producer_krita_init(mlt_profile profile,
274 mlt_service_type type,
275 const char *id,
276 const void *arg)
277{
278 // Create a new producer object
279 mlt_producer producer = mlt_producer_new(profile);
280 private_data *pdata = (private_data *) calloc(1, sizeof(private_data));
281
282 if (arg && producer && pdata) {
283 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer);
284
285 // Initialize the producer
286 mlt_properties_set(producer_properties, "resource", (char*)arg);
287 producer->child = pdata;
288 producer->get_frame = producer_get_frame;
289 producer->seek = producer_seek;
290 producer->close = (mlt_destructor) producer_close;
291
292 // Get the resource to be passed to the clip producer
293 char *resource = (char*)arg;
294
295 // Create internal producer
296 pdata->producer_internal = mlt_factory_producer(profile, "abnormal", resource);
297
298 if (pdata->producer_internal) {
299 mlt_producer_set_speed(pdata->producer_internal, 1.0);
300 mlt_properties internalProducerProps = MLT_PRODUCER_PROPERTIES(pdata->producer_internal);
301
307 mlt_properties_set_string(internalProducerProps, "eof", "continue");
308
309 const char *serviceName = mlt_properties_get(internalProducerProps, "mlt_service");
310
311 if (!strcmp(serviceName, "avformat")) {
320 mlt_properties_set_int(internalProducerProps, "noimagecache", 1);
321
327 char key[200];
328 const int numberOfStreams = mlt_properties_get_int(internalProducerProps, "meta.media.nb_streams");
329
330 for (int i = 0; i < numberOfStreams; i++) {
331 snprintf(key, sizeof(key), "meta.media.%u.stream.type", i);
332
333 const char* type = mlt_properties_get(internalProducerProps, key);
334 if (type && !strcmp(type, "audio")) {
335 snprintf(key, sizeof(key), "meta.media.%u.codec.sample_rate", i);
336 pdata->audio_sample_rate = mlt_properties_get_int(internalProducerProps, key);
337 }
338 }
339 } else if (!strcmp(serviceName, "count")) {
340 pdata->audio_sample_rate = 48000;
342 } else {
343 KIS_SAFE_ASSERT_RECOVER_NOOP(0 && "mlt_service used for media is unknown!");
344 }
345 }
346
347 mlt_properties_set_string(producer_properties, "eof", "continue");
348
349 mlt_events_listen( producer_properties, producer, "property-changed", ( mlt_listener )producer_property_changed );
350 }
351
352 const bool INVALID_CONTEXT = !producer || !pdata || !pdata->producer_internal;
353 if (INVALID_CONTEXT) { // Clean up early...
354 if (pdata) {
355 mlt_producer_close(pdata->producer_internal);
356 free(pdata);
357 }
358
359 if (producer) {
360 producer->child = NULL;
361 producer->close = NULL;
362 mlt_producer_close(producer);
363 free(producer);
364 producer = NULL;
365 }
366 }
367
368 return producer;
369}
370
371void registerKritaMLTProducer(Mlt::Repository *repository)
372{
373 repository->register_service(mlt_service_producer_type, "krita_play_chunk", producer_krita_init);
374}
static void producer_property_changed(mlt_service owner, mlt_producer self, mlt_event_data event_data)
void registerKritaMLTProducer(Mlt::Repository *repository)
static int producer_seek(mlt_producer producer, mlt_position position)
static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index)
static int restrict_range(int index, int min, int max)
void scale_audio_frequency(mlt_producer producer, mlt_audio audio)
static int is_valid_range(const int frame_start, const int frame_end)
static void producer_close(mlt_producer producer)
static int producer_get_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples)
static int producer_generate_silent_audio(mlt_frame frame, void **buffer, mlt_audio_format *format, int *frequency, int *channels, int *samples)
void * producer_krita_init(mlt_profile profile, mlt_service_type type, const char *id, const void *arg)
static bool qFuzzyIsNull(half h)
#define KIS_SAFE_ASSERT_RECOVER(cond)
Definition kis_assert.h:126
#define KIS_SAFE_ASSERT_RECOVER_NOOP(cond)
Definition kis_assert.h:130
#define MAX(a, b)
bool force_reset_audio_frequency_for_frames
mlt_producer producer_internal