| @@ -471,7 +471,7 @@ private: | |||||
| snd_midi_event_t* midiParser; | snd_midi_event_t* midiParser; | ||||
| int maxEventSize; | int maxEventSize; | ||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice); | |||||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice) | |||||
| }; | }; | ||||
| } // namespace | } // namespace | ||||
| @@ -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)); | 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) | for (i = 0; i < 6; ++i) | ||||
| { | { | ||||
| @@ -36,7 +36,7 @@ const char* const WavAudioFormat::bwavCodingHistory = "bwav coding history"; | |||||
| StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, | StringPairArray WavAudioFormat::createBWAVMetadata (const String& description, | ||||
| const String& originator, | const String& originator, | ||||
| const String& originatorRef, | const String& originatorRef, | ||||
| const Time date, | |||||
| Time date, | |||||
| const int64 timeReferenceSamples, | const int64 timeReferenceSamples, | ||||
| const String& codingHistory) | 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::acidNumerator = "acid numerator"; | ||||
| const char* const WavAudioFormat::acidTempo = "acid tempo"; | 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::ISRC = "ISRC"; | ||||
| const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; | const char* const WavAudioFormat::tracktionLoopInfo = "tracktion loop info"; | ||||
| @@ -101,7 +180,7 @@ namespace WavFileHelpers | |||||
| const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow); | const uint32 timeLow = ByteOrder::swapIfBigEndian (timeRefLow); | ||||
| const uint32 timeHigh = ByteOrder::swapIfBigEndian (timeRefHigh); | 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::bwavTimeReference, String (time)); | ||||
| values.set (WavAudioFormat::bwavCodingHistory, | 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 | 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) | static bool writeValue (const StringPairArray& values, MemoryOutputStream& out, const char* paramName) | ||||
| { | { | ||||
| const String value (values.getValue (paramName, String())); | const String value (values.getValue (paramName, String())); | ||||
| @@ -497,15 +688,12 @@ namespace WavFileHelpers | |||||
| static MemoryBlock createFrom (const StringPairArray& values) | static MemoryBlock createFrom (const StringPairArray& values) | ||||
| { | { | ||||
| static const char* params[] = { "INAM", "IART", "IPRD", "IPRT", "ISFT", | |||||
| "ISRC", "IGNR", "ICMT", "ICOP", "ICRD" }; | |||||
| MemoryOutputStream out; | MemoryOutputStream out; | ||||
| out.writeInt (chunkName ("INFO")); | out.writeInt (chunkName ("INFO")); | ||||
| bool anyParamsDefined = false; | 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; | anyParamsDefined = true; | ||||
| return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock(); | return anyParamsDefined ? out.getMemoryBlock() : MemoryBlock(); | ||||
| @@ -833,28 +1021,28 @@ public: | |||||
| bwavChunkStart = input->getPosition(); | bwavChunkStart = input->getPosition(); | ||||
| bwavSize = length; | bwavSize = length; | ||||
| HeapBlock <BWAVChunk> bwav; | |||||
| HeapBlock<BWAVChunk> bwav; | |||||
| bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); | bwav.calloc (jmax ((size_t) length + 1, sizeof (BWAVChunk)), 1); | ||||
| input->read (bwav, (int) length); | input->read (bwav, (int) length); | ||||
| bwav->copyTo (metadataValues, (int) length); | bwav->copyTo (metadataValues, (int) length); | ||||
| } | } | ||||
| else if (chunkType == chunkName ("smpl")) | else if (chunkType == chunkName ("smpl")) | ||||
| { | { | ||||
| HeapBlock <SMPLChunk> smpl; | |||||
| HeapBlock<SMPLChunk> smpl; | |||||
| smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); | smpl.calloc (jmax ((size_t) length + 1, sizeof (SMPLChunk)), 1); | ||||
| input->read (smpl, (int) length); | input->read (smpl, (int) length); | ||||
| smpl->copyTo (metadataValues, (int) length); | smpl->copyTo (metadataValues, (int) length); | ||||
| } | } | ||||
| else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which... | else if (chunkType == chunkName ("inst") || chunkType == chunkName ("INST")) // need to check which... | ||||
| { | { | ||||
| HeapBlock <InstChunk> inst; | |||||
| HeapBlock<InstChunk> inst; | |||||
| inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); | inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); | ||||
| input->read (inst, (int) length); | input->read (inst, (int) length); | ||||
| inst->copyTo (metadataValues); | inst->copyTo (metadataValues); | ||||
| } | } | ||||
| else if (chunkType == chunkName ("cue ")) | else if (chunkType == chunkName ("cue ")) | ||||
| { | { | ||||
| HeapBlock <CueChunk> cue; | |||||
| HeapBlock<CueChunk> cue; | |||||
| cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1); | cue.calloc (jmax ((size_t) length + 1, sizeof (CueChunk)), 1); | ||||
| input->read (cue, (int) length); | input->read (cue, (int) length); | ||||
| cue->copyTo (metadataValues, (int) length); | cue->copyTo (metadataValues, (int) length); | ||||
| @@ -867,7 +1055,13 @@ public: | |||||
| } | } | ||||
| else if (chunkType == chunkName ("LIST")) | 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) | while (input->getPosition() < chunkEnd) | ||||
| { | { | ||||
| @@ -1495,3 +1689,70 @@ bool WavAudioFormat::replaceMetadataInFile (const File& wavFile, const StringPai | |||||
| return slowCopyWavFileWithNewMetadata (wavFile, newMetadata); | 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<AudioFormatWriter> 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<AudioFormatReader> 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 | |||||
| @@ -39,48 +39,16 @@ public: | |||||
| ~WavAudioFormat(); | ~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 | 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 | 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 | 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; | 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. | /** 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 | This just makes it easier than using the property names directly, and it | ||||
| @@ -105,31 +66,105 @@ public: | |||||
| static StringPairArray createBWAVMetadata (const String& description, | static StringPairArray createBWAVMetadata (const String& description, | ||||
| const String& originator, | const String& originator, | ||||
| const String& originatorRef, | const String& originatorRef, | ||||
| const Time dateAndTime, | |||||
| const int64 timeReferenceSamples, | |||||
| Time dateAndTime, | |||||
| int64 timeReferenceSamples, | |||||
| const String& codingHistory); | 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. */ | /** Metadata property name used when reading an ISRC code from an AXML chunk. */ | ||||
| @@ -148,7 +183,7 @@ public: | |||||
| AudioFormatReader* createReaderFor (InputStream* sourceStream, | AudioFormatReader* createReaderFor (InputStream* sourceStream, | ||||
| bool deleteStreamIfOpeningFails) override; | bool deleteStreamIfOpeningFails) override; | ||||
| MemoryMappedAudioFormatReader* createMemoryMappedReader (const File& file) override; | |||||
| MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override; | |||||
| AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, | AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, | ||||
| double sampleRateToUse, | double sampleRateToUse, | ||||
| @@ -445,7 +445,7 @@ public: | |||||
| /** Creates a new directory for this filename. | /** 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. | any parent directories it needs in order to complete the operation. | ||||
| @returns a result to indicate whether the directory was created successfully, or | @returns a result to indicate whether the directory was created successfully, or | ||||
| @@ -208,7 +208,10 @@ public: | |||||
| bool operator!= (Range other) const noexcept { return start != other.start || end != other.end; } | 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 | bool contains (const ValueType position) const noexcept | ||||
| { | { | ||||
| return start <= position && position < end; | return start <= position && position < end; | ||||
| @@ -220,10 +223,7 @@ public: | |||||
| return jlimit (start, end, value); | 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 | bool contains (Range other) const noexcept | ||||
| { | { | ||||
| return start <= other.start && end >= other.end; | return start <= other.start && end >= other.end; | ||||
| @@ -58,10 +58,10 @@ bool File::isHidden() const | |||||
| return getFileName().startsWithChar ('.'); | return getFileName().startsWithChar ('.'); | ||||
| } | } | ||||
| static String getLinkedFile (StringRef file) | |||||
| static String getLinkedFile (const String& file) | |||||
| { | { | ||||
| HeapBlock<char> buffer (8194); | HeapBlock<char> 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)); | return String::fromUTF8 (buffer, jmax (0, numBytes)); | ||||
| }; | }; | ||||
| @@ -209,7 +209,7 @@ namespace SocketHelpers | |||||
| if (bytesThisTime <= 0 || ! connected) | if (bytesThisTime <= 0 || ! connected) | ||||
| { | { | ||||
| if (bytesRead == 0) | |||||
| if (bytesRead == 0 && blockUntilSpecifiedAmountHasArrived) | |||||
| bytesRead = -1; | bytesRead = -1; | ||||
| break; | break; | ||||
| @@ -648,22 +648,26 @@ int DatagramSocket::waitUntilReady (const bool readyForReading, | |||||
| int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) | int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock) | ||||
| { | { | ||||
| if (handle < 0) | |||||
| if (handle < 0 || ! isBound) | |||||
| return -1; | return -1; | ||||
| bool connected = true; | 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) | int DatagramSocket::read (void* destBuffer, int maxBytesToRead, bool shouldBlock, String& senderIPAddress, int& senderPort) | ||||
| { | { | ||||
| if (handle < 0) | |||||
| if (handle < 0 || ! isBound) | |||||
| return -1; | return -1; | ||||
| bool connected = true; | 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, | int DatagramSocket::write (const String& remoteHostname, int remotePortNumber, | ||||
| @@ -77,10 +77,12 @@ extern NewLine newLine; | |||||
| myString << "Hello World" << newLine << newLine; | myString << "Hello World" << newLine << newLine; | ||||
| @endcode | @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 | #endif // JUCE_NEWLINE_H_INCLUDED | ||||
| @@ -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 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 (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) | String String::charToString (const juce_wchar character) | ||||
| { | { | ||||
| @@ -768,6 +769,11 @@ String& String::operator+= (const String& other) | |||||
| return *this; | return *this; | ||||
| } | } | ||||
| String& String::operator+= (StringRef other) | |||||
| { | |||||
| return operator+= (String (other)); | |||||
| } | |||||
| String& String::operator+= (const char ch) | String& String::operator+= (const char ch) | ||||
| { | { | ||||
| const char asString[] = { ch, 0 }; | 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 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 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, 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 int number) { return s1 += number; } | ||||
| JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const short number) { return s1 += (int) 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; | 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 | int String::indexOfChar (const juce_wchar character) const noexcept | ||||
| { | { | ||||
| @@ -2308,6 +2310,10 @@ public: | |||||
| expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh")); | expect (String ("abcdEFGH").toLowerCase() == String ("abcdefgh")); | ||||
| expect (String ("abcdEFGH").toUpperCase() == 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"); | String s2 ("123"); | ||||
| s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; | s2 << ((int) 4) << ((short) 5) << "678" << L"9" << '0'; | ||||
| s2 += "xyz"; | s2 += "xyz"; | ||||
| @@ -2316,6 +2322,8 @@ public: | |||||
| expect (s2 == "1234567890xyz123"); | expect (s2 == "1234567890xyz123"); | ||||
| s2 += (int64) 123; | s2 += (int64) 123; | ||||
| expect (s2 == "1234567890xyz123123"); | expect (s2 == "1234567890xyz123123"); | ||||
| s2 << StringRef ("def"); | |||||
| expect (s2 == "1234567890xyz123123def"); | |||||
| beginTest ("Numeric conversions"); | beginTest ("Numeric conversions"); | ||||
| expect (String::empty.getIntValue() == 0); | expect (String::empty.getIntValue() == 0); | ||||
| @@ -136,6 +136,9 @@ public: | |||||
| /** Creates a string from a UTF-8 encoded std::string. */ | /** Creates a string from a UTF-8 encoded std::string. */ | ||||
| String (const std::string&); | String (const std::string&); | ||||
| /** Creates a string from a StringRef */ | |||||
| String (StringRef); | |||||
| //============================================================================== | //============================================================================== | ||||
| /** Creates a string from a single character. */ | /** Creates a string from a single character. */ | ||||
| static String charToString (juce_wchar character); | static String charToString (juce_wchar character); | ||||
| @@ -202,6 +205,8 @@ public: | |||||
| String& operator+= (const char* textToAppend); | String& operator+= (const char* textToAppend); | ||||
| /** Appends another string at the end of this one. */ | /** Appends another string at the end of this one. */ | ||||
| String& operator+= (const wchar_t* textToAppend); | 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. */ | /** Appends a decimal number at the end of this string. */ | ||||
| String& operator+= (int numberToAppend); | String& operator+= (int numberToAppend); | ||||
| /** Appends a decimal number at the end of this string. */ | /** 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); | JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const wchar_t* string2); | ||||
| /** Appends a string to the end of the first one. */ | /** Appends a string to the end of the first one. */ | ||||
| JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const String& string2); | 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. */ | /** Appends a decimal number at the end of a string. */ | ||||
| JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number); | JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, short number); | ||||
| @@ -131,8 +131,10 @@ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2 | |||||
| /** Case-sensitive comparison of two strings. */ | /** Case-sensitive comparison of two strings. */ | ||||
| JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept; | 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 | #endif // JUCE_STRINGREF_H_INCLUDED | ||||
| @@ -116,7 +116,21 @@ struct TextDiffHelpers | |||||
| b, lenB, indexInB); | b, lenB, indexInB); | ||||
| const size_t scratchSpace = sizeof (int) * (2 + 2 * (size_t) lenB); | 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<int> 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); | zeromem (lines, scratchSpace); | ||||
| int* l0 = lines; | int* l0 = lines; | ||||
| @@ -122,45 +122,50 @@ void TimeSliceThread::run() | |||||
| { | { | ||||
| Time nextClientTime; | Time nextClientTime; | ||||
| int numClients = 0; | |||||
| { | { | ||||
| const ScopedLock sl2 (listLock); | 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)) | if (TimeSliceClient* const firstClient = getNextClient (index)) | ||||
| nextClientTime = firstClient->nextCallTime; | 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; | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -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 | XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) noexcept | ||||
| : name (other.name), | : name (other.name), | ||||
| value (other.value) | value (other.value) | ||||
| @@ -35,58 +69,44 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const XmlAttributeNode& other) n | |||||
| XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept | XmlElement::XmlAttributeNode::XmlAttributeNode (const Identifier& n, const String& v) noexcept | ||||
| : name (n), value (v) | : 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) | XmlElement::XmlAttributeNode::XmlAttributeNode (String::CharPointerType nameStart, String::CharPointerType nameEnd) | ||||
| : name (nameStart, 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) | XmlElement::XmlElement (const String& tag) | ||||
| : tagName (StringPool::getGlobalPool().getPooledString (tag)) | : tagName (StringPool::getGlobalPool().getPooledString (tag)) | ||||
| { | { | ||||
| sanityCheckTagName (tagName); | |||||
| jassert (isValidXmlName (tagName)); | |||||
| } | } | ||||
| XmlElement::XmlElement (const char* tag) | XmlElement::XmlElement (const char* tag) | ||||
| : tagName (StringPool::getGlobalPool().getPooledString (tag)) | : tagName (StringPool::getGlobalPool().getPooledString (tag)) | ||||
| { | { | ||||
| sanityCheckTagName (tagName); | |||||
| jassert (isValidXmlName (tagName)); | |||||
| } | } | ||||
| XmlElement::XmlElement (StringRef tag) | XmlElement::XmlElement (StringRef tag) | ||||
| : tagName (StringPool::getGlobalPool().getPooledString (tag)) | : tagName (StringPool::getGlobalPool().getPooledString (tag)) | ||||
| { | { | ||||
| sanityCheckTagName (tagName); | |||||
| jassert (isValidXmlName (tagName)); | |||||
| } | } | ||||
| XmlElement::XmlElement (const Identifier& tag) | XmlElement::XmlElement (const Identifier& tag) | ||||
| : tagName (tag.toString()) | : tagName (tag.toString()) | ||||
| { | { | ||||
| sanityCheckTagName (tagName); | |||||
| jassert (isValidXmlName (tagName)); | |||||
| } | } | ||||
| XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd) | XmlElement::XmlElement (String::CharPointerType tagNameStart, String::CharPointerType tagNameEnd) | ||||
| : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd)) | : tagName (StringPool::getGlobalPool().getPooledString (tagNameStart, tagNameEnd)) | ||||
| { | { | ||||
| sanityCheckTagName (tagName); | |||||
| jassert (isValidXmlName (tagName)); | |||||
| } | } | ||||
| XmlElement::XmlElement (int /*dummy*/) noexcept | XmlElement::XmlElement (int /*dummy*/) noexcept | ||||
| @@ -869,6 +889,21 @@ XmlElement* XmlElement::createTextElement (const String& text) | |||||
| return e; | 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) | void XmlElement::addTextElement (const String& text) | ||||
| { | { | ||||
| addChildElement (createTextElement (text)); | addChildElement (createTextElement (text)); | ||||
| @@ -724,6 +724,9 @@ public: | |||||
| /** Creates a text element that can be added to a parent element. */ | /** Creates a text element that can be added to a parent element. */ | ||||
| static XmlElement* createTextElement (const String& text); | static XmlElement* createTextElement (const String& text); | ||||
| /** Checks if a given string is a valid XML name */ | |||||
| static bool isValidXmlName (StringRef possibleName) noexcept; | |||||
| //============================================================================== | //============================================================================== | ||||
| private: | private: | ||||
| struct XmlAttributeNode | struct XmlAttributeNode | ||||
| @@ -1066,7 +1066,12 @@ public: | |||||
| for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) | for (int i = 1 + r.nextInt (numElementsInArray (buffer) - 2); --i >= 0;) | ||||
| buffer[i] = chars [r.nextInt (sizeof (chars) - 1)]; | 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) | static String createRandomWideCharString (Random& r) | ||||
| @@ -226,14 +226,14 @@ public: | |||||
| */ | */ | ||||
| ValueTree getChild (int index) const; | 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 | If no such node is found, it'll return an invalid node. (See isValid() to find out | ||||
| whether a node is valid). | whether a node is valid). | ||||
| @see getOrCreateChildWithName | @see getOrCreateChildWithName | ||||
| */ | */ | ||||
| ValueTree getChildWithName (const Identifier& type) const; | 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. | 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 | 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); | 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 | This will scan the child nodes in order, until it finds one that has property that matches | ||||
| the specified value. | the specified value. | ||||
| @@ -31,14 +31,15 @@ public: | |||||
| TimerThread() | TimerThread() | ||||
| : Thread ("Juce Timer"), | : Thread ("Juce Timer"), | ||||
| firstTimer (nullptr), | |||||
| callbackNeeded (0) | |||||
| firstTimer (nullptr) | |||||
| { | { | ||||
| triggerAsyncUpdate(); | triggerAsyncUpdate(); | ||||
| } | } | ||||
| ~TimerThread() noexcept | ~TimerThread() noexcept | ||||
| { | { | ||||
| signalThreadShouldExit(); | |||||
| callbackArrived.signal(); | |||||
| stopThread (4000); | stopThread (4000); | ||||
| jassert (instance == this || instance == nullptr); | jassert (instance == this || instance == nullptr); | ||||
| @@ -55,12 +56,6 @@ public: | |||||
| { | { | ||||
| const uint32 now = Time::getMillisecondCounter(); | const uint32 now = Time::getMillisecondCounter(); | ||||
| if (now == lastTime) | |||||
| { | |||||
| wait (1); | |||||
| continue; | |||||
| } | |||||
| const int elapsed = (int) (now >= lastTime ? (now - lastTime) | const int elapsed = (int) (now >= lastTime ? (now - lastTime) | ||||
| : (std::numeric_limits<uint32>::max() - (lastTime - now))); | : (std::numeric_limits<uint32>::max() - (lastTime - now))); | ||||
| lastTime = now; | lastTime = now; | ||||
| @@ -69,42 +64,29 @@ public: | |||||
| if (timeUntilFirstTimer <= 0) | 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(); | 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 | 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() | void callTimersSynchronously() | ||||
| @@ -186,7 +162,7 @@ public: | |||||
| private: | private: | ||||
| Timer* volatile firstTimer; | Timer* volatile firstTimer; | ||||
| Atomic<int> callbackNeeded; | |||||
| WaitableEvent callbackArrived; | |||||
| struct CallTimersMessage : public MessageManager::MessageBase | struct CallTimersMessage : public MessageManager::MessageBase | ||||
| { | { | ||||
| @@ -282,30 +282,25 @@ public: | |||||
| Colour withBrightness (float newBrightness) const noexcept; | Colour withBrightness (float newBrightness) const noexcept; | ||||
| /** Returns a copy of this colour with it hue rotated. | /** Returns a copy of this colour with it hue rotated. | ||||
| The new colour's hue is ((this->getHue() + amountToRotate) % 1.0) | The new colour's hue is ((this->getHue() + amountToRotate) % 1.0) | ||||
| @see brighter, darker, withMultipliedBrightness | @see brighter, darker, withMultipliedBrightness | ||||
| */ | */ | ||||
| Colour withRotatedHue (float amountToRotate) const noexcept; | Colour withRotatedHue (float amountToRotate) const noexcept; | ||||
| /** Returns a copy of this colour with its saturation multiplied by the given value. | /** Returns a copy of this colour with its saturation multiplied by the given value. | ||||
| The new colour's saturation is (this->getSaturation() * multiplier) | The new colour's saturation is (this->getSaturation() * multiplier) | ||||
| (the result is clipped to legal limits). | (the result is clipped to legal limits). | ||||
| */ | */ | ||||
| Colour withMultipliedSaturation (float multiplier) const noexcept; | Colour withMultipliedSaturation (float multiplier) const noexcept; | ||||
| /** Returns a copy of this colour with its brightness multiplied by the given value. | /** 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). | (the result is clipped to legal limits). | ||||
| */ | */ | ||||
| Colour withMultipliedBrightness (float amount) const noexcept; | Colour withMultipliedBrightness (float amount) const noexcept; | ||||
| //============================================================================== | //============================================================================== | ||||
| /** Returns a brighter version of this colour. | /** 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 | @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 | unchanged, and higher values make it brighter | ||||
| @see withMultipliedBrightness | @see withMultipliedBrightness | ||||
| @@ -313,7 +308,6 @@ public: | |||||
| Colour brighter (float amountBrighter = 0.4f) const noexcept; | Colour brighter (float amountBrighter = 0.4f) const noexcept; | ||||
| /** Returns a darker version of this colour. | /** 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 | @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 | unchanged, and higher values make it darker | ||||
| @see withMultipliedBrightness | @see withMultipliedBrightness | ||||
| @@ -197,11 +197,10 @@ ComboBox* AlertWindow::getComboBoxComponent (const String& nameOfList) const | |||||
| } | } | ||||
| //============================================================================== | //============================================================================== | ||||
| class AlertTextComp : public TextEditor | |||||
| class AlertTextComp : public TextEditor | |||||
| { | { | ||||
| public: | public: | ||||
| AlertTextComp (const String& message, | |||||
| const Font& font) | |||||
| AlertTextComp (AlertWindow& owner, const String& message, const Font& font) | |||||
| { | { | ||||
| setReadOnly (true); | setReadOnly (true); | ||||
| setMultiLine (true, true); | setMultiLine (true, true); | ||||
| @@ -215,6 +214,9 @@ public: | |||||
| bestWidth = 2 * (int) std::sqrt (font.getHeight() * font.getStringWidth (message)); | 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::backgroundColourId, Colours::transparentBlack); | ||||
| setColour (TextEditor::outlineColourId, Colours::transparentBlack); | setColour (TextEditor::outlineColourId, Colours::transparentBlack); | ||||
| setColour (TextEditor::shadowColourId, Colours::transparentBlack); | setColour (TextEditor::shadowColourId, Colours::transparentBlack); | ||||
| @@ -241,7 +243,7 @@ private: | |||||
| void AlertWindow::addTextBlock (const String& textBlock) | 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); | textBlocks.add (c); | ||||
| allComps.add (c); | allComps.add (c); | ||||
| @@ -428,18 +430,9 @@ void AlertWindow::updateLayout (const bool onlyIncreaseSize) | |||||
| } | } | ||||
| if (! isVisible()) | if (! isVisible()) | ||||
| { | |||||
| centreAroundComponent (associatedComponent, w, h); | centreAroundComponent (associatedComponent, w, h); | ||||
| } | |||||
| else | 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); | textArea.setBounds (edgeGap, edgeGap, w - (edgeGap * 2), h - edgeGap); | ||||
| @@ -99,9 +99,10 @@ public: | |||||
| // Check for the plugin creating its own floating window, and if there is one, | // Check for the plugin creating its own floating window, and if there is one, | ||||
| // we need to reparent it to make it visible.. | // 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[] = | EventTypeSpec windowEventTypes[] = | ||||
| { | { | ||||