23 #include <AppConfig.h>
24 #include <juce_audio_basics/juce_audio_basics.h>
25 #include <juce_audio_devices/juce_audio_devices.h>
27 #include <QApplication>
35 #include <QHBoxLayout>
45 Frame::Frame(int64_t number,
int width,
int height, std::string color,
int samples,
int channels)
46 : audio(std::make_shared<
juce::AudioBuffer<float>>(channels, samples)),
47 number(number), capture_timestamp(std::numeric_limits<double>::quiet_NaN()), width(width), height(height),
48 pixel_ratio(1,1), color(color),
51 has_audio_data(false), has_image_data(false),
52 max_audio_sample(0), audio_is_increasing(true)
62 Frame::Frame(int64_t number,
int width,
int height, std::string color)
63 :
Frame::
Frame(number, width, height, color, 0, 2) {}
67 :
Frame::
Frame(number, 1, 1,
"#000000", samples, channels) {}
91 channels = other.channels;
93 height = other.height;
94 channel_layout = other.channel_layout;
97 sample_rate = other.sample_rate;
98 pixel_ratio =
Fraction(other.pixel_ratio.
num, other.pixel_ratio.
den);
100 max_audio_sample = other.max_audio_sample;
101 audio_is_increasing = other.audio_is_increasing;
104 image = std::make_shared<QImage>(*(other.image));
106 audio = std::make_shared<juce::AudioBuffer<float>>(*(other.
audio));
107 if (other.wave_image)
108 wave_image = std::make_shared<QImage>(*(other.wave_image));
124 if (!QApplication::instance()) {
127 static char* argv[1] = {NULL};
128 previewApp = std::make_shared<QApplication>(argc, argv);
132 std::shared_ptr<QImage> previewImage =
GetImage();
135 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
138 previewImage = std::make_shared<QImage>(previewImage->scaled(
139 previewImage->size().width(), previewImage->size().height() * pixel_ratio.
Reciprocal().
ToDouble(),
140 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
144 QWidget previewWindow;
145 previewWindow.setStyleSheet(
"background-color: #000000;");
150 previewLabel.setPixmap(QPixmap::fromImage(*previewImage));
151 previewLabel.setMask(QPixmap::fromImage(*previewImage).mask());
152 layout.addWidget(&previewLabel);
155 previewWindow.setLayout(&layout);
156 previewWindow.show();
161 std::shared_ptr<QImage>
Frame::GetWaveform(
int width,
int height,
int Red,
int Green,
int Blue,
int Alpha)
167 QVector<QPointF> lines;
168 QVector<QPointF> labels;
172 if (total_samples > 0)
175 int new_height = 200 *
audio->getNumChannels();
176 int height_padding = 20 * (
audio->getNumChannels() - 1);
177 int total_height = new_height + height_padding;
179 float zero_height = 1.0;
183 for (
int channel = 0; channel <
audio->getNumChannels(); channel++)
188 const float *samples =
audio->getReadPointer(channel);
193 float value = samples[sample] * 100.0;
197 if (value > -zero_height && value < 0.0) {
198 value = -zero_height;
199 }
else if (value > 0.0 && value < zero_height) {
204 lines.push_back(QPointF(X, Y));
205 lines.push_back(QPointF(X, Y - value));
209 labels.push_back(QPointF(5.0, Y - 5.0));
212 Y += (200 + height_padding);
217 wave_image = std::make_shared<QImage>(
218 total_width, total_height, QImage::Format_RGBA8888_Premultiplied);
219 wave_image->fill(QColor(0,0,0,0));
222 QPainter painter(wave_image.get());
226 pen.setColor(QColor(Red, Green, Blue, Alpha));
228 pen.setStyle(Qt::SolidLine);
232 painter.drawLines(lines);
238 wave_image = std::make_shared<QImage>(width, height, QImage::Format_RGBA8888_Premultiplied);
239 wave_image->fill(QColor(QString::fromStdString(
"#000000")));
243 if (wave_image->width() != width || wave_image->height() != height) {
244 QImage scaled_wave_image = wave_image->scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
245 wave_image = std::make_shared<QImage>(scaled_wave_image);
263 wave_image =
GetWaveform(width, height, Red, Green, Blue, Alpha);
266 return wave_image->constBits();
275 if (!QApplication::instance()) {
278 static char* argv[1] = {NULL};
279 previewApp = std::make_shared<QApplication>(argc, argv);
283 QWidget previewWindow;
284 previewWindow.setStyleSheet(
"background-color: #000000;");
289 previewLabel.setPixmap(QPixmap::fromImage(*wave_image));
290 previewLabel.setMask(QPixmap::fromImage(*wave_image).mask());
291 layout.addWidget(&previewLabel);
294 previewWindow.setLayout(&layout);
295 previewWindow.show();
307 return audio->getMagnitude(channel, sample, magnitude_range);
311 return audio->getMagnitude(sample, magnitude_range);
322 return buffer->getWritePointer(channel);
331 float *output = NULL;
332 int num_of_channels =
audio->getNumChannels();
336 output =
new float[num_of_channels * num_of_samples];
340 for (
int sample = 0; sample < num_of_samples; sample++)
342 for (
int channel = 0; channel < num_of_channels; channel++)
345 output[position] = buffer->getReadPointer(channel)[sample];
353 *sample_count = num_of_samples;
362 const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
364 return audio->getNumChannels();
372 const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
373 return max_audio_sample;
384 int64_t total_bytes = 0;
386 total_bytes +=
static_cast<int64_t
>(
387 width * height *
sizeof(char) * 4);
391 total_bytes += (sample_rate / 24.0) *
sizeof(float);
407 return image->constBits();
419 return image->constScanLine(row);
423 bool Frame::CheckPixel(
int row,
int col,
int red,
int green,
int blue,
int alpha,
int threshold) {
424 int col_pos = col * 4;
425 if (!image || row < 0 || row >= (height - 1) ||
426 col_pos < 0 || col_pos >= (width - 1) ) {
431 const unsigned char* pixels =
GetPixels(row);
432 if (pixels[col_pos + 0] >= (red - threshold) && pixels[col_pos + 0] <= (red + threshold) &&
433 pixels[col_pos + 1] >= (green - threshold) && pixels[col_pos + 1] <= (green + threshold) &&
434 pixels[col_pos + 2] >= (blue - threshold) && pixels[col_pos + 2] <= (blue + threshold) &&
435 pixels[col_pos + 3] >= (alpha - threshold) && pixels[col_pos + 3] <= (alpha + threshold)) {
447 pixel_ratio.
num = num;
448 pixel_ratio.
den = den;
462 if (channels <= 0 || sample_rate <= 0 || fps.
num <= 0 || fps.
den <= 0)
return 0;
468 double previous_samples = (sample_rate * fps_rate) * (
number - 1);
469 double previous_samples_remainder = fmod(previous_samples, (
double)channels);
470 previous_samples -= previous_samples_remainder;
473 double total_samples = (sample_rate * fps_rate) *
number;
474 double total_samples_remainder = fmod(total_samples, (
double)channels);
475 total_samples -= total_samples_remainder;
479 int samples_per_frame = round(total_samples - previous_samples);
480 if (samples_per_frame < 0)
481 samples_per_frame = 0;
482 return samples_per_frame;
512 return channel_layout;
520 std::shared_ptr<QImage> previewImage =
GetImage();
523 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
526 previewImage = std::make_shared<QImage>(previewImage->scaled(
527 previewImage->size().width(), previewImage->size().height() * pixel_ratio.
Reciprocal().
ToDouble(),
528 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
532 if (fabs(scale) > 1.001 || fabs(scale) < 0.999)
535 previewImage = std::make_shared<QImage>(previewImage->scaled(
536 previewImage->size().width() * scale, previewImage->size().height() * scale,
537 Qt::KeepAspectRatio, Qt::SmoothTransformation));
541 previewImage->save(QString::fromStdString(
path), format.c_str(), quality);
545 void Frame::Thumbnail(std::string
path,
int new_width,
int new_height, std::string mask_path, std::string overlay_path,
546 std::string background_color,
bool ignore_aspect, std::string format,
int quality,
float rotate,
ScaleType scale_mode) {
549 auto thumbnail = std::make_shared<QImage>(
550 new_width, new_height, QImage::Format_RGBA8888_Premultiplied);
551 thumbnail->fill(QColor(QString::fromStdString(background_color)));
554 QPainter painter(thumbnail.get());
555 painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing,
true);
558 std::shared_ptr<QImage> previewImage =
GetImage();
561 if (pixel_ratio.
num != 1 || pixel_ratio.
den != 1)
564 int aspect_width = previewImage->size().width();
565 int aspect_height = previewImage->size().height() * pixel_ratio.
Reciprocal().
ToDouble();
568 previewImage = std::make_shared<QImage>(previewImage->scaled(
569 aspect_width, aspect_height,
570 Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
574 Qt::AspectRatioMode aspect_ratio_mode = Qt::KeepAspectRatio;
576 aspect_ratio_mode = Qt::IgnoreAspectRatio;
578 switch (scale_mode) {
580 aspect_ratio_mode = Qt::KeepAspectRatioByExpanding;
583 aspect_ratio_mode = Qt::IgnoreAspectRatio;
588 aspect_ratio_mode = Qt::KeepAspectRatio;
593 previewImage = std::make_shared<QImage>(previewImage->scaled(
594 new_width, new_height,
595 aspect_ratio_mode, Qt::SmoothTransformation));
598 int x = (new_width - previewImage->size().width()) / 2.0;
599 int y = (new_height - previewImage->size().height()) / 2.0;
600 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
604 QTransform transform;
605 float origin_x = previewImage->width() / 2.0;
606 float origin_y = previewImage->height() / 2.0;
607 transform.translate(origin_x, origin_y);
608 transform.rotate(rotate);
609 transform.translate(-origin_x,-origin_y);
610 painter.setTransform(transform);
613 painter.drawImage(x, y, *previewImage);
617 if (overlay_path !=
"") {
619 auto overlay = std::make_shared<QImage>();
620 overlay->load(QString::fromStdString(overlay_path));
623 overlay = std::make_shared<QImage>(
624 overlay->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
627 overlay = std::make_shared<QImage>(overlay->scaled(
628 new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
631 painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
632 painter.drawImage(0, 0, *overlay);
637 if (mask_path !=
"") {
639 auto mask = std::make_shared<QImage>();
640 mask->load(QString::fromStdString(mask_path));
643 mask = std::make_shared<QImage>(
644 mask->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
647 mask = std::make_shared<QImage>(mask->scaled(
648 new_width, new_height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
651 mask->invertPixels();
654 unsigned char *pixels =
static_cast<unsigned char *
>(thumbnail->bits());
655 const unsigned char *mask_pixels =
static_cast<const unsigned char *
>(mask->constBits());
659 for (
int pixel = 0, byte_index=0; pixel < new_width * new_height; pixel++, byte_index+=4)
662 int gray_value = qGray(mask_pixels[byte_index], mask_pixels[byte_index] + 1, mask_pixels[byte_index] + 2);
663 int Frame_Alpha = pixels[byte_index + 3];
664 int Mask_Value = constrain(Frame_Alpha - gray_value);
667 pixels[byte_index + 3] = Mask_Value;
676 thumbnail->save(QString::fromStdString(
path), format.c_str(), quality);
680 int Frame::constrain(
int color_value)
685 else if (color_value > 255)
693 const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
698 AddColor(QColor(QString::fromStdString(new_color)));
705 const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
706 image = std::make_shared<QImage>(width, height, QImage::Format_RGBA8888_Premultiplied);
709 image->fill(new_color);
715 int new_width,
int new_height,
int bytes_per_pixel,
716 QImage::Format type,
const unsigned char *pixels_)
724 auto new_image = std::make_shared<QImage>(
726 new_width, new_height,
727 new_width * bytes_per_pixel,
729 (QImageCleanupFunction) &openshot::cleanUpBuffer,
743 const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
747 if (image->format() != QImage::Format_RGBA8888_Premultiplied)
748 *image = image->convertToFormat(QImage::Format_RGBA8888_Premultiplied);
751 width = image->width();
752 height = image->height();
771 if (image == new_image || image->size() != new_image->size()) {
774 else if (new_image->format() != QImage::Format_RGBA8888_Premultiplied) {
775 new_image = std::make_shared<QImage>(
776 new_image->convertToFormat(QImage::Format_RGBA8888_Premultiplied));
783 const std::lock_guard<std::recursive_mutex> lock(addingImageMutex);
784 unsigned char *pixels = image->bits();
785 const unsigned char *new_pixels = new_image->constBits();
792 for (
int row = start; row < image->height(); row += 2) {
793 int offset = row * image->bytesPerLine();
794 memcpy(pixels + offset, new_pixels + offset, image->bytesPerLine());
798 height = image->height();
799 width = image->width();
808 const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
811 audio->setSize(channels, length,
true,
true,
false);
812 channel_layout = layout;
816 max_audio_sample = length;
821 if (
audio && !audio_is_increasing && is_increasing) {
824 }
else if (
audio && audio_is_increasing && !is_increasing) {
828 audio_is_increasing = is_increasing;
832 void Frame::AddAudio(
bool replaceSamples,
int destChannel,
int destStartSample,
const float* source,
int numSamples,
float gainToApplyToSource = 1.0f) {
833 const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
836 int destStartSampleAdjusted = max(destStartSample, 0);
839 int new_length = destStartSampleAdjusted + numSamples;
840 int new_channel_length =
audio->getNumChannels();
841 if (destChannel >= new_channel_length)
842 new_channel_length = destChannel + 1;
843 if (new_length >
audio->getNumSamples() || new_channel_length >
audio->getNumChannels())
844 audio->setSize(new_channel_length, new_length,
true,
true,
false);
848 audio->clear(destChannel, destStartSampleAdjusted, numSamples);
851 audio->addFrom(destChannel, destStartSampleAdjusted, source, numSamples, gainToApplyToSource);
855 if (new_length > max_audio_sample)
856 max_audio_sample = new_length;
859 audio_is_increasing =
true;
863 void Frame::ApplyGainRamp(
int destChannel,
int destStartSample,
int numSamples,
float initial_gain = 0.0f,
float final_gain = 1.0f)
865 const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
868 audio->applyGainRamp(destChannel, destStartSample, numSamples, initial_gain, final_gain);
887 cv::Mat mat = cv::Mat(qimage->height(), qimage->width(), CV_8UC4, (uchar*)qimage->constBits(), qimage->bytesPerLine()).clone();
888 cv::Mat mat2 = cv::Mat(mat.rows, mat.cols, CV_8UC3 );
889 int from_to[] = { 0,0, 1,1, 2,2 };
890 cv::mixChannels( &mat, 1, &mat2, 1, from_to, 3 );
891 cv::cvtColor(mat2, mat2, cv::COLOR_RGB2BGR);
911 cv::cvtColor(img, img, cv::COLOR_BGR2RGB);
912 QImage qimg((uchar*) img.data, img.cols, img.rows, img.step, QImage::Format_RGB888);
914 std::shared_ptr<QImage> imgIn = std::make_shared<QImage>(qimg.copy());
917 if (imgIn->format() != QImage::Format_RGBA8888_Premultiplied)
918 *imgIn = imgIn->convertToFormat(QImage::Format_RGBA8888_Premultiplied);
938 juce::AudioDeviceManager deviceManager;
939 juce::String error = deviceManager.initialise (
946 if (error.isNotEmpty()) {
947 cout <<
"Error on initialise(): " << error << endl;
950 juce::AudioSourcePlayer audioSourcePlayer;
951 deviceManager.addAudioCallback (&audioSourcePlayer);
953 std::unique_ptr<AudioBufferSource> my_source;
957 juce::TimeSliceThread my_thread(
"Audio buffer thread");
960 my_thread.startThread();
962 juce::AudioTransportSource transport1;
963 transport1.setSource (my_source.get(),
966 (
double) sample_rate,
967 audio->getNumChannels());
968 transport1.setPosition (0);
969 transport1.setGain(1.0);
973 juce::MixerAudioSource mixer;
974 mixer.addInputSource(&transport1,
false);
975 audioSourcePlayer.setSource (&mixer);
980 while (transport1.isPlaying())
982 cout <<
"playing" << endl;
983 std::this_thread::sleep_for(std::chrono::seconds(1));
986 cout <<
"DONE!!!" << endl;
989 transport1.setSource (0);
990 audioSourcePlayer.setSource (0);
991 my_thread.stopThread(500);
992 deviceManager.removeAudioCallback (&audioSourcePlayer);
993 deviceManager.closeAudioDevice();
994 deviceManager.removeAllChangeListeners();
995 deviceManager.dispatchPendingMessages();
997 cout <<
"End of Play()" << endl;
1005 const std::lock_guard<std::recursive_mutex> lock(addingAudioMutex);
1008 audio->setSize(channels, numSamples,
false,
true,
false);
1013 max_audio_sample = numSamples;
1016 audio_is_increasing =
true;