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 }