Merge branch 'master' of https://github.com/highfidelity/hifi into editHandleDebugWindowFix

This commit is contained in:
David Back 2018-06-14 17:53:50 -07:00
commit 4b1d2af5fe
15 changed files with 8919 additions and 50 deletions

View file

@ -21,6 +21,7 @@ To produce an executable installer on Windows, the following are required:
- [NSISpcre Plug-in for Nullsoft](http://nsis.sourceforge.net/NSISpcre_plug-in) - 1.0
- [nsisSlideshow Plug-in for Nullsoft](http://nsis.sourceforge.net/NsisSlideshow_plug-in) - 1.7
- [Nsisunz plug-in for Nullsoft](http://nsis.sourceforge.net/Nsisunz_plug-in)
- [ApplicationID plug-in for Nullsoft](http://nsis.sourceforge.net/ApplicationID_plug-in) - 1.0
Run the `package` target to create an executable installer using the Nullsoft Scriptable Install System.

View file

@ -18,6 +18,7 @@ macro(SET_PACKAGING_PARAMETERS)
set(BUILD_GLOBAL_SERVICES "DEVELOPMENT")
set(USE_STABLE_GLOBAL_SERVICES 0)
set(BUILD_NUMBER 0)
set(APP_USER_MODEL_ID "com.highfidelity.sandbox-dev")
set_from_env(RELEASE_TYPE RELEASE_TYPE "DEV")
set_from_env(RELEASE_NUMBER RELEASE_NUMBER "")
@ -172,6 +173,7 @@ macro(SET_PACKAGING_PARAMETERS)
if (PRODUCTION_BUILD)
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface")
set(CONSOLE_SHORTCUT_NAME "Sandbox")
set(APP_USER_MODEL_ID "com.highfidelity.sandbox")
else ()
set(INTERFACE_SHORTCUT_NAME "High Fidelity Interface - ${BUILD_VERSION_NO_SHA}")
set(CONSOLE_SHORTCUT_NAME "Sandbox - ${BUILD_VERSION_NO_SHA}")

View file

@ -49,3 +49,4 @@ set(ADD_REMOVE_ICON_PATH "@ADD_REMOVE_ICON_PATH@")
set(SERVER_COMPONENT_CONDITIONAL "@SERVER_COMPONENT_CONDITIONAL@")
set(CLIENT_COMPONENT_CONDITIONAL "@CLIENT_COMPONENT_CONDITIONAL@")
set(INSTALLER_TYPE "@INSTALLER_TYPE@")
set(APP_USER_MODEL_ID "@APP_USER_MODEL_ID@")

View file

@ -905,6 +905,8 @@ Function HandlePostInstallOptions
${If} $DesktopServerState == ${BST_CHECKED}
CreateShortCut "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ YES
; Set appUserModelId
ApplicationID::Set "$DESKTOP\@CONSOLE_HF_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${Else}
!insertmacro WriteInstallOption @CONSOLE_DESKTOP_SHORTCUT_REG_KEY@ NO
${EndIf}
@ -1162,6 +1164,8 @@ Section "-Core installation"
${If} @SERVER_COMPONENT_CONDITIONAL@
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" \
"$INSTDIR\@CONSOLE_INSTALL_SUBDIR@\@CONSOLE_WIN_EXEC_NAME@"
; Set appUserModelId
ApplicationID::Set "$SMPROGRAMS\$STARTMENU_FOLDER\@CONSOLE_SHORTCUT_NAME@.lnk" "@APP_USER_MODEL_ID@"
${EndIf}
CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\@UNINSTALLER_NAME@"

View file

@ -3,5 +3,6 @@
"buildNumber": "@BUILD_NUMBER@",
"stableBuild": "@STABLE_BUILD@",
"buildIdentifier": "@BUILD_VERSION@",
"organization": "@BUILD_ORGANIZATION@"
"organization": "@BUILD_ORGANIZATION@",
"appUserModelId": "@APP_USER_MODEL_ID@"
}

View file

@ -31,6 +31,8 @@
#include "AudioLogging.h"
#include "AudioSRC.h"
#include "flump3dec.h"
QScriptValue soundSharedPointerToScriptValue(QScriptEngine* engine, const SharedSoundPointer& in) {
return engine->newQObject(new SoundScriptingInterface(in), QScriptEngine::ScriptOwnership);
}
@ -90,19 +92,35 @@ void SoundProcessor::run() {
QString fileName = _url.fileName().toLower();
static const QString WAV_EXTENSION = ".wav";
static const QString MP3_EXTENSION = ".mp3";
static const QString RAW_EXTENSION = ".raw";
if (fileName.endsWith(WAV_EXTENSION)) {
QByteArray outputAudioByteArray;
int sampleRate = interpretAsWav(rawAudioByteArray, outputAudioByteArray);
if (sampleRate == 0) {
qCDebug(audio) << "Unsupported WAV file type";
qCWarning(audio) << "Unsupported WAV file type";
emit onError(300, "Failed to load sound file, reason: unsupported WAV file type");
return;
}
downSample(outputAudioByteArray, sampleRate);
} else if (fileName.endsWith(MP3_EXTENSION)) {
QByteArray outputAudioByteArray;
int sampleRate = interpretAsMP3(rawAudioByteArray, outputAudioByteArray);
if (sampleRate == 0) {
qCWarning(audio) << "Unsupported MP3 file type";
emit onError(300, "Failed to load sound file, reason: unsupported MP3 file type");
return;
}
downSample(outputAudioByteArray, sampleRate);
} else if (fileName.endsWith(RAW_EXTENSION)) {
// check if this was a stereo raw file
// since it's raw the only way for us to know that is if the file was called .stereo.raw
@ -113,8 +131,9 @@ void SoundProcessor::run() {
// Process as 48khz RAW file
downSample(rawAudioByteArray, 48000);
} else {
qCDebug(audio) << "Unknown sound file type";
qCWarning(audio) << "Unknown sound file type";
emit onError(300, "Failed to load sound file, reason: unknown sound file type");
return;
}
@ -204,7 +223,7 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
// Read the "RIFF" chunk
RIFFHeader riff;
if (waveStream.readRawData((char*)&riff, sizeof(RIFFHeader)) != sizeof(RIFFHeader)) {
qCDebug(audio) << "Not a valid WAVE file.";
qCWarning(audio) << "Not a valid WAVE file.";
return 0;
}
@ -212,11 +231,11 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
if (strncmp(riff.descriptor.id, "RIFF", 4) == 0) {
waveStream.setByteOrder(QDataStream::LittleEndian);
} else {
qCDebug(audio) << "Currently not supporting big-endian audio files.";
qCWarning(audio) << "Currently not supporting big-endian audio files.";
return 0;
}
if (strncmp(riff.type, "WAVE", 4) != 0) {
qCDebug(audio) << "Not a valid WAVE file.";
qCWarning(audio) << "Not a valid WAVE file.";
return 0;
}
@ -224,7 +243,7 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
chunk fmt;
while (true) {
if (waveStream.readRawData((char*)&fmt, sizeof(chunk)) != sizeof(chunk)) {
qCDebug(audio) << "Not a valid WAVE file.";
qCWarning(audio) << "Not a valid WAVE file.";
return 0;
}
if (strncmp(fmt.id, "fmt ", 4) == 0) {
@ -236,14 +255,14 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
// Read the "fmt " chunk
WAVEFormat wave;
if (waveStream.readRawData((char*)&wave, sizeof(WAVEFormat)) != sizeof(WAVEFormat)) {
qCDebug(audio) << "Not a valid WAVE file.";
qCWarning(audio) << "Not a valid WAVE file.";
return 0;
}
// Parse the "fmt " chunk
if (qFromLittleEndian<quint16>(wave.audioFormat) != WAVEFORMAT_PCM &&
qFromLittleEndian<quint16>(wave.audioFormat) != WAVEFORMAT_EXTENSIBLE) {
qCDebug(audio) << "Currently not supporting non PCM audio files.";
qCWarning(audio) << "Currently not supporting non PCM audio files.";
return 0;
}
if (qFromLittleEndian<quint16>(wave.numChannels) == 2) {
@ -251,11 +270,11 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
} else if (qFromLittleEndian<quint16>(wave.numChannels) == 4) {
_isAmbisonic = true;
} else if (qFromLittleEndian<quint16>(wave.numChannels) != 1) {
qCDebug(audio) << "Currently not supporting audio files with other than 1/2/4 channels.";
qCWarning(audio) << "Currently not supporting audio files with other than 1/2/4 channels.";
return 0;
}
if (qFromLittleEndian<quint16>(wave.bitsPerSample) != 16) {
qCDebug(audio) << "Currently not supporting non 16bit audio files.";
qCWarning(audio) << "Currently not supporting non 16bit audio files.";
return 0;
}
@ -266,7 +285,7 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
chunk data;
while (true) {
if (waveStream.readRawData((char*)&data, sizeof(chunk)) != sizeof(chunk)) {
qCDebug(audio) << "Not a valid WAVE file.";
qCWarning(audio) << "Not a valid WAVE file.";
return 0;
}
if (strncmp(data.id, "data", 4) == 0) {
@ -279,10 +298,101 @@ int SoundProcessor::interpretAsWav(const QByteArray& inputAudioByteArray, QByteA
quint32 outputAudioByteArraySize = qFromLittleEndian<quint32>(data.size);
outputAudioByteArray.resize(outputAudioByteArraySize);
if (waveStream.readRawData(outputAudioByteArray.data(), outputAudioByteArraySize) != (int)outputAudioByteArraySize) {
qCDebug(audio) << "Error reading WAV file";
qCWarning(audio) << "Error reading WAV file";
return 0;
}
_duration = (float)(outputAudioByteArraySize / (wave.sampleRate * wave.numChannels * wave.bitsPerSample / 8.0f));
return wave.sampleRate;
}
// returns MP3 sample rate, used for resampling
int SoundProcessor::interpretAsMP3(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray) {
using namespace flump3dec;
static const int MP3_SAMPLES_MAX = 1152;
static const int MP3_CHANNELS_MAX = 2;
static const int MP3_BUFFER_SIZE = MP3_SAMPLES_MAX * MP3_CHANNELS_MAX * sizeof(int16_t);
uint8_t mp3Buffer[MP3_BUFFER_SIZE];
// create bitstream
Bit_stream_struc *bitstream = bs_new();
if (bitstream == nullptr) {
return 0;
}
// create decoder
mp3tl *decoder = mp3tl_new(bitstream, MP3TL_MODE_16BIT);
if (decoder == nullptr) {
bs_free(bitstream);
return 0;
}
// initialize
bs_set_data(bitstream, (uint8_t*)inputAudioByteArray.data(), inputAudioByteArray.size());
int frameCount = 0;
int sampleRate = 0;
int numChannels = 0;
// skip ID3 tag, if present
Mp3TlRetcode result = mp3tl_skip_id3(decoder);
while (!(result == MP3TL_ERR_NO_SYNC || result == MP3TL_ERR_NEED_DATA)) {
mp3tl_sync(decoder);
// find MP3 header
const fr_header *header = nullptr;
result = mp3tl_decode_header(decoder, &header);
if (result == MP3TL_ERR_OK) {
if (frameCount++ == 0) {
qCDebug(audio) << "Decoding MP3 with bitrate =" << header->bitrate
<< "sample rate =" << header->sample_rate
<< "channels =" << header->channels;
// save header info
sampleRate = header->sample_rate;
numChannels = header->channels;
// skip Xing header, if present
result = mp3tl_skip_xing(decoder, header);
}
// decode MP3 frame
if (result == MP3TL_ERR_OK) {
result = mp3tl_decode_frame(decoder, mp3Buffer, MP3_BUFFER_SIZE);
// fill bad frames with silence
int len = header->frame_samples * header->channels * sizeof(int16_t);
if (result == MP3TL_ERR_BAD_FRAME) {
memset(mp3Buffer, 0, len);
}
if (result == MP3TL_ERR_OK || result == MP3TL_ERR_BAD_FRAME) {
outputAudioByteArray.append((char*)mp3Buffer, len);
}
}
}
}
// free decoder
mp3tl_free(decoder);
// free bitstream
bs_free(bitstream);
int outputAudioByteArraySize = outputAudioByteArray.size();
if (outputAudioByteArraySize == 0) {
qCWarning(audio) << "Error decoding MP3 file";
return 0;
}
_isStereo = (numChannels == 2);
_isAmbisonic = false;
_duration = (float)outputAudioByteArraySize / (sampleRate * numChannels * sizeof(int16_t));
return sampleRate;
}

View file

@ -62,6 +62,7 @@ public:
void downSample(const QByteArray& rawAudioByteArray, int sampleRate);
int interpretAsWav(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
int interpretAsMP3(const QByteArray& inputAudioByteArray, QByteArray& outputAudioByteArray);
signals:
void onSuccess(QByteArray data, bool stereo, bool ambisonic, float duration);

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,428 @@
/*
* FLUENDO S.A.
* Copyright (C) <2005 - 2011> <support@fluendo.com>
*
* This Source Code is licensed under MIT license and the explanations attached
* in MIT License Statements.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
* of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* MIT license Statements for Fluendo's mp3 plug-in Source Code
* ------------------------------------------------------------
*
* Fluendo's mp3 software Source Code (the "Source Code") is licensed under the
* MIT license provisions.
*
* The MIT license is an open source license that permits the User to operate and
* use in many forms the Source Code, which would be governed under its
* regulations.
*
* The purpose of this note is to clarify the intellectual property rights granted
* over the Source Code by Fluendo, as well as other legal issues that concern
* your use of it.
*
* MIT license contents and provisions
* -----------------------------------
*
* The MIT license allows you to do the following things with the Source Code:
*
* - Copy and use the Source Code alone or jointly with other code for any
* purposes.
* Copy of the Source Code is not limited and is royalty-free.
*
* - Merge the Source Code with other code for developing new applications with no
* limits.
*
* - Modifying the Source Code for developing the plug-in or for implementing the
* plug-in in other applications for any purposes. The MIT License does not
* require you to share these modifications with anyone.
*
* - Publish, distribute, sublicense and sell copies of the Source Code to third
* parties.
*
* - Permit anyone to whom the Source Code is licensed to enjoy the rights above
* subject to the MIT license provisions.
*
* By licensing this Source Code under the MIT License, Fluendo is offering to the
* community the rights set out above without restriction and without any
* obligation for the User of the Source Code to release his/her modifications
* back to the community. Anyone operating with the Source Code released from
* Fluendo must grant the same MIT license rights to the community, except for any
* modifications operated on the Source Code which can be granted under a
* different license (even a proprietary license).
*
* All these rights granted to the User for the Source Code hold a limitation
* which is to include MIT permission notice and the following copyright notice:
* "Copyright 2005 Fluendo, S.L. This Source Code is licensed under MIT license
* and the explanations attached in MIT License Statements". These notices shall
* be included in all copies of the Source Code or in substantial parts of the
* Source Code which may be released separately or with modifications.
*
* Patents over the plug-in and/or Source Code
* -------------------------------------------
*
* The binaries that can be created by compiling this Source Code released by
* Fluendo might be covered by patents in various parts of the world. Fluendo
* does not own or claim to own any patents on the techniques used in the code.
* (Such patents are owned or claimed to be owned by Thompson Licensing, S.A. and
* some other entities as the case may be).
*
* Fluendo has got the relevant licenses to cover its own activities with the
* Source Code but it is not authorized to sublicense nor to grant the rights
* which it has acquired over the patents. In this sense, you can work and deal
* freely with the Source Code under MIT provisions set out above, bearing in mind
* that some activities might not be allowed under applicable patent regulations
* and that Fluendo is not granting any rights in relation to such patents.
*
* The patent license granted to Fluendo only covers Fluendo's own Software and
* Source Code activities. In any case, this software license does not allow you
* to redistribute or copy complete, ready to use mp3 software decoder binaries
* made from the Source Code as made available by Fluendo. You can of course
* distribute binaries you make yourself under any terms allowed by the MIT
* license and whatever necessary rights you have or have acquired according to
* applicable patent regulations.
*
* As Fluendo can not assure that any of the activities you undertake do not
* infringe any patents or other industrial or intellectual property rights,
* Fluendo hereby disclaims any liability for any patent infringement that may be
* claimed to you or to any other person from any legitimate rights owner, as
* stated in MIT license. So it is your responsibility to get information and to
* acquire the necessary patent licenses to undertake your activities legally.
*/
//
// Modifications and bug fixes copyright 2018 High Fidelity, Inc.
// Now passes ISO/IEC 11172-4 "full accuracy" compliance testing.
//
#ifndef __FLUMP3DEC_H__
#define __FLUMP3DEC_H__
#include <stdint.h>
#include <string.h>
#include <assert.h>
#if 0
#include <stdio.h>
#define G_GINT64_FORMAT "lld"
#define G_GUINT64_FORMAT "llu"
#define GST_LOG(f, ...) do { printf(f "\n", __VA_ARGS__); } while (0)
#define GST_DEBUG(f, ...) do { printf(f "\n", __VA_ARGS__); } while (0)
#define GST_WARNING(f, ...) do { printf(f "\n", __VA_ARGS__); } while (0)
#else
#define GST_LOG(f, ...) do {} while (0)
#define GST_DEBUG(f, ...) do {} while (0)
#define GST_WARNING(f, ...) do {} while (0)
#endif
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
#define g_assert(cond) assert(cond)
#define g_return_if_fail(cond) { if (!(cond)) return; }
#define g_return_val_if_fail(cond, val) { if (!(cond)) return (val); }
namespace flump3dec {
typedef char gchar;
typedef unsigned char guchar;
typedef int gint;
typedef unsigned int guint;
typedef float gfloat;
typedef double gdouble;
typedef int gboolean;
typedef size_t gsize;
typedef int8_t gint8;
typedef uint8_t guint8;
typedef int16_t gint16;
typedef uint16_t guint16;
typedef int32_t gint32;
typedef uint32_t guint32;
typedef int64_t gint64;
typedef uint64_t guint64;
/* Accumulator optimization on bitstream management */
#define ENABLE_OPT_BS 1
/* Bit stream reader definitions */
#define MAX_LENGTH 32 /* Maximum length of word written or
read from bit stream */
#define BS_BYTE_SIZE 8
#if ENABLE_OPT_BS
#define BS_ACUM_SIZE 32
#else
#define BS_ACUM_SIZE 8
#endif
typedef struct BSReader
{
guint64 bitpos; /* Number of bits read so far */
gsize size; /* Number of bytes in the buffer list */
const guint8 *data; /* Current data buffer */
guint8 *cur_byte; /* ptr to the current byte */
guint8 cur_bit; /* the next bit to be used in the current byte,
* numbered from 8 down to 1 */
gsize cur_used; /* Number of bytes _completely_ consumed out of
* the 'cur buffer' */
} BSReader;
typedef struct Bit_stream_struc
{
BSReader master; /* Master tracking position, advanced
* by bs_consume() */
BSReader read; /* Current read position, set back to the
* master by bs_reset() */
} Bit_stream_struc;
/* Create and initialise a new bitstream reader */
Bit_stream_struc *bs_new ();
/* Release a bitstream reader */
void bs_free (Bit_stream_struc * bs);
/* Reset the current read position to the master position */
static inline void
bs_reset (Bit_stream_struc * bs)
{
memcpy (&bs->read, &bs->master, sizeof (BSReader));
}
/* Reset master and read states */
static inline void
bs_flush (Bit_stream_struc * bs)
{
g_return_if_fail (bs != NULL);
bs->master.cur_bit = 8;
bs->master.size = 0;
bs->master.cur_used = 0;
bs->master.cur_byte = NULL;
bs->master.data = NULL;
bs->master.bitpos = 0;
bs_reset (bs);
}
/* Set data as the stream for processing */
gboolean bs_set_data (Bit_stream_struc * bs, const guint8 * data, gsize size);
/* Advance the master position by Nbits */
void bs_consume (Bit_stream_struc * bs, guint32 Nbits);
/* Number of bits available for reading */
static inline gsize bs_bits_avail (Bit_stream_struc * bs)
{
return ((bs->read.size - bs->read.cur_used) * 8 + (bs->read.cur_bit - 8));
}
/* Extract N bytes from the bitstream into the out array. */
void bs_getbytes (Bit_stream_struc * bs, guint8 * out, guint32 N);
/* Advance the read pointer by N bits */
void bs_skipbits (Bit_stream_struc * bs, guint32 N);
/* give number of consumed bytes */
static inline gsize bs_get_consumed (Bit_stream_struc * bs)
{
return bs->master.cur_used;
}
/* Current bitstream position in bits */
static inline guint64
bs_pos (Bit_stream_struc * bs)
{
return bs->master.bitpos;
}
/* Current read bitstream position in bits */
static inline guint64
bs_read_pos (Bit_stream_struc * bs)
{
return bs->read.bitpos;
}
/* Advances the read position to the first bit of next frame or
* last byte in the buffer when the sync code is not found */
gboolean bs_seek_sync (Bit_stream_struc * bs);
/* Read N bits from the stream */
/* bs - bit stream structure */
/* N - number of bits to read from the bit stream */
/* v - output value */
static inline guint32
bs_getbits (Bit_stream_struc * bs, guint32 N)
{
guint32 val = 0;
gint j = N;
g_assert (N <= MAX_LENGTH);
while (j > 0) {
gint tmp;
gint k;
gint mask;
/* Move to the next byte if we consumed the current one */
if (bs->read.cur_bit == 0) {
bs->read.cur_bit = 8;
bs->read.cur_used++;
bs->read.cur_byte++;
}
/* Protect against data limit */
if ((bs->read.cur_used >= bs->read.size)) {
GST_WARNING ("Attempted to read beyond data");
/* Return the bits we got so far */
return val;
}
/* Take as many bits as we can from the current byte */
k = MIN (j, bs->read.cur_bit);
/* We want the k bits from the current byte, starting from
* the cur_bit. Mask out the top 'already used' bits, then shift
* the bits we want down to the bottom */
mask = (1 << bs->read.cur_bit) - 1;
tmp = bs->read.cur_byte[0] & mask;
/* Trim off the bits we're leaving for next time */
tmp = tmp >> (bs->read.cur_bit - k);
/* Adjust our tracking vars */
bs->read.cur_bit -= k;
j -= k;
bs->read.bitpos += k;
/* Put these bits in the right spot in the output */
val |= tmp << j;
}
return val;
}
/* Read 1 bit from the stream */
static inline guint32
bs_get1bit (Bit_stream_struc * bs)
{
return bs_getbits (bs, 1);
}
/* read the next byte aligned N bits from the bit stream */
static inline guint32
bs_getbits_aligned (Bit_stream_struc * bs, guint32 N)
{
guint32 align;
align = bs->read.cur_bit;
if (align != 8 && align != 0)
bs_getbits (bs, align);
return bs_getbits (bs, N);
}
/* MPEG Header Definitions - ID Bit Values */
#define MPEG_VERSION_1 0x03
#define MPEG_VERSION_2 0x02
#define MPEG_VERSION_2_5 0x00
/* Header Information Structure */
typedef struct
{
/* Stuff read straight from the MPEG header */
guint version;
guint layer;
gboolean error_protection;
gint bitrate_idx; /* Index into the bitrate tables */
guint srate_idx; /* Index into the sample rate table */
gboolean padding;
gboolean extension;
guint mode;
guint mode_ext;
gboolean copyright;
gboolean original;
guint emphasis;
/* Derived attributes */
guint bitrate; /* Bitrate of the frame, kbps */
guint sample_rate; /* sample rate in Hz */
guint sample_size; /* in bits */
guint frame_samples; /* Number of samples per channels in this
frame */
guint channels; /* Number of channels in the frame */
guint bits_per_slot; /* Number of bits per slot */
guint frame_slots; /* Total number of data slots in this frame */
guint main_slots; /* Slots of main data in this frame */
guint frame_bits; /* Number of bits in the frame, including header
and sync word */
guint side_info_slots; /* Number of slots of side info in the frame */
} fr_header;
typedef struct mp3tl mp3tl;
typedef enum
{
MP3TL_ERR_OK = 0, /* Successful return code */
MP3TL_ERR_NO_SYNC, /* There was no sync word in the data buffer */
MP3TL_ERR_NEED_DATA, /* Not enough data in the buffer for the requested op */
MP3TL_ERR_BAD_FRAME, /* The frame data was corrupt and skipped */
MP3TL_ERR_STREAM, /* Encountered invalid data in the stream */
MP3TL_ERR_UNSUPPORTED_STREAM, /* Encountered valid but unplayable data in
* the stream */
MP3TL_ERR_PARAM, /* Invalid parameter was passed in */
MP3TL_ERR_UNKNOWN /* Unspecified internal decoder error (bug) */
} Mp3TlRetcode;
typedef enum
{
MP3TL_MODE_16BIT = 0 /* Decoder mode to use */
} Mp3TlMode;
mp3tl *mp3tl_new (Bit_stream_struc * bs, Mp3TlMode mode);
void mp3tl_free (mp3tl * tl);
void mp3tl_set_eos (mp3tl * tl, gboolean more_data);
Mp3TlRetcode mp3tl_sync (mp3tl * tl);
Mp3TlRetcode mp3tl_gather_frame (mp3tl * tl, guint64 * _offset, gint * _length);
Mp3TlRetcode mp3tl_decode_header (mp3tl * tl, const fr_header ** ret_hdr);
Mp3TlRetcode mp3tl_skip_frame (mp3tl * tl);
Mp3TlRetcode mp3tl_decode_frame (mp3tl * tl, guint8 * samples, guint bufsize);
const char *mp3tl_get_err_reason (mp3tl * tl);
void mp3tl_flush (mp3tl * tl);
Mp3TlRetcode mp3tl_skip_id3 (mp3tl * tl);
Mp3TlRetcode mp3tl_skip_xing (mp3tl * tl, const fr_header * hdr);
} // namespace flump3dec
#endif //__FLUMP3DEC_H__

View file

@ -65,9 +65,9 @@ function getBuildInfo() {
buildIdentifier: "dev",
buildNumber: "0",
stableBuild: "0",
organization: "High Fidelity - dev"
organization: "High Fidelity - dev",
appUserModelId: "com.highfidelity.sandbox-dev"
};
var buildInfo = DEFAULT_BUILD_INFO;
if (buildInfoPath) {
@ -258,6 +258,8 @@ function deleteOldFiles(directoryPath, maxAgeInSeconds, filenameRegex) {
}
}
app.setAppUserModelId(buildInfo.appUserModelId);
// print out uncaught exceptions in the console
process.on('uncaughtException', function(err) {
log.error(err);
@ -780,6 +782,7 @@ function onContentLoaded() {
// maybeShowSplash();
if (buildInfo.releaseType == 'PRODUCTION' && !argv.noUpdater) {
const CHECK_FOR_UPDATES_INTERVAL_SECONDS = 60 * 30;
var hasShownUpdateNotification = false;
const updateChecker = new updater.UpdateChecker(buildInfo, CHECK_FOR_UPDATES_INTERVAL_SECONDS);
@ -790,6 +793,7 @@ function onContentLoaded() {
title: 'An update is available!',
message: 'High Fidelity version ' + latestVersion + ' is available',
wait: true,
appID: buildInfo.appUserModelId,
url: url
});
hasShownUpdateNotification = true;

View file

@ -25,6 +25,8 @@ Rectangle {
HifiStylesUit.HifiConstants { id: hifi; }
id: root;
property bool uiReady: false;
property bool processingStillSnapshot: false;
property bool processing360Snapshot: false;
// Style
color: "#404040";
@ -58,7 +60,7 @@ Rectangle {
// "Spectator" text
HifiStylesUit.RalewaySemiBold {
id: titleBarText;
text: "Spectator Camera";
text: "Spectator Camera 2.2";
// Anchors
anchors.left: parent.left;
anchors.leftMargin: 30;
@ -91,13 +93,16 @@ Rectangle {
}
onClicked: {
if (!checked) {
flashCheckBox.checked = false;
}
sendToScript({method: (checked ? 'spectatorCameraOn' : 'spectatorCameraOff')});
sendToScript({method: 'updateCameravFoV', vFoV: fieldOfViewSlider.value});
}
background: Rectangle {
color: parent.checked ? "#1FC6A6" : hifi.colors.white;
implicitWidth: masterSwitch.switchWidth;
implicitWidth: masterSwitch.width;
implicitHeight: masterSwitch.height;
radius: height/2;
}
@ -127,7 +132,7 @@ Rectangle {
z: 999;
id: processingSnapshot;
anchors.fill: parent;
visible: root.processing360Snapshot;
visible: root.processing360Snapshot || !root.uiReady;
color: Qt.rgba(0.0, 0.0, 0.0, 0.85);
// This object is always used in a popup.
@ -149,7 +154,7 @@ Rectangle {
}
HifiStylesUit.RalewaySemiBold {
text: "Processing...";
text: root.uiReady ? "Processing..." : "";
// Anchors
anchors.top: processingImage.bottom;
anchors.topMargin: 4;
@ -202,10 +207,20 @@ Rectangle {
verticalAlignment: Text.AlignVCenter;
}
HifiStylesUit.FiraSansRegular {
text: ":)";
size: 28;
color: hifi.colors.white;
visible: root.processing360Snapshot || root.processingStillSnapshot;
anchors.fill: parent;
horizontalAlignment: Text.AlignHCenter;
verticalAlignment: Text.AlignVCenter;
}
// Spectator Camera Preview
Hifi.ResourceImageItem {
id: spectatorCameraPreview;
visible: masterSwitch.checked && !root.processing360Snapshot;
visible: masterSwitch.checked && !root.processing360Snapshot && !root.processingStillSnapshot;
url: showCameraView.checked || !HMD.active ? "resource://spectatorCameraFrame" : "resource://hmdPreviewFrame";
ready: masterSwitch.checked;
mirrorVertically: true;
@ -311,7 +326,30 @@ Rectangle {
}
}
}
HifiStylesUit.HiFiGlyphs {
id: flashGlyph;
visible: flashCheckBox.visible;
text: hifi.glyphs.lightning;
size: 26;
color: hifi.colors.white;
anchors.verticalCenter: flashCheckBox.verticalCenter;
anchors.right: flashCheckBox.left;
anchors.rightMargin: -2;
}
HifiControlsUit.CheckBox {
id: flashCheckBox;
visible: masterSwitch.checked;
color: hifi.colors.white;
colorScheme: hifi.colorSchemes.dark;
anchors.right: takeSnapshotButton.left;
anchors.rightMargin: -8;
anchors.verticalCenter: takeSnapshotButton.verticalCenter;
boxSize: 22;
onClicked: {
sendToScript({method: 'setFlashStatus', enabled: checked});
}
}
HifiControlsUit.Button {
id: takeSnapshotButton;
enabled: masterSwitch.checked;
@ -325,6 +363,7 @@ Rectangle {
width: 135;
height: 35;
onClicked: {
root.processingStillSnapshot = true;
sendToScript({method: 'takeSecondaryCameraSnapshot'});
}
}
@ -582,8 +621,12 @@ Rectangle {
//
function fromScript(message) {
switch (message.method) {
case 'updateSpectatorCameraCheckbox':
masterSwitch.checked = message.params;
case 'initializeUI':
masterSwitch.checked = message.masterSwitchOn;
flashCheckBox.checked = message.flashCheckboxChecked;
showCameraView.checked = message.monitorShowsCamView;
showHmdPreview.checked = !message.monitorShowsCamView;
root.uiReady = true;
break;
case 'updateMonitorShowsSwitch':
showCameraView.checked = message.params;
@ -611,6 +654,12 @@ Rectangle {
case 'finishedProcessing360Snapshot':
root.processing360Snapshot = false;
break;
case 'startedProcessingStillSnapshot':
root.processingStillSnapshot = true;
break;
case 'finishedProcessingStillSnapshot':
root.processingStillSnapshot = false;
break;
default:
console.log('Unrecognized message from spectatorCamera.js:', JSON.stringify(message));
}

View file

@ -1,4 +1,4 @@
{
"scriptURL": "http://mpassets-staging.highfidelity.com/26156ea5-cdff-43c2-9581-d6b0fa5e00ef-v1/spectatorCamera.js",
"homeURL": "http://mpassets-staging.highfidelity.com/26156ea5-cdff-43c2-9581-d6b0fa5e00ef-v1/SpectatorCamera.qml"
"scriptURL": "http://mpassets.highfidelity.com/80d02930-f409-4f1a-824f-ae0109da32d6-v1/spectatorCamera.js",
"homeURL": "http://mpassets.highfidelity.com/80d02930-f409-4f1a-824f-ae0109da32d6-v1/SpectatorCamera.qml"
}

View file

@ -97,7 +97,7 @@
if (button) {
button.editProperties({ isActive: onSpectatorCameraScreen || camera });
}
Audio.playSound(CAMERA_ON_SOUND, {
Audio.playSound(SOUND_CAMERA_ON, {
volume: 0.15,
position: cameraPosition,
localOnly: true
@ -113,8 +113,14 @@
var WAIT_AFTER_DOMAIN_SWITCH_BEFORE_CAMERA_DELETE_MS = 1 * 1000;
function spectatorCameraOff(isChangingDomains) {
function deleteCamera() {
Entities.deleteEntity(camera);
camera = false;
if (flash) {
Entities.deleteEntity(flash);
flash = false;
}
if (camera) {
Entities.deleteEntity(camera);
camera = false;
}
if (button) {
// Change button to active when window is first openend OR if the camera is on, false otherwise.
button.editProperties({ isActive: onSpectatorCameraScreen || camera });
@ -391,21 +397,81 @@
}
var takeSnapshotControllerMapping;
var takeSnapshotControllerMappingName = 'Hifi-SpectatorCamera-Mapping-TakeSnapshot';
var flash = false;
function setFlashStatus(enabled) {
var cameraPosition = Entities.getEntityProperties(camera, ["positon"]).position;
if (enabled) {
if (camera) {
Audio.playSound(SOUND_FLASH_ON, {
position: cameraPosition,
localOnly: true,
volume: 0.8
});
flash = Entities.addEntity({
"collidesWith": "",
"collisionMask": 0,
"color": {
"blue": 173,
"green": 252,
"red": 255
},
"cutoff": 90,
"dimensions": {
"x": 4,
"y": 4,
"z": 4
},
"dynamic": false,
"falloffRadius": 0.20000000298023224,
"intensity": 37,
"isSpotlight": true,
"localRotation": { w: 1, x: 0, y: 0, z: 0 },
"localPosition": { x: 0, y: -0.005, z: -0.08 },
"name": "Camera Flash",
"type": "Light",
"parentID": camera,
}, true);
}
} else {
if (flash) {
Audio.playSound(SOUND_FLASH_OFF, {
position: cameraPosition,
localOnly: true,
volume: 0.8
});
Entities.deleteEntity(flash);
flash = false;
}
}
}
function onStillSnapshotTaken() {
Render.getConfig("SecondaryCameraJob.ToneMapping").curve = 1;
sendToQml({
method: 'finishedProcessingStillSnapshot'
});
}
function maybeTakeSnapshot() {
if (camera) {
sendToQml({
method: 'startedProcessingStillSnapshot'
});
Render.getConfig("SecondaryCameraJob.ToneMapping").curve = 0;
// Wait a moment before taking the snapshot for the tonemapping curve to update
Script.setTimeout(function () {
Audio.playSound(SNAPSHOT_SOUND, {
Audio.playSound(SOUND_SNAPSHOT, {
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
localOnly: true,
volume: 1.0
});
Window.takeSecondaryCameraSnapshot();
}, 250);
} else {
sendToQml({
method: 'finishedProcessingStillSnapshot'
});
}
}
function on360SnapshotTaken() {
@ -418,7 +484,7 @@
}
function maybeTake360Snapshot() {
if (camera) {
Audio.playSound(SNAPSHOT_SOUND, {
Audio.playSound(SOUND_SNAPSHOT, {
position: { x: MyAvatar.position.x, y: MyAvatar.position.y, z: MyAvatar.position.z },
localOnly: true,
volume: 1.0
@ -508,18 +574,8 @@
}
function updateSpectatorCameraQML() {
sendToQml({ method: 'updateSpectatorCameraCheckbox', params: !!camera });
sendToQml({ method: 'updateMonitorShowsSwitch', params: monitorShowsCameraView });
if (!switchViewControllerMapping || !takeSnapshotControllerMapping) {
registerButtonMappings();
} else {
sendToQml({
method: 'updateControllerMappingCheckbox',
switchViewSetting: switchViewFromController,
takeSnapshotSetting: takeSnapshotFromController,
controller: controllerType
});
}
sendToQml({ method: 'initializeUI', masterSwitchOn: !!camera, flashCheckboxChecked: !!flash, monitorShowsCamView: monitorShowsCameraView });
registerButtonMappings();
Menu.setIsOptionChecked("Disable Preview", false);
Menu.setIsOptionChecked("Mono Preview", true);
}
@ -537,9 +593,13 @@
button.editProperties({ isActive: onSpectatorCameraScreen || camera });
}
if (onSpectatorCameraScreen) {
updateSpectatorCameraQML();
}
// In the case of a remote QML app, it takes a bit of time
// for the event bridge to actually connect, so we have to wait...
Script.setTimeout(function () {
if (onSpectatorCameraScreen) {
updateSpectatorCameraQML();
}
}, 700);
}
// Function Name: sendToQml()
@ -576,6 +636,9 @@
case 'updateCameravFoV':
spectatorCameraConfig.vFoV = message.vFoV;
break;
case 'setFlashStatus':
setFlashStatus(message.enabled);
break;
case 'takeSecondaryCameraSnapshot':
maybeTakeSnapshot();
break;
@ -600,9 +663,7 @@
// Description:
// -Called from C++ when HMD mode is changed. The argument "isHMDMode" is true if HMD is on; false otherwise.
function onHMDChanged(isHMDMode) {
if (!switchViewControllerMapping || !takeSnapshotControllerMapping) {
registerButtonMappings();
}
registerButtonMappings();
if (!isHMDMode) {
setMonitorShowsCameraView(false);
} else {
@ -646,8 +707,10 @@
}
// These functions will be called when the script is loaded.
var CAMERA_ON_SOUND = SoundCache.getSound(Script.resolvePath("cameraOn.wav"));
var SNAPSHOT_SOUND = SoundCache.getSound(Script.resourcesPath() + "sounds/snapshot/snap.wav");
var SOUND_CAMERA_ON = SoundCache.getSound(Script.resolvePath("cameraOn.wav"));
var SOUND_SNAPSHOT = SoundCache.getSound(Script.resolvePath("snap.wav"));
var SOUND_FLASH_ON = SoundCache.getSound(Script.resolvePath("flashOn.wav"));
var SOUND_FLASH_OFF = SoundCache.getSound(Script.resolvePath("flashOff.wav"));
startup();
Script.scriptEnding.connect(shutdown);