diff --git a/include/AudioReaderSource.h b/include/AudioReaderSource.h new file mode 100644 index 00000000..b78c5a69 --- /dev/null +++ b/include/AudioReaderSource.h @@ -0,0 +1,113 @@ +/** + * @file + * @brief Header file for AudioReaderSource class + * @author Jonathan Thomas + * + * @section LICENSE + * + * Copyright (c) 2008-2013 OpenShot Studios, LLC + * (http://www.openshotstudios.com). This file is part of + * OpenShot Library (http://www.openshot.org), an open-source project + * dedicated to delivering high quality video editing and animation solutions + * to the world. + * + * OpenShot Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenShot Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenShot Library. If not, see . + */ + +#ifndef OPENSHOT_AUDIOREADERSOURCE_H +#define OPENSHOT_AUDIOREADERSOURCE_H + +/// Do not include the juce unittest headers, because it collides with unittest++ +#define __JUCE_UNITTEST_JUCEHEADER__ + +#ifndef _NDEBUG + /// Define NO debug for JUCE on mac os + #define _NDEBUG +#endif + +#include +#include "JuceLibraryCode/JuceHeader.h" +#include "ReaderBase.h" + +using namespace std; + +/// This namespace is the default namespace for all code in the openshot library +namespace openshot +{ + + /** + * @brief This class is used to expose any ReaderBase derived class as an AudioSource in JUCE. + * + * This allows any reader to play audio through JUCE (our audio framework). + */ + class AudioReaderSource : public PositionableAudioSource + { + private: + int position; + int start; + bool repeat; + AudioSampleBuffer *buffer; + + int size; /// The size of the buffer + ReaderBase *reader; /// The reader to pull samples from + int64 frame_number; /// The current frame to read from + tr1::shared_ptr frame; /// The current frame object that is being read + + /// Get more samples from the reader + void GetMoreSamplesFromReader(); + + public: + + /// @brief Constructor that reads samples from a reader + /// @param reader This reader provides constant samples from a ReaderBase derived class + /// @param starting_frame_number This is the frame number to start reading samples from the reader. + AudioReaderSource(ReaderBase *audio_reader, int64 starting_frame_number, int buffer_size); + + /// Destructor + ~AudioReaderSource(); + + /// @brief Get the next block of audio samples + /// @param info This struct informs us of which samples are needed next. + void getNextAudioBlock (const AudioSourceChannelInfo& info); + + /// Prepare to play this audio source + void prepareToPlay(int, double); + + /// Release all resources + void releaseResources(); + + /// @brief Set the next read position of this source + /// @param newPosition The sample # to start reading from + void setNextReadPosition (long long newPosition); + + /// Get the next read position of this source + long long getNextReadPosition() const; + + /// Get the total length (in samples) of this audio source + long long getTotalLength() const; + + /// Determines if this audio source should repeat when it reaches the end + bool isLooping() const; + + /// @brief Set if this audio source should repeat when it reaches the end + /// @param shouldLoop Determines if the audio source should repeat when it reaches the end + void setLooping (bool shouldLoop); + + /// Update the internal buffer used by this source + void setBuffer (AudioSampleBuffer *audio_buffer); + }; + +} + +#endif diff --git a/include/Clip.h b/include/Clip.h index 52260d9e..0373af5b 100644 --- a/include/Clip.h +++ b/include/Clip.h @@ -108,9 +108,6 @@ namespace openshot { /// Adjust the audio and image of a time mapped frame tr1::shared_ptr get_time_mapped_frame(tr1::shared_ptr frame, int frame_number) throw(ReaderClosed); - /// Calculate the # of samples per video frame (for a specific frame number) - int GetSamplesPerFrame(int frame_number, Fraction rate) throw(ReaderClosed); - /// Init default settings for a clip void init_settings(); diff --git a/include/FFmpegReader.h b/include/FFmpegReader.h index 6aaf49b2..2283a72a 100644 --- a/include/FFmpegReader.h +++ b/include/FFmpegReader.h @@ -178,9 +178,6 @@ namespace openshot /// Get the smallest audio frame that is still being processed int GetSmallestAudioFrame(); - /// Calculate the # of samples per video frame (for a specific frame number) - int GetSamplesPerFrame(int frame_number); - /// Get the PTS for the current video packet int GetVideoPTS(); diff --git a/include/FFmpegWriter.h b/include/FFmpegWriter.h index cdc055d7..50cbaa78 100644 --- a/include/FFmpegWriter.h +++ b/include/FFmpegWriter.h @@ -127,6 +127,10 @@ namespace openshot int audio_input_position; AudioResampler *resampler; + /* Resample options */ + int original_sample_rate; + int original_channels; + tr1::shared_ptr last_frame; deque > spooled_audio_frames; deque > spooled_video_frames; @@ -207,6 +211,9 @@ namespace openshot /// Remove & deallocate all software scalers void RemoveScalers(); + /// Set audio resample options + void ResampleAudio(int sample_rate, int channels); + /// Set audio export options void SetAudioOptions(bool has_audio, string codec, int sample_rate, int channels, int bit_rate); diff --git a/include/Frame.h b/include/Frame.h index 385e5097..16905eb9 100644 --- a/include/Frame.h +++ b/include/Frame.h @@ -122,7 +122,6 @@ namespace openshot tr1::shared_ptr wave_image; tr1::shared_ptr audio; Fraction pixel_ratio; - int sample_rate; int channels; int width; int height; @@ -200,7 +199,7 @@ namespace openshot float* GetAudioSamples(int channel); /// Get an array of sample data (all channels interleaved together), using any sample rate - float* GetInterleavedAudioSamples(int new_sample_rate, AudioResampler* resampler, int* sample_count); + float* GetInterleavedAudioSamples(int original_sample_rate, int new_sample_rate, AudioResampler* resampler, int* sample_count); /// Get number of audio channels int GetAudioChannelsCount(); @@ -208,9 +207,6 @@ namespace openshot /// Get number of audio channels int GetAudioSamplesCount(); - /// Get the audio sample rate - int GetAudioSamplesRate(); - juce::AudioSampleBuffer *GetAudioSampleBuffer(); /// Get the size in bytes of this frame (rough estimate) @@ -228,6 +224,12 @@ namespace openshot /// Get height of image int GetHeight(); + /// Calculate the # of samples per video frame (for the current frame number) + int GetSamplesPerFrame(Fraction fps, int sample_rate); + + /// Calculate the # of samples per video frame (for a specific frame number and frame rate) + static int GetSamplesPerFrame(int frame_number, Fraction fps, int sample_rate); + /// Get an audio waveform image tr1::shared_ptr GetWaveform(int width, int height, int Red, int Green, int Blue); @@ -249,14 +251,11 @@ namespace openshot /// Set Pixel Aspect Ratio void SetPixelRatio(int num, int den); - /// Set Sample Rate, used for playback (Play() method) - void SetSampleRate(int sample_rate); - /// Make colors in a specific range transparent void TransparentColors(string color, double fuzz); /// Play audio samples for this frame - void Play(); + void Play(int sample_rate); }; } diff --git a/include/FrameMapper.h b/include/FrameMapper.h index 69acc346..2bf8a50e 100644 --- a/include/FrameMapper.h +++ b/include/FrameMapper.h @@ -147,9 +147,6 @@ namespace openshot // whether the frame rate is increasing or decreasing. void Init(); - /// Calculate the # of samples per video frame (for a specific frame number) - int GetSamplesPerFrame(int frame_number, Fraction rate); - public: // Init some containers vector fields; // List of all fields diff --git a/include/OpenShot.h b/include/OpenShot.h index 1b7ec33f..f00afbb7 100644 --- a/include/OpenShot.h +++ b/include/OpenShot.h @@ -93,6 +93,7 @@ */ #include "AudioBufferSource.h" +#include "AudioReaderSource.h" #include "AudioResampler.h" #include "Cache.h" #include "ChunkReader.h" diff --git a/include/Timeline.h b/include/Timeline.h index cb39c23a..95d71efe 100644 --- a/include/Timeline.h +++ b/include/Timeline.h @@ -163,9 +163,6 @@ namespace openshot { /// Apply effects to the source frame (if any) tr1::shared_ptr apply_effects(tr1::shared_ptr frame, int timeline_frame_number, int layer); - /// Calculate the # of samples per video frame (for a specific frame number) - int GetSamplesPerFrame(int frame_number); - /// Compare 2 floating point numbers for equality bool isEqual(double a, double b); diff --git a/src/AudioReaderSource.cpp b/src/AudioReaderSource.cpp new file mode 100644 index 00000000..2d2ece8e --- /dev/null +++ b/src/AudioReaderSource.cpp @@ -0,0 +1,204 @@ +/** + * @file + * @brief Source file for AudioReaderSource class + * @author Jonathan Thomas + * + * @section LICENSE + * + * Copyright (c) 2008-2013 OpenShot Studios, LLC + * (http://www.openshotstudios.com). This file is part of + * OpenShot Library (http://www.openshot.org), an open-source project + * dedicated to delivering high quality video editing and animation solutions + * to the world. + * + * OpenShot Library is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenShot Library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenShot Library. If not, see . + */ + +#include "../include/AudioReaderSource.h" + +using namespace std; +using namespace openshot; + +// Constructor that reads samples from a reader +AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64 starting_frame_number, int buffer_size) + : reader(audio_reader), frame_number(starting_frame_number), size(buffer_size) { + + // Initialize an audio buffer (based on reader) + buffer = new juce::AudioSampleBuffer(reader->info.channels, size); + + // initialize the audio samples to zero (silence) + buffer->clear(); +} + +// Destructor +AudioReaderSource::~AudioReaderSource() +{ + // Clear and delete the buffer + delete buffer; + buffer = NULL; +}; + +// Get more samples from the reader +void AudioReaderSource::GetMoreSamplesFromReader() { + // Determine the amount of samples needed to fill up this buffer + int amount_needed = start; // replace these used samples + int amount_remaining = size - start; + if (!frame) { + // If no frame, load entire buffer + amount_needed = size; + amount_remaining = size; + } + + // Init new buffer + juce::AudioSampleBuffer *new_buffer = new juce::AudioSampleBuffer(reader->info.channels, size); + + // Move the remaining samples into new buffer (if any) + if (start > 0) { + for (int channel = 0; channel < buffer->getNumChannels(); channel++) + new_buffer->copyFrom(channel, 0, *buffer, channel, start, amount_remaining); + start = amount_remaining; + } + + // Loop through frames until buffer filled + int fill_start = start; + while (amount_needed > 0) { + + // Get the next frame + try { + frame = reader->GetFrame(frame_number++); + } catch (const ReaderClosed & e) { + break; + } catch (const TooManySeeks & e) { + break; + } catch (const OutOfBoundsFrame & e) { + break; + } + + // Is buffer big enough to hold entire frame + if (fill_start + frame->GetAudioSamplesCount() > size) { + // Increase size of buffer + new_buffer->setSize(frame->GetAudioChannelsCount(), fill_start + frame->GetAudioSamplesCount(), true, true, false); + size = new_buffer->getNumSamples(); + } + + // Load all of its samples into the buffer + for (int channel = 0; channel < new_buffer->getNumChannels(); channel++) + new_buffer->copyFrom(channel, fill_start, *frame->GetAudioSampleBuffer(), channel, 0, frame->GetAudioSamplesCount()); + + // Adjust remaining samples + amount_remaining -= fill_start + frame->GetAudioSamplesCount(); + fill_start += frame->GetAudioSamplesCount(); + } +} + +// Get the next block of audio samples +void AudioReaderSource::getNextAudioBlock (const AudioSourceChannelInfo& info) +{ + int buffer_samples = buffer->getNumSamples(); + int buffer_channels = buffer->getNumChannels(); + + if (info.numSamples > 0) { + int start = position; + int number_to_copy = 0; + + // Do we need more samples? + if ((reader && reader->IsOpen() && !frame) or + (reader && reader->IsOpen() && buffer_samples - start < info.numSamples)) + // Refill buffer from reader + GetMoreSamplesFromReader(); + + // Determine how many samples to copy + if (start + info.numSamples <= buffer_samples) + { + // copy the full amount requested + number_to_copy = info.numSamples; + } + else if (start > buffer_samples) + { + // copy nothing + number_to_copy = 0; + } + else if (buffer_samples - start > 0) + { + // only copy what is left in the buffer + number_to_copy = buffer_samples - start; + } + else + { + // copy nothing + number_to_copy = 0; + } + + // Determine if any samples need to be copied + if (number_to_copy > 0) + { + // Loop through each channel and copy some samples + for (int channel = 0; channel < buffer_channels; channel++) + info.buffer->copyFrom(channel, info.startSample, *buffer, channel, start, number_to_copy); + + // Update the position of this audio source + position += number_to_copy; + } + + } +} + +// Prepare to play this audio source +void AudioReaderSource::prepareToPlay(int, double) { } + +// Release all resources +void AudioReaderSource::releaseResources() { } + +// Set the next read position of this source +void AudioReaderSource::setNextReadPosition (long long newPosition) +{ + // set position (if the new position is in range) + if (newPosition >= 0 && newPosition < buffer->getNumSamples()) + position = newPosition; +} + +// Get the next read position of this source +long long AudioReaderSource::getNextReadPosition() const +{ + // return the next read position + return position; +} + +// Get the total length (in samples) of this audio source +long long AudioReaderSource::getTotalLength() const +{ + // Get the length + return buffer->getNumSamples(); +} + +// Determines if this audio source should repeat when it reaches the end +bool AudioReaderSource::isLooping() const +{ + // return if this source is looping + return repeat; +} + +// Set if this audio source should repeat when it reaches the end +void AudioReaderSource::setLooping (bool shouldLoop) +{ + // Set the repeat flag + repeat = shouldLoop; +} + +// Update the internal buffer used by this source +void AudioReaderSource::setBuffer (AudioSampleBuffer *audio_buffer) +{ + buffer = audio_buffer; + setNextReadPosition(0); +} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f89cf127..d94bc0a8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -138,6 +138,7 @@ FILE(GLOB QT_PLAYER_FILES "${CMAKE_CURRENT_SOURCE_DIR}/Qt/*.cpp") ############### SET LIBRARY SOURCE FILES ################# SET ( OPENSHOT_SOURCE_FILES AudioBufferSource.cpp + AudioReaderSource.cpp AudioResampler.cpp Cache.cpp ChunkReader.cpp diff --git a/src/Clip.cpp b/src/Clip.cpp index 9cf56fc6..5ac05e74 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -299,7 +299,7 @@ tr1::shared_ptr Clip::get_time_mapped_frame(tr1::shared_ptr frame, int new_frame_number = time.GetInt(frame_number); // Create a new frame - int samples_in_frame = GetSamplesPerFrame(new_frame_number, reader->info.fps); + int samples_in_frame = Frame::GetSamplesPerFrame(new_frame_number, reader->info.fps, reader->info.sample_rate); new_frame = tr1::shared_ptr(new Frame(new_frame_number, 1, 1, "#000000", samples_in_frame, frame->GetAudioChannelsCount())); // Copy the image from the new frame @@ -310,7 +310,7 @@ tr1::shared_ptr Clip::get_time_mapped_frame(tr1::shared_ptr frame, int delta = int(round(time.GetDelta(frame_number))); // Init audio vars - int sample_rate = reader->GetFrame(new_frame_number)->GetAudioSamplesRate(); + int sample_rate = reader->info.sample_rate; int channels = reader->info.channels; int number_of_samples = reader->GetFrame(new_frame_number)->GetAudioSamplesCount(); @@ -511,25 +511,6 @@ int Clip::adjust_frame_number_minimum(int frame_number) } -// Calculate the # of samples per video frame (for a specific frame number) -int Clip::GetSamplesPerFrame(int frame_number, Fraction rate) throw(ReaderClosed) -{ - // Check for valid reader - if (!reader) - // Throw error if reader not initialized - throw ReaderClosed("No Reader has been initialized for this Clip. Call Reader(*reader) before calling this method.", ""); - - // Get the total # of samples for the previous frame, and the current frame (rounded) - double fps = rate.Reciprocal().ToDouble(); - double previous_samples = round((reader->info.sample_rate * fps) * (frame_number - 1)); - double total_samples = round((reader->info.sample_rate * fps) * frame_number); - - // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can - // be evenly divided into frames, so each frame can have have different # of samples. - double samples_per_frame = total_samples - previous_samples; - return samples_per_frame; -} - // Generate JSON string of this object string Clip::Json() { diff --git a/src/FFmpegReader.cpp b/src/FFmpegReader.cpp index 1ac88dd0..0b1307ef 100644 --- a/src/FFmpegReader.cpp +++ b/src/FFmpegReader.cpp @@ -854,7 +854,7 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int while (pts_remaining_samples) { // Get Samples per frame (for this frame number) - int samples_per_frame = GetSamplesPerFrame(previous_packet_location.frame); + int samples_per_frame = Frame::GetSamplesPerFrame(previous_packet_location.frame, info.fps, info.sample_rate); // Calculate # of samples to add to this frame int samples = samples_per_frame - previous_packet_location.sample_start; @@ -973,7 +973,7 @@ void FFmpegReader::ProcessAudioPacket(int requested_frame, int target_frame, int while (remaining_samples > 0) { // Get Samples per frame (for this frame number) - int samples_per_frame = GetSamplesPerFrame(starting_frame_number); + int samples_per_frame = Frame::GetSamplesPerFrame(starting_frame_number, info.fps, info.sample_rate); // Calculate # of samples to add to this frame int samples = samples_per_frame - start; @@ -1230,7 +1230,7 @@ AudioLocation FFmpegReader::GetAudioPTSLocation(int pts) double sample_start_percentage = frame - double(whole_frame); // Get Samples per frame - int samples_per_frame = GetSamplesPerFrame(whole_frame); + int samples_per_frame = Frame::GetSamplesPerFrame(whole_frame, info.fps, info.sample_rate); // Calculate the sample # to start on int sample_start = round(double(samples_per_frame) * sample_start_percentage); @@ -1276,20 +1276,6 @@ AudioLocation FFmpegReader::GetAudioPTSLocation(int pts) return location; } -// Calculate the # of samples per video frame (for a specific frame number) -int FFmpegReader::GetSamplesPerFrame(int frame_number) -{ - // Get the total # of samples for the previous frame, and the current frame (rounded) - double fps = info.fps.Reciprocal().ToDouble(); - double previous_samples = round((info.sample_rate * fps) * (frame_number - 1)); - double total_samples = round((info.sample_rate * fps) * frame_number); - - // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can - // be evenly divided into frames, so each frame can have have different # of samples. - double samples_per_frame = total_samples - previous_samples; - return samples_per_frame; -} - // Create a new Frame (or return an existing one) and add it to the working queue. tr1::shared_ptr FFmpegReader::CreateFrame(int requested_frame) { @@ -1303,13 +1289,9 @@ tr1::shared_ptr FFmpegReader::CreateFrame(int requested_frame) } else { - // Get Samples per frame - int samples_per_frame = GetSamplesPerFrame(requested_frame); - // Create a new frame on the working cache - tr1::shared_ptr f(new Frame(requested_frame, info.width, info.height, "#000000", samples_per_frame, info.channels)); + tr1::shared_ptr f(new Frame(requested_frame, info.width, info.height, "#000000", 0, info.channels)); f->SetPixelRatio(info.pixel_ratio.num, info.pixel_ratio.den); - f->SetSampleRate(info.sample_rate); working_cache.Add(requested_frame, f); diff --git a/src/FFmpegWriter.cpp b/src/FFmpegWriter.cpp index ff6ee70c..6d80de74 100644 --- a/src/FFmpegWriter.cpp +++ b/src/FFmpegWriter.cpp @@ -36,7 +36,8 @@ FFmpegWriter::FFmpegWriter(string path) throw (InvalidFile, InvalidFormat, Inval path(path), fmt(NULL), oc(NULL), audio_st(NULL), video_st(NULL), audio_pts(0), video_pts(0), samples(NULL), audio_outbuf(NULL), audio_outbuf_size(0), audio_input_frame_size(0), audio_input_position(0), initial_audio_input_frame_size(0), resampler(NULL), img_convert_ctx(NULL), cache_size(8), num_of_rescalers(32), - rescaler_position(0), video_codec(NULL), audio_codec(NULL), is_writing(false), write_video_count(0), write_audio_count(0) + rescaler_position(0), video_codec(NULL), audio_codec(NULL), is_writing(false), write_video_count(0), write_audio_count(0), + original_sample_rate(0), original_channels(0) { // Init FileInfo struct (clear all values) InitFileInfo(); @@ -175,6 +176,12 @@ void FFmpegWriter::SetAudioOptions(bool has_audio, string codec, int sample_rate if (bit_rate > 999) info.audio_bit_rate = bit_rate; + // init resample options (if zero) + if (original_sample_rate == 0) + original_sample_rate = info.sample_rate; + if (original_channels == 0) + original_channels = info.channels; + // Enable / Disable audio info.has_audio = has_audio; } @@ -928,7 +935,7 @@ void FFmpegWriter::write_audio_packets(bool final) channels_in_frame = frame->GetAudioChannelsCount(); // Get audio sample array - float* frame_samples_float = frame->GetInterleavedAudioSamples(info.sample_rate, new_sampler, &samples_in_frame); + float* frame_samples_float = frame->GetInterleavedAudioSamples(original_sample_rate, info.sample_rate, new_sampler, &samples_in_frame); // Calculate total samples total_frame_samples = samples_in_frame * channels_in_frame; @@ -1320,6 +1327,12 @@ void FFmpegWriter::InitScalers(int source_width, int source_height) } } +// Set audio resample options +void FFmpegWriter::ResampleAudio(int sample_rate, int channels) { + original_sample_rate = sample_rate; + original_channels = channels; +} + // Remove & deallocate all software scalers void FFmpegWriter::RemoveScalers() { diff --git a/src/Frame.cpp b/src/Frame.cpp index cf53060c..279b8ded 100644 --- a/src/Frame.cpp +++ b/src/Frame.cpp @@ -31,11 +31,11 @@ using namespace std; using namespace openshot; // Constructor - blank frame (300x200 blank image, 48kHz audio silence) -Frame::Frame() : number(1), pixel_ratio(1,1), sample_rate(48000), channels(2), width(1), height(1) +Frame::Frame() : number(1), pixel_ratio(1,1), channels(2), width(1), height(1) { // Init the image magic and audio buffer image = tr1::shared_ptr(new Magick::Image(Magick::Geometry(1,1), Magick::Color("red"))); - audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 1600)); + audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 0)); // initialize the audio samples to zero (silence) audio->clear(); @@ -43,11 +43,11 @@ Frame::Frame() : number(1), pixel_ratio(1,1), sample_rate(48000), channels(2), w // Constructor - image only (48kHz audio silence) Frame::Frame(int number, int width, int height, string color) - : number(number), pixel_ratio(1,1), sample_rate(48000), channels(2), width(width), height(height) + : number(number), pixel_ratio(1,1), channels(2), width(width), height(height) { // Init the image magic and audio buffer image = tr1::shared_ptr(new Magick::Image(Magick::Geometry(1, 1), Magick::Color(color))); - audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 1600)); + audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 0)); // initialize the audio samples to zero (silence) audio->clear(); @@ -55,11 +55,11 @@ Frame::Frame(int number, int width, int height, string color) // Constructor - image only from pixel array (48kHz audio silence) Frame::Frame(int number, int width, int height, const string map, const Magick::StorageType type, const void *pixels) - : number(number), pixel_ratio(1,1), sample_rate(48000), channels(2), width(width), height(height) + : number(number), pixel_ratio(1,1), channels(2), width(width), height(height) { // Init the image magic and audio buffer image = tr1::shared_ptr(new Magick::Image(width, height, map, type, pixels)); - audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 1600)); + audio = tr1::shared_ptr(new juce::AudioSampleBuffer(channels, 0)); // initialize the audio samples to zero (silence) audio->clear(); @@ -67,7 +67,7 @@ Frame::Frame(int number, int width, int height, const string map, const Magick:: // Constructor - audio only (300x200 blank image) Frame::Frame(int number, int samples, int channels) : - number(number), pixel_ratio(1,1), sample_rate(48000), channels(channels), width(1), height(1) + number(number), pixel_ratio(1,1), channels(channels), width(1), height(1) { // Init the image magic and audio buffer image = tr1::shared_ptr(new Magick::Image(Magick::Geometry(1, 1), Magick::Color("white"))); @@ -79,7 +79,7 @@ Frame::Frame(int number, int samples, int channels) : // Constructor - image & audio Frame::Frame(int number, int width, int height, string color, int samples, int channels) - : number(number), pixel_ratio(1,1), sample_rate(48000), channels(channels), width(width), height(height) + : number(number), pixel_ratio(1,1), channels(channels), width(width), height(height) { // Init the image magic and audio buffer image = tr1::shared_ptr(new Magick::Image(Magick::Geometry(1, 1), Magick::Color(color))); @@ -116,7 +116,6 @@ void Frame::DeepCopy(const Frame& other) image = tr1::shared_ptr(new Magick::Image(*(other.image))); audio = tr1::shared_ptr(new juce::AudioSampleBuffer(*(other.audio))); pixel_ratio = Fraction(other.pixel_ratio.num, other.pixel_ratio.den); - sample_rate = other.sample_rate; channels = other.channels; if (other.wave_image) @@ -301,7 +300,7 @@ float* Frame::GetAudioSamples(int channel) } // Get an array of sample data (all channels interleaved together), using any sample rate -float* Frame::GetInterleavedAudioSamples(int new_sample_rate, AudioResampler* resampler, int* sample_count) +float* Frame::GetInterleavedAudioSamples(int original_sample_rate, int new_sample_rate, AudioResampler* resampler, int* sample_count) { float *output = NULL; AudioSampleBuffer *buffer(audio.get()); @@ -309,10 +308,10 @@ float* Frame::GetInterleavedAudioSamples(int new_sample_rate, AudioResampler* re int num_of_samples = audio->getNumSamples(); // Resample to new sample rate (if needed) - if (new_sample_rate != sample_rate) + if (new_sample_rate != original_sample_rate) { // YES, RESAMPLE AUDIO - resampler->SetBuffer(audio.get(), sample_rate, new_sample_rate); + resampler->SetBuffer(audio.get(), original_sample_rate, new_sample_rate); // Resample data, and return new buffer pointer buffer = resampler->GetResampledBuffer(); @@ -357,12 +356,6 @@ int Frame::GetAudioSamplesCount() return audio->getNumSamples(); } -// Get the audio sample rate -int Frame::GetAudioSamplesRate() -{ - return sample_rate; -} - juce::AudioSampleBuffer *Frame::GetAudioSampleBuffer() { return audio.get(); @@ -408,10 +401,24 @@ void Frame::SetFrameNumber(int new_number) number = new_number; } -// Set Sample Rate -void Frame::SetSampleRate(int rate) +// Calculate the # of samples per video frame (for a specific frame number and frame rate) +int Frame::GetSamplesPerFrame(int number, Fraction fps, int sample_rate) { - sample_rate = rate; + // Get the total # of samples for the previous frame, and the current frame (rounded) + double fps_rate = fps.Reciprocal().ToDouble(); + double previous_samples = round((sample_rate * fps_rate) * (number - 1)); + double total_samples = round((sample_rate * fps_rate) * number); + + // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can + // be evenly divided into frames, so each frame can have have different # of samples. + double samples_per_frame = total_samples - previous_samples; + return samples_per_frame; +} + +// Calculate the # of samples per video frame (for the current frame number) +int Frame::GetSamplesPerFrame(Fraction fps, int sample_rate) +{ + return GetSamplesPerFrame(number, fps, sample_rate); } // Get height of image @@ -664,7 +671,7 @@ tr1::shared_ptr Frame::GetImage() } // Play audio samples for this frame -void Frame::Play() +void Frame::Play(int sample_rate) { // Check if samples are present if (!audio->getNumSamples()) diff --git a/src/FrameMapper.cpp b/src/FrameMapper.cpp index 6c4c2251..60a88b37 100644 --- a/src/FrameMapper.cpp +++ b/src/FrameMapper.cpp @@ -222,12 +222,12 @@ void FrameMapper::Init() // Determine the range of samples (from the original rate, to the new rate) int end_samples_frame = start_samples_frame; int end_samples_position = start_samples_position; - int remaining_samples = GetSamplesPerFrame(frame_number, target); + int remaining_samples = Frame::GetSamplesPerFrame(frame_number, target, info.sample_rate); while (remaining_samples > 0) { // get original samples - int original_samples = GetSamplesPerFrame(end_samples_frame, original) - end_samples_position; + int original_samples = Frame::GetSamplesPerFrame(end_samples_frame, original, info.sample_rate) - end_samples_position; // Enough samples if (original_samples >= remaining_samples) @@ -247,12 +247,12 @@ void FrameMapper::Init() // Create the sample mapping struct - SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, GetSamplesPerFrame(frame_number, target)}; + SampleRange Samples = {start_samples_frame, start_samples_position, end_samples_frame, end_samples_position, Frame::GetSamplesPerFrame(frame_number, target, info.sample_rate)}; // Reset the audio variables start_samples_frame = end_samples_frame; start_samples_position = end_samples_position + 1; - if (start_samples_position >= GetSamplesPerFrame(start_samples_frame, original)) + if (start_samples_position >= Frame::GetSamplesPerFrame(start_samples_frame, original, info.sample_rate)) { start_samples_frame += 1; // increment the frame (since we need to wrap onto the next one) start_samples_position = 0; // reset to 0, since we wrapped @@ -302,11 +302,10 @@ tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderCl MappedFrame mapped = GetMappedFrame(requested_frame); // Init some basic properties about this frame - int samples_in_frame = GetSamplesPerFrame(requested_frame, target); + int samples_in_frame = Frame::GetSamplesPerFrame(requested_frame, target, info.sample_rate); // Create a new frame tr1::shared_ptr frame(new Frame(requested_frame, 1, 1, "#000000", samples_in_frame, info.channels)); - frame->SetSampleRate(info.sample_rate); // Copy the image from the odd field (TODO: make this copy each field from the correct frames) frame->AddImage(reader->GetFrame(mapped.Odd.Frame)->GetImage(), true); @@ -374,20 +373,6 @@ tr1::shared_ptr FrameMapper::GetFrame(int requested_frame) throw(ReaderCl return final_cache.GetFrame(frame->number); } -// Calculate the # of samples per video frame (for a specific frame number) -int FrameMapper::GetSamplesPerFrame(int frame_number, Fraction rate) -{ - // Get the total # of samples for the previous frame, and the current frame (rounded) - double fps = rate.Reciprocal().ToDouble(); - double previous_samples = round((reader->info.sample_rate * fps) * (frame_number - 1)); - double total_samples = round((reader->info.sample_rate * fps) * frame_number); - - // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can - // be evenly divided into frames, so each frame can have have different # of samples. - double samples_per_frame = total_samples - previous_samples; - return samples_per_frame; -} - void FrameMapper::PrintMapping() { // Get the difference (in frames) between the original and target frame rates diff --git a/src/ImageReader.cpp b/src/ImageReader.cpp index 42ab73e1..6974b60d 100644 --- a/src/ImageReader.cpp +++ b/src/ImageReader.cpp @@ -111,7 +111,6 @@ tr1::shared_ptr ImageReader::GetFrame(int requested_frame) throw(ReaderCl // Create or get frame object tr1::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), "#000000", 0, 2)); - image_frame->SetSampleRate(44100); // Add Image data to frame tr1::shared_ptr copy_image(new Magick::Image(*image.get())); diff --git a/src/Qt/PlayerPrivate.cpp b/src/Qt/PlayerPrivate.cpp index 13490d62..355f881a 100644 --- a/src/Qt/PlayerPrivate.cpp +++ b/src/Qt/PlayerPrivate.cpp @@ -77,8 +77,8 @@ namespace openshot videoPlayback->render.signal(); audioPlayback->source.setBuffer(frame->GetAudioSampleBuffer()); - audioPlayback->sampleRate = frame->GetAudioSamplesRate(); - audioPlayback->numChannels = frame->GetAudioChannelsCount(); + audioPlayback->sampleRate = reader->info.sample_rate; + audioPlayback->numChannels = reader->info.channels; audioPlayback->play.signal(); audioPlayback->played.wait(); diff --git a/src/RendererBase.cpp b/src/RendererBase.cpp index 32935506..8ff6b716 100644 --- a/src/RendererBase.cpp +++ b/src/RendererBase.cpp @@ -49,7 +49,7 @@ void RendererBase::paint(const std::tr1::shared_ptr & frame) /// Use realloc for fast memory allocation. /// TODO: consider locking the buffer for mt safety buffer = reinterpret_cast(realloc(buffer, bufferSize)); -#if true +#if false // Not sure if this is actually faster... but it works now image->getPixels(0,0, width, height); // load pixels into cache image->depth( 8 ); // this is required of it crashes diff --git a/src/TextReader.cpp b/src/TextReader.cpp index 9d9003f4..44b4729a 100644 --- a/src/TextReader.cpp +++ b/src/TextReader.cpp @@ -155,7 +155,6 @@ tr1::shared_ptr TextReader::GetFrame(int requested_frame) throw(ReaderClo { // Create or get frame object tr1::shared_ptr image_frame(new Frame(requested_frame, image->size().width(), image->size().height(), "#000000", 0, 2)); - image_frame->SetSampleRate(44100); // Add Image data to frame tr1::shared_ptr copy_image(new Magick::Image(*image.get())); diff --git a/src/Timeline.cpp b/src/Timeline.cpp index d55701d9..b8cb8fb9 100644 --- a/src/Timeline.cpp +++ b/src/Timeline.cpp @@ -420,20 +420,6 @@ void Timeline::Open() is_open = true; } -// Calculate the # of samples per video frame (for a specific frame number) -int Timeline::GetSamplesPerFrame(int frame_number) -{ - // Get the total # of samples for the previous frame, and the current frame (rounded) - double fps_value = info.fps.Reciprocal().ToDouble(); - double previous_samples = round((info.sample_rate * fps_value) * (frame_number - 1)); - double total_samples = round((info.sample_rate * fps_value) * frame_number); - - // Subtract the previous frame's total samples with this frame's total samples. Not all sample rates can - // be evenly divided into frames, so each frame can have have different # of samples. - double samples_per_frame = total_samples - previous_samples; - return samples_per_frame; -} - // Compare 2 floating point numbers for equality bool Timeline::isEqual(double a, double b) { @@ -468,8 +454,7 @@ tr1::shared_ptr Timeline::GetFrame(int requested_frame) throw(ReaderClose #pragma xx omp task firstprivate(frame_number) { // Create blank frame (which will become the requested frame) - tr1::shared_ptr new_frame(tr1::shared_ptr(new Frame(frame_number, info.width, info.height, "#000000", GetSamplesPerFrame(frame_number), info.channels))); - new_frame->SetSampleRate(info.sample_rate); + tr1::shared_ptr new_frame(tr1::shared_ptr(new Frame(frame_number, info.width, info.height, "#000000", 0, info.channels))); // Calculate time of frame float requested_time = calculate_time(frame_number, info.fps);