| @@ -1,191 +1,191 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| #include <IOKit/IOKitLib.h> | |||
| #include <IOKit/network/IOEthernetInterface.h> | |||
| #include <IOKit/network/IONetworkInterface.h> | |||
| #include <IOKit/network/IOEthernetController.h> | |||
| #include <Carbon/Carbon.h> | |||
| #include <netdb.h> | |||
| #include <arpa/inet.h> | |||
| #include <netinet/in.h> | |||
| #include <sys/types.h> | |||
| #include <sys/socket.h> | |||
| #include <sys/wait.h> | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_core/text/juce_String.h" | |||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||
| #include "juce_mac_HTTPStream.h" | |||
| //============================================================================== | |||
| static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() | |||
| { | |||
| mach_port_t masterPort; | |||
| if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) | |||
| { | |||
| CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); | |||
| if (dict != 0) | |||
| { | |||
| CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, | |||
| 0, | |||
| &kCFTypeDictionaryKeyCallBacks, | |||
| &kCFTypeDictionaryValueCallBacks); | |||
| if (propDict != 0) | |||
| { | |||
| CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); | |||
| CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); | |||
| CFRelease (propDict); | |||
| } | |||
| } | |||
| return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; | |||
| } | |||
| return false; | |||
| } | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||
| { | |||
| int numResults = 0; | |||
| io_iterator_t it; | |||
| if (GetEthernetIterator (&it)) | |||
| { | |||
| io_object_t i; | |||
| while ((i = IOIteratorNext (it)) != 0) | |||
| { | |||
| io_object_t controller; | |||
| if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) | |||
| { | |||
| CFTypeRef data = IORegistryEntryCreateCFProperty (controller, | |||
| CFSTR (kIOMACAddress), | |||
| kCFAllocatorDefault, | |||
| 0); | |||
| if (data != 0) | |||
| { | |||
| UInt8 addr [kIOEthernetAddressSize]; | |||
| zeromem (addr, sizeof (addr)); | |||
| CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); | |||
| CFRelease (data); | |||
| int64 a = 0; | |||
| for (int i = 6; --i >= 0;) | |||
| a = (a << 8) | addr[i]; | |||
| if (! littleEndian) | |||
| a = (int64) swapByteOrder ((uint64) a); | |||
| if (numResults < maxNum) | |||
| { | |||
| *addresses++ = a; | |||
| ++numResults; | |||
| } | |||
| } | |||
| IOObjectRelease (controller); | |||
| } | |||
| IOObjectRelease (i); | |||
| } | |||
| IOObjectRelease (it); | |||
| } | |||
| return numResults; | |||
| } | |||
| //============================================================================== | |||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| String script; | |||
| script << "tell application \"Mail\"\r\n" | |||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||
| << emailSubject | |||
| << "\", content:\"" | |||
| << bodyText | |||
| << "\" & return & return}\r\n" | |||
| "tell newMessage\r\n" | |||
| "set visible to true\r\n" | |||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||
| "make new to recipient at end of to recipients with properties {address:\"" | |||
| << targetEmailAddress | |||
| << "\"}\r\n"; | |||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||
| { | |||
| script << "tell content\r\n" | |||
| "make new attachment with properties {file name:\"" | |||
| << filesToAttach[i] | |||
| << "\"} at after the last paragraph\r\n" | |||
| "end tell\r\n"; | |||
| } | |||
| script << "end tell\r\n" | |||
| "end tell\r\n"; | |||
| DBG (script); | |||
| ComponentInstance comp = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype); | |||
| OSAID resultID; | |||
| AEDesc source; | |||
| AECreateDesc (typeUTF8Text, (Ptr) script.toUTF8(), strlen (script.toUTF8()), &source); | |||
| OSAError err = OSACompileExecute (comp, &source, | |||
| kOSANullScript, kOSAModeNull, | |||
| &resultID); | |||
| AEDisposeDesc (&source); | |||
| CloseComponent (comp); | |||
| return false; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| #include <IOKit/IOKitLib.h> | |||
| #include <IOKit/network/IOEthernetInterface.h> | |||
| #include <IOKit/network/IONetworkInterface.h> | |||
| #include <IOKit/network/IOEthernetController.h> | |||
| #include <Carbon/Carbon.h> | |||
| #include <netdb.h> | |||
| #include <arpa/inet.h> | |||
| #include <netinet/in.h> | |||
| #include <sys/types.h> | |||
| #include <sys/socket.h> | |||
| #include <sys/wait.h> | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_core/text/juce_String.h" | |||
| #include "../../../src/juce_core/basics/juce_Time.h" | |||
| #include "../../../src/juce_core/basics/juce_SystemStats.h" | |||
| #include "../../../src/juce_core/containers/juce_MemoryBlock.h" | |||
| #include "../../../src/juce_core/text/juce_StringArray.h" | |||
| #include "../../../src/juce_core/misc/juce_PlatformUtilities.h" | |||
| #include "../../../src/juce_core/io/network/juce_URL.h" | |||
| #include "juce_mac_HTTPStream.h" | |||
| //============================================================================== | |||
| static bool GetEthernetIterator (io_iterator_t* matchingServices) throw() | |||
| { | |||
| mach_port_t masterPort; | |||
| if (IOMasterPort (MACH_PORT_NULL, &masterPort) == KERN_SUCCESS) | |||
| { | |||
| CFMutableDictionaryRef dict = IOServiceMatching (kIOEthernetInterfaceClass); | |||
| if (dict != 0) | |||
| { | |||
| CFMutableDictionaryRef propDict = CFDictionaryCreateMutable (kCFAllocatorDefault, | |||
| 0, | |||
| &kCFTypeDictionaryKeyCallBacks, | |||
| &kCFTypeDictionaryValueCallBacks); | |||
| if (propDict != 0) | |||
| { | |||
| CFDictionarySetValue (propDict, CFSTR (kIOPrimaryInterface), kCFBooleanTrue); | |||
| CFDictionarySetValue (dict, CFSTR (kIOPropertyMatchKey), propDict); | |||
| CFRelease (propDict); | |||
| } | |||
| } | |||
| return IOServiceGetMatchingServices (masterPort, dict, matchingServices) == KERN_SUCCESS; | |||
| } | |||
| return false; | |||
| } | |||
| int SystemStats::getMACAddresses (int64* addresses, int maxNum, const bool littleEndian) throw() | |||
| { | |||
| int numResults = 0; | |||
| io_iterator_t it; | |||
| if (GetEthernetIterator (&it)) | |||
| { | |||
| io_object_t i; | |||
| while ((i = IOIteratorNext (it)) != 0) | |||
| { | |||
| io_object_t controller; | |||
| if (IORegistryEntryGetParentEntry (i, kIOServicePlane, &controller) == KERN_SUCCESS) | |||
| { | |||
| CFTypeRef data = IORegistryEntryCreateCFProperty (controller, | |||
| CFSTR (kIOMACAddress), | |||
| kCFAllocatorDefault, | |||
| 0); | |||
| if (data != 0) | |||
| { | |||
| UInt8 addr [kIOEthernetAddressSize]; | |||
| zeromem (addr, sizeof (addr)); | |||
| CFDataGetBytes ((CFDataRef) data, CFRangeMake (0, sizeof (addr)), addr); | |||
| CFRelease (data); | |||
| int64 a = 0; | |||
| for (int i = 6; --i >= 0;) | |||
| a = (a << 8) | addr[i]; | |||
| if (! littleEndian) | |||
| a = (int64) swapByteOrder ((uint64) a); | |||
| if (numResults < maxNum) | |||
| { | |||
| *addresses++ = a; | |||
| ++numResults; | |||
| } | |||
| } | |||
| IOObjectRelease (controller); | |||
| } | |||
| IOObjectRelease (i); | |||
| } | |||
| IOObjectRelease (it); | |||
| } | |||
| return numResults; | |||
| } | |||
| //============================================================================== | |||
| bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAddress, | |||
| const String& emailSubject, | |||
| const String& bodyText, | |||
| const StringArray& filesToAttach) | |||
| { | |||
| String script; | |||
| script << "tell application \"Mail\"\r\n" | |||
| "set newMessage to make new outgoing message with properties {subject:\"" | |||
| << emailSubject | |||
| << "\", content:\"" | |||
| << bodyText | |||
| << "\" & return & return}\r\n" | |||
| "tell newMessage\r\n" | |||
| "set visible to true\r\n" | |||
| "set sender to \"sdfsdfsdfewf\"\r\n" | |||
| "make new to recipient at end of to recipients with properties {address:\"" | |||
| << targetEmailAddress | |||
| << "\"}\r\n"; | |||
| for (int i = 0; i < filesToAttach.size(); ++i) | |||
| { | |||
| script << "tell content\r\n" | |||
| "make new attachment with properties {file name:\"" | |||
| << filesToAttach[i] | |||
| << "\"} at after the last paragraph\r\n" | |||
| "end tell\r\n"; | |||
| } | |||
| script << "end tell\r\n" | |||
| "end tell\r\n"; | |||
| DBG (script); | |||
| ComponentInstance comp = OpenDefaultComponent (kOSAComponentType, kOSAGenericScriptingComponentSubtype); | |||
| OSAID resultID; | |||
| AEDesc source; | |||
| AECreateDesc (typeUTF8Text, (Ptr) script.toUTF8(), strlen (script.toUTF8()), &source); | |||
| OSAError err = OSACompileExecute (comp, &source, | |||
| kOSANullScript, kOSAModeNull, | |||
| &resultID); | |||
| AEDisposeDesc (&source); | |||
| CloseComponent (comp); | |||
| return false; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -235,7 +235,7 @@ bool JUCE_CALLTYPE juce_isRunningUnderDebugger() throw() | |||
| int m[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; | |||
| size_t sz = sizeof (info); | |||
| sysctl (m, 4, &info, &sz, 0, 0); | |||
| testResult = (info.kp_proc.p_flag & P_TRACED) == P_TRACED ? 1 : -1; | |||
| testResult = ((info.kp_proc.p_flag & P_TRACED) != 0) ? 1 : -1; | |||
| } | |||
| return testResult > 0; | |||
| @@ -36,13 +36,18 @@ | |||
| #include "win32_headers.h" | |||
| #include <stddef.h> | |||
| #include <imapi.h> | |||
| #include <imapierror.h> | |||
| #include "../../../src/juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "../../../src/juce_appframework/audio/audio_file_formats/juce_AudioCDReader.h" | |||
| #include "../../../src/juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h" | |||
| #include "../../../src/juce_appframework/events/juce_Timer.h" | |||
| #include "../../../src/juce_appframework/application/juce_DeletedAtShutdown.h" | |||
| #include "../../../src/juce_appframework/audio/dsp/juce_AudioDataConverters.h" | |||
| #ifdef _MSC_VER | |||
| #pragma warning (pop) | |||
| @@ -2108,4 +2113,381 @@ void AudioCDReader::ejectDisk() | |||
| ((CDDeviceWrapper*) handle)->cdH->openDrawer (true); | |||
| } | |||
| //============================================================================== | |||
| static IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master) | |||
| { | |||
| CoInitialize (0); | |||
| IDiscMaster* dm; | |||
| IDiscRecorder* result = 0; | |||
| if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0, | |||
| CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, | |||
| IID_IDiscMaster, | |||
| (void**) &dm))) | |||
| { | |||
| if (SUCCEEDED (dm->Open())) | |||
| { | |||
| IEnumDiscRecorders* drEnum = 0; | |||
| if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum))) | |||
| { | |||
| IDiscRecorder* dr = 0; | |||
| DWORD dummy; | |||
| int index = 0; | |||
| while (drEnum->Next (1, &dr, &dummy) == S_OK) | |||
| { | |||
| if (indexToOpen == index) | |||
| { | |||
| result = dr; | |||
| break; | |||
| } | |||
| else if (list != 0) | |||
| { | |||
| BSTR path; | |||
| if (SUCCEEDED (dr->GetPath (&path))) | |||
| list->add ((const WCHAR*) path); | |||
| } | |||
| ++index; | |||
| dr->Release(); | |||
| } | |||
| drEnum->Release(); | |||
| } | |||
| /*if (redbookFormat != 0) | |||
| { | |||
| IEnumDiscMasterFormats* mfEnum; | |||
| if (SUCCEEDED (dm->EnumDiscMasterFormats (&mfEnum))) | |||
| { | |||
| IID formatIID; | |||
| DWORD dummy; | |||
| while (mfEnum->Next (1, &formatIID, &dummy) == S_OK) | |||
| { | |||
| } | |||
| mfEnum->Release(); | |||
| } | |||
| redbookFormat | |||
| }*/ | |||
| if (master == 0) | |||
| dm->Close(); | |||
| } | |||
| if (master != 0) | |||
| *master = dm; | |||
| else | |||
| dm->Release(); | |||
| } | |||
| return result; | |||
| } | |||
| const StringArray AudioCDBurner::findAvailableDevices() | |||
| { | |||
| StringArray devs; | |||
| enumCDBurners (&devs, -1, 0); | |||
| return devs; | |||
| } | |||
| AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) | |||
| { | |||
| AudioCDBurner* b = new AudioCDBurner (deviceIndex); | |||
| if (b->internal == 0) | |||
| deleteAndZero (b); | |||
| return b; | |||
| } | |||
| class CDBurnerInfo : public IDiscMasterProgressEvents | |||
| { | |||
| public: | |||
| CDBurnerInfo() | |||
| : refCount (1), | |||
| progress (0), | |||
| shouldCancel (false), | |||
| listener (0) | |||
| { | |||
| } | |||
| ~CDBurnerInfo() | |||
| { | |||
| } | |||
| HRESULT __stdcall QueryInterface (REFIID id, void __RPC_FAR* __RPC_FAR* result) | |||
| { | |||
| if (result == 0) | |||
| return E_POINTER; | |||
| if (id == IID_IUnknown || id == IID_IDiscMasterProgressEvents) | |||
| { | |||
| AddRef(); | |||
| *result = this; | |||
| return S_OK; | |||
| } | |||
| *result = 0; | |||
| return E_NOINTERFACE; | |||
| } | |||
| ULONG __stdcall AddRef() { return ++refCount; } | |||
| ULONG __stdcall Release() { jassert (refCount > 0); const int r = --refCount; if (r == 0) delete this; return r; } | |||
| HRESULT __stdcall QueryCancel (boolean* pbCancel) | |||
| { | |||
| if (listener != 0 && ! shouldCancel) | |||
| shouldCancel = listener->audioCDBurnProgress (progress); | |||
| *pbCancel = shouldCancel; | |||
| return S_OK; | |||
| } | |||
| HRESULT __stdcall NotifyBlockProgress (long nCompleted, long nTotal) | |||
| { | |||
| progress = nCompleted / (float) nTotal; | |||
| shouldCancel = listener != 0 && listener->audioCDBurnProgress (progress); | |||
| return E_NOTIMPL; | |||
| } | |||
| HRESULT __stdcall NotifyPnPActivity (void) { return E_NOTIMPL; } | |||
| HRESULT __stdcall NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; } | |||
| HRESULT __stdcall NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; } | |||
| HRESULT __stdcall NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } | |||
| HRESULT __stdcall NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } | |||
| HRESULT __stdcall NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; } | |||
| HRESULT __stdcall NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; } | |||
| IDiscMaster* discMaster; | |||
| IDiscRecorder* discRecorder; | |||
| IRedbookDiscMaster* redbook; | |||
| AudioCDBurner::BurnProgressListener* listener; | |||
| float progress; | |||
| bool shouldCancel; | |||
| private: | |||
| int refCount; | |||
| }; | |||
| AudioCDBurner::AudioCDBurner (const int deviceIndex) | |||
| : internal (0) | |||
| { | |||
| IDiscMaster* discMaster; | |||
| IDiscRecorder* dr = enumCDBurners (0, deviceIndex, &discMaster); | |||
| if (dr != 0) | |||
| { | |||
| IRedbookDiscMaster* redbook; | |||
| HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); | |||
| hr = discMaster->SetActiveDiscRecorder (dr); | |||
| CDBurnerInfo* const info = new CDBurnerInfo(); | |||
| internal = info; | |||
| info->discMaster = discMaster; | |||
| info->discRecorder = dr; | |||
| info->redbook = redbook; | |||
| } | |||
| } | |||
| AudioCDBurner::~AudioCDBurner() | |||
| { | |||
| CDBurnerInfo* const info = (CDBurnerInfo*) internal; | |||
| if (info != 0) | |||
| { | |||
| info->discRecorder->Close(); | |||
| info->redbook->Release(); | |||
| info->discRecorder->Release(); | |||
| info->discMaster->Release(); | |||
| info->Release(); | |||
| } | |||
| } | |||
| bool AudioCDBurner::isDiskPresent() const | |||
| { | |||
| CDBurnerInfo* const info = (CDBurnerInfo*) internal; | |||
| HRESULT hr = info->discRecorder->OpenExclusive(); | |||
| long type, flags; | |||
| hr = info->discRecorder->QueryMediaType (&type, &flags); | |||
| info->discRecorder->Close(); | |||
| return hr == S_OK && type != 0 && (flags & MEDIA_WRITABLE) != 0; | |||
| } | |||
| int AudioCDBurner::getNumAvailableAudioBlocks() const | |||
| { | |||
| CDBurnerInfo* const info = (CDBurnerInfo*) internal; | |||
| long blocksFree = 0; | |||
| info->redbook->GetAvailableAudioTrackBlocks (&blocksFree); | |||
| return blocksFree; | |||
| } | |||
| const String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, | |||
| const bool ejectDiscAfterwards) | |||
| { | |||
| CDBurnerInfo* const info = (CDBurnerInfo*) internal; | |||
| info->listener = listener; | |||
| info->progress = 0; | |||
| info->shouldCancel = false; | |||
| UINT cookie; | |||
| HRESULT hr = info->discMaster->ProgressAdvise (info, &cookie); | |||
| hr = info->discMaster->RecordDisc (FALSE, // set this to TRUE to make it do a fake burn, without actually writing to the disc | |||
| ejectDiscAfterwards); | |||
| String error; | |||
| if (hr != S_OK) | |||
| { | |||
| const char* e = "Couldn't open or write to the CD device"; | |||
| if (hr == IMAPI_E_USERABORT) | |||
| e = "User cancelled the write operation"; | |||
| else if (hr == IMAPI_E_MEDIUM_NOTPRESENT || hr == IMAPI_E_TRACKOPEN) | |||
| e = "No Disk present"; | |||
| error = e; | |||
| } | |||
| info->discMaster->ProgressUnadvise (cookie); | |||
| info->listener = 0; | |||
| return error; | |||
| } | |||
| bool AudioCDBurner::addAudioTrack (AudioFormatReader& source, int numSamples) | |||
| { | |||
| CDBurnerInfo* const info = (CDBurnerInfo*) internal; | |||
| long bytesPerBlock; | |||
| HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock); | |||
| const int samplesPerBlock = bytesPerBlock / 4; | |||
| bool ok = true; | |||
| hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); | |||
| byte* const buffer = (byte*) juce_malloc (bytesPerBlock); | |||
| int* sourceBuffers[2]; | |||
| sourceBuffers[0] = (int*) juce_malloc (samplesPerBlock * 4); | |||
| sourceBuffers[1] = (int*) juce_malloc (samplesPerBlock * 4); | |||
| int samplesDone = 0; | |||
| for (;;) | |||
| { | |||
| zeromem (buffer, bytesPerBlock); | |||
| if (! source.read (sourceBuffers, samplesDone, samplesPerBlock)) | |||
| { | |||
| ok = false; | |||
| break; | |||
| } | |||
| short* destBuffer = (short*) buffer; | |||
| for (int j = 0; j < samplesPerBlock; ++j) | |||
| { | |||
| *destBuffer++ = (short) (sourceBuffers [0][j] >> 16); | |||
| *destBuffer++ = (short) (sourceBuffers [jmin (1, source.numChannels - 1)][j] >> 16); | |||
| } | |||
| hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); | |||
| if (hr != S_OK) | |||
| { | |||
| ok = false; | |||
| break; | |||
| } | |||
| samplesDone += samplesPerBlock; | |||
| if (samplesDone >= numSamples) | |||
| break; | |||
| } | |||
| juce_free (sourceBuffers[0]); | |||
| juce_free (sourceBuffers[1]); | |||
| juce_free (buffer); | |||
| hr = info->redbook->CloseAudioTrack(); | |||
| return ok && hr == S_OK; | |||
| } | |||
| bool AudioCDBurner::addAudioTrack (AudioSource& source, int numSamples) | |||
| { | |||
| CDBurnerInfo* const info = (CDBurnerInfo*) internal; | |||
| long bytesPerBlock; | |||
| HRESULT hr = info->redbook->GetAudioBlockSize (&bytesPerBlock); | |||
| const int samplesPerBlock = bytesPerBlock / 4; | |||
| bool ok = true; | |||
| hr = info->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); | |||
| byte* const buffer = (byte*) juce_malloc (bytesPerBlock); | |||
| AudioSampleBuffer sourceBuffer (2, samplesPerBlock); | |||
| int samplesDone = 0; | |||
| source.prepareToPlay (samplesPerBlock, 44100.0); | |||
| while (ok) | |||
| { | |||
| { | |||
| AudioSourceChannelInfo info; | |||
| info.buffer = &sourceBuffer; | |||
| info.numSamples = samplesPerBlock; | |||
| info.startSample = 0; | |||
| sourceBuffer.clear(); | |||
| source.getNextAudioBlock (info); | |||
| } | |||
| zeromem (buffer, bytesPerBlock); | |||
| AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (0, 0), | |||
| buffer, samplesPerBlock, 4); | |||
| AudioDataConverters::convertFloatToInt16LE (sourceBuffer.getSampleData (1, 0), | |||
| buffer + 2, samplesPerBlock, 4); | |||
| hr = info->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); | |||
| if (hr != S_OK) | |||
| ok = false; | |||
| samplesDone += samplesPerBlock; | |||
| if (samplesDone >= numSamples) | |||
| break; | |||
| } | |||
| juce_free (buffer); | |||
| hr = info->redbook->CloseAudioTrack(); | |||
| return ok && hr == S_OK; | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -417,7 +417,7 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd | |||
| files[i].lpszPathName = (LPTSTR) (LPCTSTR) filesToAttach [i]; | |||
| } | |||
| ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG, 0) == SUCCESS_SUCCESS); | |||
| ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS); | |||
| } | |||
| FreeLibrary (h); | |||
| @@ -533,6 +533,10 @@ | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AiffAudioFormat.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioCDBurner.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioCDReader.h" | |||
| > | |||
| @@ -641,6 +645,22 @@ | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioSubsectionReader.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioThumbnail.cpp" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioThumbnail.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioThumbnailCache.cpp" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_AudioThumbnailCache.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_appframework\audio\audio_file_formats\juce_FlacAudioFormat.cpp" | |||
| > | |||
| @@ -5242,6 +5262,14 @@ | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_BufferedInputStream.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_FileInputSource.cpp" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_FileInputSource.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_GZIPCompressorOutputStream.cpp" | |||
| > | |||
| @@ -5330,6 +5358,10 @@ | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_GZIPDecompressorInputStream.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_InputSource.h" | |||
| > | |||
| </File> | |||
| <File | |||
| RelativePath="..\..\..\src\juce_core\io\streams\juce_MemoryInputStream.cpp" | |||
| > | |||
| @@ -20,6 +20,10 @@ Changelist for version 1.46 | |||
| - added a parameter to DragAndDropTarget::isInterestedInDragSource(). This ma | |||
| - changed the parameters to AudioIODeviceCallback::audioDeviceAboutToStart(), so that it now just supplies a pointer to the device. If you need to, you can still find out the sample rate and block size by asking the device for them. | |||
| - changes to the URL class to allow file uploading | |||
| - new method: PlatformUtilities::launchEmailWithAttachments | |||
| - new classes: AudioThumbnail and AudioThumbnailCache, which allow easy rendering of low-res waveform previews | |||
| - new classes: InputSource and FileInputSource. These encapsulate some kind of resource, and also replace the XmlInputSource class. | |||
| ============================================================================== | |||
| Changelist for version 1.45 | |||
| @@ -32,7 +32,7 @@ | |||
| 84EAE2F309DAAF0B00288D0A /* AGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAE2F109DAAF0B00288D0A /* AGL.framework */; }; | |||
| 84EAE2F409DAAF0B00288D0A /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84EAE2F209DAAF0B00288D0A /* OpenGL.framework */; }; | |||
| 84F8DB0B099CA8DD00E911ED /* QuickTimeDemo.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 84F8DB0A099CA8DD00E911ED /* QuickTimeDemo.cpp */; }; | |||
| 84FDB0950C15BDDD00CD0087 /* libjuce.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FDB0940C15BDCE00CD0087 /* libjuce.a */; }; | |||
| 84FDB0950C15BDDD00CD0087 /* libjucedebug.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */; }; | |||
| 8D0C4E8D0486CD37000505A6 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0867D6AAFE840B52C02AAC07 /* InfoPlist.strings */; }; | |||
| 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 20286C33FDCF999611CA2CEA /* Carbon.framework */; }; | |||
| /* End PBXBuildFile section */ | |||
| @@ -77,7 +77,7 @@ | |||
| 84EAE2F909DAAF2F00288D0A /* Juce.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Juce.xcodeproj; path = ../../../../build/macosx/Juce.xcodeproj; sourceTree = SOURCE_ROOT; }; | |||
| 84F8DB0A099CA8DD00E911ED /* QuickTimeDemo.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = QuickTimeDemo.cpp; sourceTree = "<group>"; }; | |||
| 8D0C4E960486CD37000505A6 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; }; | |||
| 8D0C4E970486CD37000505A6 /* jucedemo.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = jucedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| 8D0C4E970486CD37000505A6 /* jucedemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = jucedemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; | |||
| /* End PBXFileReference section */ | |||
| /* Begin PBXFrameworksBuildPhase section */ | |||
| @@ -85,7 +85,7 @@ | |||
| isa = PBXFrameworksBuildPhase; | |||
| buildActionMask = 2147483647; | |||
| files = ( | |||
| 84FDB0950C15BDDD00CD0087 /* libjuce.a in Frameworks */, | |||
| 84FDB0950C15BDDD00CD0087 /* libjucedebug.a in Frameworks */, | |||
| 8D0C4E920486CD37000505A6 /* Carbon.framework in Frameworks */, | |||
| 84C49F2606C0F3200066071B /* CoreAudio.framework in Frameworks */, | |||
| 84C4A41106C0F9A00066071B /* IOKit.framework in Frameworks */, | |||
| @@ -181,7 +181,7 @@ | |||
| 84FDB0900C15BDCE00CD0087 /* Products */ = { | |||
| isa = PBXGroup; | |||
| children = ( | |||
| 84FDB0940C15BDCE00CD0087 /* libjuce.a */, | |||
| 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */, | |||
| ); | |||
| name = Products; | |||
| sourceTree = "<group>"; | |||
| @@ -228,6 +228,7 @@ | |||
| 20286C28FDCF999611CA2CEA /* Project object */ = { | |||
| isa = PBXProject; | |||
| buildConfigurationList = 84CFBCCF085F14F000C52F85 /* Build configuration list for PBXProject "jucedemo" */; | |||
| compatibilityVersion = "Xcode 2.4"; | |||
| hasScannedForEncodings = 1; | |||
| mainGroup = 20286C29FDCF999611CA2CEA /* jucedemo */; | |||
| projectDirPath = ""; | |||
| @@ -237,6 +238,7 @@ | |||
| ProjectRef = 84EAE2F909DAAF2F00288D0A /* Juce.xcodeproj */; | |||
| }, | |||
| ); | |||
| projectRoot = ""; | |||
| targets = ( | |||
| 8D0C4E890486CD37000505A6 /* jucedemo */, | |||
| ); | |||
| @@ -244,10 +246,10 @@ | |||
| /* End PBXProject section */ | |||
| /* Begin PBXReferenceProxy section */ | |||
| 84FDB0940C15BDCE00CD0087 /* libjuce.a */ = { | |||
| 84FDB0940C15BDCE00CD0087 /* libjucedebug.a */ = { | |||
| isa = PBXReferenceProxy; | |||
| fileType = archive.ar; | |||
| path = libjuce.a; | |||
| path = libjucedebug.a; | |||
| remoteRef = 84FDB0930C15BDCE00CD0087 /* PBXContainerItemProxy */; | |||
| sourceTree = BUILT_PRODUCTS_DIR; | |||
| }; | |||
| @@ -197,6 +197,9 @@ | |||
| #ifndef __JUCE_AIFFAUDIOFORMAT_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_AiffAudioFormat.h" | |||
| #endif | |||
| #ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_AudioCDBurner.h" | |||
| #endif | |||
| #ifndef __JUCE_AUDIOCDREADER_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_AudioCDReader.h" | |||
| #endif | |||
| @@ -215,6 +218,12 @@ | |||
| #ifndef __JUCE_AUDIOSUBSECTIONREADER_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_AudioSubsectionReader.h" | |||
| #endif | |||
| #ifndef __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_AudioThumbnail.h" | |||
| #endif | |||
| #ifndef __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_AudioThumbnailCache.h" | |||
| #endif | |||
| #ifndef __JUCE_FLACAUDIOFORMAT_JUCEHEADER__ | |||
| #include "juce_appframework/audio/audio_file_formats/juce_FlacAudioFormat.h" | |||
| #endif | |||
| @@ -0,0 +1,89 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef __JUCE_AUDIOCDBURNER_JUCEHEADER__ | |||
| #define __JUCE_AUDIOCDBURNER_JUCEHEADER__ | |||
| #include "juce_AudioFormatReader.h" | |||
| #include "../audio_sources/juce_AudioSource.h" | |||
| //============================================================================== | |||
| /** | |||
| */ | |||
| class AudioCDBurner | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| static const StringArray findAvailableDevices(); | |||
| static AudioCDBurner* openDevice (const int deviceIndex); | |||
| ~AudioCDBurner(); | |||
| //============================================================================== | |||
| bool isDiskPresent() const; | |||
| int getNumAvailableAudioBlocks() const; | |||
| bool addAudioTrack (AudioFormatReader& source, int numSamples); | |||
| bool addAudioTrack (AudioSource& source, int numSamples); | |||
| /** | |||
| Return true to cancel the current burn operation | |||
| */ | |||
| class BurnProgressListener | |||
| { | |||
| public: | |||
| BurnProgressListener() throw() {} | |||
| virtual ~BurnProgressListener() {} | |||
| /** Called at intervals to report on the progress of the AudioCDBurner. | |||
| To cancel the burn, return true from this. | |||
| */ | |||
| virtual bool audioCDBurnProgress (float proportionComplete) = 0; | |||
| }; | |||
| const String burn (BurnProgressListener* listener, | |||
| const bool ejectDiscAfterwards); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| AudioCDBurner (const int deviceIndex); | |||
| void* internal; | |||
| }; | |||
| #endif // __JUCE_AUDIOCDBURNER_JUCEHEADER__ | |||
| @@ -0,0 +1,535 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_AudioThumbnail.h" | |||
| #include "juce_AudioThumbnailCache.h" | |||
| const int timeBeforeDeletingReader = 1000; | |||
| //============================================================================== | |||
| struct AudioThumbnailDataFormat | |||
| { | |||
| char thumbnailMagic[4]; | |||
| int samplesPerThumbSample; | |||
| int64 totalSamples; // source samples | |||
| int64 numFinishedSamples; // source samples | |||
| int numThumbnailSamples; | |||
| int numChannels; | |||
| int sampleRate; | |||
| char future[16]; | |||
| char data[1]; | |||
| }; | |||
| #if JUCE_BIG_ENDIAN | |||
| static void swap (int& n) { n = (int) swapByteOrder ((uint32) n); } | |||
| static void swap (int64& n) { n = (int64) swapByteOrder ((uint64) n); } | |||
| #endif | |||
| static void swapEndiannessIfNeeded (AudioThumbnailDataFormat* const d) | |||
| { | |||
| (void) d; | |||
| #if JUCE_BIG_ENDIAN | |||
| swap (d->samplesPerThumbSample); | |||
| swap (d->totalSamples); | |||
| swap (d->numFinishedSamples); | |||
| swap (d->numThumbnailSamples); | |||
| swap (d->numChannels); | |||
| swap (d->sampleRate); | |||
| #endif | |||
| } | |||
| //============================================================================== | |||
| AudioThumbnail::AudioThumbnail (const int orginalSamplesPerThumbnailSample_, | |||
| AudioFormatManager& formatManagerToUse_, | |||
| AudioThumbnailCache& cacheToUse) | |||
| : cache (cacheToUse), | |||
| formatManagerToUse (formatManagerToUse_), | |||
| source (0), | |||
| reader (0), | |||
| orginalSamplesPerThumbnailSample (orginalSamplesPerThumbnailSample_) | |||
| { | |||
| clear(); | |||
| } | |||
| AudioThumbnail::~AudioThumbnail() | |||
| { | |||
| cache.removeThumbnail (this); | |||
| const ScopedLock sl (readerLock); | |||
| deleteAndZero (reader); | |||
| delete source; | |||
| } | |||
| void AudioThumbnail::setSource (InputSource* const newSource) | |||
| { | |||
| cache.removeThumbnail (this); | |||
| delete source; | |||
| source = newSource; | |||
| { | |||
| const ScopedLock sl (readerLock); | |||
| deleteAndZero (reader); | |||
| reader = createReader(); | |||
| } | |||
| clear(); | |||
| if (reader != 0) | |||
| { | |||
| startTimer (timeBeforeDeletingReader); | |||
| initialiseFromAudioFile (*reader); | |||
| cache.loadThumb (*this, newSource->hashCode()); | |||
| if (! isFullyLoaded()) | |||
| cache.addThumbnail (this); | |||
| } | |||
| } | |||
| bool AudioThumbnail::useTimeSlice() | |||
| { | |||
| const ScopedLock sl (readerLock); | |||
| if (isFullyLoaded()) | |||
| { | |||
| if (reader != 0) | |||
| startTimer (timeBeforeDeletingReader); | |||
| cache.removeThumbnail (this); | |||
| return false; | |||
| } | |||
| if (reader == 0) | |||
| reader = createReader(); | |||
| if (reader != 0) | |||
| { | |||
| readNextBlockFromAudioFile (*reader); | |||
| startTimer (timeBeforeDeletingReader); | |||
| sendChangeMessage (this); | |||
| const bool justFinished = isFullyLoaded(); | |||
| if (justFinished) | |||
| cache.storeThumb (*this, source->hashCode()); | |||
| return ! justFinished; | |||
| } | |||
| return false; | |||
| } | |||
| AudioFormatReader* AudioThumbnail::createReader() const | |||
| { | |||
| if (source != 0) | |||
| { | |||
| InputStream* const audioFileStream = source->createInputStream(); | |||
| if (audioFileStream != 0) | |||
| return formatManagerToUse.createReaderFor (audioFileStream); | |||
| } | |||
| return 0; | |||
| } | |||
| void AudioThumbnail::timerCallback() | |||
| { | |||
| stopTimer(); | |||
| const ScopedLock sl (readerLock); | |||
| deleteAndZero (reader); | |||
| } | |||
| void AudioThumbnail::clear() | |||
| { | |||
| data.setSize (sizeof (AudioThumbnailDataFormat) + 3); | |||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||
| d->thumbnailMagic[0] = 'j'; | |||
| d->thumbnailMagic[1] = 'a'; | |||
| d->thumbnailMagic[2] = 't'; | |||
| d->thumbnailMagic[3] = 'm'; | |||
| d->samplesPerThumbSample = orginalSamplesPerThumbnailSample; | |||
| d->totalSamples = 0; | |||
| d->numFinishedSamples = 0; | |||
| d->numThumbnailSamples = 0; | |||
| d->numChannels = 0; | |||
| d->sampleRate = 0; | |||
| numSamplesCached = 0; | |||
| } | |||
| void AudioThumbnail::loadFrom (InputStream& input) | |||
| { | |||
| data.setSize (0); | |||
| input.readIntoMemoryBlock (data); | |||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||
| swapEndiannessIfNeeded (d); | |||
| if (! (d->thumbnailMagic[0] == 'j' | |||
| && d->thumbnailMagic[1] == 'a' | |||
| && d->thumbnailMagic[2] == 't' | |||
| && d->thumbnailMagic[3] == 'm')) | |||
| { | |||
| clear(); | |||
| } | |||
| numSamplesCached = 0; | |||
| } | |||
| void AudioThumbnail::saveTo (OutputStream& output) const | |||
| { | |||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||
| swapEndiannessIfNeeded (d); | |||
| output.write (data.getData(), data.getSize()); | |||
| swapEndiannessIfNeeded (d); | |||
| } | |||
| bool AudioThumbnail::initialiseFromAudioFile (AudioFormatReader& reader) | |||
| { | |||
| AudioThumbnailDataFormat* d = (AudioThumbnailDataFormat*) data.getData(); | |||
| d->totalSamples = reader.lengthInSamples; | |||
| d->numChannels = jmin (2, reader.numChannels); | |||
| d->numFinishedSamples = 0; | |||
| d->sampleRate = roundDoubleToInt (reader.sampleRate); | |||
| d->numThumbnailSamples = (int) (d->totalSamples / d->samplesPerThumbSample) + 1; | |||
| data.setSize (sizeof (AudioThumbnailDataFormat) + 3 + d->numThumbnailSamples * d->numChannels * 2); | |||
| d = (AudioThumbnailDataFormat*) data.getData(); | |||
| zeromem (&(d->data[0]), d->numThumbnailSamples * d->numChannels * 2); | |||
| return d->totalSamples > 0; | |||
| } | |||
| bool AudioThumbnail::readNextBlockFromAudioFile (AudioFormatReader& reader) | |||
| { | |||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||
| if (d->numFinishedSamples < d->totalSamples) | |||
| { | |||
| const int numToDo = (int) jmin ((int64) 65536, d->totalSamples - d->numFinishedSamples); | |||
| generateSection (reader, | |||
| d->numFinishedSamples, | |||
| numToDo); | |||
| d->numFinishedSamples += numToDo; | |||
| } | |||
| numSamplesCached = 0; // zap the cache | |||
| return (d->numFinishedSamples < d->totalSamples); | |||
| } | |||
| int AudioThumbnail::getNumChannels() const throw() | |||
| { | |||
| const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); | |||
| jassert (d != 0); | |||
| return d->numChannels; | |||
| } | |||
| double AudioThumbnail::getTotalLength() const throw() | |||
| { | |||
| const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); | |||
| jassert (d != 0); | |||
| if (d->sampleRate > 0) | |||
| return d->totalSamples / (double)d->sampleRate; | |||
| else | |||
| return 0.0; | |||
| } | |||
| void AudioThumbnail::generateSection (AudioFormatReader& reader, | |||
| int64 startSample, | |||
| int numSamples) | |||
| { | |||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||
| jassert (d != 0); | |||
| int firstDataPos = (int) (startSample / d->samplesPerThumbSample); | |||
| int lastDataPos = (int) ((startSample + numSamples) / d->samplesPerThumbSample); | |||
| char* l = getChannelData (0); | |||
| char* r = getChannelData (1); | |||
| for (int i = firstDataPos; i < lastDataPos; ++i) | |||
| { | |||
| const int sourceStart = i * d->samplesPerThumbSample; | |||
| const int sourceEnd = sourceStart + d->samplesPerThumbSample; | |||
| float lowestLeft, highestLeft, lowestRight, highestRight; | |||
| reader.readMaxLevels (sourceStart, | |||
| sourceEnd - sourceStart, | |||
| lowestLeft, | |||
| highestLeft, | |||
| lowestRight, | |||
| highestRight); | |||
| int n = i * 2; | |||
| if (r != 0) | |||
| { | |||
| l [n] = (char) jlimit (-128.0f, 127.0f, lowestLeft * 127.0f); | |||
| r [n++] = (char) jlimit (-128.0f, 127.0f, lowestRight * 127.0f); | |||
| l [n] = (char) jlimit (-128.0f, 127.0f, highestLeft * 127.0f); | |||
| r [n++] = (char) jlimit (-128.0f, 127.0f, highestRight * 127.0f); | |||
| } | |||
| else | |||
| { | |||
| l [n++] = (char) jlimit (-128.0f, 127.0f, lowestLeft * 127.0f); | |||
| l [n++] = (char) jlimit (-128.0f, 127.0f, highestLeft * 127.0f); | |||
| } | |||
| } | |||
| } | |||
| char* AudioThumbnail::getChannelData (int channel) const | |||
| { | |||
| AudioThumbnailDataFormat* const d = (AudioThumbnailDataFormat*) data.getData(); | |||
| jassert (d != 0); | |||
| if (channel >= 0 && channel < d->numChannels) | |||
| return d->data + (channel * 2 * d->numThumbnailSamples); | |||
| return 0; | |||
| } | |||
| bool AudioThumbnail::isFullyLoaded() const throw() | |||
| { | |||
| const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); | |||
| jassert (d != 0); | |||
| return d->numFinishedSamples >= d->totalSamples; | |||
| } | |||
| void AudioThumbnail::refillCache (const int numSamples, | |||
| double startTime, | |||
| const double timePerPixel) | |||
| { | |||
| const AudioThumbnailDataFormat* const d = (const AudioThumbnailDataFormat*) data.getData(); | |||
| jassert (d != 0); | |||
| if (numSamples <= 0 | |||
| || timePerPixel <= 0.0 | |||
| || d->sampleRate <= 0) | |||
| { | |||
| numSamplesCached = 0; | |||
| return; | |||
| } | |||
| if (numSamples == numSamplesCached | |||
| && numChannelsCached == d->numChannels | |||
| && startTime == cachedStart | |||
| && timePerPixel == cachedTimePerPixel) | |||
| { | |||
| return; | |||
| } | |||
| numSamplesCached = numSamples; | |||
| numChannelsCached = d->numChannels; | |||
| cachedStart = startTime; | |||
| cachedTimePerPixel = timePerPixel; | |||
| cachedLevels.ensureSize (2 * numChannelsCached * numSamples); | |||
| const bool needExtraDetail = (timePerPixel * d->sampleRate <= d->samplesPerThumbSample); | |||
| const ScopedLock sl (readerLock); | |||
| if (needExtraDetail && reader == 0) | |||
| reader = createReader(); | |||
| if (reader != 0 && timePerPixel * d->sampleRate <= d->samplesPerThumbSample) | |||
| { | |||
| startTimer (timeBeforeDeletingReader); | |||
| char* cacheData = (char*) cachedLevels; | |||
| int sample = roundDoubleToInt (startTime * d->sampleRate); | |||
| for (int i = numSamples; --i >= 0;) | |||
| { | |||
| const int nextSample = roundDoubleToInt ((startTime + timePerPixel) * d->sampleRate); | |||
| if (sample >= 0) | |||
| { | |||
| if (sample >= reader->lengthInSamples) | |||
| break; | |||
| float lmin, lmax, rmin, rmax; | |||
| reader->readMaxLevels (sample, | |||
| jmax (1, nextSample - sample), | |||
| lmin, lmax, rmin, rmax); | |||
| cacheData[0] = (char) jlimit (-128, 127, roundFloatToInt (lmin * 127.0f)); | |||
| cacheData[1] = (char) jlimit (-128, 127, roundFloatToInt (lmax * 127.0f)); | |||
| if (numChannelsCached > 1) | |||
| { | |||
| cacheData[2] = (char) jlimit (-128, 127, roundFloatToInt (rmin * 127.0f)); | |||
| cacheData[3] = (char) jlimit (-128, 127, roundFloatToInt (rmax * 127.0f)); | |||
| } | |||
| cacheData += 2 * numChannelsCached; | |||
| } | |||
| startTime += timePerPixel; | |||
| sample = nextSample; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (int channelNum = 0; channelNum < 2; ++channelNum) | |||
| { | |||
| char* const data = getChannelData (channelNum); | |||
| char* cacheData = ((char*) cachedLevels) + channelNum * 2; | |||
| const double timeToThumbSampleFactor = d->sampleRate / (double) d->samplesPerThumbSample; | |||
| startTime = cachedStart; | |||
| int sample = roundDoubleToInt (startTime * timeToThumbSampleFactor); | |||
| const int numFinished = (int) (d->numFinishedSamples / d->samplesPerThumbSample); | |||
| for (int i = numSamples; --i >= 0;) | |||
| { | |||
| const int nextSample = roundDoubleToInt ((startTime + timePerPixel) * timeToThumbSampleFactor); | |||
| if (sample >= 0 && data != 0) | |||
| { | |||
| char mx = -128; | |||
| char mn = 127; | |||
| while (sample <= nextSample) | |||
| { | |||
| if (sample >= numFinished) | |||
| break; | |||
| const int n = sample << 1; | |||
| const char sampMin = data [n]; | |||
| const char sampMax = data [n + 1]; | |||
| if (sampMin < mn) | |||
| mn = sampMin; | |||
| if (sampMax > mx) | |||
| mx = sampMax; | |||
| ++sample; | |||
| } | |||
| if (mn <= mx) | |||
| { | |||
| cacheData[0] = mn; | |||
| cacheData[1] = mx; | |||
| } | |||
| else | |||
| { | |||
| cacheData[0] = 1; | |||
| cacheData[1] = 0; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| cacheData[0] = 1; | |||
| cacheData[1] = 0; | |||
| } | |||
| cacheData += numChannelsCached * 2; | |||
| startTime += timePerPixel; | |||
| sample = nextSample; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void AudioThumbnail::drawChannel (Graphics& g, | |||
| int x, int y, int w, int h, | |||
| double startTime, | |||
| double endTime, | |||
| int channelNum, | |||
| const float verticalZoomFactor) | |||
| { | |||
| refillCache (w, startTime, (endTime - startTime) / w); | |||
| if (numSamplesCached >= w | |||
| && channelNum >= 0 | |||
| && channelNum < numChannelsCached) | |||
| { | |||
| const float topY = (float) y; | |||
| const float bottomY = topY + h; | |||
| const float midY = topY + h * 0.5f; | |||
| const float vscale = verticalZoomFactor * h / 256.0f; | |||
| const Rectangle clip (g.getClipBounds()); | |||
| const int skipLeft = clip.getX() - x; | |||
| w -= skipLeft; | |||
| x += skipLeft; | |||
| const char* cacheData = ((const char*) cachedLevels) | |||
| + (channelNum << 1) | |||
| + skipLeft * (numChannelsCached << 1); | |||
| while (--w >= 0) | |||
| { | |||
| const char mn = cacheData[0]; | |||
| const char mx = cacheData[1]; | |||
| cacheData += numChannelsCached << 1; | |||
| if (mn <= mx) // if the wrong way round, signifies that the sample's not yet known | |||
| g.drawLine ((float) x, jmax (midY - mx * vscale - 0.3f, topY), | |||
| (float) x, jmin (midY - mn * vscale + 0.3f, bottomY)); | |||
| ++x; | |||
| if (x >= clip.getRight()) | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,194 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ | |||
| #define __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ | |||
| #include "../../../juce_core/threads/juce_TimeSliceThread.h" | |||
| #include "../../../juce_core/io/streams/juce_InputSource.h" | |||
| #include "../../../juce_core/io/juce_OutputStream.h" | |||
| #include "../../events/juce_ChangeBroadcaster.h" | |||
| #include "../../events/juce_Timer.h" | |||
| #include "../../gui/graphics/contexts/juce_Graphics.h" | |||
| #include "juce_AudioFormatReader.h" | |||
| #include "juce_AudioFormatManager.h" | |||
| class AudioThumbnailCache; | |||
| //============================================================================== | |||
| /** | |||
| Makes it easy to quickly draw scaled views of the waveform shape of an | |||
| audio file. | |||
| To use this class, just create an AudioThumbNail class for the file you want | |||
| to draw, call setSource to tell it which file or resource to use, then call | |||
| drawChannel() to draw it. | |||
| The class will asynchronously scan the wavefile to create its scaled-down view, | |||
| so you should make your UI repaint itself as this data comes in. To do this, the | |||
| AudioThumbnail is a ChangeBroadcaster, and will broadcast a message when its | |||
| listeners should repaint themselves. | |||
| The thumbnail stores an internal low-res version of the wave data, and this can | |||
| be loaded and saved to avoid having to scan the file again. | |||
| @see AudioThumbnailCache | |||
| */ | |||
| class JUCE_API AudioThumbnail : public ChangeBroadcaster, | |||
| public TimeSliceClient, | |||
| private Timer | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an audio thumbnail. | |||
| @param sourceSamplesPerThumbnailSample when creating a stored, low-res version | |||
| of the audio data, this is the scale at which it should be done | |||
| @param formatManagerToUse the audio format manager that is used to open the file | |||
| @param cacheToUse an instance of an AudioThumbnailCache - this provides a background | |||
| thread and storage that is used to by the thumbnail, and the cache | |||
| object can be shared between multiple thumbnails | |||
| */ | |||
| AudioThumbnail (const int sourceSamplesPerThumbnailSample, | |||
| AudioFormatManager& formatManagerToUse, | |||
| AudioThumbnailCache& cacheToUse); | |||
| /** Destructor. */ | |||
| ~AudioThumbnail(); | |||
| //============================================================================== | |||
| /** Specifies the file or stream that contains the audio file. | |||
| For a file, just call | |||
| @code | |||
| setSource (new FileInputSource (file)) | |||
| @endcode | |||
| You can pass a zero in here to clear the thumbnail. | |||
| The source that is passed in will be deleted by this object when it is no | |||
| longer needed | |||
| */ | |||
| void setSource (InputSource* const newSource); | |||
| /** Reloads the low res thumbnail data from an input stream. | |||
| The thumb will automatically attempt to reload itself from its | |||
| AudioThumbnailCache. | |||
| */ | |||
| void loadFrom (InputStream& input); | |||
| /** Saves the low res thumbnail data to an output stream. | |||
| The thumb will automatically attempt to save itself to its | |||
| AudioThumbnailCache after it finishes scanning the wave file. | |||
| */ | |||
| void saveTo (OutputStream& output) const; | |||
| //============================================================================== | |||
| /** Returns the number of channels in the file. | |||
| */ | |||
| int getNumChannels() const throw(); | |||
| /** Returns the length of the audio file. | |||
| */ | |||
| double getTotalLength() const throw(); | |||
| /** Renders the waveform shape for a channel. | |||
| The waveform will be drawn within the specified rectangle, where startTime | |||
| and endTime specify the times within the audio file that should be positioned | |||
| at the left and right edges of the rectangle. | |||
| The waveform will be scaled vertically so that a full-volume sample will fill | |||
| the rectangle vertically, but you can also specify an extra vertical scale factor | |||
| with the verticalZoomFactor parameter. | |||
| */ | |||
| void drawChannel (Graphics& g, | |||
| int x, int y, int w, int h, | |||
| double startTime, | |||
| double endTime, | |||
| int channelNum, | |||
| const float verticalZoomFactor); | |||
| /** Returns true if the low res preview is fully generated. | |||
| */ | |||
| bool isFullyLoaded() const throw(); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| bool useTimeSlice(); | |||
| /** @internal */ | |||
| void timerCallback(); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| AudioFormatManager& formatManagerToUse; | |||
| AudioThumbnailCache& cache; | |||
| InputSource* source; | |||
| CriticalSection readerLock; | |||
| AudioFormatReader* reader; | |||
| MemoryBlock data, cachedLevels; | |||
| int orginalSamplesPerThumbnailSample; | |||
| int numChannelsCached, numSamplesCached; | |||
| double cachedStart, cachedTimePerPixel; | |||
| void clear(); | |||
| AudioFormatReader* createReader() const; | |||
| void generateSection (AudioFormatReader& reader, | |||
| int64 startSample, | |||
| int numSamples); | |||
| char* getChannelData (int channel) const; | |||
| void refillCache (const int numSamples, | |||
| double startTime, | |||
| const double timePerPixel); | |||
| friend class AudioThumbnailCache; | |||
| // true if it needs more callbacks from the readNextBlockFromAudioFile() method | |||
| bool initialiseFromAudioFile (AudioFormatReader& reader); | |||
| // returns true if more needs to be read | |||
| bool readNextBlockFromAudioFile (AudioFormatReader& reader); | |||
| }; | |||
| #endif // __JUCE_AUDIOTHUMBNAIL_JUCEHEADER__ | |||
| @@ -0,0 +1,145 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../../juce_core/basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_AudioThumbnailCache.h" | |||
| #include "../../../juce_core/io/streams/juce_MemoryInputStream.h" | |||
| #include "../../../juce_core/io/streams/juce_MemoryOutputStream.h" | |||
| //============================================================================== | |||
| struct ThumbnailCacheEntry | |||
| { | |||
| int64 hash; | |||
| uint32 lastUsed; | |||
| MemoryBlock data; | |||
| juce_UseDebuggingNewOperator | |||
| }; | |||
| //============================================================================== | |||
| AudioThumbnailCache::AudioThumbnailCache (const int maxNumThumbsToStore_) | |||
| : TimeSliceThread (T("thumb cache")), | |||
| maxNumThumbsToStore (maxNumThumbsToStore_) | |||
| { | |||
| startThread (2); | |||
| } | |||
| AudioThumbnailCache::~AudioThumbnailCache() | |||
| { | |||
| } | |||
| bool AudioThumbnailCache::loadThumb (AudioThumbnail& thumb, const int64 hashCode) | |||
| { | |||
| for (int i = thumbs.size(); --i >= 0;) | |||
| { | |||
| if (thumbs[i]->hash == hashCode) | |||
| { | |||
| MemoryInputStream in ((const char*) thumbs[i]->data, | |||
| thumbs[i]->data.getSize(), | |||
| false); | |||
| thumb.loadFrom (in); | |||
| thumbs[i]->lastUsed = Time::getMillisecondCounter(); | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| void AudioThumbnailCache::storeThumb (const AudioThumbnail& thumb, | |||
| const int64 hashCode) | |||
| { | |||
| MemoryOutputStream out; | |||
| thumb.saveTo (out); | |||
| ThumbnailCacheEntry* te = 0; | |||
| for (int i = thumbs.size(); --i >= 0;) | |||
| { | |||
| if (thumbs[i]->hash == hashCode) | |||
| { | |||
| te = thumbs[i]; | |||
| break; | |||
| } | |||
| } | |||
| if (te == 0) | |||
| { | |||
| te = new ThumbnailCacheEntry(); | |||
| te->hash = hashCode; | |||
| if (thumbs.size() < maxNumThumbsToStore) | |||
| { | |||
| thumbs.add (te); | |||
| } | |||
| else | |||
| { | |||
| int oldest = 0; | |||
| unsigned int oldestTime = Time::getMillisecondCounter() + 1; | |||
| int i; | |||
| for (i = thumbs.size(); --i >= 0;) | |||
| if (thumbs[i]->lastUsed < oldestTime) | |||
| oldest = i; | |||
| thumbs.set (i, te); | |||
| } | |||
| } | |||
| te->lastUsed = Time::getMillisecondCounter(); | |||
| te->data.setSize (0); | |||
| te->data.append (out.getData(), out.getDataSize()); | |||
| } | |||
| void AudioThumbnailCache::clear() | |||
| { | |||
| thumbs.clear(); | |||
| } | |||
| //============================================================================== | |||
| void AudioThumbnailCache::addThumbnail (AudioThumbnail* const thumb) | |||
| { | |||
| addTimeSliceClient (thumb); | |||
| } | |||
| void AudioThumbnailCache::removeThumbnail (AudioThumbnail* const thumb) | |||
| { | |||
| removeTimeSliceClient (thumb); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,98 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ | |||
| #define __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ | |||
| #include "juce_AudioThumbnail.h" | |||
| struct ThumbnailCacheEntry; | |||
| //============================================================================== | |||
| /** | |||
| An instance of this class is used to manage multiple AudioThumbnail objects. | |||
| The cache runs a single background thread that is shared by all the thumbnails | |||
| that need it, and it maintains a set of low-res previews in memory, to avoid | |||
| having to re-scan audio files too often. | |||
| @see AudioThumbnail | |||
| */ | |||
| class JUCE_API AudioThumbnailCache : public TimeSliceThread | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a cache object. | |||
| The maxNumThumbsToStore parameter lets you specify how many previews should | |||
| be kept in memory at once. | |||
| */ | |||
| AudioThumbnailCache (const int maxNumThumbsToStore); | |||
| /** Destructor. */ | |||
| ~AudioThumbnailCache(); | |||
| //============================================================================== | |||
| /** Clears out any stored thumbnails. | |||
| */ | |||
| void clear(); | |||
| /** Reloads the specified thumb if this cache contains the appropriate stored | |||
| data. | |||
| This is called automatically by the AudioThumbnail class, so you shouldn't | |||
| normally need to call it directly. | |||
| */ | |||
| bool loadThumb (AudioThumbnail& thumb, const int64 hashCode); | |||
| /** Stores the cachable data from the specified thumb in this cache. | |||
| This is called automatically by the AudioThumbnail class, so you shouldn't | |||
| normally need to call it directly. | |||
| */ | |||
| void storeThumb (const AudioThumbnail& thumb, const int64 hashCode); | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| //============================================================================== | |||
| OwnedArray <ThumbnailCacheEntry> thumbs; | |||
| int maxNumThumbsToStore; | |||
| friend class AudioThumbnail; | |||
| void addThumbnail (AudioThumbnail* const thumb); | |||
| void removeThumbnail (AudioThumbnail* const thumb); | |||
| }; | |||
| #endif // __JUCE_AUDIOTHUMBNAILCACHE_JUCEHEADER__ | |||
| @@ -666,7 +666,7 @@ int ListBox::getRowNumberOfComponent (Component* const rowComponent) const throw | |||
| return viewport->getRowNumberOfComponent (rowComponent); | |||
| } | |||
| const Rectangle ListBox::getRowPosition (const int rowNumber, | |||
| const Rectangle ListBox::getRowPosition (const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const throw() | |||
| { | |||
| const int rowHeight = getRowHeight(); | |||
| @@ -675,7 +675,7 @@ const Rectangle ListBox::getRowPosition (const int rowNumber, | |||
| if (relativeToComponentTopLeft) | |||
| y -= viewport->getViewPositionY(); | |||
| return Rectangle (viewport->getX(), y, | |||
| return Rectangle (viewport->getX(), y, | |||
| viewport->getViewedComponent()->getWidth(), rowHeight); | |||
| } | |||
| @@ -384,8 +384,8 @@ public: | |||
| /** Finds a row index that would be the most suitable place to insert a new | |||
| item for a given position. | |||
| This is useful when the user is e.g. dragging and dropping onto the listbox, | |||
| because it lets you easily choose the best position to insert the item that | |||
| This is useful when the user is e.g. dragging and dropping onto the listbox, | |||
| because it lets you easily choose the best position to insert the item that | |||
| they drop, based on where they drop it. | |||
| If the position is out of range, this will return -1. If the position is | |||
| @@ -388,7 +388,7 @@ bool TableListBox::isAutoSizeMenuOptionShown() const throw() | |||
| return autoSizeOptionsShown; | |||
| } | |||
| const Rectangle TableListBox::getCellPosition (const int columnId, | |||
| const Rectangle TableListBox::getCellPosition (const int columnId, | |||
| const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const | |||
| { | |||
| @@ -264,14 +264,14 @@ public: | |||
| /** Returns the position of one of the cells in the table. | |||
| If relativeToComponentTopLeft is true, the co-ordinates are relative to | |||
| the table component's top-left. The row number isn't checked to see if it's | |||
| If relativeToComponentTopLeft is true, the co-ordinates are relative to | |||
| the table component's top-left. The row number isn't checked to see if it's | |||
| in-range, but the column ID must exist or this will return an empty rectangle. | |||
| If relativeToComponentTopLeft is false, the co-ords are relative to the | |||
| If relativeToComponentTopLeft is false, the co-ords are relative to the | |||
| top-left of the table's top-left cell. | |||
| */ | |||
| const Rectangle getCellPosition (const int columnId, | |||
| const Rectangle getCellPosition (const int columnId, | |||
| const int rowNumber, | |||
| const bool relativeToComponentTopLeft) const; | |||
| @@ -155,10 +155,10 @@ inline void bigEndian24BitToChars (const int value, char* const destBytes) throw | |||
| This is faster than using the normal c++ cast to convert a double to an int, and | |||
| it will round the value to the nearest integer, rather than rounding it down | |||
| like the normal cast does. | |||
| Note that this routine gets its speed at the expense of some accuracy, and when | |||
| rounding values whose floating point component is exactly 0.5, odd numbers and | |||
| like the normal cast does. | |||
| Note that this routine gets its speed at the expense of some accuracy, and when | |||
| rounding values whose floating point component is exactly 0.5, odd numbers and | |||
| even numbers will be rounded up or down differently. For a more accurate conversion, | |||
| see roundDoubleToIntAccurate(). | |||
| */ | |||
| @@ -190,8 +190,8 @@ inline int roundDoubleToIntAccurate (const double value) throw() | |||
| it will round the value to the nearest integer, rather than rounding it down | |||
| like the normal cast does. | |||
| Note that this routine gets its speed at the expense of some accuracy, and when | |||
| rounding values whose floating point component is exactly 0.5, odd numbers and | |||
| Note that this routine gets its speed at the expense of some accuracy, and when | |||
| rounding values whose floating point component is exactly 0.5, odd numbers and | |||
| even numbers will be rounded up or down differently. | |||
| */ | |||
| inline int roundFloatToInt (const float value) throw() | |||
| @@ -46,7 +46,7 @@ static VoidArray activeStreams; | |||
| void juce_CheckForDanglingStreams() | |||
| { | |||
| /* | |||
| /* | |||
| It's always a bad idea to leak any object, but if you're leaking output | |||
| streams, then there's a good chance that you're failing to flush a file | |||
| to disk properly, which could result in corrupted data and other similar | |||
| @@ -0,0 +1,66 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "../../basics/juce_StandardHeader.h" | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_FileInputSource.h" | |||
| #include "../files/juce_FileInputStream.h" | |||
| //============================================================================== | |||
| FileInputSource::FileInputSource (const File& file_) throw() | |||
| : file (file_) | |||
| { | |||
| } | |||
| FileInputSource::~FileInputSource() | |||
| { | |||
| } | |||
| InputStream* FileInputSource::createInputStream() | |||
| { | |||
| return file.createInputStream(); | |||
| } | |||
| InputStream* FileInputSource::createInputStreamFor (const String& relatedItemPath) | |||
| { | |||
| return file.getSiblingFile (relatedItemPath).createInputStream(); | |||
| } | |||
| int64 FileInputSource::hashCode() const | |||
| { | |||
| return file.hashCode(); | |||
| } | |||
| END_JUCE_NAMESPACE | |||
| @@ -0,0 +1,67 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ | |||
| #define __JUCE_FILEINPUTSOURCE_JUCEHEADER__ | |||
| #include "juce_InputSource.h" | |||
| #include "../files/juce_File.h" | |||
| //============================================================================== | |||
| /** | |||
| A type of InputSource that represents a normal file. | |||
| @see InputSource | |||
| */ | |||
| class JUCE_API FileInputSource : public InputSource | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| FileInputSource (const File& file) throw(); | |||
| ~FileInputSource(); | |||
| InputStream* createInputStream(); | |||
| InputStream* createInputStreamFor (const String& relatedItemPath); | |||
| int64 hashCode() const; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| private: | |||
| const File file; | |||
| FileInputSource (const FileInputSource&); | |||
| const FileInputSource& operator= (const FileInputSource&); | |||
| }; | |||
| #endif // __JUCE_FILEINPUTSOURCE_JUCEHEADER__ | |||
| @@ -0,0 +1,80 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library - "Jules' Utility Class Extensions" | |||
| Copyright 2004-7 by Raw Material Software ltd. | |||
| ------------------------------------------------------------------------------ | |||
| JUCE can be redistributed and/or modified under the terms of the | |||
| GNU General Public License, as published by the Free Software Foundation; | |||
| either version 2 of the License, or (at your option) any later version. | |||
| JUCE is distributed in the hope that it will be useful, | |||
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| GNU General Public License for more details. | |||
| You should have received a copy of the GNU General Public License | |||
| along with JUCE; if not, visit www.gnu.org/licenses or write to the | |||
| Free Software Foundation, Inc., 59 Temple Place, Suite 330, | |||
| Boston, MA 02111-1307 USA | |||
| ------------------------------------------------------------------------------ | |||
| If you'd like to release a closed-source product which uses JUCE, commercial | |||
| licenses are also available: visit www.rawmaterialsoftware.com/juce for | |||
| more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ | |||
| #define __JUCE_INPUTSOURCE_JUCEHEADER__ | |||
| #include "../juce_InputStream.h" | |||
| //============================================================================== | |||
| /** | |||
| A lightweight object that can create a stream to read some kind of resource. | |||
| This may be used to refer to a file, or some other kind of source, allowing a | |||
| caller to create an input stream that can read from it when required. | |||
| @see FileInputSource | |||
| */ | |||
| class JUCE_API InputSource | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| InputSource() throw() {} | |||
| /** Destructor. */ | |||
| virtual ~InputSource() {} | |||
| //============================================================================== | |||
| /** Returns a new InputStream to read this item. | |||
| @returns an inputstream that the caller will delete, or 0 if | |||
| the filename isn't found. | |||
| */ | |||
| virtual InputStream* createInputStream() = 0; | |||
| /** Returns a new InputStream to read an item, relative. | |||
| @param relatedItemPath the relative pathname of the resource that is required | |||
| @returns an inputstream that the caller will delete, or 0 if | |||
| the item isn't found. | |||
| */ | |||
| virtual InputStream* createInputStreamFor (const String& relatedItemPath) = 0; | |||
| /** Returns a hash code that uniquely represents this item. | |||
| */ | |||
| virtual int64 hashCode() const = 0; | |||
| //============================================================================== | |||
| juce_UseDebuggingNewOperator | |||
| }; | |||
| #endif // __JUCE_INPUTSOURCE_JUCEHEADER__ | |||
| @@ -34,17 +34,7 @@ | |||
| BEGIN_JUCE_NAMESPACE | |||
| #include "juce_XmlDocument.h" | |||
| #include "../io/files/juce_FileInputStream.h" | |||
| //============================================================================== | |||
| XmlInputSource::XmlInputSource() throw() | |||
| { | |||
| } | |||
| XmlInputSource::~XmlInputSource() | |||
| { | |||
| } | |||
| #include "../io/streams/juce_FileInputSource.h" | |||
| //============================================================================== | |||
| @@ -60,34 +50,6 @@ static bool isXmlIdentifierChar_Slow (const tchar c) throw() | |||
| #define isXmlIdentifierChar(c) \ | |||
| ((c > 0 && c <= 127) ? identifierLookupTable [(int) c] : isXmlIdentifierChar_Slow (c)) | |||
| //============================================================================== | |||
| class FileInputSource : public XmlInputSource | |||
| { | |||
| public: | |||
| FileInputSource (const File& file_) throw() | |||
| : file (file_) | |||
| { | |||
| } | |||
| ~FileInputSource() | |||
| { | |||
| } | |||
| InputStream* createInputStreamFor (const String& filename) | |||
| { | |||
| if (filename.isEmpty()) | |||
| return file.createInputStream(); | |||
| else | |||
| return file.getSiblingFile (filename).createInputStream(); | |||
| } | |||
| private: | |||
| const File file; | |||
| FileInputSource (const FileInputSource&); | |||
| const FileInputSource& operator= (const FileInputSource&); | |||
| }; | |||
| //============================================================================== | |||
| XmlDocument::XmlDocument (const String& documentText) throw() | |||
| @@ -106,7 +68,7 @@ XmlDocument::~XmlDocument() throw() | |||
| delete inputSource; | |||
| } | |||
| void XmlDocument::setInputSource (XmlInputSource* const newSource) throw() | |||
| void XmlDocument::setInputSource (InputSource* const newSource) throw() | |||
| { | |||
| if (inputSource != newSource) | |||
| { | |||
| @@ -121,7 +83,7 @@ XmlElement* XmlDocument::getDocumentElement (const bool onlyReadOuterDocumentEle | |||
| if (textToParse.isEmpty() && inputSource != 0) | |||
| { | |||
| InputStream* const in = inputSource->createInputStreamFor (String::empty); | |||
| InputStream* const in = inputSource->createInputStream(); | |||
| if (in != 0) | |||
| { | |||
| @@ -35,39 +35,7 @@ | |||
| #include "juce_XmlElement.h" | |||
| #include "juce_StringArray.h" | |||
| #include "../io/files/juce_File.h" | |||
| #include "../io/juce_InputStream.h" | |||
| //============================================================================== | |||
| /** | |||
| Used by the XmlDocument class to find a document's associated files. | |||
| Because an XML document might need to reference other files for its | |||
| external DTDs, this class can be used to create input streams for | |||
| these files. | |||
| @see XmlDocument | |||
| */ | |||
| class JUCE_API XmlInputSource | |||
| { | |||
| protected: | |||
| /** Creates a default source that can read from files. */ | |||
| XmlInputSource() throw(); | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~XmlInputSource(); | |||
| /** Returns a new InputStream to read a required file. | |||
| @param filename the partial filename of a file that needs to be read, | |||
| or an empty string to open the root document that the | |||
| source refers to | |||
| @returns an inputstream that the caller will delete, or 0 if | |||
| the filename isn't found. | |||
| */ | |||
| virtual InputStream* createInputStreamFor (const String& filename) = 0; | |||
| }; | |||
| #include "../io/streams/juce_InputSource.h" | |||
| //============================================================================== | |||
| @@ -150,9 +118,9 @@ public: | |||
| The object that is passed-in will be deleted automatically when no longer needed. | |||
| @see XmlInputSource | |||
| @see InputSource | |||
| */ | |||
| void setInputSource (XmlInputSource* const newSource) throw(); | |||
| void setInputSource (InputSource* const newSource) throw(); | |||
| //============================================================================== | |||
| @@ -167,7 +135,7 @@ private: | |||
| String lastError, dtdText; | |||
| StringArray tokenisedDTD; | |||
| bool needToLoadDTD; | |||
| XmlInputSource* inputSource; | |||
| InputSource* inputSource; | |||
| void setLastError (const String& desc, const bool carryOn) throw(); | |||
| void skipHeader() throw(); | |||
| @@ -155,12 +155,18 @@ | |||
| #ifndef __JUCE_BUFFEREDINPUTSTREAM_JUCEHEADER__ | |||
| #include "juce_core/io/streams/juce_BufferedInputStream.h" | |||
| #endif | |||
| #ifndef __JUCE_FILEINPUTSOURCE_JUCEHEADER__ | |||
| #include "juce_core/io/streams/juce_FileInputSource.h" | |||
| #endif | |||
| #ifndef __JUCE_GZIPCOMPRESSOROUTPUTSTREAM_JUCEHEADER__ | |||
| #include "juce_core/io/streams/juce_GZIPCompressorOutputStream.h" | |||
| #endif | |||
| #ifndef __JUCE_GZIPDECOMPRESSORINPUTSTREAM_JUCEHEADER__ | |||
| #include "juce_core/io/streams/juce_GZIPDecompressorInputStream.h" | |||
| #endif | |||
| #ifndef __JUCE_INPUTSOURCE_JUCEHEADER__ | |||
| #include "juce_core/io/streams/juce_InputSource.h" | |||
| #endif | |||
| #ifndef __JUCE_MEMORYINPUTSTREAM_JUCEHEADER__ | |||
| #include "juce_core/io/streams/juce_MemoryInputStream.h" | |||
| #endif | |||