diff --git a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp index 801a3b1c5..7801a286e 100644 --- a/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp +++ b/source/modules/juce_audio_devices/native/juce_linux_Midi.cpp @@ -471,7 +471,7 @@ private: snd_midi_event_t* midiParser; int maxEventSize; - JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice); + JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice) }; } // namespace diff --git a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp index cf6fad623..8f9f6d654 100644 --- a/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_MP3AudioFormat.cpp @@ -695,7 +695,7 @@ private: win[0][i + 18] = win[3][i + 18] = (float) (0.5 * std::sin (double_Pi / 72.0 * (2 * (i + 18) + 1)) / std::cos (double_Pi * (2 * (i + 18) + 19) / 72.0)); } - const double piOver72 = double_Pi; + const double piOver72 = double_Pi / 72.0; for (i = 0; i < 6; ++i) { diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp index 3eb7928c4..28b494ff4 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.cpp @@ -36,7 +36,7 @@ const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history"; StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, const String& originator, const String& originatorRef, - const Time date, + Time date, const int64 timeReferenceSamples, const String& codingHistory) { @@ -64,6 +64,85 @@ const char* const WavAudioFormat::acidDenominator = "acid denominator"; const char* const WavAudioFormat::acidNumerator = "acid numerator"; const char* const WavAudioFormat::acidTempo = "acid tempo"; +const char* const WavAudioFormat::riffInfoArchivalLocation = "IARL"; +const char* const WavAudioFormat::riffInfoArtist = "IART"; +const char* const WavAudioFormat::riffInfoBaseURL = "IBSU"; +const char* const WavAudioFormat::riffInfoCinematographer = "ICNM"; +const char* const WavAudioFormat::riffInfoComment = "CMNT"; +const char* const WavAudioFormat::riffInfoComments = "COMM"; +const char* const WavAudioFormat::riffInfoCommissioned = "ICMS"; +const char* const WavAudioFormat::riffInfoCopyright = "ICOP"; +const char* const WavAudioFormat::riffInfoCostumeDesigner = "ICDS"; +const char* const WavAudioFormat::riffInfoCountry = "ICNT"; +const char* const WavAudioFormat::riffInfoCropped = "ICRP"; +const char* const WavAudioFormat::riffInfoDateCreated = "ICRD"; +const char* const WavAudioFormat::riffInfoDateTimeOriginal = "IDIT"; +const char* const WavAudioFormat::riffInfoDefaultAudioStream = "ICAS"; +const char* const WavAudioFormat::riffInfoDimension = "IDIM"; +const char* const WavAudioFormat::riffInfoDirectory = "DIRC"; +const char* const WavAudioFormat::riffInfoDistributedBy = "IDST"; +const char* const WavAudioFormat::riffInfoDotsPerInch = "IDPI"; +const char* const WavAudioFormat::riffInfoEditedBy = "IEDT"; +const char* const WavAudioFormat::riffInfoEighthLanguage = "IAS8"; +const char* const WavAudioFormat::riffInfoEncodedBy = "CODE"; +const char* const WavAudioFormat::riffInfoEndTimecode = "TCDO"; +const char* const WavAudioFormat::riffInfoEngineer = "IENG"; +const char* const WavAudioFormat::riffInfoFifthLanguage = "IAS5"; +const char* const WavAudioFormat::riffInfoFirstLanguage = "IAS1"; +const char* const WavAudioFormat::riffInfoFourthLanguage = "IAS4"; +const char* const WavAudioFormat::riffInfoGenre = "GENR"; +const char* const WavAudioFormat::riffInfoKeywords = "IKEY"; +const char* const WavAudioFormat::riffInfoLanguage = "LANG"; +const char* const WavAudioFormat::riffInfoLength = "TLEN"; +const char* const WavAudioFormat::riffInfoLightness = "ILGT"; +const char* const WavAudioFormat::riffInfoLocation = "LOCA"; +const char* const WavAudioFormat::riffInfoLogoIconURL = "ILIU"; +const char* const WavAudioFormat::riffInfoLogoURL = "ILGU"; +const char* const WavAudioFormat::riffInfoMedium = "IMED"; +const char* const WavAudioFormat::riffInfoMoreInfoBannerImage = "IMBI"; +const char* const WavAudioFormat::riffInfoMoreInfoBannerURL = "IMBU"; +const char* const WavAudioFormat::riffInfoMoreInfoText = "IMIT"; +const char* const WavAudioFormat::riffInfoMoreInfoURL = "IMIU"; +const char* const WavAudioFormat::riffInfoMusicBy = "IMUS"; +const char* const WavAudioFormat::riffInfoNinthLanguage = "IAS9"; +const char* const WavAudioFormat::riffInfoNumberOfParts = "PRT2"; +const char* const WavAudioFormat::riffInfoOrganisation = "TORG"; +const char* const WavAudioFormat::riffInfoPart = "PRT1"; +const char* const WavAudioFormat::riffInfoProducedBy = "IPRO"; +const char* const WavAudioFormat::riffInfoProductionDesigner = "IPDS"; +const char* const WavAudioFormat::riffInfoProductionStudio = "ISDT"; +const char* const WavAudioFormat::riffInfoRate = "RATE"; +const char* const WavAudioFormat::riffInfoRated = "AGES"; +const char* const WavAudioFormat::riffInfoRating = "IRTD"; +const char* const WavAudioFormat::riffInfoRippedBy = "IRIP"; +const char* const WavAudioFormat::riffInfoSecondaryGenre = "ISGN"; +const char* const WavAudioFormat::riffInfoSecondLanguage = "IAS2"; +const char* const WavAudioFormat::riffInfoSeventhLanguage = "IAS7"; +const char* const WavAudioFormat::riffInfoSharpness = "ISHP"; +const char* const WavAudioFormat::riffInfoSixthLanguage = "IAS6"; +const char* const WavAudioFormat::riffInfoSoftware = "ISFT"; +const char* const WavAudioFormat::riffInfoSoundSchemeTitle = "DISP"; +const char* const WavAudioFormat::riffInfoSource = "ISRC"; +const char* const WavAudioFormat::riffInfoSourceFrom = "ISRF"; +const char* const WavAudioFormat::riffInfoStarring_ISTR = "ISTR"; +const char* const WavAudioFormat::riffInfoStarring_STAR = "STAR"; +const char* const WavAudioFormat::riffInfoStartTimecode = "TCOD"; +const char* const WavAudioFormat::riffInfoStatistics = "STAT"; +const char* const WavAudioFormat::riffInfoSubject = "ISBJ"; +const char* const WavAudioFormat::riffInfoTapeName = "TAPE"; +const char* const WavAudioFormat::riffInfoTechnician = "ITCH"; +const char* const WavAudioFormat::riffInfoThirdLanguage = "IAS3"; +const char* const WavAudioFormat::riffInfoTimeCode = "ISMP"; +const char* const WavAudioFormat::riffInfoTitle = "INAM"; +const char* const WavAudioFormat::riffInfoTrackNumber = "TRCK"; +const char* const WavAudioFormat::riffInfoURL = "TURL"; +const char* const WavAudioFormat::riffInfoVegasVersionMajor = "VMAJ"; +const char* const WavAudioFormat::riffInfoVegasVersionMinor = "VMIN"; +const char* const WavAudioFormat::riffInfoVersion = "TVER"; +const char* const WavAudioFormat::riffInfoWatermarkURL = "IWMU"; +const char* const WavAudioFormat::riffInfoWrittenBy = "IWRI"; +const char* const WavAudioFormat::riffInfoYear = "YEAR"; + const char* const WavAudioFormat::ISRC = "ISRC"; const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; @@ -101,7 +180,7 @@ namespace WavFileHelpers const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow); const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh); - const int64 time = (((int64)timeHigh) << 32) + timeLow; + const int64 time = (((int64) timeHigh) << 32) + timeLow; values.set (WavAudioFormat::bwavTimeReference, String (time)); values.set (WavAudioFormat::bwavCodingHistory, @@ -473,8 +552,120 @@ namespace WavFileHelpers } //============================================================================== + /** Reads a RIFF List Info chunk from a stream positioned just after the size byte. */ namespace ListInfoChunk { + static const char* const types[] = + { + WavAudioFormat::riffInfoArchivalLocation, + WavAudioFormat::riffInfoArtist, + WavAudioFormat::riffInfoBaseURL, + WavAudioFormat::riffInfoCinematographer, + WavAudioFormat::riffInfoComment, + WavAudioFormat::riffInfoComments, + WavAudioFormat::riffInfoCommissioned, + WavAudioFormat::riffInfoCopyright, + WavAudioFormat::riffInfoCostumeDesigner, + WavAudioFormat::riffInfoCountry, + WavAudioFormat::riffInfoCropped, + WavAudioFormat::riffInfoDateCreated, + WavAudioFormat::riffInfoDateTimeOriginal, + WavAudioFormat::riffInfoDefaultAudioStream, + WavAudioFormat::riffInfoDimension, + WavAudioFormat::riffInfoDirectory, + WavAudioFormat::riffInfoDistributedBy, + WavAudioFormat::riffInfoDotsPerInch, + WavAudioFormat::riffInfoEditedBy, + WavAudioFormat::riffInfoEighthLanguage, + WavAudioFormat::riffInfoEncodedBy, + WavAudioFormat::riffInfoEndTimecode, + WavAudioFormat::riffInfoEngineer, + WavAudioFormat::riffInfoFifthLanguage, + WavAudioFormat::riffInfoFirstLanguage, + WavAudioFormat::riffInfoFourthLanguage, + WavAudioFormat::riffInfoGenre, + WavAudioFormat::riffInfoKeywords, + WavAudioFormat::riffInfoLanguage, + WavAudioFormat::riffInfoLength, + WavAudioFormat::riffInfoLightness, + WavAudioFormat::riffInfoLocation, + WavAudioFormat::riffInfoLogoIconURL, + WavAudioFormat::riffInfoLogoURL, + WavAudioFormat::riffInfoMedium, + WavAudioFormat::riffInfoMoreInfoBannerImage, + WavAudioFormat::riffInfoMoreInfoBannerURL, + WavAudioFormat::riffInfoMoreInfoText, + WavAudioFormat::riffInfoMoreInfoURL, + WavAudioFormat::riffInfoMusicBy, + WavAudioFormat::riffInfoNinthLanguage, + WavAudioFormat::riffInfoNumberOfParts, + WavAudioFormat::riffInfoOrganisation, + WavAudioFormat::riffInfoPart, + WavAudioFormat::riffInfoProducedBy, + WavAudioFormat::riffInfoProductionDesigner, + WavAudioFormat::riffInfoProductionStudio, + WavAudioFormat::riffInfoRate, + WavAudioFormat::riffInfoRated, + WavAudioFormat::riffInfoRating, + WavAudioFormat::riffInfoRippedBy, + WavAudioFormat::riffInfoSecondaryGenre, + WavAudioFormat::riffInfoSecondLanguage, + WavAudioFormat::riffInfoSeventhLanguage, + WavAudioFormat::riffInfoSharpness, + WavAudioFormat::riffInfoSixthLanguage, + WavAudioFormat::riffInfoSoftware, + WavAudioFormat::riffInfoSoundSchemeTitle, + WavAudioFormat::riffInfoSource, + WavAudioFormat::riffInfoSourceFrom, + WavAudioFormat::riffInfoStarring_ISTR, + WavAudioFormat::riffInfoStarring_STAR, + WavAudioFormat::riffInfoStartTimecode, + WavAudioFormat::riffInfoStatistics, + WavAudioFormat::riffInfoSubject, + WavAudioFormat::riffInfoTapeName, + WavAudioFormat::riffInfoTechnician, + WavAudioFormat::riffInfoThirdLanguage, + WavAudioFormat::riffInfoTimeCode, + WavAudioFormat::riffInfoTitle, + WavAudioFormat::riffInfoTrackNumber, + WavAudioFormat::riffInfoURL, + WavAudioFormat::riffInfoVegasVersionMajor, + WavAudioFormat::riffInfoVegasVersionMinor, + WavAudioFormat::riffInfoVersion, + WavAudioFormat::riffInfoWatermarkURL, + WavAudioFormat::riffInfoWrittenBy, + WavAudioFormat::riffInfoYear + }; + + static bool isMatchingTypeIgnoringCase (const int value, const char* const name) noexcept + { + for (int i = 0; i < 4; ++i) + if ((juce_wchar) name[i] != CharacterFunctions::toUpperCase ((juce_wchar) ((value >> (i * 8)) & 0xff))) + return false; + + return true; + } + + static void addToMetadata (StringPairArray& values, InputStream& input, int64 chunkEnd) + { + while (input.getPosition() < chunkEnd) + { + const int infoType = input.readInt(); + const int64 infoLength = jlimit ((int64) 0, chunkEnd - input.getPosition(), (int64) input.readInt()); + + for (int i = 0; i < numElementsInArray (types); ++i) + { + if (isMatchingTypeIgnoringCase (infoType, types[i])) + { + MemoryBlock mb; + input.readIntoMemoryBlock (mb, (ssize_t) infoLength); + values.set (types[i], mb.toString()); + break; + } + } + } + } + static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName) { const String value (values.getValue (paramName, String())); @@ -497,15 +688,12 @@ namespace WavFileHelpers static MemoryBlock createFrom (const StringPairArray& values) { - static const char* params[] = { "INAM", "IART", "IPRD", "IPRT", "ISFT", - "ISRC", "IGNR", "ICMT", "ICOP", "ICRD" }; - MemoryOutputStream out; out.writeInt (chunkName ("INFO")); bool anyParamsDefined = false; - for (int i = 0; i < numElementsInArray (params); ++i) - if (writeValue (values, out, params[i])) + for (int i = 0; i < numElementsInArray (types); ++i) + if (writeValue (values, out, types[i])) anyParamsDefined = true; return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock(); @@ -833,28 +1021,28 @@ public: bwavChunkStart = input->getPosition(); bwavSize = length; - HeapBlock bwav; + HeapBlock bwav; bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); input->read (bwav, (int) length); bwav->copyTo (metadataValues, (int) length); } else if (chunkType == chunkName ("smpl")) { - HeapBlock smpl; + HeapBlock smpl; smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); input->read (smpl, (int) length); smpl->copyTo (metadataValues, (int) length); } else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which... { - HeapBlock inst; + HeapBlock inst; inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); input->read (inst, (int) length); inst->copyTo (metadataValues); } else if (chunkType == chunkName ("cue ")) { - HeapBlock cue; + HeapBlock cue; cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1); input->read (cue, (int) length); cue->copyTo (metadataValues, (int) length); @@ -867,7 +1055,13 @@ public: } else if (chunkType == chunkName ("LIST")) { - if (input->readInt() == chunkName ("adtl")) + const int subChunkType = input->readInt(); + + if (subChunkType == chunkName ("info") || subChunkType == chunkName ("INFO")) + { + ListInfoChunk::addToMetadata (metadataValues, *input, chunkEnd); + } + else if (subChunkType == chunkName ("adtl")) { while (input->getPosition() < chunkEnd) { @@ -1495,3 +1689,70 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai return slowCopyWavFileWithNewMetadata (wavFile, newMetadata); } + +//============================================================================== +#if JUCE_UNIT_TESTS + +class WaveAudioFormatTests : public UnitTest +{ +public: + WaveAudioFormatTests() : UnitTest ("Wave audio format tests") {} + + void runTest() override + { + beginTest ("Setting up metadata"); + + StringPairArray metadataValues = WavAudioFormat::createBWAVMetadata ("description", + "originator", + "originatorRef", + Time::getCurrentTime(), + numTestAudioBufferSamples, + "codingHistory"); + + for (int i = numElementsInArray (WavFileHelpers::ListInfoChunk::types); --i >= 0;) + metadataValues.set (WavFileHelpers::ListInfoChunk::types[i], + WavFileHelpers::ListInfoChunk::types[i]); + + if (metadataValues.size() > 0) + metadataValues.set ("MetaDataSource", "WAV"); + + WavAudioFormat format; + MemoryBlock memoryBlock; + + { + beginTest ("Creating a basic wave writer"); + + ScopedPointer writer (format.createWriterFor (new MemoryOutputStream (memoryBlock, false), + 44100.0, numTestAudioBufferChannels, + 32, metadataValues, 0)); + expect (writer != nullptr); + + AudioSampleBuffer buffer (numTestAudioBufferChannels, numTestAudioBufferSamples); + buffer.clear(); + + beginTest ("Writing audio data to the basic wave writer"); + expect (writer->writeFromAudioSampleBuffer (buffer, 0, numTestAudioBufferSamples)); + } + + { + beginTest ("Creating a basic wave reader"); + + ScopedPointer reader (format.createReaderFor (new MemoryInputStream (memoryBlock, false), false)); + expect (reader != nullptr); + expect (reader->metadataValues == metadataValues, "Somehow, the metadata is different!"); + } + } + +private: + enum + { + numTestAudioBufferChannels = 2, + numTestAudioBufferSamples = 256 + }; + + JUCE_DECLARE_NON_COPYABLE (WaveAudioFormatTests) +}; + +static const WaveAudioFormatTests waveAudioFormatTests; + +#endif diff --git a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h index ff0ca946d..5b63f283e 100644 --- a/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h +++ b/source/modules/juce_audio_formats/codecs/juce_WavAudioFormat.h @@ -39,48 +39,16 @@ public: ~WavAudioFormat(); //============================================================================== - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. + // BWAV chunk properties: - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const char* const bwavDescription; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const char* const bwavOriginator; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const char* const bwavOriginatorRef; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - Date format is: yyyy-mm-dd - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const char* const bwavOriginationDate; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - Time format is: hh-mm-ss - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const char* const bwavOriginationTime; - - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. + static const char* const bwavDescription; /**< Metadata property name used in BWAV chunks. */ + static const char* const bwavOriginator; /**< Metadata property name used in BWAV chunks. */ + static const char* const bwavOriginatorRef; /**< Metadata property name used in BWAV chunks. */ + static const char* const bwavOriginationDate; /**< Metadata property name used in BWAV chunks. The format should be: yyyy-mm-dd */ + static const char* const bwavOriginationTime; /**< Metadata property name used in BWAV chunks. The format should be: format is: hh-mm-ss */ + static const char* const bwavCodingHistory; /**< Metadata property name used in BWAV chunks. */ + /** Metadata property name used in BWAV chunks. This is the number of samples from the start of an edit that the file is supposed to begin at. Seems like an obvious mistake to only allow a file to occur in an edit once, but that's the way @@ -90,13 +58,6 @@ public: */ static const char* const bwavTimeReference; - /** Metadata property name used by wav readers and writers for adding - a BWAV chunk to the file. - - @see AudioFormatReader::metadataValues, createWriterFor - */ - static const char* const bwavCodingHistory; - /** Utility function to fill out the appropriate metadata for a BWAV file. This just makes it easier than using the property names directly, and it @@ -105,31 +66,105 @@ public: static StringPairArray createBWAVMetadata (const String& description, const String& originator, const String& originatorRef, - const Time dateAndTime, - const int64 timeReferenceSamples, + Time dateAndTime, + int64 timeReferenceSamples, const String& codingHistory); //============================================================================== - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidOneShot; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidRootSet; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidStretch; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidDiskBased; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidizerFlag; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidRootNote; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidBeats; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidDenominator; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidNumerator; - /** Metadata property name used when reading a WAV file with an acid chunk. */ - static const char* const acidTempo; + // 'acid' chunk properties: + + static const char* const acidOneShot; /**< Metadata property name used in acid chunks. */ + static const char* const acidRootSet; /**< Metadata property name used in acid chunks. */ + static const char* const acidStretch; /**< Metadata property name used in acid chunks. */ + static const char* const acidDiskBased; /**< Metadata property name used in acid chunks. */ + static const char* const acidizerFlag; /**< Metadata property name used in acid chunks. */ + static const char* const acidRootNote; /**< Metadata property name used in acid chunks. */ + static const char* const acidBeats; /**< Metadata property name used in acid chunks. */ + static const char* const acidDenominator; /**< Metadata property name used in acid chunks. */ + static const char* const acidNumerator; /**< Metadata property name used in acid chunks. */ + static const char* const acidTempo; /**< Metadata property name used in acid chunks. */ + + //============================================================================== + // INFO chunk properties: + + static const char* const riffInfoArchivalLocation; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoArtist; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoBaseURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoCinematographer; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoComment; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoComments; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoCommissioned; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoCopyright; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoCostumeDesigner; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoCountry; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoCropped; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDateCreated; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDateTimeOriginal; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDefaultAudioStream; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDimension; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDirectory; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDistributedBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoDotsPerInch; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoEditedBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoEighthLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoEncodedBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoEndTimecode; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoEngineer; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoFifthLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoFirstLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoFourthLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoGenre; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoKeywords; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoLength; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoLightness; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoLocation; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoLogoIconURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoLogoURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoMedium; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoMoreInfoBannerImage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoMoreInfoBannerURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoMoreInfoText; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoMoreInfoURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoMusicBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoNinthLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoNumberOfParts; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoOrganisation; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoPart; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoProducedBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoProductionDesigner; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoProductionStudio; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoRate; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoRated; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoRating; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoRippedBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSecondaryGenre; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSecondLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSeventhLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSharpness; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSixthLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSoftware; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSoundSchemeTitle; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSource; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSourceFrom; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoStarring_ISTR; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoStarring_STAR; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoStartTimecode; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoStatistics; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoSubject; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoTapeName; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoTechnician; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoThirdLanguage; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoTimeCode; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoTitle; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoTrackNumber; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoVegasVersionMajor; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoVegasVersionMinor; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoVersion; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoWatermarkURL; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoWrittenBy; /**< Metadata property name used in INFO chunks. */ + static const char* const riffInfoYear; /**< Metadata property name used in INFO chunks. */ //============================================================================== /** Metadata property name used when reading an ISRC code from an AXML chunk. */ @@ -148,7 +183,7 @@ public: AudioFormatReader* createReaderFor (InputStream* sourceStream, bool deleteStreamIfOpeningFails) override; - MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file) override; + MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override; AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, double sampleRateToUse, diff --git a/source/modules/juce_core/files/juce_File.h b/source/modules/juce_core/files/juce_File.h index 8a28d51c3..c8e3bbcaa 100644 --- a/source/modules/juce_core/files/juce_File.h +++ b/source/modules/juce_core/files/juce_File.h @@ -445,7 +445,7 @@ public: /** Creates a new directory for this filename. - This will try to create the file as a directory, and fill also create + This will try to create the file as a directory, and will also create any parent directories it needs in order to complete the operation. @returns a result to indicate whether the directory was created successfully, or diff --git a/source/modules/juce_core/maths/juce_Range.h b/source/modules/juce_core/maths/juce_Range.h index d1b1e45bd..67a4324b9 100644 --- a/source/modules/juce_core/maths/juce_Range.h +++ b/source/modules/juce_core/maths/juce_Range.h @@ -208,7 +208,10 @@ public: bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; } //============================================================================== - /** Returns true if the given position lies inside this range. */ + /** Returns true if the given position lies inside this range. + When making this comparison, the start value is considered to be inclusive, + and the end of the range exclusive. + */ bool contains (const ValueType position) const noexcept { return start <= position && position < end; @@ -220,10 +223,7 @@ public: return jlimit (start, end, value); } - /** Returns true if the given range lies entirely inside this range. - When making this comparison, the start value is considered to be inclusive, - and the end of the range exclusive. - */ + /** Returns true if the given range lies entirely inside this range. */ bool contains (Range other) const noexcept { return start <= other.start && end >= other.end; diff --git a/source/modules/juce_core/native/juce_linux_CommonFile.cpp b/source/modules/juce_core/native/juce_linux_CommonFile.cpp index 924d5e905..5f6c93b72 100644 --- a/source/modules/juce_core/native/juce_linux_CommonFile.cpp +++ b/source/modules/juce_core/native/juce_linux_CommonFile.cpp @@ -58,10 +58,10 @@ bool File::isHidden() const return getFileName().startsWithChar ('.'); } -static String getLinkedFile (StringRef file) +static String getLinkedFile (const String& file) { HeapBlock buffer (8194); - const int numBytes = (int) readlink (file.text, buffer, 8192); + const int numBytes = (int) readlink (file.toRawUTF8(), buffer, 8192); return String::fromUTF8 (buffer, jmax (0, numBytes)); }; diff --git a/source/modules/juce_core/network/juce_Socket.cpp b/source/modules/juce_core/network/juce_Socket.cpp index 64c770885..7de9475b5 100644 --- a/source/modules/juce_core/network/juce_Socket.cpp +++ b/source/modules/juce_core/network/juce_Socket.cpp @@ -209,7 +209,7 @@ namespace SocketHelpers if (bytesThisTime <= 0 || ! connected) { - if (bytesRead == 0) + if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived) bytesRead = -1; break; @@ -648,22 +648,26 @@ int DatagramSocket::waitUntilReady (const bool readyForReading, int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) { - if (handle < 0) + if (handle < 0 || ! isBound) return -1; bool connected = true; - return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, - connected, shouldBlock, readLock) : -1; + + SocketHelpers::setSocketBlockingState (handle, shouldBlock); + return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, + connected, shouldBlock, readLock); } int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort) { - if (handle < 0) + if (handle < 0 || ! isBound) return -1; bool connected = true; - return isBound ? SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, - shouldBlock, readLock, &senderIPAddress, &senderPort) : -1; + + SocketHelpers::setSocketBlockingState (handle, shouldBlock); + return SocketHelpers::readSocket (handle, destBuffer, maxBytesToRead, connected, + shouldBlock, readLock, &senderIPAddress, &senderPort); } int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, diff --git a/source/modules/juce_core/text/juce_NewLine.h b/source/modules/juce_core/text/juce_NewLine.h index 0d310cddb..ec2d17a21 100644 --- a/source/modules/juce_core/text/juce_NewLine.h +++ b/source/modules/juce_core/text/juce_NewLine.h @@ -77,10 +77,12 @@ extern NewLine newLine; myString << "Hello World" << newLine << newLine; @endcode */ -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&); +inline String& operator<< (String& string1, const NewLine&) { return string1 += NewLine::getDefault(); } +inline String& operator+= (String& s1, const NewLine&) { return s1 += NewLine::getDefault(); } + +inline String operator+ (const NewLine&, const NewLine&) { return String (NewLine::getDefault()) + NewLine::getDefault(); } +inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); } +inline String operator+ (const NewLine&, const char* s2) { return String (NewLine::getDefault()) + s2; } -#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) - inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); } -#endif #endif // JUCE_NEWLINE_H_INCLUDED diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index cf9336d72..26bf4d107 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -365,6 +365,7 @@ String::String (const CharPointer_UTF16 start, const CharPointer_UTF16 end) : t String::String (const CharPointer_UTF32 start, const CharPointer_UTF32 end) : text (StringHolder::createFromCharPointer (start, end)) {} String::String (const std::string& s) : text (StringHolder::createFromFixedLength (s.data(), s.size())) {} +String::String (StringRef s) : text (StringHolder::createFromCharPointer (s.text)) {} String String::charToString (const juce_wchar character) { @@ -768,6 +769,11 @@ String& String::operator+= (const String& other) return *this; } +String& String::operator+= (StringRef other) +{ + return operator+= (String (other)); +} + String& String::operator+= (const char ch) { const char asString[] = { ch, 0 }; @@ -843,6 +849,7 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t s2) JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const char* const s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const wchar_t* const s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const String& s2) { return s1 += s2; } +JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, StringRef s2) { return s1 += s2; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const int number) { return s1 += number; } JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const short number) { return s1 += (int) number; } @@ -874,11 +881,6 @@ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef return stream; } -JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&) -{ - return string1 += NewLine::getDefault(); -} - //============================================================================== int String::indexOfChar (const juce_wchar character) const noexcept { @@ -2308,6 +2310,10 @@ public: expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh")); expect (String ("abcdEFGH").toUpperCase() == String ("ABCDEFGH")); + expect (String (StringRef ("abc")) == "abc"); + expect (String (StringRef ("abc")) == StringRef ("abc")); + expect (String ("abc") + StringRef ("def") == "abcdef"); + String s2 ("123"); s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; s2 += "xyz"; @@ -2316,6 +2322,8 @@ public: expect (s2 == "1234567890xyz123"); s2 += (int64) 123; expect (s2 == "1234567890xyz123123"); + s2 << StringRef ("def"); + expect (s2 == "1234567890xyz123123def"); beginTest ("Numeric conversions"); expect (String::empty.getIntValue() == 0); diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index 5c2a89f1c..a93a46cc8 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -136,6 +136,9 @@ public: /** Creates a string from a UTF-8 encoded std::string. */ String (const std::string&); + /** Creates a string from a StringRef */ + String (StringRef); + //============================================================================== /** Creates a string from a single character. */ static String charToString (juce_wchar character); @@ -202,6 +205,8 @@ public: String& operator+= (const char* textToAppend); /** Appends another string at the end of this one. */ String& operator+= (const wchar_t* textToAppend); + /** Appends another string at the end of this one. */ + String& operator+= (StringRef textToAppend); /** Appends a decimal number at the end of this string. */ String& operator+= (int numberToAppend); /** Appends a decimal number at the end of this string. */ @@ -1289,6 +1294,8 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const char* string2) JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const wchar_t* string2); /** Appends a string to the end of the first one. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const String& string2); +/** Appends a string to the end of the first one. */ +JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, StringRef string2); /** Appends a decimal number at the end of a string. */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number); diff --git a/source/modules/juce_core/text/juce_StringRef.h b/source/modules/juce_core/text/juce_StringRef.h index 3c17600ab..0c70d99ad 100644 --- a/source/modules/juce_core/text/juce_StringRef.h +++ b/source/modules/juce_core/text/juce_StringRef.h @@ -131,8 +131,10 @@ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2 /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept; -#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) - inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); } -#endif +inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); } +inline String operator+ (StringRef s1, const String& s2) { return String (s1.text) + s2; } +inline String operator+ (const char* s1, StringRef s2) { return String (s1) + String (s2.text); } +inline String operator+ (StringRef s1, const char* s2) { return String (s1.text) + String (s2); } + #endif // JUCE_STRINGREF_H_INCLUDED diff --git a/source/modules/juce_core/text/juce_TextDiff.cpp b/source/modules/juce_core/text/juce_TextDiff.cpp index 4b07015b2..a8bff4e7e 100644 --- a/source/modules/juce_core/text/juce_TextDiff.cpp +++ b/source/modules/juce_core/text/juce_TextDiff.cpp @@ -116,7 +116,21 @@ struct TextDiffHelpers b, lenB, indexInB); const size_t scratchSpace = sizeof (int) * (2 + 2 * (size_t) lenB); - int* const lines = (int*) alloca (scratchSpace); + + if (scratchSpace < 4096) + { + int* scratch = (int*) alloca (scratchSpace); + return findLongestCommonSubstring (a, lenA, indexInA, b, lenB, indexInB, scratchSpace, scratch); + } + + HeapBlock scratch (scratchSpace); + return findLongestCommonSubstring (a, lenA, indexInA, b, lenB, indexInB, scratchSpace, scratch); + } + + static int findLongestCommonSubstring (String::CharPointerType a, const int lenA, int& indexInA, + String::CharPointerType b, const int lenB, int& indexInB, + const size_t scratchSpace, int* const lines) noexcept + { zeromem (lines, scratchSpace); int* l0 = lines; diff --git a/source/modules/juce_core/threads/juce_TimeSliceThread.cpp b/source/modules/juce_core/threads/juce_TimeSliceThread.cpp index 63d537c3b..2689007e4 100644 --- a/source/modules/juce_core/threads/juce_TimeSliceThread.cpp +++ b/source/modules/juce_core/threads/juce_TimeSliceThread.cpp @@ -122,45 +122,50 @@ void TimeSliceThread::run() { Time nextClientTime; + int numClients = 0; { const ScopedLock sl2 (listLock); - index = clients.size() > 0 ? ((index + 1) % clients.size()) : 0; + numClients = clients.size(); + index = numClients > 0 ? ((index + 1) % numClients) : 0; if (TimeSliceClient* const firstClient = getNextClient (index)) nextClientTime = firstClient->nextCallTime; } - const Time now (Time::getCurrentTime()); - - if (nextClientTime > now) - { - timeToWait = (int) jmin ((int64) 500, (nextClientTime - now).inMilliseconds()); - } - else + if (numClients > 0) { - timeToWait = index == 0 ? 1 : 0; - - const ScopedLock sl (callbackLock); + const Time now (Time::getCurrentTime()); + if (nextClientTime > now) { - const ScopedLock sl2 (listLock); - clientBeingCalled = getNextClient (index); + timeToWait = (int) jmin ((int64) 500, (nextClientTime - now).inMilliseconds()); } - - if (clientBeingCalled != nullptr) + else { - const int msUntilNextCall = clientBeingCalled->useTimeSlice(); + timeToWait = index == 0 ? 1 : 0; + + const ScopedLock sl (callbackLock); + + { + const ScopedLock sl2 (listLock); + clientBeingCalled = getNextClient (index); + } + + if (clientBeingCalled != nullptr) + { + const int msUntilNextCall = clientBeingCalled->useTimeSlice(); - const ScopedLock sl2 (listLock); + const ScopedLock sl2 (listLock); - if (msUntilNextCall >= 0) - clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall); - else - clients.removeFirstMatchingValue (clientBeingCalled); + if (msUntilNextCall >= 0) + clientBeingCalled->nextCallTime = now + RelativeTime::milliseconds (msUntilNextCall); + else + clients.removeFirstMatchingValue (clientBeingCalled); - clientBeingCalled = nullptr; + clientBeingCalled = nullptr; + } } } } diff --git a/source/modules/juce_core/xml/juce_XmlElement.cpp b/source/modules/juce_core/xml/juce_XmlElement.cpp index d0fd99d9a..745dcd786 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.cpp +++ b/source/modules/juce_core/xml/juce_XmlElement.cpp @@ -26,6 +26,40 @@ ============================================================================== */ +namespace +{ + inline bool isValidXmlNameStartCharacter (const juce_wchar character) noexcept + { + return character == ':' + || character == '_' + || (character >= 'a' && character <= 'z') + || (character >= 'A' && character <= 'Z') + || (character >= 0xc0 && character <= 0xd6) + || (character >= 0xd8 && character <= 0xf6) + || (character >= 0xf8 && character <= 0x2ff) + || (character >= 0x370 && character <= 0x37d) + || (character >= 0x37f && character <= 0x1fff) + || (character >= 0x200c && character <= 0x200d) + || (character >= 0x2070 && character <= 0x218f) + || (character >= 0x2c00 && character <= 0x2fef) + || (character >= 0x3001 && character <= 0xd7ff) + || (character >= 0xf900 && character <= 0xfdcf) + || (character >= 0xfdf0 && character <= 0xfffd) + || (character >= 0x10000 && character <= 0xeffff); + } + + inline bool isValidXmlNameBodyCharacter (const juce_wchar character) noexcept + { + return isValidXmlNameStartCharacter (character) + || character == '-' + || character == '.' + || character == 0xb7 + || (character >= '0' && character <= '9') + || (character >= 0x300 && character <= 0x036f) + || (character >= 0x203f && character <= 0x2040); + } +} + XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept : name (other.name), value (other.value) @@ -35,58 +69,44 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) n XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept : name (n), value (v) { - #if JUCE_DEBUG - // this checks whether the attribute name string contains any illegal characters.. - for (String::CharPointerType t (name.getCharPointer()); ! t.isEmpty(); ++t) - jassert (t.isLetterOrDigit() || *t == '_' || *t == '-' || *t == ':'); - #endif + jassert (isValidXmlName (name)); } XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd) : name (nameStart, nameEnd) { + jassert (isValidXmlName (name)); } //============================================================================== -static void sanityCheckTagName (const String& tag) -{ - (void) tag; - - // the tag name mustn't be empty, or it'll look like a text element! - jassert (tag.containsNonWhitespaceChars()); - - // The tag can't contain spaces or other characters that would create invalid XML! - jassert (! tag.containsAnyOf (" <>/&(){}")); -} - XmlElement::XmlElement (const String& tag) : tagName (StringPool::getGlobalPool().getPooledString (tag)) { - sanityCheckTagName (tagName); + jassert (isValidXmlName (tagName)); } XmlElement::XmlElement (const char* tag) : tagName (StringPool::getGlobalPool().getPooledString (tag)) { - sanityCheckTagName (tagName); + jassert (isValidXmlName (tagName)); } XmlElement::XmlElement (StringRef tag) : tagName (StringPool::getGlobalPool().getPooledString (tag)) { - sanityCheckTagName (tagName); + jassert (isValidXmlName (tagName)); } XmlElement::XmlElement (const Identifier& tag) : tagName (tag.toString()) { - sanityCheckTagName (tagName); + jassert (isValidXmlName (tagName)); } XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd) : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd)) { - sanityCheckTagName (tagName); + jassert (isValidXmlName (tagName)); } XmlElement::XmlElement (int /*dummy*/) noexcept @@ -869,6 +889,21 @@ XmlElement* XmlElement::createTextElement (const String& text) return e; } +bool XmlElement::isValidXmlName (StringRef text) noexcept +{ + if (text.isEmpty() || ! isValidXmlNameStartCharacter (text.text.getAndAdvance())) + return false; + + for (;;) + { + if (text.isEmpty()) + return true; + + if (! isValidXmlNameBodyCharacter (text.text.getAndAdvance())) + return false; + } +} + void XmlElement::addTextElement (const String& text) { addChildElement (createTextElement (text)); diff --git a/source/modules/juce_core/xml/juce_XmlElement.h b/source/modules/juce_core/xml/juce_XmlElement.h index b4101652c..c253dbe15 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.h +++ b/source/modules/juce_core/xml/juce_XmlElement.h @@ -724,6 +724,9 @@ public: /** Creates a text element that can be added to a parent element. */ static XmlElement* createTextElement (const String& text); + /** Checks if a given string is a valid XML name */ + static bool isValidXmlName (StringRef possibleName) noexcept; + //============================================================================== private: struct XmlAttributeNode diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.cpp b/source/modules/juce_data_structures/values/juce_ValueTree.cpp index 8b6d2ac25..f601bf71c 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.cpp +++ b/source/modules/juce_data_structures/values/juce_ValueTree.cpp @@ -1066,7 +1066,12 @@ public: for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; - return CharPointer_ASCII (buffer); + String result (buffer); + + if (! XmlElement::isValidXmlName (result)) + result = createRandomIdentifier (r); + + return result; } static String createRandomWideCharString (Random& r) diff --git a/source/modules/juce_data_structures/values/juce_ValueTree.h b/source/modules/juce_data_structures/values/juce_ValueTree.h index 92c9fd608..c65a08150 100644 --- a/source/modules/juce_data_structures/values/juce_ValueTree.h +++ b/source/modules/juce_data_structures/values/juce_ValueTree.h @@ -226,14 +226,14 @@ public: */ ValueTree getChild (int index) const; - /** Returns the first child node with the speficied type name. + /** Returns the first child node with the specified type name. If no such node is found, it'll return an invalid node. (See isValid() to find out whether a node is valid). @see getOrCreateChildWithName */ ValueTree getChildWithName (const Identifier& type) const; - /** Returns the first child node with the speficied type name, creating and adding + /** Returns the first child node with the specified type name, creating and adding a child with this name if there wasn't already one there. The only time this will return an invalid object is when the object that you're calling @@ -242,7 +242,7 @@ public: */ ValueTree getOrCreateChildWithName (const Identifier& type, UndoManager* undoManager); - /** Looks for the first child node that has the speficied property value. + /** Looks for the first child node that has the specified property value. This will scan the child nodes in order, until it finds one that has property that matches the specified value. diff --git a/source/modules/juce_events/timers/juce_Timer.cpp b/source/modules/juce_events/timers/juce_Timer.cpp index 284e1ce07..5d9c38c43 100644 --- a/source/modules/juce_events/timers/juce_Timer.cpp +++ b/source/modules/juce_events/timers/juce_Timer.cpp @@ -31,14 +31,15 @@ public: TimerThread() : Thread ("Juce Timer"), - firstTimer (nullptr), - callbackNeeded (0) + firstTimer (nullptr) { triggerAsyncUpdate(); } ~TimerThread() noexcept { + signalThreadShouldExit(); + callbackArrived.signal(); stopThread (4000); jassert (instance == this || instance == nullptr); @@ -55,12 +56,6 @@ public: { const uint32 now = Time::getMillisecondCounter(); - if (now == lastTime) - { - wait (1); - continue; - } - const int elapsed = (int) (now >= lastTime ? (now - lastTime) : (std::numeric_limits::max() - (lastTime - now))); lastTime = now; @@ -69,42 +64,29 @@ public: if (timeUntilFirstTimer <= 0) { - /* If we managed to set the atomic boolean to true then send a message, this is needed - as a memory barrier so the message won't be sent before callbackNeeded is set to true, - but if it fails it means the message-thread changed the value from under us so at least - some processing is happenening and we can just loop around and try again - */ - if (callbackNeeded.compareAndSetBool (1, 0)) + if (callbackArrived.wait (0)) + { + // already a message in flight - do nothing.. + } + else { messageToSend->post(); - /* Sometimes our message can get discarded by the OS (e.g. when running as an RTAS - when the app has a modal loop), so this is how long to wait before assuming the - message has been lost and trying again. - */ - const uint32 messageDeliveryTimeout = now + 300; - - while (callbackNeeded.get() != 0) + if (! callbackArrived.wait (300)) { - wait (4); - - if (threadShouldExit()) - return; - - if (Time::getMillisecondCounter() > messageDeliveryTimeout) - { - messageToSend->post(); - break; - } + // Sometimes our message can get discarded by the OS (e.g. when running as an RTAS + // when the app has a modal loop), so this is how long to wait before assuming the + // message has been lost and trying again. + messageToSend->post(); } + + continue; } } - else - { - // don't wait for too long because running this loop also helps keep the - // Time::getApproximateMillisecondTimer value stay up-to-date - wait (jlimit (1, 50, timeUntilFirstTimer)); - } + + // don't wait for too long because running this loop also helps keep the + // Time::getApproximateMillisecondTimer value stay up-to-date + wait (jlimit (1, 100, timeUntilFirstTimer)); } } @@ -129,13 +111,7 @@ public: JUCE_CATCH_EXCEPTION } - /* This is needed as a memory barrier to make sure all processing of current timers is done - before the boolean is set. This set should never fail since if it was false in the first place, - we wouldn't get a message (so it can't be changed from false to true from under us), and if we - get a message then the value is true and the other thread can only set it to true again and - we will get another callback to set it to false. - */ - callbackNeeded.set (0); + callbackArrived.signal(); } void callTimersSynchronously() @@ -186,7 +162,7 @@ public: private: Timer* volatile firstTimer; - Atomic callbackNeeded; + WaitableEvent callbackArrived; struct CallTimersMessage : public MessageManager::MessageBase { diff --git a/source/modules/juce_graphics/colour/juce_Colour.h b/source/modules/juce_graphics/colour/juce_Colour.h index 885eaa301..85abc04ef 100644 --- a/source/modules/juce_graphics/colour/juce_Colour.h +++ b/source/modules/juce_graphics/colour/juce_Colour.h @@ -282,30 +282,25 @@ public: Colour withBrightness (float newBrightness) const noexcept; /** Returns a copy of this colour with it hue rotated. - The new colour's hue is ((this->getHue() + amountToRotate) % 1.0) - @see brighter, darker, withMultipliedBrightness */ Colour withRotatedHue (float amountToRotate) const noexcept; /** Returns a copy of this colour with its saturation multiplied by the given value. - The new colour's saturation is (this->getSaturation() * multiplier) (the result is clipped to legal limits). */ Colour withMultipliedSaturation (float multiplier) const noexcept; /** Returns a copy of this colour with its brightness multiplied by the given value. - - The new colour's saturation is (this->getBrightness() * multiplier) + The new colour's brightness is (this->getBrightness() * multiplier) (the result is clipped to legal limits). */ Colour withMultipliedBrightness (float amount) const noexcept; //============================================================================== /** Returns a brighter version of this colour. - @param amountBrighter how much brighter to make it - a value from 0 to 1.0 where 0 is unchanged, and higher values make it brighter @see withMultipliedBrightness @@ -313,7 +308,6 @@ public: Colour brighter (float amountBrighter = 0.4f) const noexcept; /** Returns a darker version of this colour. - @param amountDarker how much darker to make it - a value from 0 to 1.0 where 0 is unchanged, and higher values make it darker @see withMultipliedBrightness diff --git a/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp b/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp index 8f2eb7f0c..625b95aba 100644 --- a/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp +++ b/source/modules/juce_gui_basics/windows/juce_AlertWindow.cpp @@ -197,11 +197,10 @@ ComboBox* AlertWindow::getComboBoxComponent (const String& nameOfList) const } //============================================================================== -class AlertTextComp : public TextEditor +class AlertTextComp : public TextEditor { public: - AlertTextComp (const String& message, - const Font& font) + AlertTextComp (AlertWindow& owner, const String& message, const Font& font) { setReadOnly (true); setMultiLine (true, true); @@ -215,6 +214,9 @@ public: bestWidth = 2 * (int) std::sqrt (font.getHeight() * font.getStringWidth (message)); + if (owner.isColourSpecified (AlertWindow::textColourId)) + setColour (TextEditor::textColourId, owner.findColour (AlertWindow::textColourId)); + setColour (TextEditor::backgroundColourId, Colours::transparentBlack); setColour (TextEditor::outlineColourId, Colours::transparentBlack); setColour (TextEditor::shadowColourId, Colours::transparentBlack); @@ -241,7 +243,7 @@ private: void AlertWindow::addTextBlock (const String& textBlock) { - AlertTextComp* const c = new AlertTextComp (textBlock, getLookAndFeel().getAlertWindowMessageFont()); + AlertTextComp* const c = new AlertTextComp (*this, textBlock, getLookAndFeel().getAlertWindowMessageFont()); textBlocks.add (c); allComps.add (c); @@ -428,18 +430,9 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) } if (! isVisible()) - { centreAroundComponent (associatedComponent, w, h); - } else - { - const int cx = getX() + getWidth() / 2; - const int cy = getY() + getHeight() / 2; - - setBounds (cx - w / 2, - cy - h / 2, - w, h); - } + setBounds (getBounds().withSizeKeepingCentre (w, h)); textArea.setBounds (edgeGap, edgeGap, w - (edgeGap * 2), h - edgeGap); diff --git a/source/modules/juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h b/source/modules/juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h index 6b47a2fcb..6a1b747c1 100644 --- a/source/modules/juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h +++ b/source/modules/juce_gui_extra/native/juce_mac_CarbonViewWrapperComponent.h @@ -99,9 +99,10 @@ public: // Check for the plugin creating its own floating window, and if there is one, // we need to reparent it to make it visible.. - if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0]) - [getOwnerWindow() addChildWindow: floatingChildWindow - ordered: NSWindowAbove]; + if (carbonWindow.childWindows.count > 0) + if (NSWindow* floatingChildWindow = [[carbonWindow childWindows] objectAtIndex: 0]) + [getOwnerWindow() addChildWindow: floatingChildWindow + ordered: NSWindowAbove]; EventTypeSpec windowEventTypes[] = {