File indexing completed on 2024-05-19 04:29:16
0001 /* 0002 * KisMLTProducerKrita 0003 * Produces variable-speed audio within a restricted range of frames. Used internally by Krita to drive audio-synced animation playback. 0004 * Copyright (C) 2022 Eoin O'Neill <eoinoneill1991@gmail.com> 0005 * Copyright (C) 2022 Emmet O'Neill <emmetoneill.pdx@gmail.com> 0006 * 0007 * This library is free software; you can redistribute it and/or 0008 * modify it under the terms of the GNU Lesser General Public 0009 * License as published by the Free Software Foundation; either 0010 * version 2.1 of the License, or (at your option) any later version. 0011 * 0012 * This library is distributed in the hope that it will be useful, 0013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 * Lesser General Public License for more details. 0016 * 0017 * You should have received a copy of the GNU Lesser General Public 0018 * License along with this library; if not, write to the Free Software 0019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0020 */ 0021 0022 #include "KisMLTProducerKrita.h" 0023 0024 #include <framework/mlt.h> 0025 #include <limits.h> 0026 #include <math.h> 0027 #include <stdbool.h> 0028 #include <stdio.h> 0029 #include <stdlib.h> 0030 #include <string.h> 0031 0032 #include "kis_assert.h" 0033 0034 #include <framework/mlt_factory.h> 0035 #include <framework/mlt_frame.h> 0036 #include <framework/mlt_producer.h> 0037 #include <framework/mlt_property.h> 0038 #include <framework/mlt_service.h> 0039 0040 typedef struct 0041 { 0042 mlt_producer producer_internal; 0043 } private_data; 0044 0045 /** Restricts frame index to within range by modulus wrapping (not clamping). 0046 */ 0047 static int restrict_range(int index, int min, int max) 0048 { 0049 const int span = max - min; 0050 return (MAX(index - min, 0) % (span + 1)) + min; 0051 } 0052 0053 static int is_valid_range(const int frame_start, const int frame_end) 0054 { 0055 const bool NON_NEGATIVE = frame_start >= 0 && frame_end >= 0; 0056 const bool NON_INVERTED = frame_end > frame_start; 0057 0058 return NON_NEGATIVE && NON_INVERTED; 0059 } 0060 0061 static int producer_get_audio(mlt_frame frame, 0062 void **buffer, 0063 mlt_audio_format *format, 0064 int *frequency, 0065 int *channels, 0066 int *samples) 0067 { 0068 mlt_producer producer = static_cast<mlt_producer>(mlt_frame_pop_audio(frame)); 0069 0070 struct mlt_audio_s audio; 0071 0072 mlt_audio_set_values(&audio, *buffer, *frequency, *format, *samples, *channels); 0073 0074 int error = mlt_frame_get_audio(frame, 0075 &audio.data, 0076 &audio.format, 0077 &audio.frequency, 0078 &audio.channels, 0079 &audio.samples); 0080 0081 mlt_properties props = MLT_PRODUCER_PROPERTIES(producer); 0082 0083 // Scale the frequency to account for the dynamic speed (normalized). 0084 double SPEED = mlt_properties_get_double(props, "speed"); 0085 0086 KIS_SAFE_ASSERT_RECOVER(!qFuzzyIsNull(SPEED)) { 0087 SPEED = 1.0; 0088 } 0089 0090 audio.frequency = (double) audio.frequency * fabs(SPEED); 0091 if (SPEED < 0.0) { 0092 mlt_audio_reverse(&audio); 0093 } 0094 0095 mlt_audio_get_values(&audio, buffer, frequency, format, samples, channels); 0096 0097 return error; 0098 } 0099 0100 static int producer_get_frame(mlt_producer producer, mlt_frame_ptr frame, int index) 0101 { 0102 mlt_properties props = MLT_PRODUCER_PROPERTIES(producer); 0103 const int FRAME_START = mlt_properties_get_int(props, "start_frame"); 0104 const int FRAME_END = mlt_properties_get_int(props, "end_frame"); 0105 const bool IS_RANGE_LIMITED = mlt_properties_get_int(props, "limit_enabled"); 0106 0107 private_data *pdata = (private_data *) producer->child; 0108 const int POSITION = mlt_producer_position(pdata->producer_internal); 0109 0110 if (IS_RANGE_LIMITED && is_valid_range(FRAME_START, FRAME_END)) { 0111 mlt_properties_set_position(MLT_PRODUCER_PROPERTIES(pdata->producer_internal), 0112 "_position", 0113 restrict_range(POSITION, FRAME_START, FRAME_END)); 0114 } 0115 0116 int retval = mlt_service_get_frame((mlt_service) pdata->producer_internal, frame, index); 0117 0118 if (!mlt_frame_is_test_audio(*frame)) { 0119 mlt_frame_push_audio(*frame, producer); 0120 mlt_frame_push_audio(*frame, (void*)producer_get_audio); 0121 } 0122 0123 return retval; 0124 } 0125 0126 static void updateInternalProducesEofStatus(mlt_producer producer_internal, int effectiveLimitEnabled) 0127 { 0128 const char *effectiveEofMode = effectiveLimitEnabled ? "continue" : "pause"; 0129 0130 mlt_properties internalProducerProps = MLT_PRODUCER_PROPERTIES(producer_internal); 0131 0132 const char *currentEofMode = mlt_properties_get(internalProducerProps, "eof"); 0133 0134 if (!currentEofMode || strcmp(currentEofMode, effectiveEofMode) != 0) { 0135 mlt_properties_set_string(internalProducerProps, "eof", effectiveEofMode); 0136 } 0137 } 0138 0139 static void producer_property_changed( mlt_service owner, mlt_producer self, mlt_event_data event_data) 0140 { 0141 const char *name = mlt_event_data_to_string(event_data); 0142 if (!name) return; 0143 0144 if (strcmp(name, "start_frame") == 0 || 0145 strcmp(name, "end_frame" ) == 0 || 0146 strcmp(name, "limit_enabled") == 0) { 0147 0148 mlt_properties props = MLT_PRODUCER_PROPERTIES(self); 0149 0150 const int effectiveLimitEnabled = 0151 is_valid_range(mlt_properties_get_int(props, "start_frame"), 0152 mlt_properties_get_int(props, "end_frame")) && 0153 mlt_properties_get_int(props, "limit_enabled"); 0154 0155 private_data* pdata = (private_data*)self->child; 0156 updateInternalProducesEofStatus(pdata->producer_internal, effectiveLimitEnabled); 0157 } 0158 } 0159 0160 static int producer_seek(mlt_producer producer, mlt_position position) 0161 { 0162 private_data *pdata = (private_data *) producer->child; 0163 0164 int retval = mlt_producer_seek(pdata->producer_internal, position); 0165 0166 return retval; 0167 } 0168 0169 static void producer_close(mlt_producer producer) 0170 { 0171 private_data *pdata = (private_data *) producer->child; 0172 0173 if (pdata) { 0174 mlt_producer_close(pdata->producer_internal); 0175 free(pdata); 0176 } 0177 0178 producer->close = NULL; 0179 mlt_producer_close(producer); 0180 free(producer); 0181 } 0182 0183 /** Constructor for the producer. 0184 */ 0185 extern "C" void* producer_krita_init(mlt_profile profile, 0186 mlt_service_type type, 0187 const char *id, 0188 const void *arg) 0189 { 0190 // Create a new producer object 0191 mlt_producer producer = mlt_producer_new(profile); 0192 private_data *pdata = (private_data *) calloc(1, sizeof(private_data)); 0193 0194 if (arg && producer && pdata) { 0195 mlt_properties producer_properties = MLT_PRODUCER_PROPERTIES(producer); 0196 0197 // Initialize the producer 0198 mlt_properties_set(producer_properties, "resource", (char*)arg); 0199 producer->child = pdata; 0200 producer->get_frame = producer_get_frame; 0201 producer->seek = producer_seek; 0202 producer->close = (mlt_destructor) producer_close; 0203 0204 // Get the resource to be passed to the clip producer 0205 char *resource = (char*)arg; 0206 0207 // Create internal producer 0208 pdata->producer_internal = mlt_factory_producer(profile, "abnormal", resource); 0209 0210 if (pdata->producer_internal) { 0211 mlt_producer_set_speed(pdata->producer_internal, 1.0); 0212 } 0213 0214 mlt_events_listen( producer_properties, producer, "property-changed", ( mlt_listener )producer_property_changed ); 0215 updateInternalProducesEofStatus(pdata->producer_internal, 0); 0216 } 0217 0218 const bool INVALID_CONTEXT = !producer || !pdata || !pdata->producer_internal; 0219 if (INVALID_CONTEXT) { // Clean up early... 0220 if (pdata) { 0221 mlt_producer_close(pdata->producer_internal); 0222 free(pdata); 0223 } 0224 0225 if (producer) { 0226 producer->child = NULL; 0227 producer->close = NULL; 0228 mlt_producer_close(producer); 0229 free(producer); 0230 producer = NULL; 0231 } 0232 } 0233 0234 return producer; 0235 } 0236 0237 void registerKritaMLTProducer(Mlt::Repository *repository) 0238 { 0239 repository->register_service(mlt_service_producer_type, "krita_play_chunk", producer_krita_init); 0240 }