| @@ -0,0 +1,28 @@ | |||
| /* | |||
| * Carla Juce setup | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 2 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program 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. | |||
| * | |||
| * For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
| */ | |||
| #ifndef CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED | |||
| #define CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED | |||
| #include "juce_events.h" | |||
| #include "juce_audio_basics.h" | |||
| #include "juce_audio_formats.h" | |||
| #include "juce_audio_devices/AppConfig.h" | |||
| #include "juce_audio_devices/juce_audio_devices.h" | |||
| #endif // CARLA_JUCE_AUDIO_DEVICES_H_INCLUDED | |||
| @@ -0,0 +1,79 @@ | |||
| /* | |||
| ============================================================================== | |||
| Build options for juce_audio_devices static library | |||
| ============================================================================== | |||
| */ | |||
| #ifndef CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED | |||
| #define CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED | |||
| #include "../juce_events/AppConfig.h" | |||
| #include "../juce_audio_basics/AppConfig.h" | |||
| #include "../juce_audio_formats/AppConfig.h" | |||
| //============================================================================= | |||
| /** Config: JUCE_ASIO | |||
| Enables ASIO audio devices (MS Windows only). | |||
| Turning this on means that you'll need to have the Steinberg ASIO SDK installed | |||
| on your Windows build machine. | |||
| See the comments in the ASIOAudioIODevice class's header file for more | |||
| info about this. | |||
| */ | |||
| #if WINDOWS | |||
| #define JUCE_ASIO 1 | |||
| #else | |||
| #define JUCE_ASIO 0 | |||
| #endif | |||
| /** Config: JUCE_WASAPI | |||
| Enables WASAPI audio devices (Windows Vista and above). | |||
| */ | |||
| #define JUCE_WASAPI 0 | |||
| /** Config: JUCE_DIRECTSOUND | |||
| Enables DirectSound audio (MS Windows only). | |||
| */ | |||
| #if WINDOWS | |||
| #define JUCE_DIRECTSOUND 1 | |||
| #else | |||
| #define JUCE_DIRECTSOUND 0 | |||
| #endif | |||
| /** Config: JUCE_ALSA | |||
| Enables ALSA audio devices (Linux only). | |||
| */ | |||
| #if LINUX | |||
| #define JUCE_ALSA 1 | |||
| #define JUCE_ALSA_MIDI_INPUT_NAME "Carla" | |||
| #define JUCE_ALSA_MIDI_OUTPUT_NAME "Carla" | |||
| #define JUCE_ALSA_MIDI_INPUT_PORT_NAME "Midi In" | |||
| #define JUCE_ALSA_MIDI_OUTPUT_PORT_NAME "Midi Out" | |||
| #else | |||
| #define JUCE_ALSA 0 | |||
| #endif | |||
| /** Config: JUCE_JACK | |||
| Enables JACK audio devices (Linux only). | |||
| */ | |||
| #if LINUX | |||
| #define JUCE_JACK 1 | |||
| #define JUCE_JACK_CLIENT_NAME "Carla" | |||
| #else | |||
| #define JUCE_JACK 0 | |||
| #endif | |||
| //============================================================================= | |||
| /** Config: JUCE_USE_CDREADER | |||
| Enables the AudioCDReader class (on supported platforms). | |||
| */ | |||
| #define JUCE_USE_CDREADER 0 | |||
| /** Config: JUCE_USE_CDBURNER | |||
| Enables the AudioCDBurner class (on supported platforms). | |||
| */ | |||
| #define JUCE_USE_CDBURNER 0 | |||
| #endif // CARLA_JUCE_AUDIO_DEVICES_APPCONFIG_H_INCLUDED | |||
| @@ -0,0 +1,88 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for juce_audio_devices # | |||
| # ------------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| include ../../Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| BUILD_CXX_FLAGS += -I. | |||
| ifeq ($(MACOS),true) | |||
| BUILD_CXX_FLAGS += -objc++ | |||
| OBJS = juce_audio_devices.mm.o | |||
| OBJS_posix32 = juce_audio_devices.mm.posix32.o | |||
| OBJS_posix64 = juce_audio_devices.mm.posix64.o | |||
| else | |||
| OBJS = juce_audio_devices.cpp.o | |||
| OBJS_posix32 = juce_audio_devices.cpp.posix32.o | |||
| OBJS_posix64 = juce_audio_devices.cpp.posix64.o | |||
| endif | |||
| OBJS_win32 = juce_audio_devices.cpp.win32.o | |||
| OBJS_win64 = juce_audio_devices.cpp.win64.o | |||
| # -------------------------------------------------------------- | |||
| all: ../juce_audio_devices.a | |||
| posix32: ../juce_audio_devices.posix32.a | |||
| posix64: ../juce_audio_devices.posix64.a | |||
| win32: ../juce_audio_devices.win32.a | |||
| win64: ../juce_audio_devices.win64.a | |||
| # -------------------------------------------------------------- | |||
| ../juce_audio_devices.a: $(OBJS) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_devices.posix32.a: $(OBJS_posix32) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_devices.posix64.a: $(OBJS_posix64) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_devices.win32.a: $(OBJS_win32) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_devices.win64.a: $(OBJS_win64) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_devices.dll: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||
| ../juce_audio_devices.dylib: $(OBJS) | |||
| $(CXX) $^ -dynamiclib $(LINK_FLAGS) -o $@ | |||
| ../juce_audio_devices.so: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||
| # -------------------------------------------------------------- | |||
| %.cpp.o: %.cpp | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| %.mm.o: %.mm | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| %.posix32.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
| %.posix64.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
| %.win32.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
| %.win64.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| rm -f *.o ../juce_audio_devices*.a | |||
| debug: | |||
| $(MAKE) DEBUG=true | |||
| @@ -0,0 +1,169 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIOCDBURNER_H_INCLUDED | |||
| #define JUCE_AUDIOCDBURNER_H_INCLUDED | |||
| #if JUCE_USE_CDBURNER || DOXYGEN | |||
| //============================================================================== | |||
| /** | |||
| */ | |||
| class AudioCDBurner : public ChangeBroadcaster | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns a list of available optical drives. | |||
| Use openDevice() to open one of the items from this list. | |||
| */ | |||
| static StringArray findAvailableDevices(); | |||
| /** Tries to open one of the optical drives. | |||
| The deviceIndex is an index into the array returned by findAvailableDevices(). | |||
| */ | |||
| static AudioCDBurner* openDevice (const int deviceIndex); | |||
| /** Destructor. */ | |||
| ~AudioCDBurner(); | |||
| //============================================================================== | |||
| enum DiskState | |||
| { | |||
| unknown, /**< An error condition, if the device isn't responding. */ | |||
| trayOpen, /**< The drive is currently open. Note that a slot-loading drive | |||
| may seem to be permanently open. */ | |||
| noDisc, /**< The drive has no disk in it. */ | |||
| writableDiskPresent, /**< The drive contains a writeable disk. */ | |||
| readOnlyDiskPresent /**< The drive contains a read-only disk. */ | |||
| }; | |||
| /** Returns the current status of the device. | |||
| To get informed when the drive's status changes, attach a ChangeListener to | |||
| the AudioCDBurner. | |||
| */ | |||
| DiskState getDiskState() const; | |||
| /** Returns true if there's a writable disk in the drive. */ | |||
| bool isDiskPresent() const; | |||
| /** Sends an eject signal to the drive. | |||
| The eject will happen asynchronously, so you can use getDiskState() and | |||
| waitUntilStateChange() to monitor its progress. | |||
| */ | |||
| bool openTray(); | |||
| /** Blocks the current thread until the drive's state changes, or until the timeout expires. | |||
| @returns the device's new state | |||
| */ | |||
| DiskState waitUntilStateChange (int timeOutMilliseconds); | |||
| //============================================================================== | |||
| /** Returns the set of possible write speeds that the device can handle. | |||
| These are as a multiple of 'normal' speed, so e.g. '24x' returns 24, etc. | |||
| Note that if there's no media present in the drive, this value may be unavailable! | |||
| @see setWriteSpeed, getWriteSpeed | |||
| */ | |||
| Array<int> getAvailableWriteSpeeds() const; | |||
| //============================================================================== | |||
| /** Tries to enable or disable buffer underrun safety on devices that support it. | |||
| @returns true if it's now enabled. If the device doesn't support it, this | |||
| will always return false. | |||
| */ | |||
| bool setBufferUnderrunProtection (bool shouldBeEnabled); | |||
| //============================================================================== | |||
| /** Returns the number of free blocks on the disk. | |||
| There are 75 blocks per second, at 44100Hz. | |||
| */ | |||
| int getNumAvailableAudioBlocks() const; | |||
| /** Adds a track to be written. | |||
| The source passed-in here will be kept by this object, and it will | |||
| be used and deleted at some point in the future, either during the | |||
| burn() method or when this AudioCDBurner object is deleted. Your caller | |||
| method shouldn't keep a reference to it or use it again after passing | |||
| it in here. | |||
| */ | |||
| bool addAudioTrack (AudioSource* source, int numSamples); | |||
| //============================================================================== | |||
| /** Receives progress callbacks during a cd-burn operation. | |||
| @see AudioCDBurner::burn() | |||
| */ | |||
| class BurnProgressListener | |||
| { | |||
| public: | |||
| BurnProgressListener() noexcept {} | |||
| virtual ~BurnProgressListener() {} | |||
| /** Called at intervals to report on the progress of the AudioCDBurner. | |||
| To cancel the burn, return true from this method. | |||
| */ | |||
| virtual bool audioCDBurnProgress (float proportionComplete) = 0; | |||
| }; | |||
| /** Runs the burn process. | |||
| This method will block until the operation is complete. | |||
| @param listener the object to receive callbacks about progress | |||
| @param ejectDiscAfterwards whether to eject the disk after the burn completes | |||
| @param performFakeBurnForTesting if true, no data will actually be written to the disk | |||
| @param writeSpeed one of the write speeds from getAvailableWriteSpeeds(), or | |||
| 0 or less to mean the fastest speed. | |||
| */ | |||
| String burn (BurnProgressListener* listener, | |||
| bool ejectDiscAfterwards, | |||
| bool performFakeBurnForTesting, | |||
| int writeSpeed); | |||
| /** If a burn operation is currently in progress, this tells it to stop | |||
| as soon as possible. | |||
| It's also possible to stop the burn process by returning true from | |||
| BurnProgressListener::audioCDBurnProgress() | |||
| */ | |||
| void abortBurn(); | |||
| private: | |||
| //============================================================================== | |||
| AudioCDBurner (const int deviceIndex); | |||
| class Pimpl; | |||
| friend struct ContainerDeletePolicy<Pimpl>; | |||
| ScopedPointer<Pimpl> pimpl; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDBurner) | |||
| }; | |||
| #endif | |||
| #endif // JUCE_AUDIOCDBURNER_H_INCLUDED | |||
| @@ -0,0 +1,57 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_USE_CDREADER | |||
| int AudioCDReader::getNumTracks() const | |||
| { | |||
| return trackStartSamples.size() - 1; | |||
| } | |||
| int AudioCDReader::getPositionOfTrackStart (int trackNum) const | |||
| { | |||
| return trackStartSamples [trackNum]; | |||
| } | |||
| const Array<int>& AudioCDReader::getTrackOffsets() const | |||
| { | |||
| return trackStartSamples; | |||
| } | |||
| int AudioCDReader::getCDDBId() | |||
| { | |||
| int checksum = 0; | |||
| const int numTracks = getNumTracks(); | |||
| for (int i = 0; i < numTracks; ++i) | |||
| for (int offset = (trackStartSamples.getUnchecked(i) + 88200) / 44100; offset > 0; offset /= 10) | |||
| checksum += offset % 10; | |||
| const int length = (trackStartSamples.getLast() - trackStartSamples.getFirst()) / 44100; | |||
| // CCLLLLTT: checksum, length, tracks | |||
| return ((checksum & 0xff) << 24) | (length << 8) | numTracks; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,174 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIOCDREADER_H_INCLUDED | |||
| #define JUCE_AUDIOCDREADER_H_INCLUDED | |||
| #if JUCE_USE_CDREADER || DOXYGEN | |||
| //============================================================================== | |||
| /** | |||
| A type of AudioFormatReader that reads from an audio CD. | |||
| One of these can be used to read a CD as if it's one big audio stream. Use the | |||
| getPositionOfTrackStart() method to find where the individual tracks are | |||
| within the stream. | |||
| @see AudioFormatReader | |||
| */ | |||
| class JUCE_API AudioCDReader : public AudioFormatReader | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns a list of names of Audio CDs currently available for reading. | |||
| If there's a CD drive but no CD in it, this might return an empty list, or | |||
| possibly a device that can be opened but which has no tracks, depending | |||
| on the platform. | |||
| @see createReaderForCD | |||
| */ | |||
| static StringArray getAvailableCDNames(); | |||
| /** Tries to create an AudioFormatReader that can read from an Audio CD. | |||
| @param index the index of one of the available CDs - use getAvailableCDNames() | |||
| to find out how many there are. | |||
| @returns a new AudioCDReader object, or nullptr if it couldn't be created. The | |||
| caller will be responsible for deleting the object returned. | |||
| */ | |||
| static AudioCDReader* createReaderForCD (const int index); | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| ~AudioCDReader(); | |||
| /** Implementation of the AudioFormatReader method. */ | |||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) override; | |||
| /** Checks whether the CD has been removed from the drive. */ | |||
| bool isCDStillPresent() const; | |||
| /** Returns the total number of tracks (audio + data). */ | |||
| int getNumTracks() const; | |||
| /** Finds the sample offset of the start of a track. | |||
| @param trackNum the track number, where trackNum = 0 is the first track | |||
| and trackNum = getNumTracks() means the end of the CD. | |||
| */ | |||
| int getPositionOfTrackStart (int trackNum) const; | |||
| /** Returns true if a given track is an audio track. | |||
| @param trackNum the track number, where 0 is the first track. | |||
| */ | |||
| bool isTrackAudio (int trackNum) const; | |||
| /** Returns an array of sample offsets for the start of each track, followed by | |||
| the sample position of the end of the CD. | |||
| */ | |||
| const Array<int>& getTrackOffsets() const; | |||
| /** Refreshes the object's table of contents. | |||
| If the disc has been ejected and a different one put in since this | |||
| object was created, this will cause it to update its idea of how many tracks | |||
| there are, etc. | |||
| */ | |||
| void refreshTrackLengths(); | |||
| /** Enables scanning for indexes within tracks. | |||
| @see getLastIndex | |||
| */ | |||
| void enableIndexScanning (bool enabled); | |||
| /** Returns the index number found during the last read() call. | |||
| Index scanning is turned off by default - turn it on with enableIndexScanning(). | |||
| Then when the read() method is called, if it comes across an index within that | |||
| block, the index number is stored and returned by this method. | |||
| Some devices might not support indexes, of course. | |||
| (If you don't know what CD indexes are, it's unlikely you'll ever need them). | |||
| @see enableIndexScanning | |||
| */ | |||
| int getLastIndex() const; | |||
| /** Scans a track to find the position of any indexes within it. | |||
| @param trackNumber the track to look in, where 0 is the first track on the disc | |||
| @returns an array of sample positions of any index points found (not including | |||
| the index that marks the start of the track) | |||
| */ | |||
| Array<int> findIndexesInTrack (const int trackNumber); | |||
| /** Returns the CDDB id number for the CD. | |||
| It's not a great way of identifying a disc, but it's traditional. | |||
| */ | |||
| int getCDDBId(); | |||
| /** Tries to eject the disk. | |||
| Ejecting the disk might not actually be possible, e.g. if some other process is using it. | |||
| */ | |||
| void ejectDisk(); | |||
| //============================================================================== | |||
| enum | |||
| { | |||
| framesPerSecond = 75, | |||
| samplesPerFrame = 44100 / framesPerSecond | |||
| }; | |||
| private: | |||
| //============================================================================== | |||
| Array<int> trackStartSamples; | |||
| #if JUCE_MAC | |||
| File volumeDir; | |||
| Array<File> tracks; | |||
| int currentReaderTrack; | |||
| ScopedPointer <AudioFormatReader> reader; | |||
| AudioCDReader (const File& volume); | |||
| #elif JUCE_WINDOWS | |||
| bool audioTracks [100]; | |||
| void* handle; | |||
| MemoryBlock buffer; | |||
| bool indexingEnabled; | |||
| int lastIndex, firstFrameInBuffer, samplesInBuffer; | |||
| AudioCDReader (void* handle); | |||
| int getIndexAt (int samplePos); | |||
| #elif JUCE_LINUX | |||
| AudioCDReader(); | |||
| #endif | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioCDReader) | |||
| }; | |||
| #endif | |||
| #endif // JUCE_AUDIOCDREADER_H_INCLUDED | |||
| @@ -0,0 +1,968 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| AudioDeviceManager::AudioDeviceSetup::AudioDeviceSetup() | |||
| : sampleRate (0), | |||
| bufferSize (0), | |||
| useDefaultInputChannels (true), | |||
| useDefaultOutputChannels (true) | |||
| { | |||
| } | |||
| bool AudioDeviceManager::AudioDeviceSetup::operator== (const AudioDeviceManager::AudioDeviceSetup& other) const | |||
| { | |||
| return outputDeviceName == other.outputDeviceName | |||
| && inputDeviceName == other.inputDeviceName | |||
| && sampleRate == other.sampleRate | |||
| && bufferSize == other.bufferSize | |||
| && inputChannels == other.inputChannels | |||
| && useDefaultInputChannels == other.useDefaultInputChannels | |||
| && outputChannels == other.outputChannels | |||
| && useDefaultOutputChannels == other.useDefaultOutputChannels; | |||
| } | |||
| //============================================================================== | |||
| class AudioDeviceManager::CallbackHandler : public AudioIODeviceCallback, | |||
| public MidiInputCallback, | |||
| public AudioIODeviceType::Listener | |||
| { | |||
| public: | |||
| CallbackHandler (AudioDeviceManager& adm) noexcept : owner (adm) {} | |||
| private: | |||
| void audioDeviceIOCallback (const float** ins, int numIns, float** outs, int numOuts, int numSamples) override | |||
| { | |||
| owner.audioDeviceIOCallbackInt (ins, numIns, outs, numOuts, numSamples); | |||
| } | |||
| void audioDeviceAboutToStart (AudioIODevice* device) override | |||
| { | |||
| owner.audioDeviceAboutToStartInt (device); | |||
| } | |||
| void audioDeviceStopped() override | |||
| { | |||
| owner.audioDeviceStoppedInt(); | |||
| } | |||
| void audioDeviceError (const String& message) override | |||
| { | |||
| owner.audioDeviceErrorInt (message); | |||
| } | |||
| void handleIncomingMidiMessage (MidiInput* source, const MidiMessage& message) override | |||
| { | |||
| owner.handleIncomingMidiMessageInt (source, message); | |||
| } | |||
| void audioDeviceListChanged() override | |||
| { | |||
| owner.audioDeviceListChanged(); | |||
| } | |||
| AudioDeviceManager& owner; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CallbackHandler) | |||
| }; | |||
| //============================================================================== | |||
| AudioDeviceManager::AudioDeviceManager() | |||
| : numInputChansNeeded (0), | |||
| numOutputChansNeeded (2), | |||
| listNeedsScanning (true), | |||
| useInputNames (false), | |||
| inputLevel (0), | |||
| testSoundPosition (0), | |||
| tempBuffer (2, 2), | |||
| cpuUsageMs (0), | |||
| timeToCpuScale (0) | |||
| { | |||
| callbackHandler = new CallbackHandler (*this); | |||
| } | |||
| AudioDeviceManager::~AudioDeviceManager() | |||
| { | |||
| currentAudioDevice = nullptr; | |||
| defaultMidiOutput = nullptr; | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::createDeviceTypesIfNeeded() | |||
| { | |||
| if (availableDeviceTypes.size() == 0) | |||
| { | |||
| OwnedArray <AudioIODeviceType> types; | |||
| createAudioDeviceTypes (types); | |||
| for (int i = 0; i < types.size(); ++i) | |||
| addAudioDeviceType (types.getUnchecked(i)); | |||
| types.clear (false); | |||
| if (AudioIODeviceType* first = availableDeviceTypes.getFirst()) | |||
| currentDeviceType = first->getTypeName(); | |||
| } | |||
| } | |||
| const OwnedArray <AudioIODeviceType>& AudioDeviceManager::getAvailableDeviceTypes() | |||
| { | |||
| scanDevicesIfNeeded(); | |||
| return availableDeviceTypes; | |||
| } | |||
| void AudioDeviceManager::audioDeviceListChanged() | |||
| { | |||
| if (currentAudioDevice != nullptr) | |||
| { | |||
| currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
| currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); | |||
| currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); | |||
| currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); | |||
| } | |||
| sendChangeMessage(); | |||
| } | |||
| //============================================================================== | |||
| static void addIfNotNull (OwnedArray <AudioIODeviceType>& list, AudioIODeviceType* const device) | |||
| { | |||
| if (device != nullptr) | |||
| list.add (device); | |||
| } | |||
| void AudioDeviceManager::createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& list) | |||
| { | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_WASAPI()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_DirectSound()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ASIO()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_CoreAudio()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_iOSAudio()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_JACK()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_ALSA()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_OpenSLES()); | |||
| addIfNotNull (list, AudioIODeviceType::createAudioIODeviceType_Android()); | |||
| } | |||
| void AudioDeviceManager::addAudioDeviceType (AudioIODeviceType* newDeviceType) | |||
| { | |||
| if (newDeviceType != nullptr) | |||
| { | |||
| jassert (lastDeviceTypeConfigs.size() == availableDeviceTypes.size()); | |||
| availableDeviceTypes.add (newDeviceType); | |||
| lastDeviceTypeConfigs.add (new AudioDeviceSetup()); | |||
| newDeviceType->addListener (callbackHandler); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| String AudioDeviceManager::initialise (const int numInputChannelsNeeded, | |||
| const int numOutputChannelsNeeded, | |||
| const XmlElement* const e, | |||
| const bool selectDefaultDeviceOnFailure, | |||
| const String& preferredDefaultDeviceName, | |||
| const AudioDeviceSetup* preferredSetupOptions) | |||
| { | |||
| scanDevicesIfNeeded(); | |||
| numInputChansNeeded = numInputChannelsNeeded; | |||
| numOutputChansNeeded = numOutputChannelsNeeded; | |||
| if (e != nullptr && e->hasTagName ("DEVICESETUP")) | |||
| { | |||
| lastExplicitSettings = new XmlElement (*e); | |||
| String error; | |||
| AudioDeviceSetup setup; | |||
| if (preferredSetupOptions != nullptr) | |||
| setup = *preferredSetupOptions; | |||
| if (e->getStringAttribute ("audioDeviceName").isNotEmpty()) | |||
| { | |||
| setup.inputDeviceName = setup.outputDeviceName | |||
| = e->getStringAttribute ("audioDeviceName"); | |||
| } | |||
| else | |||
| { | |||
| setup.inputDeviceName = e->getStringAttribute ("audioInputDeviceName"); | |||
| setup.outputDeviceName = e->getStringAttribute ("audioOutputDeviceName"); | |||
| } | |||
| currentDeviceType = e->getStringAttribute ("deviceType"); | |||
| if (findType (currentDeviceType) == nullptr) | |||
| { | |||
| if (AudioIODeviceType* const type = findType (setup.inputDeviceName, setup.outputDeviceName)) | |||
| currentDeviceType = type->getTypeName(); | |||
| else if (availableDeviceTypes.size() > 0) | |||
| currentDeviceType = availableDeviceTypes.getUnchecked(0)->getTypeName(); | |||
| } | |||
| setup.bufferSize = e->getIntAttribute ("audioDeviceBufferSize"); | |||
| setup.sampleRate = e->getDoubleAttribute ("audioDeviceRate"); | |||
| setup.inputChannels .parseString (e->getStringAttribute ("audioDeviceInChans", "11"), 2); | |||
| setup.outputChannels.parseString (e->getStringAttribute ("audioDeviceOutChans", "11"), 2); | |||
| setup.useDefaultInputChannels = ! e->hasAttribute ("audioDeviceInChans"); | |||
| setup.useDefaultOutputChannels = ! e->hasAttribute ("audioDeviceOutChans"); | |||
| error = setAudioDeviceSetup (setup, true); | |||
| midiInsFromXml.clear(); | |||
| forEachXmlChildElementWithTagName (*e, c, "MIDIINPUT") | |||
| midiInsFromXml.add (c->getStringAttribute ("name")); | |||
| const StringArray allMidiIns (MidiInput::getDevices()); | |||
| for (int i = allMidiIns.size(); --i >= 0;) | |||
| setMidiInputEnabled (allMidiIns[i], midiInsFromXml.contains (allMidiIns[i])); | |||
| if (error.isNotEmpty() && selectDefaultDeviceOnFailure) | |||
| error = initialise (numInputChannelsNeeded, numOutputChannelsNeeded, 0, | |||
| false, preferredDefaultDeviceName); | |||
| setDefaultMidiOutput (e->getStringAttribute ("defaultMidiOutput")); | |||
| return error; | |||
| } | |||
| else | |||
| { | |||
| AudioDeviceSetup setup; | |||
| if (preferredSetupOptions != nullptr) | |||
| { | |||
| setup = *preferredSetupOptions; | |||
| } | |||
| else if (preferredDefaultDeviceName.isNotEmpty()) | |||
| { | |||
| for (int j = availableDeviceTypes.size(); --j >= 0;) | |||
| { | |||
| AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(j); | |||
| const StringArray outs (type->getDeviceNames (false)); | |||
| for (int i = 0; i < outs.size(); ++i) | |||
| { | |||
| if (outs[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||
| { | |||
| setup.outputDeviceName = outs[i]; | |||
| break; | |||
| } | |||
| } | |||
| const StringArray ins (type->getDeviceNames (true)); | |||
| for (int i = 0; i < ins.size(); ++i) | |||
| { | |||
| if (ins[i].matchesWildcard (preferredDefaultDeviceName, true)) | |||
| { | |||
| setup.inputDeviceName = ins[i]; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| insertDefaultDeviceNames (setup); | |||
| return setAudioDeviceSetup (setup, false); | |||
| } | |||
| } | |||
| void AudioDeviceManager::insertDefaultDeviceNames (AudioDeviceSetup& setup) const | |||
| { | |||
| if (AudioIODeviceType* type = getCurrentDeviceTypeObject()) | |||
| { | |||
| if (setup.outputDeviceName.isEmpty()) | |||
| setup.outputDeviceName = type->getDeviceNames (false) [type->getDefaultDeviceIndex (false)]; | |||
| if (setup.inputDeviceName.isEmpty()) | |||
| setup.inputDeviceName = type->getDeviceNames (true) [type->getDefaultDeviceIndex (true)]; | |||
| } | |||
| } | |||
| XmlElement* AudioDeviceManager::createStateXml() const | |||
| { | |||
| return lastExplicitSettings.createCopy(); | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::scanDevicesIfNeeded() | |||
| { | |||
| if (listNeedsScanning) | |||
| { | |||
| listNeedsScanning = false; | |||
| createDeviceTypesIfNeeded(); | |||
| for (int i = availableDeviceTypes.size(); --i >= 0;) | |||
| availableDeviceTypes.getUnchecked(i)->scanForDevices(); | |||
| } | |||
| } | |||
| AudioIODeviceType* AudioDeviceManager::findType (const String& typeName) | |||
| { | |||
| scanDevicesIfNeeded(); | |||
| for (int i = availableDeviceTypes.size(); --i >= 0;) | |||
| if (availableDeviceTypes.getUnchecked(i)->getTypeName() == typeName) | |||
| return availableDeviceTypes.getUnchecked(i); | |||
| return nullptr; | |||
| } | |||
| AudioIODeviceType* AudioDeviceManager::findType (const String& inputName, const String& outputName) | |||
| { | |||
| scanDevicesIfNeeded(); | |||
| for (int i = availableDeviceTypes.size(); --i >= 0;) | |||
| { | |||
| AudioIODeviceType* const type = availableDeviceTypes.getUnchecked(i); | |||
| if ((inputName.isNotEmpty() && type->getDeviceNames (true).contains (inputName, true)) | |||
| || (outputName.isNotEmpty() && type->getDeviceNames (false).contains (outputName, true))) | |||
| { | |||
| return type; | |||
| } | |||
| } | |||
| return nullptr; | |||
| } | |||
| void AudioDeviceManager::getAudioDeviceSetup (AudioDeviceSetup& setup) | |||
| { | |||
| setup = currentSetup; | |||
| } | |||
| void AudioDeviceManager::deleteCurrentDevice() | |||
| { | |||
| currentAudioDevice = nullptr; | |||
| currentSetup.inputDeviceName = String::empty; | |||
| currentSetup.outputDeviceName = String::empty; | |||
| } | |||
| void AudioDeviceManager::setCurrentAudioDeviceType (const String& type, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| { | |||
| if (availableDeviceTypes.getUnchecked(i)->getTypeName() == type | |||
| && currentDeviceType != type) | |||
| { | |||
| if (currentAudioDevice != nullptr) | |||
| { | |||
| closeAudioDevice(); | |||
| Thread::sleep (1500); // allow a moment for OS devices to sort themselves out, to help | |||
| // avoid things like DirectSound/ASIO clashes | |||
| } | |||
| currentDeviceType = type; | |||
| AudioDeviceSetup s (*lastDeviceTypeConfigs.getUnchecked(i)); | |||
| insertDefaultDeviceNames (s); | |||
| setAudioDeviceSetup (s, treatAsChosenDevice); | |||
| sendChangeMessage(); | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| AudioIODeviceType* AudioDeviceManager::getCurrentDeviceTypeObject() const | |||
| { | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| if (availableDeviceTypes.getUnchecked(i)->getTypeName() == currentDeviceType) | |||
| return availableDeviceTypes.getUnchecked(i); | |||
| return availableDeviceTypes[0]; | |||
| } | |||
| String AudioDeviceManager::setAudioDeviceSetup (const AudioDeviceSetup& newSetup, | |||
| const bool treatAsChosenDevice) | |||
| { | |||
| jassert (&newSetup != ¤tSetup); // this will have no effect | |||
| if (newSetup == currentSetup && currentAudioDevice != nullptr) | |||
| return String::empty; | |||
| if (! (newSetup == currentSetup)) | |||
| sendChangeMessage(); | |||
| stopDevice(); | |||
| const String newInputDeviceName (numInputChansNeeded == 0 ? String::empty : newSetup.inputDeviceName); | |||
| const String newOutputDeviceName (numOutputChansNeeded == 0 ? String::empty : newSetup.outputDeviceName); | |||
| String error; | |||
| AudioIODeviceType* type = getCurrentDeviceTypeObject(); | |||
| if (type == nullptr || (newInputDeviceName.isEmpty() && newOutputDeviceName.isEmpty())) | |||
| { | |||
| deleteCurrentDevice(); | |||
| if (treatAsChosenDevice) | |||
| updateXml(); | |||
| return String::empty; | |||
| } | |||
| if (currentSetup.inputDeviceName != newInputDeviceName | |||
| || currentSetup.outputDeviceName != newOutputDeviceName | |||
| || currentAudioDevice == nullptr) | |||
| { | |||
| deleteCurrentDevice(); | |||
| scanDevicesIfNeeded(); | |||
| if (newOutputDeviceName.isNotEmpty() | |||
| && ! type->getDeviceNames (false).contains (newOutputDeviceName)) | |||
| { | |||
| return "No such device: " + newOutputDeviceName; | |||
| } | |||
| if (newInputDeviceName.isNotEmpty() | |||
| && ! type->getDeviceNames (true).contains (newInputDeviceName)) | |||
| { | |||
| return "No such device: " + newInputDeviceName; | |||
| } | |||
| currentAudioDevice = type->createDevice (newOutputDeviceName, newInputDeviceName); | |||
| if (currentAudioDevice == nullptr) | |||
| error = "Can't open the audio device!\n\n" | |||
| "This may be because another application is currently using the same device - " | |||
| "if so, you should close any other applications and try again!"; | |||
| else | |||
| error = currentAudioDevice->getLastError(); | |||
| if (error.isNotEmpty()) | |||
| { | |||
| deleteCurrentDevice(); | |||
| return error; | |||
| } | |||
| if (newSetup.useDefaultInputChannels) | |||
| { | |||
| inputChannels.clear(); | |||
| inputChannels.setRange (0, numInputChansNeeded, true); | |||
| } | |||
| if (newSetup.useDefaultOutputChannels) | |||
| { | |||
| outputChannels.clear(); | |||
| outputChannels.setRange (0, numOutputChansNeeded, true); | |||
| } | |||
| if (newInputDeviceName.isEmpty()) inputChannels.clear(); | |||
| if (newOutputDeviceName.isEmpty()) outputChannels.clear(); | |||
| } | |||
| if (! newSetup.useDefaultInputChannels) inputChannels = newSetup.inputChannels; | |||
| if (! newSetup.useDefaultOutputChannels) outputChannels = newSetup.outputChannels; | |||
| currentSetup = newSetup; | |||
| currentSetup.sampleRate = chooseBestSampleRate (newSetup.sampleRate); | |||
| currentSetup.bufferSize = chooseBestBufferSize (newSetup.bufferSize); | |||
| error = currentAudioDevice->open (inputChannels, | |||
| outputChannels, | |||
| currentSetup.sampleRate, | |||
| currentSetup.bufferSize); | |||
| if (error.isEmpty()) | |||
| { | |||
| currentDeviceType = currentAudioDevice->getTypeName(); | |||
| currentAudioDevice->start (callbackHandler); | |||
| currentSetup.sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
| currentSetup.bufferSize = currentAudioDevice->getCurrentBufferSizeSamples(); | |||
| currentSetup.inputChannels = currentAudioDevice->getActiveInputChannels(); | |||
| currentSetup.outputChannels = currentAudioDevice->getActiveOutputChannels(); | |||
| for (int i = 0; i < availableDeviceTypes.size(); ++i) | |||
| if (availableDeviceTypes.getUnchecked (i)->getTypeName() == currentDeviceType) | |||
| *(lastDeviceTypeConfigs.getUnchecked (i)) = currentSetup; | |||
| if (treatAsChosenDevice) | |||
| updateXml(); | |||
| } | |||
| else | |||
| { | |||
| deleteCurrentDevice(); | |||
| } | |||
| return error; | |||
| } | |||
| double AudioDeviceManager::chooseBestSampleRate (double rate) const | |||
| { | |||
| jassert (currentAudioDevice != nullptr); | |||
| if (rate > 0) | |||
| for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) | |||
| if (currentAudioDevice->getSampleRate (i) == rate) | |||
| return rate; | |||
| double lowestAbove44 = 0.0; | |||
| for (int i = currentAudioDevice->getNumSampleRates(); --i >= 0;) | |||
| { | |||
| const double sr = currentAudioDevice->getSampleRate (i); | |||
| if (sr >= 44100.0 && (lowestAbove44 < 1.0 || sr < lowestAbove44)) | |||
| lowestAbove44 = sr; | |||
| } | |||
| if (lowestAbove44 > 0.0) | |||
| return lowestAbove44; | |||
| return currentAudioDevice->getSampleRate (0); | |||
| } | |||
| int AudioDeviceManager::chooseBestBufferSize (int bufferSize) const | |||
| { | |||
| jassert (currentAudioDevice != nullptr); | |||
| if (bufferSize > 0) | |||
| for (int i = currentAudioDevice->getNumBufferSizesAvailable(); --i >= 0;) | |||
| if (currentAudioDevice->getBufferSizeSamples(i) == bufferSize) | |||
| return bufferSize; | |||
| return currentAudioDevice->getDefaultBufferSize(); | |||
| } | |||
| void AudioDeviceManager::stopDevice() | |||
| { | |||
| if (currentAudioDevice != nullptr) | |||
| currentAudioDevice->stop(); | |||
| testSound = nullptr; | |||
| } | |||
| void AudioDeviceManager::closeAudioDevice() | |||
| { | |||
| stopDevice(); | |||
| currentAudioDevice = nullptr; | |||
| } | |||
| void AudioDeviceManager::restartLastAudioDevice() | |||
| { | |||
| if (currentAudioDevice == nullptr) | |||
| { | |||
| if (currentSetup.inputDeviceName.isEmpty() | |||
| && currentSetup.outputDeviceName.isEmpty()) | |||
| { | |||
| // This method will only reload the last device that was running | |||
| // before closeAudioDevice() was called - you need to actually open | |||
| // one first, with setAudioDevice(). | |||
| jassertfalse; | |||
| return; | |||
| } | |||
| AudioDeviceSetup s (currentSetup); | |||
| setAudioDeviceSetup (s, false); | |||
| } | |||
| } | |||
| void AudioDeviceManager::updateXml() | |||
| { | |||
| lastExplicitSettings = new XmlElement ("DEVICESETUP"); | |||
| lastExplicitSettings->setAttribute ("deviceType", currentDeviceType); | |||
| lastExplicitSettings->setAttribute ("audioOutputDeviceName", currentSetup.outputDeviceName); | |||
| lastExplicitSettings->setAttribute ("audioInputDeviceName", currentSetup.inputDeviceName); | |||
| if (currentAudioDevice != nullptr) | |||
| { | |||
| lastExplicitSettings->setAttribute ("audioDeviceRate", currentAudioDevice->getCurrentSampleRate()); | |||
| if (currentAudioDevice->getDefaultBufferSize() != currentAudioDevice->getCurrentBufferSizeSamples()) | |||
| lastExplicitSettings->setAttribute ("audioDeviceBufferSize", currentAudioDevice->getCurrentBufferSizeSamples()); | |||
| if (! currentSetup.useDefaultInputChannels) | |||
| lastExplicitSettings->setAttribute ("audioDeviceInChans", currentSetup.inputChannels.toString (2)); | |||
| if (! currentSetup.useDefaultOutputChannels) | |||
| lastExplicitSettings->setAttribute ("audioDeviceOutChans", currentSetup.outputChannels.toString (2)); | |||
| } | |||
| for (int i = 0; i < enabledMidiInputs.size(); ++i) | |||
| lastExplicitSettings->createNewChildElement ("MIDIINPUT") | |||
| ->setAttribute ("name", enabledMidiInputs[i]->getName()); | |||
| if (midiInsFromXml.size() > 0) | |||
| { | |||
| // Add any midi devices that have been enabled before, but which aren't currently | |||
| // open because the device has been disconnected. | |||
| const StringArray availableMidiDevices (MidiInput::getDevices()); | |||
| for (int i = 0; i < midiInsFromXml.size(); ++i) | |||
| if (! availableMidiDevices.contains (midiInsFromXml[i], true)) | |||
| lastExplicitSettings->createNewChildElement ("MIDIINPUT") | |||
| ->setAttribute ("name", midiInsFromXml[i]); | |||
| } | |||
| if (defaultMidiOutputName.isNotEmpty()) | |||
| lastExplicitSettings->setAttribute ("defaultMidiOutput", defaultMidiOutputName); | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::addAudioCallback (AudioIODeviceCallback* newCallback) | |||
| { | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| if (callbacks.contains (newCallback)) | |||
| return; | |||
| } | |||
| if (currentAudioDevice != nullptr && newCallback != nullptr) | |||
| newCallback->audioDeviceAboutToStart (currentAudioDevice); | |||
| const ScopedLock sl (audioCallbackLock); | |||
| callbacks.add (newCallback); | |||
| } | |||
| void AudioDeviceManager::removeAudioCallback (AudioIODeviceCallback* callbackToRemove) | |||
| { | |||
| if (callbackToRemove != nullptr) | |||
| { | |||
| bool needsDeinitialising = currentAudioDevice != nullptr; | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| needsDeinitialising = needsDeinitialising && callbacks.contains (callbackToRemove); | |||
| callbacks.removeFirstMatchingValue (callbackToRemove); | |||
| } | |||
| if (needsDeinitialising) | |||
| callbackToRemove->audioDeviceStopped(); | |||
| } | |||
| } | |||
| void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelData, | |||
| int numInputChannels, | |||
| float** outputChannelData, | |||
| int numOutputChannels, | |||
| int numSamples) | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| if (inputLevelMeasurementEnabledCount.get() > 0 && numInputChannels > 0) | |||
| { | |||
| for (int j = 0; j < numSamples; ++j) | |||
| { | |||
| float s = 0; | |||
| for (int i = 0; i < numInputChannels; ++i) | |||
| s += std::abs (inputChannelData[i][j]); | |||
| s /= numInputChannels; | |||
| const double decayFactor = 0.99992; | |||
| if (s > inputLevel) | |||
| inputLevel = s; | |||
| else if (inputLevel > 0.001f) | |||
| inputLevel *= decayFactor; | |||
| else | |||
| inputLevel = 0; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| inputLevel = 0; | |||
| } | |||
| if (callbacks.size() > 0) | |||
| { | |||
| const double callbackStartTime = Time::getMillisecondCounterHiRes(); | |||
| tempBuffer.setSize (jmax (1, numOutputChannels), jmax (1, numSamples), false, false, true); | |||
| callbacks.getUnchecked(0)->audioDeviceIOCallback (inputChannelData, numInputChannels, | |||
| outputChannelData, numOutputChannels, numSamples); | |||
| float** const tempChans = tempBuffer.getArrayOfChannels(); | |||
| for (int i = callbacks.size(); --i > 0;) | |||
| { | |||
| callbacks.getUnchecked(i)->audioDeviceIOCallback (inputChannelData, numInputChannels, | |||
| tempChans, numOutputChannels, numSamples); | |||
| for (int chan = 0; chan < numOutputChannels; ++chan) | |||
| { | |||
| if (const float* const src = tempChans [chan]) | |||
| if (float* const dst = outputChannelData [chan]) | |||
| for (int j = 0; j < numSamples; ++j) | |||
| dst[j] += src[j]; | |||
| } | |||
| } | |||
| const double msTaken = Time::getMillisecondCounterHiRes() - callbackStartTime; | |||
| const double filterAmount = 0.2; | |||
| cpuUsageMs += filterAmount * (msTaken - cpuUsageMs); | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < numOutputChannels; ++i) | |||
| zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); | |||
| } | |||
| if (testSound != nullptr) | |||
| { | |||
| const int numSamps = jmin (numSamples, testSound->getNumSamples() - testSoundPosition); | |||
| const float* const src = testSound->getSampleData (0, testSoundPosition); | |||
| for (int i = 0; i < numOutputChannels; ++i) | |||
| for (int j = 0; j < numSamps; ++j) | |||
| outputChannelData [i][j] += src[j]; | |||
| testSoundPosition += numSamps; | |||
| if (testSoundPosition >= testSound->getNumSamples()) | |||
| testSound = nullptr; | |||
| } | |||
| } | |||
| void AudioDeviceManager::audioDeviceAboutToStartInt (AudioIODevice* const device) | |||
| { | |||
| cpuUsageMs = 0; | |||
| const double sampleRate = device->getCurrentSampleRate(); | |||
| const int blockSize = device->getCurrentBufferSizeSamples(); | |||
| if (sampleRate > 0.0 && blockSize > 0) | |||
| { | |||
| const double msPerBlock = 1000.0 * blockSize / sampleRate; | |||
| timeToCpuScale = (msPerBlock > 0.0) ? (1.0 / msPerBlock) : 0.0; | |||
| } | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| for (int i = callbacks.size(); --i >= 0;) | |||
| callbacks.getUnchecked(i)->audioDeviceAboutToStart (device); | |||
| } | |||
| sendChangeMessage(); | |||
| } | |||
| void AudioDeviceManager::audioDeviceStoppedInt() | |||
| { | |||
| cpuUsageMs = 0; | |||
| timeToCpuScale = 0; | |||
| sendChangeMessage(); | |||
| const ScopedLock sl (audioCallbackLock); | |||
| for (int i = callbacks.size(); --i >= 0;) | |||
| callbacks.getUnchecked(i)->audioDeviceStopped(); | |||
| } | |||
| void AudioDeviceManager::audioDeviceErrorInt (const String& message) | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| for (int i = callbacks.size(); --i >= 0;) | |||
| callbacks.getUnchecked(i)->audioDeviceError (message); | |||
| } | |||
| double AudioDeviceManager::getCpuUsage() const | |||
| { | |||
| return jlimit (0.0, 1.0, timeToCpuScale * cpuUsageMs); | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::setMidiInputEnabled (const String& name, const bool enabled) | |||
| { | |||
| if (enabled != isMidiInputEnabled (name)) | |||
| { | |||
| if (enabled) | |||
| { | |||
| const int index = MidiInput::getDevices().indexOf (name); | |||
| if (index >= 0) | |||
| { | |||
| if (MidiInput* const midiIn = MidiInput::openDevice (index, callbackHandler)) | |||
| { | |||
| enabledMidiInputs.add (midiIn); | |||
| midiIn->start(); | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (int i = enabledMidiInputs.size(); --i >= 0;) | |||
| if (enabledMidiInputs[i]->getName() == name) | |||
| enabledMidiInputs.remove (i); | |||
| } | |||
| updateXml(); | |||
| sendChangeMessage(); | |||
| } | |||
| } | |||
| bool AudioDeviceManager::isMidiInputEnabled (const String& name) const | |||
| { | |||
| for (int i = enabledMidiInputs.size(); --i >= 0;) | |||
| if (enabledMidiInputs[i]->getName() == name) | |||
| return true; | |||
| return false; | |||
| } | |||
| void AudioDeviceManager::addMidiInputCallback (const String& name, MidiInputCallback* callbackToAdd) | |||
| { | |||
| removeMidiInputCallback (name, callbackToAdd); | |||
| if (name.isEmpty() || isMidiInputEnabled (name)) | |||
| { | |||
| const ScopedLock sl (midiCallbackLock); | |||
| midiCallbacks.add (callbackToAdd); | |||
| midiCallbackDevices.add (name); | |||
| } | |||
| } | |||
| void AudioDeviceManager::removeMidiInputCallback (const String& name, MidiInputCallback* callbackToRemove) | |||
| { | |||
| for (int i = midiCallbacks.size(); --i >= 0;) | |||
| { | |||
| if (midiCallbackDevices[i] == name && midiCallbacks.getUnchecked(i) == callbackToRemove) | |||
| { | |||
| const ScopedLock sl (midiCallbackLock); | |||
| midiCallbacks.remove (i); | |||
| midiCallbackDevices.remove (i); | |||
| } | |||
| } | |||
| } | |||
| void AudioDeviceManager::handleIncomingMidiMessageInt (MidiInput* source, const MidiMessage& message) | |||
| { | |||
| if (! message.isActiveSense()) | |||
| { | |||
| const bool isDefaultSource = (source == nullptr || source == enabledMidiInputs.getFirst()); | |||
| const ScopedLock sl (midiCallbackLock); | |||
| for (int i = midiCallbackDevices.size(); --i >= 0;) | |||
| { | |||
| const String name (midiCallbackDevices[i]); | |||
| if ((isDefaultSource && name.isEmpty()) || (name.isNotEmpty() && name == source->getName())) | |||
| midiCallbacks.getUnchecked(i)->handleIncomingMidiMessage (source, message); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::setDefaultMidiOutput (const String& deviceName) | |||
| { | |||
| if (defaultMidiOutputName != deviceName) | |||
| { | |||
| Array <AudioIODeviceCallback*> oldCallbacks; | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| oldCallbacks.swapWith (callbacks); | |||
| } | |||
| if (currentAudioDevice != nullptr) | |||
| for (int i = oldCallbacks.size(); --i >= 0;) | |||
| oldCallbacks.getUnchecked(i)->audioDeviceStopped(); | |||
| defaultMidiOutput = nullptr; | |||
| defaultMidiOutputName = deviceName; | |||
| if (deviceName.isNotEmpty()) | |||
| defaultMidiOutput = MidiOutput::openDevice (MidiOutput::getDevices().indexOf (deviceName)); | |||
| if (currentAudioDevice != nullptr) | |||
| for (int i = oldCallbacks.size(); --i >= 0;) | |||
| oldCallbacks.getUnchecked(i)->audioDeviceAboutToStart (currentAudioDevice); | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| oldCallbacks.swapWith (callbacks); | |||
| } | |||
| updateXml(); | |||
| sendChangeMessage(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void AudioDeviceManager::playTestSound() | |||
| { | |||
| { // cunningly nested to swap, unlock and delete in that order. | |||
| ScopedPointer <AudioSampleBuffer> oldSound; | |||
| { | |||
| const ScopedLock sl (audioCallbackLock); | |||
| oldSound = testSound; | |||
| } | |||
| } | |||
| testSoundPosition = 0; | |||
| if (currentAudioDevice != nullptr) | |||
| { | |||
| const double sampleRate = currentAudioDevice->getCurrentSampleRate(); | |||
| const int soundLength = (int) sampleRate; | |||
| AudioSampleBuffer* const newSound = new AudioSampleBuffer (1, soundLength); | |||
| float* samples = newSound->getSampleData (0); | |||
| const double frequency = 440.0; | |||
| const float amplitude = 0.5f; | |||
| const double phasePerSample = double_Pi * 2.0 / (sampleRate / frequency); | |||
| for (int i = 0; i < soundLength; ++i) | |||
| samples[i] = amplitude * (float) std::sin (i * phasePerSample); | |||
| newSound->applyGainRamp (0, 0, soundLength / 10, 0.0f, 1.0f); | |||
| newSound->applyGainRamp (0, soundLength - soundLength / 4, soundLength / 4, 1.0f, 0.0f); | |||
| const ScopedLock sl (audioCallbackLock); | |||
| testSound = newSound; | |||
| } | |||
| } | |||
| void AudioDeviceManager::enableInputLevelMeasurement (const bool enableMeasurement) | |||
| { | |||
| if (enableMeasurement) | |||
| ++inputLevelMeasurementEnabledCount; | |||
| else | |||
| --inputLevelMeasurementEnabledCount; | |||
| inputLevel = 0; | |||
| } | |||
| double AudioDeviceManager::getCurrentInputLevel() const | |||
| { | |||
| jassert (inputLevelMeasurementEnabledCount.get() > 0); // you need to call enableInputLevelMeasurement() before using this! | |||
| return inputLevel; | |||
| } | |||
| @@ -0,0 +1,510 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIODEVICEMANAGER_H_INCLUDED | |||
| #define JUCE_AUDIODEVICEMANAGER_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Manages the state of some audio and midi i/o devices. | |||
| This class keeps tracks of a currently-selected audio device, through | |||
| with which it continuously streams data from an audio callback, as well as | |||
| one or more midi inputs. | |||
| The idea is that your application will create one global instance of this object, | |||
| and let it take care of creating and deleting specific types of audio devices | |||
| internally. So when the device is changed, your callbacks will just keep running | |||
| without having to worry about this. | |||
| The manager can save and reload all of its device settings as XML, which | |||
| makes it very easy for you to save and reload the audio setup of your | |||
| application. | |||
| And to make it easy to let the user change its settings, there's a component | |||
| to do just that - the AudioDeviceSelectorComponent class, which contains a set of | |||
| device selection/sample-rate/latency controls. | |||
| To use an AudioDeviceManager, create one, and use initialise() to set it up. Then | |||
| call addAudioCallback() to register your audio callback with it, and use that to process | |||
| your audio data. | |||
| The manager also acts as a handy hub for incoming midi messages, allowing a | |||
| listener to register for messages from either a specific midi device, or from whatever | |||
| the current default midi input device is. The listener then doesn't have to worry about | |||
| re-registering with different midi devices if they are changed or deleted. | |||
| And yet another neat trick is that amount of CPU time being used is measured and | |||
| available with the getCpuUsage() method. | |||
| The AudioDeviceManager is a ChangeBroadcaster, and will send a change message to | |||
| listeners whenever one of its settings is changed. | |||
| @see AudioDeviceSelectorComponent, AudioIODevice, AudioIODeviceType | |||
| */ | |||
| class JUCE_API AudioDeviceManager : public ChangeBroadcaster | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a default AudioDeviceManager. | |||
| Initially no audio device will be selected. You should call the initialise() method | |||
| and register an audio callback with setAudioCallback() before it'll be able to | |||
| actually make any noise. | |||
| */ | |||
| AudioDeviceManager(); | |||
| /** Destructor. */ | |||
| ~AudioDeviceManager(); | |||
| //============================================================================== | |||
| /** | |||
| This structure holds a set of properties describing the current audio setup. | |||
| An AudioDeviceManager uses this class to save/load its current settings, and to | |||
| specify your preferred options when opening a device. | |||
| @see AudioDeviceManager::setAudioDeviceSetup(), AudioDeviceManager::initialise() | |||
| */ | |||
| struct JUCE_API AudioDeviceSetup | |||
| { | |||
| /** Creates an AudioDeviceSetup object. | |||
| The default constructor sets all the member variables to indicate default values. | |||
| You can then fill-in any values you want to before passing the object to | |||
| AudioDeviceManager::initialise(). | |||
| */ | |||
| AudioDeviceSetup(); | |||
| bool operator== (const AudioDeviceSetup& other) const; | |||
| /** The name of the audio device used for output. | |||
| The name has to be one of the ones listed by the AudioDeviceManager's currently | |||
| selected device type. | |||
| This may be the same as the input device. | |||
| An empty string indicates the default device. | |||
| */ | |||
| String outputDeviceName; | |||
| /** The name of the audio device used for input. | |||
| This may be the same as the output device. | |||
| An empty string indicates the default device. | |||
| */ | |||
| String inputDeviceName; | |||
| /** The current sample rate. | |||
| This rate is used for both the input and output devices. | |||
| A value of 0 indicates that you don't care what rate is used, and the | |||
| device will choose a sensible rate for you. | |||
| */ | |||
| double sampleRate; | |||
| /** The buffer size, in samples. | |||
| This buffer size is used for both the input and output devices. | |||
| A value of 0 indicates the default buffer size. | |||
| */ | |||
| int bufferSize; | |||
| /** The set of active input channels. | |||
| The bits that are set in this array indicate the channels of the | |||
| input device that are active. | |||
| If useDefaultInputChannels is true, this value is ignored. | |||
| */ | |||
| BigInteger inputChannels; | |||
| /** If this is true, it indicates that the inputChannels array | |||
| should be ignored, and instead, the device's default channels | |||
| should be used. | |||
| */ | |||
| bool useDefaultInputChannels; | |||
| /** The set of active output channels. | |||
| The bits that are set in this array indicate the channels of the | |||
| input device that are active. | |||
| If useDefaultOutputChannels is true, this value is ignored. | |||
| */ | |||
| BigInteger outputChannels; | |||
| /** If this is true, it indicates that the outputChannels array | |||
| should be ignored, and instead, the device's default channels | |||
| should be used. | |||
| */ | |||
| bool useDefaultOutputChannels; | |||
| }; | |||
| //============================================================================== | |||
| /** Opens a set of audio devices ready for use. | |||
| This will attempt to open either a default audio device, or one that was | |||
| previously saved as XML. | |||
| @param numInputChannelsNeeded the maximum number of input channels your app would like to | |||
| use (the actual number of channels opened may be less than | |||
| the number requested) | |||
| @param numOutputChannelsNeeded the maximum number of output channels your app would like to | |||
| use (the actual number of channels opened may be less than | |||
| the number requested) | |||
| @param savedState either a previously-saved state that was produced | |||
| by createStateXml(), or nullptr if you want the manager | |||
| to choose the best device to open. | |||
| @param selectDefaultDeviceOnFailure if true, then if the device specified in the XML | |||
| fails to open, then a default device will be used | |||
| instead. If false, then on failure, no device is | |||
| opened. | |||
| @param preferredDefaultDeviceName if this is not empty, and there's a device with this | |||
| name, then that will be used as the default device | |||
| (assuming that there wasn't one specified in the XML). | |||
| The string can actually be a simple wildcard, containing "*" | |||
| and "?" characters | |||
| @param preferredSetupOptions if this is non-null, the structure will be used as the | |||
| set of preferred settings when opening the device. If you | |||
| use this parameter, the preferredDefaultDeviceName | |||
| field will be ignored | |||
| @returns an error message if anything went wrong, or an empty string if it worked ok. | |||
| */ | |||
| String initialise (int numInputChannelsNeeded, | |||
| int numOutputChannelsNeeded, | |||
| const XmlElement* savedState, | |||
| bool selectDefaultDeviceOnFailure, | |||
| const String& preferredDefaultDeviceName = String::empty, | |||
| const AudioDeviceSetup* preferredSetupOptions = 0); | |||
| /** Returns some XML representing the current state of the manager. | |||
| This stores the current device, its samplerate, block size, etc, and | |||
| can be restored later with initialise(). | |||
| Note that this can return a null pointer if no settings have been explicitly changed | |||
| (i.e. if the device manager has just been left in its default state). | |||
| */ | |||
| XmlElement* createStateXml() const; | |||
| //============================================================================== | |||
| /** Returns the current device properties that are in use. | |||
| @see setAudioDeviceSetup | |||
| */ | |||
| void getAudioDeviceSetup (AudioDeviceSetup& setup); | |||
| /** Changes the current device or its settings. | |||
| If you want to change a device property, like the current sample rate or | |||
| block size, you can call getAudioDeviceSetup() to retrieve the current | |||
| settings, then tweak the appropriate fields in the AudioDeviceSetup structure, | |||
| and pass it back into this method to apply the new settings. | |||
| @param newSetup the settings that you'd like to use | |||
| @param treatAsChosenDevice if this is true and if the device opens correctly, these new | |||
| settings will be taken as having been explicitly chosen by the | |||
| user, and the next time createStateXml() is called, these settings | |||
| will be returned. If it's false, then the device is treated as a | |||
| temporary or default device, and a call to createStateXml() will | |||
| return either the last settings that were made with treatAsChosenDevice | |||
| as true, or the last XML settings that were passed into initialise(). | |||
| @returns an error message if anything went wrong, or an empty string if it worked ok. | |||
| @see getAudioDeviceSetup | |||
| */ | |||
| String setAudioDeviceSetup (const AudioDeviceSetup& newSetup, | |||
| bool treatAsChosenDevice); | |||
| /** Returns the currently-active audio device. */ | |||
| AudioIODevice* getCurrentAudioDevice() const noexcept { return currentAudioDevice; } | |||
| /** Returns the type of audio device currently in use. | |||
| @see setCurrentAudioDeviceType | |||
| */ | |||
| String getCurrentAudioDeviceType() const { return currentDeviceType; } | |||
| /** Returns the currently active audio device type object. | |||
| Don't keep a copy of this pointer - it's owned by the device manager and could | |||
| change at any time. | |||
| */ | |||
| AudioIODeviceType* getCurrentDeviceTypeObject() const; | |||
| /** Changes the class of audio device being used. | |||
| This switches between, e.g. ASIO and DirectSound. On the Mac you probably won't ever call | |||
| this because there's only one type: CoreAudio. | |||
| For a list of types, see getAvailableDeviceTypes(). | |||
| */ | |||
| void setCurrentAudioDeviceType (const String& type, | |||
| bool treatAsChosenDevice); | |||
| /** Closes the currently-open device. | |||
| You can call restartLastAudioDevice() later to reopen it in the same state | |||
| that it was just in. | |||
| */ | |||
| void closeAudioDevice(); | |||
| /** Tries to reload the last audio device that was running. | |||
| Note that this only reloads the last device that was running before | |||
| closeAudioDevice() was called - it doesn't reload any kind of saved-state, | |||
| and can only be called after a device has been opened with SetAudioDevice(). | |||
| If a device is already open, this call will do nothing. | |||
| */ | |||
| void restartLastAudioDevice(); | |||
| //============================================================================== | |||
| /** Registers an audio callback to be used. | |||
| The manager will redirect callbacks from whatever audio device is currently | |||
| in use to all registered callback objects. If more than one callback is | |||
| active, they will all be given the same input data, and their outputs will | |||
| be summed. | |||
| If necessary, this method will invoke audioDeviceAboutToStart() on the callback | |||
| object before returning. | |||
| To remove a callback, use removeAudioCallback(). | |||
| */ | |||
| void addAudioCallback (AudioIODeviceCallback* newCallback); | |||
| /** Deregisters a previously added callback. | |||
| If necessary, this method will invoke audioDeviceStopped() on the callback | |||
| object before returning. | |||
| @see addAudioCallback | |||
| */ | |||
| void removeAudioCallback (AudioIODeviceCallback* callback); | |||
| //============================================================================== | |||
| /** Returns the average proportion of available CPU being spent inside the audio callbacks. | |||
| Returns a value between 0 and 1.0 | |||
| */ | |||
| double getCpuUsage() const; | |||
| //============================================================================== | |||
| /** Enables or disables a midi input device. | |||
| The list of devices can be obtained with the MidiInput::getDevices() method. | |||
| Any incoming messages from enabled input devices will be forwarded on to all the | |||
| listeners that have been registered with the addMidiInputCallback() method. They | |||
| can either register for messages from a particular device, or from just the | |||
| "default" midi input. | |||
| Routing the midi input via an AudioDeviceManager means that when a listener | |||
| registers for the default midi input, this default device can be changed by the | |||
| manager without the listeners having to know about it or re-register. | |||
| It also means that a listener can stay registered for a midi input that is disabled | |||
| or not present, so that when the input is re-enabled, the listener will start | |||
| receiving messages again. | |||
| @see addMidiInputCallback, isMidiInputEnabled | |||
| */ | |||
| void setMidiInputEnabled (const String& midiInputDeviceName, bool enabled); | |||
| /** Returns true if a given midi input device is being used. | |||
| @see setMidiInputEnabled | |||
| */ | |||
| bool isMidiInputEnabled (const String& midiInputDeviceName) const; | |||
| /** Registers a listener for callbacks when midi events arrive from a midi input. | |||
| The device name can be empty to indicate that it wants events from whatever the | |||
| current "default" device is. Or it can be the name of one of the midi input devices | |||
| (see MidiInput::getDevices() for the names). | |||
| Only devices which are enabled (see the setMidiInputEnabled() method) will have their | |||
| events forwarded on to listeners. | |||
| */ | |||
| void addMidiInputCallback (const String& midiInputDeviceName, | |||
| MidiInputCallback* callback); | |||
| /** Removes a listener that was previously registered with addMidiInputCallback(). | |||
| */ | |||
| void removeMidiInputCallback (const String& midiInputDeviceName, | |||
| MidiInputCallback* callback); | |||
| //============================================================================== | |||
| /** Sets a midi output device to use as the default. | |||
| The list of devices can be obtained with the MidiOutput::getDevices() method. | |||
| The specified device will be opened automatically and can be retrieved with the | |||
| getDefaultMidiOutput() method. | |||
| Pass in an empty string to deselect all devices. For the default device, you | |||
| can use MidiOutput::getDevices() [MidiOutput::getDefaultDeviceIndex()]. | |||
| @see getDefaultMidiOutput, getDefaultMidiOutputName | |||
| */ | |||
| void setDefaultMidiOutput (const String& deviceName); | |||
| /** Returns the name of the default midi output. | |||
| @see setDefaultMidiOutput, getDefaultMidiOutput | |||
| */ | |||
| String getDefaultMidiOutputName() const { return defaultMidiOutputName; } | |||
| /** Returns the current default midi output device. | |||
| If no device has been selected, or the device can't be opened, this will | |||
| return 0. | |||
| @see getDefaultMidiOutputName | |||
| */ | |||
| MidiOutput* getDefaultMidiOutput() const noexcept { return defaultMidiOutput; } | |||
| /** Returns a list of the types of device supported. | |||
| */ | |||
| const OwnedArray <AudioIODeviceType>& getAvailableDeviceTypes(); | |||
| //============================================================================== | |||
| /** Creates a list of available types. | |||
| This will add a set of new AudioIODeviceType objects to the specified list, to | |||
| represent each available types of device. | |||
| You can override this if your app needs to do something specific, like avoid | |||
| using DirectSound devices, etc. | |||
| */ | |||
| virtual void createAudioDeviceTypes (OwnedArray <AudioIODeviceType>& types); | |||
| /** Adds a new device type to the list of types. | |||
| The manager will take ownership of the object that is passed-in. | |||
| */ | |||
| void addAudioDeviceType (AudioIODeviceType* newDeviceType); | |||
| //============================================================================== | |||
| /** Plays a beep through the current audio device. | |||
| This is here to allow the audio setup UI panels to easily include a "test" | |||
| button so that the user can check where the audio is coming from. | |||
| */ | |||
| void playTestSound(); | |||
| /** Turns on level-measuring. | |||
| When enabled, the device manager will measure the peak input level | |||
| across all channels, and you can get this level by calling getCurrentInputLevel(). | |||
| This is mainly intended for audio setup UI panels to use to create a mic | |||
| level display, so that the user can check that they've selected the right | |||
| device. | |||
| A simple filter is used to make the level decay smoothly, but this is | |||
| only intended for giving rough feedback, and not for any kind of accurate | |||
| measurement. | |||
| */ | |||
| void enableInputLevelMeasurement (bool enableMeasurement); | |||
| /** Returns the current input level. | |||
| To use this, you must first enable it by calling enableInputLevelMeasurement(). | |||
| See enableInputLevelMeasurement() for more info. | |||
| */ | |||
| double getCurrentInputLevel() const; | |||
| /** Returns the a lock that can be used to synchronise access to the audio callback. | |||
| Obviously while this is locked, you're blocking the audio thread from running, so | |||
| it must only be used for very brief periods when absolutely necessary. | |||
| */ | |||
| CriticalSection& getAudioCallbackLock() noexcept { return audioCallbackLock; } | |||
| /** Returns the a lock that can be used to synchronise access to the midi callback. | |||
| Obviously while this is locked, you're blocking the midi system from running, so | |||
| it must only be used for very brief periods when absolutely necessary. | |||
| */ | |||
| CriticalSection& getMidiCallbackLock() noexcept { return midiCallbackLock; } | |||
| private: | |||
| //============================================================================== | |||
| OwnedArray <AudioIODeviceType> availableDeviceTypes; | |||
| OwnedArray <AudioDeviceSetup> lastDeviceTypeConfigs; | |||
| AudioDeviceSetup currentSetup; | |||
| ScopedPointer <AudioIODevice> currentAudioDevice; | |||
| Array <AudioIODeviceCallback*> callbacks; | |||
| int numInputChansNeeded, numOutputChansNeeded; | |||
| String currentDeviceType; | |||
| BigInteger inputChannels, outputChannels; | |||
| ScopedPointer <XmlElement> lastExplicitSettings; | |||
| mutable bool listNeedsScanning; | |||
| bool useInputNames; | |||
| Atomic<int> inputLevelMeasurementEnabledCount; | |||
| double inputLevel; | |||
| ScopedPointer <AudioSampleBuffer> testSound; | |||
| int testSoundPosition; | |||
| AudioSampleBuffer tempBuffer; | |||
| StringArray midiInsFromXml; | |||
| OwnedArray <MidiInput> enabledMidiInputs; | |||
| Array <MidiInputCallback*> midiCallbacks; | |||
| StringArray midiCallbackDevices; | |||
| String defaultMidiOutputName; | |||
| ScopedPointer <MidiOutput> defaultMidiOutput; | |||
| CriticalSection audioCallbackLock, midiCallbackLock; | |||
| double cpuUsageMs, timeToCpuScale; | |||
| //============================================================================== | |||
| class CallbackHandler; | |||
| friend class CallbackHandler; | |||
| friend struct ContainerDeletePolicy<CallbackHandler>; | |||
| ScopedPointer<CallbackHandler> callbackHandler; | |||
| void audioDeviceIOCallbackInt (const float** inputChannelData, int totalNumInputChannels, | |||
| float** outputChannelData, int totalNumOutputChannels, int numSamples); | |||
| void audioDeviceAboutToStartInt (AudioIODevice*); | |||
| void audioDeviceStoppedInt(); | |||
| void audioDeviceErrorInt (const String&); | |||
| void handleIncomingMidiMessageInt (MidiInput*, const MidiMessage&); | |||
| void audioDeviceListChanged(); | |||
| String restartDevice (int blockSizeToUse, double sampleRateToUse, | |||
| const BigInteger& ins, const BigInteger& outs); | |||
| void stopDevice(); | |||
| void updateXml(); | |||
| void createDeviceTypesIfNeeded(); | |||
| void scanDevicesIfNeeded(); | |||
| void deleteCurrentDevice(); | |||
| double chooseBestSampleRate (double preferred) const; | |||
| int chooseBestBufferSize (int preferred) const; | |||
| void insertDefaultDeviceNames (AudioDeviceSetup&) const; | |||
| AudioIODeviceType* findType (const String& inputName, const String& outputName); | |||
| AudioIODeviceType* findType (const String& typeName); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioDeviceManager) | |||
| }; | |||
| #endif // JUCE_AUDIODEVICEMANAGER_H_INCLUDED | |||
| @@ -0,0 +1,48 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| AudioIODevice::AudioIODevice (const String& deviceName, const String& typeName_) | |||
| : name (deviceName), | |||
| typeName (typeName_) | |||
| { | |||
| } | |||
| AudioIODevice::~AudioIODevice() | |||
| { | |||
| } | |||
| bool AudioIODevice::hasControlPanel() const | |||
| { | |||
| return false; | |||
| } | |||
| bool AudioIODevice::showControlPanel() | |||
| { | |||
| jassertfalse; // this should only be called for devices which return true from | |||
| // their hasControlPanel() method. | |||
| return false; | |||
| } | |||
| //============================================================================== | |||
| void AudioIODeviceCallback::audioDeviceError (const String&) {} | |||
| @@ -0,0 +1,332 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIOIODEVICE_H_INCLUDED | |||
| #define JUCE_AUDIOIODEVICE_H_INCLUDED | |||
| class AudioIODevice; | |||
| //============================================================================== | |||
| /** | |||
| One of these is passed to an AudioIODevice object to stream the audio data | |||
| in and out. | |||
| The AudioIODevice will repeatedly call this class's audioDeviceIOCallback() | |||
| method on its own high-priority audio thread, when it needs to send or receive | |||
| the next block of data. | |||
| @see AudioIODevice, AudioDeviceManager | |||
| */ | |||
| class JUCE_API AudioIODeviceCallback | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~AudioIODeviceCallback() {} | |||
| /** Processes a block of incoming and outgoing audio data. | |||
| The subclass's implementation should use the incoming audio for whatever | |||
| purposes it needs to, and must fill all the output channels with the next | |||
| block of output data before returning. | |||
| The channel data is arranged with the same array indices as the channel name | |||
| array returned by AudioIODevice::getOutputChannelNames(), but those channels | |||
| that aren't specified in AudioIODevice::open() will have a null pointer for their | |||
| associated channel, so remember to check for this. | |||
| @param inputChannelData a set of arrays containing the audio data for each | |||
| incoming channel - this data is valid until the function | |||
| returns. There will be one channel of data for each input | |||
| channel that was enabled when the audio device was opened | |||
| (see AudioIODevice::open()) | |||
| @param numInputChannels the number of pointers to channel data in the | |||
| inputChannelData array. | |||
| @param outputChannelData a set of arrays which need to be filled with the data | |||
| that should be sent to each outgoing channel of the device. | |||
| There will be one channel of data for each output channel | |||
| that was enabled when the audio device was opened (see | |||
| AudioIODevice::open()) | |||
| The initial contents of the array is undefined, so the | |||
| callback function must fill all the channels with zeros if | |||
| its output is silence. Failing to do this could cause quite | |||
| an unpleasant noise! | |||
| @param numOutputChannels the number of pointers to channel data in the | |||
| outputChannelData array. | |||
| @param numSamples the number of samples in each channel of the input and | |||
| output arrays. The number of samples will depend on the | |||
| audio device's buffer size and will usually remain constant, | |||
| although this isn't guaranteed, so make sure your code can | |||
| cope with reasonable changes in the buffer size from one | |||
| callback to the next. | |||
| */ | |||
| virtual void audioDeviceIOCallback (const float** inputChannelData, | |||
| int numInputChannels, | |||
| float** outputChannelData, | |||
| int numOutputChannels, | |||
| int numSamples) = 0; | |||
| /** Called to indicate that the device is about to start calling back. | |||
| This will be called just before the audio callbacks begin, either when this | |||
| callback has just been added to an audio device, or after the device has been | |||
| restarted because of a sample-rate or block-size change. | |||
| You can use this opportunity to find out the sample rate and block size | |||
| that the device is going to use by calling the AudioIODevice::getCurrentSampleRate() | |||
| and AudioIODevice::getCurrentBufferSizeSamples() on the supplied pointer. | |||
| @param device the audio IO device that will be used to drive the callback. | |||
| Note that if you're going to store this this pointer, it is | |||
| only valid until the next time that audioDeviceStopped is called. | |||
| */ | |||
| virtual void audioDeviceAboutToStart (AudioIODevice* device) = 0; | |||
| /** Called to indicate that the device has stopped. */ | |||
| virtual void audioDeviceStopped() = 0; | |||
| /** This can be overridden to be told if the device generates an error while operating. | |||
| Be aware that this could be called by any thread! And not all devices perform | |||
| this callback. | |||
| */ | |||
| virtual void audioDeviceError (const String& errorMessage); | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Base class for an audio device with synchronised input and output channels. | |||
| Subclasses of this are used to implement different protocols such as DirectSound, | |||
| ASIO, CoreAudio, etc. | |||
| To create one of these, you'll need to use the AudioIODeviceType class - see the | |||
| documentation for that class for more info. | |||
| For an easier way of managing audio devices and their settings, have a look at the | |||
| AudioDeviceManager class. | |||
| @see AudioIODeviceType, AudioDeviceManager | |||
| */ | |||
| class JUCE_API AudioIODevice | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~AudioIODevice(); | |||
| //============================================================================== | |||
| /** Returns the device's name, (as set in the constructor). */ | |||
| const String& getName() const noexcept { return name; } | |||
| /** Returns the type of the device. | |||
| E.g. "CoreAudio", "ASIO", etc. - this comes from the AudioIODeviceType that created it. | |||
| */ | |||
| const String& getTypeName() const noexcept { return typeName; } | |||
| //============================================================================== | |||
| /** Returns the names of all the available output channels on this device. | |||
| To find out which of these are currently in use, call getActiveOutputChannels(). | |||
| */ | |||
| virtual StringArray getOutputChannelNames() = 0; | |||
| /** Returns the names of all the available input channels on this device. | |||
| To find out which of these are currently in use, call getActiveInputChannels(). | |||
| */ | |||
| virtual StringArray getInputChannelNames() = 0; | |||
| //============================================================================== | |||
| /** Returns the number of sample-rates this device supports. | |||
| To find out which rates are available on this device, use this method to | |||
| find out how many there are, and getSampleRate() to get the rates. | |||
| @see getSampleRate | |||
| */ | |||
| virtual int getNumSampleRates() = 0; | |||
| /** Returns one of the sample-rates this device supports. | |||
| To find out which rates are available on this device, use getNumSampleRates() to | |||
| find out how many there are, and getSampleRate() to get the individual rates. | |||
| The sample rate is set by the open() method. | |||
| (Note that for DirectSound some rates might not work, depending on combinations | |||
| of i/o channels that are being opened). | |||
| @see getNumSampleRates | |||
| */ | |||
| virtual double getSampleRate (int index) = 0; | |||
| /** Returns the number of sizes of buffer that are available. | |||
| @see getBufferSizeSamples, getDefaultBufferSize | |||
| */ | |||
| virtual int getNumBufferSizesAvailable() = 0; | |||
| /** Returns one of the possible buffer-sizes. | |||
| @param index the index of the buffer-size to use, from 0 to getNumBufferSizesAvailable() - 1 | |||
| @returns a number of samples | |||
| @see getNumBufferSizesAvailable, getDefaultBufferSize | |||
| */ | |||
| virtual int getBufferSizeSamples (int index) = 0; | |||
| /** Returns the default buffer-size to use. | |||
| @returns a number of samples | |||
| @see getNumBufferSizesAvailable, getBufferSizeSamples | |||
| */ | |||
| virtual int getDefaultBufferSize() = 0; | |||
| //============================================================================== | |||
| /** Tries to open the device ready to play. | |||
| @param inputChannels a BigInteger in which a set bit indicates that the corresponding | |||
| input channel should be enabled | |||
| @param outputChannels a BigInteger in which a set bit indicates that the corresponding | |||
| output channel should be enabled | |||
| @param sampleRate the sample rate to try to use - to find out which rates are | |||
| available, see getNumSampleRates() and getSampleRate() | |||
| @param bufferSizeSamples the size of i/o buffer to use - to find out the available buffer | |||
| sizes, see getNumBufferSizesAvailable() and getBufferSizeSamples() | |||
| @returns an error description if there's a problem, or an empty string if it succeeds in | |||
| opening the device | |||
| @see close | |||
| */ | |||
| virtual String open (const BigInteger& inputChannels, | |||
| const BigInteger& outputChannels, | |||
| double sampleRate, | |||
| int bufferSizeSamples) = 0; | |||
| /** Closes and releases the device if it's open. */ | |||
| virtual void close() = 0; | |||
| /** Returns true if the device is still open. | |||
| A device might spontaneously close itself if something goes wrong, so this checks if | |||
| it's still open. | |||
| */ | |||
| virtual bool isOpen() = 0; | |||
| /** Starts the device actually playing. | |||
| This must be called after the device has been opened. | |||
| @param callback the callback to use for streaming the data. | |||
| @see AudioIODeviceCallback, open | |||
| */ | |||
| virtual void start (AudioIODeviceCallback* callback) = 0; | |||
| /** Stops the device playing. | |||
| Once a device has been started, this will stop it. Any pending calls to the | |||
| callback class will be flushed before this method returns. | |||
| */ | |||
| virtual void stop() = 0; | |||
| /** Returns true if the device is still calling back. | |||
| The device might mysteriously stop, so this checks whether it's | |||
| still playing. | |||
| */ | |||
| virtual bool isPlaying() = 0; | |||
| /** Returns the last error that happened if anything went wrong. */ | |||
| virtual String getLastError() = 0; | |||
| //============================================================================== | |||
| /** Returns the buffer size that the device is currently using. | |||
| If the device isn't actually open, this value doesn't really mean much. | |||
| */ | |||
| virtual int getCurrentBufferSizeSamples() = 0; | |||
| /** Returns the sample rate that the device is currently using. | |||
| If the device isn't actually open, this value doesn't really mean much. | |||
| */ | |||
| virtual double getCurrentSampleRate() = 0; | |||
| /** Returns the device's current physical bit-depth. | |||
| If the device isn't actually open, this value doesn't really mean much. | |||
| */ | |||
| virtual int getCurrentBitDepth() = 0; | |||
| /** Returns a mask showing which of the available output channels are currently | |||
| enabled. | |||
| @see getOutputChannelNames | |||
| */ | |||
| virtual BigInteger getActiveOutputChannels() const = 0; | |||
| /** Returns a mask showing which of the available input channels are currently | |||
| enabled. | |||
| @see getInputChannelNames | |||
| */ | |||
| virtual BigInteger getActiveInputChannels() const = 0; | |||
| /** Returns the device's output latency. | |||
| This is the delay in samples between a callback getting a block of data, and | |||
| that data actually getting played. | |||
| */ | |||
| virtual int getOutputLatencyInSamples() = 0; | |||
| /** Returns the device's input latency. | |||
| This is the delay in samples between some audio actually arriving at the soundcard, | |||
| and the callback getting passed this block of data. | |||
| */ | |||
| virtual int getInputLatencyInSamples() = 0; | |||
| //============================================================================== | |||
| /** True if this device can show a pop-up control panel for editing its settings. | |||
| This is generally just true of ASIO devices. If true, you can call showControlPanel() | |||
| to display it. | |||
| */ | |||
| virtual bool hasControlPanel() const; | |||
| /** Shows a device-specific control panel if there is one. | |||
| This should only be called for devices which return true from hasControlPanel(). | |||
| */ | |||
| virtual bool showControlPanel(); | |||
| //============================================================================== | |||
| protected: | |||
| /** Creates a device, setting its name and type member variables. */ | |||
| AudioIODevice (const String& deviceName, | |||
| const String& typeName); | |||
| /** @internal */ | |||
| String name, typeName; | |||
| }; | |||
| #endif // JUCE_AUDIOIODEVICE_H_INCLUDED | |||
| @@ -0,0 +1,78 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| AudioIODeviceType::AudioIODeviceType (const String& name) | |||
| : typeName (name) | |||
| { | |||
| } | |||
| AudioIODeviceType::~AudioIODeviceType() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| void AudioIODeviceType::addListener (Listener* l) { listeners.add (l); } | |||
| void AudioIODeviceType::removeListener (Listener* l) { listeners.remove (l); } | |||
| void AudioIODeviceType::callDeviceChangeListeners() | |||
| { | |||
| listeners.call (&AudioIODeviceType::Listener::audioDeviceListChanged); | |||
| } | |||
| //============================================================================== | |||
| #if ! JUCE_MAC | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_CoreAudio() { return nullptr; } | |||
| #endif | |||
| #if ! JUCE_IOS | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() { return nullptr; } | |||
| #endif | |||
| #if ! (JUCE_WINDOWS && JUCE_WASAPI) | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_WASAPI() { return nullptr; } | |||
| #endif | |||
| #if ! (JUCE_WINDOWS && JUCE_DIRECTSOUND) | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_DirectSound() { return nullptr; } | |||
| #endif | |||
| #if ! (JUCE_WINDOWS && JUCE_ASIO) | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ASIO() { return nullptr; } | |||
| #endif | |||
| #if ! (JUCE_LINUX && JUCE_ALSA) | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_ALSA() { return nullptr; } | |||
| #endif | |||
| #if ! (JUCE_LINUX && JUCE_JACK) | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() { return nullptr; } | |||
| #endif | |||
| #if ! JUCE_ANDROID | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() { return nullptr; } | |||
| #endif | |||
| #if ! (JUCE_ANDROID && JUCE_USE_ANDROID_OPENSLES) | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() { return nullptr; } | |||
| #endif | |||
| @@ -0,0 +1,182 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIOIODEVICETYPE_H_INCLUDED | |||
| #define JUCE_AUDIOIODEVICETYPE_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Represents a type of audio driver, such as DirectSound, ASIO, CoreAudio, etc. | |||
| To get a list of available audio driver types, use the AudioDeviceManager::createAudioDeviceTypes() | |||
| method. Each of the objects returned can then be used to list the available | |||
| devices of that type. E.g. | |||
| @code | |||
| OwnedArray <AudioIODeviceType> types; | |||
| myAudioDeviceManager.createAudioDeviceTypes (types); | |||
| for (int i = 0; i < types.size(); ++i) | |||
| { | |||
| String typeName (types[i]->getTypeName()); // This will be things like "DirectSound", "CoreAudio", etc. | |||
| types[i]->scanForDevices(); // This must be called before getting the list of devices | |||
| StringArray deviceNames (types[i]->getDeviceNames()); // This will now return a list of available devices of this type | |||
| for (int j = 0; j < deviceNames.size(); ++j) | |||
| { | |||
| AudioIODevice* device = types[i]->createDevice (deviceNames [j]); | |||
| ... | |||
| } | |||
| } | |||
| @endcode | |||
| For an easier way of managing audio devices and their settings, have a look at the | |||
| AudioDeviceManager class. | |||
| @see AudioIODevice, AudioDeviceManager | |||
| */ | |||
| class JUCE_API AudioIODeviceType | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns the name of this type of driver that this object manages. | |||
| This will be something like "DirectSound", "ASIO", "CoreAudio", "ALSA", etc. | |||
| */ | |||
| const String& getTypeName() const noexcept { return typeName; } | |||
| //============================================================================== | |||
| /** Refreshes the object's cached list of known devices. | |||
| This must be called at least once before calling getDeviceNames() or any of | |||
| the other device creation methods. | |||
| */ | |||
| virtual void scanForDevices() = 0; | |||
| /** Returns the list of available devices of this type. | |||
| The scanForDevices() method must have been called to create this list. | |||
| @param wantInputNames only really used by DirectSound where devices are split up | |||
| into inputs and outputs, this indicates whether to use | |||
| the input or output name to refer to a pair of devices. | |||
| */ | |||
| virtual StringArray getDeviceNames (bool wantInputNames = false) const = 0; | |||
| /** Returns the name of the default device. | |||
| This will be one of the names from the getDeviceNames() list. | |||
| @param forInput if true, this means that a default input device should be | |||
| returned; if false, it should return the default output | |||
| */ | |||
| virtual int getDefaultDeviceIndex (bool forInput) const = 0; | |||
| /** Returns the index of a given device in the list of device names. | |||
| If asInput is true, it shows the index in the inputs list, otherwise it | |||
| looks for it in the outputs list. | |||
| */ | |||
| virtual int getIndexOfDevice (AudioIODevice* device, bool asInput) const = 0; | |||
| /** Returns true if two different devices can be used for the input and output. | |||
| */ | |||
| virtual bool hasSeparateInputsAndOutputs() const = 0; | |||
| /** Creates one of the devices of this type. | |||
| The deviceName must be one of the strings returned by getDeviceNames(), and | |||
| scanForDevices() must have been called before this method is used. | |||
| */ | |||
| virtual AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) = 0; | |||
| //============================================================================== | |||
| /** | |||
| A class for receiving events when audio devices are inserted or removed. | |||
| You can register a AudioIODeviceType::Listener with an~AudioIODeviceType object | |||
| using the AudioIODeviceType::addListener() method, and it will be called when | |||
| devices of that type are added or removed. | |||
| @see AudioIODeviceType::addListener, AudioIODeviceType::removeListener | |||
| */ | |||
| class Listener | |||
| { | |||
| public: | |||
| virtual ~Listener() {} | |||
| /** Called when the list of available audio devices changes. */ | |||
| virtual void audioDeviceListChanged() = 0; | |||
| }; | |||
| /** Adds a listener that will be called when this type of device is added or | |||
| removed from the system. | |||
| */ | |||
| void addListener (Listener* listener); | |||
| /** Removes a listener that was previously added with addListener(). */ | |||
| void removeListener (Listener* listener); | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| virtual ~AudioIODeviceType(); | |||
| //============================================================================== | |||
| /** Creates a CoreAudio device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_CoreAudio(); | |||
| /** Creates an iOS device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_iOSAudio(); | |||
| /** Creates a WASAPI device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_WASAPI(); | |||
| /** Creates a DirectSound device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_DirectSound(); | |||
| /** Creates an ASIO device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_ASIO(); | |||
| /** Creates an ALSA device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_ALSA(); | |||
| /** Creates a JACK device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_JACK(); | |||
| /** Creates an Android device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_Android(); | |||
| /** Creates an Android OpenSLES device type if it's available on this platform, or returns null. */ | |||
| static AudioIODeviceType* createAudioIODeviceType_OpenSLES(); | |||
| protected: | |||
| explicit AudioIODeviceType (const String& typeName); | |||
| /** Synchronously calls all the registered device list change listeners. */ | |||
| void callDeviceChangeListeners(); | |||
| private: | |||
| String typeName; | |||
| ListenerList<Listener> listeners; | |||
| JUCE_DECLARE_NON_COPYABLE (AudioIODeviceType) | |||
| }; | |||
| #endif // JUCE_AUDIOIODEVICETYPE_H_INCLUDED | |||
| @@ -0,0 +1,61 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED | |||
| #define JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Contains functions to control the system's master volume. | |||
| */ | |||
| class JUCE_API SystemAudioVolume | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns the operating system's current volume level in the range 0 to 1.0 */ | |||
| static float JUCE_CALLTYPE getGain(); | |||
| /** Attempts to set the operating system's current volume level. | |||
| @param newGain the level, between 0 and 1.0 | |||
| @returns true if the operation succeeds | |||
| */ | |||
| static bool JUCE_CALLTYPE setGain (float newGain); | |||
| /** Returns true if the system's audio output is currently muted. */ | |||
| static bool JUCE_CALLTYPE isMuted(); | |||
| /** Attempts to mute the operating system's audio output. | |||
| @param shouldBeMuted true if you want it to be muted | |||
| @returns true if the operation succeeds | |||
| */ | |||
| static bool JUCE_CALLTYPE setMuted (bool shouldBeMuted); | |||
| private: | |||
| SystemAudioVolume(); // Don't instantiate this class, just call its static fns. | |||
| JUCE_DECLARE_NON_COPYABLE (SystemAudioVolume) | |||
| }; | |||
| #endif // JUCE_SYSTEMAUDIOVOLUME_H_INCLUDED | |||
| @@ -0,0 +1,226 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if defined (JUCE_AUDIO_DEVICES_H_INCLUDED) && ! JUCE_AMALGAMATED_INCLUDE | |||
| /* When you add this cpp file to your project, you mustn't include it in a file where you've | |||
| already included any other headers - just put it inside a file on its own, possibly with your config | |||
| flags preceding it, but don't include anything else. That also includes avoiding any automatic prefix | |||
| header files that the compiler may be using. | |||
| */ | |||
| #error "Incorrect use of JUCE cpp file" | |||
| #endif | |||
| // Your project must contain an AppConfig.h file with your project-specific settings in it, | |||
| // and your header search path must make it accessible to the module's files. | |||
| #include "AppConfig.h" | |||
| #include "../juce_core/native/juce_BasicNativeHeaders.h" | |||
| #include "juce_audio_devices.h" | |||
| //============================================================================== | |||
| #if JUCE_MAC | |||
| #define Point CarbonDummyPointName | |||
| #define Component CarbonDummyCompName | |||
| #import <CoreAudio/AudioHardware.h> | |||
| #import <CoreMIDI/MIDIServices.h> | |||
| #import <DiscRecording/DiscRecording.h> | |||
| #import <AudioToolbox/AudioServices.h> | |||
| #undef Point | |||
| #undef Component | |||
| #elif JUCE_IOS | |||
| #import <AudioToolbox/AudioToolbox.h> | |||
| #import <AVFoundation/AVFoundation.h> | |||
| #import <CoreMIDI/MIDIServices.h> | |||
| //============================================================================== | |||
| #elif JUCE_WINDOWS | |||
| #if JUCE_WASAPI | |||
| #include <MMReg.h> | |||
| #endif | |||
| #if JUCE_ASIO | |||
| /* This is very frustrating - we only need to use a handful of definitions from | |||
| a couple of the header files in Steinberg's ASIO SDK, and it'd be easy to copy | |||
| about 30 lines of code into this cpp file to create a fully stand-alone ASIO | |||
| implementation... | |||
| ..unfortunately that would break Steinberg's license agreement for use of | |||
| their SDK, so I'm not allowed to do this. | |||
| This means that anyone who wants to use JUCE's ASIO abilities will have to: | |||
| 1) Agree to Steinberg's licensing terms and download the ASIO SDK | |||
| (see www.steinberg.net/Steinberg/Developers.asp). | |||
| 2) Enable this code with a global definition #define JUCE_ASIO 1. | |||
| 3) Make sure that your header search path contains the iasiodrv.h file that | |||
| comes with the SDK. (Only about a handful of the SDK header files are actually | |||
| needed - so to simplify things, you could just copy these into your JUCE directory). | |||
| */ | |||
| #include <iasiodrv.h> | |||
| #endif | |||
| #if JUCE_USE_CDBURNER | |||
| /* You'll need the Platform SDK for these headers - if you don't have it and don't | |||
| need to use CD-burning, then you might just want to set the JUCE_USE_CDBURNER flag | |||
| to 0, to avoid these includes. | |||
| */ | |||
| #include <imapi.h> | |||
| #include <imapierror.h> | |||
| #endif | |||
| //============================================================================== | |||
| #elif JUCE_LINUX | |||
| #if JUCE_ALSA | |||
| /* Got an include error here? If so, you've either not got ALSA installed, or you've | |||
| not got your paths set up correctly to find its header files. | |||
| The package you need to install to get ASLA support is "libasound2-dev". | |||
| If you don't have the ALSA library and don't want to build Juce with audio support, | |||
| just set the JUCE_ALSA flag to 0. | |||
| */ | |||
| #include <alsa/asoundlib.h> | |||
| #endif | |||
| #if JUCE_JACK | |||
| /* Got an include error here? If so, you've either not got jack-audio-connection-kit | |||
| installed, or you've not got your paths set up correctly to find its header files. | |||
| The package you need to install to get JACK support is "libjack-dev". | |||
| If you don't have the jack-audio-connection-kit library and don't want to build | |||
| Juce with low latency audio support, just set the JUCE_JACK flag to 0. | |||
| */ | |||
| #include <jack/jack.h> | |||
| #endif | |||
| #undef SIZEOF | |||
| //============================================================================== | |||
| #elif JUCE_ANDROID | |||
| #if JUCE_USE_ANDROID_OPENSLES | |||
| #include <SLES/OpenSLES.h> | |||
| #include <SLES/OpenSLES_Android.h> | |||
| #endif | |||
| #endif | |||
| namespace juce | |||
| { | |||
| #include "audio_io/juce_AudioDeviceManager.cpp" | |||
| #include "audio_io/juce_AudioIODevice.cpp" | |||
| #include "audio_io/juce_AudioIODeviceType.cpp" | |||
| #include "midi_io/juce_MidiMessageCollector.cpp" | |||
| #include "midi_io/juce_MidiOutput.cpp" | |||
| #include "audio_cd/juce_AudioCDReader.cpp" | |||
| #include "sources/juce_AudioSourcePlayer.cpp" | |||
| #include "sources/juce_AudioTransportSource.cpp" | |||
| #include "native/juce_MidiDataConcatenator.h" | |||
| //============================================================================== | |||
| #if JUCE_MAC | |||
| #include "../juce_core/native/juce_osx_ObjCHelpers.h" | |||
| #include "native/juce_mac_CoreAudio.cpp" | |||
| #include "native/juce_mac_CoreMidi.cpp" | |||
| #if JUCE_USE_CDREADER | |||
| #include "native/juce_mac_AudioCDReader.mm" | |||
| #endif | |||
| #if JUCE_USE_CDBURNER | |||
| #include "native/juce_mac_AudioCDBurner.mm" | |||
| #endif | |||
| //============================================================================== | |||
| #elif JUCE_IOS | |||
| #include "native/juce_ios_Audio.cpp" | |||
| #include "native/juce_mac_CoreMidi.cpp" | |||
| //============================================================================== | |||
| #elif JUCE_WINDOWS | |||
| #include "../juce_core/native/juce_win32_ComSmartPtr.h" | |||
| #include "../juce_events/native/juce_win32_HiddenMessageWindow.h" | |||
| #if JUCE_WASAPI | |||
| #include "native/juce_win32_WASAPI.cpp" | |||
| #endif | |||
| #if JUCE_DIRECTSOUND | |||
| #include "native/juce_win32_DirectSound.cpp" | |||
| #endif | |||
| #include "native/juce_win32_Midi.cpp" | |||
| #if JUCE_ASIO | |||
| #include "native/juce_win32_ASIO.cpp" | |||
| #endif | |||
| #if JUCE_USE_CDREADER | |||
| #include "native/juce_win32_AudioCDReader.cpp" | |||
| #endif | |||
| #if JUCE_USE_CDBURNER | |||
| #include "native/juce_win32_AudioCDBurner.cpp" | |||
| #endif | |||
| //============================================================================== | |||
| #elif JUCE_LINUX | |||
| #if JUCE_ALSA | |||
| #include "native/juce_linux_ALSA.cpp" | |||
| #endif | |||
| #include "native/juce_linux_Midi.cpp" | |||
| #if JUCE_JACK | |||
| #include "native/juce_linux_JackAudio.cpp" | |||
| #endif | |||
| #if JUCE_USE_CDREADER | |||
| #include "native/juce_linux_AudioCDReader.cpp" | |||
| #endif | |||
| //============================================================================== | |||
| #elif JUCE_ANDROID | |||
| #include "../juce_core/native/juce_android_JNIHelpers.h" | |||
| #include "native/juce_android_Audio.cpp" | |||
| #include "native/juce_android_Midi.cpp" | |||
| #if JUCE_USE_ANDROID_OPENSLES | |||
| #include "native/juce_android_OpenSL.cpp" | |||
| #endif | |||
| #endif | |||
| #if ! JUCE_SYSTEMAUDIOVOL_IMPLEMENTED | |||
| // None of these methods are available. (On Windows you might need to enable WASAPI for this) | |||
| float JUCE_CALLTYPE SystemAudioVolume::getGain() { jassertfalse; return 0.0f; } | |||
| bool JUCE_CALLTYPE SystemAudioVolume::setGain (float) { jassertfalse; return false; } | |||
| bool JUCE_CALLTYPE SystemAudioVolume::isMuted() { jassertfalse; return false; } | |||
| bool JUCE_CALLTYPE SystemAudioVolume::setMuted (bool) { jassertfalse; return false; } | |||
| #endif | |||
| } | |||
| @@ -0,0 +1,117 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIO_DEVICES_H_INCLUDED | |||
| #define JUCE_AUDIO_DEVICES_H_INCLUDED | |||
| #include "../juce_events/juce_events.h" | |||
| #include "../juce_audio_basics/juce_audio_basics.h" | |||
| #include "../juce_audio_formats/juce_audio_formats.h" | |||
| //============================================================================= | |||
| /** Config: JUCE_ASIO | |||
| Enables ASIO audio devices (MS Windows only). | |||
| Turning this on means that you'll need to have the Steinberg ASIO SDK installed | |||
| on your Windows build machine. | |||
| See the comments in the ASIOAudioIODevice class's header file for more | |||
| info about this. | |||
| */ | |||
| #ifndef JUCE_ASIO | |||
| #define JUCE_ASIO 0 | |||
| #endif | |||
| /** Config: JUCE_WASAPI | |||
| Enables WASAPI audio devices (Windows Vista and above). | |||
| */ | |||
| #ifndef JUCE_WASAPI | |||
| #define JUCE_WASAPI 1 | |||
| #endif | |||
| /** Config: JUCE_DIRECTSOUND | |||
| Enables DirectSound audio (MS Windows only). | |||
| */ | |||
| #ifndef JUCE_DIRECTSOUND | |||
| #define JUCE_DIRECTSOUND 1 | |||
| #endif | |||
| /** Config: JUCE_ALSA | |||
| Enables ALSA audio devices (Linux only). | |||
| */ | |||
| #ifndef JUCE_ALSA | |||
| #define JUCE_ALSA 1 | |||
| #endif | |||
| /** Config: JUCE_JACK | |||
| Enables JACK audio devices (Linux only). | |||
| */ | |||
| #ifndef JUCE_JACK | |||
| #define JUCE_JACK 0 | |||
| #endif | |||
| /** Config: JUCE_USE_ANDROID_OPENSLES | |||
| Enables OpenSLES devices (Android only). | |||
| */ | |||
| #ifndef JUCE_USE_ANDROID_OPENSLES | |||
| #if JUCE_ANDROID_API_VERSION > 8 | |||
| #define JUCE_USE_ANDROID_OPENSLES 1 | |||
| #else | |||
| #define JUCE_USE_ANDROID_OPENSLES 0 | |||
| #endif | |||
| #endif | |||
| //============================================================================= | |||
| /** Config: JUCE_USE_CDREADER | |||
| Enables the AudioCDReader class (on supported platforms). | |||
| */ | |||
| #ifndef JUCE_USE_CDREADER | |||
| #define JUCE_USE_CDREADER 0 | |||
| #endif | |||
| /** Config: JUCE_USE_CDBURNER | |||
| Enables the AudioCDBurner class (on supported platforms). | |||
| */ | |||
| #ifndef JUCE_USE_CDBURNER | |||
| #define JUCE_USE_CDBURNER 0 | |||
| #endif | |||
| //============================================================================= | |||
| namespace juce | |||
| { | |||
| #include "audio_io/juce_AudioIODevice.h" | |||
| #include "audio_io/juce_AudioIODeviceType.h" | |||
| #include "audio_io/juce_SystemAudioVolume.h" | |||
| #include "midi_io/juce_MidiInput.h" | |||
| #include "midi_io/juce_MidiMessageCollector.h" | |||
| #include "midi_io/juce_MidiOutput.h" | |||
| #include "sources/juce_AudioSourcePlayer.h" | |||
| #include "sources/juce_AudioTransportSource.h" | |||
| #include "audio_cd/juce_AudioCDBurner.h" | |||
| #include "audio_cd/juce_AudioCDReader.h" | |||
| #include "audio_io/juce_AudioDeviceManager.h" | |||
| } | |||
| #endif // JUCE_AUDIO_DEVICES_H_INCLUDED | |||
| @@ -0,0 +1,25 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #include "juce_audio_devices.cpp" | |||
| @@ -0,0 +1,28 @@ | |||
| { | |||
| "id": "juce_audio_devices", | |||
| "name": "JUCE audio and midi I/O device classes", | |||
| "version": "2.1.3", | |||
| "description": "Classes to play and record from audio and midi i/o devices.", | |||
| "website": "http://www.juce.com/juce", | |||
| "license": "GPL/Commercial", | |||
| "dependencies": [ { "id": "juce_audio_basics", "version": "matching" }, | |||
| { "id": "juce_audio_formats", "version": "matching" }, | |||
| { "id": "juce_events", "version": "matching" } ], | |||
| "include": "juce_audio_devices.h", | |||
| "compile": [ { "file": "juce_audio_devices.cpp", "target": "! xcode" }, | |||
| { "file": "juce_audio_devices.mm", "target": "xcode" } ], | |||
| "browse": [ "audio_io/*", | |||
| "midi_io/*", | |||
| "sources/*", | |||
| "audio_cd/*", | |||
| "native/*" ], | |||
| "OSXFrameworks": "CoreAudio CoreMIDI DiscRecording", | |||
| "iOSFrameworks": "AudioToolbox CoreMIDI", | |||
| "LinuxLibs": "asound", | |||
| "mingwLibs": "winmm" | |||
| } | |||
| @@ -0,0 +1,182 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MIDIINPUT_H_INCLUDED | |||
| #define JUCE_MIDIINPUT_H_INCLUDED | |||
| class MidiInput; | |||
| //============================================================================== | |||
| /** | |||
| Receives incoming messages from a physical MIDI input device. | |||
| This class is overridden to handle incoming midi messages. See the MidiInput | |||
| class for more details. | |||
| @see MidiInput | |||
| */ | |||
| class JUCE_API MidiInputCallback | |||
| { | |||
| public: | |||
| /** Destructor. */ | |||
| virtual ~MidiInputCallback() {} | |||
| /** Receives an incoming message. | |||
| A MidiInput object will call this method when a midi event arrives. It'll be | |||
| called on a high-priority system thread, so avoid doing anything time-consuming | |||
| in here, and avoid making any UI calls. You might find the MidiBuffer class helpful | |||
| for queueing incoming messages for use later. | |||
| @param source the MidiInput object that generated the message | |||
| @param message the incoming message. The message's timestamp is set to a value | |||
| equivalent to (Time::getMillisecondCounter() / 1000.0) to specify the | |||
| time when the message arrived. | |||
| */ | |||
| virtual void handleIncomingMidiMessage (MidiInput* source, | |||
| const MidiMessage& message) = 0; | |||
| /** Notification sent each time a packet of a multi-packet sysex message arrives. | |||
| If a long sysex message is broken up into multiple packets, this callback is made | |||
| for each packet that arrives until the message is finished, at which point | |||
| the normal handleIncomingMidiMessage() callback will be made with the entire | |||
| message. | |||
| The message passed in will contain the start of a sysex, but won't be finished | |||
| with the terminating 0xf7 byte. | |||
| */ | |||
| virtual void handlePartialSysexMessage (MidiInput* source, | |||
| const uint8* messageData, | |||
| int numBytesSoFar, | |||
| double timestamp) | |||
| { | |||
| // (this bit is just to avoid compiler warnings about unused variables) | |||
| (void) source; (void) messageData; (void) numBytesSoFar; (void) timestamp; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| /** | |||
| Represents a midi input device. | |||
| To create one of these, use the static getDevices() method to find out what inputs are | |||
| available, and then use the openDevice() method to try to open one. | |||
| @see MidiOutput | |||
| */ | |||
| class JUCE_API MidiInput | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns a list of the available midi input devices. | |||
| You can open one of the devices by passing its index into the | |||
| openDevice() method. | |||
| @see getDefaultDeviceIndex, openDevice | |||
| */ | |||
| static StringArray getDevices(); | |||
| /** Returns the index of the default midi input device to use. | |||
| This refers to the index in the list returned by getDevices(). | |||
| */ | |||
| static int getDefaultDeviceIndex(); | |||
| /** Tries to open one of the midi input devices. | |||
| This will return a MidiInput object if it manages to open it. You can then | |||
| call start() and stop() on this device, and delete it when no longer needed. | |||
| If the device can't be opened, this will return a null pointer. | |||
| @param deviceIndex the index of a device from the list returned by getDevices() | |||
| @param callback the object that will receive the midi messages from this device. | |||
| @see MidiInputCallback, getDevices | |||
| */ | |||
| static MidiInput* openDevice (int deviceIndex, | |||
| MidiInputCallback* callback); | |||
| #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN | |||
| /** This will try to create a new midi input device (Not available on Windows). | |||
| This will attempt to create a new midi input device with the specified name, | |||
| for other apps to connect to. | |||
| Returns nullptr if a device can't be created. | |||
| @param deviceName the name to use for the new device | |||
| @param callback the object that will receive the midi messages from this device. | |||
| */ | |||
| static MidiInput* createNewDevice (const String& deviceName, | |||
| MidiInputCallback* callback); | |||
| #endif | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| virtual ~MidiInput(); | |||
| /** Returns the name of this device. */ | |||
| const String& getName() const noexcept { return name; } | |||
| /** Allows you to set a custom name for the device, in case you don't like the name | |||
| it was given when created. | |||
| */ | |||
| void setName (const String& newName) noexcept { name = newName; } | |||
| //============================================================================== | |||
| /** Starts the device running. | |||
| After calling this, the device will start sending midi messages to the | |||
| MidiInputCallback object that was specified when the openDevice() method | |||
| was called. | |||
| @see stop | |||
| */ | |||
| virtual void start(); | |||
| /** Stops the device running. | |||
| @see start | |||
| */ | |||
| virtual void stop(); | |||
| protected: | |||
| //============================================================================== | |||
| String name; | |||
| void* internal; | |||
| explicit MidiInput (const String& name); | |||
| private: | |||
| //============================================================================== | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInput) | |||
| }; | |||
| #endif // JUCE_MIDIINPUT_H_INCLUDED | |||
| @@ -0,0 +1,153 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| MidiMessageCollector::MidiMessageCollector() | |||
| : lastCallbackTime (0), | |||
| sampleRate (44100.0001) | |||
| { | |||
| } | |||
| MidiMessageCollector::~MidiMessageCollector() | |||
| { | |||
| } | |||
| //============================================================================== | |||
| void MidiMessageCollector::reset (const double sampleRate_) | |||
| { | |||
| jassert (sampleRate_ > 0); | |||
| const ScopedLock sl (midiCallbackLock); | |||
| sampleRate = sampleRate_; | |||
| incomingMessages.clear(); | |||
| lastCallbackTime = Time::getMillisecondCounterHiRes(); | |||
| } | |||
| void MidiMessageCollector::addMessageToQueue (const MidiMessage& message) | |||
| { | |||
| // you need to call reset() to set the correct sample rate before using this object | |||
| jassert (sampleRate != 44100.0001); | |||
| // the messages that come in here need to be time-stamped correctly - see MidiInput | |||
| // for details of what the number should be. | |||
| jassert (message.getTimeStamp() != 0); | |||
| const ScopedLock sl (midiCallbackLock); | |||
| const int sampleNumber | |||
| = (int) ((message.getTimeStamp() - 0.001 * lastCallbackTime) * sampleRate); | |||
| incomingMessages.addEvent (message, sampleNumber); | |||
| // if the messages don't get used for over a second, we'd better | |||
| // get rid of any old ones to avoid the queue getting too big | |||
| if (sampleNumber > sampleRate) | |||
| incomingMessages.clear (0, sampleNumber - (int) sampleRate); | |||
| } | |||
| void MidiMessageCollector::removeNextBlockOfMessages (MidiBuffer& destBuffer, | |||
| const int numSamples) | |||
| { | |||
| // you need to call reset() to set the correct sample rate before using this object | |||
| jassert (sampleRate != 44100.0001); | |||
| jassert (numSamples > 0); | |||
| const double timeNow = Time::getMillisecondCounterHiRes(); | |||
| const double msElapsed = timeNow - lastCallbackTime; | |||
| const ScopedLock sl (midiCallbackLock); | |||
| lastCallbackTime = timeNow; | |||
| if (! incomingMessages.isEmpty()) | |||
| { | |||
| int numSourceSamples = jmax (1, roundToInt (msElapsed * 0.001 * sampleRate)); | |||
| int startSample = 0; | |||
| int scale = 1 << 16; | |||
| const uint8* midiData; | |||
| int numBytes, samplePosition; | |||
| MidiBuffer::Iterator iter (incomingMessages); | |||
| if (numSourceSamples > numSamples) | |||
| { | |||
| // if our list of events is longer than the buffer we're being | |||
| // asked for, scale them down to squeeze them all in.. | |||
| const int maxBlockLengthToUse = numSamples << 5; | |||
| if (numSourceSamples > maxBlockLengthToUse) | |||
| { | |||
| startSample = numSourceSamples - maxBlockLengthToUse; | |||
| numSourceSamples = maxBlockLengthToUse; | |||
| iter.setNextSamplePosition (startSample); | |||
| } | |||
| scale = (numSamples << 10) / numSourceSamples; | |||
| while (iter.getNextEvent (midiData, numBytes, samplePosition)) | |||
| { | |||
| samplePosition = ((samplePosition - startSample) * scale) >> 10; | |||
| destBuffer.addEvent (midiData, numBytes, | |||
| jlimit (0, numSamples - 1, samplePosition)); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| // if our event list is shorter than the number we need, put them | |||
| // towards the end of the buffer | |||
| startSample = numSamples - numSourceSamples; | |||
| while (iter.getNextEvent (midiData, numBytes, samplePosition)) | |||
| { | |||
| destBuffer.addEvent (midiData, numBytes, | |||
| jlimit (0, numSamples - 1, samplePosition + startSample)); | |||
| } | |||
| } | |||
| incomingMessages.clear(); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void MidiMessageCollector::handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) | |||
| { | |||
| MidiMessage m (MidiMessage::noteOn (midiChannel, midiNoteNumber, velocity)); | |||
| m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001); | |||
| addMessageToQueue (m); | |||
| } | |||
| void MidiMessageCollector::handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) | |||
| { | |||
| MidiMessage m (MidiMessage::noteOff (midiChannel, midiNoteNumber)); | |||
| m.setTimeStamp (Time::getMillisecondCounterHiRes() * 0.001); | |||
| addMessageToQueue (m); | |||
| } | |||
| void MidiMessageCollector::handleIncomingMidiMessage (MidiInput*, const MidiMessage& message) | |||
| { | |||
| addMessageToQueue (message); | |||
| } | |||
| @@ -0,0 +1,104 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED | |||
| #define JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Collects incoming realtime MIDI messages and turns them into blocks suitable for | |||
| processing by a block-based audio callback. | |||
| The class can also be used as either a MidiKeyboardStateListener or a MidiInputCallback | |||
| so it can easily use a midi input or keyboard component as its source. | |||
| @see MidiMessage, MidiInput | |||
| */ | |||
| class JUCE_API MidiMessageCollector : public MidiKeyboardStateListener, | |||
| public MidiInputCallback | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a MidiMessageCollector. */ | |||
| MidiMessageCollector(); | |||
| /** Destructor. */ | |||
| ~MidiMessageCollector(); | |||
| //============================================================================== | |||
| /** Clears any messages from the queue. | |||
| You need to call this method before starting to use the collector, so that | |||
| it knows the correct sample rate to use. | |||
| */ | |||
| void reset (double sampleRate); | |||
| /** Takes an incoming real-time message and adds it to the queue. | |||
| The message's timestamp is taken, and it will be ready for retrieval as part | |||
| of the block returned by the next call to removeNextBlockOfMessages(). | |||
| This method is fully thread-safe when overlapping calls are made with | |||
| removeNextBlockOfMessages(). | |||
| */ | |||
| void addMessageToQueue (const MidiMessage& message); | |||
| /** Removes all the pending messages from the queue as a buffer. | |||
| This will also correct the messages' timestamps to make sure they're in | |||
| the range 0 to numSamples - 1. | |||
| This call should be made regularly by something like an audio processing | |||
| callback, because the time that it happens is used in calculating the | |||
| midi event positions. | |||
| This method is fully thread-safe when overlapping calls are made with | |||
| addMessageToQueue(). | |||
| Precondition: numSamples must be greater than 0. | |||
| */ | |||
| void removeNextBlockOfMessages (MidiBuffer& destBuffer, int numSamples); | |||
| //============================================================================== | |||
| /** @internal */ | |||
| void handleNoteOn (MidiKeyboardState*, int midiChannel, int midiNoteNumber, float velocity) override; | |||
| /** @internal */ | |||
| void handleNoteOff (MidiKeyboardState*, int midiChannel, int midiNoteNumber) override; | |||
| /** @internal */ | |||
| void handleIncomingMidiMessage (MidiInput*, const MidiMessage&) override; | |||
| private: | |||
| //============================================================================== | |||
| double lastCallbackTime; | |||
| CriticalSection midiCallbackLock; | |||
| MidiBuffer incomingMessages; | |||
| double sampleRate; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiMessageCollector) | |||
| }; | |||
| #endif // JUCE_MIDIMESSAGECOLLECTOR_H_INCLUDED | |||
| @@ -0,0 +1,162 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| struct MidiOutput::PendingMessage | |||
| { | |||
| PendingMessage (const void* const data, const int len, const double timeStamp) | |||
| : message (data, len, timeStamp) | |||
| {} | |||
| MidiMessage message; | |||
| PendingMessage* next; | |||
| }; | |||
| MidiOutput::MidiOutput() | |||
| : Thread ("midi out"), | |||
| internal (nullptr), | |||
| firstMessage (nullptr) | |||
| { | |||
| } | |||
| void MidiOutput::sendBlockOfMessages (const MidiBuffer& buffer, | |||
| const double millisecondCounterToStartAt, | |||
| double samplesPerSecondForBuffer) | |||
| { | |||
| // You've got to call startBackgroundThread() for this to actually work.. | |||
| jassert (isThreadRunning()); | |||
| // this needs to be a value in the future - RTFM for this method! | |||
| jassert (millisecondCounterToStartAt > 0); | |||
| const double timeScaleFactor = 1000.0 / samplesPerSecondForBuffer; | |||
| MidiBuffer::Iterator i (buffer); | |||
| const uint8* data; | |||
| int len, time; | |||
| while (i.getNextEvent (data, len, time)) | |||
| { | |||
| const double eventTime = millisecondCounterToStartAt + timeScaleFactor * time; | |||
| PendingMessage* const m = new PendingMessage (data, len, eventTime); | |||
| const ScopedLock sl (lock); | |||
| if (firstMessage == nullptr || firstMessage->message.getTimeStamp() > eventTime) | |||
| { | |||
| m->next = firstMessage; | |||
| firstMessage = m; | |||
| } | |||
| else | |||
| { | |||
| PendingMessage* mm = firstMessage; | |||
| while (mm->next != nullptr && mm->next->message.getTimeStamp() <= eventTime) | |||
| mm = mm->next; | |||
| m->next = mm->next; | |||
| mm->next = m; | |||
| } | |||
| } | |||
| notify(); | |||
| } | |||
| void MidiOutput::clearAllPendingMessages() | |||
| { | |||
| const ScopedLock sl (lock); | |||
| while (firstMessage != nullptr) | |||
| { | |||
| PendingMessage* const m = firstMessage; | |||
| firstMessage = firstMessage->next; | |||
| delete m; | |||
| } | |||
| } | |||
| void MidiOutput::startBackgroundThread() | |||
| { | |||
| startThread (9); | |||
| } | |||
| void MidiOutput::stopBackgroundThread() | |||
| { | |||
| stopThread (5000); | |||
| } | |||
| void MidiOutput::run() | |||
| { | |||
| while (! threadShouldExit()) | |||
| { | |||
| uint32 now = Time::getMillisecondCounter(); | |||
| uint32 eventTime = 0; | |||
| uint32 timeToWait = 500; | |||
| PendingMessage* message; | |||
| { | |||
| const ScopedLock sl (lock); | |||
| message = firstMessage; | |||
| if (message != nullptr) | |||
| { | |||
| eventTime = (uint32) roundToInt (message->message.getTimeStamp()); | |||
| if (eventTime > now + 20) | |||
| { | |||
| timeToWait = eventTime - (now + 20); | |||
| message = nullptr; | |||
| } | |||
| else | |||
| { | |||
| firstMessage = message->next; | |||
| } | |||
| } | |||
| } | |||
| if (message != nullptr) | |||
| { | |||
| const ScopedPointer<PendingMessage> messageDeleter (message); | |||
| if (eventTime > now) | |||
| { | |||
| Time::waitForMillisecondCounter (eventTime); | |||
| if (threadShouldExit()) | |||
| break; | |||
| } | |||
| if (eventTime > now - 200) | |||
| sendMessageNow (message->message); | |||
| } | |||
| else | |||
| { | |||
| jassert (timeToWait < 1000 * 30); | |||
| wait ((int) timeToWait); | |||
| } | |||
| } | |||
| clearAllPendingMessages(); | |||
| } | |||
| @@ -0,0 +1,147 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MIDIOUTPUT_H_INCLUDED | |||
| #define JUCE_MIDIOUTPUT_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Controls a physical MIDI output device. | |||
| To create one of these, use the static getDevices() method to get a list of the | |||
| available output devices, then use the openDevice() method to try to open one. | |||
| @see MidiInput | |||
| */ | |||
| class JUCE_API MidiOutput : private Thread | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Returns a list of the available midi output devices. | |||
| You can open one of the devices by passing its index into the | |||
| openDevice() method. | |||
| @see getDefaultDeviceIndex, openDevice | |||
| */ | |||
| static StringArray getDevices(); | |||
| /** Returns the index of the default midi output device to use. | |||
| This refers to the index in the list returned by getDevices(). | |||
| */ | |||
| static int getDefaultDeviceIndex(); | |||
| /** Tries to open one of the midi output devices. | |||
| This will return a MidiOutput object if it manages to open it. You can then | |||
| send messages to this device, and delete it when no longer needed. | |||
| If the device can't be opened, this will return a null pointer. | |||
| @param deviceIndex the index of a device from the list returned by getDevices() | |||
| @see getDevices | |||
| */ | |||
| static MidiOutput* openDevice (int deviceIndex); | |||
| #if JUCE_LINUX || JUCE_MAC || JUCE_IOS || DOXYGEN | |||
| /** This will try to create a new midi output device (Not available on Windows). | |||
| This will attempt to create a new midi output device that other apps can connect | |||
| to and use as their midi input. | |||
| Returns nullptr if a device can't be created. | |||
| @param deviceName the name to use for the new device | |||
| */ | |||
| static MidiOutput* createNewDevice (const String& deviceName); | |||
| #endif | |||
| //============================================================================== | |||
| /** Destructor. */ | |||
| virtual ~MidiOutput(); | |||
| /** Makes this device output a midi message. | |||
| @see MidiMessage | |||
| */ | |||
| virtual void sendMessageNow (const MidiMessage& message); | |||
| //============================================================================== | |||
| /** This lets you supply a block of messages that will be sent out at some point | |||
| in the future. | |||
| The MidiOutput class has an internal thread that can send out timestamped | |||
| messages - this appends a set of messages to its internal buffer, ready for | |||
| sending. | |||
| This will only work if you've already started the thread with startBackgroundThread(). | |||
| A time is supplied, at which the block of messages should be sent. This time uses | |||
| the same time base as Time::getMillisecondCounter(), and must be in the future. | |||
| The samplesPerSecondForBuffer parameter indicates the number of samples per second | |||
| used by the MidiBuffer. Each event in a MidiBuffer has a sample position, and the | |||
| samplesPerSecondForBuffer value is needed to convert this sample position to a | |||
| real time. | |||
| */ | |||
| virtual void sendBlockOfMessages (const MidiBuffer& buffer, | |||
| double millisecondCounterToStartAt, | |||
| double samplesPerSecondForBuffer); | |||
| /** Gets rid of any midi messages that had been added by sendBlockOfMessages(). | |||
| */ | |||
| virtual void clearAllPendingMessages(); | |||
| /** Starts up a background thread so that the device can send blocks of data. | |||
| Call this to get the device ready, before using sendBlockOfMessages(). | |||
| */ | |||
| virtual void startBackgroundThread(); | |||
| /** Stops the background thread, and clears any pending midi events. | |||
| @see startBackgroundThread | |||
| */ | |||
| virtual void stopBackgroundThread(); | |||
| protected: | |||
| //============================================================================== | |||
| void* internal; | |||
| CriticalSection lock; | |||
| struct PendingMessage; | |||
| PendingMessage* firstMessage; | |||
| MidiOutput(); | |||
| void run() override; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutput) | |||
| }; | |||
| #endif // JUCE_MIDIOUTPUT_H_INCLUDED | |||
| @@ -0,0 +1,175 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_MIDIDATACONCATENATOR_H_INCLUDED | |||
| #define JUCE_MIDIDATACONCATENATOR_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Helper class that takes chunks of incoming midi bytes, packages them into | |||
| messages, and dispatches them to a midi callback. | |||
| */ | |||
| class MidiDataConcatenator | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| MidiDataConcatenator (const int initialBufferSize) | |||
| : pendingData ((size_t) initialBufferSize), | |||
| pendingDataTime (0), pendingBytes (0), runningStatus (0) | |||
| { | |||
| } | |||
| void reset() | |||
| { | |||
| pendingBytes = 0; | |||
| runningStatus = 0; | |||
| pendingDataTime = 0; | |||
| } | |||
| template <typename UserDataType, typename CallbackType> | |||
| void pushMidiData (const void* inputData, int numBytes, double time, | |||
| UserDataType* input, CallbackType& callback) | |||
| { | |||
| const uint8* d = static_cast <const uint8*> (inputData); | |||
| while (numBytes > 0) | |||
| { | |||
| if (pendingBytes > 0 || d[0] == 0xf0) | |||
| { | |||
| processSysex (d, numBytes, time, input, callback); | |||
| runningStatus = 0; | |||
| } | |||
| else | |||
| { | |||
| int len = 0; | |||
| uint8 data[3]; | |||
| while (numBytes > 0) | |||
| { | |||
| // If there's a realtime message embedded in the middle of | |||
| // the normal message, handle it now.. | |||
| if (*d >= 0xf8 && *d <= 0xfe) | |||
| { | |||
| const MidiMessage m (*d++, time); | |||
| callback.handleIncomingMidiMessage (input, m); | |||
| --numBytes; | |||
| } | |||
| else | |||
| { | |||
| if (len == 0 && *d < 0x80 && runningStatus >= 0x80) | |||
| data[len++] = runningStatus; | |||
| data[len++] = *d++; | |||
| --numBytes; | |||
| if (len >= MidiMessage::getMessageLengthFromFirstByte (data[0])) | |||
| break; | |||
| } | |||
| } | |||
| if (len > 0) | |||
| { | |||
| int used = 0; | |||
| const MidiMessage m (data, len, used, 0, time); | |||
| if (used <= 0) | |||
| break; // malformed message.. | |||
| jassert (used == len); | |||
| callback.handleIncomingMidiMessage (input, m); | |||
| runningStatus = data[0]; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| private: | |||
| template <typename UserDataType, typename CallbackType> | |||
| void processSysex (const uint8*& d, int& numBytes, double time, | |||
| UserDataType* input, CallbackType& callback) | |||
| { | |||
| if (*d == 0xf0) | |||
| { | |||
| pendingBytes = 0; | |||
| pendingDataTime = time; | |||
| } | |||
| pendingData.ensureSize ((size_t) (pendingBytes + numBytes), false); | |||
| uint8* totalMessage = static_cast<uint8*> (pendingData.getData()); | |||
| uint8* dest = totalMessage + pendingBytes; | |||
| do | |||
| { | |||
| if (pendingBytes > 0 && *d >= 0x80) | |||
| { | |||
| if (*d >= 0xfa || *d == 0xf8) | |||
| { | |||
| callback.handleIncomingMidiMessage (input, MidiMessage (*d, time)); | |||
| ++d; | |||
| --numBytes; | |||
| } | |||
| else | |||
| { | |||
| if (*d == 0xf7) | |||
| { | |||
| *dest++ = *d++; | |||
| pendingBytes++; | |||
| --numBytes; | |||
| } | |||
| break; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| *dest++ = *d++; | |||
| pendingBytes++; | |||
| --numBytes; | |||
| } | |||
| } | |||
| while (numBytes > 0); | |||
| if (pendingBytes > 0) | |||
| { | |||
| if (totalMessage [pendingBytes - 1] == 0xf7) | |||
| { | |||
| callback.handleIncomingMidiMessage (input, MidiMessage (totalMessage, pendingBytes, pendingDataTime)); | |||
| pendingBytes = 0; | |||
| } | |||
| else | |||
| { | |||
| callback.handlePartialSysexMessage (input, totalMessage, pendingBytes, pendingDataTime); | |||
| } | |||
| } | |||
| } | |||
| MemoryBlock pendingData; | |||
| double pendingDataTime; | |||
| int pendingBytes; | |||
| uint8 runningStatus; | |||
| JUCE_DECLARE_NON_COPYABLE (MidiDataConcatenator) | |||
| }; | |||
| #endif // JUCE_MIDIDATACONCATENATOR_H_INCLUDED | |||
| @@ -0,0 +1,443 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \ | |||
| STATICMETHOD (getNativeOutputSampleRate, "getNativeOutputSampleRate", "(I)I") \ | |||
| METHOD (constructor, "<init>", "(IIIIII)V") \ | |||
| METHOD (getState, "getState", "()I") \ | |||
| METHOD (play, "play", "()V") \ | |||
| METHOD (stop, "stop", "()V") \ | |||
| METHOD (release, "release", "()V") \ | |||
| METHOD (flush, "flush", "()V") \ | |||
| METHOD (write, "write", "([SII)I") \ | |||
| DECLARE_JNI_CLASS (AudioTrack, "android/media/AudioTrack"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| //============================================================================== | |||
| #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ | |||
| STATICMETHOD (getMinBufferSize, "getMinBufferSize", "(III)I") \ | |||
| METHOD (constructor, "<init>", "(IIIII)V") \ | |||
| METHOD (getState, "getState", "()I") \ | |||
| METHOD (startRecording, "startRecording", "()V") \ | |||
| METHOD (stop, "stop", "()V") \ | |||
| METHOD (read, "read", "([SII)I") \ | |||
| METHOD (release, "release", "()V") \ | |||
| DECLARE_JNI_CLASS (AudioRecord, "android/media/AudioRecord"); | |||
| #undef JNI_CLASS_MEMBERS | |||
| //============================================================================== | |||
| enum | |||
| { | |||
| CHANNEL_OUT_STEREO = 12, | |||
| CHANNEL_IN_STEREO = 12, | |||
| CHANNEL_IN_MONO = 16, | |||
| ENCODING_PCM_16BIT = 2, | |||
| STREAM_MUSIC = 3, | |||
| MODE_STREAM = 1, | |||
| STATE_UNINITIALIZED = 0 | |||
| }; | |||
| const char* const javaAudioTypeName = "Android Audio"; | |||
| //============================================================================== | |||
| class AndroidAudioIODevice : public AudioIODevice, | |||
| public Thread | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| AndroidAudioIODevice (const String& deviceName) | |||
| : AudioIODevice (deviceName, javaAudioTypeName), | |||
| Thread ("audio"), | |||
| minBufferSizeOut (0), minBufferSizeIn (0), callback (0), sampleRate (0), | |||
| numClientInputChannels (0), numDeviceInputChannels (0), numDeviceInputChannelsAvailable (2), | |||
| numClientOutputChannels (0), numDeviceOutputChannels (0), | |||
| actualBufferSize (0), isRunning (false), | |||
| inputChannelBuffer (1, 1), | |||
| outputChannelBuffer (1, 1) | |||
| { | |||
| JNIEnv* env = getEnv(); | |||
| sampleRate = env->CallStaticIntMethod (AudioTrack, AudioTrack.getNativeOutputSampleRate, MODE_STREAM); | |||
| minBufferSizeOut = (int) env->CallStaticIntMethod (AudioTrack, AudioTrack.getMinBufferSize, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT); | |||
| minBufferSizeIn = (int) env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_STEREO, ENCODING_PCM_16BIT); | |||
| if (minBufferSizeIn <= 0) | |||
| { | |||
| minBufferSizeIn = env->CallStaticIntMethod (AudioRecord, AudioRecord.getMinBufferSize, sampleRate, CHANNEL_IN_MONO, ENCODING_PCM_16BIT); | |||
| if (minBufferSizeIn > 0) | |||
| numDeviceInputChannelsAvailable = 1; | |||
| else | |||
| numDeviceInputChannelsAvailable = 0; | |||
| } | |||
| DBG ("Audio device - min buffers: " << minBufferSizeOut << ", " << minBufferSizeIn << "; " | |||
| << sampleRate << " Hz; input chans: " << numDeviceInputChannelsAvailable); | |||
| } | |||
| ~AndroidAudioIODevice() | |||
| { | |||
| close(); | |||
| } | |||
| StringArray getOutputChannelNames() | |||
| { | |||
| StringArray s; | |||
| s.add ("Left"); | |||
| s.add ("Right"); | |||
| return s; | |||
| } | |||
| StringArray getInputChannelNames() | |||
| { | |||
| StringArray s; | |||
| if (numDeviceInputChannelsAvailable == 2) | |||
| { | |||
| s.add ("Left"); | |||
| s.add ("Right"); | |||
| } | |||
| else if (numDeviceInputChannelsAvailable == 1) | |||
| { | |||
| s.add ("Audio Input"); | |||
| } | |||
| return s; | |||
| } | |||
| int getNumSampleRates() { return 1;} | |||
| double getSampleRate (int index) { return sampleRate; } | |||
| int getDefaultBufferSize() { return 2048; } | |||
| int getNumBufferSizesAvailable() { return 50; } | |||
| int getBufferSizeSamples (int index) | |||
| { | |||
| int n = 16; | |||
| for (int i = 0; i < index; ++i) | |||
| n += n < 64 ? 16 | |||
| : (n < 512 ? 32 | |||
| : (n < 1024 ? 64 | |||
| : (n < 2048 ? 128 : 256))); | |||
| return n; | |||
| } | |||
| String open (const BigInteger& inputChannels, | |||
| const BigInteger& outputChannels, | |||
| double requestedSampleRate, | |||
| int bufferSize) | |||
| { | |||
| close(); | |||
| if (sampleRate != (int) requestedSampleRate) | |||
| return "Sample rate not allowed"; | |||
| lastError = String::empty; | |||
| int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; | |||
| numDeviceInputChannels = 0; | |||
| numDeviceOutputChannels = 0; | |||
| activeOutputChans = outputChannels; | |||
| activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); | |||
| numClientOutputChannels = activeOutputChans.countNumberOfSetBits(); | |||
| activeInputChans = inputChannels; | |||
| activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); | |||
| numClientInputChannels = activeInputChans.countNumberOfSetBits(); | |||
| actualBufferSize = preferredBufferSize; | |||
| inputChannelBuffer.setSize (2, actualBufferSize); | |||
| inputChannelBuffer.clear(); | |||
| outputChannelBuffer.setSize (2, actualBufferSize); | |||
| outputChannelBuffer.clear(); | |||
| JNIEnv* env = getEnv(); | |||
| if (numClientOutputChannels > 0) | |||
| { | |||
| numDeviceOutputChannels = 2; | |||
| outputDevice = GlobalRef (env->NewObject (AudioTrack, AudioTrack.constructor, | |||
| STREAM_MUSIC, sampleRate, CHANNEL_OUT_STEREO, ENCODING_PCM_16BIT, | |||
| (jint) (minBufferSizeOut * numDeviceOutputChannels * sizeof (int16)), MODE_STREAM)); | |||
| if (env->CallIntMethod (outputDevice, AudioTrack.getState) != STATE_UNINITIALIZED) | |||
| isRunning = true; | |||
| else | |||
| outputDevice.clear(); // failed to open the device | |||
| } | |||
| if (numClientInputChannels > 0 && numDeviceInputChannelsAvailable > 0) | |||
| { | |||
| numDeviceInputChannels = jmin (numClientInputChannels, numDeviceInputChannelsAvailable); | |||
| inputDevice = GlobalRef (env->NewObject (AudioRecord, AudioRecord.constructor, | |||
| 0 /* (default audio source) */, sampleRate, | |||
| numDeviceInputChannelsAvailable > 1 ? CHANNEL_IN_STEREO : CHANNEL_IN_MONO, | |||
| ENCODING_PCM_16BIT, | |||
| (jint) (minBufferSizeIn * numDeviceInputChannels * sizeof (int16)))); | |||
| if (env->CallIntMethod (inputDevice, AudioRecord.getState) != STATE_UNINITIALIZED) | |||
| isRunning = true; | |||
| else | |||
| inputDevice.clear(); // failed to open the device | |||
| } | |||
| if (isRunning) | |||
| { | |||
| if (outputDevice != nullptr) | |||
| env->CallVoidMethod (outputDevice, AudioTrack.play); | |||
| if (inputDevice != nullptr) | |||
| env->CallVoidMethod (inputDevice, AudioRecord.startRecording); | |||
| startThread (8); | |||
| } | |||
| else | |||
| { | |||
| closeDevices(); | |||
| } | |||
| return lastError; | |||
| } | |||
| void close() | |||
| { | |||
| if (isRunning) | |||
| { | |||
| stopThread (2000); | |||
| isRunning = false; | |||
| closeDevices(); | |||
| } | |||
| } | |||
| int getOutputLatencyInSamples() { return (minBufferSizeOut * 3) / 4; } | |||
| int getInputLatencyInSamples() { return (minBufferSizeIn * 3) / 4; } | |||
| bool isOpen() { return isRunning; } | |||
| int getCurrentBufferSizeSamples() { return actualBufferSize; } | |||
| int getCurrentBitDepth() { return 16; } | |||
| double getCurrentSampleRate() { return sampleRate; } | |||
| BigInteger getActiveOutputChannels() const { return activeOutputChans; } | |||
| BigInteger getActiveInputChannels() const { return activeInputChans; } | |||
| String getLastError() { return lastError; } | |||
| bool isPlaying() { return isRunning && callback != 0; } | |||
| void start (AudioIODeviceCallback* newCallback) | |||
| { | |||
| if (isRunning && callback != newCallback) | |||
| { | |||
| if (newCallback != nullptr) | |||
| newCallback->audioDeviceAboutToStart (this); | |||
| const ScopedLock sl (callbackLock); | |||
| callback = newCallback; | |||
| } | |||
| } | |||
| void stop() | |||
| { | |||
| if (isRunning) | |||
| { | |||
| AudioIODeviceCallback* lastCallback; | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| lastCallback = callback; | |||
| callback = nullptr; | |||
| } | |||
| if (lastCallback != nullptr) | |||
| lastCallback->audioDeviceStopped(); | |||
| } | |||
| } | |||
| void run() override | |||
| { | |||
| JNIEnv* env = getEnv(); | |||
| jshortArray audioBuffer = env->NewShortArray (actualBufferSize * jmax (numDeviceOutputChannels, numDeviceInputChannels)); | |||
| while (! threadShouldExit()) | |||
| { | |||
| if (inputDevice != nullptr) | |||
| { | |||
| jint numRead = env->CallIntMethod (inputDevice, AudioRecord.read, audioBuffer, 0, actualBufferSize * numDeviceInputChannels); | |||
| if (numRead < actualBufferSize * numDeviceInputChannels) | |||
| { | |||
| DBG ("Audio read under-run! " << numRead); | |||
| } | |||
| jshort* const src = env->GetShortArrayElements (audioBuffer, 0); | |||
| for (int chan = 0; chan < inputChannelBuffer.getNumChannels(); ++chan) | |||
| { | |||
| AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> d (inputChannelBuffer.getSampleData (chan)); | |||
| if (chan < numDeviceInputChannels) | |||
| { | |||
| AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::Const> s (src + chan, numDeviceInputChannels); | |||
| d.convertSamples (s, actualBufferSize); | |||
| } | |||
| else | |||
| { | |||
| d.clearSamples (actualBufferSize); | |||
| } | |||
| } | |||
| env->ReleaseShortArrayElements (audioBuffer, src, 0); | |||
| } | |||
| if (threadShouldExit()) | |||
| break; | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| { | |||
| callback->audioDeviceIOCallback ((const float**) inputChannelBuffer.getArrayOfChannels(), numClientInputChannels, | |||
| outputChannelBuffer.getArrayOfChannels(), numClientOutputChannels, | |||
| actualBufferSize); | |||
| } | |||
| else | |||
| { | |||
| outputChannelBuffer.clear(); | |||
| } | |||
| } | |||
| if (outputDevice != nullptr) | |||
| { | |||
| if (threadShouldExit()) | |||
| break; | |||
| jshort* const dest = env->GetShortArrayElements (audioBuffer, 0); | |||
| for (int chan = 0; chan < numDeviceOutputChannels; ++chan) | |||
| { | |||
| AudioData::Pointer <AudioData::Int16, AudioData::NativeEndian, AudioData::Interleaved, AudioData::NonConst> d (dest + chan, numDeviceOutputChannels); | |||
| const float* const sourceChanData = outputChannelBuffer.getSampleData (jmin (chan, outputChannelBuffer.getNumChannels() - 1)); | |||
| AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> s (sourceChanData); | |||
| d.convertSamples (s, actualBufferSize); | |||
| } | |||
| env->ReleaseShortArrayElements (audioBuffer, dest, 0); | |||
| jint numWritten = env->CallIntMethod (outputDevice, AudioTrack.write, audioBuffer, 0, actualBufferSize * numDeviceOutputChannels); | |||
| if (numWritten < actualBufferSize * numDeviceOutputChannels) | |||
| { | |||
| DBG ("Audio write underrun! " << numWritten); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| int minBufferSizeOut, minBufferSizeIn; | |||
| private: | |||
| //================================================================================================== | |||
| CriticalSection callbackLock; | |||
| AudioIODeviceCallback* callback; | |||
| jint sampleRate; | |||
| int numClientInputChannels, numDeviceInputChannels, numDeviceInputChannelsAvailable; | |||
| int numClientOutputChannels, numDeviceOutputChannels; | |||
| int actualBufferSize; | |||
| bool isRunning; | |||
| String lastError; | |||
| BigInteger activeOutputChans, activeInputChans; | |||
| GlobalRef outputDevice, inputDevice; | |||
| AudioSampleBuffer inputChannelBuffer, outputChannelBuffer; | |||
| void closeDevices() | |||
| { | |||
| if (outputDevice != nullptr) | |||
| { | |||
| outputDevice.callVoidMethod (AudioTrack.stop); | |||
| outputDevice.callVoidMethod (AudioTrack.release); | |||
| outputDevice.clear(); | |||
| } | |||
| if (inputDevice != nullptr) | |||
| { | |||
| inputDevice.callVoidMethod (AudioRecord.stop); | |||
| inputDevice.callVoidMethod (AudioRecord.release); | |||
| inputDevice.clear(); | |||
| } | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE (AndroidAudioIODevice) | |||
| }; | |||
| //============================================================================== | |||
| class AndroidAudioIODeviceType : public AudioIODeviceType | |||
| { | |||
| public: | |||
| AndroidAudioIODeviceType() : AudioIODeviceType (javaAudioTypeName) {} | |||
| //============================================================================== | |||
| void scanForDevices() {} | |||
| StringArray getDeviceNames (bool wantInputNames) const { return StringArray (javaAudioTypeName); } | |||
| int getDefaultDeviceIndex (bool forInput) const { return 0; } | |||
| int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; } | |||
| bool hasSeparateInputsAndOutputs() const { return false; } | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| ScopedPointer<AndroidAudioIODevice> dev; | |||
| if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) | |||
| { | |||
| dev = new AndroidAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName | |||
| : inputDeviceName); | |||
| if (dev->getCurrentSampleRate() <= 0 || dev->getDefaultBufferSize() <= 0) | |||
| dev = nullptr; | |||
| } | |||
| return dev.release(); | |||
| } | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AndroidAudioIODeviceType) | |||
| }; | |||
| //============================================================================== | |||
| extern bool isOpenSLAvailable(); | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_Android() | |||
| { | |||
| #if JUCE_USE_ANDROID_OPENSLES | |||
| if (isOpenSLAvailable()) | |||
| return nullptr; | |||
| #endif | |||
| return new AndroidAudioIODeviceType(); | |||
| } | |||
| @@ -0,0 +1,84 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| StringArray MidiOutput::getDevices() | |||
| { | |||
| StringArray devices; | |||
| return devices; | |||
| } | |||
| int MidiOutput::getDefaultDeviceIndex() | |||
| { | |||
| return 0; | |||
| } | |||
| MidiOutput* MidiOutput::openDevice (int index) | |||
| { | |||
| return nullptr; | |||
| } | |||
| MidiOutput::~MidiOutput() | |||
| { | |||
| } | |||
| void MidiOutput::sendMessageNow (const MidiMessage&) | |||
| { | |||
| } | |||
| //============================================================================== | |||
| MidiInput::MidiInput (const String& name_) | |||
| : name (name_), | |||
| internal (0) | |||
| { | |||
| } | |||
| MidiInput::~MidiInput() | |||
| { | |||
| } | |||
| void MidiInput::start() | |||
| { | |||
| } | |||
| void MidiInput::stop() | |||
| { | |||
| } | |||
| int MidiInput::getDefaultDeviceIndex() | |||
| { | |||
| return 0; | |||
| } | |||
| StringArray MidiInput::getDevices() | |||
| { | |||
| StringArray devs; | |||
| return devs; | |||
| } | |||
| MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) | |||
| { | |||
| return nullptr; | |||
| } | |||
| @@ -0,0 +1,622 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| const char* const openSLTypeName = "Android OpenSL"; | |||
| bool isOpenSLAvailable() | |||
| { | |||
| DynamicLibrary library; | |||
| return library.open ("libOpenSLES.so"); | |||
| } | |||
| const unsigned short openSLRates[] = { 8000, 16000, 32000, 44100, 48000 }; | |||
| const unsigned short openSLBufferSizes[] = { 256, 512, 768, 1024, 1280, 1600 }; // must all be multiples of the block size | |||
| //============================================================================== | |||
| class OpenSLAudioIODevice : public AudioIODevice, | |||
| public Thread | |||
| { | |||
| public: | |||
| OpenSLAudioIODevice (const String& deviceName) | |||
| : AudioIODevice (deviceName, openSLTypeName), | |||
| Thread ("OpenSL"), | |||
| callback (nullptr), sampleRate (0), deviceOpen (false), | |||
| inputBuffer (2, 2), outputBuffer (2, 2) | |||
| { | |||
| // OpenSL has piss-poor support for determining latency, so the only way I can find to | |||
| // get a number for this is by asking the AudioTrack/AudioRecord classes.. | |||
| AndroidAudioIODevice javaDevice (String::empty); | |||
| // this is a total guess about how to calculate the latency, but seems to vaguely agree | |||
| // with the devices I've tested.. YMMV | |||
| inputLatency = ((javaDevice.minBufferSizeIn * 2) / 3); | |||
| outputLatency = ((javaDevice.minBufferSizeOut * 2) / 3); | |||
| const int longestLatency = jmax (inputLatency, outputLatency); | |||
| const int totalLatency = inputLatency + outputLatency; | |||
| inputLatency = ((longestLatency * inputLatency) / totalLatency) & ~15; | |||
| outputLatency = ((longestLatency * outputLatency) / totalLatency) & ~15; | |||
| } | |||
| ~OpenSLAudioIODevice() | |||
| { | |||
| close(); | |||
| } | |||
| bool openedOk() const { return engine.outputMixObject != nullptr; } | |||
| StringArray getOutputChannelNames() | |||
| { | |||
| StringArray s; | |||
| s.add ("Left"); | |||
| s.add ("Right"); | |||
| return s; | |||
| } | |||
| StringArray getInputChannelNames() | |||
| { | |||
| StringArray s; | |||
| s.add ("Audio Input"); | |||
| return s; | |||
| } | |||
| int getNumSampleRates() { return numElementsInArray (openSLRates); } | |||
| double getSampleRate (int index) | |||
| { | |||
| jassert (index >= 0 && index < getNumSampleRates()); | |||
| return (int) openSLRates [index]; | |||
| } | |||
| int getDefaultBufferSize() { return 1024; } | |||
| int getNumBufferSizesAvailable() { return numElementsInArray (openSLBufferSizes); } | |||
| int getBufferSizeSamples (int index) | |||
| { | |||
| jassert (index >= 0 && index < getNumBufferSizesAvailable()); | |||
| return (int) openSLBufferSizes [index]; | |||
| } | |||
| String open (const BigInteger& inputChannels, | |||
| const BigInteger& outputChannels, | |||
| double requestedSampleRate, | |||
| int bufferSize) | |||
| { | |||
| close(); | |||
| lastError = String::empty; | |||
| sampleRate = (int) requestedSampleRate; | |||
| int preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; | |||
| activeOutputChans = outputChannels; | |||
| activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); | |||
| numOutputChannels = activeOutputChans.countNumberOfSetBits(); | |||
| activeInputChans = inputChannels; | |||
| activeInputChans.setRange (1, activeInputChans.getHighestBit(), false); | |||
| numInputChannels = activeInputChans.countNumberOfSetBits(); | |||
| actualBufferSize = preferredBufferSize; | |||
| inputBuffer.setSize (jmax (1, numInputChannels), actualBufferSize); | |||
| outputBuffer.setSize (jmax (1, numOutputChannels), actualBufferSize); | |||
| outputBuffer.clear(); | |||
| recorder = engine.createRecorder (numInputChannels, sampleRate); | |||
| player = engine.createPlayer (numOutputChannels, sampleRate); | |||
| startThread (8); | |||
| deviceOpen = true; | |||
| return lastError; | |||
| } | |||
| void close() | |||
| { | |||
| stop(); | |||
| stopThread (6000); | |||
| deviceOpen = false; | |||
| recorder = nullptr; | |||
| player = nullptr; | |||
| } | |||
| int getOutputLatencyInSamples() { return outputLatency; } | |||
| int getInputLatencyInSamples() { return inputLatency; } | |||
| bool isOpen() { return deviceOpen; } | |||
| int getCurrentBufferSizeSamples() { return actualBufferSize; } | |||
| int getCurrentBitDepth() { return 16; } | |||
| double getCurrentSampleRate() { return sampleRate; } | |||
| BigInteger getActiveOutputChannels() const { return activeOutputChans; } | |||
| BigInteger getActiveInputChannels() const { return activeInputChans; } | |||
| String getLastError() { return lastError; } | |||
| bool isPlaying() { return callback != nullptr; } | |||
| void start (AudioIODeviceCallback* newCallback) | |||
| { | |||
| stop(); | |||
| if (deviceOpen && callback != newCallback) | |||
| { | |||
| if (newCallback != nullptr) | |||
| newCallback->audioDeviceAboutToStart (this); | |||
| setCallback (newCallback); | |||
| } | |||
| } | |||
| void stop() | |||
| { | |||
| if (AudioIODeviceCallback* const oldCallback = setCallback (nullptr)) | |||
| oldCallback->audioDeviceStopped(); | |||
| } | |||
| void run() override | |||
| { | |||
| if (recorder != nullptr) recorder->start(); | |||
| if (player != nullptr) player->start(); | |||
| while (! threadShouldExit()) | |||
| { | |||
| if (player != nullptr) player->writeBuffer (outputBuffer, *this); | |||
| if (recorder != nullptr) recorder->readNextBlock (inputBuffer, *this); | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| { | |||
| callback->audioDeviceIOCallback (numInputChannels > 0 ? (const float**) inputBuffer.getArrayOfChannels() : nullptr, | |||
| numInputChannels, | |||
| numOutputChannels > 0 ? outputBuffer.getArrayOfChannels() : nullptr, | |||
| numOutputChannels, | |||
| actualBufferSize); | |||
| } | |||
| else | |||
| { | |||
| outputBuffer.clear(); | |||
| } | |||
| } | |||
| } | |||
| private: | |||
| //================================================================================================== | |||
| CriticalSection callbackLock; | |||
| AudioIODeviceCallback* callback; | |||
| int actualBufferSize, sampleRate; | |||
| int inputLatency, outputLatency; | |||
| bool deviceOpen; | |||
| String lastError; | |||
| BigInteger activeOutputChans, activeInputChans; | |||
| int numInputChannels, numOutputChannels; | |||
| AudioSampleBuffer inputBuffer, outputBuffer; | |||
| struct Player; | |||
| struct Recorder; | |||
| AudioIODeviceCallback* setCallback (AudioIODeviceCallback* const newCallback) | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| AudioIODeviceCallback* const oldCallback = callback; | |||
| callback = newCallback; | |||
| return oldCallback; | |||
| } | |||
| //================================================================================================== | |||
| struct Engine | |||
| { | |||
| Engine() | |||
| : engineObject (nullptr), engineInterface (nullptr), outputMixObject (nullptr) | |||
| { | |||
| if (library.open ("libOpenSLES.so")) | |||
| { | |||
| typedef SLresult (*CreateEngineFunc) (SLObjectItf*, SLuint32, const SLEngineOption*, SLuint32, const SLInterfaceID*, const SLboolean*); | |||
| if (CreateEngineFunc createEngine = (CreateEngineFunc) library.getFunction ("slCreateEngine")) | |||
| { | |||
| check (createEngine (&engineObject, 0, nullptr, 0, nullptr, nullptr)); | |||
| SLInterfaceID* SL_IID_ENGINE = (SLInterfaceID*) library.getFunction ("SL_IID_ENGINE"); | |||
| SL_IID_ANDROIDSIMPLEBUFFERQUEUE = (SLInterfaceID*) library.getFunction ("SL_IID_ANDROIDSIMPLEBUFFERQUEUE"); | |||
| SL_IID_PLAY = (SLInterfaceID*) library.getFunction ("SL_IID_PLAY"); | |||
| SL_IID_RECORD = (SLInterfaceID*) library.getFunction ("SL_IID_RECORD"); | |||
| check ((*engineObject)->Realize (engineObject, SL_BOOLEAN_FALSE)); | |||
| check ((*engineObject)->GetInterface (engineObject, *SL_IID_ENGINE, &engineInterface)); | |||
| check ((*engineInterface)->CreateOutputMix (engineInterface, &outputMixObject, 0, nullptr, nullptr)); | |||
| check ((*outputMixObject)->Realize (outputMixObject, SL_BOOLEAN_FALSE)); | |||
| } | |||
| } | |||
| } | |||
| ~Engine() | |||
| { | |||
| if (outputMixObject != nullptr) (*outputMixObject)->Destroy (outputMixObject); | |||
| if (engineObject != nullptr) (*engineObject)->Destroy (engineObject); | |||
| } | |||
| Player* createPlayer (const int numChannels, const int sampleRate) | |||
| { | |||
| if (numChannels <= 0) | |||
| return nullptr; | |||
| ScopedPointer<Player> player (new Player (numChannels, sampleRate, *this)); | |||
| return player->openedOk() ? player.release() : nullptr; | |||
| } | |||
| Recorder* createRecorder (const int numChannels, const int sampleRate) | |||
| { | |||
| if (numChannels <= 0) | |||
| return nullptr; | |||
| ScopedPointer<Recorder> recorder (new Recorder (numChannels, sampleRate, *this)); | |||
| return recorder->openedOk() ? recorder.release() : nullptr; | |||
| } | |||
| SLObjectItf engineObject; | |||
| SLEngineItf engineInterface; | |||
| SLObjectItf outputMixObject; | |||
| SLInterfaceID* SL_IID_ANDROIDSIMPLEBUFFERQUEUE; | |||
| SLInterfaceID* SL_IID_PLAY; | |||
| SLInterfaceID* SL_IID_RECORD; | |||
| private: | |||
| DynamicLibrary library; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Engine) | |||
| }; | |||
| //================================================================================================== | |||
| struct BufferList | |||
| { | |||
| BufferList (const int numChannels_) | |||
| : numChannels (numChannels_), bufferSpace (numChannels_ * numSamples * numBuffers), nextBlock (0) | |||
| { | |||
| } | |||
| int16* waitForFreeBuffer (Thread& threadToCheck) | |||
| { | |||
| while (numBlocksOut.get() == numBuffers) | |||
| { | |||
| dataArrived.wait (1); | |||
| if (threadToCheck.threadShouldExit()) | |||
| return nullptr; | |||
| } | |||
| return getNextBuffer(); | |||
| } | |||
| int16* getNextBuffer() | |||
| { | |||
| if (++nextBlock == numBuffers) | |||
| nextBlock = 0; | |||
| return bufferSpace + nextBlock * numChannels * numSamples; | |||
| } | |||
| void bufferReturned() { --numBlocksOut; dataArrived.signal(); } | |||
| void bufferSent() { ++numBlocksOut; dataArrived.signal(); } | |||
| int getBufferSizeBytes() const { return numChannels * numSamples * sizeof (int16); } | |||
| const int numChannels; | |||
| enum { numSamples = 256, numBuffers = 16 }; | |||
| private: | |||
| HeapBlock<int16> bufferSpace; | |||
| int nextBlock; | |||
| Atomic<int> numBlocksOut; | |||
| WaitableEvent dataArrived; | |||
| }; | |||
| //================================================================================================== | |||
| struct Player | |||
| { | |||
| Player (int numChannels, int sampleRate, Engine& engine) | |||
| : playerObject (nullptr), playerPlay (nullptr), playerBufferQueue (nullptr), | |||
| bufferList (numChannels) | |||
| { | |||
| jassert (numChannels == 2); | |||
| SLDataFormat_PCM pcmFormat = | |||
| { | |||
| SL_DATAFORMAT_PCM, | |||
| numChannels, | |||
| sampleRate * 1000, // (sample rate units are millihertz) | |||
| SL_PCMSAMPLEFORMAT_FIXED_16, | |||
| SL_PCMSAMPLEFORMAT_FIXED_16, | |||
| SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, | |||
| SL_BYTEORDER_LITTLEENDIAN | |||
| }; | |||
| SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers }; | |||
| SLDataSource audioSrc = { &bufferQueue, &pcmFormat }; | |||
| SLDataLocator_OutputMix outputMix = { SL_DATALOCATOR_OUTPUTMIX, engine.outputMixObject }; | |||
| SLDataSink audioSink = { &outputMix, nullptr }; | |||
| // (SL_IID_BUFFERQUEUE is not guaranteed to remain future-proof, so use SL_IID_ANDROIDSIMPLEBUFFERQUEUE) | |||
| const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; | |||
| const SLboolean flags[] = { SL_BOOLEAN_TRUE }; | |||
| check ((*engine.engineInterface)->CreateAudioPlayer (engine.engineInterface, &playerObject, &audioSrc, &audioSink, | |||
| 1, interfaceIDs, flags)); | |||
| check ((*playerObject)->Realize (playerObject, SL_BOOLEAN_FALSE)); | |||
| check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_PLAY, &playerPlay)); | |||
| check ((*playerObject)->GetInterface (playerObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &playerBufferQueue)); | |||
| check ((*playerBufferQueue)->RegisterCallback (playerBufferQueue, staticCallback, this)); | |||
| } | |||
| ~Player() | |||
| { | |||
| if (playerPlay != nullptr) | |||
| check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_STOPPED)); | |||
| if (playerBufferQueue != nullptr) | |||
| check ((*playerBufferQueue)->Clear (playerBufferQueue)); | |||
| if (playerObject != nullptr) | |||
| (*playerObject)->Destroy (playerObject); | |||
| } | |||
| bool openedOk() const noexcept { return playerBufferQueue != nullptr; } | |||
| void start() | |||
| { | |||
| jassert (openedOk()); | |||
| check ((*playerPlay)->SetPlayState (playerPlay, SL_PLAYSTATE_PLAYING)); | |||
| } | |||
| void writeBuffer (const AudioSampleBuffer& buffer, Thread& thread) | |||
| { | |||
| jassert (buffer.getNumChannels() == bufferList.numChannels); | |||
| jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers); | |||
| int offset = 0; | |||
| int numSamples = buffer.getNumSamples(); | |||
| while (numSamples > 0) | |||
| { | |||
| int16* const destBuffer = bufferList.waitForFreeBuffer (thread); | |||
| if (destBuffer == nullptr) | |||
| break; | |||
| for (int i = 0; i < bufferList.numChannels; ++i) | |||
| { | |||
| typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::NonConst> DstSampleType; | |||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::Const> SrcSampleType; | |||
| DstSampleType dstData (destBuffer + i, bufferList.numChannels); | |||
| SrcSampleType srcData (buffer.getSampleData (i, offset)); | |||
| dstData.convertSamples (srcData, bufferList.numSamples); | |||
| } | |||
| check ((*playerBufferQueue)->Enqueue (playerBufferQueue, destBuffer, bufferList.getBufferSizeBytes())); | |||
| bufferList.bufferSent(); | |||
| numSamples -= bufferList.numSamples; | |||
| offset += bufferList.numSamples; | |||
| } | |||
| } | |||
| private: | |||
| SLObjectItf playerObject; | |||
| SLPlayItf playerPlay; | |||
| SLAndroidSimpleBufferQueueItf playerBufferQueue; | |||
| BufferList bufferList; | |||
| static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) | |||
| { | |||
| jassert (queue == static_cast <Player*> (context)->playerBufferQueue); (void) queue; | |||
| static_cast <Player*> (context)->bufferList.bufferReturned(); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Player) | |||
| }; | |||
| //================================================================================================== | |||
| struct Recorder | |||
| { | |||
| Recorder (int numChannels, int sampleRate, Engine& engine) | |||
| : recorderObject (nullptr), recorderRecord (nullptr), recorderBufferQueue (nullptr), | |||
| bufferList (numChannels) | |||
| { | |||
| jassert (numChannels == 1); // STEREO doesn't always work!! | |||
| SLDataFormat_PCM pcmFormat = | |||
| { | |||
| SL_DATAFORMAT_PCM, | |||
| numChannels, | |||
| sampleRate * 1000, // (sample rate units are millihertz) | |||
| SL_PCMSAMPLEFORMAT_FIXED_16, | |||
| SL_PCMSAMPLEFORMAT_FIXED_16, | |||
| (numChannels == 1) ? SL_SPEAKER_FRONT_CENTER : (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT), | |||
| SL_BYTEORDER_LITTLEENDIAN | |||
| }; | |||
| SLDataLocator_IODevice ioDevice = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, nullptr }; | |||
| SLDataSource audioSrc = { &ioDevice, nullptr }; | |||
| SLDataLocator_AndroidSimpleBufferQueue bufferQueue = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, bufferList.numBuffers }; | |||
| SLDataSink audioSink = { &bufferQueue, &pcmFormat }; | |||
| const SLInterfaceID interfaceIDs[] = { *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE }; | |||
| const SLboolean flags[] = { SL_BOOLEAN_TRUE }; | |||
| if (check ((*engine.engineInterface)->CreateAudioRecorder (engine.engineInterface, &recorderObject, &audioSrc, | |||
| &audioSink, 1, interfaceIDs, flags))) | |||
| { | |||
| if (check ((*recorderObject)->Realize (recorderObject, SL_BOOLEAN_FALSE))) | |||
| { | |||
| check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_RECORD, &recorderRecord)); | |||
| check ((*recorderObject)->GetInterface (recorderObject, *engine.SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue)); | |||
| check ((*recorderBufferQueue)->RegisterCallback (recorderBufferQueue, staticCallback, this)); | |||
| check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); | |||
| for (int i = bufferList.numBuffers; --i >= 0;) | |||
| { | |||
| int16* const buffer = bufferList.getNextBuffer(); | |||
| jassert (buffer != nullptr); | |||
| enqueueBuffer (buffer); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| ~Recorder() | |||
| { | |||
| if (recorderRecord != nullptr) | |||
| check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_STOPPED)); | |||
| if (recorderBufferQueue != nullptr) | |||
| check ((*recorderBufferQueue)->Clear (recorderBufferQueue)); | |||
| if (recorderObject != nullptr) | |||
| (*recorderObject)->Destroy (recorderObject); | |||
| } | |||
| bool openedOk() const noexcept { return recorderBufferQueue != nullptr; } | |||
| void start() | |||
| { | |||
| jassert (openedOk()); | |||
| check ((*recorderRecord)->SetRecordState (recorderRecord, SL_RECORDSTATE_RECORDING)); | |||
| } | |||
| void readNextBlock (AudioSampleBuffer& buffer, Thread& thread) | |||
| { | |||
| jassert (buffer.getNumChannels() == bufferList.numChannels); | |||
| jassert (buffer.getNumSamples() < bufferList.numSamples * bufferList.numBuffers); | |||
| jassert ((buffer.getNumSamples() % bufferList.numSamples) == 0); | |||
| int offset = 0; | |||
| int numSamples = buffer.getNumSamples(); | |||
| while (numSamples > 0) | |||
| { | |||
| int16* const srcBuffer = bufferList.waitForFreeBuffer (thread); | |||
| if (srcBuffer == nullptr) | |||
| break; | |||
| for (int i = 0; i < bufferList.numChannels; ++i) | |||
| { | |||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, AudioData::NonInterleaved, AudioData::NonConst> DstSampleType; | |||
| typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, AudioData::Interleaved, AudioData::Const> SrcSampleType; | |||
| DstSampleType dstData (buffer.getSampleData (i, offset)); | |||
| SrcSampleType srcData (srcBuffer + i, bufferList.numChannels); | |||
| dstData.convertSamples (srcData, bufferList.numSamples); | |||
| } | |||
| enqueueBuffer (srcBuffer); | |||
| numSamples -= bufferList.numSamples; | |||
| offset += bufferList.numSamples; | |||
| } | |||
| } | |||
| private: | |||
| SLObjectItf recorderObject; | |||
| SLRecordItf recorderRecord; | |||
| SLAndroidSimpleBufferQueueItf recorderBufferQueue; | |||
| BufferList bufferList; | |||
| void enqueueBuffer (int16* buffer) | |||
| { | |||
| check ((*recorderBufferQueue)->Enqueue (recorderBufferQueue, buffer, bufferList.getBufferSizeBytes())); | |||
| bufferList.bufferSent(); | |||
| } | |||
| static void staticCallback (SLAndroidSimpleBufferQueueItf queue, void* context) | |||
| { | |||
| jassert (queue == static_cast <Recorder*> (context)->recorderBufferQueue); (void) queue; | |||
| static_cast <Recorder*> (context)->bufferList.bufferReturned(); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Recorder) | |||
| }; | |||
| //============================================================================== | |||
| Engine engine; | |||
| ScopedPointer<Player> player; | |||
| ScopedPointer<Recorder> recorder; | |||
| //============================================================================== | |||
| static bool check (const SLresult result) | |||
| { | |||
| jassert (result == SL_RESULT_SUCCESS); | |||
| return result == SL_RESULT_SUCCESS; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioIODevice) | |||
| }; | |||
| //============================================================================== | |||
| class OpenSLAudioDeviceType : public AudioIODeviceType | |||
| { | |||
| public: | |||
| OpenSLAudioDeviceType() : AudioIODeviceType (openSLTypeName) {} | |||
| //============================================================================== | |||
| void scanForDevices() {} | |||
| StringArray getDeviceNames (bool wantInputNames) const { return StringArray (openSLTypeName); } | |||
| int getDefaultDeviceIndex (bool forInput) const { return 0; } | |||
| int getIndexOfDevice (AudioIODevice* device, bool asInput) const { return device != nullptr ? 0 : -1; } | |||
| bool hasSeparateInputsAndOutputs() const { return false; } | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| ScopedPointer<OpenSLAudioIODevice> dev; | |||
| if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) | |||
| { | |||
| dev = new OpenSLAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName | |||
| : inputDeviceName); | |||
| if (! dev->openedOk()) | |||
| dev = nullptr; | |||
| } | |||
| return dev.release(); | |||
| } | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OpenSLAudioDeviceType) | |||
| }; | |||
| //============================================================================== | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_OpenSLES() | |||
| { | |||
| return isOpenSLAvailable() ? new OpenSLAudioDeviceType() : nullptr; | |||
| } | |||
| @@ -0,0 +1,555 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| class iOSAudioIODevice : public AudioIODevice | |||
| { | |||
| public: | |||
| iOSAudioIODevice (const String& deviceName) | |||
| : AudioIODevice (deviceName, "Audio"), | |||
| actualBufferSize (0), | |||
| isRunning (false), | |||
| audioUnit (0), | |||
| callback (nullptr), | |||
| floatData (1, 2) | |||
| { | |||
| getSessionHolder().activeDevices.add (this); | |||
| numInputChannels = 2; | |||
| numOutputChannels = 2; | |||
| preferredBufferSize = 0; | |||
| updateDeviceInfo(); | |||
| } | |||
| ~iOSAudioIODevice() | |||
| { | |||
| getSessionHolder().activeDevices.removeFirstMatchingValue (this); | |||
| close(); | |||
| } | |||
| StringArray getOutputChannelNames() | |||
| { | |||
| StringArray s; | |||
| s.add ("Left"); | |||
| s.add ("Right"); | |||
| return s; | |||
| } | |||
| StringArray getInputChannelNames() | |||
| { | |||
| StringArray s; | |||
| if (audioInputIsAvailable) | |||
| { | |||
| s.add ("Left"); | |||
| s.add ("Right"); | |||
| } | |||
| return s; | |||
| } | |||
| int getNumSampleRates() { return 1; } | |||
| double getSampleRate (int index) { return sampleRate; } | |||
| int getNumBufferSizesAvailable() { return 6; } | |||
| int getBufferSizeSamples (int index) { return 1 << (jlimit (0, 5, index) + 6); } | |||
| int getDefaultBufferSize() { return 1024; } | |||
| String open (const BigInteger& inputChannels, | |||
| const BigInteger& outputChannels, | |||
| double sampleRate, | |||
| int bufferSize) | |||
| { | |||
| close(); | |||
| lastError = String::empty; | |||
| preferredBufferSize = (bufferSize <= 0) ? getDefaultBufferSize() : bufferSize; | |||
| // xxx set up channel mapping | |||
| activeOutputChans = outputChannels; | |||
| activeOutputChans.setRange (2, activeOutputChans.getHighestBit(), false); | |||
| numOutputChannels = activeOutputChans.countNumberOfSetBits(); | |||
| monoOutputChannelNumber = activeOutputChans.findNextSetBit (0); | |||
| activeInputChans = inputChannels; | |||
| activeInputChans.setRange (2, activeInputChans.getHighestBit(), false); | |||
| numInputChannels = activeInputChans.countNumberOfSetBits(); | |||
| monoInputChannelNumber = activeInputChans.findNextSetBit (0); | |||
| AudioSessionSetActive (true); | |||
| UInt32 audioCategory = (numInputChannels > 0 && audioInputIsAvailable) ? kAudioSessionCategory_PlayAndRecord | |||
| : kAudioSessionCategory_MediaPlayback; | |||
| AudioSessionSetProperty (kAudioSessionProperty_AudioCategory, sizeof (audioCategory), &audioCategory); | |||
| if (audioCategory == kAudioSessionCategory_PlayAndRecord) | |||
| { | |||
| // (note: mustn't set this until after the audio category property has been set) | |||
| UInt32 allowBluetoothInput = 1; | |||
| AudioSessionSetProperty (kAudioSessionProperty_OverrideCategoryEnableBluetoothInput, | |||
| sizeof (allowBluetoothInput), &allowBluetoothInput); | |||
| } | |||
| AudioSessionAddPropertyListener (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); | |||
| fixAudioRouteIfSetToReceiver(); | |||
| updateDeviceInfo(); | |||
| Float32 bufferDuration = preferredBufferSize / sampleRate; | |||
| AudioSessionSetProperty (kAudioSessionProperty_PreferredHardwareIOBufferDuration, sizeof (bufferDuration), &bufferDuration); | |||
| actualBufferSize = preferredBufferSize; | |||
| prepareFloatBuffers (actualBufferSize); | |||
| isRunning = true; | |||
| routingChanged (nullptr); // creates and starts the AU | |||
| lastError = audioUnit != 0 ? "" : "Couldn't open the device"; | |||
| return lastError; | |||
| } | |||
| void close() | |||
| { | |||
| if (isRunning) | |||
| { | |||
| isRunning = false; | |||
| AudioSessionRemovePropertyListenerWithUserData (kAudioSessionProperty_AudioRouteChange, routingChangedStatic, this); | |||
| AudioSessionSetActive (false); | |||
| if (audioUnit != 0) | |||
| { | |||
| AudioComponentInstanceDispose (audioUnit); | |||
| audioUnit = 0; | |||
| } | |||
| } | |||
| } | |||
| bool isOpen() { return isRunning; } | |||
| int getCurrentBufferSizeSamples() { return actualBufferSize; } | |||
| double getCurrentSampleRate() { return sampleRate; } | |||
| int getCurrentBitDepth() { return 16; } | |||
| BigInteger getActiveOutputChannels() const { return activeOutputChans; } | |||
| BigInteger getActiveInputChannels() const { return activeInputChans; } | |||
| int getOutputLatencyInSamples() { return 0; } //xxx | |||
| int getInputLatencyInSamples() { return 0; } //xxx | |||
| void start (AudioIODeviceCallback* newCallback) | |||
| { | |||
| if (isRunning && callback != newCallback) | |||
| { | |||
| if (newCallback != nullptr) | |||
| newCallback->audioDeviceAboutToStart (this); | |||
| const ScopedLock sl (callbackLock); | |||
| callback = newCallback; | |||
| } | |||
| } | |||
| void stop() | |||
| { | |||
| if (isRunning) | |||
| { | |||
| AudioIODeviceCallback* lastCallback; | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| lastCallback = callback; | |||
| callback = nullptr; | |||
| } | |||
| if (lastCallback != nullptr) | |||
| lastCallback->audioDeviceStopped(); | |||
| } | |||
| } | |||
| bool isPlaying() { return isRunning && callback != nullptr; } | |||
| String getLastError() { return lastError; } | |||
| private: | |||
| //================================================================================================== | |||
| CriticalSection callbackLock; | |||
| Float64 sampleRate; | |||
| int numInputChannels, numOutputChannels; | |||
| int preferredBufferSize, actualBufferSize; | |||
| bool isRunning; | |||
| String lastError; | |||
| AudioStreamBasicDescription format; | |||
| AudioUnit audioUnit; | |||
| UInt32 audioInputIsAvailable; | |||
| AudioIODeviceCallback* callback; | |||
| BigInteger activeOutputChans, activeInputChans; | |||
| AudioSampleBuffer floatData; | |||
| float* inputChannels[3]; | |||
| float* outputChannels[3]; | |||
| bool monoInputChannelNumber, monoOutputChannelNumber; | |||
| void prepareFloatBuffers (int bufferSize) | |||
| { | |||
| if (numInputChannels + numOutputChannels > 0) | |||
| { | |||
| floatData.setSize (numInputChannels + numOutputChannels, bufferSize); | |||
| zeromem (inputChannels, sizeof (inputChannels)); | |||
| zeromem (outputChannels, sizeof (outputChannels)); | |||
| for (int i = 0; i < numInputChannels; ++i) | |||
| inputChannels[i] = floatData.getSampleData (i); | |||
| for (int i = 0; i < numOutputChannels; ++i) | |||
| outputChannels[i] = floatData.getSampleData (i + numInputChannels); | |||
| } | |||
| } | |||
| //================================================================================================== | |||
| OSStatus process (AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | |||
| const UInt32 numFrames, AudioBufferList* data) | |||
| { | |||
| OSStatus err = noErr; | |||
| if (audioInputIsAvailable && numInputChannels > 0) | |||
| err = AudioUnitRender (audioUnit, flags, time, 1, numFrames, data); | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| { | |||
| // This shouldn't ever get triggered, but please let me know if it does! | |||
| jassert (numFrames <= floatData.getNumSamples()); | |||
| if (audioInputIsAvailable && numInputChannels > 0) | |||
| { | |||
| short* shortData = (short*) data->mBuffers[0].mData; | |||
| if (numInputChannels >= 2) | |||
| { | |||
| for (UInt32 i = 0; i < numFrames; ++i) | |||
| { | |||
| inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); | |||
| inputChannels[1][i] = *shortData++ * (1.0f / 32768.0f); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| if (monoInputChannelNumber > 0) | |||
| ++shortData; | |||
| for (UInt32 i = 0; i < numFrames; ++i) | |||
| { | |||
| inputChannels[0][i] = *shortData++ * (1.0f / 32768.0f); | |||
| ++shortData; | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (int i = numInputChannels; --i >= 0;) | |||
| zeromem (inputChannels[i], sizeof (float) * numFrames); | |||
| } | |||
| callback->audioDeviceIOCallback ((const float**) inputChannels, numInputChannels, | |||
| outputChannels, numOutputChannels, (int) numFrames); | |||
| short* shortData = (short*) data->mBuffers[0].mData; | |||
| int n = 0; | |||
| if (numOutputChannels >= 2) | |||
| { | |||
| for (UInt32 i = 0; i < numFrames; ++i) | |||
| { | |||
| shortData [n++] = (short) (outputChannels[0][i] * 32767.0f); | |||
| shortData [n++] = (short) (outputChannels[1][i] * 32767.0f); | |||
| } | |||
| } | |||
| else if (numOutputChannels == 1) | |||
| { | |||
| for (UInt32 i = 0; i < numFrames; ++i) | |||
| { | |||
| const short s = (short) (outputChannels[monoOutputChannelNumber][i] * 32767.0f); | |||
| shortData [n++] = s; | |||
| shortData [n++] = s; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| zeromem (data->mBuffers[0].mData, 2 * sizeof (short) * numFrames); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| zeromem (data->mBuffers[0].mData, 2 * sizeof (short) * numFrames); | |||
| } | |||
| return err; | |||
| } | |||
| void updateDeviceInfo() | |||
| { | |||
| UInt32 size = sizeof (sampleRate); | |||
| AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareSampleRate, &size, &sampleRate); | |||
| size = sizeof (audioInputIsAvailable); | |||
| AudioSessionGetProperty (kAudioSessionProperty_AudioInputAvailable, &size, &audioInputIsAvailable); | |||
| } | |||
| void routingChanged (const void* propertyValue) | |||
| { | |||
| if (! isRunning) | |||
| return; | |||
| if (propertyValue != nullptr) | |||
| { | |||
| CFDictionaryRef routeChangeDictionary = (CFDictionaryRef) propertyValue; | |||
| CFNumberRef routeChangeReasonRef = (CFNumberRef) CFDictionaryGetValue (routeChangeDictionary, | |||
| CFSTR (kAudioSession_AudioRouteChangeKey_Reason)); | |||
| SInt32 routeChangeReason; | |||
| CFNumberGetValue (routeChangeReasonRef, kCFNumberSInt32Type, &routeChangeReason); | |||
| if (routeChangeReason == kAudioSessionRouteChangeReason_OldDeviceUnavailable) | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| callback->audioDeviceError ("Old device unavailable"); | |||
| } | |||
| } | |||
| updateDeviceInfo(); | |||
| createAudioUnit(); | |||
| AudioSessionSetActive (true); | |||
| if (audioUnit != 0) | |||
| { | |||
| UInt32 formatSize = sizeof (format); | |||
| AudioUnitGetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, &formatSize); | |||
| Float32 bufferDuration = preferredBufferSize / sampleRate; | |||
| UInt32 bufferDurationSize = sizeof (bufferDuration); | |||
| AudioSessionGetProperty (kAudioSessionProperty_CurrentHardwareIOBufferDuration, &bufferDurationSize, &bufferDuration); | |||
| actualBufferSize = (int) (sampleRate * bufferDuration + 0.5); | |||
| AudioOutputUnitStart (audioUnit); | |||
| } | |||
| } | |||
| //================================================================================================== | |||
| struct AudioSessionHolder | |||
| { | |||
| AudioSessionHolder() | |||
| { | |||
| AudioSessionInitialize (0, 0, interruptionListenerCallback, this); | |||
| } | |||
| static void interruptionListenerCallback (void* client, UInt32 interruptionType) | |||
| { | |||
| const Array <iOSAudioIODevice*>& activeDevices = static_cast <AudioSessionHolder*> (client)->activeDevices; | |||
| for (int i = activeDevices.size(); --i >= 0;) | |||
| activeDevices.getUnchecked(i)->interruptionListener (interruptionType); | |||
| } | |||
| Array <iOSAudioIODevice*> activeDevices; | |||
| }; | |||
| static AudioSessionHolder& getSessionHolder() | |||
| { | |||
| static AudioSessionHolder audioSessionHolder; | |||
| return audioSessionHolder; | |||
| } | |||
| void interruptionListener (const UInt32 interruptionType) | |||
| { | |||
| if (interruptionType == kAudioSessionBeginInterruption) | |||
| { | |||
| isRunning = false; | |||
| AudioOutputUnitStop (audioUnit); | |||
| AudioSessionSetActive (false); | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| callback->audioDeviceError ("iOS audio session interruption"); | |||
| } | |||
| if (interruptionType == kAudioSessionEndInterruption) | |||
| { | |||
| isRunning = true; | |||
| AudioSessionSetActive (true); | |||
| AudioOutputUnitStart (audioUnit); | |||
| } | |||
| } | |||
| //================================================================================================== | |||
| static OSStatus processStatic (void* client, AudioUnitRenderActionFlags* flags, const AudioTimeStamp* time, | |||
| UInt32 /*busNumber*/, UInt32 numFrames, AudioBufferList* data) | |||
| { | |||
| return static_cast <iOSAudioIODevice*> (client)->process (flags, time, numFrames, data); | |||
| } | |||
| static void routingChangedStatic (void* client, AudioSessionPropertyID, UInt32 /*inDataSize*/, const void* propertyValue) | |||
| { | |||
| static_cast <iOSAudioIODevice*> (client)->routingChanged (propertyValue); | |||
| } | |||
| //================================================================================================== | |||
| void resetFormat (const int numChannels) noexcept | |||
| { | |||
| zerostruct (format); | |||
| format.mFormatID = kAudioFormatLinearPCM; | |||
| format.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked | kAudioFormatFlagsNativeEndian; | |||
| format.mBitsPerChannel = 8 * sizeof (short); | |||
| format.mChannelsPerFrame = numChannels; | |||
| format.mFramesPerPacket = 1; | |||
| format.mBytesPerFrame = format.mBytesPerPacket = numChannels * sizeof (short); | |||
| } | |||
| bool createAudioUnit() | |||
| { | |||
| if (audioUnit != 0) | |||
| { | |||
| AudioComponentInstanceDispose (audioUnit); | |||
| audioUnit = 0; | |||
| } | |||
| resetFormat (2); | |||
| AudioComponentDescription desc; | |||
| desc.componentType = kAudioUnitType_Output; | |||
| desc.componentSubType = kAudioUnitSubType_RemoteIO; | |||
| desc.componentManufacturer = kAudioUnitManufacturer_Apple; | |||
| desc.componentFlags = 0; | |||
| desc.componentFlagsMask = 0; | |||
| AudioComponent comp = AudioComponentFindNext (0, &desc); | |||
| AudioComponentInstanceNew (comp, &audioUnit); | |||
| if (audioUnit == 0) | |||
| return false; | |||
| if (numInputChannels > 0) | |||
| { | |||
| const UInt32 one = 1; | |||
| AudioUnitSetProperty (audioUnit, kAudioOutputUnitProperty_EnableIO, kAudioUnitScope_Input, 1, &one, sizeof (one)); | |||
| } | |||
| { | |||
| AudioChannelLayout layout; | |||
| layout.mChannelBitmap = 0; | |||
| layout.mNumberChannelDescriptions = 0; | |||
| layout.mChannelLayoutTag = kAudioChannelLayoutTag_Stereo; | |||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Input, 0, &layout, sizeof (layout)); | |||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_AudioChannelLayout, kAudioUnitScope_Output, 0, &layout, sizeof (layout)); | |||
| } | |||
| { | |||
| AURenderCallbackStruct inputProc; | |||
| inputProc.inputProc = processStatic; | |||
| inputProc.inputProcRefCon = this; | |||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &inputProc, sizeof (inputProc)); | |||
| } | |||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &format, sizeof (format)); | |||
| AudioUnitSetProperty (audioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 1, &format, sizeof (format)); | |||
| AudioUnitInitialize (audioUnit); | |||
| return true; | |||
| } | |||
| // If the routing is set to go through the receiver (i.e. the speaker, but quiet), this re-routes it | |||
| // to make it loud. Needed because by default when using an input + output, the output is kept quiet. | |||
| static void fixAudioRouteIfSetToReceiver() | |||
| { | |||
| CFStringRef audioRoute = 0; | |||
| UInt32 propertySize = sizeof (audioRoute); | |||
| if (AudioSessionGetProperty (kAudioSessionProperty_AudioRoute, &propertySize, &audioRoute) == noErr) | |||
| { | |||
| NSString* route = (NSString*) audioRoute; | |||
| //DBG ("audio route: " + nsStringToJuce (route)); | |||
| if ([route hasPrefix: @"Receiver"]) | |||
| { | |||
| UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker; | |||
| AudioSessionSetProperty (kAudioSessionProperty_OverrideAudioRoute, sizeof (audioRouteOverride), &audioRouteOverride); | |||
| } | |||
| CFRelease (audioRoute); | |||
| } | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE (iOSAudioIODevice) | |||
| }; | |||
| //============================================================================== | |||
| class iOSAudioIODeviceType : public AudioIODeviceType | |||
| { | |||
| public: | |||
| iOSAudioIODeviceType() : AudioIODeviceType ("iOS Audio") | |||
| { | |||
| } | |||
| void scanForDevices() {} | |||
| StringArray getDeviceNames (bool wantInputNames) const | |||
| { | |||
| return StringArray ("iOS Audio"); | |||
| } | |||
| int getDefaultDeviceIndex (bool forInput) const | |||
| { | |||
| return 0; | |||
| } | |||
| int getIndexOfDevice (AudioIODevice* device, bool asInput) const | |||
| { | |||
| return device != nullptr ? 0 : -1; | |||
| } | |||
| bool hasSeparateInputsAndOutputs() const { return false; } | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| if (outputDeviceName.isNotEmpty() || inputDeviceName.isNotEmpty()) | |||
| return new iOSAudioIODevice (outputDeviceName.isNotEmpty() ? outputDeviceName | |||
| : inputDeviceName); | |||
| return nullptr; | |||
| } | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (iOSAudioIODeviceType) | |||
| }; | |||
| //============================================================================== | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_iOSAudio() | |||
| { | |||
| return new iOSAudioIODeviceType(); | |||
| } | |||
| @@ -0,0 +1,77 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| AudioCDReader::AudioCDReader() | |||
| : AudioFormatReader (0, "CD Audio") | |||
| { | |||
| } | |||
| StringArray AudioCDReader::getAvailableCDNames() | |||
| { | |||
| StringArray names; | |||
| return names; | |||
| } | |||
| AudioCDReader* AudioCDReader::createReaderForCD (const int index) | |||
| { | |||
| return nullptr; | |||
| } | |||
| AudioCDReader::~AudioCDReader() | |||
| { | |||
| } | |||
| void AudioCDReader::refreshTrackLengths() | |||
| { | |||
| } | |||
| bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) | |||
| { | |||
| return false; | |||
| } | |||
| bool AudioCDReader::isCDStillPresent() const | |||
| { | |||
| return false; | |||
| } | |||
| bool AudioCDReader::isTrackAudio (int trackNum) const | |||
| { | |||
| return false; | |||
| } | |||
| void AudioCDReader::enableIndexScanning (bool b) | |||
| { | |||
| } | |||
| int AudioCDReader::getLastIndex() const | |||
| { | |||
| return 0; | |||
| } | |||
| Array<int> AudioCDReader::findIndexesInTrack (const int trackNumber) | |||
| { | |||
| return Array<int>(); | |||
| } | |||
| @@ -0,0 +1,590 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| //============================================================================== | |||
| static void* juce_libjackHandle = nullptr; | |||
| static void* juce_loadJackFunction (const char* const name) | |||
| { | |||
| if (juce_libjackHandle == nullptr) | |||
| return nullptr; | |||
| return dlsym (juce_libjackHandle, name); | |||
| } | |||
| #define JUCE_DECL_JACK_FUNCTION(return_type, fn_name, argument_types, arguments) \ | |||
| return_type fn_name argument_types \ | |||
| { \ | |||
| typedef return_type (*fn_type) argument_types; \ | |||
| static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ | |||
| return (fn != nullptr) ? ((*fn) arguments) : (return_type) 0; \ | |||
| } | |||
| #define JUCE_DECL_VOID_JACK_FUNCTION(fn_name, argument_types, arguments) \ | |||
| void fn_name argument_types \ | |||
| { \ | |||
| typedef void (*fn_type) argument_types; \ | |||
| static fn_type fn = (fn_type) juce_loadJackFunction (#fn_name); \ | |||
| if (fn != nullptr) (*fn) arguments; \ | |||
| } | |||
| //============================================================================== | |||
| JUCE_DECL_JACK_FUNCTION (jack_client_t*, jack_client_open, (const char* client_name, jack_options_t options, jack_status_t* status, ...), (client_name, options, status)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_client_close, (jack_client_t *client), (client)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_activate, (jack_client_t* client), (client)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_deactivate, (jack_client_t* client), (client)); | |||
| JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_buffer_size, (jack_client_t* client), (client)); | |||
| JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_get_sample_rate, (jack_client_t* client), (client)); | |||
| JUCE_DECL_VOID_JACK_FUNCTION (jack_on_shutdown, (jack_client_t* client, void (*function)(void* arg), void* arg), (client, function, arg)); | |||
| JUCE_DECL_JACK_FUNCTION (void* , jack_port_get_buffer, (jack_port_t* port, jack_nframes_t nframes), (port, nframes)); | |||
| JUCE_DECL_JACK_FUNCTION (jack_nframes_t, jack_port_get_total_latency, (jack_client_t* client, jack_port_t* port), (client, port)); | |||
| JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_register, (jack_client_t* client, const char* port_name, const char* port_type, unsigned long flags, unsigned long buffer_size), (client, port_name, port_type, flags, buffer_size)); | |||
| JUCE_DECL_VOID_JACK_FUNCTION (jack_set_error_function, (void (*func)(const char*)), (func)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_set_process_callback, (jack_client_t* client, JackProcessCallback process_callback, void* arg), (client, process_callback, arg)); | |||
| JUCE_DECL_JACK_FUNCTION (const char**, jack_get_ports, (jack_client_t* client, const char* port_name_pattern, const char* type_name_pattern, unsigned long flags), (client, port_name_pattern, type_name_pattern, flags)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_connect, (jack_client_t* client, const char* source_port, const char* destination_port), (client, source_port, destination_port)); | |||
| JUCE_DECL_JACK_FUNCTION (const char*, jack_port_name, (const jack_port_t* port), (port)); | |||
| JUCE_DECL_JACK_FUNCTION (void*, jack_set_port_connect_callback, (jack_client_t* client, JackPortConnectCallback connect_callback, void* arg), (client, connect_callback, arg)); | |||
| JUCE_DECL_JACK_FUNCTION (jack_port_t* , jack_port_by_id, (jack_client_t* client, jack_port_id_t port_id), (client, port_id)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_port_connected, (const jack_port_t* port), (port)); | |||
| JUCE_DECL_JACK_FUNCTION (int, jack_port_connected_to, (const jack_port_t* port, const char* port_name), (port, port_name)); | |||
| #if JUCE_DEBUG | |||
| #define JACK_LOGGING_ENABLED 1 | |||
| #endif | |||
| #if JACK_LOGGING_ENABLED | |||
| namespace | |||
| { | |||
| void jack_Log (const String& s) | |||
| { | |||
| std::cerr << s << std::endl; | |||
| } | |||
| const char* getJackErrorMessage (const jack_status_t status) | |||
| { | |||
| if (status & JackServerFailed | |||
| || status & JackServerError) return "Unable to connect to JACK server"; | |||
| if (status & JackVersionError) return "Client's protocol version does not match"; | |||
| if (status & JackInvalidOption) return "The operation contained an invalid or unsupported option"; | |||
| if (status & JackNameNotUnique) return "The desired client name was not unique"; | |||
| if (status & JackNoSuchClient) return "Requested client does not exist"; | |||
| if (status & JackInitFailure) return "Unable to initialize client"; | |||
| return nullptr; | |||
| } | |||
| } | |||
| #define JUCE_JACK_LOG_STATUS(x) { if (const char* m = getJackErrorMessage (x)) jack_Log (m); } | |||
| #define JUCE_JACK_LOG(x) jack_Log(x) | |||
| #else | |||
| #define JUCE_JACK_LOG_STATUS(x) {} | |||
| #define JUCE_JACK_LOG(x) {} | |||
| #endif | |||
| //============================================================================== | |||
| #ifndef JUCE_JACK_CLIENT_NAME | |||
| #define JUCE_JACK_CLIENT_NAME "JUCEJack" | |||
| #endif | |||
| struct JackPortIterator | |||
| { | |||
| JackPortIterator (jack_client_t* const client, const bool forInput) | |||
| : ports (nullptr), index (-1) | |||
| { | |||
| if (client != nullptr) | |||
| ports = juce::jack_get_ports (client, nullptr, nullptr, | |||
| forInput ? JackPortIsOutput : JackPortIsInput); | |||
| // (NB: This looks like it's the wrong way round, but it is correct!) | |||
| } | |||
| ~JackPortIterator() | |||
| { | |||
| ::free (ports); | |||
| } | |||
| bool next() | |||
| { | |||
| if (ports == nullptr || ports [index + 1] == nullptr) | |||
| return false; | |||
| name = CharPointer_UTF8 (ports[++index]); | |||
| clientName = name.upToFirstOccurrenceOf (":", false, false); | |||
| return true; | |||
| } | |||
| const char** ports; | |||
| int index; | |||
| String name; | |||
| String clientName; | |||
| }; | |||
| class JackAudioIODeviceType; | |||
| static Array<JackAudioIODeviceType*> activeDeviceTypes; | |||
| //============================================================================== | |||
| class JackAudioIODevice : public AudioIODevice | |||
| { | |||
| public: | |||
| JackAudioIODevice (const String& deviceName, | |||
| const String& inId, | |||
| const String& outId) | |||
| : AudioIODevice (deviceName, "JACK"), | |||
| inputId (inId), | |||
| outputId (outId), | |||
| deviceIsOpen (false), | |||
| callback (nullptr), | |||
| totalNumberOfInputChannels (0), | |||
| totalNumberOfOutputChannels (0) | |||
| { | |||
| jassert (deviceName.isNotEmpty()); | |||
| jack_status_t status; | |||
| client = juce::jack_client_open (JUCE_JACK_CLIENT_NAME, JackNoStartServer, &status); | |||
| if (client == nullptr) | |||
| { | |||
| JUCE_JACK_LOG_STATUS (status); | |||
| } | |||
| else | |||
| { | |||
| juce::jack_set_error_function (errorCallback); | |||
| // open input ports | |||
| const StringArray inputChannels (getInputChannelNames()); | |||
| for (int i = 0; i < inputChannels.size(); ++i) | |||
| { | |||
| String inputName; | |||
| inputName << "in_" << ++totalNumberOfInputChannels; | |||
| inputPorts.add (juce::jack_port_register (client, inputName.toUTF8(), | |||
| JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0)); | |||
| } | |||
| // open output ports | |||
| const StringArray outputChannels (getOutputChannelNames()); | |||
| for (int i = 0; i < outputChannels.size (); ++i) | |||
| { | |||
| String outputName; | |||
| outputName << "out_" << ++totalNumberOfOutputChannels; | |||
| outputPorts.add (juce::jack_port_register (client, outputName.toUTF8(), | |||
| JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0)); | |||
| } | |||
| inChans.calloc (totalNumberOfInputChannels + 2); | |||
| outChans.calloc (totalNumberOfOutputChannels + 2); | |||
| } | |||
| } | |||
| ~JackAudioIODevice() | |||
| { | |||
| close(); | |||
| if (client != nullptr) | |||
| { | |||
| juce::jack_client_close (client); | |||
| client = nullptr; | |||
| } | |||
| } | |||
| StringArray getChannelNames (bool forInput) const | |||
| { | |||
| StringArray names; | |||
| for (JackPortIterator i (client, forInput); i.next();) | |||
| if (i.clientName == getName()) | |||
| names.add (i.name.fromFirstOccurrenceOf (":", false, false)); | |||
| return names; | |||
| } | |||
| StringArray getOutputChannelNames() { return getChannelNames (false); } | |||
| StringArray getInputChannelNames() { return getChannelNames (true); } | |||
| int getNumSampleRates() { return client != nullptr ? 1 : 0; } | |||
| double getSampleRate (int /*index*/) { return client != nullptr ? juce::jack_get_sample_rate (client) : 0; } | |||
| int getNumBufferSizesAvailable() { return client != nullptr ? 1 : 0; } | |||
| int getBufferSizeSamples (int /*index*/) { return getDefaultBufferSize(); } | |||
| int getDefaultBufferSize() { return client != nullptr ? juce::jack_get_buffer_size (client) : 0; } | |||
| String open (const BigInteger& inputChannels, const BigInteger& outputChannels, | |||
| double /* sampleRate */, int /* bufferSizeSamples */) | |||
| { | |||
| if (client == nullptr) | |||
| { | |||
| lastError = "No JACK client running"; | |||
| return lastError; | |||
| } | |||
| lastError = String::empty; | |||
| close(); | |||
| juce::jack_set_process_callback (client, processCallback, this); | |||
| juce::jack_set_port_connect_callback (client, portConnectCallback, this); | |||
| juce::jack_on_shutdown (client, shutdownCallback, this); | |||
| juce::jack_activate (client); | |||
| deviceIsOpen = true; | |||
| if (! inputChannels.isZero()) | |||
| { | |||
| for (JackPortIterator i (client, true); i.next();) | |||
| { | |||
| if (inputChannels [i.index] && i.clientName == getName()) | |||
| { | |||
| int error = juce::jack_connect (client, i.ports[i.index], juce::jack_port_name ((jack_port_t*) inputPorts[i.index])); | |||
| if (error != 0) | |||
| JUCE_JACK_LOG ("Cannot connect input port " + String (i.index) + " (" + i.name + "), error " + String (error)); | |||
| } | |||
| } | |||
| } | |||
| if (! outputChannels.isZero()) | |||
| { | |||
| for (JackPortIterator i (client, false); i.next();) | |||
| { | |||
| if (outputChannels [i.index] && i.clientName == getName()) | |||
| { | |||
| int error = juce::jack_connect (client, juce::jack_port_name ((jack_port_t*) outputPorts[i.index]), i.ports[i.index]); | |||
| if (error != 0) | |||
| JUCE_JACK_LOG ("Cannot connect output port " + String (i.index) + " (" + i.name + "), error " + String (error)); | |||
| } | |||
| } | |||
| } | |||
| return lastError; | |||
| } | |||
| void close() | |||
| { | |||
| stop(); | |||
| if (client != nullptr) | |||
| { | |||
| juce::jack_deactivate (client); | |||
| juce::jack_set_process_callback (client, processCallback, nullptr); | |||
| juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr); | |||
| juce::jack_on_shutdown (client, shutdownCallback, nullptr); | |||
| } | |||
| deviceIsOpen = false; | |||
| } | |||
| void start (AudioIODeviceCallback* newCallback) | |||
| { | |||
| if (deviceIsOpen && newCallback != callback) | |||
| { | |||
| if (newCallback != nullptr) | |||
| newCallback->audioDeviceAboutToStart (this); | |||
| AudioIODeviceCallback* const oldCallback = callback; | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| callback = newCallback; | |||
| } | |||
| if (oldCallback != nullptr) | |||
| oldCallback->audioDeviceStopped(); | |||
| } | |||
| } | |||
| void stop() | |||
| { | |||
| start (nullptr); | |||
| } | |||
| bool isOpen() { return deviceIsOpen; } | |||
| bool isPlaying() { return callback != nullptr; } | |||
| int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); } | |||
| double getCurrentSampleRate() { return getSampleRate (0); } | |||
| int getCurrentBitDepth() { return 32; } | |||
| String getLastError() { return lastError; } | |||
| BigInteger getActiveOutputChannels() const { return activeOutputChannels; } | |||
| BigInteger getActiveInputChannels() const { return activeInputChannels; } | |||
| int getOutputLatencyInSamples() | |||
| { | |||
| int latency = 0; | |||
| for (int i = 0; i < outputPorts.size(); i++) | |||
| latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) outputPorts [i])); | |||
| return latency; | |||
| } | |||
| int getInputLatencyInSamples() | |||
| { | |||
| int latency = 0; | |||
| for (int i = 0; i < inputPorts.size(); i++) | |||
| latency = jmax (latency, (int) juce::jack_port_get_total_latency (client, (jack_port_t*) inputPorts [i])); | |||
| return latency; | |||
| } | |||
| String inputId, outputId; | |||
| private: | |||
| void process (const int numSamples) | |||
| { | |||
| int numActiveInChans = 0, numActiveOutChans = 0; | |||
| for (int i = 0; i < totalNumberOfInputChannels; ++i) | |||
| { | |||
| if (activeInputChannels[i]) | |||
| if (jack_default_audio_sample_t* in | |||
| = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) inputPorts.getUnchecked(i), numSamples)) | |||
| inChans [numActiveInChans++] = (float*) in; | |||
| } | |||
| for (int i = 0; i < totalNumberOfOutputChannels; ++i) | |||
| { | |||
| if (activeOutputChannels[i]) | |||
| if (jack_default_audio_sample_t* out | |||
| = (jack_default_audio_sample_t*) juce::jack_port_get_buffer ((jack_port_t*) outputPorts.getUnchecked(i), numSamples)) | |||
| outChans [numActiveOutChans++] = (float*) out; | |||
| } | |||
| const ScopedLock sl (callbackLock); | |||
| if (callback != nullptr) | |||
| { | |||
| if ((numActiveInChans + numActiveOutChans) > 0) | |||
| callback->audioDeviceIOCallback (const_cast <const float**> (inChans.getData()), numActiveInChans, | |||
| outChans, numActiveOutChans, numSamples); | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < numActiveOutChans; ++i) | |||
| zeromem (outChans[i], sizeof (float) * numSamples); | |||
| } | |||
| } | |||
| static int processCallback (jack_nframes_t nframes, void* callbackArgument) | |||
| { | |||
| if (callbackArgument != nullptr) | |||
| ((JackAudioIODevice*) callbackArgument)->process (nframes); | |||
| return 0; | |||
| } | |||
| void updateActivePorts() | |||
| { | |||
| BigInteger newOutputChannels, newInputChannels; | |||
| for (int i = 0; i < outputPorts.size(); ++i) | |||
| if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i))) | |||
| newOutputChannels.setBit (i); | |||
| for (int i = 0; i < inputPorts.size(); ++i) | |||
| if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i))) | |||
| newInputChannels.setBit (i); | |||
| if (newOutputChannels != activeOutputChannels | |||
| || newInputChannels != activeInputChannels) | |||
| { | |||
| AudioIODeviceCallback* const oldCallback = callback; | |||
| stop(); | |||
| activeOutputChannels = newOutputChannels; | |||
| activeInputChannels = newInputChannels; | |||
| if (oldCallback != nullptr) | |||
| start (oldCallback); | |||
| sendDeviceChangedCallback(); | |||
| } | |||
| } | |||
| static void portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg) | |||
| { | |||
| if (JackAudioIODevice* device = static_cast <JackAudioIODevice*> (arg)) | |||
| device->updateActivePorts(); | |||
| } | |||
| static void threadInitCallback (void* /* callbackArgument */) | |||
| { | |||
| JUCE_JACK_LOG ("JackAudioIODevice::initialise"); | |||
| } | |||
| static void shutdownCallback (void* callbackArgument) | |||
| { | |||
| JUCE_JACK_LOG ("JackAudioIODevice::shutdown"); | |||
| if (JackAudioIODevice* device = (JackAudioIODevice*) callbackArgument) | |||
| { | |||
| device->client = nullptr; | |||
| device->close(); | |||
| } | |||
| } | |||
| static void errorCallback (const char* msg) | |||
| { | |||
| JUCE_JACK_LOG ("JackAudioIODevice::errorCallback " + String (msg)); | |||
| } | |||
| static void sendDeviceChangedCallback(); | |||
| bool deviceIsOpen; | |||
| jack_client_t* client; | |||
| String lastError; | |||
| AudioIODeviceCallback* callback; | |||
| CriticalSection callbackLock; | |||
| HeapBlock <float*> inChans, outChans; | |||
| int totalNumberOfInputChannels; | |||
| int totalNumberOfOutputChannels; | |||
| Array<void*> inputPorts, outputPorts; | |||
| BigInteger activeInputChannels, activeOutputChannels; | |||
| }; | |||
| //============================================================================== | |||
| class JackAudioIODeviceType : public AudioIODeviceType | |||
| { | |||
| public: | |||
| JackAudioIODeviceType() | |||
| : AudioIODeviceType ("JACK"), | |||
| hasScanned (false) | |||
| { | |||
| activeDeviceTypes.add (this); | |||
| } | |||
| ~JackAudioIODeviceType() | |||
| { | |||
| activeDeviceTypes.removeFirstMatchingValue (this); | |||
| } | |||
| void scanForDevices() | |||
| { | |||
| hasScanned = true; | |||
| inputNames.clear(); | |||
| inputIds.clear(); | |||
| outputNames.clear(); | |||
| outputIds.clear(); | |||
| if (juce_libjackHandle == nullptr) | |||
| { | |||
| juce_libjackHandle = dlopen ("libjack.so", RTLD_LAZY); | |||
| if (juce_libjackHandle == nullptr) | |||
| return; | |||
| } | |||
| jack_status_t status; | |||
| // open a dummy client | |||
| if (jack_client_t* const client = juce::jack_client_open ("JuceJackDummy", JackNoStartServer, &status)) | |||
| { | |||
| // scan for output devices | |||
| for (JackPortIterator i (client, false); i.next();) | |||
| { | |||
| if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! inputNames.contains (i.clientName)) | |||
| { | |||
| inputNames.add (i.clientName); | |||
| inputIds.add (i.ports [i.index]); | |||
| } | |||
| } | |||
| // scan for input devices | |||
| for (JackPortIterator i (client, true); i.next();) | |||
| { | |||
| if (i.clientName != (JUCE_JACK_CLIENT_NAME) && ! outputNames.contains (i.clientName)) | |||
| { | |||
| outputNames.add (i.clientName); | |||
| outputIds.add (i.ports [i.index]); | |||
| } | |||
| } | |||
| juce::jack_client_close (client); | |||
| } | |||
| else | |||
| { | |||
| JUCE_JACK_LOG_STATUS (status); | |||
| } | |||
| } | |||
| StringArray getDeviceNames (bool wantInputNames) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return wantInputNames ? inputNames : outputNames; | |||
| } | |||
| int getDefaultDeviceIndex (bool /* forInput */) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| return 0; | |||
| } | |||
| bool hasSeparateInputsAndOutputs() const { return true; } | |||
| int getIndexOfDevice (AudioIODevice* device, bool asInput) const | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| if (JackAudioIODevice* d = dynamic_cast <JackAudioIODevice*> (device)) | |||
| return asInput ? inputIds.indexOf (d->inputId) | |||
| : outputIds.indexOf (d->outputId); | |||
| return -1; | |||
| } | |||
| AudioIODevice* createDevice (const String& outputDeviceName, | |||
| const String& inputDeviceName) | |||
| { | |||
| jassert (hasScanned); // need to call scanForDevices() before doing this | |||
| const int inputIndex = inputNames.indexOf (inputDeviceName); | |||
| const int outputIndex = outputNames.indexOf (outputDeviceName); | |||
| if (inputIndex >= 0 || outputIndex >= 0) | |||
| return new JackAudioIODevice (outputIndex >= 0 ? outputDeviceName | |||
| : inputDeviceName, | |||
| inputIds [inputIndex], | |||
| outputIds [outputIndex]); | |||
| return nullptr; | |||
| } | |||
| void portConnectionChange() { callDeviceChangeListeners(); } | |||
| private: | |||
| StringArray inputNames, outputNames, inputIds, outputIds; | |||
| bool hasScanned; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType) | |||
| }; | |||
| void JackAudioIODevice::sendDeviceChangedCallback() | |||
| { | |||
| for (int i = activeDeviceTypes.size(); --i >= 0;) | |||
| if (JackAudioIODeviceType* d = activeDeviceTypes[i]) | |||
| d->portConnectionChange(); | |||
| } | |||
| //============================================================================== | |||
| AudioIODeviceType* AudioIODeviceType::createAudioIODeviceType_JACK() | |||
| { | |||
| return new JackAudioIODeviceType(); | |||
| } | |||
| @@ -0,0 +1,610 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_ALSA | |||
| // You can define these strings in your app if you want to override the default names: | |||
| #ifndef JUCE_ALSA_MIDI_INPUT_NAME | |||
| #define JUCE_ALSA_MIDI_INPUT_NAME "Juce Midi Input" | |||
| #endif | |||
| #ifndef JUCE_ALSA_MIDI_OUTPUT_NAME | |||
| #define JUCE_ALSA_MIDI_OUTPUT_NAME "Juce Midi Output" | |||
| #endif | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| class AlsaPortAndCallback; | |||
| //============================================================================== | |||
| class AlsaClient : public ReferenceCountedObject | |||
| { | |||
| public: | |||
| typedef ReferenceCountedObjectPtr<AlsaClient> Ptr; | |||
| AlsaClient (bool forInput) | |||
| : input (forInput), handle (nullptr) | |||
| { | |||
| snd_seq_open (&handle, "default", forInput ? SND_SEQ_OPEN_INPUT | |||
| : SND_SEQ_OPEN_OUTPUT, 0); | |||
| } | |||
| ~AlsaClient() | |||
| { | |||
| if (handle != nullptr) | |||
| { | |||
| snd_seq_close (handle); | |||
| handle = nullptr; | |||
| } | |||
| jassert (activeCallbacks.size() == 0); | |||
| if (inputThread) | |||
| { | |||
| inputThread->stopThread (3000); | |||
| inputThread = nullptr; | |||
| } | |||
| } | |||
| bool isInput() const noexcept { return input; } | |||
| void setName (const String& name) | |||
| { | |||
| snd_seq_set_client_name (handle, name.toUTF8()); | |||
| } | |||
| void registerCallback (AlsaPortAndCallback* cb) | |||
| { | |||
| if (cb != nullptr) | |||
| { | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| activeCallbacks.add (cb); | |||
| if (inputThread == nullptr) | |||
| inputThread = new MidiInputThread (*this); | |||
| } | |||
| inputThread->startThread(); | |||
| } | |||
| } | |||
| void unregisterCallback (AlsaPortAndCallback* cb) | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| jassert (activeCallbacks.contains (cb)); | |||
| activeCallbacks.removeAllInstancesOf (cb); | |||
| if (activeCallbacks.size() == 0 && inputThread->isThreadRunning()) | |||
| inputThread->signalThreadShouldExit(); | |||
| } | |||
| void handleIncomingMidiMessage (const MidiMessage& message, int port); | |||
| snd_seq_t* get() const noexcept { return handle; } | |||
| private: | |||
| bool input; | |||
| snd_seq_t* handle; | |||
| Array<AlsaPortAndCallback*> activeCallbacks; | |||
| CriticalSection callbackLock; | |||
| //============================================================================== | |||
| class MidiInputThread : public Thread | |||
| { | |||
| public: | |||
| MidiInputThread (AlsaClient& c) | |||
| : Thread ("Juce MIDI Input"), client (c) | |||
| { | |||
| jassert (client.input && client.get() != nullptr); | |||
| } | |||
| void run() override | |||
| { | |||
| const int maxEventSize = 16 * 1024; | |||
| snd_midi_event_t* midiParser; | |||
| snd_seq_t* seqHandle = client.get(); | |||
| if (snd_midi_event_new (maxEventSize, &midiParser) >= 0) | |||
| { | |||
| const int numPfds = snd_seq_poll_descriptors_count (seqHandle, POLLIN); | |||
| HeapBlock<pollfd> pfd (numPfds); | |||
| snd_seq_poll_descriptors (seqHandle, pfd, numPfds, POLLIN); | |||
| HeapBlock <uint8> buffer (maxEventSize); | |||
| while (! threadShouldExit()) | |||
| { | |||
| if (poll (pfd, numPfds, 100) > 0) // there was a "500" here which is a bit long when we exit the program and have to wait for a timeout on this poll call | |||
| { | |||
| if (threadShouldExit()) | |||
| break; | |||
| snd_seq_nonblock (seqHandle, 1); | |||
| do | |||
| { | |||
| snd_seq_event_t* inputEvent = nullptr; | |||
| if (snd_seq_event_input (seqHandle, &inputEvent) >= 0) | |||
| { | |||
| // xxx what about SYSEXes that are too big for the buffer? | |||
| const int numBytes = snd_midi_event_decode (midiParser, buffer, | |||
| maxEventSize, inputEvent); | |||
| snd_midi_event_reset_decode (midiParser); | |||
| if (numBytes > 0) | |||
| { | |||
| const MidiMessage message ((const uint8*) buffer, numBytes, | |||
| Time::getMillisecondCounter() * 0.001); | |||
| client.handleIncomingMidiMessage (message, inputEvent->dest.port); | |||
| } | |||
| snd_seq_free_event (inputEvent); | |||
| } | |||
| } | |||
| while (snd_seq_event_input_pending (seqHandle, 0) > 0); | |||
| } | |||
| } | |||
| snd_midi_event_free (midiParser); | |||
| } | |||
| }; | |||
| private: | |||
| AlsaClient& client; | |||
| }; | |||
| ScopedPointer<MidiInputThread> inputThread; | |||
| }; | |||
| static AlsaClient::Ptr globalAlsaSequencerIn() | |||
| { | |||
| static AlsaClient::Ptr global (new AlsaClient (true)); | |||
| return global; | |||
| } | |||
| static AlsaClient::Ptr globalAlsaSequencerOut() | |||
| { | |||
| static AlsaClient::Ptr global (new AlsaClient (false)); | |||
| return global; | |||
| } | |||
| static AlsaClient::Ptr globalAlsaSequencer (bool input) | |||
| { | |||
| return input ? globalAlsaSequencerIn() | |||
| : globalAlsaSequencerOut(); | |||
| } | |||
| //============================================================================== | |||
| // represents an input or output port of the supplied AlsaClient | |||
| class AlsaPort | |||
| { | |||
| public: | |||
| AlsaPort() noexcept : portId (-1) {} | |||
| AlsaPort (const AlsaClient::Ptr& c, int port) noexcept : client (c), portId (port) {} | |||
| void createPort (const AlsaClient::Ptr& c, const String& name, bool forInput) | |||
| { | |||
| client = c; | |||
| if (snd_seq_t* handle = client->get()) | |||
| portId = snd_seq_create_simple_port (handle, name.toUTF8(), | |||
| forInput ? (SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE) | |||
| : (SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ), | |||
| SND_SEQ_PORT_TYPE_MIDI_GENERIC); | |||
| } | |||
| void deletePort() | |||
| { | |||
| if (isValid()) | |||
| { | |||
| snd_seq_delete_simple_port (client->get(), portId); | |||
| portId = -1; | |||
| } | |||
| } | |||
| void connectWith (int sourceClient, int sourcePort) | |||
| { | |||
| if (client->isInput()) | |||
| snd_seq_connect_from (client->get(), portId, sourceClient, sourcePort); | |||
| else | |||
| snd_seq_connect_to (client->get(), portId, sourceClient, sourcePort); | |||
| } | |||
| bool isValid() const noexcept | |||
| { | |||
| return client != nullptr && client->get() != nullptr && portId >= 0; | |||
| } | |||
| AlsaClient::Ptr client; | |||
| int portId; | |||
| }; | |||
| //============================================================================== | |||
| class AlsaPortAndCallback | |||
| { | |||
| public: | |||
| AlsaPortAndCallback (AlsaPort p, MidiInput* in, MidiInputCallback* cb) | |||
| : port (p), midiInput (in), callback (cb), callbackEnabled (false) | |||
| { | |||
| } | |||
| ~AlsaPortAndCallback() | |||
| { | |||
| enableCallback (false); | |||
| port.deletePort(); | |||
| } | |||
| void enableCallback (bool enable) | |||
| { | |||
| if (callbackEnabled != enable) | |||
| { | |||
| callbackEnabled = enable; | |||
| if (enable) | |||
| port.client->registerCallback (this); | |||
| else | |||
| port.client->unregisterCallback (this); | |||
| } | |||
| } | |||
| void handleIncomingMidiMessage (const MidiMessage& message) const | |||
| { | |||
| callback->handleIncomingMidiMessage (midiInput, message); | |||
| } | |||
| private: | |||
| AlsaPort port; | |||
| MidiInput* midiInput; | |||
| MidiInputCallback* callback; | |||
| bool callbackEnabled; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AlsaPortAndCallback) | |||
| }; | |||
| void AlsaClient::handleIncomingMidiMessage (const MidiMessage& message, int port) | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| if (AlsaPortAndCallback* const cb = activeCallbacks[port]) | |||
| cb->handleIncomingMidiMessage (message); | |||
| } | |||
| //============================================================================== | |||
| static AlsaPort iterateMidiClient (const AlsaClient::Ptr& seq, | |||
| snd_seq_client_info_t* clientInfo, | |||
| const bool forInput, | |||
| StringArray& deviceNamesFound, | |||
| const int deviceIndexToOpen) | |||
| { | |||
| AlsaPort port; | |||
| snd_seq_t* seqHandle = seq->get(); | |||
| snd_seq_port_info_t* portInfo = nullptr; | |||
| if (snd_seq_port_info_malloc (&portInfo) == 0) | |||
| { | |||
| int numPorts = snd_seq_client_info_get_num_ports (clientInfo); | |||
| const int client = snd_seq_client_info_get_client (clientInfo); | |||
| snd_seq_port_info_set_client (portInfo, client); | |||
| snd_seq_port_info_set_port (portInfo, -1); | |||
| while (--numPorts >= 0) | |||
| { | |||
| if (snd_seq_query_next_port (seqHandle, portInfo) == 0 | |||
| && (snd_seq_port_info_get_capability (portInfo) & (forInput ? SND_SEQ_PORT_CAP_READ | |||
| : SND_SEQ_PORT_CAP_WRITE)) != 0) | |||
| { | |||
| deviceNamesFound.add (snd_seq_client_info_get_name (clientInfo)); | |||
| if (deviceNamesFound.size() == deviceIndexToOpen + 1) | |||
| { | |||
| const int sourcePort = snd_seq_port_info_get_port (portInfo); | |||
| const int sourceClient = snd_seq_client_info_get_client (clientInfo); | |||
| if (sourcePort != -1) | |||
| { | |||
| const String name (forInput ? JUCE_ALSA_MIDI_INPUT_NAME | |||
| : JUCE_ALSA_MIDI_OUTPUT_NAME); | |||
| seq->setName (name); | |||
| port.createPort (seq, name, forInput); | |||
| port.connectWith (sourceClient, sourcePort); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| snd_seq_port_info_free (portInfo); | |||
| } | |||
| return port; | |||
| } | |||
| static AlsaPort iterateMidiDevices (const bool forInput, | |||
| StringArray& deviceNamesFound, | |||
| const int deviceIndexToOpen) | |||
| { | |||
| AlsaPort port; | |||
| const AlsaClient::Ptr client (globalAlsaSequencer (forInput)); | |||
| if (snd_seq_t* const seqHandle = client->get()) | |||
| { | |||
| snd_seq_system_info_t* systemInfo = nullptr; | |||
| snd_seq_client_info_t* clientInfo = nullptr; | |||
| if (snd_seq_system_info_malloc (&systemInfo) == 0) | |||
| { | |||
| if (snd_seq_system_info (seqHandle, systemInfo) == 0 | |||
| && snd_seq_client_info_malloc (&clientInfo) == 0) | |||
| { | |||
| int numClients = snd_seq_system_info_get_cur_clients (systemInfo); | |||
| while (--numClients >= 0 && ! port.isValid()) | |||
| if (snd_seq_query_next_client (seqHandle, clientInfo) == 0) | |||
| port = iterateMidiClient (client, clientInfo, forInput, | |||
| deviceNamesFound, deviceIndexToOpen); | |||
| snd_seq_client_info_free (clientInfo); | |||
| } | |||
| snd_seq_system_info_free (systemInfo); | |||
| } | |||
| } | |||
| deviceNamesFound.appendNumbersToDuplicates (true, true); | |||
| return port; | |||
| } | |||
| AlsaPort createMidiDevice (const bool forInput, const String& deviceNameToOpen) | |||
| { | |||
| AlsaPort port; | |||
| AlsaClient::Ptr client (new AlsaClient (forInput)); | |||
| if (client->get()) | |||
| { | |||
| client->setName (deviceNameToOpen + (forInput ? " Input" : " Output")); | |||
| port.createPort (client, forInput ? "in" : "out", forInput); | |||
| } | |||
| return port; | |||
| } | |||
| //============================================================================== | |||
| class MidiOutputDevice | |||
| { | |||
| public: | |||
| MidiOutputDevice (MidiOutput* const output, const AlsaPort& p) | |||
| : midiOutput (output), port (p), | |||
| maxEventSize (16 * 1024) | |||
| { | |||
| jassert (port.isValid() && midiOutput != nullptr); | |||
| snd_midi_event_new (maxEventSize, &midiParser); | |||
| } | |||
| ~MidiOutputDevice() | |||
| { | |||
| snd_midi_event_free (midiParser); | |||
| port.deletePort(); | |||
| } | |||
| void sendMessageNow (const MidiMessage& message) | |||
| { | |||
| if (message.getRawDataSize() > maxEventSize) | |||
| { | |||
| maxEventSize = message.getRawDataSize(); | |||
| snd_midi_event_free (midiParser); | |||
| snd_midi_event_new (maxEventSize, &midiParser); | |||
| } | |||
| snd_seq_event_t event; | |||
| snd_seq_ev_clear (&event); | |||
| long numBytes = (long) message.getRawDataSize(); | |||
| const uint8* data = message.getRawData(); | |||
| snd_seq_t* seqHandle = port.client->get(); | |||
| while (numBytes > 0) | |||
| { | |||
| const long numSent = snd_midi_event_encode (midiParser, data, numBytes, &event); | |||
| if (numSent <= 0) | |||
| break; | |||
| numBytes -= numSent; | |||
| data += numSent; | |||
| snd_seq_ev_set_source (&event, 0); | |||
| snd_seq_ev_set_subs (&event); | |||
| snd_seq_ev_set_direct (&event); | |||
| snd_seq_event_output (seqHandle, &event); | |||
| } | |||
| snd_seq_drain_output (seqHandle); | |||
| snd_midi_event_reset_encode (midiParser); | |||
| } | |||
| private: | |||
| MidiOutput* const midiOutput; | |||
| AlsaPort port; | |||
| snd_midi_event_t* midiParser; | |||
| int maxEventSize; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiOutputDevice); | |||
| }; | |||
| } // namespace | |||
| StringArray MidiOutput::getDevices() | |||
| { | |||
| StringArray devices; | |||
| iterateMidiDevices (false, devices, -1); | |||
| return devices; | |||
| } | |||
| int MidiOutput::getDefaultDeviceIndex() | |||
| { | |||
| return 0; | |||
| } | |||
| MidiOutput* MidiOutput::openDevice (int deviceIndex) | |||
| { | |||
| MidiOutput* newDevice = nullptr; | |||
| StringArray devices; | |||
| AlsaPort port (iterateMidiDevices (false, devices, deviceIndex)); | |||
| if (port.isValid()) | |||
| { | |||
| newDevice = new MidiOutput(); | |||
| newDevice->internal = new MidiOutputDevice (newDevice, port); | |||
| } | |||
| return newDevice; | |||
| } | |||
| MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | |||
| { | |||
| MidiOutput* newDevice = nullptr; | |||
| AlsaPort port (createMidiDevice (false, deviceName)); | |||
| if (port.isValid()) | |||
| { | |||
| newDevice = new MidiOutput(); | |||
| newDevice->internal = new MidiOutputDevice (newDevice, port); | |||
| } | |||
| return newDevice; | |||
| } | |||
| MidiOutput::~MidiOutput() | |||
| { | |||
| delete static_cast <MidiOutputDevice*> (internal); | |||
| } | |||
| void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
| { | |||
| static_cast <MidiOutputDevice*> (internal)->sendMessageNow (message); | |||
| } | |||
| //============================================================================== | |||
| MidiInput::MidiInput (const String& nm) | |||
| : name (nm), internal (nullptr) | |||
| { | |||
| } | |||
| MidiInput::~MidiInput() | |||
| { | |||
| stop(); | |||
| delete static_cast <AlsaPortAndCallback*> (internal); | |||
| } | |||
| void MidiInput::start() | |||
| { | |||
| static_cast<AlsaPortAndCallback*> (internal)->enableCallback (true); | |||
| } | |||
| void MidiInput::stop() | |||
| { | |||
| static_cast<AlsaPortAndCallback*> (internal)->enableCallback (false); | |||
| } | |||
| int MidiInput::getDefaultDeviceIndex() | |||
| { | |||
| return 0; | |||
| } | |||
| StringArray MidiInput::getDevices() | |||
| { | |||
| StringArray devices; | |||
| iterateMidiDevices (true, devices, -1); | |||
| return devices; | |||
| } | |||
| MidiInput* MidiInput::openDevice (int deviceIndex, MidiInputCallback* callback) | |||
| { | |||
| MidiInput* newDevice = nullptr; | |||
| StringArray devices; | |||
| AlsaPort port (iterateMidiDevices (true, devices, deviceIndex)); | |||
| if (port.isValid()) | |||
| { | |||
| newDevice = new MidiInput (devices [deviceIndex]); | |||
| newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback); | |||
| } | |||
| return newDevice; | |||
| } | |||
| MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) | |||
| { | |||
| MidiInput* newDevice = nullptr; | |||
| AlsaPort port (createMidiDevice (true, deviceName)); | |||
| if (port.isValid()) | |||
| { | |||
| newDevice = new MidiInput (deviceName); | |||
| newDevice->internal = new AlsaPortAndCallback (port, newDevice, callback); | |||
| } | |||
| return newDevice; | |||
| } | |||
| //============================================================================== | |||
| #else | |||
| // (These are just stub functions if ALSA is unavailable...) | |||
| StringArray MidiOutput::getDevices() { return StringArray(); } | |||
| int MidiOutput::getDefaultDeviceIndex() { return 0; } | |||
| MidiOutput* MidiOutput::openDevice (int) { return nullptr; } | |||
| MidiOutput* MidiOutput::createNewDevice (const String&) { return nullptr; } | |||
| MidiOutput::~MidiOutput() {} | |||
| void MidiOutput::sendMessageNow (const MidiMessage&) {} | |||
| MidiInput::MidiInput (const String& nm) : name (nm), internal (nullptr) {} | |||
| MidiInput::~MidiInput() {} | |||
| void MidiInput::start() {} | |||
| void MidiInput::stop() {} | |||
| int MidiInput::getDefaultDeviceIndex() { return 0; } | |||
| StringArray MidiInput::getDevices() { return StringArray(); } | |||
| MidiInput* MidiInput::openDevice (int, MidiInputCallback*) { return nullptr; } | |||
| MidiInput* MidiInput::createNewDevice (const String&, MidiInputCallback*) { return nullptr; } | |||
| #endif | |||
| @@ -0,0 +1,491 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| const int kilobytesPerSecond1x = 176; | |||
| struct AudioTrackProducerClass : public ObjCClass <NSObject> | |||
| { | |||
| AudioTrackProducerClass() : ObjCClass <NSObject> ("JUCEAudioTrackProducer_") | |||
| { | |||
| addIvar<AudioSourceHolder*> ("source"); | |||
| addMethod (@selector (initWithAudioSourceHolder:), initWithAudioSourceHolder, "@@:^v"); | |||
| addMethod (@selector (cleanupTrackAfterBurn:), cleanupTrackAfterBurn, "v@:@"); | |||
| addMethod (@selector (cleanupTrackAfterVerification:), cleanupTrackAfterVerification, "c@:@"); | |||
| addMethod (@selector (estimateLengthOfTrack:), estimateLengthOfTrack, "Q@:@"); | |||
| addMethod (@selector (prepareTrack:forBurn:toMedia:), prepareTrack, "c@:@@@"); | |||
| addMethod (@selector (prepareTrackForVerification:), prepareTrackForVerification, "c@:@"); | |||
| addMethod (@selector (produceDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), | |||
| produceDataForTrack, "I@:@^cIQI^I"); | |||
| addMethod (@selector (producePreGapForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), | |||
| produceDataForTrack, "I@:@^cIQI^I"); | |||
| addMethod (@selector (verifyDataForTrack:intoBuffer:length:atAddress:blockSize:ioFlags:), | |||
| produceDataForTrack, "I@:@^cIQI^I"); | |||
| registerClass(); | |||
| } | |||
| struct AudioSourceHolder | |||
| { | |||
| AudioSourceHolder (AudioSource* source_, int numFrames) | |||
| : source (source_), readPosition (0), lengthInFrames (numFrames) | |||
| { | |||
| } | |||
| ~AudioSourceHolder() | |||
| { | |||
| if (source != nullptr) | |||
| source->releaseResources(); | |||
| } | |||
| ScopedPointer<AudioSource> source; | |||
| int readPosition, lengthInFrames; | |||
| }; | |||
| private: | |||
| static id initWithAudioSourceHolder (id self, SEL, AudioSourceHolder* source) | |||
| { | |||
| self = sendSuperclassMessage (self, @selector (init)); | |||
| object_setInstanceVariable (self, "source", source); | |||
| return self; | |||
| } | |||
| static AudioSourceHolder* getSource (id self) | |||
| { | |||
| return getIvar<AudioSourceHolder*> (self, "source"); | |||
| } | |||
| static void dealloc (id self, SEL) | |||
| { | |||
| delete getSource (self); | |||
| sendSuperclassMessage (self, @selector (dealloc)); | |||
| } | |||
| static void cleanupTrackAfterBurn (id self, SEL, DRTrack*) {} | |||
| static BOOL cleanupTrackAfterVerification (id self, SEL, DRTrack*) { return true; } | |||
| static uint64_t estimateLengthOfTrack (id self, SEL, DRTrack*) | |||
| { | |||
| return getSource (self)->lengthInFrames; | |||
| } | |||
| static BOOL prepareTrack (id self, SEL, DRTrack*, DRBurn*, NSDictionary*) | |||
| { | |||
| if (AudioSourceHolder* const source = getSource (self)) | |||
| { | |||
| source->source->prepareToPlay (44100 / 75, 44100); | |||
| source->readPosition = 0; | |||
| } | |||
| return true; | |||
| } | |||
| static BOOL prepareTrackForVerification (id self, SEL, DRTrack*) | |||
| { | |||
| if (AudioSourceHolder* const source = getSource (self)) | |||
| source->source->prepareToPlay (44100 / 75, 44100); | |||
| return true; | |||
| } | |||
| static uint32_t produceDataForTrack (id self, SEL, DRTrack*, char* buffer, | |||
| uint32_t bufferLength, uint64_t /*address*/, | |||
| uint32_t /*blockSize*/, uint32_t* /*flags*/) | |||
| { | |||
| if (AudioSourceHolder* const source = getSource (self)) | |||
| { | |||
| const int numSamples = jmin ((int) bufferLength / 4, | |||
| (source->lengthInFrames * (44100 / 75)) - source->readPosition); | |||
| if (numSamples > 0) | |||
| { | |||
| AudioSampleBuffer tempBuffer (2, numSamples); | |||
| AudioSourceChannelInfo info (tempBuffer); | |||
| source->source->getNextAudioBlock (info); | |||
| typedef AudioData::Pointer <AudioData::Int16, | |||
| AudioData::LittleEndian, | |||
| AudioData::Interleaved, | |||
| AudioData::NonConst> CDSampleFormat; | |||
| typedef AudioData::Pointer <AudioData::Float32, | |||
| AudioData::NativeEndian, | |||
| AudioData::NonInterleaved, | |||
| AudioData::Const> SourceSampleFormat; | |||
| CDSampleFormat left (buffer, 2); | |||
| left.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (0)), numSamples); | |||
| CDSampleFormat right (buffer + 2, 2); | |||
| right.convertSamples (SourceSampleFormat (tempBuffer.getSampleData (1)), numSamples); | |||
| source->readPosition += numSamples; | |||
| } | |||
| return numSamples * 4; | |||
| } | |||
| return 0; | |||
| } | |||
| static uint32_t producePreGapForTrack (id self, SEL, DRTrack*, char* buffer, | |||
| uint32_t bufferLength, uint64_t /*address*/, | |||
| uint32_t /*blockSize*/, uint32_t* /*flags*/) | |||
| { | |||
| zeromem (buffer, bufferLength); | |||
| return bufferLength; | |||
| } | |||
| static BOOL verifyDataForTrack (id self, SEL, DRTrack*, const char*, | |||
| uint32_t /*bufferLength*/, uint64_t /*address*/, | |||
| uint32_t /*blockSize*/, uint32_t* /*flags*/) | |||
| { | |||
| return true; | |||
| } | |||
| }; | |||
| struct OpenDiskDevice | |||
| { | |||
| OpenDiskDevice (DRDevice* device_) | |||
| : device (device_), | |||
| tracks ([[NSMutableArray alloc] init]), | |||
| underrunProtection (true) | |||
| { | |||
| } | |||
| ~OpenDiskDevice() | |||
| { | |||
| [tracks release]; | |||
| } | |||
| void addSourceTrack (AudioSource* source, int numSamples) | |||
| { | |||
| if (source != nullptr) | |||
| { | |||
| const int numFrames = (numSamples + 587) / 588; | |||
| static AudioTrackProducerClass cls; | |||
| NSObject* producer = [cls.createInstance() performSelector: @selector (initWithAudioSourceHolder:) | |||
| withObject: (id) new AudioTrackProducerClass::AudioSourceHolder (source, numFrames)]; | |||
| DRTrack* track = [[DRTrack alloc] initWithProducer: producer]; | |||
| { | |||
| NSMutableDictionary* p = [[track properties] mutableCopy]; | |||
| [p setObject: [DRMSF msfWithFrames: numFrames] forKey: DRTrackLengthKey]; | |||
| [p setObject: [NSNumber numberWithUnsignedShort: 2352] forKey: DRBlockSizeKey]; | |||
| [p setObject: [NSNumber numberWithInt: 0] forKey: DRDataFormKey]; | |||
| [p setObject: [NSNumber numberWithInt: 0] forKey: DRBlockTypeKey]; | |||
| [p setObject: [NSNumber numberWithInt: 0] forKey: DRTrackModeKey]; | |||
| [p setObject: [NSNumber numberWithInt: 0] forKey: DRSessionFormatKey]; | |||
| [track setProperties: p]; | |||
| [p release]; | |||
| } | |||
| [tracks addObject: track]; | |||
| [track release]; | |||
| [producer release]; | |||
| } | |||
| } | |||
| String burn (AudioCDBurner::BurnProgressListener* listener, | |||
| bool shouldEject, bool peformFakeBurnForTesting, int burnSpeed) | |||
| { | |||
| DRBurn* burn = [DRBurn burnForDevice: device]; | |||
| if (! [device acquireExclusiveAccess]) | |||
| return "Couldn't open or write to the CD device"; | |||
| [device acquireMediaReservation]; | |||
| NSMutableDictionary* d = [[burn properties] mutableCopy]; | |||
| [d autorelease]; | |||
| [d setObject: [NSNumber numberWithBool: peformFakeBurnForTesting] forKey: DRBurnTestingKey]; | |||
| [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnVerifyDiscKey]; | |||
| [d setObject: (shouldEject ? DRBurnCompletionActionEject : DRBurnCompletionActionMount) forKey: DRBurnCompletionActionKey]; | |||
| if (burnSpeed > 0) | |||
| [d setObject: [NSNumber numberWithFloat: burnSpeed * kilobytesPerSecond1x] forKey: DRBurnRequestedSpeedKey]; | |||
| if (! underrunProtection) | |||
| [d setObject: [NSNumber numberWithBool: false] forKey: DRBurnUnderrunProtectionKey]; | |||
| [burn setProperties: d]; | |||
| [burn writeLayout: tracks]; | |||
| for (;;) | |||
| { | |||
| Thread::sleep (300); | |||
| float progress = [[[burn status] objectForKey: DRStatusPercentCompleteKey] floatValue]; | |||
| if (listener != nullptr && listener->audioCDBurnProgress (progress)) | |||
| { | |||
| [burn abort]; | |||
| return "User cancelled the write operation"; | |||
| } | |||
| if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateFailed]) | |||
| return "Write operation failed"; | |||
| if ([[[burn status] objectForKey: DRStatusStateKey] isEqualTo: DRStatusStateDone]) | |||
| break; | |||
| NSString* err = (NSString*) [[[burn status] objectForKey: DRErrorStatusKey] | |||
| objectForKey: DRErrorStatusErrorStringKey]; | |||
| if ([err length] > 0) | |||
| return CharPointer_UTF8 ([err UTF8String]); | |||
| } | |||
| [device releaseMediaReservation]; | |||
| [device releaseExclusiveAccess]; | |||
| return String::empty; | |||
| } | |||
| DRDevice* device; | |||
| NSMutableArray* tracks; | |||
| bool underrunProtection; | |||
| }; | |||
| //============================================================================== | |||
| class AudioCDBurner::Pimpl : public Timer | |||
| { | |||
| public: | |||
| Pimpl (AudioCDBurner& owner_, const int deviceIndex) | |||
| : device (0), owner (owner_) | |||
| { | |||
| DRDevice* dev = [[DRDevice devices] objectAtIndex: deviceIndex]; | |||
| if (dev != nil) | |||
| { | |||
| device = new OpenDiskDevice (dev); | |||
| lastState = getDiskState(); | |||
| startTimer (1000); | |||
| } | |||
| } | |||
| ~Pimpl() | |||
| { | |||
| stopTimer(); | |||
| } | |||
| void timerCallback() override | |||
| { | |||
| const DiskState state = getDiskState(); | |||
| if (state != lastState) | |||
| { | |||
| lastState = state; | |||
| owner.sendChangeMessage(); | |||
| } | |||
| } | |||
| DiskState getDiskState() const | |||
| { | |||
| if ([device->device isValid]) | |||
| { | |||
| NSDictionary* status = [device->device status]; | |||
| NSString* state = [status objectForKey: DRDeviceMediaStateKey]; | |||
| if ([state isEqualTo: DRDeviceMediaStateNone]) | |||
| { | |||
| if ([[status objectForKey: DRDeviceIsTrayOpenKey] boolValue]) | |||
| return trayOpen; | |||
| return noDisc; | |||
| } | |||
| if ([state isEqualTo: DRDeviceMediaStateMediaPresent]) | |||
| { | |||
| if ([[[status objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceMediaBlocksFreeKey] intValue] > 0) | |||
| return writableDiskPresent; | |||
| else | |||
| return readOnlyDiskPresent; | |||
| } | |||
| } | |||
| return unknown; | |||
| } | |||
| bool openTray() { return [device->device isValid] && [device->device ejectMedia]; } | |||
| Array<int> getAvailableWriteSpeeds() const | |||
| { | |||
| Array<int> results; | |||
| if ([device->device isValid]) | |||
| { | |||
| NSArray* speeds = [[[device->device status] objectForKey: DRDeviceMediaInfoKey] objectForKey: DRDeviceBurnSpeedsKey]; | |||
| for (unsigned int i = 0; i < [speeds count]; ++i) | |||
| { | |||
| const int kbPerSec = [[speeds objectAtIndex: i] intValue]; | |||
| results.add (kbPerSec / kilobytesPerSecond1x); | |||
| } | |||
| } | |||
| return results; | |||
| } | |||
| bool setBufferUnderrunProtection (const bool shouldBeEnabled) | |||
| { | |||
| if ([device->device isValid]) | |||
| { | |||
| device->underrunProtection = shouldBeEnabled; | |||
| return shouldBeEnabled && [[[device->device status] objectForKey: DRDeviceCanUnderrunProtectCDKey] boolValue]; | |||
| } | |||
| return false; | |||
| } | |||
| int getNumAvailableAudioBlocks() const | |||
| { | |||
| return [[[[device->device status] objectForKey: DRDeviceMediaInfoKey] | |||
| objectForKey: DRDeviceMediaBlocksFreeKey] intValue]; | |||
| } | |||
| ScopedPointer<OpenDiskDevice> device; | |||
| private: | |||
| DiskState lastState; | |||
| AudioCDBurner& owner; | |||
| }; | |||
| //============================================================================== | |||
| AudioCDBurner::AudioCDBurner (const int deviceIndex) | |||
| { | |||
| pimpl = new Pimpl (*this, deviceIndex); | |||
| } | |||
| AudioCDBurner::~AudioCDBurner() | |||
| { | |||
| } | |||
| AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) | |||
| { | |||
| ScopedPointer <AudioCDBurner> b (new AudioCDBurner (deviceIndex)); | |||
| if (b->pimpl->device == nil) | |||
| b = 0; | |||
| return b.release(); | |||
| } | |||
| namespace | |||
| { | |||
| NSArray* findDiskBurnerDevices() | |||
| { | |||
| NSMutableArray* results = [NSMutableArray array]; | |||
| NSArray* devs = [DRDevice devices]; | |||
| for (int i = 0; i < [devs count]; ++i) | |||
| { | |||
| NSDictionary* dic = [[devs objectAtIndex: i] info]; | |||
| NSString* name = [dic valueForKey: DRDeviceProductNameKey]; | |||
| if (name != nil) | |||
| [results addObject: name]; | |||
| } | |||
| return results; | |||
| } | |||
| } | |||
| StringArray AudioCDBurner::findAvailableDevices() | |||
| { | |||
| NSArray* names = findDiskBurnerDevices(); | |||
| StringArray s; | |||
| for (unsigned int i = 0; i < [names count]; ++i) | |||
| s.add (CharPointer_UTF8 ([[names objectAtIndex: i] UTF8String])); | |||
| return s; | |||
| } | |||
| AudioCDBurner::DiskState AudioCDBurner::getDiskState() const | |||
| { | |||
| return pimpl->getDiskState(); | |||
| } | |||
| bool AudioCDBurner::isDiskPresent() const | |||
| { | |||
| return getDiskState() == writableDiskPresent; | |||
| } | |||
| bool AudioCDBurner::openTray() | |||
| { | |||
| return pimpl->openTray(); | |||
| } | |||
| AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds) | |||
| { | |||
| const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds; | |||
| DiskState oldState = getDiskState(); | |||
| DiskState newState = oldState; | |||
| while (newState == oldState && Time::currentTimeMillis() < timeout) | |||
| { | |||
| newState = getDiskState(); | |||
| Thread::sleep (100); | |||
| } | |||
| return newState; | |||
| } | |||
| Array<int> AudioCDBurner::getAvailableWriteSpeeds() const | |||
| { | |||
| return pimpl->getAvailableWriteSpeeds(); | |||
| } | |||
| bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled) | |||
| { | |||
| return pimpl->setBufferUnderrunProtection (shouldBeEnabled); | |||
| } | |||
| int AudioCDBurner::getNumAvailableAudioBlocks() const | |||
| { | |||
| return pimpl->getNumAvailableAudioBlocks(); | |||
| } | |||
| bool AudioCDBurner::addAudioTrack (AudioSource* source, int numSamps) | |||
| { | |||
| if ([pimpl->device->device isValid]) | |||
| { | |||
| pimpl->device->addSourceTrack (source, numSamps); | |||
| return true; | |||
| } | |||
| return false; | |||
| } | |||
| String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, | |||
| bool ejectDiscAfterwards, | |||
| bool performFakeBurnForTesting, | |||
| int writeSpeed) | |||
| { | |||
| if ([pimpl->device->device isValid]) | |||
| return pimpl->device->burn (listener, ejectDiscAfterwards, performFakeBurnForTesting, writeSpeed); | |||
| return "Couldn't open or write to the CD device"; | |||
| } | |||
| @@ -0,0 +1,261 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| namespace CDReaderHelpers | |||
| { | |||
| inline const XmlElement* getElementForKey (const XmlElement& xml, const String& key) | |||
| { | |||
| forEachXmlChildElementWithTagName (xml, child, "key") | |||
| if (child->getAllSubText().trim() == key) | |||
| return child->getNextElement(); | |||
| return nullptr; | |||
| } | |||
| static int getIntValueForKey (const XmlElement& xml, const String& key, int defaultValue = -1) | |||
| { | |||
| const XmlElement* const block = getElementForKey (xml, key); | |||
| return block != nullptr ? block->getAllSubText().trim().getIntValue() : defaultValue; | |||
| } | |||
| // Get the track offsets for a CD given an XmlElement representing its TOC.Plist. | |||
| // Returns NULL on success, otherwise a const char* representing an error. | |||
| static const char* getTrackOffsets (XmlDocument& xmlDocument, Array<int>& offsets) | |||
| { | |||
| const ScopedPointer<XmlElement> xml (xmlDocument.getDocumentElement()); | |||
| if (xml == nullptr) | |||
| return "Couldn't parse XML in file"; | |||
| const XmlElement* const dict = xml->getChildByName ("dict"); | |||
| if (dict == nullptr) | |||
| return "Couldn't get top level dictionary"; | |||
| const XmlElement* const sessions = getElementForKey (*dict, "Sessions"); | |||
| if (sessions == nullptr) | |||
| return "Couldn't find sessions key"; | |||
| const XmlElement* const session = sessions->getFirstChildElement(); | |||
| if (session == nullptr) | |||
| return "Couldn't find first session"; | |||
| const int leadOut = getIntValueForKey (*session, "Leadout Block"); | |||
| if (leadOut < 0) | |||
| return "Couldn't find Leadout Block"; | |||
| const XmlElement* const trackArray = getElementForKey (*session, "Track Array"); | |||
| if (trackArray == nullptr) | |||
| return "Couldn't find Track Array"; | |||
| forEachXmlChildElement (*trackArray, track) | |||
| { | |||
| const int trackValue = getIntValueForKey (*track, "Start Block"); | |||
| if (trackValue < 0) | |||
| return "Couldn't find Start Block in the track"; | |||
| offsets.add (trackValue * AudioCDReader::samplesPerFrame - 88200); | |||
| } | |||
| offsets.add (leadOut * AudioCDReader::samplesPerFrame - 88200); | |||
| return nullptr; | |||
| } | |||
| static void findDevices (Array<File>& cds) | |||
| { | |||
| File volumes ("/Volumes"); | |||
| volumes.findChildFiles (cds, File::findDirectories, false); | |||
| for (int i = cds.size(); --i >= 0;) | |||
| if (! cds.getReference(i).getChildFile (".TOC.plist").exists()) | |||
| cds.remove (i); | |||
| } | |||
| struct TrackSorter | |||
| { | |||
| static int getCDTrackNumber (const File& file) | |||
| { | |||
| return file.getFileName().initialSectionContainingOnly ("0123456789").getIntValue(); | |||
| } | |||
| static int compareElements (const File& first, const File& second) | |||
| { | |||
| const int firstTrack = getCDTrackNumber (first); | |||
| const int secondTrack = getCDTrackNumber (second); | |||
| jassert (firstTrack > 0 && secondTrack > 0); | |||
| return firstTrack - secondTrack; | |||
| } | |||
| }; | |||
| } | |||
| //============================================================================== | |||
| StringArray AudioCDReader::getAvailableCDNames() | |||
| { | |||
| Array<File> cds; | |||
| CDReaderHelpers::findDevices (cds); | |||
| StringArray names; | |||
| for (int i = 0; i < cds.size(); ++i) | |||
| names.add (cds.getReference(i).getFileName()); | |||
| return names; | |||
| } | |||
| AudioCDReader* AudioCDReader::createReaderForCD (const int index) | |||
| { | |||
| Array<File> cds; | |||
| CDReaderHelpers::findDevices (cds); | |||
| if (cds[index].exists()) | |||
| return new AudioCDReader (cds[index]); | |||
| return nullptr; | |||
| } | |||
| AudioCDReader::AudioCDReader (const File& volume) | |||
| : AudioFormatReader (0, "CD Audio"), | |||
| volumeDir (volume), | |||
| currentReaderTrack (-1), | |||
| reader (0) | |||
| { | |||
| sampleRate = 44100.0; | |||
| bitsPerSample = 16; | |||
| numChannels = 2; | |||
| usesFloatingPointData = false; | |||
| refreshTrackLengths(); | |||
| } | |||
| AudioCDReader::~AudioCDReader() | |||
| { | |||
| } | |||
| void AudioCDReader::refreshTrackLengths() | |||
| { | |||
| tracks.clear(); | |||
| trackStartSamples.clear(); | |||
| lengthInSamples = 0; | |||
| volumeDir.findChildFiles (tracks, File::findFiles | File::ignoreHiddenFiles, false, "*.aiff"); | |||
| CDReaderHelpers::TrackSorter sorter; | |||
| tracks.sort (sorter); | |||
| const File toc (volumeDir.getChildFile (".TOC.plist")); | |||
| if (toc.exists()) | |||
| { | |||
| XmlDocument doc (toc); | |||
| const char* error = CDReaderHelpers::getTrackOffsets (doc, trackStartSamples); | |||
| (void) error; // could be logged.. | |||
| lengthInSamples = trackStartSamples.getLast() - trackStartSamples.getFirst(); | |||
| } | |||
| } | |||
| bool AudioCDReader::readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) | |||
| { | |||
| while (numSamples > 0) | |||
| { | |||
| int track = -1; | |||
| for (int i = 0; i < trackStartSamples.size() - 1; ++i) | |||
| { | |||
| if (startSampleInFile < trackStartSamples.getUnchecked (i + 1)) | |||
| { | |||
| track = i; | |||
| break; | |||
| } | |||
| } | |||
| if (track < 0) | |||
| return false; | |||
| if (track != currentReaderTrack) | |||
| { | |||
| reader = nullptr; | |||
| if (FileInputStream* const in = tracks [track].createInputStream()) | |||
| { | |||
| BufferedInputStream* const bin = new BufferedInputStream (in, 65536, true); | |||
| AiffAudioFormat format; | |||
| reader = format.createReaderFor (bin, true); | |||
| if (reader == nullptr) | |||
| currentReaderTrack = -1; | |||
| else | |||
| currentReaderTrack = track; | |||
| } | |||
| } | |||
| if (reader == nullptr) | |||
| return false; | |||
| const int startPos = (int) (startSampleInFile - trackStartSamples.getUnchecked (track)); | |||
| const int numAvailable = (int) jmin ((int64) numSamples, reader->lengthInSamples - startPos); | |||
| reader->readSamples (destSamples, numDestChannels, startOffsetInDestBuffer, startPos, numAvailable); | |||
| numSamples -= numAvailable; | |||
| startSampleInFile += numAvailable; | |||
| } | |||
| return true; | |||
| } | |||
| bool AudioCDReader::isCDStillPresent() const | |||
| { | |||
| return volumeDir.exists(); | |||
| } | |||
| void AudioCDReader::ejectDisk() | |||
| { | |||
| JUCE_AUTORELEASEPOOL | |||
| { | |||
| [[NSWorkspace sharedWorkspace] unmountAndEjectDeviceAtPath: juceStringToNS (volumeDir.getFullPathName())]; | |||
| } | |||
| } | |||
| bool AudioCDReader::isTrackAudio (int trackNum) const | |||
| { | |||
| return tracks [trackNum].hasFileExtension (".aiff"); | |||
| } | |||
| void AudioCDReader::enableIndexScanning (bool) | |||
| { | |||
| // any way to do this on a Mac?? | |||
| } | |||
| int AudioCDReader::getLastIndex() const | |||
| { | |||
| return 0; | |||
| } | |||
| Array<int> AudioCDReader::findIndexesInTrack (const int /*trackNumber*/) | |||
| { | |||
| return Array<int>(); | |||
| } | |||
| @@ -0,0 +1,528 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_LOG_COREMIDI_ERRORS | |||
| #define JUCE_LOG_COREMIDI_ERRORS 1 | |||
| #endif | |||
| namespace CoreMidiHelpers | |||
| { | |||
| static bool checkError (const OSStatus err, const int lineNum) | |||
| { | |||
| if (err == noErr) | |||
| return true; | |||
| #if JUCE_LOG_COREMIDI_ERRORS | |||
| Logger::writeToLog ("CoreMIDI error: " + String (lineNum) + " - " + String::toHexString ((int) err)); | |||
| #endif | |||
| (void) lineNum; | |||
| return false; | |||
| } | |||
| #undef CHECK_ERROR | |||
| #define CHECK_ERROR(a) CoreMidiHelpers::checkError (a, __LINE__) | |||
| //============================================================================== | |||
| static String getMidiObjectName (MIDIObjectRef entity) | |||
| { | |||
| String result; | |||
| CFStringRef str = nullptr; | |||
| MIDIObjectGetStringProperty (entity, kMIDIPropertyName, &str); | |||
| if (str != nullptr) | |||
| { | |||
| result = String::fromCFString (str); | |||
| CFRelease (str); | |||
| } | |||
| return result; | |||
| } | |||
| static String getEndpointName (MIDIEndpointRef endpoint, bool isExternal) | |||
| { | |||
| String result (getMidiObjectName (endpoint)); | |||
| MIDIEntityRef entity = 0; // NB: don't attempt to use nullptr for refs - it fails in some types of build. | |||
| MIDIEndpointGetEntity (endpoint, &entity); | |||
| if (entity == 0) | |||
| return result; // probably virtual | |||
| if (result.isEmpty()) | |||
| result = getMidiObjectName (entity); // endpoint name is empty - try the entity | |||
| // now consider the device's name | |||
| MIDIDeviceRef device = 0; | |||
| MIDIEntityGetDevice (entity, &device); | |||
| if (device != 0) | |||
| { | |||
| const String deviceName (getMidiObjectName (device)); | |||
| if (deviceName.isNotEmpty()) | |||
| { | |||
| // if an external device has only one entity, throw away | |||
| // the endpoint name and just use the device name | |||
| if (isExternal && MIDIDeviceGetNumberOfEntities (device) < 2) | |||
| { | |||
| result = deviceName; | |||
| } | |||
| else if (! result.startsWithIgnoreCase (deviceName)) | |||
| { | |||
| // prepend the device name to the entity name | |||
| result = (deviceName + " " + result).trimEnd(); | |||
| } | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| static String getConnectedEndpointName (MIDIEndpointRef endpoint) | |||
| { | |||
| String result; | |||
| // Does the endpoint have connections? | |||
| CFDataRef connections = nullptr; | |||
| int numConnections = 0; | |||
| MIDIObjectGetDataProperty (endpoint, kMIDIPropertyConnectionUniqueID, &connections); | |||
| if (connections != nullptr) | |||
| { | |||
| numConnections = ((int) CFDataGetLength (connections)) / (int) sizeof (MIDIUniqueID); | |||
| if (numConnections > 0) | |||
| { | |||
| const SInt32* pid = reinterpret_cast <const SInt32*> (CFDataGetBytePtr (connections)); | |||
| for (int i = 0; i < numConnections; ++i, ++pid) | |||
| { | |||
| MIDIUniqueID uid = (MIDIUniqueID) ByteOrder::swapIfLittleEndian ((uint32) *pid); | |||
| MIDIObjectRef connObject; | |||
| MIDIObjectType connObjectType; | |||
| OSStatus err = MIDIObjectFindByUniqueID (uid, &connObject, &connObjectType); | |||
| if (err == noErr) | |||
| { | |||
| String s; | |||
| if (connObjectType == kMIDIObjectType_ExternalSource | |||
| || connObjectType == kMIDIObjectType_ExternalDestination) | |||
| { | |||
| // Connected to an external device's endpoint (10.3 and later). | |||
| s = getEndpointName (static_cast <MIDIEndpointRef> (connObject), true); | |||
| } | |||
| else | |||
| { | |||
| // Connected to an external device (10.2) (or something else, catch-all) | |||
| s = getMidiObjectName (connObject); | |||
| } | |||
| if (s.isNotEmpty()) | |||
| { | |||
| if (result.isNotEmpty()) | |||
| result += ", "; | |||
| result += s; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| CFRelease (connections); | |||
| } | |||
| if (result.isEmpty()) // Here, either the endpoint had no connections, or we failed to obtain names for them. | |||
| result = getEndpointName (endpoint, false); | |||
| return result; | |||
| } | |||
| static StringArray findDevices (const bool forInput) | |||
| { | |||
| const ItemCount num = forInput ? MIDIGetNumberOfSources() | |||
| : MIDIGetNumberOfDestinations(); | |||
| StringArray s; | |||
| for (ItemCount i = 0; i < num; ++i) | |||
| { | |||
| MIDIEndpointRef dest = forInput ? MIDIGetSource (i) | |||
| : MIDIGetDestination (i); | |||
| String name; | |||
| if (dest != 0) | |||
| name = getConnectedEndpointName (dest); | |||
| if (name.isEmpty()) | |||
| name = "<error>"; | |||
| s.add (name); | |||
| } | |||
| return s; | |||
| } | |||
| static void globalSystemChangeCallback (const MIDINotification*, void*) | |||
| { | |||
| // TODO.. Should pass-on this notification.. | |||
| } | |||
| static String getGlobalMidiClientName() | |||
| { | |||
| if (JUCEApplicationBase* const app = JUCEApplicationBase::getInstance()) | |||
| return app->getApplicationName(); | |||
| return "JUCE"; | |||
| } | |||
| static MIDIClientRef getGlobalMidiClient() | |||
| { | |||
| static MIDIClientRef globalMidiClient = 0; | |||
| if (globalMidiClient == 0) | |||
| { | |||
| // Since OSX 10.6, the MIDIClientCreate function will only work | |||
| // correctly when called from the message thread! | |||
| jassert (MessageManager::getInstance()->isThisTheMessageThread()); | |||
| CFStringRef name = getGlobalMidiClientName().toCFString(); | |||
| CHECK_ERROR (MIDIClientCreate (name, &globalSystemChangeCallback, nullptr, &globalMidiClient)); | |||
| CFRelease (name); | |||
| } | |||
| return globalMidiClient; | |||
| } | |||
| //============================================================================== | |||
| class MidiPortAndEndpoint | |||
| { | |||
| public: | |||
| MidiPortAndEndpoint (MIDIPortRef p, MIDIEndpointRef ep) | |||
| : port (p), endPoint (ep) | |||
| { | |||
| } | |||
| ~MidiPortAndEndpoint() | |||
| { | |||
| if (port != 0) | |||
| MIDIPortDispose (port); | |||
| if (port == 0 && endPoint != 0) // if port == nullptr, it means we created the endpoint, so it's safe to delete it | |||
| MIDIEndpointDispose (endPoint); | |||
| } | |||
| void send (const MIDIPacketList* const packets) | |||
| { | |||
| if (port != 0) | |||
| MIDISend (port, endPoint, packets); | |||
| else | |||
| MIDIReceived (endPoint, packets); | |||
| } | |||
| MIDIPortRef port; | |||
| MIDIEndpointRef endPoint; | |||
| }; | |||
| //============================================================================== | |||
| class MidiPortAndCallback; | |||
| CriticalSection callbackLock; | |||
| Array<MidiPortAndCallback*> activeCallbacks; | |||
| class MidiPortAndCallback | |||
| { | |||
| public: | |||
| MidiPortAndCallback (MidiInputCallback& cb) | |||
| : input (nullptr), active (false), callback (cb), concatenator (2048) | |||
| { | |||
| } | |||
| ~MidiPortAndCallback() | |||
| { | |||
| active = false; | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| activeCallbacks.removeFirstMatchingValue (this); | |||
| } | |||
| if (portAndEndpoint != 0 && portAndEndpoint->port != 0) | |||
| CHECK_ERROR (MIDIPortDisconnectSource (portAndEndpoint->port, portAndEndpoint->endPoint)); | |||
| } | |||
| void handlePackets (const MIDIPacketList* const pktlist) | |||
| { | |||
| const double time = Time::getMillisecondCounterHiRes() * 0.001; | |||
| const ScopedLock sl (callbackLock); | |||
| if (activeCallbacks.contains (this) && active) | |||
| { | |||
| const MIDIPacket* packet = &pktlist->packet[0]; | |||
| for (unsigned int i = 0; i < pktlist->numPackets; ++i) | |||
| { | |||
| concatenator.pushMidiData (packet->data, (int) packet->length, time, | |||
| input, callback); | |||
| packet = MIDIPacketNext (packet); | |||
| } | |||
| } | |||
| } | |||
| MidiInput* input; | |||
| ScopedPointer<MidiPortAndEndpoint> portAndEndpoint; | |||
| volatile bool active; | |||
| private: | |||
| MidiInputCallback& callback; | |||
| MidiDataConcatenator concatenator; | |||
| }; | |||
| static void midiInputProc (const MIDIPacketList* pktlist, void* readProcRefCon, void* /*srcConnRefCon*/) | |||
| { | |||
| static_cast <MidiPortAndCallback*> (readProcRefCon)->handlePackets (pktlist); | |||
| } | |||
| } | |||
| //============================================================================== | |||
| StringArray MidiOutput::getDevices() { return CoreMidiHelpers::findDevices (false); } | |||
| int MidiOutput::getDefaultDeviceIndex() { return 0; } | |||
| MidiOutput* MidiOutput::openDevice (int index) | |||
| { | |||
| MidiOutput* mo = nullptr; | |||
| if (isPositiveAndBelow (index, (int) MIDIGetNumberOfDestinations())) | |||
| { | |||
| MIDIEndpointRef endPoint = MIDIGetDestination ((ItemCount) index); | |||
| CFStringRef pname; | |||
| if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &pname))) | |||
| { | |||
| MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); | |||
| MIDIPortRef port; | |||
| if (client != 0 && CHECK_ERROR (MIDIOutputPortCreate (client, pname, &port))) | |||
| { | |||
| mo = new MidiOutput(); | |||
| mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (port, endPoint); | |||
| } | |||
| CFRelease (pname); | |||
| } | |||
| } | |||
| return mo; | |||
| } | |||
| MidiOutput* MidiOutput::createNewDevice (const String& deviceName) | |||
| { | |||
| MidiOutput* mo = nullptr; | |||
| MIDIClientRef client = CoreMidiHelpers::getGlobalMidiClient(); | |||
| MIDIEndpointRef endPoint; | |||
| CFStringRef name = deviceName.toCFString(); | |||
| if (client != 0 && CHECK_ERROR (MIDISourceCreate (client, name, &endPoint))) | |||
| { | |||
| mo = new MidiOutput(); | |||
| mo->internal = new CoreMidiHelpers::MidiPortAndEndpoint (0, endPoint); | |||
| } | |||
| CFRelease (name); | |||
| return mo; | |||
| } | |||
| MidiOutput::~MidiOutput() | |||
| { | |||
| delete static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal); | |||
| } | |||
| void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
| { | |||
| #if JUCE_IOS | |||
| const MIDITimeStamp timeStamp = mach_absolute_time(); | |||
| #else | |||
| const MIDITimeStamp timeStamp = AudioGetCurrentHostTime(); | |||
| #endif | |||
| HeapBlock <MIDIPacketList> allocatedPackets; | |||
| MIDIPacketList stackPacket; | |||
| MIDIPacketList* packetToSend = &stackPacket; | |||
| const size_t dataSize = (size_t) message.getRawDataSize(); | |||
| if (message.isSysEx()) | |||
| { | |||
| const int maxPacketSize = 256; | |||
| int pos = 0, bytesLeft = (int) dataSize; | |||
| const int numPackets = (bytesLeft + maxPacketSize - 1) / maxPacketSize; | |||
| allocatedPackets.malloc ((size_t) (32 * (size_t) numPackets + dataSize), 1); | |||
| packetToSend = allocatedPackets; | |||
| packetToSend->numPackets = (UInt32) numPackets; | |||
| MIDIPacket* p = packetToSend->packet; | |||
| for (int i = 0; i < numPackets; ++i) | |||
| { | |||
| p->timeStamp = timeStamp; | |||
| p->length = (UInt16) jmin (maxPacketSize, bytesLeft); | |||
| memcpy (p->data, message.getRawData() + pos, p->length); | |||
| pos += p->length; | |||
| bytesLeft -= p->length; | |||
| p = MIDIPacketNext (p); | |||
| } | |||
| } | |||
| else if (dataSize < 65536) // max packet size | |||
| { | |||
| const size_t stackCapacity = sizeof (stackPacket.packet->data); | |||
| if (dataSize > stackCapacity) | |||
| { | |||
| allocatedPackets.malloc ((sizeof (MIDIPacketList) - stackCapacity) + dataSize, 1); | |||
| packetToSend = allocatedPackets; | |||
| } | |||
| packetToSend->numPackets = 1; | |||
| MIDIPacket& p = *(packetToSend->packet); | |||
| p.timeStamp = timeStamp; | |||
| p.length = (UInt16) dataSize; | |||
| memcpy (p.data, message.getRawData(), dataSize); | |||
| } | |||
| else | |||
| { | |||
| jassertfalse; // packet too large to send! | |||
| return; | |||
| } | |||
| static_cast<CoreMidiHelpers::MidiPortAndEndpoint*> (internal)->send (packetToSend); | |||
| } | |||
| //============================================================================== | |||
| StringArray MidiInput::getDevices() { return CoreMidiHelpers::findDevices (true); } | |||
| int MidiInput::getDefaultDeviceIndex() { return 0; } | |||
| MidiInput* MidiInput::openDevice (int index, MidiInputCallback* callback) | |||
| { | |||
| jassert (callback != nullptr); | |||
| using namespace CoreMidiHelpers; | |||
| MidiInput* newInput = nullptr; | |||
| if (isPositiveAndBelow (index, (int) MIDIGetNumberOfSources())) | |||
| { | |||
| if (MIDIEndpointRef endPoint = MIDIGetSource ((ItemCount) index)) | |||
| { | |||
| CFStringRef name; | |||
| if (CHECK_ERROR (MIDIObjectGetStringProperty (endPoint, kMIDIPropertyName, &name))) | |||
| { | |||
| if (MIDIClientRef client = getGlobalMidiClient()) | |||
| { | |||
| MIDIPortRef port; | |||
| ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback)); | |||
| if (CHECK_ERROR (MIDIInputPortCreate (client, name, midiInputProc, mpc, &port))) | |||
| { | |||
| if (CHECK_ERROR (MIDIPortConnectSource (port, endPoint, nullptr))) | |||
| { | |||
| mpc->portAndEndpoint = new MidiPortAndEndpoint (port, endPoint); | |||
| newInput = new MidiInput (getDevices() [index]); | |||
| mpc->input = newInput; | |||
| newInput->internal = mpc; | |||
| const ScopedLock sl (callbackLock); | |||
| activeCallbacks.add (mpc.release()); | |||
| } | |||
| else | |||
| { | |||
| CHECK_ERROR (MIDIPortDispose (port)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| CFRelease (name); | |||
| } | |||
| } | |||
| return newInput; | |||
| } | |||
| MidiInput* MidiInput::createNewDevice (const String& deviceName, MidiInputCallback* callback) | |||
| { | |||
| jassert (callback != nullptr); | |||
| using namespace CoreMidiHelpers; | |||
| MidiInput* mi = nullptr; | |||
| if (MIDIClientRef client = getGlobalMidiClient()) | |||
| { | |||
| ScopedPointer <MidiPortAndCallback> mpc (new MidiPortAndCallback (*callback)); | |||
| mpc->active = false; | |||
| MIDIEndpointRef endPoint; | |||
| CFStringRef name = deviceName.toCFString(); | |||
| if (CHECK_ERROR (MIDIDestinationCreate (client, name, midiInputProc, mpc, &endPoint))) | |||
| { | |||
| mpc->portAndEndpoint = new MidiPortAndEndpoint (0, endPoint); | |||
| mi = new MidiInput (deviceName); | |||
| mpc->input = mi; | |||
| mi->internal = mpc; | |||
| const ScopedLock sl (callbackLock); | |||
| activeCallbacks.add (mpc.release()); | |||
| } | |||
| CFRelease (name); | |||
| } | |||
| return mi; | |||
| } | |||
| MidiInput::MidiInput (const String& nm) : name (nm) | |||
| { | |||
| } | |||
| MidiInput::~MidiInput() | |||
| { | |||
| delete static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal); | |||
| } | |||
| void MidiInput::start() | |||
| { | |||
| const ScopedLock sl (CoreMidiHelpers::callbackLock); | |||
| static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = true; | |||
| } | |||
| void MidiInput::stop() | |||
| { | |||
| const ScopedLock sl (CoreMidiHelpers::callbackLock); | |||
| static_cast<CoreMidiHelpers::MidiPortAndCallback*> (internal)->active = false; | |||
| } | |||
| #undef CHECK_ERROR | |||
| @@ -0,0 +1,411 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| namespace CDBurnerHelpers | |||
| { | |||
| IDiscRecorder* enumCDBurners (StringArray* list, int indexToOpen, IDiscMaster** master) | |||
| { | |||
| CoInitialize (0); | |||
| IDiscMaster* dm; | |||
| IDiscRecorder* result = nullptr; | |||
| if (SUCCEEDED (CoCreateInstance (CLSID_MSDiscMasterObj, 0, | |||
| CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, | |||
| IID_IDiscMaster, | |||
| (void**) &dm))) | |||
| { | |||
| if (SUCCEEDED (dm->Open())) | |||
| { | |||
| IEnumDiscRecorders* drEnum = nullptr; | |||
| if (SUCCEEDED (dm->EnumDiscRecorders (&drEnum))) | |||
| { | |||
| IDiscRecorder* dr = nullptr; | |||
| DWORD dummy; | |||
| int index = 0; | |||
| while (drEnum->Next (1, &dr, &dummy) == S_OK) | |||
| { | |||
| if (indexToOpen == index) | |||
| { | |||
| result = dr; | |||
| break; | |||
| } | |||
| else if (list != nullptr) | |||
| { | |||
| BSTR path; | |||
| if (SUCCEEDED (dr->GetPath (&path))) | |||
| list->add ((const WCHAR*) path); | |||
| } | |||
| ++index; | |||
| dr->Release(); | |||
| } | |||
| drEnum->Release(); | |||
| } | |||
| if (master == 0) | |||
| dm->Close(); | |||
| } | |||
| if (master != nullptr) | |||
| *master = dm; | |||
| else | |||
| dm->Release(); | |||
| } | |||
| return result; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| class AudioCDBurner::Pimpl : public ComBaseClassHelper <IDiscMasterProgressEvents>, | |||
| public Timer | |||
| { | |||
| public: | |||
| Pimpl (AudioCDBurner& owner_, IDiscMaster* discMaster_, IDiscRecorder* discRecorder_) | |||
| : owner (owner_), discMaster (discMaster_), discRecorder (discRecorder_), redbook (0), | |||
| listener (0), progress (0), shouldCancel (false) | |||
| { | |||
| HRESULT hr = discMaster->SetActiveDiscMasterFormat (IID_IRedbookDiscMaster, (void**) &redbook); | |||
| jassert (SUCCEEDED (hr)); | |||
| hr = discMaster->SetActiveDiscRecorder (discRecorder); | |||
| //jassert (SUCCEEDED (hr)); | |||
| lastState = getDiskState(); | |||
| startTimer (2000); | |||
| } | |||
| ~Pimpl() {} | |||
| void releaseObjects() | |||
| { | |||
| discRecorder->Close(); | |||
| if (redbook != nullptr) | |||
| redbook->Release(); | |||
| discRecorder->Release(); | |||
| discMaster->Release(); | |||
| Release(); | |||
| } | |||
| JUCE_COMRESULT QueryCancel (boolean* pbCancel) | |||
| { | |||
| if (listener != nullptr && ! shouldCancel) | |||
| shouldCancel = listener->audioCDBurnProgress (progress); | |||
| *pbCancel = shouldCancel; | |||
| return S_OK; | |||
| } | |||
| JUCE_COMRESULT NotifyBlockProgress (long nCompleted, long nTotal) | |||
| { | |||
| progress = nCompleted / (float) nTotal; | |||
| shouldCancel = listener != nullptr && listener->audioCDBurnProgress (progress); | |||
| return E_NOTIMPL; | |||
| } | |||
| JUCE_COMRESULT NotifyPnPActivity (void) { return E_NOTIMPL; } | |||
| JUCE_COMRESULT NotifyAddProgress (long /*nCompletedSteps*/, long /*nTotalSteps*/) { return E_NOTIMPL; } | |||
| JUCE_COMRESULT NotifyTrackProgress (long /*nCurrentTrack*/, long /*nTotalTracks*/) { return E_NOTIMPL; } | |||
| JUCE_COMRESULT NotifyPreparingBurn (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } | |||
| JUCE_COMRESULT NotifyClosingDisc (long /*nEstimatedSeconds*/) { return E_NOTIMPL; } | |||
| JUCE_COMRESULT NotifyBurnComplete (HRESULT /*status*/) { return E_NOTIMPL; } | |||
| JUCE_COMRESULT NotifyEraseComplete (HRESULT /*status*/) { return E_NOTIMPL; } | |||
| class ScopedDiscOpener | |||
| { | |||
| public: | |||
| ScopedDiscOpener (Pimpl& p) : pimpl (p) { pimpl.discRecorder->OpenExclusive(); } | |||
| ~ScopedDiscOpener() { pimpl.discRecorder->Close(); } | |||
| private: | |||
| Pimpl& pimpl; | |||
| JUCE_DECLARE_NON_COPYABLE (ScopedDiscOpener) | |||
| }; | |||
| DiskState getDiskState() | |||
| { | |||
| const ScopedDiscOpener opener (*this); | |||
| long type, flags; | |||
| HRESULT hr = discRecorder->QueryMediaType (&type, &flags); | |||
| if (FAILED (hr)) | |||
| return unknown; | |||
| if (type != 0 && (flags & MEDIA_WRITABLE) != 0) | |||
| return writableDiskPresent; | |||
| if (type == 0) | |||
| return noDisc; | |||
| return readOnlyDiskPresent; | |||
| } | |||
| int getIntProperty (const LPOLESTR name, const int defaultReturn) const | |||
| { | |||
| ComSmartPtr<IPropertyStorage> prop; | |||
| if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress()))) | |||
| return defaultReturn; | |||
| PROPSPEC iPropSpec; | |||
| iPropSpec.ulKind = PRSPEC_LPWSTR; | |||
| iPropSpec.lpwstr = name; | |||
| PROPVARIANT iPropVariant; | |||
| return FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant)) | |||
| ? defaultReturn : (int) iPropVariant.lVal; | |||
| } | |||
| bool setIntProperty (const LPOLESTR name, const int value) const | |||
| { | |||
| ComSmartPtr<IPropertyStorage> prop; | |||
| if (FAILED (discRecorder->GetRecorderProperties (prop.resetAndGetPointerAddress()))) | |||
| return false; | |||
| PROPSPEC iPropSpec; | |||
| iPropSpec.ulKind = PRSPEC_LPWSTR; | |||
| iPropSpec.lpwstr = name; | |||
| PROPVARIANT iPropVariant; | |||
| if (FAILED (prop->ReadMultiple (1, &iPropSpec, &iPropVariant))) | |||
| return false; | |||
| iPropVariant.lVal = (long) value; | |||
| return SUCCEEDED (prop->WriteMultiple (1, &iPropSpec, &iPropVariant, iPropVariant.vt)) | |||
| && SUCCEEDED (discRecorder->SetRecorderProperties (prop)); | |||
| } | |||
| void timerCallback() override | |||
| { | |||
| const DiskState state = getDiskState(); | |||
| if (state != lastState) | |||
| { | |||
| lastState = state; | |||
| owner.sendChangeMessage(); | |||
| } | |||
| } | |||
| AudioCDBurner& owner; | |||
| DiskState lastState; | |||
| IDiscMaster* discMaster; | |||
| IDiscRecorder* discRecorder; | |||
| IRedbookDiscMaster* redbook; | |||
| AudioCDBurner::BurnProgressListener* listener; | |||
| float progress; | |||
| bool shouldCancel; | |||
| }; | |||
| //============================================================================== | |||
| AudioCDBurner::AudioCDBurner (const int deviceIndex) | |||
| { | |||
| IDiscMaster* discMaster = nullptr; | |||
| IDiscRecorder* discRecorder = CDBurnerHelpers::enumCDBurners (0, deviceIndex, &discMaster); | |||
| if (discRecorder != nullptr) | |||
| pimpl = new Pimpl (*this, discMaster, discRecorder); | |||
| } | |||
| AudioCDBurner::~AudioCDBurner() | |||
| { | |||
| if (pimpl != nullptr) | |||
| pimpl.release()->releaseObjects(); | |||
| } | |||
| StringArray AudioCDBurner::findAvailableDevices() | |||
| { | |||
| StringArray devs; | |||
| CDBurnerHelpers::enumCDBurners (&devs, -1, 0); | |||
| return devs; | |||
| } | |||
| AudioCDBurner* AudioCDBurner::openDevice (const int deviceIndex) | |||
| { | |||
| ScopedPointer<AudioCDBurner> b (new AudioCDBurner (deviceIndex)); | |||
| if (b->pimpl == 0) | |||
| b = nullptr; | |||
| return b.release(); | |||
| } | |||
| AudioCDBurner::DiskState AudioCDBurner::getDiskState() const | |||
| { | |||
| return pimpl->getDiskState(); | |||
| } | |||
| bool AudioCDBurner::isDiskPresent() const | |||
| { | |||
| return getDiskState() == writableDiskPresent; | |||
| } | |||
| bool AudioCDBurner::openTray() | |||
| { | |||
| const Pimpl::ScopedDiscOpener opener (*pimpl); | |||
| return SUCCEEDED (pimpl->discRecorder->Eject()); | |||
| } | |||
| AudioCDBurner::DiskState AudioCDBurner::waitUntilStateChange (int timeOutMilliseconds) | |||
| { | |||
| const int64 timeout = Time::currentTimeMillis() + timeOutMilliseconds; | |||
| DiskState oldState = getDiskState(); | |||
| DiskState newState = oldState; | |||
| while (newState == oldState && Time::currentTimeMillis() < timeout) | |||
| { | |||
| newState = getDiskState(); | |||
| Thread::sleep (jmin (250, (int) (timeout - Time::currentTimeMillis()))); | |||
| } | |||
| return newState; | |||
| } | |||
| Array<int> AudioCDBurner::getAvailableWriteSpeeds() const | |||
| { | |||
| Array<int> results; | |||
| const int maxSpeed = pimpl->getIntProperty (L"MaxWriteSpeed", 1); | |||
| const int speeds[] = { 1, 2, 4, 8, 12, 16, 20, 24, 32, 40, 64, 80 }; | |||
| for (int i = 0; i < numElementsInArray (speeds); ++i) | |||
| if (speeds[i] <= maxSpeed) | |||
| results.add (speeds[i]); | |||
| results.addIfNotAlreadyThere (maxSpeed); | |||
| return results; | |||
| } | |||
| bool AudioCDBurner::setBufferUnderrunProtection (const bool shouldBeEnabled) | |||
| { | |||
| if (pimpl->getIntProperty (L"BufferUnderrunFreeCapable", 0) == 0) | |||
| return false; | |||
| pimpl->setIntProperty (L"EnableBufferUnderrunFree", shouldBeEnabled ? -1 : 0); | |||
| return pimpl->getIntProperty (L"EnableBufferUnderrunFree", 0) != 0; | |||
| } | |||
| int AudioCDBurner::getNumAvailableAudioBlocks() const | |||
| { | |||
| long blocksFree = 0; | |||
| pimpl->redbook->GetAvailableAudioTrackBlocks (&blocksFree); | |||
| return blocksFree; | |||
| } | |||
| String AudioCDBurner::burn (AudioCDBurner::BurnProgressListener* listener, bool ejectDiscAfterwards, | |||
| bool performFakeBurnForTesting, int writeSpeed) | |||
| { | |||
| pimpl->setIntProperty (L"WriteSpeed", writeSpeed > 0 ? writeSpeed : -1); | |||
| pimpl->listener = listener; | |||
| pimpl->progress = 0; | |||
| pimpl->shouldCancel = false; | |||
| UINT_PTR cookie; | |||
| HRESULT hr = pimpl->discMaster->ProgressAdvise ((AudioCDBurner::Pimpl*) pimpl, &cookie); | |||
| hr = pimpl->discMaster->RecordDisc (performFakeBurnForTesting, | |||
| 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; | |||
| } | |||
| pimpl->discMaster->ProgressUnadvise (cookie); | |||
| pimpl->listener = 0; | |||
| return error; | |||
| } | |||
| bool AudioCDBurner::addAudioTrack (AudioSource* audioSource, int numSamples) | |||
| { | |||
| if (audioSource == 0) | |||
| return false; | |||
| ScopedPointer<AudioSource> source (audioSource); | |||
| long bytesPerBlock; | |||
| HRESULT hr = pimpl->redbook->GetAudioBlockSize (&bytesPerBlock); | |||
| const int samplesPerBlock = bytesPerBlock / 4; | |||
| bool ok = true; | |||
| hr = pimpl->redbook->CreateAudioTrack ((long) numSamples / (bytesPerBlock * 4)); | |||
| HeapBlock <byte> buffer (bytesPerBlock); | |||
| AudioSampleBuffer sourceBuffer (2, samplesPerBlock); | |||
| int samplesDone = 0; | |||
| source->prepareToPlay (samplesPerBlock, 44100.0); | |||
| while (ok) | |||
| { | |||
| { | |||
| AudioSourceChannelInfo info (&sourceBuffer, 0, samplesPerBlock); | |||
| sourceBuffer.clear(); | |||
| source->getNextAudioBlock (info); | |||
| } | |||
| buffer.clear (bytesPerBlock); | |||
| typedef AudioData::Pointer <AudioData::Int16, AudioData::LittleEndian, | |||
| AudioData::Interleaved, AudioData::NonConst> CDSampleFormat; | |||
| typedef AudioData::Pointer <AudioData::Float32, AudioData::NativeEndian, | |||
| AudioData::NonInterleaved, AudioData::Const> SourceSampleFormat; | |||
| CDSampleFormat left (buffer, 2); | |||
| left.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (0)), samplesPerBlock); | |||
| CDSampleFormat right (buffer + 2, 2); | |||
| right.convertSamples (SourceSampleFormat (sourceBuffer.getSampleData (1)), samplesPerBlock); | |||
| hr = pimpl->redbook->AddAudioTrackBlocks (buffer, bytesPerBlock); | |||
| if (FAILED (hr)) | |||
| ok = false; | |||
| samplesDone += samplesPerBlock; | |||
| if (samplesDone >= numSamples) | |||
| break; | |||
| } | |||
| hr = pimpl->redbook->CloseAudioTrack(); | |||
| return ok && hr == S_OK; | |||
| } | |||
| @@ -0,0 +1,483 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| class MidiInCollector | |||
| { | |||
| public: | |||
| MidiInCollector (MidiInput* const input_, | |||
| MidiInputCallback& callback_) | |||
| : deviceHandle (0), | |||
| input (input_), | |||
| callback (callback_), | |||
| concatenator (4096), | |||
| isStarted (false), | |||
| startTime (0) | |||
| { | |||
| } | |||
| ~MidiInCollector() | |||
| { | |||
| stop(); | |||
| if (deviceHandle != 0) | |||
| { | |||
| for (int count = 5; --count >= 0;) | |||
| { | |||
| if (midiInClose (deviceHandle) == MMSYSERR_NOERROR) | |||
| break; | |||
| Sleep (20); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| void handleMessage (const uint8* bytes, const uint32 timeStamp) | |||
| { | |||
| if (bytes[0] >= 0x80 && isStarted) | |||
| { | |||
| concatenator.pushMidiData (bytes, MidiMessage::getMessageLengthFromFirstByte (bytes[0]), | |||
| convertTimeStamp (timeStamp), input, callback); | |||
| writeFinishedBlocks(); | |||
| } | |||
| } | |||
| void handleSysEx (MIDIHDR* const hdr, const uint32 timeStamp) | |||
| { | |||
| if (isStarted && hdr->dwBytesRecorded > 0) | |||
| { | |||
| concatenator.pushMidiData (hdr->lpData, (int) hdr->dwBytesRecorded, | |||
| convertTimeStamp (timeStamp), input, callback); | |||
| writeFinishedBlocks(); | |||
| } | |||
| } | |||
| void start() | |||
| { | |||
| if (deviceHandle != 0 && ! isStarted) | |||
| { | |||
| activeMidiCollectors.addIfNotAlreadyThere (this); | |||
| for (int i = 0; i < (int) numHeaders; ++i) | |||
| { | |||
| headers[i].prepare (deviceHandle); | |||
| headers[i].write (deviceHandle); | |||
| } | |||
| startTime = Time::getMillisecondCounterHiRes(); | |||
| MMRESULT res = midiInStart (deviceHandle); | |||
| if (res == MMSYSERR_NOERROR) | |||
| { | |||
| concatenator.reset(); | |||
| isStarted = true; | |||
| } | |||
| else | |||
| { | |||
| unprepareAllHeaders(); | |||
| } | |||
| } | |||
| } | |||
| void stop() | |||
| { | |||
| if (isStarted) | |||
| { | |||
| isStarted = false; | |||
| midiInReset (deviceHandle); | |||
| midiInStop (deviceHandle); | |||
| activeMidiCollectors.removeFirstMatchingValue (this); | |||
| unprepareAllHeaders(); | |||
| concatenator.reset(); | |||
| } | |||
| } | |||
| static void CALLBACK midiInCallback (HMIDIIN, UINT uMsg, DWORD_PTR dwInstance, | |||
| DWORD_PTR midiMessage, DWORD_PTR timeStamp) | |||
| { | |||
| MidiInCollector* const collector = reinterpret_cast <MidiInCollector*> (dwInstance); | |||
| if (activeMidiCollectors.contains (collector)) | |||
| { | |||
| if (uMsg == MIM_DATA) | |||
| collector->handleMessage ((const uint8*) &midiMessage, (uint32) timeStamp); | |||
| else if (uMsg == MIM_LONGDATA) | |||
| collector->handleSysEx ((MIDIHDR*) midiMessage, (uint32) timeStamp); | |||
| } | |||
| } | |||
| HMIDIIN deviceHandle; | |||
| private: | |||
| static Array <MidiInCollector*, CriticalSection> activeMidiCollectors; | |||
| MidiInput* input; | |||
| MidiInputCallback& callback; | |||
| MidiDataConcatenator concatenator; | |||
| bool volatile isStarted; | |||
| double startTime; | |||
| class MidiHeader | |||
| { | |||
| public: | |||
| MidiHeader() {} | |||
| void prepare (HMIDIIN deviceHandle) | |||
| { | |||
| zerostruct (hdr); | |||
| hdr.lpData = data; | |||
| hdr.dwBufferLength = (DWORD) numElementsInArray (data); | |||
| midiInPrepareHeader (deviceHandle, &hdr, sizeof (hdr)); | |||
| } | |||
| void unprepare (HMIDIIN deviceHandle) | |||
| { | |||
| if ((hdr.dwFlags & WHDR_DONE) != 0) | |||
| { | |||
| int c = 10; | |||
| while (--c >= 0 && midiInUnprepareHeader (deviceHandle, &hdr, sizeof (hdr)) == MIDIERR_STILLPLAYING) | |||
| Thread::sleep (20); | |||
| jassert (c >= 0); | |||
| } | |||
| } | |||
| void write (HMIDIIN deviceHandle) | |||
| { | |||
| hdr.dwBytesRecorded = 0; | |||
| midiInAddBuffer (deviceHandle, &hdr, sizeof (hdr)); | |||
| } | |||
| void writeIfFinished (HMIDIIN deviceHandle) | |||
| { | |||
| if ((hdr.dwFlags & WHDR_DONE) != 0) | |||
| write (deviceHandle); | |||
| } | |||
| private: | |||
| MIDIHDR hdr; | |||
| char data [256]; | |||
| JUCE_DECLARE_NON_COPYABLE (MidiHeader) | |||
| }; | |||
| enum { numHeaders = 32 }; | |||
| MidiHeader headers [numHeaders]; | |||
| void writeFinishedBlocks() | |||
| { | |||
| for (int i = 0; i < (int) numHeaders; ++i) | |||
| headers[i].writeIfFinished (deviceHandle); | |||
| } | |||
| void unprepareAllHeaders() | |||
| { | |||
| for (int i = 0; i < (int) numHeaders; ++i) | |||
| headers[i].unprepare (deviceHandle); | |||
| } | |||
| double convertTimeStamp (uint32 timeStamp) | |||
| { | |||
| double t = startTime + timeStamp; | |||
| const double now = Time::getMillisecondCounterHiRes(); | |||
| if (t > now) | |||
| { | |||
| if (t > now + 2.0) | |||
| startTime -= 1.0; | |||
| t = now; | |||
| } | |||
| return t * 0.001; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiInCollector) | |||
| }; | |||
| Array <MidiInCollector*, CriticalSection> MidiInCollector::activeMidiCollectors; | |||
| //============================================================================== | |||
| StringArray MidiInput::getDevices() | |||
| { | |||
| StringArray s; | |||
| const UINT num = midiInGetNumDevs(); | |||
| for (UINT i = 0; i < num; ++i) | |||
| { | |||
| MIDIINCAPS mc = { 0 }; | |||
| if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) | |||
| s.add (String (mc.szPname, sizeof (mc.szPname))); | |||
| } | |||
| return s; | |||
| } | |||
| int MidiInput::getDefaultDeviceIndex() | |||
| { | |||
| return 0; | |||
| } | |||
| MidiInput* MidiInput::openDevice (const int index, MidiInputCallback* const callback) | |||
| { | |||
| if (callback == nullptr) | |||
| return nullptr; | |||
| UINT deviceId = MIDI_MAPPER; | |||
| int n = 0; | |||
| String name; | |||
| const UINT num = midiInGetNumDevs(); | |||
| for (UINT i = 0; i < num; ++i) | |||
| { | |||
| MIDIINCAPS mc = { 0 }; | |||
| if (midiInGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) | |||
| { | |||
| if (index == n) | |||
| { | |||
| deviceId = i; | |||
| name = String (mc.szPname, (size_t) numElementsInArray (mc.szPname)); | |||
| break; | |||
| } | |||
| ++n; | |||
| } | |||
| } | |||
| ScopedPointer <MidiInput> in (new MidiInput (name)); | |||
| ScopedPointer <MidiInCollector> collector (new MidiInCollector (in, *callback)); | |||
| HMIDIIN h; | |||
| MMRESULT err = midiInOpen (&h, deviceId, | |||
| (DWORD_PTR) &MidiInCollector::midiInCallback, | |||
| (DWORD_PTR) (MidiInCollector*) collector, | |||
| CALLBACK_FUNCTION); | |||
| if (err == MMSYSERR_NOERROR) | |||
| { | |||
| collector->deviceHandle = h; | |||
| in->internal = collector.release(); | |||
| return in.release(); | |||
| } | |||
| return nullptr; | |||
| } | |||
| MidiInput::MidiInput (const String& name_) | |||
| : name (name_), | |||
| internal (0) | |||
| { | |||
| } | |||
| MidiInput::~MidiInput() | |||
| { | |||
| delete static_cast <MidiInCollector*> (internal); | |||
| } | |||
| void MidiInput::start() { static_cast <MidiInCollector*> (internal)->start(); } | |||
| void MidiInput::stop() { static_cast <MidiInCollector*> (internal)->stop(); } | |||
| //============================================================================== | |||
| struct MidiOutHandle | |||
| { | |||
| int refCount; | |||
| UINT deviceId; | |||
| HMIDIOUT handle; | |||
| static Array<MidiOutHandle*> activeHandles; | |||
| private: | |||
| JUCE_LEAK_DETECTOR (MidiOutHandle) | |||
| }; | |||
| Array<MidiOutHandle*> MidiOutHandle::activeHandles; | |||
| //============================================================================== | |||
| StringArray MidiOutput::getDevices() | |||
| { | |||
| StringArray s; | |||
| const UINT num = midiOutGetNumDevs(); | |||
| for (UINT i = 0; i < num; ++i) | |||
| { | |||
| MIDIOUTCAPS mc = { 0 }; | |||
| if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) | |||
| s.add (String (mc.szPname, sizeof (mc.szPname))); | |||
| } | |||
| return s; | |||
| } | |||
| int MidiOutput::getDefaultDeviceIndex() | |||
| { | |||
| const UINT num = midiOutGetNumDevs(); | |||
| int n = 0; | |||
| for (UINT i = 0; i < num; ++i) | |||
| { | |||
| MIDIOUTCAPS mc = { 0 }; | |||
| if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) | |||
| { | |||
| if ((mc.wTechnology & MOD_MAPPER) != 0) | |||
| return n; | |||
| ++n; | |||
| } | |||
| } | |||
| return 0; | |||
| } | |||
| MidiOutput* MidiOutput::openDevice (int index) | |||
| { | |||
| UINT deviceId = MIDI_MAPPER; | |||
| const UINT num = midiOutGetNumDevs(); | |||
| int n = 0; | |||
| for (UINT i = 0; i < num; ++i) | |||
| { | |||
| MIDIOUTCAPS mc = { 0 }; | |||
| if (midiOutGetDevCaps (i, &mc, sizeof (mc)) == MMSYSERR_NOERROR) | |||
| { | |||
| // use the microsoft sw synth as a default - best not to allow deviceId | |||
| // to be MIDI_MAPPER, or else device sharing breaks | |||
| if (String (mc.szPname, sizeof (mc.szPname)).containsIgnoreCase ("microsoft")) | |||
| deviceId = i; | |||
| if (index == n) | |||
| { | |||
| deviceId = i; | |||
| break; | |||
| } | |||
| ++n; | |||
| } | |||
| } | |||
| for (int i = MidiOutHandle::activeHandles.size(); --i >= 0;) | |||
| { | |||
| MidiOutHandle* const han = MidiOutHandle::activeHandles.getUnchecked(i); | |||
| if (han->deviceId == deviceId) | |||
| { | |||
| han->refCount++; | |||
| MidiOutput* const out = new MidiOutput(); | |||
| out->internal = han; | |||
| return out; | |||
| } | |||
| } | |||
| for (int i = 4; --i >= 0;) | |||
| { | |||
| HMIDIOUT h = 0; | |||
| MMRESULT res = midiOutOpen (&h, deviceId, 0, 0, CALLBACK_NULL); | |||
| if (res == MMSYSERR_NOERROR) | |||
| { | |||
| MidiOutHandle* const han = new MidiOutHandle(); | |||
| han->deviceId = deviceId; | |||
| han->refCount = 1; | |||
| han->handle = h; | |||
| MidiOutHandle::activeHandles.add (han); | |||
| MidiOutput* const out = new MidiOutput(); | |||
| out->internal = han; | |||
| return out; | |||
| } | |||
| else if (res == MMSYSERR_ALLOCATED) | |||
| { | |||
| Sleep (100); | |||
| } | |||
| else | |||
| { | |||
| break; | |||
| } | |||
| } | |||
| return nullptr; | |||
| } | |||
| MidiOutput::~MidiOutput() | |||
| { | |||
| stopBackgroundThread(); | |||
| MidiOutHandle* const h = static_cast <MidiOutHandle*> (internal); | |||
| if (MidiOutHandle::activeHandles.contains (h) && --(h->refCount) == 0) | |||
| { | |||
| midiOutClose (h->handle); | |||
| MidiOutHandle::activeHandles.removeFirstMatchingValue (h); | |||
| delete h; | |||
| } | |||
| } | |||
| void MidiOutput::sendMessageNow (const MidiMessage& message) | |||
| { | |||
| const MidiOutHandle* const handle = static_cast <const MidiOutHandle*> (internal); | |||
| if (message.getRawDataSize() > 3 || message.isSysEx()) | |||
| { | |||
| MIDIHDR h = { 0 }; | |||
| h.lpData = (char*) message.getRawData(); | |||
| h.dwBytesRecorded = h.dwBufferLength = (DWORD) message.getRawDataSize(); | |||
| if (midiOutPrepareHeader (handle->handle, &h, sizeof (MIDIHDR)) == MMSYSERR_NOERROR) | |||
| { | |||
| MMRESULT res = midiOutLongMsg (handle->handle, &h, sizeof (MIDIHDR)); | |||
| if (res == MMSYSERR_NOERROR) | |||
| { | |||
| while ((h.dwFlags & MHDR_DONE) == 0) | |||
| Sleep (1); | |||
| int count = 500; // 1 sec timeout | |||
| while (--count >= 0) | |||
| { | |||
| res = midiOutUnprepareHeader (handle->handle, &h, sizeof (MIDIHDR)); | |||
| if (res == MIDIERR_STILLPLAYING) | |||
| Sleep (2); | |||
| else | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| midiOutShortMsg (handle->handle, *(unsigned int*) message.getRawData()); | |||
| } | |||
| } | |||
| @@ -0,0 +1,183 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| AudioSourcePlayer::AudioSourcePlayer() | |||
| : source (nullptr), | |||
| sampleRate (0), | |||
| bufferSize (0), | |||
| tempBuffer (2, 8), | |||
| lastGain (1.0f), | |||
| gain (1.0f) | |||
| { | |||
| } | |||
| AudioSourcePlayer::~AudioSourcePlayer() | |||
| { | |||
| setSource (nullptr); | |||
| } | |||
| void AudioSourcePlayer::setSource (AudioSource* newSource) | |||
| { | |||
| if (source != newSource) | |||
| { | |||
| AudioSource* const oldSource = source; | |||
| if (newSource != nullptr && bufferSize > 0 && sampleRate > 0) | |||
| newSource->prepareToPlay (bufferSize, sampleRate); | |||
| { | |||
| const ScopedLock sl (readLock); | |||
| source = newSource; | |||
| } | |||
| if (oldSource != nullptr) | |||
| oldSource->releaseResources(); | |||
| } | |||
| } | |||
| void AudioSourcePlayer::setGain (const float newGain) noexcept | |||
| { | |||
| gain = newGain; | |||
| } | |||
| void AudioSourcePlayer::audioDeviceIOCallback (const float** inputChannelData, | |||
| int totalNumInputChannels, | |||
| float** outputChannelData, | |||
| int totalNumOutputChannels, | |||
| int numSamples) | |||
| { | |||
| // these should have been prepared by audioDeviceAboutToStart()... | |||
| jassert (sampleRate > 0 && bufferSize > 0); | |||
| const ScopedLock sl (readLock); | |||
| if (source != nullptr) | |||
| { | |||
| int numActiveChans = 0, numInputs = 0, numOutputs = 0; | |||
| // messy stuff needed to compact the channels down into an array | |||
| // of non-zero pointers.. | |||
| for (int i = 0; i < totalNumInputChannels; ++i) | |||
| { | |||
| if (inputChannelData[i] != nullptr) | |||
| { | |||
| inputChans [numInputs++] = inputChannelData[i]; | |||
| if (numInputs >= numElementsInArray (inputChans)) | |||
| break; | |||
| } | |||
| } | |||
| for (int i = 0; i < totalNumOutputChannels; ++i) | |||
| { | |||
| if (outputChannelData[i] != nullptr) | |||
| { | |||
| outputChans [numOutputs++] = outputChannelData[i]; | |||
| if (numOutputs >= numElementsInArray (outputChans)) | |||
| break; | |||
| } | |||
| } | |||
| if (numInputs > numOutputs) | |||
| { | |||
| // if there aren't enough output channels for the number of | |||
| // inputs, we need to create some temporary extra ones (can't | |||
| // use the input data in case it gets written to) | |||
| tempBuffer.setSize (numInputs - numOutputs, numSamples, | |||
| false, false, true); | |||
| for (int i = 0; i < numOutputs; ++i) | |||
| { | |||
| channels[numActiveChans] = outputChans[i]; | |||
| memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); | |||
| ++numActiveChans; | |||
| } | |||
| for (int i = numOutputs; i < numInputs; ++i) | |||
| { | |||
| channels[numActiveChans] = tempBuffer.getSampleData (i - numOutputs, 0); | |||
| memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); | |||
| ++numActiveChans; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < numInputs; ++i) | |||
| { | |||
| channels[numActiveChans] = outputChans[i]; | |||
| memcpy (channels[numActiveChans], inputChans[i], sizeof (float) * (size_t) numSamples); | |||
| ++numActiveChans; | |||
| } | |||
| for (int i = numInputs; i < numOutputs; ++i) | |||
| { | |||
| channels[numActiveChans] = outputChans[i]; | |||
| zeromem (channels[numActiveChans], sizeof (float) * (size_t) numSamples); | |||
| ++numActiveChans; | |||
| } | |||
| } | |||
| AudioSampleBuffer buffer (channels, numActiveChans, numSamples); | |||
| AudioSourceChannelInfo info (&buffer, 0, numSamples); | |||
| source->getNextAudioBlock (info); | |||
| for (int i = info.buffer->getNumChannels(); --i >= 0;) | |||
| buffer.applyGainRamp (i, info.startSample, info.numSamples, lastGain, gain); | |||
| lastGain = gain; | |||
| } | |||
| else | |||
| { | |||
| for (int i = 0; i < totalNumOutputChannels; ++i) | |||
| if (outputChannelData[i] != nullptr) | |||
| zeromem (outputChannelData[i], sizeof (float) * (size_t) numSamples); | |||
| } | |||
| } | |||
| void AudioSourcePlayer::audioDeviceAboutToStart (AudioIODevice* device) | |||
| { | |||
| prepareToPlay (device->getCurrentSampleRate(), | |||
| device->getCurrentBufferSizeSamples()); | |||
| } | |||
| void AudioSourcePlayer::prepareToPlay (double newSampleRate, int newBufferSize) | |||
| { | |||
| sampleRate = newSampleRate; | |||
| bufferSize = newBufferSize; | |||
| zeromem (channels, sizeof (channels)); | |||
| if (source != nullptr) | |||
| source->prepareToPlay (bufferSize, sampleRate); | |||
| } | |||
| void AudioSourcePlayer::audioDeviceStopped() | |||
| { | |||
| if (source != nullptr) | |||
| source->releaseResources(); | |||
| sampleRate = 0.0; | |||
| bufferSize = 0; | |||
| tempBuffer.setSize (2, 8); | |||
| } | |||
| @@ -0,0 +1,116 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIOSOURCEPLAYER_H_INCLUDED | |||
| #define JUCE_AUDIOSOURCEPLAYER_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| Wrapper class to continuously stream audio from an audio source to an | |||
| AudioIODevice. | |||
| This object acts as an AudioIODeviceCallback, so can be attached to an | |||
| output device, and will stream audio from an AudioSource. | |||
| */ | |||
| class JUCE_API AudioSourcePlayer : public AudioIODeviceCallback | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an empty AudioSourcePlayer. */ | |||
| AudioSourcePlayer(); | |||
| /** Destructor. | |||
| Make sure this object isn't still being used by an AudioIODevice before | |||
| deleting it! | |||
| */ | |||
| virtual ~AudioSourcePlayer(); | |||
| //============================================================================== | |||
| /** Changes the current audio source to play from. | |||
| If the source passed in is already being used, this method will do nothing. | |||
| If the source is not null, its prepareToPlay() method will be called | |||
| before it starts being used for playback. | |||
| If there's another source currently playing, its releaseResources() method | |||
| will be called after it has been swapped for the new one. | |||
| @param newSource the new source to use - this will NOT be deleted | |||
| by this object when no longer needed, so it's the | |||
| caller's responsibility to manage it. | |||
| */ | |||
| void setSource (AudioSource* newSource); | |||
| /** Returns the source that's playing. | |||
| May return 0 if there's no source. | |||
| */ | |||
| AudioSource* getCurrentSource() const noexcept { return source; } | |||
| /** Sets a gain to apply to the audio data. | |||
| @see getGain | |||
| */ | |||
| void setGain (float newGain) noexcept; | |||
| /** Returns the current gain. | |||
| @see setGain | |||
| */ | |||
| float getGain() const noexcept { return gain; } | |||
| //============================================================================== | |||
| /** Implementation of the AudioIODeviceCallback method. */ | |||
| void audioDeviceIOCallback (const float** inputChannelData, | |||
| int totalNumInputChannels, | |||
| float** outputChannelData, | |||
| int totalNumOutputChannels, | |||
| int numSamples) override; | |||
| /** Implementation of the AudioIODeviceCallback method. */ | |||
| void audioDeviceAboutToStart (AudioIODevice* device) override; | |||
| /** Implementation of the AudioIODeviceCallback method. */ | |||
| void audioDeviceStopped() override; | |||
| /** An alternative method for initialising the source without an AudioIODevice. */ | |||
| void prepareToPlay (double sampleRate, int blockSize); | |||
| private: | |||
| //============================================================================== | |||
| CriticalSection readLock; | |||
| AudioSource* source; | |||
| double sampleRate; | |||
| int bufferSize; | |||
| float* channels [128]; | |||
| float* outputChans [128]; | |||
| const float* inputChans [128]; | |||
| AudioSampleBuffer tempBuffer; | |||
| float lastGain, gain; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioSourcePlayer) | |||
| }; | |||
| #endif // JUCE_AUDIOSOURCEPLAYER_H_INCLUDED | |||
| @@ -0,0 +1,300 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| AudioTransportSource::AudioTransportSource() | |||
| : source (nullptr), | |||
| resamplerSource (nullptr), | |||
| bufferingSource (nullptr), | |||
| positionableSource (nullptr), | |||
| masterSource (nullptr), | |||
| gain (1.0f), | |||
| lastGain (1.0f), | |||
| playing (false), | |||
| stopped (true), | |||
| sampleRate (44100.0), | |||
| sourceSampleRate (0.0), | |||
| blockSize (128), | |||
| readAheadBufferSize (0), | |||
| isPrepared (false), | |||
| inputStreamEOF (false) | |||
| { | |||
| } | |||
| AudioTransportSource::~AudioTransportSource() | |||
| { | |||
| setSource (nullptr); | |||
| releaseMasterResources(); | |||
| } | |||
| void AudioTransportSource::setSource (PositionableAudioSource* const newSource, | |||
| int readAheadBufferSize_, | |||
| TimeSliceThread* readAheadThread, | |||
| double sourceSampleRateToCorrectFor, | |||
| int maxNumChannels) | |||
| { | |||
| if (source == newSource) | |||
| { | |||
| if (source == nullptr) | |||
| return; | |||
| setSource (nullptr, 0, nullptr); // deselect and reselect to avoid releasing resources wrongly | |||
| } | |||
| readAheadBufferSize = readAheadBufferSize_; | |||
| sourceSampleRate = sourceSampleRateToCorrectFor; | |||
| ResamplingAudioSource* newResamplerSource = nullptr; | |||
| BufferingAudioSource* newBufferingSource = nullptr; | |||
| PositionableAudioSource* newPositionableSource = nullptr; | |||
| AudioSource* newMasterSource = nullptr; | |||
| ScopedPointer <ResamplingAudioSource> oldResamplerSource (resamplerSource); | |||
| ScopedPointer <BufferingAudioSource> oldBufferingSource (bufferingSource); | |||
| AudioSource* oldMasterSource = masterSource; | |||
| if (newSource != nullptr) | |||
| { | |||
| newPositionableSource = newSource; | |||
| if (readAheadBufferSize_ > 0) | |||
| { | |||
| // If you want to use a read-ahead buffer, you must also provide a TimeSliceThread | |||
| // for it to use! | |||
| jassert (readAheadThread != nullptr); | |||
| newPositionableSource = newBufferingSource | |||
| = new BufferingAudioSource (newPositionableSource, *readAheadThread, | |||
| false, readAheadBufferSize_, maxNumChannels); | |||
| } | |||
| newPositionableSource->setNextReadPosition (0); | |||
| if (sourceSampleRateToCorrectFor > 0) | |||
| newMasterSource = newResamplerSource | |||
| = new ResamplingAudioSource (newPositionableSource, false, maxNumChannels); | |||
| else | |||
| newMasterSource = newPositionableSource; | |||
| if (isPrepared) | |||
| { | |||
| if (newResamplerSource != nullptr && sourceSampleRate > 0 && sampleRate > 0) | |||
| newResamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); | |||
| newMasterSource->prepareToPlay (blockSize, sampleRate); | |||
| } | |||
| } | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| source = newSource; | |||
| resamplerSource = newResamplerSource; | |||
| bufferingSource = newBufferingSource; | |||
| masterSource = newMasterSource; | |||
| positionableSource = newPositionableSource; | |||
| playing = false; | |||
| } | |||
| if (oldMasterSource != nullptr) | |||
| oldMasterSource->releaseResources(); | |||
| } | |||
| void AudioTransportSource::start() | |||
| { | |||
| if ((! playing) && masterSource != nullptr) | |||
| { | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| playing = true; | |||
| stopped = false; | |||
| inputStreamEOF = false; | |||
| } | |||
| sendChangeMessage(); | |||
| } | |||
| } | |||
| void AudioTransportSource::stop() | |||
| { | |||
| if (playing) | |||
| { | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| playing = false; | |||
| } | |||
| int n = 500; | |||
| while (--n >= 0 && ! stopped) | |||
| Thread::sleep (2); | |||
| sendChangeMessage(); | |||
| } | |||
| } | |||
| void AudioTransportSource::setPosition (double newPosition) | |||
| { | |||
| if (sampleRate > 0.0) | |||
| setNextReadPosition ((int64) (newPosition * sampleRate)); | |||
| } | |||
| double AudioTransportSource::getCurrentPosition() const | |||
| { | |||
| if (sampleRate > 0.0) | |||
| return getNextReadPosition() / sampleRate; | |||
| return 0.0; | |||
| } | |||
| double AudioTransportSource::getLengthInSeconds() const | |||
| { | |||
| return getTotalLength() / sampleRate; | |||
| } | |||
| void AudioTransportSource::setNextReadPosition (int64 newPosition) | |||
| { | |||
| if (positionableSource != nullptr) | |||
| { | |||
| if (sampleRate > 0 && sourceSampleRate > 0) | |||
| newPosition = (int64) (newPosition * sourceSampleRate / sampleRate); | |||
| positionableSource->setNextReadPosition (newPosition); | |||
| } | |||
| } | |||
| int64 AudioTransportSource::getNextReadPosition() const | |||
| { | |||
| if (positionableSource != nullptr) | |||
| { | |||
| const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; | |||
| return (int64) (positionableSource->getNextReadPosition() * ratio); | |||
| } | |||
| return 0; | |||
| } | |||
| int64 AudioTransportSource::getTotalLength() const | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| if (positionableSource != nullptr) | |||
| { | |||
| const double ratio = (sampleRate > 0 && sourceSampleRate > 0) ? sampleRate / sourceSampleRate : 1.0; | |||
| return (int64) (positionableSource->getTotalLength() * ratio); | |||
| } | |||
| return 0; | |||
| } | |||
| bool AudioTransportSource::isLooping() const | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| return positionableSource != nullptr | |||
| && positionableSource->isLooping(); | |||
| } | |||
| void AudioTransportSource::setGain (const float newGain) noexcept | |||
| { | |||
| gain = newGain; | |||
| } | |||
| void AudioTransportSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| sampleRate = newSampleRate; | |||
| blockSize = samplesPerBlockExpected; | |||
| if (masterSource != nullptr) | |||
| masterSource->prepareToPlay (samplesPerBlockExpected, sampleRate); | |||
| if (resamplerSource != nullptr && sourceSampleRate > 0) | |||
| resamplerSource->setResamplingRatio (sourceSampleRate / sampleRate); | |||
| isPrepared = true; | |||
| } | |||
| void AudioTransportSource::releaseMasterResources() | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| if (masterSource != nullptr) | |||
| masterSource->releaseResources(); | |||
| isPrepared = false; | |||
| } | |||
| void AudioTransportSource::releaseResources() | |||
| { | |||
| releaseMasterResources(); | |||
| } | |||
| void AudioTransportSource::getNextAudioBlock (const AudioSourceChannelInfo& info) | |||
| { | |||
| const ScopedLock sl (callbackLock); | |||
| inputStreamEOF = false; | |||
| if (masterSource != nullptr && ! stopped) | |||
| { | |||
| masterSource->getNextAudioBlock (info); | |||
| if (! playing) | |||
| { | |||
| // just stopped playing, so fade out the last block.. | |||
| for (int i = info.buffer->getNumChannels(); --i >= 0;) | |||
| info.buffer->applyGainRamp (i, info.startSample, jmin (256, info.numSamples), 1.0f, 0.0f); | |||
| if (info.numSamples > 256) | |||
| info.buffer->clear (info.startSample + 256, info.numSamples - 256); | |||
| } | |||
| if (positionableSource->getNextReadPosition() > positionableSource->getTotalLength() + 1 | |||
| && ! positionableSource->isLooping()) | |||
| { | |||
| playing = false; | |||
| inputStreamEOF = true; | |||
| sendChangeMessage(); | |||
| } | |||
| stopped = ! playing; | |||
| for (int i = info.buffer->getNumChannels(); --i >= 0;) | |||
| { | |||
| info.buffer->applyGainRamp (i, info.startSample, info.numSamples, | |||
| lastGain, gain); | |||
| } | |||
| } | |||
| else | |||
| { | |||
| info.clearActiveBufferRegion(); | |||
| stopped = true; | |||
| } | |||
| lastGain = gain; | |||
| } | |||
| @@ -0,0 +1,186 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #ifndef JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED | |||
| #define JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED | |||
| //============================================================================== | |||
| /** | |||
| An AudioSource that takes a PositionableAudioSource and allows it to be | |||
| played, stopped, started, etc. | |||
| This can also be told use a buffer and background thread to read ahead, and | |||
| if can correct for different sample-rates. | |||
| You may want to use one of these along with an AudioSourcePlayer and AudioIODevice | |||
| to control playback of an audio file. | |||
| @see AudioSource, AudioSourcePlayer | |||
| */ | |||
| class JUCE_API AudioTransportSource : public PositionableAudioSource, | |||
| public ChangeBroadcaster | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an AudioTransportSource. | |||
| After creating one of these, use the setSource() method to select an input source. | |||
| */ | |||
| AudioTransportSource(); | |||
| /** Destructor. */ | |||
| ~AudioTransportSource(); | |||
| //============================================================================== | |||
| /** Sets the reader that is being used as the input source. | |||
| This will stop playback, reset the position to 0 and change to the new reader. | |||
| The source passed in will not be deleted by this object, so must be managed by | |||
| the caller. | |||
| @param newSource the new input source to use. This may be zero | |||
| @param readAheadBufferSize a size of buffer to use for reading ahead. If this | |||
| is zero, no reading ahead will be done; if it's | |||
| greater than zero, a BufferingAudioSource will be used | |||
| to do the reading-ahead. If you set a non-zero value here, | |||
| you'll also need to set the readAheadThread parameter. | |||
| @param readAheadThread if you set readAheadBufferSize to a non-zero value, then | |||
| you'll also need to supply this TimeSliceThread object for | |||
| the background reader to use. The thread object must not be | |||
| deleted while the AudioTransport source is still using it. | |||
| @param sourceSampleRateToCorrectFor if this is non-zero, it specifies the sample | |||
| rate of the source, and playback will be sample-rate | |||
| adjusted to maintain playback at the correct pitch. If | |||
| this is 0, no sample-rate adjustment will be performed | |||
| @param maxNumChannels the maximum number of channels that may need to be played | |||
| */ | |||
| void setSource (PositionableAudioSource* newSource, | |||
| int readAheadBufferSize = 0, | |||
| TimeSliceThread* readAheadThread = nullptr, | |||
| double sourceSampleRateToCorrectFor = 0.0, | |||
| int maxNumChannels = 2); | |||
| //============================================================================== | |||
| /** Changes the current playback position in the source stream. | |||
| The next time the getNextAudioBlock() method is called, this | |||
| is the time from which it'll read data. | |||
| @see getPosition | |||
| */ | |||
| void setPosition (double newPosition); | |||
| /** Returns the position that the next data block will be read from | |||
| This is a time in seconds. | |||
| */ | |||
| double getCurrentPosition() const; | |||
| /** Returns the stream's length in seconds. */ | |||
| double getLengthInSeconds() const; | |||
| /** Returns true if the player has stopped because its input stream ran out of data. | |||
| */ | |||
| bool hasStreamFinished() const noexcept { return inputStreamEOF; } | |||
| //============================================================================== | |||
| /** Starts playing (if a source has been selected). | |||
| If it starts playing, this will send a message to any ChangeListeners | |||
| that are registered with this object. | |||
| */ | |||
| void start(); | |||
| /** Stops playing. | |||
| If it's actually playing, this will send a message to any ChangeListeners | |||
| that are registered with this object. | |||
| */ | |||
| void stop(); | |||
| /** Returns true if it's currently playing. */ | |||
| bool isPlaying() const noexcept { return playing; } | |||
| //============================================================================== | |||
| /** Changes the gain to apply to the output. | |||
| @param newGain a factor by which to multiply the outgoing samples, | |||
| so 1.0 = 0dB, 0.5 = -6dB, 2.0 = 6dB, etc. | |||
| */ | |||
| void setGain (float newGain) noexcept; | |||
| /** Returns the current gain setting. | |||
| @see setGain | |||
| */ | |||
| float getGain() const noexcept { return gain; } | |||
| //============================================================================== | |||
| /** Implementation of the AudioSource method. */ | |||
| void prepareToPlay (int samplesPerBlockExpected, double sampleRate) override; | |||
| /** Implementation of the AudioSource method. */ | |||
| void releaseResources() override; | |||
| /** Implementation of the AudioSource method. */ | |||
| void getNextAudioBlock (const AudioSourceChannelInfo&) override; | |||
| //============================================================================== | |||
| /** Implements the PositionableAudioSource method. */ | |||
| void setNextReadPosition (int64 newPosition) override; | |||
| /** Implements the PositionableAudioSource method. */ | |||
| int64 getNextReadPosition() const override; | |||
| /** Implements the PositionableAudioSource method. */ | |||
| int64 getTotalLength() const override; | |||
| /** Implements the PositionableAudioSource method. */ | |||
| bool isLooping() const override; | |||
| private: | |||
| //============================================================================== | |||
| PositionableAudioSource* source; | |||
| ResamplingAudioSource* resamplerSource; | |||
| BufferingAudioSource* bufferingSource; | |||
| PositionableAudioSource* positionableSource; | |||
| AudioSource* masterSource; | |||
| CriticalSection callbackLock; | |||
| float volatile gain, lastGain; | |||
| bool volatile playing, stopped; | |||
| double sampleRate, sourceSampleRate; | |||
| int blockSize, readAheadBufferSize; | |||
| bool isPrepared, inputStreamEOF; | |||
| void releaseMasterResources(); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AudioTransportSource) | |||
| }; | |||
| #endif // JUCE_AUDIOTRANSPORTSOURCE_H_INCLUDED | |||
| @@ -0,0 +1,26 @@ | |||
| /* | |||
| * Carla Juce setup | |||
| * Copyright (C) 2013 Filipe Coelho <falktx@falktx.com> | |||
| * | |||
| * This program is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU General Public License as | |||
| * published by the Free Software Foundation; either version 2 of | |||
| * the License, or any later version. | |||
| * | |||
| * This program 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. | |||
| * | |||
| * For a full copy of the GNU General Public License see the doc/GPL.txt file. | |||
| */ | |||
| #ifndef CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED | |||
| #define CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED | |||
| #include "juce_audio_basics.h" | |||
| #include "juce_audio_formats/AppConfig.h" | |||
| #include "juce_audio_formats/juce_audio_formats.h" | |||
| #endif // CARLA_JUCE_AUDIO_FORMATS_H_INCLUDED | |||
| @@ -0,0 +1,53 @@ | |||
| /* | |||
| ============================================================================== | |||
| Build options for juce_audio_formats static library | |||
| ============================================================================== | |||
| */ | |||
| #ifndef CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED | |||
| #define CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED | |||
| #include "../juce_audio_basics/AppConfig.h" | |||
| //============================================================================= | |||
| /** Config: JUCE_USE_FLAC | |||
| Enables the FLAC audio codec classes (available on all platforms). | |||
| If your app doesn't need to read FLAC files, you might want to disable this to | |||
| reduce the size of your codebase and build time. | |||
| */ | |||
| #define JUCE_USE_FLAC 0 | |||
| /** Config: JUCE_USE_OGGVORBIS | |||
| Enables the Ogg-Vorbis audio codec classes (available on all platforms). | |||
| If your app doesn't need to read Ogg-Vorbis files, you might want to disable this to | |||
| reduce the size of your codebase and build time. | |||
| */ | |||
| #define JUCE_USE_OGGVORBIS 0 | |||
| /** Config: JUCE_USE_MP3AUDIOFORMAT | |||
| Enables the software-based MP3AudioFormat class. | |||
| IMPORTANT DISCLAIMER: By choosing to enable the JUCE_USE_MP3AUDIOFORMAT flag and to compile | |||
| this MP3 code into your software, you do so AT YOUR OWN RISK! By doing so, you are agreeing | |||
| that Raw Material Software is in no way responsible for any patent, copyright, or other | |||
| legal issues that you may suffer as a result. | |||
| The code in juce_MP3AudioFormat.cpp is NOT guaranteed to be free from infringements of 3rd-party | |||
| intellectual property. If you wish to use it, please seek your own independent advice about the | |||
| legality of doing so. If you are not willing to accept full responsibility for the consequences | |||
| of using this code, then do not enable this setting. | |||
| */ | |||
| #define JUCE_USE_MP3AUDIOFORMAT 0 | |||
| /** Config: JUCE_USE_LAME_AUDIO_FORMAT | |||
| Enables the LameEncoderAudioFormat class. | |||
| */ | |||
| #define JUCE_USE_LAME_AUDIO_FORMAT 0 | |||
| /** Config: JUCE_USE_WINDOWS_MEDIA_FORMAT | |||
| Enables the Windows Media SDK codecs. | |||
| */ | |||
| #define JUCE_USE_WINDOWS_MEDIA_FORMAT 0 | |||
| #endif // CARLA_JUCE_AUDIO_FORMATS_APPCONFIG_H_INCLUDED | |||
| @@ -0,0 +1,88 @@ | |||
| #!/usr/bin/make -f | |||
| # Makefile for juce_audio_formats # | |||
| # ------------------------------- # | |||
| # Created by falkTX | |||
| # | |||
| include ../../Makefile.mk | |||
| # -------------------------------------------------------------- | |||
| BUILD_CXX_FLAGS += -I. | |||
| ifeq ($(MACOS),true) | |||
| BUILD_CXX_FLAGS += -objc++ | |||
| OBJS = juce_audio_formats.mm.o | |||
| OBJS_posix32 = juce_audio_formats.mm.posix32.o | |||
| OBJS_posix64 = juce_audio_formats.mm.posix64.o | |||
| else | |||
| OBJS = juce_audio_formats.cpp.o | |||
| OBJS_posix32 = juce_audio_formats.cpp.posix32.o | |||
| OBJS_posix64 = juce_audio_formats.cpp.posix64.o | |||
| endif | |||
| OBJS_win32 = juce_audio_formats.cpp.win32.o | |||
| OBJS_win64 = juce_audio_formats.cpp.win64.o | |||
| # -------------------------------------------------------------- | |||
| all: ../juce_audio_formats.a | |||
| posix32: ../juce_audio_formats.posix32.a | |||
| posix64: ../juce_audio_formats.posix64.a | |||
| win32: ../juce_audio_formats.win32.a | |||
| win64: ../juce_audio_formats.win64.a | |||
| # -------------------------------------------------------------- | |||
| ../juce_audio_formats.a: $(OBJS) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_formats.posix32.a: $(OBJS_posix32) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_formats.posix64.a: $(OBJS_posix64) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_formats.win32.a: $(OBJS_win32) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_formats.win64.a: $(OBJS_win64) | |||
| $(AR) rs $@ $^ | |||
| ../juce_audio_formats.dll: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||
| ../juce_audio_formats.dylib: $(OBJS) | |||
| $(CXX) $^ -dynamiclib $(LINK_FLAGS) -o $@ | |||
| ../juce_audio_formats.so: $(OBJS) | |||
| $(CXX) $^ -shared $(LINK_FLAGS) -o $@ | |||
| # -------------------------------------------------------------- | |||
| %.cpp.o: %.cpp | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| %.mm.o: %.mm | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) -c -o $@ | |||
| %.posix32.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
| %.posix64.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
| %.win32.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(32BIT_FLAGS) -c -o $@ | |||
| %.win64.o: % | |||
| $(CXX) $< $(BUILD_CXX_FLAGS) $(64BIT_FLAGS) -c -o $@ | |||
| # -------------------------------------------------------------- | |||
| clean: | |||
| rm -f *.o ../juce_audio_formats*.a | |||
| debug: | |||
| $(MAKE) DEBUG=true | |||
| @@ -0,0 +1,49 @@ | |||
| ===================================================================== | |||
| I've incorporated FLAC directly into the Juce codebase because it makes | |||
| things much easier than having to make all your builds link correctly to | |||
| the appropriate libraries on every different platform. | |||
| I've made minimal changes to the FLAC code - just tweaked a few include paths | |||
| to make it build smoothly, added some headers to allow you to turn off FLAC | |||
| compilation, and commented-out a couple of unused bits of code. | |||
| ===================================================================== | |||
| The following license is the BSD-style license that comes with the | |||
| Flac distribution, and which applies just to the files I've | |||
| included in this directory. For more info, and to get the rest of the | |||
| distribution, visit the Flac homepage: flac.sourceforge.net | |||
| ===================================================================== | |||
| Copyright (C) 2000,2001,2002,2003,2004,2005,2006 Josh Coalson | |||
| Redistribution and use in source and binary forms, with or without | |||
| modification, are permitted provided that the following conditions | |||
| are met: | |||
| - Redistributions of source code must retain the above copyright | |||
| notice, this list of conditions and the following disclaimer. | |||
| - Redistributions in binary form must reproduce the above copyright | |||
| notice, this list of conditions and the following disclaimer in the | |||
| documentation and/or other materials provided with the distribution. | |||
| - Neither the name of the Xiph.org Foundation nor the names of its | |||
| contributors may be used to endorse or promote products derived from | |||
| this software without specific prior written permission. | |||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| @@ -0,0 +1,402 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__ALL_H | |||
| #define FLAC__ALL_H | |||
| #include "export.h" | |||
| #include "assert.h" | |||
| #include "callback.h" | |||
| #include "format.h" | |||
| #include "metadata.h" | |||
| #include "ordinals.h" | |||
| #include "stream_decoder.h" | |||
| #include "stream_encoder.h" | |||
| #ifdef _MSC_VER | |||
| /* OPT: an MSVC built-in would be better */ | |||
| static _inline FLAC__uint32 local_swap32_(FLAC__uint32 x) | |||
| { | |||
| x = ((x<<8)&0xFF00FF00) | ((x>>8)&0x00FF00FF); | |||
| return (x>>16) | (x<<16); | |||
| } | |||
| #endif | |||
| #if defined(_MSC_VER) && defined(_X86_) | |||
| /* OPT: an MSVC built-in would be better */ | |||
| static void local_swap32_block_(FLAC__uint32 *start, FLAC__uint32 len) | |||
| { | |||
| __asm { | |||
| mov edx, start | |||
| mov ecx, len | |||
| test ecx, ecx | |||
| loop1: | |||
| jz done1 | |||
| mov eax, [edx] | |||
| bswap eax | |||
| mov [edx], eax | |||
| add edx, 4 | |||
| dec ecx | |||
| jmp short loop1 | |||
| done1: | |||
| } | |||
| } | |||
| #endif | |||
| /** \mainpage | |||
| * | |||
| * \section intro Introduction | |||
| * | |||
| * This is the documentation for the FLAC C and C++ APIs. It is | |||
| * highly interconnected; this introduction should give you a top | |||
| * level idea of the structure and how to find the information you | |||
| * need. As a prerequisite you should have at least a basic | |||
| * knowledge of the FLAC format, documented | |||
| * <A HREF="../format.html">here</A>. | |||
| * | |||
| * \section c_api FLAC C API | |||
| * | |||
| * The FLAC C API is the interface to libFLAC, a set of structures | |||
| * describing the components of FLAC streams, and functions for | |||
| * encoding and decoding streams, as well as manipulating FLAC | |||
| * metadata in files. The public include files will be installed | |||
| * in your include area (for example /usr/include/FLAC/...). | |||
| * | |||
| * By writing a little code and linking against libFLAC, it is | |||
| * relatively easy to add FLAC support to another program. The | |||
| * library is licensed under <A HREF="../license.html">Xiph's BSD license</A>. | |||
| * Complete source code of libFLAC as well as the command-line | |||
| * encoder and plugins is available and is a useful source of | |||
| * examples. | |||
| * | |||
| * Aside from encoders and decoders, libFLAC provides a powerful | |||
| * metadata interface for manipulating metadata in FLAC files. It | |||
| * allows the user to add, delete, and modify FLAC metadata blocks | |||
| * and it can automatically take advantage of PADDING blocks to avoid | |||
| * rewriting the entire FLAC file when changing the size of the | |||
| * metadata. | |||
| * | |||
| * libFLAC usually only requires the standard C library and C math | |||
| * library. In particular, threading is not used so there is no | |||
| * dependency on a thread library. However, libFLAC does not use | |||
| * global variables and should be thread-safe. | |||
| * | |||
| * libFLAC also supports encoding to and decoding from Ogg FLAC. | |||
| * However the metadata editing interfaces currently have limited | |||
| * read-only support for Ogg FLAC files. | |||
| * | |||
| * \section cpp_api FLAC C++ API | |||
| * | |||
| * The FLAC C++ API is a set of classes that encapsulate the | |||
| * structures and functions in libFLAC. They provide slightly more | |||
| * functionality with respect to metadata but are otherwise | |||
| * equivalent. For the most part, they share the same usage as | |||
| * their counterparts in libFLAC, and the FLAC C API documentation | |||
| * can be used as a supplement. The public include files | |||
| * for the C++ API will be installed in your include area (for | |||
| * example /usr/include/FLAC++/...). | |||
| * | |||
| * libFLAC++ is also licensed under | |||
| * <A HREF="../license.html">Xiph's BSD license</A>. | |||
| * | |||
| * \section getting_started Getting Started | |||
| * | |||
| * A good starting point for learning the API is to browse through | |||
| * the <A HREF="modules.html">modules</A>. Modules are logical | |||
| * groupings of related functions or classes, which correspond roughly | |||
| * to header files or sections of header files. Each module includes a | |||
| * detailed description of the general usage of its functions or | |||
| * classes. | |||
| * | |||
| * From there you can go on to look at the documentation of | |||
| * individual functions. You can see different views of the individual | |||
| * functions through the links in top bar across this page. | |||
| * | |||
| * If you prefer a more hands-on approach, you can jump right to some | |||
| * <A HREF="../documentation_example_code.html">example code</A>. | |||
| * | |||
| * \section porting_guide Porting Guide | |||
| * | |||
| * Starting with FLAC 1.1.3 a \link porting Porting Guide \endlink | |||
| * has been introduced which gives detailed instructions on how to | |||
| * port your code to newer versions of FLAC. | |||
| * | |||
| * \section embedded_developers Embedded Developers | |||
| * | |||
| * libFLAC has grown larger over time as more functionality has been | |||
| * included, but much of it may be unnecessary for a particular embedded | |||
| * implementation. Unused parts may be pruned by some simple editing of | |||
| * src/libFLAC/Makefile.am. In general, the decoders, encoders, and | |||
| * metadata interface are all independent from each other. | |||
| * | |||
| * It is easiest to just describe the dependencies: | |||
| * | |||
| * - All modules depend on the \link flac_format Format \endlink module. | |||
| * - The decoders and encoders depend on the bitbuffer. | |||
| * - The decoder is independent of the encoder. The encoder uses the | |||
| * decoder because of the verify feature, but this can be removed if | |||
| * not needed. | |||
| * - Parts of the metadata interface require the stream decoder (but not | |||
| * the encoder). | |||
| * - Ogg support is selectable through the compile time macro | |||
| * \c FLAC__HAS_OGG. | |||
| * | |||
| * For example, if your application only requires the stream decoder, no | |||
| * encoder, and no metadata interface, you can remove the stream encoder | |||
| * and the metadata interface, which will greatly reduce the size of the | |||
| * library. | |||
| * | |||
| * Also, there are several places in the libFLAC code with comments marked | |||
| * with "OPT:" where a #define can be changed to enable code that might be | |||
| * faster on a specific platform. Experimenting with these can yield faster | |||
| * binaries. | |||
| */ | |||
| /** \defgroup porting Porting Guide for New Versions | |||
| * | |||
| * This module describes differences in the library interfaces from | |||
| * version to version. It assists in the porting of code that uses | |||
| * the libraries to newer versions of FLAC. | |||
| * | |||
| * One simple facility for making porting easier that has been added | |||
| * in FLAC 1.1.3 is a set of \c #defines in \c export.h of each | |||
| * library's includes (e.g. \c include/FLAC/export.h). The | |||
| * \c #defines mirror the libraries' | |||
| * <A HREF="http://www.gnu.org/software/libtool/manual.html#Libtool-versioning">libtool version numbers</A>, | |||
| * e.g. in libFLAC there are \c FLAC_API_VERSION_CURRENT, | |||
| * \c FLAC_API_VERSION_REVISION, and \c FLAC_API_VERSION_AGE. | |||
| * These can be used to support multiple versions of an API during the | |||
| * transition phase, e.g. | |||
| * | |||
| * \code | |||
| * #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7 | |||
| * legacy code | |||
| * #else | |||
| * new code | |||
| * #endif | |||
| * \endcode | |||
| * | |||
| * The the source will work for multiple versions and the legacy code can | |||
| * easily be removed when the transition is complete. | |||
| * | |||
| * Another available symbol is FLAC_API_SUPPORTS_OGG_FLAC (defined in | |||
| * include/FLAC/export.h), which can be used to determine whether or not | |||
| * the library has been compiled with support for Ogg FLAC. This is | |||
| * simpler than trying to call an Ogg init function and catching the | |||
| * error. | |||
| */ | |||
| /** \defgroup porting_1_1_2_to_1_1_3 Porting from FLAC 1.1.2 to 1.1.3 | |||
| * \ingroup porting | |||
| * | |||
| * \brief | |||
| * This module describes porting from FLAC 1.1.2 to FLAC 1.1.3. | |||
| * | |||
| * The main change between the APIs in 1.1.2 and 1.1.3 is that they have | |||
| * been simplified. First, libOggFLAC has been merged into libFLAC and | |||
| * libOggFLAC++ has been merged into libFLAC++. Second, both the three | |||
| * decoding layers and three encoding layers have been merged into a | |||
| * single stream decoder and stream encoder. That is, the functionality | |||
| * of FLAC__SeekableStreamDecoder and FLAC__FileDecoder has been merged | |||
| * into FLAC__StreamDecoder, and FLAC__SeekableStreamEncoder and | |||
| * FLAC__FileEncoder into FLAC__StreamEncoder. Only the | |||
| * FLAC__StreamDecoder and FLAC__StreamEncoder remain. What this means | |||
| * is there is now a single API that can be used to encode or decode | |||
| * streams to/from native FLAC or Ogg FLAC and the single API can work | |||
| * on both seekable and non-seekable streams. | |||
| * | |||
| * Instead of creating an encoder or decoder of a certain layer, now the | |||
| * client will always create a FLAC__StreamEncoder or | |||
| * FLAC__StreamDecoder. The old layers are now differentiated by the | |||
| * initialization function. For example, for the decoder, | |||
| * FLAC__stream_decoder_init() has been replaced by | |||
| * FLAC__stream_decoder_init_stream(). This init function takes | |||
| * callbacks for the I/O, and the seeking callbacks are optional. This | |||
| * allows the client to use the same object for seekable and | |||
| * non-seekable streams. For decoding a FLAC file directly, the client | |||
| * can use FLAC__stream_decoder_init_file() and pass just a filename | |||
| * and fewer callbacks; most of the other callbacks are supplied | |||
| * internally. For situations where fopen()ing by filename is not | |||
| * possible (e.g. Unicode filenames on Windows) the client can instead | |||
| * open the file itself and supply the FILE* to | |||
| * FLAC__stream_decoder_init_FILE(). The init functions now returns a | |||
| * FLAC__StreamDecoderInitStatus instead of FLAC__StreamDecoderState. | |||
| * Since the callbacks and client data are now passed to the init | |||
| * function, the FLAC__stream_decoder_set_*_callback() functions and | |||
| * FLAC__stream_decoder_set_client_data() are no longer needed. The | |||
| * rest of the calls to the decoder are the same as before. | |||
| * | |||
| * There are counterpart init functions for Ogg FLAC, e.g. | |||
| * FLAC__stream_decoder_init_ogg_stream(). All the rest of the calls | |||
| * and callbacks are the same as for native FLAC. | |||
| * | |||
| * As an example, in FLAC 1.1.2 a seekable stream decoder would have | |||
| * been set up like so: | |||
| * | |||
| * \code | |||
| * FLAC__SeekableStreamDecoder *decoder = FLAC__seekable_stream_decoder_new(); | |||
| * if(decoder == NULL) do_something; | |||
| * FLAC__seekable_stream_decoder_set_md5_checking(decoder, true); | |||
| * [... other settings ...] | |||
| * FLAC__seekable_stream_decoder_set_read_callback(decoder, my_read_callback); | |||
| * FLAC__seekable_stream_decoder_set_seek_callback(decoder, my_seek_callback); | |||
| * FLAC__seekable_stream_decoder_set_tell_callback(decoder, my_tell_callback); | |||
| * FLAC__seekable_stream_decoder_set_length_callback(decoder, my_length_callback); | |||
| * FLAC__seekable_stream_decoder_set_eof_callback(decoder, my_eof_callback); | |||
| * FLAC__seekable_stream_decoder_set_write_callback(decoder, my_write_callback); | |||
| * FLAC__seekable_stream_decoder_set_metadata_callback(decoder, my_metadata_callback); | |||
| * FLAC__seekable_stream_decoder_set_error_callback(decoder, my_error_callback); | |||
| * FLAC__seekable_stream_decoder_set_client_data(decoder, my_client_data); | |||
| * if(FLAC__seekable_stream_decoder_init(decoder) != FLAC__SEEKABLE_STREAM_DECODER_OK) do_something; | |||
| * \endcode | |||
| * | |||
| * In FLAC 1.1.3 it is like this: | |||
| * | |||
| * \code | |||
| * FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new(); | |||
| * if(decoder == NULL) do_something; | |||
| * FLAC__stream_decoder_set_md5_checking(decoder, true); | |||
| * [... other settings ...] | |||
| * if(FLAC__stream_decoder_init_stream( | |||
| * decoder, | |||
| * my_read_callback, | |||
| * my_seek_callback, // or NULL | |||
| * my_tell_callback, // or NULL | |||
| * my_length_callback, // or NULL | |||
| * my_eof_callback, // or NULL | |||
| * my_write_callback, | |||
| * my_metadata_callback, // or NULL | |||
| * my_error_callback, | |||
| * my_client_data | |||
| * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; | |||
| * \endcode | |||
| * | |||
| * or you could do; | |||
| * | |||
| * \code | |||
| * [...] | |||
| * FILE *file = fopen("somefile.flac","rb"); | |||
| * if(file == NULL) do_somthing; | |||
| * if(FLAC__stream_decoder_init_FILE( | |||
| * decoder, | |||
| * file, | |||
| * my_write_callback, | |||
| * my_metadata_callback, // or NULL | |||
| * my_error_callback, | |||
| * my_client_data | |||
| * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; | |||
| * \endcode | |||
| * | |||
| * or just: | |||
| * | |||
| * \code | |||
| * [...] | |||
| * if(FLAC__stream_decoder_init_file( | |||
| * decoder, | |||
| * "somefile.flac", | |||
| * my_write_callback, | |||
| * my_metadata_callback, // or NULL | |||
| * my_error_callback, | |||
| * my_client_data | |||
| * ) != FLAC__STREAM_DECODER_INIT_STATUS_OK) do_something; | |||
| * \endcode | |||
| * | |||
| * Another small change to the decoder is in how it handles unparseable | |||
| * streams. Before, when the decoder found an unparseable stream | |||
| * (reserved for when the decoder encounters a stream from a future | |||
| * encoder that it can't parse), it changed the state to | |||
| * \c FLAC__STREAM_DECODER_UNPARSEABLE_STREAM. Now the decoder instead | |||
| * drops sync and calls the error callback with a new error code | |||
| * \c FLAC__STREAM_DECODER_ERROR_STATUS_UNPARSEABLE_STREAM. This is | |||
| * more robust. If your error callback does not discriminate on the the | |||
| * error state, your code does not need to be changed. | |||
| * | |||
| * The encoder now has a new setting: | |||
| * FLAC__stream_encoder_set_apodization(). This is for setting the | |||
| * method used to window the data before LPC analysis. You only need to | |||
| * add a call to this function if the default is not suitable. There | |||
| * are also two new convenience functions that may be useful: | |||
| * FLAC__metadata_object_cuesheet_calculate_cddb_id() and | |||
| * FLAC__metadata_get_cuesheet(). | |||
| * | |||
| * The \a bytes parameter to FLAC__StreamDecoderReadCallback, | |||
| * FLAC__StreamEncoderReadCallback, and FLAC__StreamEncoderWriteCallback | |||
| * is now \c size_t instead of \c unsigned. | |||
| */ | |||
| /** \defgroup porting_1_1_3_to_1_1_4 Porting from FLAC 1.1.3 to 1.1.4 | |||
| * \ingroup porting | |||
| * | |||
| * \brief | |||
| * This module describes porting from FLAC 1.1.3 to FLAC 1.1.4. | |||
| * | |||
| * There were no changes to any of the interfaces from 1.1.3 to 1.1.4. | |||
| * There was a slight change in the implementation of | |||
| * FLAC__stream_encoder_set_metadata(); the function now makes a copy | |||
| * of the \a metadata array of pointers so the client no longer needs | |||
| * to maintain it after the call. The objects themselves that are | |||
| * pointed to by the array are still not copied though and must be | |||
| * maintained until the call to FLAC__stream_encoder_finish(). | |||
| */ | |||
| /** \defgroup porting_1_1_4_to_1_2_0 Porting from FLAC 1.1.4 to 1.2.0 | |||
| * \ingroup porting | |||
| * | |||
| * \brief | |||
| * This module describes porting from FLAC 1.1.4 to FLAC 1.2.0. | |||
| * | |||
| * There were only very minor changes to the interfaces from 1.1.4 to 1.2.0. | |||
| * In libFLAC, \c FLAC__format_sample_rate_is_subset() was added. | |||
| * In libFLAC++, \c FLAC::Decoder::Stream::get_decode_position() was added. | |||
| * | |||
| * Finally, value of the constant \c FLAC__FRAME_HEADER_RESERVED_LEN | |||
| * has changed to reflect the conversion of one of the reserved bits | |||
| * into active use. It used to be \c 2 and now is \c 1. However the | |||
| * FLAC frame header length has not changed, so to skip the proper | |||
| * number of bits, use \c FLAC__FRAME_HEADER_RESERVED_LEN + | |||
| * \c FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN | |||
| */ | |||
| /** \defgroup flac FLAC C API | |||
| * | |||
| * The FLAC C API is the interface to libFLAC, a set of structures | |||
| * describing the components of FLAC streams, and functions for | |||
| * encoding and decoding streams, as well as manipulating FLAC | |||
| * metadata in files. | |||
| * | |||
| * You should start with the format components as all other modules | |||
| * are dependent on it. | |||
| */ | |||
| #endif | |||
| @@ -0,0 +1,212 @@ | |||
| /* alloc - Convenience routines for safely allocating memory | |||
| * Copyright (C) 2007 Josh Coalson | |||
| * | |||
| * This library is free software; you can redistribute it and/or | |||
| * modify it under the terms of the GNU Lesser General Public | |||
| * License as published by the Free Software Foundation; either | |||
| * version 2.1 of the License, or (at your option) any later version. | |||
| * | |||
| * This library is distributed in the hope that it will be useful, | |||
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| * Lesser General Public License for more details. | |||
| * | |||
| * You should have received a copy of the GNU Lesser General Public | |||
| * License along with this library; if not, write to the Free Software | |||
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |||
| */ | |||
| #ifndef FLAC__SHARE__ALLOC_H | |||
| #define FLAC__SHARE__ALLOC_H | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| /* WATCHOUT: for c++ you may have to #define __STDC_LIMIT_MACROS 1 real early | |||
| * before #including this file, otherwise SIZE_MAX might not be defined | |||
| */ | |||
| #include <limits.h> /* for SIZE_MAX */ | |||
| #if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__ | |||
| #include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */ | |||
| #endif | |||
| #include <stdlib.h> /* for size_t, malloc(), etc */ | |||
| #ifndef SIZE_MAX | |||
| # ifndef SIZE_T_MAX | |||
| # ifdef _MSC_VER | |||
| # define SIZE_T_MAX UINT_MAX | |||
| # else | |||
| # error | |||
| # endif | |||
| # endif | |||
| # define SIZE_MAX SIZE_T_MAX | |||
| #endif | |||
| #ifndef FLaC__INLINE | |||
| #define FLaC__INLINE | |||
| #endif | |||
| /* avoid malloc()ing 0 bytes, see: | |||
| * https://www.securecoding.cert.org/confluence/display/seccode/MEM04-A.+Do+not+make+assumptions+about+the+result+of+allocating+0+bytes?focusedCommentId=5407003 | |||
| */ | |||
| static FLaC__INLINE void *safe_malloc_(size_t size) | |||
| { | |||
| /* malloc(0) is undefined; FLAC src convention is to always allocate */ | |||
| if(!size) | |||
| size++; | |||
| return malloc(size); | |||
| } | |||
| static FLaC__INLINE void *safe_calloc_(size_t nmemb, size_t size) | |||
| { | |||
| if(!nmemb || !size) | |||
| return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ | |||
| return calloc(nmemb, size); | |||
| } | |||
| /*@@@@ there's probably a better way to prevent overflows when allocating untrusted sums but this works for now */ | |||
| static FLaC__INLINE void *safe_malloc_add_2op_(size_t size1, size_t size2) | |||
| { | |||
| size2 += size1; | |||
| if(size2 < size1) | |||
| return 0; | |||
| return safe_malloc_(size2); | |||
| } | |||
| static FLaC__INLINE void *safe_malloc_add_3op_(size_t size1, size_t size2, size_t size3) | |||
| { | |||
| size2 += size1; | |||
| if(size2 < size1) | |||
| return 0; | |||
| size3 += size2; | |||
| if(size3 < size2) | |||
| return 0; | |||
| return safe_malloc_(size3); | |||
| } | |||
| static FLaC__INLINE void *safe_malloc_add_4op_(size_t size1, size_t size2, size_t size3, size_t size4) | |||
| { | |||
| size2 += size1; | |||
| if(size2 < size1) | |||
| return 0; | |||
| size3 += size2; | |||
| if(size3 < size2) | |||
| return 0; | |||
| size4 += size3; | |||
| if(size4 < size3) | |||
| return 0; | |||
| return safe_malloc_(size4); | |||
| } | |||
| static FLaC__INLINE void *safe_malloc_mul_2op_(size_t size1, size_t size2) | |||
| #if 0 | |||
| needs support for cases where sizeof(size_t) != 4 | |||
| { | |||
| /* could be faster #ifdef'ing off SIZEOF_SIZE_T */ | |||
| if(sizeof(size_t) == 4) { | |||
| if ((double)size1 * (double)size2 < 4294967296.0) | |||
| return malloc(size1*size2); | |||
| } | |||
| return 0; | |||
| } | |||
| #else | |||
| /* better? */ | |||
| { | |||
| if(!size1 || !size2) | |||
| return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ | |||
| if(size1 > SIZE_MAX / size2) | |||
| return 0; | |||
| return malloc(size1*size2); | |||
| } | |||
| #endif | |||
| static FLaC__INLINE void *safe_malloc_mul_3op_(size_t size1, size_t size2, size_t size3) | |||
| { | |||
| if(!size1 || !size2 || !size3) | |||
| return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ | |||
| if(size1 > SIZE_MAX / size2) | |||
| return 0; | |||
| size1 *= size2; | |||
| if(size1 > SIZE_MAX / size3) | |||
| return 0; | |||
| return malloc(size1*size3); | |||
| } | |||
| /* size1*size2 + size3 */ | |||
| static FLaC__INLINE void *safe_malloc_mul2add_(size_t size1, size_t size2, size_t size3) | |||
| { | |||
| if(!size1 || !size2) | |||
| return safe_malloc_(size3); | |||
| if(size1 > SIZE_MAX / size2) | |||
| return 0; | |||
| return safe_malloc_add_2op_(size1*size2, size3); | |||
| } | |||
| /* size1 * (size2 + size3) */ | |||
| static FLaC__INLINE void *safe_malloc_muladd2_(size_t size1, size_t size2, size_t size3) | |||
| { | |||
| if(!size1 || (!size2 && !size3)) | |||
| return malloc(1); /* malloc(0) is undefined; FLAC src convention is to always allocate */ | |||
| size2 += size3; | |||
| if(size2 < size3) | |||
| return 0; | |||
| return safe_malloc_mul_2op_(size1, size2); | |||
| } | |||
| static FLaC__INLINE void *safe_realloc_add_2op_(void *ptr, size_t size1, size_t size2) | |||
| { | |||
| size2 += size1; | |||
| if(size2 < size1) | |||
| return 0; | |||
| return realloc(ptr, size2); | |||
| } | |||
| static FLaC__INLINE void *safe_realloc_add_3op_(void *ptr, size_t size1, size_t size2, size_t size3) | |||
| { | |||
| size2 += size1; | |||
| if(size2 < size1) | |||
| return 0; | |||
| size3 += size2; | |||
| if(size3 < size2) | |||
| return 0; | |||
| return realloc(ptr, size3); | |||
| } | |||
| static FLaC__INLINE void *safe_realloc_add_4op_(void *ptr, size_t size1, size_t size2, size_t size3, size_t size4) | |||
| { | |||
| size2 += size1; | |||
| if(size2 < size1) | |||
| return 0; | |||
| size3 += size2; | |||
| if(size3 < size2) | |||
| return 0; | |||
| size4 += size3; | |||
| if(size4 < size3) | |||
| return 0; | |||
| return realloc(ptr, size4); | |||
| } | |||
| static FLaC__INLINE void *safe_realloc_mul_2op_(void *ptr, size_t size1, size_t size2) | |||
| { | |||
| if(!size1 || !size2) | |||
| return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ | |||
| if(size1 > SIZE_MAX / size2) | |||
| return 0; | |||
| return realloc(ptr, size1*size2); | |||
| } | |||
| /* size1 * (size2 + size3) */ | |||
| static FLaC__INLINE void *safe_realloc_muladd2_(void *ptr, size_t size1, size_t size2, size_t size3) | |||
| { | |||
| if(!size1 || (!size2 && !size3)) | |||
| return realloc(ptr, 0); /* preserve POSIX realloc(ptr, 0) semantics */ | |||
| size2 += size3; | |||
| if(size2 < size3) | |||
| return 0; | |||
| return safe_realloc_mul_2op_(ptr, size1, size2); | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,45 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__ASSERT_H | |||
| #define FLAC__ASSERT_H | |||
| /* we need this since some compilers (like MSVC) leave assert()s on release code (and we don't want to use their ASSERT) */ | |||
| #ifdef DEBUG | |||
| #include <assert.h> | |||
| #define FLAC__ASSERT(x) assert(x) | |||
| #define FLAC__ASSERT_DECLARATION(x) x | |||
| #else | |||
| #define FLAC__ASSERT(x) | |||
| #define FLAC__ASSERT_DECLARATION(x) | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,184 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__CALLBACK_H | |||
| #define FLAC__CALLBACK_H | |||
| #include "ordinals.h" | |||
| #include <stdlib.h> /* for size_t */ | |||
| /** \file include/FLAC/callback.h | |||
| * | |||
| * \brief | |||
| * This module defines the structures for describing I/O callbacks | |||
| * to the other FLAC interfaces. | |||
| * | |||
| * See the detailed documentation for callbacks in the | |||
| * \link flac_callbacks callbacks \endlink module. | |||
| */ | |||
| /** \defgroup flac_callbacks FLAC/callback.h: I/O callback structures | |||
| * \ingroup flac | |||
| * | |||
| * \brief | |||
| * This module defines the structures for describing I/O callbacks | |||
| * to the other FLAC interfaces. | |||
| * | |||
| * The purpose of the I/O callback functions is to create a common way | |||
| * for the metadata interfaces to handle I/O. | |||
| * | |||
| * Originally the metadata interfaces required filenames as the way of | |||
| * specifying FLAC files to operate on. This is problematic in some | |||
| * environments so there is an additional option to specify a set of | |||
| * callbacks for doing I/O on the FLAC file, instead of the filename. | |||
| * | |||
| * In addition to the callbacks, a FLAC__IOHandle type is defined as an | |||
| * opaque structure for a data source. | |||
| * | |||
| * The callback function prototypes are similar (but not identical) to the | |||
| * stdio functions fread, fwrite, fseek, ftell, feof, and fclose. If you use | |||
| * stdio streams to implement the callbacks, you can pass fread, fwrite, and | |||
| * fclose anywhere a FLAC__IOCallback_Read, FLAC__IOCallback_Write, or | |||
| * FLAC__IOCallback_Close is required, and a FILE* anywhere a FLAC__IOHandle | |||
| * is required. \warning You generally CANNOT directly use fseek or ftell | |||
| * for FLAC__IOCallback_Seek or FLAC__IOCallback_Tell since on most systems | |||
| * these use 32-bit offsets and FLAC requires 64-bit offsets to deal with | |||
| * large files. You will have to find an equivalent function (e.g. ftello), | |||
| * or write a wrapper. The same is true for feof() since this is usually | |||
| * implemented as a macro, not as a function whose address can be taken. | |||
| * | |||
| * \{ | |||
| */ | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /** This is the opaque handle type used by the callbacks. Typically | |||
| * this is a \c FILE* or address of a file descriptor. | |||
| */ | |||
| typedef void* FLAC__IOHandle; | |||
| /** Signature for the read callback. | |||
| * The signature and semantics match POSIX fread() implementations | |||
| * and can generally be used interchangeably. | |||
| * | |||
| * \param ptr The address of the read buffer. | |||
| * \param size The size of the records to be read. | |||
| * \param nmemb The number of records to be read. | |||
| * \param handle The handle to the data source. | |||
| * \retval size_t | |||
| * The number of records read. | |||
| */ | |||
| typedef size_t (*FLAC__IOCallback_Read) (void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); | |||
| /** Signature for the write callback. | |||
| * The signature and semantics match POSIX fwrite() implementations | |||
| * and can generally be used interchangeably. | |||
| * | |||
| * \param ptr The address of the write buffer. | |||
| * \param size The size of the records to be written. | |||
| * \param nmemb The number of records to be written. | |||
| * \param handle The handle to the data source. | |||
| * \retval size_t | |||
| * The number of records written. | |||
| */ | |||
| typedef size_t (*FLAC__IOCallback_Write) (const void *ptr, size_t size, size_t nmemb, FLAC__IOHandle handle); | |||
| /** Signature for the seek callback. | |||
| * The signature and semantics mostly match POSIX fseek() WITH ONE IMPORTANT | |||
| * EXCEPTION: the offset is a 64-bit type whereas fseek() is generally 'long' | |||
| * and 32-bits wide. | |||
| * | |||
| * \param handle The handle to the data source. | |||
| * \param offset The new position, relative to \a whence | |||
| * \param whence \c SEEK_SET, \c SEEK_CUR, or \c SEEK_END | |||
| * \retval int | |||
| * \c 0 on success, \c -1 on error. | |||
| */ | |||
| typedef int (*FLAC__IOCallback_Seek) (FLAC__IOHandle handle, FLAC__int64 offset, int whence); | |||
| /** Signature for the tell callback. | |||
| * The signature and semantics mostly match POSIX ftell() WITH ONE IMPORTANT | |||
| * EXCEPTION: the offset is a 64-bit type whereas ftell() is generally 'long' | |||
| * and 32-bits wide. | |||
| * | |||
| * \param handle The handle to the data source. | |||
| * \retval FLAC__int64 | |||
| * The current position on success, \c -1 on error. | |||
| */ | |||
| typedef FLAC__int64 (*FLAC__IOCallback_Tell) (FLAC__IOHandle handle); | |||
| /** Signature for the EOF callback. | |||
| * The signature and semantics mostly match POSIX feof() but WATCHOUT: | |||
| * on many systems, feof() is a macro, so in this case a wrapper function | |||
| * must be provided instead. | |||
| * | |||
| * \param handle The handle to the data source. | |||
| * \retval int | |||
| * \c 0 if not at end of file, nonzero if at end of file. | |||
| */ | |||
| typedef int (*FLAC__IOCallback_Eof) (FLAC__IOHandle handle); | |||
| /** Signature for the close callback. | |||
| * The signature and semantics match POSIX fclose() implementations | |||
| * and can generally be used interchangeably. | |||
| * | |||
| * \param handle The handle to the data source. | |||
| * \retval int | |||
| * \c 0 on success, \c EOF on error. | |||
| */ | |||
| typedef int (*FLAC__IOCallback_Close) (FLAC__IOHandle handle); | |||
| /** A structure for holding a set of callbacks. | |||
| * Each FLAC interface that requires a FLAC__IOCallbacks structure will | |||
| * describe which of the callbacks are required. The ones that are not | |||
| * required may be set to NULL. | |||
| * | |||
| * If the seek requirement for an interface is optional, you can signify that | |||
| * a data sorce is not seekable by setting the \a seek field to \c NULL. | |||
| */ | |||
| typedef struct { | |||
| FLAC__IOCallback_Read read; | |||
| FLAC__IOCallback_Write write; | |||
| FLAC__IOCallback_Seek seek; | |||
| FLAC__IOCallback_Tell tell; | |||
| FLAC__IOCallback_Eof eof; | |||
| FLAC__IOCallback_Close close; | |||
| } FLAC__IOCallbacks; | |||
| /* \} */ | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,91 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__EXPORT_H | |||
| #define FLAC__EXPORT_H | |||
| /** \file include/FLAC/export.h | |||
| * | |||
| * \brief | |||
| * This module contains #defines and symbols for exporting function | |||
| * calls, and providing version information and compiled-in features. | |||
| * | |||
| * See the \link flac_export export \endlink module. | |||
| */ | |||
| /** \defgroup flac_export FLAC/export.h: export symbols | |||
| * \ingroup flac | |||
| * | |||
| * \brief | |||
| * This module contains #defines and symbols for exporting function | |||
| * calls, and providing version information and compiled-in features. | |||
| * | |||
| * If you are compiling with MSVC and will link to the static library | |||
| * (libFLAC.lib) you should define FLAC__NO_DLL in your project to | |||
| * make sure the symbols are exported properly. | |||
| * | |||
| * \{ | |||
| */ | |||
| #if defined(FLAC__NO_DLL) || !defined(_MSC_VER) | |||
| #define FLAC_API | |||
| #else | |||
| #ifdef FLAC_API_EXPORTS | |||
| #define FLAC_API _declspec(dllexport) | |||
| #else | |||
| #define FLAC_API _declspec(dllimport) | |||
| #endif | |||
| #endif | |||
| /** These #defines will mirror the libtool-based library version number, see | |||
| * http://www.gnu.org/software/libtool/manual.html#Libtool-versioning | |||
| */ | |||
| #define FLAC_API_VERSION_CURRENT 10 | |||
| #define FLAC_API_VERSION_REVISION 0 /**< see above */ | |||
| #define FLAC_API_VERSION_AGE 2 /**< see above */ | |||
| #ifdef __cplusplus | |||
| extern "C" { | |||
| #endif | |||
| /** \c 1 if the library has been compiled with support for Ogg FLAC, else \c 0. */ | |||
| extern FLAC_API int FLAC_API_SUPPORTS_OGG_FLAC; | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| /* \} */ | |||
| #endif | |||
| @@ -0,0 +1,149 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include "include/private/bitmath.h" | |||
| #include "../assert.h" | |||
| /* An example of what FLAC__bitmath_ilog2() computes: | |||
| * | |||
| * ilog2( 0) = assertion failure | |||
| * ilog2( 1) = 0 | |||
| * ilog2( 2) = 1 | |||
| * ilog2( 3) = 1 | |||
| * ilog2( 4) = 2 | |||
| * ilog2( 5) = 2 | |||
| * ilog2( 6) = 2 | |||
| * ilog2( 7) = 2 | |||
| * ilog2( 8) = 3 | |||
| * ilog2( 9) = 3 | |||
| * ilog2(10) = 3 | |||
| * ilog2(11) = 3 | |||
| * ilog2(12) = 3 | |||
| * ilog2(13) = 3 | |||
| * ilog2(14) = 3 | |||
| * ilog2(15) = 3 | |||
| * ilog2(16) = 4 | |||
| * ilog2(17) = 4 | |||
| * ilog2(18) = 4 | |||
| */ | |||
| unsigned FLAC__bitmath_ilog2(FLAC__uint32 v) | |||
| { | |||
| unsigned l = 0; | |||
| FLAC__ASSERT(v > 0); | |||
| while(v >>= 1) | |||
| l++; | |||
| return l; | |||
| } | |||
| unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v) | |||
| { | |||
| unsigned l = 0; | |||
| FLAC__ASSERT(v > 0); | |||
| while(v >>= 1) | |||
| l++; | |||
| return l; | |||
| } | |||
| /* An example of what FLAC__bitmath_silog2() computes: | |||
| * | |||
| * silog2(-10) = 5 | |||
| * silog2(- 9) = 5 | |||
| * silog2(- 8) = 4 | |||
| * silog2(- 7) = 4 | |||
| * silog2(- 6) = 4 | |||
| * silog2(- 5) = 4 | |||
| * silog2(- 4) = 3 | |||
| * silog2(- 3) = 3 | |||
| * silog2(- 2) = 2 | |||
| * silog2(- 1) = 2 | |||
| * silog2( 0) = 0 | |||
| * silog2( 1) = 2 | |||
| * silog2( 2) = 3 | |||
| * silog2( 3) = 3 | |||
| * silog2( 4) = 4 | |||
| * silog2( 5) = 4 | |||
| * silog2( 6) = 4 | |||
| * silog2( 7) = 4 | |||
| * silog2( 8) = 5 | |||
| * silog2( 9) = 5 | |||
| * silog2( 10) = 5 | |||
| */ | |||
| unsigned FLAC__bitmath_silog2(int v) | |||
| { | |||
| while(1) { | |||
| if(v == 0) { | |||
| return 0; | |||
| } | |||
| else if(v > 0) { | |||
| unsigned l = 0; | |||
| while(v) { | |||
| l++; | |||
| v >>= 1; | |||
| } | |||
| return l+1; | |||
| } | |||
| else if(v == -1) { | |||
| return 2; | |||
| } | |||
| else { | |||
| v++; | |||
| v = -v; | |||
| } | |||
| } | |||
| } | |||
| unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v) | |||
| { | |||
| while(1) { | |||
| if(v == 0) { | |||
| return 0; | |||
| } | |||
| else if(v > 0) { | |||
| unsigned l = 0; | |||
| while(v) { | |||
| l++; | |||
| v >>= 1; | |||
| } | |||
| return l+1; | |||
| } | |||
| else if(v == -1) { | |||
| return 2; | |||
| } | |||
| else { | |||
| v++; | |||
| v = -v; | |||
| } | |||
| } | |||
| } | |||
| @@ -0,0 +1,880 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include <stdlib.h> /* for malloc() */ | |||
| #include <string.h> /* for memcpy(), memset() */ | |||
| #ifdef _MSC_VER | |||
| #include <winsock.h> /* for ntohl() */ | |||
| #elif defined FLAC__SYS_DARWIN | |||
| #include <machine/endian.h> /* for ntohl() */ | |||
| #elif defined __MINGW32__ | |||
| #include <winsock.h> /* for ntohl() */ | |||
| #else | |||
| #include <netinet/in.h> /* for ntohl() */ | |||
| #endif | |||
| #if 0 /* UNUSED */ | |||
| #include "include/private/bitmath.h" | |||
| #endif | |||
| #include "include/private/bitwriter.h" | |||
| #include "include/private/crc.h" | |||
| #include "../assert.h" | |||
| #include "../alloc.h" | |||
| /* Things should be fastest when this matches the machine word size */ | |||
| /* WATCHOUT: if you change this you must also change the following #defines down to SWAP_BE_WORD_TO_HOST below to match */ | |||
| /* WATCHOUT: there are a few places where the code will not work unless bwword is >= 32 bits wide */ | |||
| typedef FLAC__uint32 bwword; | |||
| #define FLAC__BYTES_PER_WORD 4 | |||
| #define FLAC__BITS_PER_WORD 32 | |||
| #define FLAC__WORD_ALL_ONES ((FLAC__uint32)0xffffffff) | |||
| /* SWAP_BE_WORD_TO_HOST swaps bytes in a bwword (which is always big-endian) if necessary to match host byte order */ | |||
| #if WORDS_BIGENDIAN | |||
| #define SWAP_BE_WORD_TO_HOST(x) (x) | |||
| #else | |||
| #ifdef _MSC_VER | |||
| #define SWAP_BE_WORD_TO_HOST(x) local_swap32_(x) | |||
| #else | |||
| #define SWAP_BE_WORD_TO_HOST(x) ntohl(x) | |||
| #endif | |||
| #endif | |||
| /* | |||
| * The default capacity here doesn't matter too much. The buffer always grows | |||
| * to hold whatever is written to it. Usually the encoder will stop adding at | |||
| * a frame or metadata block, then write that out and clear the buffer for the | |||
| * next one. | |||
| */ | |||
| static const unsigned FLAC__BITWRITER_DEFAULT_CAPACITY = 32768u / sizeof(bwword); /* size in words */ | |||
| /* When growing, increment 4K at a time */ | |||
| static const unsigned FLAC__BITWRITER_DEFAULT_INCREMENT = 4096u / sizeof(bwword); /* size in words */ | |||
| #define FLAC__WORDS_TO_BITS(words) ((words) * FLAC__BITS_PER_WORD) | |||
| #define FLAC__TOTAL_BITS(bw) (FLAC__WORDS_TO_BITS((bw)->words) + (bw)->bits) | |||
| #ifdef min | |||
| #undef min | |||
| #endif | |||
| #define min(x,y) ((x)<(y)?(x):(y)) | |||
| /* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ | |||
| #ifdef _MSC_VER | |||
| #define FLAC__U64L(x) x | |||
| #else | |||
| #define FLAC__U64L(x) x##LLU | |||
| #endif | |||
| #ifndef FLaC__INLINE | |||
| #define FLaC__INLINE | |||
| #endif | |||
| struct FLAC__BitWriter { | |||
| bwword *buffer; | |||
| bwword accum; /* accumulator; bits are right-justified; when full, accum is appended to buffer */ | |||
| unsigned capacity; /* capacity of buffer in words */ | |||
| unsigned words; /* # of complete words in buffer */ | |||
| unsigned bits; /* # of used bits in accum */ | |||
| }; | |||
| /* * WATCHOUT: The current implementation only grows the buffer. */ | |||
| static FLAC__bool bitwriter_grow_(FLAC__BitWriter *bw, unsigned bits_to_add) | |||
| { | |||
| unsigned new_capacity; | |||
| bwword *new_buffer; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| /* calculate total words needed to store 'bits_to_add' additional bits */ | |||
| new_capacity = bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD); | |||
| /* it's possible (due to pessimism in the growth estimation that | |||
| * leads to this call) that we don't actually need to grow | |||
| */ | |||
| if(bw->capacity >= new_capacity) | |||
| return true; | |||
| /* round up capacity increase to the nearest FLAC__BITWRITER_DEFAULT_INCREMENT */ | |||
| if((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT) | |||
| new_capacity += FLAC__BITWRITER_DEFAULT_INCREMENT - ((new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); | |||
| /* make sure we got everything right */ | |||
| FLAC__ASSERT(0 == (new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT); | |||
| FLAC__ASSERT(new_capacity > bw->capacity); | |||
| FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD)); | |||
| new_buffer = (bwword*)safe_realloc_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity); | |||
| if(new_buffer == 0) | |||
| return false; | |||
| bw->buffer = new_buffer; | |||
| bw->capacity = new_capacity; | |||
| return true; | |||
| } | |||
| /*********************************************************************** | |||
| * | |||
| * Class constructor/destructor | |||
| * | |||
| ***********************************************************************/ | |||
| FLAC__BitWriter *FLAC__bitwriter_new(void) | |||
| { | |||
| FLAC__BitWriter *bw = (FLAC__BitWriter*)calloc(1, sizeof(FLAC__BitWriter)); | |||
| /* note that calloc() sets all members to 0 for us */ | |||
| return bw; | |||
| } | |||
| void FLAC__bitwriter_delete(FLAC__BitWriter *bw) | |||
| { | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__bitwriter_free(bw); | |||
| free(bw); | |||
| } | |||
| /*********************************************************************** | |||
| * | |||
| * Public class methods | |||
| * | |||
| ***********************************************************************/ | |||
| FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw) | |||
| { | |||
| FLAC__ASSERT(0 != bw); | |||
| bw->words = bw->bits = 0; | |||
| bw->capacity = FLAC__BITWRITER_DEFAULT_CAPACITY; | |||
| bw->buffer = (bwword*)malloc(sizeof(bwword) * bw->capacity); | |||
| if(bw->buffer == 0) | |||
| return false; | |||
| return true; | |||
| } | |||
| void FLAC__bitwriter_free(FLAC__BitWriter *bw) | |||
| { | |||
| FLAC__ASSERT(0 != bw); | |||
| if(0 != bw->buffer) | |||
| free(bw->buffer); | |||
| bw->buffer = 0; | |||
| bw->capacity = 0; | |||
| bw->words = bw->bits = 0; | |||
| } | |||
| void FLAC__bitwriter_clear(FLAC__BitWriter *bw) | |||
| { | |||
| bw->words = bw->bits = 0; | |||
| } | |||
| void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out) | |||
| { | |||
| unsigned i, j; | |||
| if(bw == 0) { | |||
| fprintf(out, "bitwriter is NULL\n"); | |||
| } | |||
| else { | |||
| fprintf(out, "bitwriter: capacity=%u words=%u bits=%u total_bits=%u\n", bw->capacity, bw->words, bw->bits, FLAC__TOTAL_BITS(bw)); | |||
| for(i = 0; i < bw->words; i++) { | |||
| fprintf(out, "%08X: ", i); | |||
| for(j = 0; j < FLAC__BITS_PER_WORD; j++) | |||
| fprintf(out, "%01u", bw->buffer[i] & (1 << (FLAC__BITS_PER_WORD-j-1)) ? 1:0); | |||
| fprintf(out, "\n"); | |||
| } | |||
| if(bw->bits > 0) { | |||
| fprintf(out, "%08X: ", i); | |||
| for(j = 0; j < bw->bits; j++) | |||
| fprintf(out, "%01u", bw->accum & (1 << (bw->bits-j-1)) ? 1:0); | |||
| fprintf(out, "\n"); | |||
| } | |||
| } | |||
| } | |||
| FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc) | |||
| { | |||
| const FLAC__byte *buffer; | |||
| size_t bytes; | |||
| FLAC__ASSERT((bw->bits & 7) == 0); /* assert that we're byte-aligned */ | |||
| if(!FLAC__bitwriter_get_buffer(bw, &buffer, &bytes)) | |||
| return false; | |||
| *crc = (FLAC__uint16)FLAC__crc16(buffer, bytes); | |||
| FLAC__bitwriter_release_buffer(bw); | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc) | |||
| { | |||
| const FLAC__byte *buffer; | |||
| size_t bytes; | |||
| FLAC__ASSERT((bw->bits & 7) == 0); /* assert that we're byte-aligned */ | |||
| if(!FLAC__bitwriter_get_buffer(bw, &buffer, &bytes)) | |||
| return false; | |||
| *crc = FLAC__crc8(buffer, bytes); | |||
| FLAC__bitwriter_release_buffer(bw); | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw) | |||
| { | |||
| return ((bw->bits & 7) == 0); | |||
| } | |||
| unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw) | |||
| { | |||
| return FLAC__TOTAL_BITS(bw); | |||
| } | |||
| FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes) | |||
| { | |||
| FLAC__ASSERT((bw->bits & 7) == 0); | |||
| /* double protection */ | |||
| if(bw->bits & 7) | |||
| return false; | |||
| /* if we have bits in the accumulator we have to flush those to the buffer first */ | |||
| if(bw->bits) { | |||
| FLAC__ASSERT(bw->words <= bw->capacity); | |||
| if(bw->words == bw->capacity && !bitwriter_grow_(bw, FLAC__BITS_PER_WORD)) | |||
| return false; | |||
| /* append bits as complete word to buffer, but don't change bw->accum or bw->bits */ | |||
| bw->buffer[bw->words] = SWAP_BE_WORD_TO_HOST(bw->accum << (FLAC__BITS_PER_WORD-bw->bits)); | |||
| } | |||
| /* now we can just return what we have */ | |||
| *buffer = (FLAC__byte*)bw->buffer; | |||
| *bytes = (FLAC__BYTES_PER_WORD * bw->words) + (bw->bits >> 3); | |||
| return true; | |||
| } | |||
| void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw) | |||
| { | |||
| /* nothing to do. in the future, strict checking of a 'writer-is-in- | |||
| * get-mode' flag could be added everywhere and then cleared here | |||
| */ | |||
| (void)bw; | |||
| } | |||
| FLaC__INLINE FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits) | |||
| { | |||
| unsigned n; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| if(bits == 0) | |||
| return true; | |||
| /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ | |||
| if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) | |||
| return false; | |||
| /* first part gets to word alignment */ | |||
| if(bw->bits) { | |||
| n = min(FLAC__BITS_PER_WORD - bw->bits, bits); | |||
| bw->accum <<= n; | |||
| bits -= n; | |||
| bw->bits += n; | |||
| if(bw->bits == FLAC__BITS_PER_WORD) { | |||
| bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); | |||
| bw->bits = 0; | |||
| } | |||
| else | |||
| return true; | |||
| } | |||
| /* do whole words */ | |||
| while(bits >= FLAC__BITS_PER_WORD) { | |||
| bw->buffer[bw->words++] = 0; | |||
| bits -= FLAC__BITS_PER_WORD; | |||
| } | |||
| /* do any leftovers */ | |||
| if(bits > 0) { | |||
| bw->accum = 0; | |||
| bw->bits = bits; | |||
| } | |||
| return true; | |||
| } | |||
| FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits) | |||
| { | |||
| register unsigned left; | |||
| /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ | |||
| FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(bits <= 32); | |||
| if(bits == 0) | |||
| return true; | |||
| /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+bits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ | |||
| if(bw->capacity <= bw->words + bits && !bitwriter_grow_(bw, bits)) | |||
| return false; | |||
| left = FLAC__BITS_PER_WORD - bw->bits; | |||
| if(bits < left) { | |||
| bw->accum <<= bits; | |||
| bw->accum |= val; | |||
| bw->bits += bits; | |||
| } | |||
| else if(bw->bits) { /* WATCHOUT: if bw->bits == 0, left==FLAC__BITS_PER_WORD and bw->accum<<=left is a NOP instead of setting to 0 */ | |||
| bw->accum <<= left; | |||
| bw->accum |= val >> (bw->bits = bits - left); | |||
| bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); | |||
| bw->accum = val; | |||
| } | |||
| else { | |||
| bw->accum = val; | |||
| bw->bits = 0; | |||
| bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(val); | |||
| } | |||
| return true; | |||
| } | |||
| FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits) | |||
| { | |||
| /* zero-out unused bits */ | |||
| if(bits < 32) | |||
| val &= (~(0xffffffff << bits)); | |||
| return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); | |||
| } | |||
| FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits) | |||
| { | |||
| /* this could be a little faster but it's not used for much */ | |||
| if(bits > 32) { | |||
| return | |||
| FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(val>>32), bits-32) && | |||
| FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 32); | |||
| } | |||
| else | |||
| return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, bits); | |||
| } | |||
| FLaC__INLINE FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val) | |||
| { | |||
| /* this doesn't need to be that fast as currently it is only used for vorbis comments */ | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, val & 0xff, 8)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>8) & 0xff, 8)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, (val>>16) & 0xff, 8)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, val>>24, 8)) | |||
| return false; | |||
| return true; | |||
| } | |||
| FLaC__INLINE FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals) | |||
| { | |||
| unsigned i; | |||
| /* this could be faster but currently we don't need it to be since it's only used for writing metadata */ | |||
| for(i = 0; i < nvals; i++) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)(vals[i]), 8)) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val) | |||
| { | |||
| if(val < 32) | |||
| return FLAC__bitwriter_write_raw_uint32(bw, 1, ++val); | |||
| else | |||
| return | |||
| FLAC__bitwriter_write_zeroes(bw, val) && | |||
| FLAC__bitwriter_write_raw_uint32(bw, 1, 1); | |||
| } | |||
| unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter) | |||
| { | |||
| FLAC__uint32 uval; | |||
| FLAC__ASSERT(parameter < sizeof(unsigned)*8); | |||
| /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ | |||
| uval = (val<<1) ^ (val>>31); | |||
| return 1 + parameter + (uval >> parameter); | |||
| } | |||
| #if 0 /* UNUSED */ | |||
| unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter) | |||
| { | |||
| unsigned bits, msbs, uval; | |||
| unsigned k; | |||
| FLAC__ASSERT(parameter > 0); | |||
| /* fold signed to unsigned */ | |||
| if(val < 0) | |||
| uval = (unsigned)(((-(++val)) << 1) + 1); | |||
| else | |||
| uval = (unsigned)(val << 1); | |||
| k = FLAC__bitmath_ilog2(parameter); | |||
| if(parameter == 1u<<k) { | |||
| FLAC__ASSERT(k <= 30); | |||
| msbs = uval >> k; | |||
| bits = 1 + k + msbs; | |||
| } | |||
| else { | |||
| unsigned q, r, d; | |||
| d = (1 << (k+1)) - parameter; | |||
| q = uval / parameter; | |||
| r = uval - (q * parameter); | |||
| bits = 1 + q + k; | |||
| if(r >= d) | |||
| bits++; | |||
| } | |||
| return bits; | |||
| } | |||
| unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned uval, unsigned parameter) | |||
| { | |||
| unsigned bits, msbs; | |||
| unsigned k; | |||
| FLAC__ASSERT(parameter > 0); | |||
| k = FLAC__bitmath_ilog2(parameter); | |||
| if(parameter == 1u<<k) { | |||
| FLAC__ASSERT(k <= 30); | |||
| msbs = uval >> k; | |||
| bits = 1 + k + msbs; | |||
| } | |||
| else { | |||
| unsigned q, r, d; | |||
| d = (1 << (k+1)) - parameter; | |||
| q = uval / parameter; | |||
| r = uval - (q * parameter); | |||
| bits = 1 + q + k; | |||
| if(r >= d) | |||
| bits++; | |||
| } | |||
| return bits; | |||
| } | |||
| #endif /* UNUSED */ | |||
| FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter) | |||
| { | |||
| unsigned total_bits, interesting_bits, msbs; | |||
| FLAC__uint32 uval, pattern; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(parameter < 8*sizeof(uval)); | |||
| /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ | |||
| uval = (val<<1) ^ (val>>31); | |||
| msbs = uval >> parameter; | |||
| interesting_bits = 1 + parameter; | |||
| total_bits = interesting_bits + msbs; | |||
| pattern = 1 << parameter; /* the unary end bit */ | |||
| pattern |= (uval & ((1<<parameter)-1)); /* the binary LSBs */ | |||
| if(total_bits <= 32) | |||
| return FLAC__bitwriter_write_raw_uint32(bw, pattern, total_bits); | |||
| else | |||
| return | |||
| FLAC__bitwriter_write_zeroes(bw, msbs) && /* write the unary MSBs */ | |||
| FLAC__bitwriter_write_raw_uint32(bw, pattern, interesting_bits); /* write the unary end bit and binary LSBs */ | |||
| } | |||
| FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter) | |||
| { | |||
| const FLAC__uint32 mask1 = FLAC__WORD_ALL_ONES << parameter; /* we val|=mask1 to set the stop bit above it... */ | |||
| const FLAC__uint32 mask2 = FLAC__WORD_ALL_ONES >> (31-parameter); /* ...then mask off the bits above the stop bit with val&=mask2*/ | |||
| FLAC__uint32 uval; | |||
| unsigned left; | |||
| const unsigned lsbits = 1 + parameter; | |||
| unsigned msbits; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(parameter < 8*sizeof(bwword)-1); | |||
| /* WATCHOUT: code does not work with <32bit words; we can make things much faster with this assertion */ | |||
| FLAC__ASSERT(FLAC__BITS_PER_WORD >= 32); | |||
| while(nvals) { | |||
| /* fold signed to unsigned; actual formula is: negative(v)? -2v-1 : 2v */ | |||
| uval = (*vals<<1) ^ (*vals>>31); | |||
| msbits = uval >> parameter; | |||
| #if 0 /* OPT: can remove this special case if it doesn't make up for the extra compare (doesn't make a statistically significant difference with msvc or gcc/x86) */ | |||
| if(bw->bits && bw->bits + msbits + lsbits <= FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current bwword */ | |||
| /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free bwword to work in */ | |||
| bw->bits = bw->bits + msbits + lsbits; | |||
| uval |= mask1; /* set stop bit */ | |||
| uval &= mask2; /* mask off unused top bits */ | |||
| /* NOT: bw->accum <<= msbits + lsbits because msbits+lsbits could be 32, then the shift would be a NOP */ | |||
| bw->accum <<= msbits; | |||
| bw->accum <<= lsbits; | |||
| bw->accum |= uval; | |||
| if(bw->bits == FLAC__BITS_PER_WORD) { | |||
| bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); | |||
| bw->bits = 0; | |||
| /* burying the capacity check down here means we have to grow the buffer a little if there are more vals to do */ | |||
| if(bw->capacity <= bw->words && nvals > 1 && !bitwriter_grow_(bw, 1)) { | |||
| FLAC__ASSERT(bw->capacity == bw->words); | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| else { | |||
| #elif 1 /*@@@@@@ OPT: try this version with MSVC6 to see if better, not much difference for gcc-4 */ | |||
| if(bw->bits && bw->bits + msbits + lsbits < FLAC__BITS_PER_WORD) { /* i.e. if the whole thing fits in the current bwword */ | |||
| /* ^^^ if bw->bits is 0 then we may have filled the buffer and have no free bwword to work in */ | |||
| bw->bits = bw->bits + msbits + lsbits; | |||
| uval |= mask1; /* set stop bit */ | |||
| uval &= mask2; /* mask off unused top bits */ | |||
| bw->accum <<= msbits + lsbits; | |||
| bw->accum |= uval; | |||
| } | |||
| else { | |||
| #endif | |||
| /* slightly pessimistic size check but faster than "<= bw->words + (bw->bits+msbits+lsbits+FLAC__BITS_PER_WORD-1)/FLAC__BITS_PER_WORD" */ | |||
| /* OPT: pessimism may cause flurry of false calls to grow_ which eat up all savings before it */ | |||
| if(bw->capacity <= bw->words + bw->bits + msbits + 1/*lsbits always fit in 1 bwword*/ && !bitwriter_grow_(bw, msbits+lsbits)) | |||
| return false; | |||
| if(msbits) { | |||
| /* first part gets to word alignment */ | |||
| if(bw->bits) { | |||
| left = FLAC__BITS_PER_WORD - bw->bits; | |||
| if(msbits < left) { | |||
| bw->accum <<= msbits; | |||
| bw->bits += msbits; | |||
| goto break1; | |||
| } | |||
| else { | |||
| bw->accum <<= left; | |||
| msbits -= left; | |||
| bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); | |||
| bw->bits = 0; | |||
| } | |||
| } | |||
| /* do whole words */ | |||
| while(msbits >= FLAC__BITS_PER_WORD) { | |||
| bw->buffer[bw->words++] = 0; | |||
| msbits -= FLAC__BITS_PER_WORD; | |||
| } | |||
| /* do any leftovers */ | |||
| if(msbits > 0) { | |||
| bw->accum = 0; | |||
| bw->bits = msbits; | |||
| } | |||
| } | |||
| break1: | |||
| uval |= mask1; /* set stop bit */ | |||
| uval &= mask2; /* mask off unused top bits */ | |||
| left = FLAC__BITS_PER_WORD - bw->bits; | |||
| if(lsbits < left) { | |||
| bw->accum <<= lsbits; | |||
| bw->accum |= uval; | |||
| bw->bits += lsbits; | |||
| } | |||
| else { | |||
| /* if bw->bits == 0, left==FLAC__BITS_PER_WORD which will always | |||
| * be > lsbits (because of previous assertions) so it would have | |||
| * triggered the (lsbits<left) case above. | |||
| */ | |||
| FLAC__ASSERT(bw->bits); | |||
| FLAC__ASSERT(left < FLAC__BITS_PER_WORD); | |||
| bw->accum <<= left; | |||
| bw->accum |= uval >> (bw->bits = lsbits - left); | |||
| bw->buffer[bw->words++] = SWAP_BE_WORD_TO_HOST(bw->accum); | |||
| bw->accum = uval; | |||
| } | |||
| #if 1 | |||
| } | |||
| #endif | |||
| vals++; | |||
| nvals--; | |||
| } | |||
| return true; | |||
| } | |||
| #if 0 /* UNUSED */ | |||
| FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter) | |||
| { | |||
| unsigned total_bits, msbs, uval; | |||
| unsigned k; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(parameter > 0); | |||
| /* fold signed to unsigned */ | |||
| if(val < 0) | |||
| uval = (unsigned)(((-(++val)) << 1) + 1); | |||
| else | |||
| uval = (unsigned)(val << 1); | |||
| k = FLAC__bitmath_ilog2(parameter); | |||
| if(parameter == 1u<<k) { | |||
| unsigned pattern; | |||
| FLAC__ASSERT(k <= 30); | |||
| msbs = uval >> k; | |||
| total_bits = 1 + k + msbs; | |||
| pattern = 1 << k; /* the unary end bit */ | |||
| pattern |= (uval & ((1u<<k)-1)); /* the binary LSBs */ | |||
| if(total_bits <= 32) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, pattern, total_bits)) | |||
| return false; | |||
| } | |||
| else { | |||
| /* write the unary MSBs */ | |||
| if(!FLAC__bitwriter_write_zeroes(bw, msbs)) | |||
| return false; | |||
| /* write the unary end bit and binary LSBs */ | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, pattern, k+1)) | |||
| return false; | |||
| } | |||
| } | |||
| else { | |||
| unsigned q, r, d; | |||
| d = (1 << (k+1)) - parameter; | |||
| q = uval / parameter; | |||
| r = uval - (q * parameter); | |||
| /* write the unary MSBs */ | |||
| if(!FLAC__bitwriter_write_zeroes(bw, q)) | |||
| return false; | |||
| /* write the unary end bit */ | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, 1, 1)) | |||
| return false; | |||
| /* write the binary LSBs */ | |||
| if(r >= d) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) | |||
| return false; | |||
| } | |||
| else { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, r, k)) | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned uval, unsigned parameter) | |||
| { | |||
| unsigned total_bits, msbs; | |||
| unsigned k; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(parameter > 0); | |||
| k = FLAC__bitmath_ilog2(parameter); | |||
| if(parameter == 1u<<k) { | |||
| unsigned pattern; | |||
| FLAC__ASSERT(k <= 30); | |||
| msbs = uval >> k; | |||
| total_bits = 1 + k + msbs; | |||
| pattern = 1 << k; /* the unary end bit */ | |||
| pattern |= (uval & ((1u<<k)-1)); /* the binary LSBs */ | |||
| if(total_bits <= 32) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, pattern, total_bits)) | |||
| return false; | |||
| } | |||
| else { | |||
| /* write the unary MSBs */ | |||
| if(!FLAC__bitwriter_write_zeroes(bw, msbs)) | |||
| return false; | |||
| /* write the unary end bit and binary LSBs */ | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, pattern, k+1)) | |||
| return false; | |||
| } | |||
| } | |||
| else { | |||
| unsigned q, r, d; | |||
| d = (1 << (k+1)) - parameter; | |||
| q = uval / parameter; | |||
| r = uval - (q * parameter); | |||
| /* write the unary MSBs */ | |||
| if(!FLAC__bitwriter_write_zeroes(bw, q)) | |||
| return false; | |||
| /* write the unary end bit */ | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, 1, 1)) | |||
| return false; | |||
| /* write the binary LSBs */ | |||
| if(r >= d) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, r+d, k+1)) | |||
| return false; | |||
| } | |||
| else { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, r, k)) | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| #endif /* UNUSED */ | |||
| FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val) | |||
| { | |||
| FLAC__bool ok = 1; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(!(val & 0x80000000)); /* this version only handles 31 bits */ | |||
| if(val < 0x80) { | |||
| return FLAC__bitwriter_write_raw_uint32(bw, val, 8); | |||
| } | |||
| else if(val < 0x800) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (val>>6), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); | |||
| } | |||
| else if(val < 0x10000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (val>>12), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); | |||
| } | |||
| else if(val < 0x200000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (val>>18), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); | |||
| } | |||
| else if(val < 0x4000000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (val>>24), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); | |||
| } | |||
| else { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (val>>30), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>24)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>18)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | ((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (val&0x3F), 8); | |||
| } | |||
| return ok; | |||
| } | |||
| FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val) | |||
| { | |||
| FLAC__bool ok = 1; | |||
| FLAC__ASSERT(0 != bw); | |||
| FLAC__ASSERT(0 != bw->buffer); | |||
| FLAC__ASSERT(!(val & FLAC__U64L(0xFFFFFFF000000000))); /* this version only handles 36 bits */ | |||
| if(val < 0x80) { | |||
| return FLAC__bitwriter_write_raw_uint32(bw, (FLAC__uint32)val, 8); | |||
| } | |||
| else if(val < 0x800) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xC0 | (FLAC__uint32)(val>>6), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); | |||
| } | |||
| else if(val < 0x10000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xE0 | (FLAC__uint32)(val>>12), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); | |||
| } | |||
| else if(val < 0x200000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF0 | (FLAC__uint32)(val>>18), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); | |||
| } | |||
| else if(val < 0x4000000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xF8 | (FLAC__uint32)(val>>24), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); | |||
| } | |||
| else if(val < 0x80000000) { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFC | (FLAC__uint32)(val>>30), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); | |||
| } | |||
| else { | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0xFE, 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>30)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>24)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>18)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>12)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)((val>>6)&0x3F), 8); | |||
| ok &= FLAC__bitwriter_write_raw_uint32(bw, 0x80 | (FLAC__uint32)(val&0x3F), 8); | |||
| } | |||
| return ok; | |||
| } | |||
| FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw) | |||
| { | |||
| /* 0-pad to byte boundary */ | |||
| if(bw->bits & 7u) | |||
| return FLAC__bitwriter_write_zeroes(bw, 8 - (bw->bits & 7u)); | |||
| else | |||
| return true; | |||
| } | |||
| @@ -0,0 +1,418 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include "include/private/cpu.h" | |||
| #include <stdlib.h> | |||
| #include <stdio.h> | |||
| #if defined FLAC__CPU_IA32 | |||
| # include <signal.h> | |||
| #elif defined FLAC__CPU_PPC | |||
| # if !defined FLAC__NO_ASM | |||
| # if defined FLAC__SYS_DARWIN | |||
| # include <sys/sysctl.h> | |||
| # include <mach/mach.h> | |||
| # include <mach/mach_host.h> | |||
| # include <mach/host_info.h> | |||
| # include <mach/machine.h> | |||
| # ifndef CPU_SUBTYPE_POWERPC_970 | |||
| # define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) | |||
| # endif | |||
| # else /* FLAC__SYS_DARWIN */ | |||
| # include <signal.h> | |||
| # include <setjmp.h> | |||
| static sigjmp_buf jmpbuf; | |||
| static volatile sig_atomic_t canjump = 0; | |||
| static void sigill_handler (int sig) | |||
| { | |||
| if (!canjump) { | |||
| signal (sig, SIG_DFL); | |||
| raise (sig); | |||
| } | |||
| canjump = 0; | |||
| siglongjmp (jmpbuf, 1); | |||
| } | |||
| # endif /* FLAC__SYS_DARWIN */ | |||
| # endif /* FLAC__NO_ASM */ | |||
| #endif /* FLAC__CPU_PPC */ | |||
| #if defined (__NetBSD__) || defined(__OpenBSD__) | |||
| #include <sys/param.h> | |||
| #include <sys/sysctl.h> | |||
| #include <machine/cpu.h> | |||
| #endif | |||
| #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) | |||
| #include <sys/types.h> | |||
| #include <sys/sysctl.h> | |||
| #endif | |||
| #if defined(__APPLE__) | |||
| /* how to get sysctlbyname()? */ | |||
| #endif | |||
| /* these are flags in EDX of CPUID AX=00000001 */ | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_CMOV = 0x00008000; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_MMX = 0x00800000; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_FXSR = 0x01000000; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE = 0x02000000; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE2 = 0x04000000; | |||
| /* these are flags in ECX of CPUID AX=00000001 */ | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_SSE3 = 0x00000001; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_SSSE3 = 0x00000200; | |||
| /* these are flags in EDX of CPUID AX=80000001 */ | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW = 0x80000000; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW = 0x40000000; | |||
| static const unsigned FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX = 0x00400000; | |||
| /* | |||
| * Extra stuff needed for detection of OS support for SSE on IA-32 | |||
| */ | |||
| #if defined(FLAC__CPU_IA32) && !defined FLAC__NO_ASM && defined FLAC__HAS_NASM && !defined FLAC__NO_SSE_OS && !defined FLAC__SSE_OS | |||
| # if defined(__linux__) | |||
| /* | |||
| * If the OS doesn't support SSE, we will get here with a SIGILL. We | |||
| * modify the return address to jump over the offending SSE instruction | |||
| * and also the operation following it that indicates the instruction | |||
| * executed successfully. In this way we use no global variables and | |||
| * stay thread-safe. | |||
| * | |||
| * 3 + 3 + 6: | |||
| * 3 bytes for "xorps xmm0,xmm0" | |||
| * 3 bytes for estimate of how long the follwing "inc var" instruction is | |||
| * 6 bytes extra in case our estimate is wrong | |||
| * 12 bytes puts us in the NOP "landing zone" | |||
| */ | |||
| # undef USE_OBSOLETE_SIGCONTEXT_FLAVOR /* #define this to use the older signal handler method */ | |||
| # ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR | |||
| static void sigill_handler_sse_os(int signal, struct sigcontext sc) | |||
| { | |||
| (void)signal; | |||
| sc.eip += 3 + 3 + 6; | |||
| } | |||
| # else | |||
| # include <sys/ucontext.h> | |||
| static void sigill_handler_sse_os(int signal, siginfo_t *si, void *uc) | |||
| { | |||
| (void)signal, (void)si; | |||
| ((ucontext_t*)uc)->uc_mcontext.gregs[14/*REG_EIP*/] += 3 + 3 + 6; | |||
| } | |||
| # endif | |||
| # elif defined(_MSC_VER) | |||
| # include <windows.h> | |||
| # undef USE_TRY_CATCH_FLAVOR /* #define this to use the try/catch method for catching illegal opcode exception */ | |||
| # ifdef USE_TRY_CATCH_FLAVOR | |||
| # else | |||
| LONG CALLBACK sigill_handler_sse_os(EXCEPTION_POINTERS *ep) | |||
| { | |||
| if(ep->ExceptionRecord->ExceptionCode == EXCEPTION_ILLEGAL_INSTRUCTION) { | |||
| ep->ContextRecord->Eip += 3 + 3 + 6; | |||
| return EXCEPTION_CONTINUE_EXECUTION; | |||
| } | |||
| return EXCEPTION_CONTINUE_SEARCH; | |||
| } | |||
| # endif | |||
| # endif | |||
| #endif | |||
| void FLAC__cpu_info(FLAC__CPUInfo *info) | |||
| { | |||
| /* | |||
| * IA32-specific | |||
| */ | |||
| #ifdef FLAC__CPU_IA32 | |||
| info->type = FLAC__CPUINFO_TYPE_IA32; | |||
| #if !defined FLAC__NO_ASM && defined FLAC__HAS_NASM | |||
| info->use_asm = true; /* we assume a minimum of 80386 with FLAC__CPU_IA32 */ | |||
| info->data.ia32.cpuid = FLAC__cpu_have_cpuid_asm_ia32()? true : false; | |||
| info->data.ia32.bswap = info->data.ia32.cpuid; /* CPUID => BSWAP since it came after */ | |||
| info->data.ia32.cmov = false; | |||
| info->data.ia32.mmx = false; | |||
| info->data.ia32.fxsr = false; | |||
| info->data.ia32.sse = false; | |||
| info->data.ia32.sse2 = false; | |||
| info->data.ia32.sse3 = false; | |||
| info->data.ia32.ssse3 = false; | |||
| info->data.ia32._3dnow = false; | |||
| info->data.ia32.ext3dnow = false; | |||
| info->data.ia32.extmmx = false; | |||
| if(info->data.ia32.cpuid) { | |||
| /* http://www.sandpile.org/ia32/cpuid.htm */ | |||
| FLAC__uint32 flags_edx, flags_ecx; | |||
| FLAC__cpu_info_asm_ia32(&flags_edx, &flags_ecx); | |||
| info->data.ia32.cmov = (flags_edx & FLAC__CPUINFO_IA32_CPUID_CMOV )? true : false; | |||
| info->data.ia32.mmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_MMX )? true : false; | |||
| info->data.ia32.fxsr = (flags_edx & FLAC__CPUINFO_IA32_CPUID_FXSR )? true : false; | |||
| info->data.ia32.sse = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE )? true : false; | |||
| info->data.ia32.sse2 = (flags_edx & FLAC__CPUINFO_IA32_CPUID_SSE2 )? true : false; | |||
| info->data.ia32.sse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSE3 )? true : false; | |||
| info->data.ia32.ssse3 = (flags_ecx & FLAC__CPUINFO_IA32_CPUID_SSSE3)? true : false; | |||
| #ifdef FLAC__USE_3DNOW | |||
| flags_edx = FLAC__cpu_info_extended_amd_asm_ia32(); | |||
| info->data.ia32._3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_3DNOW )? true : false; | |||
| info->data.ia32.ext3dnow = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXT3DNOW)? true : false; | |||
| info->data.ia32.extmmx = (flags_edx & FLAC__CPUINFO_IA32_CPUID_EXTENDED_AMD_EXTMMX )? true : false; | |||
| #else | |||
| info->data.ia32._3dnow = info->data.ia32.ext3dnow = info->data.ia32.extmmx = false; | |||
| #endif | |||
| #ifdef DEBUG | |||
| fprintf(stderr, "CPU info (IA-32):\n"); | |||
| fprintf(stderr, " CPUID ...... %c\n", info->data.ia32.cpuid ? 'Y' : 'n'); | |||
| fprintf(stderr, " BSWAP ...... %c\n", info->data.ia32.bswap ? 'Y' : 'n'); | |||
| fprintf(stderr, " CMOV ....... %c\n", info->data.ia32.cmov ? 'Y' : 'n'); | |||
| fprintf(stderr, " MMX ........ %c\n", info->data.ia32.mmx ? 'Y' : 'n'); | |||
| fprintf(stderr, " FXSR ....... %c\n", info->data.ia32.fxsr ? 'Y' : 'n'); | |||
| fprintf(stderr, " SSE ........ %c\n", info->data.ia32.sse ? 'Y' : 'n'); | |||
| fprintf(stderr, " SSE2 ....... %c\n", info->data.ia32.sse2 ? 'Y' : 'n'); | |||
| fprintf(stderr, " SSE3 ....... %c\n", info->data.ia32.sse3 ? 'Y' : 'n'); | |||
| fprintf(stderr, " SSSE3 ...... %c\n", info->data.ia32.ssse3 ? 'Y' : 'n'); | |||
| fprintf(stderr, " 3DNow! ..... %c\n", info->data.ia32._3dnow ? 'Y' : 'n'); | |||
| fprintf(stderr, " 3DNow!-ext . %c\n", info->data.ia32.ext3dnow? 'Y' : 'n'); | |||
| fprintf(stderr, " 3DNow!-MMX . %c\n", info->data.ia32.extmmx ? 'Y' : 'n'); | |||
| #endif | |||
| /* | |||
| * now have to check for OS support of SSE/SSE2 | |||
| */ | |||
| if(info->data.ia32.fxsr || info->data.ia32.sse || info->data.ia32.sse2) { | |||
| #if defined FLAC__NO_SSE_OS | |||
| /* assume user knows better than us; turn it off */ | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| #elif defined FLAC__SSE_OS | |||
| /* assume user knows better than us; leave as detected above */ | |||
| #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__) || defined(__APPLE__) | |||
| int sse = 0; | |||
| size_t len; | |||
| /* at least one of these must work: */ | |||
| len = sizeof(sse); sse = sse || (sysctlbyname("hw.instruction_sse", &sse, &len, NULL, 0) == 0 && sse); | |||
| len = sizeof(sse); sse = sse || (sysctlbyname("hw.optional.sse" , &sse, &len, NULL, 0) == 0 && sse); /* __APPLE__ ? */ | |||
| if(!sse) | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| #elif defined(__NetBSD__) || defined (__OpenBSD__) | |||
| # if __NetBSD_Version__ >= 105250000 || (defined __OpenBSD__) | |||
| int val = 0, mib[2] = { CTL_MACHDEP, CPU_SSE }; | |||
| size_t len = sizeof(val); | |||
| if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| else { /* double-check SSE2 */ | |||
| mib[1] = CPU_SSE2; | |||
| len = sizeof(val); | |||
| if(sysctl(mib, 2, &val, &len, NULL, 0) < 0 || !val) | |||
| info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| } | |||
| # else | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| # endif | |||
| #elif defined(__linux__) | |||
| int sse = 0; | |||
| struct sigaction sigill_save; | |||
| #ifdef USE_OBSOLETE_SIGCONTEXT_FLAVOR | |||
| if(0 == sigaction(SIGILL, NULL, &sigill_save) && signal(SIGILL, (void (*)(int))sigill_handler_sse_os) != SIG_ERR) | |||
| #else | |||
| struct sigaction sigill_sse; | |||
| sigill_sse.sa_sigaction = sigill_handler_sse_os; | |||
| __sigemptyset(&sigill_sse.sa_mask); | |||
| sigill_sse.sa_flags = SA_SIGINFO | SA_RESETHAND; /* SA_RESETHAND just in case our SIGILL return jump breaks, so we don't get stuck in a loop */ | |||
| if(0 == sigaction(SIGILL, &sigill_sse, &sigill_save)) | |||
| #endif | |||
| { | |||
| /* http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html */ | |||
| /* see sigill_handler_sse_os() for an explanation of the following: */ | |||
| asm volatile ( | |||
| "xorl %0,%0\n\t" /* for some reason, still need to do this to clear 'sse' var */ | |||
| "xorps %%xmm0,%%xmm0\n\t" /* will cause SIGILL if unsupported by OS */ | |||
| "incl %0\n\t" /* SIGILL handler will jump over this */ | |||
| /* landing zone */ | |||
| "nop\n\t" /* SIGILL jump lands here if "inc" is 9 bytes */ | |||
| "nop\n\t" | |||
| "nop\n\t" | |||
| "nop\n\t" | |||
| "nop\n\t" | |||
| "nop\n\t" | |||
| "nop\n\t" /* SIGILL jump lands here if "inc" is 3 bytes (expected) */ | |||
| "nop\n\t" | |||
| "nop" /* SIGILL jump lands here if "inc" is 1 byte */ | |||
| : "=r"(sse) | |||
| : "r"(sse) | |||
| ); | |||
| sigaction(SIGILL, &sigill_save, NULL); | |||
| } | |||
| if(!sse) | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| #elif defined(_MSC_VER) | |||
| # ifdef USE_TRY_CATCH_FLAVOR | |||
| _try { | |||
| __asm { | |||
| # if _MSC_VER <= 1200 | |||
| /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ | |||
| _emit 0x0F | |||
| _emit 0x57 | |||
| _emit 0xC0 | |||
| # else | |||
| xorps xmm0,xmm0 | |||
| # endif | |||
| } | |||
| } | |||
| _except(EXCEPTION_EXECUTE_HANDLER) { | |||
| if (_exception_code() == STATUS_ILLEGAL_INSTRUCTION) | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| } | |||
| # else | |||
| int sse = 0; | |||
| LPTOP_LEVEL_EXCEPTION_FILTER save = SetUnhandledExceptionFilter(sigill_handler_sse_os); | |||
| /* see GCC version above for explanation */ | |||
| /* http://msdn2.microsoft.com/en-us/library/4ks26t93.aspx */ | |||
| /* http://www.codeproject.com/cpp/gccasm.asp */ | |||
| /* http://www.hick.org/~mmiller/msvc_inline_asm.html */ | |||
| __asm { | |||
| # if _MSC_VER <= 1200 | |||
| /* VC6 assembler doesn't know SSE, have to emit bytecode instead */ | |||
| _emit 0x0F | |||
| _emit 0x57 | |||
| _emit 0xC0 | |||
| # else | |||
| xorps xmm0,xmm0 | |||
| # endif | |||
| inc sse | |||
| nop | |||
| nop | |||
| nop | |||
| nop | |||
| nop | |||
| nop | |||
| nop | |||
| nop | |||
| nop | |||
| } | |||
| SetUnhandledExceptionFilter(save); | |||
| if(!sse) | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| # endif | |||
| #else | |||
| /* no way to test, disable to be safe */ | |||
| info->data.ia32.fxsr = info->data.ia32.sse = info->data.ia32.sse2 = info->data.ia32.sse3 = info->data.ia32.ssse3 = false; | |||
| #endif | |||
| #ifdef DEBUG | |||
| fprintf(stderr, " SSE OS sup . %c\n", info->data.ia32.sse ? 'Y' : 'n'); | |||
| #endif | |||
| } | |||
| } | |||
| #else | |||
| info->use_asm = false; | |||
| #endif | |||
| /* | |||
| * PPC-specific | |||
| */ | |||
| #elif defined FLAC__CPU_PPC | |||
| info->type = FLAC__CPUINFO_TYPE_PPC; | |||
| # if !defined FLAC__NO_ASM | |||
| info->use_asm = true; | |||
| # ifdef FLAC__USE_ALTIVEC | |||
| # if defined FLAC__SYS_DARWIN | |||
| { | |||
| int val = 0, mib[2] = { CTL_HW, HW_VECTORUNIT }; | |||
| size_t len = sizeof(val); | |||
| info->data.ppc.altivec = !(sysctl(mib, 2, &val, &len, NULL, 0) || !val); | |||
| } | |||
| { | |||
| host_basic_info_data_t hostInfo; | |||
| mach_msg_type_number_t infoCount; | |||
| infoCount = HOST_BASIC_INFO_COUNT; | |||
| host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); | |||
| info->data.ppc.ppc64 = (hostInfo.cpu_type == CPU_TYPE_POWERPC) && (hostInfo.cpu_subtype == CPU_SUBTYPE_POWERPC_970); | |||
| } | |||
| # else /* FLAC__USE_ALTIVEC && !FLAC__SYS_DARWIN */ | |||
| { | |||
| /* no Darwin, do it the brute-force way */ | |||
| /* @@@@@@ this is not thread-safe; replace with SSE OS method above or remove */ | |||
| info->data.ppc.altivec = 0; | |||
| info->data.ppc.ppc64 = 0; | |||
| signal (SIGILL, sigill_handler); | |||
| canjump = 0; | |||
| if (!sigsetjmp (jmpbuf, 1)) { | |||
| canjump = 1; | |||
| asm volatile ( | |||
| "mtspr 256, %0\n\t" | |||
| "vand %%v0, %%v0, %%v0" | |||
| : | |||
| : "r" (-1) | |||
| ); | |||
| info->data.ppc.altivec = 1; | |||
| } | |||
| canjump = 0; | |||
| if (!sigsetjmp (jmpbuf, 1)) { | |||
| int x = 0; | |||
| canjump = 1; | |||
| /* PPC64 hardware implements the cntlzd instruction */ | |||
| asm volatile ("cntlzd %0, %1" : "=r" (x) : "r" (x) ); | |||
| info->data.ppc.ppc64 = 1; | |||
| } | |||
| signal (SIGILL, SIG_DFL); /*@@@@@@ should save and restore old signal */ | |||
| } | |||
| # endif | |||
| # else /* !FLAC__USE_ALTIVEC */ | |||
| info->data.ppc.altivec = 0; | |||
| info->data.ppc.ppc64 = 0; | |||
| # endif | |||
| # else | |||
| info->use_asm = false; | |||
| # endif | |||
| /* | |||
| * unknown CPI | |||
| */ | |||
| #else | |||
| info->type = FLAC__CPUINFO_TYPE_UNKNOWN; | |||
| info->use_asm = false; | |||
| #endif | |||
| } | |||
| @@ -0,0 +1,142 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include "include/private/crc.h" | |||
| /* CRC-8, poly = x^8 + x^2 + x^1 + x^0, init = 0 */ | |||
| FLAC__byte const FLAC__crc8_table[256] = { | |||
| 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, | |||
| 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, | |||
| 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, | |||
| 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, | |||
| 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, | |||
| 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, | |||
| 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, | |||
| 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, | |||
| 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, | |||
| 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, | |||
| 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, | |||
| 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, | |||
| 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, | |||
| 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, | |||
| 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, | |||
| 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, | |||
| 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, | |||
| 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, | |||
| 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, | |||
| 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, | |||
| 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, | |||
| 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, | |||
| 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, | |||
| 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, | |||
| 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, | |||
| 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, | |||
| 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, | |||
| 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, | |||
| 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, | |||
| 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, | |||
| 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, | |||
| 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 | |||
| }; | |||
| /* CRC-16, poly = x^16 + x^15 + x^2 + x^0, init = 0 */ | |||
| unsigned FLAC__crc16_table[256] = { | |||
| 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, | |||
| 0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022, | |||
| 0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072, | |||
| 0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041, | |||
| 0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2, | |||
| 0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1, | |||
| 0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1, | |||
| 0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082, | |||
| 0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192, | |||
| 0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1, | |||
| 0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1, | |||
| 0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2, | |||
| 0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151, | |||
| 0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162, | |||
| 0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132, | |||
| 0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101, | |||
| 0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312, | |||
| 0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321, | |||
| 0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371, | |||
| 0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342, | |||
| 0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1, | |||
| 0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2, | |||
| 0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2, | |||
| 0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381, | |||
| 0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291, | |||
| 0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2, | |||
| 0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2, | |||
| 0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1, | |||
| 0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252, | |||
| 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, | |||
| 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, | |||
| 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 | |||
| }; | |||
| void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc) | |||
| { | |||
| *crc = FLAC__crc8_table[*crc ^ data]; | |||
| } | |||
| void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc) | |||
| { | |||
| while(len--) | |||
| *crc = FLAC__crc8_table[*crc ^ *data++]; | |||
| } | |||
| FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len) | |||
| { | |||
| FLAC__uint8 crc = 0; | |||
| while(len--) | |||
| crc = FLAC__crc8_table[crc ^ *data++]; | |||
| return crc; | |||
| } | |||
| unsigned FLAC__crc16(const FLAC__byte *data, unsigned len) | |||
| { | |||
| unsigned crc = 0; | |||
| while(len--) | |||
| crc = ((crc<<8) ^ FLAC__crc16_table[(crc>>8) ^ *data++]) & 0xffff; | |||
| return crc; | |||
| } | |||
| @@ -0,0 +1,435 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include <math.h> | |||
| #include <string.h> | |||
| #include "include/private/bitmath.h" | |||
| #include "include/private/fixed.h" | |||
| #include "../assert.h" | |||
| #ifndef M_LN2 | |||
| /* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ | |||
| #define M_LN2 0.69314718055994530942 | |||
| #endif | |||
| #ifdef min | |||
| #undef min | |||
| #endif | |||
| #define min(x,y) ((x) < (y)? (x) : (y)) | |||
| #ifdef local_abs | |||
| #undef local_abs | |||
| #endif | |||
| #define local_abs(x) ((unsigned)((x)<0? -(x) : (x))) | |||
| #ifdef FLAC__INTEGER_ONLY_LIBRARY | |||
| /* rbps stands for residual bits per sample | |||
| * | |||
| * (ln(2) * err) | |||
| * rbps = log (-----------) | |||
| * 2 ( n ) | |||
| */ | |||
| static FLAC__fixedpoint local__compute_rbps_integerized(FLAC__uint32 err, FLAC__uint32 n) | |||
| { | |||
| FLAC__uint32 rbps; | |||
| unsigned bits; /* the number of bits required to represent a number */ | |||
| int fracbits; /* the number of bits of rbps that comprise the fractional part */ | |||
| FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); | |||
| FLAC__ASSERT(err > 0); | |||
| FLAC__ASSERT(n > 0); | |||
| FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); | |||
| if(err <= n) | |||
| return 0; | |||
| /* | |||
| * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. | |||
| * These allow us later to know we won't lose too much precision in the | |||
| * fixed-point division (err<<fracbits)/n. | |||
| */ | |||
| fracbits = (8*sizeof(err)) - (FLAC__bitmath_ilog2(err)+1); | |||
| err <<= fracbits; | |||
| err /= n; | |||
| /* err now holds err/n with fracbits fractional bits */ | |||
| /* | |||
| * Whittle err down to 16 bits max. 16 significant bits is enough for | |||
| * our purposes. | |||
| */ | |||
| FLAC__ASSERT(err > 0); | |||
| bits = FLAC__bitmath_ilog2(err)+1; | |||
| if(bits > 16) { | |||
| err >>= (bits-16); | |||
| fracbits -= (bits-16); | |||
| } | |||
| rbps = (FLAC__uint32)err; | |||
| /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ | |||
| rbps *= FLAC__FP_LN2; | |||
| fracbits += 16; | |||
| FLAC__ASSERT(fracbits >= 0); | |||
| /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ | |||
| { | |||
| const int f = fracbits & 3; | |||
| if(f) { | |||
| rbps >>= f; | |||
| fracbits -= f; | |||
| } | |||
| } | |||
| rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); | |||
| if(rbps == 0) | |||
| return 0; | |||
| /* | |||
| * The return value must have 16 fractional bits. Since the whole part | |||
| * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits | |||
| * must be >= -3, these assertion allows us to be able to shift rbps | |||
| * left if necessary to get 16 fracbits without losing any bits of the | |||
| * whole part of rbps. | |||
| * | |||
| * There is a slight chance due to accumulated error that the whole part | |||
| * will require 6 bits, so we use 6 in the assertion. Really though as | |||
| * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. | |||
| */ | |||
| FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); | |||
| FLAC__ASSERT(fracbits >= -3); | |||
| /* now shift the decimal point into place */ | |||
| if(fracbits < 16) | |||
| return rbps << (16-fracbits); | |||
| else if(fracbits > 16) | |||
| return rbps >> (fracbits-16); | |||
| else | |||
| return rbps; | |||
| } | |||
| static FLAC__fixedpoint local__compute_rbps_wide_integerized(FLAC__uint64 err, FLAC__uint32 n) | |||
| { | |||
| FLAC__uint32 rbps; | |||
| unsigned bits; /* the number of bits required to represent a number */ | |||
| int fracbits; /* the number of bits of rbps that comprise the fractional part */ | |||
| FLAC__ASSERT(sizeof(rbps) == sizeof(FLAC__fixedpoint)); | |||
| FLAC__ASSERT(err > 0); | |||
| FLAC__ASSERT(n > 0); | |||
| FLAC__ASSERT(n <= FLAC__MAX_BLOCK_SIZE); | |||
| if(err <= n) | |||
| return 0; | |||
| /* | |||
| * The above two things tell us 1) n fits in 16 bits; 2) err/n > 1. | |||
| * These allow us later to know we won't lose too much precision in the | |||
| * fixed-point division (err<<fracbits)/n. | |||
| */ | |||
| fracbits = (8*sizeof(err)) - (FLAC__bitmath_ilog2_wide(err)+1); | |||
| err <<= fracbits; | |||
| err /= n; | |||
| /* err now holds err/n with fracbits fractional bits */ | |||
| /* | |||
| * Whittle err down to 16 bits max. 16 significant bits is enough for | |||
| * our purposes. | |||
| */ | |||
| FLAC__ASSERT(err > 0); | |||
| bits = FLAC__bitmath_ilog2_wide(err)+1; | |||
| if(bits > 16) { | |||
| err >>= (bits-16); | |||
| fracbits -= (bits-16); | |||
| } | |||
| rbps = (FLAC__uint32)err; | |||
| /* Multiply by fixed-point version of ln(2), with 16 fractional bits */ | |||
| rbps *= FLAC__FP_LN2; | |||
| fracbits += 16; | |||
| FLAC__ASSERT(fracbits >= 0); | |||
| /* FLAC__fixedpoint_log2 requires fracbits%4 to be 0 */ | |||
| { | |||
| const int f = fracbits & 3; | |||
| if(f) { | |||
| rbps >>= f; | |||
| fracbits -= f; | |||
| } | |||
| } | |||
| rbps = FLAC__fixedpoint_log2(rbps, fracbits, (unsigned)(-1)); | |||
| if(rbps == 0) | |||
| return 0; | |||
| /* | |||
| * The return value must have 16 fractional bits. Since the whole part | |||
| * of the base-2 log of a 32 bit number must fit in 5 bits, and fracbits | |||
| * must be >= -3, these assertion allows us to be able to shift rbps | |||
| * left if necessary to get 16 fracbits without losing any bits of the | |||
| * whole part of rbps. | |||
| * | |||
| * There is a slight chance due to accumulated error that the whole part | |||
| * will require 6 bits, so we use 6 in the assertion. Really though as | |||
| * long as it fits in 13 bits (32 - (16 - (-3))) we are fine. | |||
| */ | |||
| FLAC__ASSERT((int)FLAC__bitmath_ilog2(rbps)+1 <= fracbits + 6); | |||
| FLAC__ASSERT(fracbits >= -3); | |||
| /* now shift the decimal point into place */ | |||
| if(fracbits < 16) | |||
| return rbps << (16-fracbits); | |||
| else if(fracbits > 16) | |||
| return rbps >> (fracbits-16); | |||
| else | |||
| return rbps; | |||
| } | |||
| #endif | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) | |||
| #else | |||
| unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) | |||
| #endif | |||
| { | |||
| FLAC__int32 last_error_0 = data[-1]; | |||
| FLAC__int32 last_error_1 = data[-1] - data[-2]; | |||
| FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); | |||
| FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); | |||
| FLAC__int32 error, save; | |||
| FLAC__uint32 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; | |||
| unsigned i, order; | |||
| for(i = 0; i < data_len; i++) { | |||
| error = data[i] ; total_error_0 += local_abs(error); save = error; | |||
| error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; | |||
| error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; | |||
| error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; | |||
| error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; | |||
| } | |||
| if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) | |||
| order = 0; | |||
| else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) | |||
| order = 1; | |||
| else if(total_error_2 < min(total_error_3, total_error_4)) | |||
| order = 2; | |||
| else if(total_error_3 < total_error_4) | |||
| order = 3; | |||
| else | |||
| order = 4; | |||
| /* Estimate the expected number of bits per residual signal sample. */ | |||
| /* 'total_error*' is linearly related to the variance of the residual */ | |||
| /* signal, so we use it directly to compute E(|x|) */ | |||
| FLAC__ASSERT(data_len > 0 || total_error_0 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_1 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_2 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_3 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_4 == 0); | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| #else | |||
| residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_integerized(total_error_0, data_len) : 0; | |||
| residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_integerized(total_error_1, data_len) : 0; | |||
| residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_integerized(total_error_2, data_len) : 0; | |||
| residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_integerized(total_error_3, data_len) : 0; | |||
| residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_integerized(total_error_4, data_len) : 0; | |||
| #endif | |||
| return order; | |||
| } | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) | |||
| #else | |||
| unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]) | |||
| #endif | |||
| { | |||
| FLAC__int32 last_error_0 = data[-1]; | |||
| FLAC__int32 last_error_1 = data[-1] - data[-2]; | |||
| FLAC__int32 last_error_2 = last_error_1 - (data[-2] - data[-3]); | |||
| FLAC__int32 last_error_3 = last_error_2 - (data[-2] - 2*data[-3] + data[-4]); | |||
| FLAC__int32 error, save; | |||
| /* total_error_* are 64-bits to avoid overflow when encoding | |||
| * erratic signals when the bits-per-sample and blocksize are | |||
| * large. | |||
| */ | |||
| FLAC__uint64 total_error_0 = 0, total_error_1 = 0, total_error_2 = 0, total_error_3 = 0, total_error_4 = 0; | |||
| unsigned i, order; | |||
| for(i = 0; i < data_len; i++) { | |||
| error = data[i] ; total_error_0 += local_abs(error); save = error; | |||
| error -= last_error_0; total_error_1 += local_abs(error); last_error_0 = save; save = error; | |||
| error -= last_error_1; total_error_2 += local_abs(error); last_error_1 = save; save = error; | |||
| error -= last_error_2; total_error_3 += local_abs(error); last_error_2 = save; save = error; | |||
| error -= last_error_3; total_error_4 += local_abs(error); last_error_3 = save; | |||
| } | |||
| if(total_error_0 < min(min(min(total_error_1, total_error_2), total_error_3), total_error_4)) | |||
| order = 0; | |||
| else if(total_error_1 < min(min(total_error_2, total_error_3), total_error_4)) | |||
| order = 1; | |||
| else if(total_error_2 < min(total_error_3, total_error_4)) | |||
| order = 2; | |||
| else if(total_error_3 < total_error_4) | |||
| order = 3; | |||
| else | |||
| order = 4; | |||
| /* Estimate the expected number of bits per residual signal sample. */ | |||
| /* 'total_error*' is linearly related to the variance of the residual */ | |||
| /* signal, so we use it directly to compute E(|x|) */ | |||
| FLAC__ASSERT(data_len > 0 || total_error_0 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_1 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_2 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_3 == 0); | |||
| FLAC__ASSERT(data_len > 0 || total_error_4 == 0); | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| #if defined _MSC_VER || defined __MINGW32__ | |||
| /* with MSVC you have to spoon feed it the casting */ | |||
| residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)(FLAC__int64)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| #else | |||
| residual_bits_per_sample[0] = (FLAC__float)((total_error_0 > 0) ? log(M_LN2 * (FLAC__double)total_error_0 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[1] = (FLAC__float)((total_error_1 > 0) ? log(M_LN2 * (FLAC__double)total_error_1 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[2] = (FLAC__float)((total_error_2 > 0) ? log(M_LN2 * (FLAC__double)total_error_2 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[3] = (FLAC__float)((total_error_3 > 0) ? log(M_LN2 * (FLAC__double)total_error_3 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| residual_bits_per_sample[4] = (FLAC__float)((total_error_4 > 0) ? log(M_LN2 * (FLAC__double)total_error_4 / (FLAC__double)data_len) / M_LN2 : 0.0); | |||
| #endif | |||
| #else | |||
| residual_bits_per_sample[0] = (total_error_0 > 0) ? local__compute_rbps_wide_integerized(total_error_0, data_len) : 0; | |||
| residual_bits_per_sample[1] = (total_error_1 > 0) ? local__compute_rbps_wide_integerized(total_error_1, data_len) : 0; | |||
| residual_bits_per_sample[2] = (total_error_2 > 0) ? local__compute_rbps_wide_integerized(total_error_2, data_len) : 0; | |||
| residual_bits_per_sample[3] = (total_error_3 > 0) ? local__compute_rbps_wide_integerized(total_error_3, data_len) : 0; | |||
| residual_bits_per_sample[4] = (total_error_4 > 0) ? local__compute_rbps_wide_integerized(total_error_4, data_len) : 0; | |||
| #endif | |||
| return order; | |||
| } | |||
| void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]) | |||
| { | |||
| const int idata_len = (int)data_len; | |||
| int i; | |||
| switch(order) { | |||
| case 0: | |||
| FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); | |||
| memcpy(residual, data, sizeof(residual[0])*data_len); | |||
| break; | |||
| case 1: | |||
| for(i = 0; i < idata_len; i++) | |||
| residual[i] = data[i] - data[i-1]; | |||
| break; | |||
| case 2: | |||
| for(i = 0; i < idata_len; i++) | |||
| #if 1 /* OPT: may be faster with some compilers on some systems */ | |||
| residual[i] = data[i] - (data[i-1] << 1) + data[i-2]; | |||
| #else | |||
| residual[i] = data[i] - 2*data[i-1] + data[i-2]; | |||
| #endif | |||
| break; | |||
| case 3: | |||
| for(i = 0; i < idata_len; i++) | |||
| #if 1 /* OPT: may be faster with some compilers on some systems */ | |||
| residual[i] = data[i] - (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) - data[i-3]; | |||
| #else | |||
| residual[i] = data[i] - 3*data[i-1] + 3*data[i-2] - data[i-3]; | |||
| #endif | |||
| break; | |||
| case 4: | |||
| for(i = 0; i < idata_len; i++) | |||
| #if 1 /* OPT: may be faster with some compilers on some systems */ | |||
| residual[i] = data[i] - ((data[i-1]+data[i-3])<<2) + ((data[i-2]<<2) + (data[i-2]<<1)) + data[i-4]; | |||
| #else | |||
| residual[i] = data[i] - 4*data[i-1] + 6*data[i-2] - 4*data[i-3] + data[i-4]; | |||
| #endif | |||
| break; | |||
| default: | |||
| FLAC__ASSERT(0); | |||
| } | |||
| } | |||
| void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]) | |||
| { | |||
| int i, idata_len = (int)data_len; | |||
| switch(order) { | |||
| case 0: | |||
| FLAC__ASSERT(sizeof(residual[0]) == sizeof(data[0])); | |||
| memcpy(data, residual, sizeof(residual[0])*data_len); | |||
| break; | |||
| case 1: | |||
| for(i = 0; i < idata_len; i++) | |||
| data[i] = residual[i] + data[i-1]; | |||
| break; | |||
| case 2: | |||
| for(i = 0; i < idata_len; i++) | |||
| #if 1 /* OPT: may be faster with some compilers on some systems */ | |||
| data[i] = residual[i] + (data[i-1]<<1) - data[i-2]; | |||
| #else | |||
| data[i] = residual[i] + 2*data[i-1] - data[i-2]; | |||
| #endif | |||
| break; | |||
| case 3: | |||
| for(i = 0; i < idata_len; i++) | |||
| #if 1 /* OPT: may be faster with some compilers on some systems */ | |||
| data[i] = residual[i] + (((data[i-1]-data[i-2])<<1) + (data[i-1]-data[i-2])) + data[i-3]; | |||
| #else | |||
| data[i] = residual[i] + 3*data[i-1] - 3*data[i-2] + data[i-3]; | |||
| #endif | |||
| break; | |||
| case 4: | |||
| for(i = 0; i < idata_len; i++) | |||
| #if 1 /* OPT: may be faster with some compilers on some systems */ | |||
| data[i] = residual[i] + ((data[i-1]+data[i-3])<<2) - ((data[i-2]<<2) + (data[i-2]<<1)) - data[i-4]; | |||
| #else | |||
| data[i] = residual[i] + 4*data[i-1] - 6*data[i-2] + 4*data[i-3] - data[i-4]; | |||
| #endif | |||
| break; | |||
| default: | |||
| FLAC__ASSERT(0); | |||
| } | |||
| } | |||
| @@ -0,0 +1,308 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include "../assert.h" | |||
| #include "include/private/float.h" | |||
| #ifdef FLAC__INTEGER_ONLY_LIBRARY | |||
| /* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ | |||
| #ifdef _MSC_VER | |||
| #define FLAC__U64L(x) x | |||
| #else | |||
| #define FLAC__U64L(x) x##LLU | |||
| #endif | |||
| const FLAC__fixedpoint FLAC__FP_ZERO = 0; | |||
| const FLAC__fixedpoint FLAC__FP_ONE_HALF = 0x00008000; | |||
| const FLAC__fixedpoint FLAC__FP_ONE = 0x00010000; | |||
| const FLAC__fixedpoint FLAC__FP_LN2 = 45426; | |||
| const FLAC__fixedpoint FLAC__FP_E = 178145; | |||
| /* Lookup tables for Knuth's logarithm algorithm */ | |||
| #define LOG2_LOOKUP_PRECISION 16 | |||
| static const FLAC__uint32 log2_lookup[][LOG2_LOOKUP_PRECISION] = { | |||
| { | |||
| /* | |||
| * 0 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x00000001, | |||
| /* lg(4/3) = */ 0x00000000, | |||
| /* lg(8/7) = */ 0x00000000, | |||
| /* lg(16/15) = */ 0x00000000, | |||
| /* lg(32/31) = */ 0x00000000, | |||
| /* lg(64/63) = */ 0x00000000, | |||
| /* lg(128/127) = */ 0x00000000, | |||
| /* lg(256/255) = */ 0x00000000, | |||
| /* lg(512/511) = */ 0x00000000, | |||
| /* lg(1024/1023) = */ 0x00000000, | |||
| /* lg(2048/2047) = */ 0x00000000, | |||
| /* lg(4096/4095) = */ 0x00000000, | |||
| /* lg(8192/8191) = */ 0x00000000, | |||
| /* lg(16384/16383) = */ 0x00000000, | |||
| /* lg(32768/32767) = */ 0x00000000 | |||
| }, | |||
| { | |||
| /* | |||
| * 4 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x00000010, | |||
| /* lg(4/3) = */ 0x00000007, | |||
| /* lg(8/7) = */ 0x00000003, | |||
| /* lg(16/15) = */ 0x00000001, | |||
| /* lg(32/31) = */ 0x00000001, | |||
| /* lg(64/63) = */ 0x00000000, | |||
| /* lg(128/127) = */ 0x00000000, | |||
| /* lg(256/255) = */ 0x00000000, | |||
| /* lg(512/511) = */ 0x00000000, | |||
| /* lg(1024/1023) = */ 0x00000000, | |||
| /* lg(2048/2047) = */ 0x00000000, | |||
| /* lg(4096/4095) = */ 0x00000000, | |||
| /* lg(8192/8191) = */ 0x00000000, | |||
| /* lg(16384/16383) = */ 0x00000000, | |||
| /* lg(32768/32767) = */ 0x00000000 | |||
| }, | |||
| { | |||
| /* | |||
| * 8 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x00000100, | |||
| /* lg(4/3) = */ 0x0000006a, | |||
| /* lg(8/7) = */ 0x00000031, | |||
| /* lg(16/15) = */ 0x00000018, | |||
| /* lg(32/31) = */ 0x0000000c, | |||
| /* lg(64/63) = */ 0x00000006, | |||
| /* lg(128/127) = */ 0x00000003, | |||
| /* lg(256/255) = */ 0x00000001, | |||
| /* lg(512/511) = */ 0x00000001, | |||
| /* lg(1024/1023) = */ 0x00000000, | |||
| /* lg(2048/2047) = */ 0x00000000, | |||
| /* lg(4096/4095) = */ 0x00000000, | |||
| /* lg(8192/8191) = */ 0x00000000, | |||
| /* lg(16384/16383) = */ 0x00000000, | |||
| /* lg(32768/32767) = */ 0x00000000 | |||
| }, | |||
| { | |||
| /* | |||
| * 12 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x00001000, | |||
| /* lg(4/3) = */ 0x000006a4, | |||
| /* lg(8/7) = */ 0x00000315, | |||
| /* lg(16/15) = */ 0x0000017d, | |||
| /* lg(32/31) = */ 0x000000bc, | |||
| /* lg(64/63) = */ 0x0000005d, | |||
| /* lg(128/127) = */ 0x0000002e, | |||
| /* lg(256/255) = */ 0x00000017, | |||
| /* lg(512/511) = */ 0x0000000c, | |||
| /* lg(1024/1023) = */ 0x00000006, | |||
| /* lg(2048/2047) = */ 0x00000003, | |||
| /* lg(4096/4095) = */ 0x00000001, | |||
| /* lg(8192/8191) = */ 0x00000001, | |||
| /* lg(16384/16383) = */ 0x00000000, | |||
| /* lg(32768/32767) = */ 0x00000000 | |||
| }, | |||
| { | |||
| /* | |||
| * 16 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x00010000, | |||
| /* lg(4/3) = */ 0x00006a40, | |||
| /* lg(8/7) = */ 0x00003151, | |||
| /* lg(16/15) = */ 0x000017d6, | |||
| /* lg(32/31) = */ 0x00000bba, | |||
| /* lg(64/63) = */ 0x000005d1, | |||
| /* lg(128/127) = */ 0x000002e6, | |||
| /* lg(256/255) = */ 0x00000172, | |||
| /* lg(512/511) = */ 0x000000b9, | |||
| /* lg(1024/1023) = */ 0x0000005c, | |||
| /* lg(2048/2047) = */ 0x0000002e, | |||
| /* lg(4096/4095) = */ 0x00000017, | |||
| /* lg(8192/8191) = */ 0x0000000c, | |||
| /* lg(16384/16383) = */ 0x00000006, | |||
| /* lg(32768/32767) = */ 0x00000003 | |||
| }, | |||
| { | |||
| /* | |||
| * 20 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x00100000, | |||
| /* lg(4/3) = */ 0x0006a3fe, | |||
| /* lg(8/7) = */ 0x00031513, | |||
| /* lg(16/15) = */ 0x00017d60, | |||
| /* lg(32/31) = */ 0x0000bb9d, | |||
| /* lg(64/63) = */ 0x00005d10, | |||
| /* lg(128/127) = */ 0x00002e59, | |||
| /* lg(256/255) = */ 0x00001721, | |||
| /* lg(512/511) = */ 0x00000b8e, | |||
| /* lg(1024/1023) = */ 0x000005c6, | |||
| /* lg(2048/2047) = */ 0x000002e3, | |||
| /* lg(4096/4095) = */ 0x00000171, | |||
| /* lg(8192/8191) = */ 0x000000b9, | |||
| /* lg(16384/16383) = */ 0x0000005c, | |||
| /* lg(32768/32767) = */ 0x0000002e | |||
| }, | |||
| { | |||
| /* | |||
| * 24 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x01000000, | |||
| /* lg(4/3) = */ 0x006a3fe6, | |||
| /* lg(8/7) = */ 0x00315130, | |||
| /* lg(16/15) = */ 0x0017d605, | |||
| /* lg(32/31) = */ 0x000bb9ca, | |||
| /* lg(64/63) = */ 0x0005d0fc, | |||
| /* lg(128/127) = */ 0x0002e58f, | |||
| /* lg(256/255) = */ 0x0001720e, | |||
| /* lg(512/511) = */ 0x0000b8d8, | |||
| /* lg(1024/1023) = */ 0x00005c61, | |||
| /* lg(2048/2047) = */ 0x00002e2d, | |||
| /* lg(4096/4095) = */ 0x00001716, | |||
| /* lg(8192/8191) = */ 0x00000b8b, | |||
| /* lg(16384/16383) = */ 0x000005c5, | |||
| /* lg(32768/32767) = */ 0x000002e3 | |||
| }, | |||
| { | |||
| /* | |||
| * 28 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ 0x10000000, | |||
| /* lg(4/3) = */ 0x06a3fe5c, | |||
| /* lg(8/7) = */ 0x03151301, | |||
| /* lg(16/15) = */ 0x017d6049, | |||
| /* lg(32/31) = */ 0x00bb9ca6, | |||
| /* lg(64/63) = */ 0x005d0fba, | |||
| /* lg(128/127) = */ 0x002e58f7, | |||
| /* lg(256/255) = */ 0x001720da, | |||
| /* lg(512/511) = */ 0x000b8d87, | |||
| /* lg(1024/1023) = */ 0x0005c60b, | |||
| /* lg(2048/2047) = */ 0x0002e2d7, | |||
| /* lg(4096/4095) = */ 0x00017160, | |||
| /* lg(8192/8191) = */ 0x0000b8ad, | |||
| /* lg(16384/16383) = */ 0x00005c56, | |||
| /* lg(32768/32767) = */ 0x00002e2b | |||
| } | |||
| }; | |||
| #if 0 | |||
| static const FLAC__uint64 log2_lookup_wide[] = { | |||
| { | |||
| /* | |||
| * 32 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ FLAC__U64L(0x100000000), | |||
| /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c6), | |||
| /* lg(8/7) = */ FLAC__U64L(0x31513015), | |||
| /* lg(16/15) = */ FLAC__U64L(0x17d60497), | |||
| /* lg(32/31) = */ FLAC__U64L(0x0bb9ca65), | |||
| /* lg(64/63) = */ FLAC__U64L(0x05d0fba2), | |||
| /* lg(128/127) = */ FLAC__U64L(0x02e58f74), | |||
| /* lg(256/255) = */ FLAC__U64L(0x01720d9c), | |||
| /* lg(512/511) = */ FLAC__U64L(0x00b8d875), | |||
| /* lg(1024/1023) = */ FLAC__U64L(0x005c60aa), | |||
| /* lg(2048/2047) = */ FLAC__U64L(0x002e2d72), | |||
| /* lg(4096/4095) = */ FLAC__U64L(0x00171600), | |||
| /* lg(8192/8191) = */ FLAC__U64L(0x000b8ad2), | |||
| /* lg(16384/16383) = */ FLAC__U64L(0x0005c55d), | |||
| /* lg(32768/32767) = */ FLAC__U64L(0x0002e2ac) | |||
| }, | |||
| { | |||
| /* | |||
| * 48 fraction bits | |||
| */ | |||
| /* undefined */ 0x00000000, | |||
| /* lg(2/1) = */ FLAC__U64L(0x1000000000000), | |||
| /* lg(4/3) = */ FLAC__U64L(0x6a3fe5c60429), | |||
| /* lg(8/7) = */ FLAC__U64L(0x315130157f7a), | |||
| /* lg(16/15) = */ FLAC__U64L(0x17d60496cfbb), | |||
| /* lg(32/31) = */ FLAC__U64L(0xbb9ca64ecac), | |||
| /* lg(64/63) = */ FLAC__U64L(0x5d0fba187cd), | |||
| /* lg(128/127) = */ FLAC__U64L(0x2e58f7441ee), | |||
| /* lg(256/255) = */ FLAC__U64L(0x1720d9c06a8), | |||
| /* lg(512/511) = */ FLAC__U64L(0xb8d8752173), | |||
| /* lg(1024/1023) = */ FLAC__U64L(0x5c60aa252e), | |||
| /* lg(2048/2047) = */ FLAC__U64L(0x2e2d71b0d8), | |||
| /* lg(4096/4095) = */ FLAC__U64L(0x1716001719), | |||
| /* lg(8192/8191) = */ FLAC__U64L(0xb8ad1de1b), | |||
| /* lg(16384/16383) = */ FLAC__U64L(0x5c55d640d), | |||
| /* lg(32768/32767) = */ FLAC__U64L(0x2e2abcf52) | |||
| } | |||
| }; | |||
| #endif | |||
| FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision) | |||
| { | |||
| const FLAC__uint32 ONE = (1u << fracbits); | |||
| const FLAC__uint32 *table = log2_lookup[fracbits >> 2]; | |||
| FLAC__ASSERT(fracbits < 32); | |||
| FLAC__ASSERT((fracbits & 0x3) == 0); | |||
| if(x < ONE) | |||
| return 0; | |||
| if(precision > LOG2_LOOKUP_PRECISION) | |||
| precision = LOG2_LOOKUP_PRECISION; | |||
| /* Knuth's algorithm for computing logarithms, optimized for base-2 with lookup tables */ | |||
| { | |||
| FLAC__uint32 y = 0; | |||
| FLAC__uint32 z = x >> 1, k = 1; | |||
| while (x > ONE && k < precision) { | |||
| if (x - z >= ONE) { | |||
| x -= z; | |||
| z = x >> k; | |||
| y += table[k]; | |||
| } | |||
| else { | |||
| z >>= 1; | |||
| k++; | |||
| } | |||
| } | |||
| return y; | |||
| } | |||
| } | |||
| #endif /* defined FLAC__INTEGER_ONLY_LIBRARY */ | |||
| @@ -0,0 +1,598 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include <stdio.h> | |||
| #include <stdlib.h> /* for qsort() */ | |||
| #include <string.h> /* for memset() */ | |||
| #include "../assert.h" | |||
| #include "../format.h" | |||
| #include "include/private/format.h" | |||
| #ifndef FLaC__INLINE | |||
| #define FLaC__INLINE | |||
| #endif | |||
| #ifdef min | |||
| #undef min | |||
| #endif | |||
| #define min(a,b) ((a)<(b)?(a):(b)) | |||
| /* adjust for compilers that can't understand using LLU suffix for uint64_t literals */ | |||
| #ifdef _MSC_VER | |||
| #define FLAC__U64L(x) x | |||
| #else | |||
| #define FLAC__U64L(x) x##LLU | |||
| #endif | |||
| /* VERSION should come from configure */ | |||
| FLAC_API const char *FLAC__VERSION_STRING = VERSION | |||
| ; | |||
| #if defined _MSC_VER || defined __BORLANDC__ || defined __MINW32__ | |||
| /* yet one more hack because of MSVC6: */ | |||
| FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC 1.2.1 20070917"; | |||
| #else | |||
| FLAC_API const char *FLAC__VENDOR_STRING = "reference libFLAC " VERSION " 20070917"; | |||
| #endif | |||
| FLAC_API const FLAC__byte FLAC__STREAM_SYNC_STRING[4] = { 'f','L','a','C' }; | |||
| FLAC_API const unsigned FLAC__STREAM_SYNC = 0x664C6143; | |||
| FLAC_API const unsigned FLAC__STREAM_SYNC_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN = 16; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN = 16; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN = 24; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN = 24; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN = 20; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN = 3; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN = 5; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN = 36; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_STREAMINFO_MD5SUM_LEN = 128; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_APPLICATION_ID_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN = 64; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN = 64; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN = 16; /* bits */ | |||
| FLAC_API const FLAC__uint64 FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER = FLAC__U64L(0xffffffffffffffff); | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_ENTRY_LENGTH_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_VORBIS_COMMENT_NUM_COMMENTS_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN = 64; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN = 8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN = 3*8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN = 64; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN = 8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN = 12*8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN = 1; /* bit */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN = 1; /* bit */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN = 6+13*8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN = 8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN = 128*8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN = 64; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN = 1; /* bit */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN = 7+258*8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN = 8; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_TYPE_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_COLORS_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN = 32; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_IS_LAST_LEN = 1; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_TYPE_LEN = 7; /* bits */ | |||
| FLAC_API const unsigned FLAC__STREAM_METADATA_LENGTH_LEN = 24; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC = 0x3ffe; | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_SYNC_LEN = 14; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_RESERVED_LEN = 1; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN = 1; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_BLOCK_SIZE_LEN = 4; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_SAMPLE_RATE_LEN = 4; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN = 4; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN = 3; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_ZERO_PAD_LEN = 1; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_HEADER_CRC_LEN = 8; /* bits */ | |||
| FLAC_API const unsigned FLAC__FRAME_FOOTER_CRC_LEN = 16; /* bits */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_TYPE_LEN = 2; /* bits */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN = 4; /* bits */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN = 4; /* bits */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN = 5; /* bits */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN = 5; /* bits */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER = 15; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN)-1 */ | |||
| FLAC_API const unsigned FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER = 31; /* == (1<<FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN)-1 */ | |||
| FLAC_API const char * const FLAC__EntropyCodingMethodTypeString[] = { | |||
| "PARTITIONED_RICE", | |||
| "PARTITIONED_RICE2" | |||
| }; | |||
| FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN = 4; /* bits */ | |||
| FLAC_API const unsigned FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN = 5; /* bits */ | |||
| FLAC_API const unsigned FLAC__SUBFRAME_ZERO_PAD_LEN = 1; /* bits */ | |||
| FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LEN = 6; /* bits */ | |||
| FLAC_API const unsigned FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN = 1; /* bits */ | |||
| FLAC_API const unsigned FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK = 0x00; | |||
| FLAC_API const unsigned FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK = 0x02; | |||
| FLAC_API const unsigned FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK = 0x10; | |||
| FLAC_API const unsigned FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK = 0x40; | |||
| FLAC_API const char * const FLAC__SubframeTypeString[] = { | |||
| "CONSTANT", | |||
| "VERBATIM", | |||
| "FIXED", | |||
| "LPC" | |||
| }; | |||
| FLAC_API const char * const FLAC__ChannelAssignmentString[] = { | |||
| "INDEPENDENT", | |||
| "LEFT_SIDE", | |||
| "RIGHT_SIDE", | |||
| "MID_SIDE" | |||
| }; | |||
| FLAC_API const char * const FLAC__FrameNumberTypeString[] = { | |||
| "FRAME_NUMBER_TYPE_FRAME_NUMBER", | |||
| "FRAME_NUMBER_TYPE_SAMPLE_NUMBER" | |||
| }; | |||
| FLAC_API const char * const FLAC__MetadataTypeString[] = { | |||
| "STREAMINFO", | |||
| "PADDING", | |||
| "APPLICATION", | |||
| "SEEKTABLE", | |||
| "VORBIS_COMMENT", | |||
| "CUESHEET", | |||
| "PICTURE" | |||
| }; | |||
| FLAC_API const char * const FLAC__StreamMetadata_Picture_TypeString[] = { | |||
| "Other", | |||
| "32x32 pixels 'file icon' (PNG only)", | |||
| "Other file icon", | |||
| "Cover (front)", | |||
| "Cover (back)", | |||
| "Leaflet page", | |||
| "Media (e.g. label side of CD)", | |||
| "Lead artist/lead performer/soloist", | |||
| "Artist/performer", | |||
| "Conductor", | |||
| "Band/Orchestra", | |||
| "Composer", | |||
| "Lyricist/text writer", | |||
| "Recording Location", | |||
| "During recording", | |||
| "During performance", | |||
| "Movie/video screen capture", | |||
| "A bright coloured fish", | |||
| "Illustration", | |||
| "Band/artist logotype", | |||
| "Publisher/Studio logotype" | |||
| }; | |||
| FLAC_API FLAC__bool FLAC__format_sample_rate_is_valid(unsigned sample_rate) | |||
| { | |||
| if(sample_rate == 0 || sample_rate > FLAC__MAX_SAMPLE_RATE) { | |||
| return false; | |||
| } | |||
| else | |||
| return true; | |||
| } | |||
| FLAC_API FLAC__bool FLAC__format_sample_rate_is_subset(unsigned sample_rate) | |||
| { | |||
| if( | |||
| !FLAC__format_sample_rate_is_valid(sample_rate) || | |||
| ( | |||
| sample_rate >= (1u << 16) && | |||
| !(sample_rate % 1000 == 0 || sample_rate % 10 == 0) | |||
| ) | |||
| ) { | |||
| return false; | |||
| } | |||
| else | |||
| return true; | |||
| } | |||
| /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ | |||
| FLAC_API FLAC__bool FLAC__format_seektable_is_legal(const FLAC__StreamMetadata_SeekTable *seek_table) | |||
| { | |||
| unsigned i; | |||
| FLAC__uint64 prev_sample_number = 0; | |||
| FLAC__bool got_prev = false; | |||
| FLAC__ASSERT(0 != seek_table); | |||
| for(i = 0; i < seek_table->num_points; i++) { | |||
| if(got_prev) { | |||
| if( | |||
| seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER && | |||
| seek_table->points[i].sample_number <= prev_sample_number | |||
| ) | |||
| return false; | |||
| } | |||
| prev_sample_number = seek_table->points[i].sample_number; | |||
| got_prev = true; | |||
| } | |||
| return true; | |||
| } | |||
| /* used as the sort predicate for qsort() */ | |||
| static int JUCE_CDECL seekpoint_compare_(const FLAC__StreamMetadata_SeekPoint *l, const FLAC__StreamMetadata_SeekPoint *r) | |||
| { | |||
| /* we don't just 'return l->sample_number - r->sample_number' since the result (FLAC__int64) might overflow an 'int' */ | |||
| if(l->sample_number == r->sample_number) | |||
| return 0; | |||
| else if(l->sample_number < r->sample_number) | |||
| return -1; | |||
| else | |||
| return 1; | |||
| } | |||
| /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ | |||
| FLAC_API unsigned FLAC__format_seektable_sort(FLAC__StreamMetadata_SeekTable *seek_table) | |||
| { | |||
| unsigned i, j; | |||
| FLAC__bool first; | |||
| FLAC__ASSERT(0 != seek_table); | |||
| /* sort the seekpoints */ | |||
| qsort(seek_table->points, seek_table->num_points, sizeof(FLAC__StreamMetadata_SeekPoint), (int (JUCE_CDECL *)(const void *, const void *))seekpoint_compare_); | |||
| /* uniquify the seekpoints */ | |||
| first = true; | |||
| for(i = j = 0; i < seek_table->num_points; i++) { | |||
| if(seek_table->points[i].sample_number != FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER) { | |||
| if(!first) { | |||
| if(seek_table->points[i].sample_number == seek_table->points[j-1].sample_number) | |||
| continue; | |||
| } | |||
| } | |||
| first = false; | |||
| seek_table->points[j++] = seek_table->points[i]; | |||
| } | |||
| for(i = j; i < seek_table->num_points; i++) { | |||
| seek_table->points[i].sample_number = FLAC__STREAM_METADATA_SEEKPOINT_PLACEHOLDER; | |||
| seek_table->points[i].stream_offset = 0; | |||
| seek_table->points[i].frame_samples = 0; | |||
| } | |||
| return j; | |||
| } | |||
| /* | |||
| * also disallows non-shortest-form encodings, c.f. | |||
| * http://www.unicode.org/versions/corrigendum1.html | |||
| * and a more clear explanation at the end of this section: | |||
| * http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 | |||
| */ | |||
| static FLaC__INLINE unsigned utf8len_(const FLAC__byte *utf8) | |||
| { | |||
| FLAC__ASSERT(0 != utf8); | |||
| if ((utf8[0] & 0x80) == 0) { | |||
| return 1; | |||
| } | |||
| else if ((utf8[0] & 0xE0) == 0xC0 && (utf8[1] & 0xC0) == 0x80) { | |||
| if ((utf8[0] & 0xFE) == 0xC0) /* overlong sequence check */ | |||
| return 0; | |||
| return 2; | |||
| } | |||
| else if ((utf8[0] & 0xF0) == 0xE0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80) { | |||
| if (utf8[0] == 0xE0 && (utf8[1] & 0xE0) == 0x80) /* overlong sequence check */ | |||
| return 0; | |||
| /* illegal surrogates check (U+D800...U+DFFF and U+FFFE...U+FFFF) */ | |||
| if (utf8[0] == 0xED && (utf8[1] & 0xE0) == 0xA0) /* D800-DFFF */ | |||
| return 0; | |||
| if (utf8[0] == 0xEF && utf8[1] == 0xBF && (utf8[2] & 0xFE) == 0xBE) /* FFFE-FFFF */ | |||
| return 0; | |||
| return 3; | |||
| } | |||
| else if ((utf8[0] & 0xF8) == 0xF0 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80) { | |||
| if (utf8[0] == 0xF0 && (utf8[1] & 0xF0) == 0x80) /* overlong sequence check */ | |||
| return 0; | |||
| return 4; | |||
| } | |||
| else if ((utf8[0] & 0xFC) == 0xF8 && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80) { | |||
| if (utf8[0] == 0xF8 && (utf8[1] & 0xF8) == 0x80) /* overlong sequence check */ | |||
| return 0; | |||
| return 5; | |||
| } | |||
| else if ((utf8[0] & 0xFE) == 0xFC && (utf8[1] & 0xC0) == 0x80 && (utf8[2] & 0xC0) == 0x80 && (utf8[3] & 0xC0) == 0x80 && (utf8[4] & 0xC0) == 0x80 && (utf8[5] & 0xC0) == 0x80) { | |||
| if (utf8[0] == 0xFC && (utf8[1] & 0xFC) == 0x80) /* overlong sequence check */ | |||
| return 0; | |||
| return 6; | |||
| } | |||
| else { | |||
| return 0; | |||
| } | |||
| } | |||
| FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_name_is_legal(const char *name) | |||
| { | |||
| char c; | |||
| for(c = *name; c; c = *(++name)) | |||
| if(c < 0x20 || c == 0x3d || c > 0x7d) | |||
| return false; | |||
| return true; | |||
| } | |||
| FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_value_is_legal(const FLAC__byte *value, unsigned length) | |||
| { | |||
| if(length == (unsigned)(-1)) { | |||
| while(*value) { | |||
| unsigned n = utf8len_(value); | |||
| if(n == 0) | |||
| return false; | |||
| value += n; | |||
| } | |||
| } | |||
| else { | |||
| const FLAC__byte *end = value + length; | |||
| while(value < end) { | |||
| unsigned n = utf8len_(value); | |||
| if(n == 0) | |||
| return false; | |||
| value += n; | |||
| } | |||
| if(value != end) | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| FLAC_API FLAC__bool FLAC__format_vorbiscomment_entry_is_legal(const FLAC__byte *entry, unsigned length) | |||
| { | |||
| const FLAC__byte *s, *end; | |||
| for(s = entry, end = s + length; s < end && *s != '='; s++) { | |||
| if(*s < 0x20 || *s > 0x7D) | |||
| return false; | |||
| } | |||
| if(s == end) | |||
| return false; | |||
| s++; /* skip '=' */ | |||
| while(s < end) { | |||
| unsigned n = utf8len_(s); | |||
| if(n == 0) | |||
| return false; | |||
| s += n; | |||
| } | |||
| if(s != end) | |||
| return false; | |||
| return true; | |||
| } | |||
| /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ | |||
| FLAC_API FLAC__bool FLAC__format_cuesheet_is_legal(const FLAC__StreamMetadata_CueSheet *cue_sheet, FLAC__bool check_cd_da_subset, const char **violation) | |||
| { | |||
| unsigned i, j; | |||
| if(check_cd_da_subset) { | |||
| if(cue_sheet->lead_in < 2 * 44100) { | |||
| if(violation) *violation = "CD-DA cue sheet must have a lead-in length of at least 2 seconds"; | |||
| return false; | |||
| } | |||
| if(cue_sheet->lead_in % 588 != 0) { | |||
| if(violation) *violation = "CD-DA cue sheet lead-in length must be evenly divisible by 588 samples"; | |||
| return false; | |||
| } | |||
| } | |||
| if(cue_sheet->num_tracks == 0) { | |||
| if(violation) *violation = "cue sheet must have at least one track (the lead-out)"; | |||
| return false; | |||
| } | |||
| if(check_cd_da_subset && cue_sheet->tracks[cue_sheet->num_tracks-1].number != 170) { | |||
| if(violation) *violation = "CD-DA cue sheet must have a lead-out track number 170 (0xAA)"; | |||
| return false; | |||
| } | |||
| for(i = 0; i < cue_sheet->num_tracks; i++) { | |||
| if(cue_sheet->tracks[i].number == 0) { | |||
| if(violation) *violation = "cue sheet may not have a track number 0"; | |||
| return false; | |||
| } | |||
| if(check_cd_da_subset) { | |||
| if(!((cue_sheet->tracks[i].number >= 1 && cue_sheet->tracks[i].number <= 99) || cue_sheet->tracks[i].number == 170)) { | |||
| if(violation) *violation = "CD-DA cue sheet track number must be 1-99 or 170"; | |||
| return false; | |||
| } | |||
| } | |||
| if(check_cd_da_subset && cue_sheet->tracks[i].offset % 588 != 0) { | |||
| if(violation) { | |||
| if(i == cue_sheet->num_tracks-1) /* the lead-out track... */ | |||
| *violation = "CD-DA cue sheet lead-out offset must be evenly divisible by 588 samples"; | |||
| else | |||
| *violation = "CD-DA cue sheet track offset must be evenly divisible by 588 samples"; | |||
| } | |||
| return false; | |||
| } | |||
| if(i < cue_sheet->num_tracks - 1) { | |||
| if(cue_sheet->tracks[i].num_indices == 0) { | |||
| if(violation) *violation = "cue sheet track must have at least one index point"; | |||
| return false; | |||
| } | |||
| if(cue_sheet->tracks[i].indices[0].number > 1) { | |||
| if(violation) *violation = "cue sheet track's first index number must be 0 or 1"; | |||
| return false; | |||
| } | |||
| } | |||
| for(j = 0; j < cue_sheet->tracks[i].num_indices; j++) { | |||
| if(check_cd_da_subset && cue_sheet->tracks[i].indices[j].offset % 588 != 0) { | |||
| if(violation) *violation = "CD-DA cue sheet track index offset must be evenly divisible by 588 samples"; | |||
| return false; | |||
| } | |||
| if(j > 0) { | |||
| if(cue_sheet->tracks[i].indices[j].number != cue_sheet->tracks[i].indices[j-1].number + 1) { | |||
| if(violation) *violation = "cue sheet track index numbers must increase by 1"; | |||
| return false; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| /* @@@@ add to unit tests; it is already indirectly tested by the metadata_object tests */ | |||
| FLAC_API FLAC__bool FLAC__format_picture_is_legal(const FLAC__StreamMetadata_Picture *picture, const char **violation) | |||
| { | |||
| char *p; | |||
| FLAC__byte *b; | |||
| for(p = picture->mime_type; *p; p++) { | |||
| if(*p < 0x20 || *p > 0x7e) { | |||
| if(violation) *violation = "MIME type string must contain only printable ASCII characters (0x20-0x7e)"; | |||
| return false; | |||
| } | |||
| } | |||
| for(b = picture->description; *b; ) { | |||
| unsigned n = utf8len_(b); | |||
| if(n == 0) { | |||
| if(violation) *violation = "description string must be valid UTF-8"; | |||
| return false; | |||
| } | |||
| b += n; | |||
| } | |||
| return true; | |||
| } | |||
| /* | |||
| * These routines are private to libFLAC | |||
| */ | |||
| unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order) | |||
| { | |||
| return | |||
| FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order( | |||
| FLAC__format_get_max_rice_partition_order_from_blocksize(blocksize), | |||
| blocksize, | |||
| predictor_order | |||
| ); | |||
| } | |||
| unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize) | |||
| { | |||
| unsigned max_rice_partition_order = 0; | |||
| while(!(blocksize & 1)) { | |||
| max_rice_partition_order++; | |||
| blocksize >>= 1; | |||
| } | |||
| return min(FLAC__MAX_RICE_PARTITION_ORDER, max_rice_partition_order); | |||
| } | |||
| unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order) | |||
| { | |||
| unsigned max_rice_partition_order = limit; | |||
| while(max_rice_partition_order > 0 && (blocksize >> max_rice_partition_order) <= predictor_order) | |||
| max_rice_partition_order--; | |||
| FLAC__ASSERT( | |||
| (max_rice_partition_order == 0 && blocksize >= predictor_order) || | |||
| (max_rice_partition_order > 0 && blocksize >> max_rice_partition_order > predictor_order) | |||
| ); | |||
| return max_rice_partition_order; | |||
| } | |||
| void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) | |||
| { | |||
| FLAC__ASSERT(0 != object); | |||
| object->parameters = 0; | |||
| object->raw_bits = 0; | |||
| object->capacity_by_order = 0; | |||
| } | |||
| void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object) | |||
| { | |||
| FLAC__ASSERT(0 != object); | |||
| if(0 != object->parameters) | |||
| free(object->parameters); | |||
| if(0 != object->raw_bits) | |||
| free(object->raw_bits); | |||
| FLAC__format_entropy_coding_method_partitioned_rice_contents_init(object); | |||
| } | |||
| FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order) | |||
| { | |||
| FLAC__ASSERT(0 != object); | |||
| FLAC__ASSERT(object->capacity_by_order > 0 || (0 == object->parameters && 0 == object->raw_bits)); | |||
| if(object->capacity_by_order < max_partition_order) { | |||
| if(0 == (object->parameters = (unsigned*)realloc(object->parameters, sizeof(unsigned)*(1 << max_partition_order)))) | |||
| return false; | |||
| if(0 == (object->raw_bits = (unsigned*)realloc(object->raw_bits, sizeof(unsigned)*(1 << max_partition_order)))) | |||
| return false; | |||
| memset(object->raw_bits, 0, sizeof(unsigned)*(1 << max_partition_order)); | |||
| object->capacity_by_order = max_partition_order; | |||
| } | |||
| return true; | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__ALL_H | |||
| #define FLAC__PRIVATE__ALL_H | |||
| #include "bitmath.h" | |||
| #include "bitreader.h" | |||
| #include "bitwriter.h" | |||
| #include "cpu.h" | |||
| #include "crc.h" | |||
| #include "fixed.h" | |||
| #include "float.h" | |||
| #include "format.h" | |||
| #include "lpc.h" | |||
| #include "md5.h" | |||
| #include "memory.h" | |||
| #include "metadata.h" | |||
| #include "stream_encoder_framing.h" | |||
| #endif | |||
| @@ -0,0 +1,42 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__BITMATH_H | |||
| #define FLAC__PRIVATE__BITMATH_H | |||
| #include "../../../ordinals.h" | |||
| unsigned FLAC__bitmath_ilog2(FLAC__uint32 v); | |||
| unsigned FLAC__bitmath_ilog2_wide(FLAC__uint64 v); | |||
| unsigned FLAC__bitmath_silog2(int v); | |||
| unsigned FLAC__bitmath_silog2_wide(FLAC__int64 v); | |||
| #endif | |||
| @@ -0,0 +1,99 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__BITREADER_H | |||
| #define FLAC__PRIVATE__BITREADER_H | |||
| #include <stdio.h> /* for FILE */ | |||
| #include "../../../ordinals.h" | |||
| #include "cpu.h" | |||
| /* | |||
| * opaque structure definition | |||
| */ | |||
| struct FLAC__BitReader; | |||
| typedef struct FLAC__BitReader FLAC__BitReader; | |||
| typedef FLAC__bool (*FLAC__BitReaderReadCallback)(FLAC__byte buffer[], size_t *bytes, void *client_data); | |||
| /* | |||
| * construction, deletion, initialization, etc functions | |||
| */ | |||
| FLAC__BitReader *FLAC__bitreader_new(void); | |||
| void FLAC__bitreader_delete(FLAC__BitReader *br); | |||
| FLAC__bool FLAC__bitreader_init(FLAC__BitReader *br, FLAC__CPUInfo cpu, FLAC__BitReaderReadCallback rcb, void *cd); | |||
| void FLAC__bitreader_free(FLAC__BitReader *br); /* does not 'free(br)' */ | |||
| FLAC__bool FLAC__bitreader_clear(FLAC__BitReader *br); | |||
| void FLAC__bitreader_dump(const FLAC__BitReader *br, FILE *out); | |||
| /* | |||
| * CRC functions | |||
| */ | |||
| void FLAC__bitreader_reset_read_crc16(FLAC__BitReader *br, FLAC__uint16 seed); | |||
| FLAC__uint16 FLAC__bitreader_get_read_crc16(FLAC__BitReader *br); | |||
| /* | |||
| * info functions | |||
| */ | |||
| FLAC__bool FLAC__bitreader_is_consumed_byte_aligned(const FLAC__BitReader *br); | |||
| unsigned FLAC__bitreader_bits_left_for_byte_alignment(const FLAC__BitReader *br); | |||
| unsigned FLAC__bitreader_get_input_bits_unconsumed(const FLAC__BitReader *br); | |||
| /* | |||
| * read functions | |||
| */ | |||
| FLAC__bool FLAC__bitreader_read_raw_uint32(FLAC__BitReader *br, FLAC__uint32 *val, unsigned bits); | |||
| FLAC__bool FLAC__bitreader_read_raw_int32(FLAC__BitReader *br, FLAC__int32 *val, unsigned bits); | |||
| FLAC__bool FLAC__bitreader_read_raw_uint64(FLAC__BitReader *br, FLAC__uint64 *val, unsigned bits); | |||
| FLAC__bool FLAC__bitreader_read_uint32_little_endian(FLAC__BitReader *br, FLAC__uint32 *val); /*only for bits=32*/ | |||
| FLAC__bool FLAC__bitreader_skip_bits_no_crc(FLAC__BitReader *br, unsigned bits); /* WATCHOUT: does not CRC the skipped data! */ /*@@@@ add to unit tests */ | |||
| FLAC__bool FLAC__bitreader_skip_byte_block_aligned_no_crc(FLAC__BitReader *br, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ | |||
| FLAC__bool FLAC__bitreader_read_byte_block_aligned_no_crc(FLAC__BitReader *br, FLAC__byte *val, unsigned nvals); /* WATCHOUT: does not CRC the read data! */ | |||
| FLAC__bool FLAC__bitreader_read_unary_unsigned(FLAC__BitReader *br, unsigned *val); | |||
| FLAC__bool FLAC__bitreader_read_rice_signed(FLAC__BitReader *br, int *val, unsigned parameter); | |||
| FLAC__bool FLAC__bitreader_read_rice_signed_block(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); | |||
| #ifndef FLAC__NO_ASM | |||
| # ifdef FLAC__CPU_IA32 | |||
| # ifdef FLAC__HAS_NASM | |||
| FLAC__bool FLAC__bitreader_read_rice_signed_block_asm_ia32_bswap(FLAC__BitReader *br, int vals[], unsigned nvals, unsigned parameter); | |||
| # endif | |||
| # endif | |||
| #endif | |||
| #if 0 /* UNUSED */ | |||
| FLAC__bool FLAC__bitreader_read_golomb_signed(FLAC__BitReader *br, int *val, unsigned parameter); | |||
| FLAC__bool FLAC__bitreader_read_golomb_unsigned(FLAC__BitReader *br, unsigned *val, unsigned parameter); | |||
| #endif | |||
| FLAC__bool FLAC__bitreader_read_utf8_uint32(FLAC__BitReader *br, FLAC__uint32 *val, FLAC__byte *raw, unsigned *rawlen); | |||
| FLAC__bool FLAC__bitreader_read_utf8_uint64(FLAC__BitReader *br, FLAC__uint64 *val, FLAC__byte *raw, unsigned *rawlen); | |||
| FLAC__bool bitreader_read_from_client_(FLAC__BitReader *br); | |||
| #endif | |||
| @@ -0,0 +1,103 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__BITWRITER_H | |||
| #define FLAC__PRIVATE__BITWRITER_H | |||
| #include <stdio.h> /* for FILE */ | |||
| #include "../../../ordinals.h" | |||
| /* | |||
| * opaque structure definition | |||
| */ | |||
| struct FLAC__BitWriter; | |||
| typedef struct FLAC__BitWriter FLAC__BitWriter; | |||
| /* | |||
| * construction, deletion, initialization, etc functions | |||
| */ | |||
| FLAC__BitWriter *FLAC__bitwriter_new(void); | |||
| void FLAC__bitwriter_delete(FLAC__BitWriter *bw); | |||
| FLAC__bool FLAC__bitwriter_init(FLAC__BitWriter *bw); | |||
| void FLAC__bitwriter_free(FLAC__BitWriter *bw); /* does not 'free(buffer)' */ | |||
| void FLAC__bitwriter_clear(FLAC__BitWriter *bw); | |||
| void FLAC__bitwriter_dump(const FLAC__BitWriter *bw, FILE *out); | |||
| /* | |||
| * CRC functions | |||
| * | |||
| * non-const *bw because they have to cal FLAC__bitwriter_get_buffer() | |||
| */ | |||
| FLAC__bool FLAC__bitwriter_get_write_crc16(FLAC__BitWriter *bw, FLAC__uint16 *crc); | |||
| FLAC__bool FLAC__bitwriter_get_write_crc8(FLAC__BitWriter *bw, FLAC__byte *crc); | |||
| /* | |||
| * info functions | |||
| */ | |||
| FLAC__bool FLAC__bitwriter_is_byte_aligned(const FLAC__BitWriter *bw); | |||
| unsigned FLAC__bitwriter_get_input_bits_unconsumed(const FLAC__BitWriter *bw); /* can be called anytime, returns total # of bits unconsumed */ | |||
| /* | |||
| * direct buffer access | |||
| * | |||
| * there may be no calls on the bitwriter between get and release. | |||
| * the bitwriter continues to own the returned buffer. | |||
| * before get, bitwriter MUST be byte aligned: check with FLAC__bitwriter_is_byte_aligned() | |||
| */ | |||
| FLAC__bool FLAC__bitwriter_get_buffer(FLAC__BitWriter *bw, const FLAC__byte **buffer, size_t *bytes); | |||
| void FLAC__bitwriter_release_buffer(FLAC__BitWriter *bw); | |||
| /* | |||
| * write functions | |||
| */ | |||
| FLAC__bool FLAC__bitwriter_write_zeroes(FLAC__BitWriter *bw, unsigned bits); | |||
| FLAC__bool FLAC__bitwriter_write_raw_uint32(FLAC__BitWriter *bw, FLAC__uint32 val, unsigned bits); | |||
| FLAC__bool FLAC__bitwriter_write_raw_int32(FLAC__BitWriter *bw, FLAC__int32 val, unsigned bits); | |||
| FLAC__bool FLAC__bitwriter_write_raw_uint64(FLAC__BitWriter *bw, FLAC__uint64 val, unsigned bits); | |||
| FLAC__bool FLAC__bitwriter_write_raw_uint32_little_endian(FLAC__BitWriter *bw, FLAC__uint32 val); /*only for bits=32*/ | |||
| FLAC__bool FLAC__bitwriter_write_byte_block(FLAC__BitWriter *bw, const FLAC__byte vals[], unsigned nvals); | |||
| FLAC__bool FLAC__bitwriter_write_unary_unsigned(FLAC__BitWriter *bw, unsigned val); | |||
| unsigned FLAC__bitwriter_rice_bits(FLAC__int32 val, unsigned parameter); | |||
| #if 0 /* UNUSED */ | |||
| unsigned FLAC__bitwriter_golomb_bits_signed(int val, unsigned parameter); | |||
| unsigned FLAC__bitwriter_golomb_bits_unsigned(unsigned val, unsigned parameter); | |||
| #endif | |||
| FLAC__bool FLAC__bitwriter_write_rice_signed(FLAC__BitWriter *bw, FLAC__int32 val, unsigned parameter); | |||
| FLAC__bool FLAC__bitwriter_write_rice_signed_block(FLAC__BitWriter *bw, const FLAC__int32 *vals, unsigned nvals, unsigned parameter); | |||
| #if 0 /* UNUSED */ | |||
| FLAC__bool FLAC__bitwriter_write_golomb_signed(FLAC__BitWriter *bw, int val, unsigned parameter); | |||
| FLAC__bool FLAC__bitwriter_write_golomb_unsigned(FLAC__BitWriter *bw, unsigned val, unsigned parameter); | |||
| #endif | |||
| FLAC__bool FLAC__bitwriter_write_utf8_uint32(FLAC__BitWriter *bw, FLAC__uint32 val); | |||
| FLAC__bool FLAC__bitwriter_write_utf8_uint64(FLAC__BitWriter *bw, FLAC__uint64 val); | |||
| FLAC__bool FLAC__bitwriter_zero_pad_to_byte_boundary(FLAC__BitWriter *bw); | |||
| #endif | |||
| @@ -0,0 +1,88 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__CPU_H | |||
| #define FLAC__PRIVATE__CPU_H | |||
| #include "../../../ordinals.h" | |||
| #ifdef HAVE_CONFIG_H | |||
| #include <config.h> | |||
| #endif | |||
| typedef enum { | |||
| FLAC__CPUINFO_TYPE_IA32, | |||
| FLAC__CPUINFO_TYPE_PPC, | |||
| FLAC__CPUINFO_TYPE_UNKNOWN | |||
| } FLAC__CPUInfo_Type; | |||
| typedef struct { | |||
| FLAC__bool cpuid; | |||
| FLAC__bool bswap; | |||
| FLAC__bool cmov; | |||
| FLAC__bool mmx; | |||
| FLAC__bool fxsr; | |||
| FLAC__bool sse; | |||
| FLAC__bool sse2; | |||
| FLAC__bool sse3; | |||
| FLAC__bool ssse3; | |||
| FLAC__bool _3dnow; | |||
| FLAC__bool ext3dnow; | |||
| FLAC__bool extmmx; | |||
| } FLAC__CPUInfo_IA32; | |||
| typedef struct { | |||
| FLAC__bool altivec; | |||
| FLAC__bool ppc64; | |||
| } FLAC__CPUInfo_PPC; | |||
| typedef struct { | |||
| FLAC__bool use_asm; | |||
| FLAC__CPUInfo_Type type; | |||
| union { | |||
| FLAC__CPUInfo_IA32 ia32; | |||
| FLAC__CPUInfo_PPC ppc; | |||
| } data; | |||
| } FLAC__CPUInfo; | |||
| void FLAC__cpu_info(FLAC__CPUInfo *info); | |||
| #ifndef FLAC__NO_ASM | |||
| #ifdef FLAC__CPU_IA32 | |||
| #ifdef FLAC__HAS_NASM | |||
| FLAC__uint32 FLAC__cpu_have_cpuid_asm_ia32(void); | |||
| void FLAC__cpu_info_asm_ia32(FLAC__uint32 *flags_edx, FLAC__uint32 *flags_ecx); | |||
| FLAC__uint32 FLAC__cpu_info_extended_amd_asm_ia32(void); | |||
| #endif | |||
| #endif | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,61 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__CRC_H | |||
| #define FLAC__PRIVATE__CRC_H | |||
| #include "../../../ordinals.h" | |||
| /* 8 bit CRC generator, MSB shifted first | |||
| ** polynomial = x^8 + x^2 + x^1 + x^0 | |||
| ** init = 0 | |||
| */ | |||
| extern FLAC__byte const FLAC__crc8_table[256]; | |||
| #define FLAC__CRC8_UPDATE(data, crc) (crc) = FLAC__crc8_table[(crc) ^ (data)]; | |||
| void FLAC__crc8_update(const FLAC__byte data, FLAC__uint8 *crc); | |||
| void FLAC__crc8_update_block(const FLAC__byte *data, unsigned len, FLAC__uint8 *crc); | |||
| FLAC__uint8 FLAC__crc8(const FLAC__byte *data, unsigned len); | |||
| /* 16 bit CRC generator, MSB shifted first | |||
| ** polynomial = x^16 + x^15 + x^2 + x^0 | |||
| ** init = 0 | |||
| */ | |||
| extern unsigned FLAC__crc16_table[256]; | |||
| #define FLAC__CRC16_UPDATE(data, crc) (((((crc)<<8) & 0xffff) ^ FLAC__crc16_table[((crc)>>8) ^ (data)])) | |||
| /* this alternate may be faster on some systems/compilers */ | |||
| #if 0 | |||
| #define FLAC__CRC16_UPDATE(data, crc) ((((crc)<<8) ^ FLAC__crc16_table[((crc)>>8) ^ (data)]) & 0xffff) | |||
| #endif | |||
| unsigned FLAC__crc16(const FLAC__byte *data, unsigned len); | |||
| #endif | |||
| @@ -0,0 +1,97 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__FIXED_H | |||
| #define FLAC__PRIVATE__FIXED_H | |||
| #ifdef HAVE_CONFIG_H | |||
| #include <config.h> | |||
| #endif | |||
| #include "float.h" | |||
| #include "format.h" | |||
| /* | |||
| * FLAC__fixed_compute_best_predictor() | |||
| * -------------------------------------------------------------------- | |||
| * Compute the best fixed predictor and the expected bits-per-sample | |||
| * of the residual signal for each order. The _wide() version uses | |||
| * 64-bit integers which is statistically necessary when bits-per- | |||
| * sample + log2(blocksize) > 30 | |||
| * | |||
| * IN data[0,data_len-1] | |||
| * IN data_len | |||
| * OUT residual_bits_per_sample[0,FLAC__MAX_FIXED_ORDER] | |||
| */ | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); | |||
| # ifndef FLAC__NO_ASM | |||
| # ifdef FLAC__CPU_IA32 | |||
| # ifdef FLAC__HAS_NASM | |||
| unsigned FLAC__fixed_compute_best_predictor_asm_ia32_mmx_cmov(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); | |||
| # endif | |||
| # endif | |||
| # endif | |||
| unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__float residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); | |||
| #else | |||
| unsigned FLAC__fixed_compute_best_predictor(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); | |||
| unsigned FLAC__fixed_compute_best_predictor_wide(const FLAC__int32 data[], unsigned data_len, FLAC__fixedpoint residual_bits_per_sample[FLAC__MAX_FIXED_ORDER+1]); | |||
| #endif | |||
| /* | |||
| * FLAC__fixed_compute_residual() | |||
| * -------------------------------------------------------------------- | |||
| * Compute the residual signal obtained from sutracting the predicted | |||
| * signal from the original. | |||
| * | |||
| * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) | |||
| * IN data_len length of original signal | |||
| * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order | |||
| * OUT residual[0,data_len-1] residual signal | |||
| */ | |||
| void FLAC__fixed_compute_residual(const FLAC__int32 data[], unsigned data_len, unsigned order, FLAC__int32 residual[]); | |||
| /* | |||
| * FLAC__fixed_restore_signal() | |||
| * -------------------------------------------------------------------- | |||
| * Restore the original signal by summing the residual and the | |||
| * predictor. | |||
| * | |||
| * IN residual[0,data_len-1] residual signal | |||
| * IN data_len length of original signal | |||
| * IN order <= FLAC__MAX_FIXED_ORDER fixed-predictor order | |||
| * *** IMPORTANT: the caller must pass in the historical samples: | |||
| * IN data[-order,-1] previously-reconstructed historical samples | |||
| * OUT data[0,data_len-1] original signal | |||
| */ | |||
| void FLAC__fixed_restore_signal(const FLAC__int32 residual[], unsigned data_len, unsigned order, FLAC__int32 data[]); | |||
| #endif | |||
| @@ -0,0 +1,97 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__FLOAT_H | |||
| #define FLAC__PRIVATE__FLOAT_H | |||
| #ifdef HAVE_CONFIG_H | |||
| #include <config.h> | |||
| #endif | |||
| #include "../../../ordinals.h" | |||
| /* | |||
| * These typedefs make it easier to ensure that integer versions of | |||
| * the library really only contain integer operations. All the code | |||
| * in libFLAC should use FLAC__float and FLAC__double in place of | |||
| * float and double, and be protected by checks of the macro | |||
| * FLAC__INTEGER_ONLY_LIBRARY. | |||
| * | |||
| * FLAC__real is the basic floating point type used in LPC analysis. | |||
| */ | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| typedef double FLAC__double; | |||
| typedef float FLAC__float; | |||
| /* | |||
| * WATCHOUT: changing FLAC__real will change the signatures of many | |||
| * functions that have assembly language equivalents and break them. | |||
| */ | |||
| typedef float FLAC__real; | |||
| #else | |||
| /* | |||
| * The convention for FLAC__fixedpoint is to use the upper 16 bits | |||
| * for the integer part and lower 16 bits for the fractional part. | |||
| */ | |||
| typedef FLAC__int32 FLAC__fixedpoint; | |||
| extern const FLAC__fixedpoint FLAC__FP_ZERO; | |||
| extern const FLAC__fixedpoint FLAC__FP_ONE_HALF; | |||
| extern const FLAC__fixedpoint FLAC__FP_ONE; | |||
| extern const FLAC__fixedpoint FLAC__FP_LN2; | |||
| extern const FLAC__fixedpoint FLAC__FP_E; | |||
| #define FLAC__fixedpoint_trunc(x) ((x)>>16) | |||
| #define FLAC__fixedpoint_mul(x, y) ( (FLAC__fixedpoint) ( ((FLAC__int64)(x)*(FLAC__int64)(y)) >> 16 ) ) | |||
| #define FLAC__fixedpoint_div(x, y) ( (FLAC__fixedpoint) ( ( ((FLAC__int64)(x)<<32) / (FLAC__int64)(y) ) >> 16 ) ) | |||
| /* | |||
| * FLAC__fixedpoint_log2() | |||
| * -------------------------------------------------------------------- | |||
| * Returns the base-2 logarithm of the fixed-point number 'x' using an | |||
| * algorithm by Knuth for x >= 1.0 | |||
| * | |||
| * 'fracbits' is the number of fractional bits of 'x'. 'fracbits' must | |||
| * be < 32 and evenly divisible by 4 (0 is OK but not very precise). | |||
| * | |||
| * 'precision' roughly limits the number of iterations that are done; | |||
| * use (unsigned)(-1) for maximum precision. | |||
| * | |||
| * If 'x' is less than one -- that is, x < (1<<fracbits) -- then this | |||
| * function will punt and return 0. | |||
| * | |||
| * The return value will also have 'fracbits' fractional bits. | |||
| */ | |||
| FLAC__uint32 FLAC__fixedpoint_log2(FLAC__uint32 x, unsigned fracbits, unsigned precision); | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,44 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__FORMAT_H | |||
| #define FLAC__PRIVATE__FORMAT_H | |||
| #include "../../../format.h" | |||
| unsigned FLAC__format_get_max_rice_partition_order(unsigned blocksize, unsigned predictor_order); | |||
| unsigned FLAC__format_get_max_rice_partition_order_from_blocksize(unsigned blocksize); | |||
| unsigned FLAC__format_get_max_rice_partition_order_from_blocksize_limited_max_and_predictor_order(unsigned limit, unsigned blocksize, unsigned predictor_order); | |||
| void FLAC__format_entropy_coding_method_partitioned_rice_contents_init(FLAC__EntropyCodingMethod_PartitionedRiceContents *object); | |||
| void FLAC__format_entropy_coding_method_partitioned_rice_contents_clear(FLAC__EntropyCodingMethod_PartitionedRiceContents *object); | |||
| FLAC__bool FLAC__format_entropy_coding_method_partitioned_rice_contents_ensure_size(FLAC__EntropyCodingMethod_PartitionedRiceContents *object, unsigned max_partition_order); | |||
| #endif | |||
| @@ -0,0 +1,214 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__LPC_H | |||
| #define FLAC__PRIVATE__LPC_H | |||
| #ifdef HAVE_CONFIG_H | |||
| #include <config.h> | |||
| #endif | |||
| #include "float.h" | |||
| #include "../../../format.h" | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| /* | |||
| * FLAC__lpc_window_data() | |||
| * -------------------------------------------------------------------- | |||
| * Applies the given window to the data. | |||
| * OPT: asm implementation | |||
| * | |||
| * IN in[0,data_len-1] | |||
| * IN window[0,data_len-1] | |||
| * OUT out[0,lag-1] | |||
| * IN data_len | |||
| */ | |||
| void FLAC__lpc_window_data(const FLAC__int32 in[], const FLAC__real window[], FLAC__real out[], unsigned data_len); | |||
| /* | |||
| * FLAC__lpc_compute_autocorrelation() | |||
| * -------------------------------------------------------------------- | |||
| * Compute the autocorrelation for lags between 0 and lag-1. | |||
| * Assumes data[] outside of [0,data_len-1] == 0. | |||
| * Asserts that lag > 0. | |||
| * | |||
| * IN data[0,data_len-1] | |||
| * IN data_len | |||
| * IN 0 < lag <= data_len | |||
| * OUT autoc[0,lag-1] | |||
| */ | |||
| void FLAC__lpc_compute_autocorrelation(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); | |||
| #ifndef FLAC__NO_ASM | |||
| # ifdef FLAC__CPU_IA32 | |||
| # ifdef FLAC__HAS_NASM | |||
| void FLAC__lpc_compute_autocorrelation_asm_ia32(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); | |||
| void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_4(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); | |||
| void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_8(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); | |||
| void FLAC__lpc_compute_autocorrelation_asm_ia32_sse_lag_12(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); | |||
| void FLAC__lpc_compute_autocorrelation_asm_ia32_3dnow(const FLAC__real data[], unsigned data_len, unsigned lag, FLAC__real autoc[]); | |||
| # endif | |||
| # endif | |||
| #endif | |||
| /* | |||
| * FLAC__lpc_compute_lp_coefficients() | |||
| * -------------------------------------------------------------------- | |||
| * Computes LP coefficients for orders 1..max_order. | |||
| * Do not call if autoc[0] == 0.0. This means the signal is zero | |||
| * and there is no point in calculating a predictor. | |||
| * | |||
| * IN autoc[0,max_order] autocorrelation values | |||
| * IN 0 < max_order <= FLAC__MAX_LPC_ORDER max LP order to compute | |||
| * OUT lp_coeff[0,max_order-1][0,max_order-1] LP coefficients for each order | |||
| * *** IMPORTANT: | |||
| * *** lp_coeff[0,max_order-1][max_order,FLAC__MAX_LPC_ORDER-1] are untouched | |||
| * OUT error[0,max_order-1] error for each order (more | |||
| * specifically, the variance of | |||
| * the error signal times # of | |||
| * samples in the signal) | |||
| * | |||
| * Example: if max_order is 9, the LP coefficients for order 9 will be | |||
| * in lp_coeff[8][0,8], the LP coefficients for order 8 will be | |||
| * in lp_coeff[7][0,7], etc. | |||
| */ | |||
| void FLAC__lpc_compute_lp_coefficients(const FLAC__real autoc[], unsigned *max_order, FLAC__real lp_coeff[][FLAC__MAX_LPC_ORDER], FLAC__double error[]); | |||
| /* | |||
| * FLAC__lpc_quantize_coefficients() | |||
| * -------------------------------------------------------------------- | |||
| * Quantizes the LP coefficients. NOTE: precision + bits_per_sample | |||
| * must be less than 32 (sizeof(FLAC__int32)*8). | |||
| * | |||
| * IN lp_coeff[0,order-1] LP coefficients | |||
| * IN order LP order | |||
| * IN FLAC__MIN_QLP_COEFF_PRECISION < precision | |||
| * desired precision (in bits, including sign | |||
| * bit) of largest coefficient | |||
| * OUT qlp_coeff[0,order-1] quantized coefficients | |||
| * OUT shift # of bits to shift right to get approximated | |||
| * LP coefficients. NOTE: could be negative. | |||
| * RETURN 0 => quantization OK | |||
| * 1 => coefficients require too much shifting for *shift to | |||
| * fit in the LPC subframe header. 'shift' is unset. | |||
| * 2 => coefficients are all zero, which is bad. 'shift' is | |||
| * unset. | |||
| */ | |||
| int FLAC__lpc_quantize_coefficients(const FLAC__real lp_coeff[], unsigned order, unsigned precision, FLAC__int32 qlp_coeff[], int *shift); | |||
| /* | |||
| * FLAC__lpc_compute_residual_from_qlp_coefficients() | |||
| * -------------------------------------------------------------------- | |||
| * Compute the residual signal obtained from sutracting the predicted | |||
| * signal from the original. | |||
| * | |||
| * IN data[-order,data_len-1] original signal (NOTE THE INDICES!) | |||
| * IN data_len length of original signal | |||
| * IN qlp_coeff[0,order-1] quantized LP coefficients | |||
| * IN order > 0 LP order | |||
| * IN lp_quantization quantization of LP coefficients in bits | |||
| * OUT residual[0,data_len-1] residual signal | |||
| */ | |||
| void FLAC__lpc_compute_residual_from_qlp_coefficients(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); | |||
| void FLAC__lpc_compute_residual_from_qlp_coefficients_wide(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); | |||
| #ifndef FLAC__NO_ASM | |||
| # ifdef FLAC__CPU_IA32 | |||
| # ifdef FLAC__HAS_NASM | |||
| void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); | |||
| void FLAC__lpc_compute_residual_from_qlp_coefficients_asm_ia32_mmx(const FLAC__int32 *data, unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 residual[]); | |||
| # endif | |||
| # endif | |||
| #endif | |||
| #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ | |||
| /* | |||
| * FLAC__lpc_restore_signal() | |||
| * -------------------------------------------------------------------- | |||
| * Restore the original signal by summing the residual and the | |||
| * predictor. | |||
| * | |||
| * IN residual[0,data_len-1] residual signal | |||
| * IN data_len length of original signal | |||
| * IN qlp_coeff[0,order-1] quantized LP coefficients | |||
| * IN order > 0 LP order | |||
| * IN lp_quantization quantization of LP coefficients in bits | |||
| * *** IMPORTANT: the caller must pass in the historical samples: | |||
| * IN data[-order,-1] previously-reconstructed historical samples | |||
| * OUT data[0,data_len-1] original signal | |||
| */ | |||
| void FLAC__lpc_restore_signal(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); | |||
| void FLAC__lpc_restore_signal_wide(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); | |||
| #ifndef FLAC__NO_ASM | |||
| # ifdef FLAC__CPU_IA32 | |||
| # ifdef FLAC__HAS_NASM | |||
| void FLAC__lpc_restore_signal_asm_ia32(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); | |||
| void FLAC__lpc_restore_signal_asm_ia32_mmx(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); | |||
| # endif /* FLAC__HAS_NASM */ | |||
| # elif defined FLAC__CPU_PPC | |||
| void FLAC__lpc_restore_signal_asm_ppc_altivec_16(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); | |||
| void FLAC__lpc_restore_signal_asm_ppc_altivec_16_order8(const FLAC__int32 residual[], unsigned data_len, const FLAC__int32 qlp_coeff[], unsigned order, int lp_quantization, FLAC__int32 data[]); | |||
| # endif/* FLAC__CPU_IA32 || FLAC__CPU_PPC */ | |||
| #endif /* FLAC__NO_ASM */ | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| /* | |||
| * FLAC__lpc_compute_expected_bits_per_residual_sample() | |||
| * -------------------------------------------------------------------- | |||
| * Compute the expected number of bits per residual signal sample | |||
| * based on the LP error (which is related to the residual variance). | |||
| * | |||
| * IN lpc_error >= 0.0 error returned from calculating LP coefficients | |||
| * IN total_samples > 0 # of samples in residual signal | |||
| * RETURN expected bits per sample | |||
| */ | |||
| FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample(FLAC__double lpc_error, unsigned total_samples); | |||
| FLAC__double FLAC__lpc_compute_expected_bits_per_residual_sample_with_error_scale(FLAC__double lpc_error, FLAC__double error_scale); | |||
| /* | |||
| * FLAC__lpc_compute_best_order() | |||
| * -------------------------------------------------------------------- | |||
| * Compute the best order from the array of signal errors returned | |||
| * during coefficient computation. | |||
| * | |||
| * IN lpc_error[0,max_order-1] >= 0.0 error returned from calculating LP coefficients | |||
| * IN max_order > 0 max LP order | |||
| * IN total_samples > 0 # of samples in residual signal | |||
| * IN overhead_bits_per_order # of bits overhead for each increased LP order | |||
| * (includes warmup sample size and quantized LP coefficient) | |||
| * RETURN [1,max_order] best order | |||
| */ | |||
| unsigned FLAC__lpc_compute_best_order(const FLAC__double lpc_error[], unsigned max_order, unsigned total_samples, unsigned overhead_bits_per_order); | |||
| #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ | |||
| #endif | |||
| @@ -0,0 +1,44 @@ | |||
| #ifndef FLAC__PRIVATE__MD5_H | |||
| #define FLAC__PRIVATE__MD5_H | |||
| /* | |||
| * This is the header file for the MD5 message-digest algorithm. | |||
| * The algorithm is due to Ron Rivest. This code was | |||
| * written by Colin Plumb in 1993, no copyright is claimed. | |||
| * This code is in the public domain; do with it what you wish. | |||
| * | |||
| * Equivalent code is available from RSA Data Security, Inc. | |||
| * This code has been tested against that, and is equivalent, | |||
| * except that you don't need to include two pages of legalese | |||
| * with every copy. | |||
| * | |||
| * To compute the message digest of a chunk of bytes, declare an | |||
| * MD5Context structure, pass it to MD5Init, call MD5Update as | |||
| * needed on buffers full of bytes, and then call MD5Final, which | |||
| * will fill a supplied 16-byte array with the digest. | |||
| * | |||
| * Changed so as no longer to depend on Colin Plumb's `usual.h' | |||
| * header definitions; now uses stuff from dpkg's config.h | |||
| * - Ian Jackson <ijackson@nyx.cs.du.edu>. | |||
| * Still in the public domain. | |||
| * | |||
| * Josh Coalson: made some changes to integrate with libFLAC. | |||
| * Still in the public domain, with no warranty. | |||
| */ | |||
| #include "../../../ordinals.h" | |||
| typedef struct { | |||
| FLAC__uint32 in[16]; | |||
| FLAC__uint32 buf[4]; | |||
| FLAC__uint32 bytes[2]; | |||
| FLAC__byte *internal_buf; | |||
| size_t capacity; | |||
| } FLAC__MD5Context; | |||
| void FLAC__MD5Init(FLAC__MD5Context *context); | |||
| void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *context); | |||
| FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample); | |||
| #endif | |||
| @@ -0,0 +1,56 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__MEMORY_H | |||
| #define FLAC__PRIVATE__MEMORY_H | |||
| #ifdef HAVE_CONFIG_H | |||
| #include <config.h> | |||
| #endif | |||
| #include <stdlib.h> /* for size_t */ | |||
| #include "float.h" | |||
| #include "../../../ordinals.h" /* for FLAC__bool */ | |||
| /* Returns the unaligned address returned by malloc. | |||
| * Use free() on this address to deallocate. | |||
| */ | |||
| void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address); | |||
| FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer); | |||
| FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer); | |||
| FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer); | |||
| FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer); | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer); | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,45 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__METADATA_H | |||
| #define FLAC__PRIVATE__METADATA_H | |||
| #include "metadata.h" | |||
| /* WATCHOUT: all malloc()ed data in the block is free()ed; this may not | |||
| * be a consistent state (e.g. PICTURE) or equivalent to the initial | |||
| * state after FLAC__metadata_object_new() | |||
| */ | |||
| void FLAC__metadata_object_delete_data(FLAC__StreamMetadata *object); | |||
| void FLAC__metadata_object_cuesheet_track_delete_data(FLAC__StreamMetadata_CueSheet_Track *object); | |||
| #endif | |||
| @@ -0,0 +1,45 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H | |||
| #define FLAC__PRIVATE__STREAM_ENCODER_FRAMING_H | |||
| #include "../../../format.h" | |||
| #include "bitwriter.h" | |||
| FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw); | |||
| FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw); | |||
| FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); | |||
| FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); | |||
| FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); | |||
| FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw); | |||
| #endif | |||
| @@ -0,0 +1,71 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PRIVATE__WINDOW_H | |||
| #define FLAC__PRIVATE__WINDOW_H | |||
| #ifdef HAVE_CONFIG_H | |||
| #include <config.h> | |||
| #endif | |||
| #include "float.h" | |||
| #include "../../../format.h" | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| /* | |||
| * FLAC__window_*() | |||
| * -------------------------------------------------------------------- | |||
| * Calculates window coefficients according to different apodization | |||
| * functions. | |||
| * | |||
| * OUT window[0,L-1] | |||
| * IN L (number of points in window) | |||
| */ | |||
| void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev); /* 0.0 < stddev <= 0.5 */ | |||
| void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L); | |||
| void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p); | |||
| void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L); | |||
| #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ | |||
| #endif | |||
| @@ -0,0 +1,38 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PROTECTED__ALL_H | |||
| #define FLAC__PROTECTED__ALL_H | |||
| #include "stream_decoder.h" | |||
| #include "stream_encoder.h" | |||
| #endif | |||
| @@ -0,0 +1,58 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PROTECTED__STREAM_DECODER_H | |||
| #define FLAC__PROTECTED__STREAM_DECODER_H | |||
| #include "../../../stream_decoder.h" | |||
| #if FLAC__HAS_OGG | |||
| #include "include/private/ogg_decoder_aspect.h" | |||
| #endif | |||
| typedef struct FLAC__StreamDecoderProtected { | |||
| FLAC__StreamDecoderState state; | |||
| unsigned channels; | |||
| FLAC__ChannelAssignment channel_assignment; | |||
| unsigned bits_per_sample; | |||
| unsigned sample_rate; /* in Hz */ | |||
| unsigned blocksize; /* in samples (per channel) */ | |||
| FLAC__bool md5_checking; /* if true, generate MD5 signature of decoded data and compare against signature in the STREAMINFO metadata block */ | |||
| #if FLAC__HAS_OGG | |||
| FLAC__OggDecoderAspect ogg_decoder_aspect; | |||
| #endif | |||
| } FLAC__StreamDecoderProtected; | |||
| /* | |||
| * return the number of input bytes consumed | |||
| */ | |||
| unsigned FLAC__stream_decoder_get_input_bytes_unconsumed(const FLAC__StreamDecoder *decoder); | |||
| #endif | |||
| @@ -0,0 +1,110 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__PROTECTED__STREAM_ENCODER_H | |||
| #define FLAC__PROTECTED__STREAM_ENCODER_H | |||
| #include "../../../stream_encoder.h" | |||
| #if FLAC__HAS_OGG | |||
| #include "private/ogg_encoder_aspect.h" | |||
| #endif | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| #include "../private/float.h" | |||
| #define FLAC__MAX_APODIZATION_FUNCTIONS 32 | |||
| typedef enum { | |||
| FLAC__APODIZATION_BARTLETT, | |||
| FLAC__APODIZATION_BARTLETT_HANN, | |||
| FLAC__APODIZATION_BLACKMAN, | |||
| FLAC__APODIZATION_BLACKMAN_HARRIS_4TERM_92DB_SIDELOBE, | |||
| FLAC__APODIZATION_CONNES, | |||
| FLAC__APODIZATION_FLATTOP, | |||
| FLAC__APODIZATION_GAUSS, | |||
| FLAC__APODIZATION_HAMMING, | |||
| FLAC__APODIZATION_HANN, | |||
| FLAC__APODIZATION_KAISER_BESSEL, | |||
| FLAC__APODIZATION_NUTTALL, | |||
| FLAC__APODIZATION_RECTANGLE, | |||
| FLAC__APODIZATION_TRIANGLE, | |||
| FLAC__APODIZATION_TUKEY, | |||
| FLAC__APODIZATION_WELCH | |||
| } FLAC__ApodizationFunction; | |||
| typedef struct { | |||
| FLAC__ApodizationFunction type; | |||
| union { | |||
| struct { | |||
| FLAC__real stddev; | |||
| } gauss; | |||
| struct { | |||
| FLAC__real p; | |||
| } tukey; | |||
| } parameters; | |||
| } FLAC__ApodizationSpecification; | |||
| #endif // #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| typedef struct FLAC__StreamEncoderProtected { | |||
| FLAC__StreamEncoderState state; | |||
| FLAC__bool verify; | |||
| FLAC__bool streamable_subset; | |||
| FLAC__bool do_md5; | |||
| FLAC__bool do_mid_side_stereo; | |||
| FLAC__bool loose_mid_side_stereo; | |||
| unsigned channels; | |||
| unsigned bits_per_sample; | |||
| unsigned sample_rate; | |||
| unsigned blocksize; | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| unsigned num_apodizations; | |||
| FLAC__ApodizationSpecification apodizations[FLAC__MAX_APODIZATION_FUNCTIONS]; | |||
| #endif | |||
| unsigned max_lpc_order; | |||
| unsigned qlp_coeff_precision; | |||
| FLAC__bool do_qlp_coeff_prec_search; | |||
| FLAC__bool do_exhaustive_model_search; | |||
| FLAC__bool do_escape_coding; | |||
| unsigned min_residual_partition_order; | |||
| unsigned max_residual_partition_order; | |||
| unsigned rice_parameter_search_dist; | |||
| FLAC__uint64 total_samples_estimate; | |||
| FLAC__StreamMetadata **metadata; | |||
| unsigned num_metadata_blocks; | |||
| FLAC__uint64 streaminfo_offset, seektable_offset, audio_offset; | |||
| #if FLAC__HAS_OGG | |||
| FLAC__OggEncoderAspect ogg_encoder_aspect; | |||
| #endif | |||
| } FLAC__StreamEncoderProtected; | |||
| #endif | |||
| @@ -0,0 +1,425 @@ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include <stdlib.h> /* for malloc() */ | |||
| #include <string.h> /* for memcpy() */ | |||
| #include "include/private/md5.h" | |||
| #include "../alloc.h" | |||
| #ifndef FLaC__INLINE | |||
| #define FLaC__INLINE | |||
| #endif | |||
| /* | |||
| * This code implements the MD5 message-digest algorithm. | |||
| * The algorithm is due to Ron Rivest. This code was | |||
| * written by Colin Plumb in 1993, no copyright is claimed. | |||
| * This code is in the public domain; do with it what you wish. | |||
| * | |||
| * Equivalent code is available from RSA Data Security, Inc. | |||
| * This code has been tested against that, and is equivalent, | |||
| * except that you don't need to include two pages of legalese | |||
| * with every copy. | |||
| * | |||
| * To compute the message digest of a chunk of bytes, declare an | |||
| * MD5Context structure, pass it to MD5Init, call MD5Update as | |||
| * needed on buffers full of bytes, and then call MD5Final, which | |||
| * will fill a supplied 16-byte array with the digest. | |||
| * | |||
| * Changed so as no longer to depend on Colin Plumb's `usual.h' header | |||
| * definitions; now uses stuff from dpkg's config.h. | |||
| * - Ian Jackson <ijackson@nyx.cs.du.edu>. | |||
| * Still in the public domain. | |||
| * | |||
| * Josh Coalson: made some changes to integrate with libFLAC. | |||
| * Still in the public domain. | |||
| */ | |||
| /* The four core functions - F1 is optimized somewhat */ | |||
| /* #define F1(x, y, z) (x & y | ~x & z) */ | |||
| #define F1(x, y, z) (z ^ (x & (y ^ z))) | |||
| #define F2(x, y, z) F1(z, x, y) | |||
| #define F3(x, y, z) (x ^ y ^ z) | |||
| #define F4(x, y, z) (y ^ (x | ~z)) | |||
| /* This is the central step in the MD5 algorithm. */ | |||
| #define MD5STEP(f,w,x,y,z,in,s) \ | |||
| (w += f(x,y,z) + in, w = (w<<s | w>>(32-s)) + x) | |||
| /* | |||
| * The core of the MD5 algorithm, this alters an existing MD5 hash to | |||
| * reflect the addition of 16 longwords of new data. MD5Update blocks | |||
| * the data and converts bytes into longwords for this routine. | |||
| */ | |||
| static void FLAC__MD5Transform(FLAC__uint32 buf[4], FLAC__uint32 const in[16]) | |||
| { | |||
| register FLAC__uint32 a, b, c, d; | |||
| a = buf[0]; | |||
| b = buf[1]; | |||
| c = buf[2]; | |||
| d = buf[3]; | |||
| MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); | |||
| MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); | |||
| MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); | |||
| MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); | |||
| MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); | |||
| MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); | |||
| MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); | |||
| MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); | |||
| MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); | |||
| MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); | |||
| MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); | |||
| MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); | |||
| MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); | |||
| MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); | |||
| MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); | |||
| MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); | |||
| MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); | |||
| MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); | |||
| MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); | |||
| MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); | |||
| MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); | |||
| MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); | |||
| MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); | |||
| MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); | |||
| MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); | |||
| MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); | |||
| MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); | |||
| MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); | |||
| MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); | |||
| MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); | |||
| MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); | |||
| MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); | |||
| MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); | |||
| MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); | |||
| MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); | |||
| MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); | |||
| MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); | |||
| MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); | |||
| MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); | |||
| MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); | |||
| MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); | |||
| MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); | |||
| MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); | |||
| MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); | |||
| MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); | |||
| MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); | |||
| MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); | |||
| MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); | |||
| MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); | |||
| MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); | |||
| MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); | |||
| MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); | |||
| MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); | |||
| MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); | |||
| MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); | |||
| MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); | |||
| MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); | |||
| MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); | |||
| MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); | |||
| MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); | |||
| MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); | |||
| MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); | |||
| MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); | |||
| MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); | |||
| buf[0] += a; | |||
| buf[1] += b; | |||
| buf[2] += c; | |||
| buf[3] += d; | |||
| } | |||
| #if WORDS_BIGENDIAN | |||
| //@@@@@@ OPT: use bswap/intrinsics | |||
| static void byteSwap(FLAC__uint32 *buf, unsigned words) | |||
| { | |||
| register FLAC__uint32 x; | |||
| do { | |||
| x = *buf; | |||
| x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); | |||
| *buf++ = (x >> 16) | (x << 16); | |||
| } while (--words); | |||
| } | |||
| static void byteSwapX16(FLAC__uint32 *buf) | |||
| { | |||
| register FLAC__uint32 x; | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf++ = (x >> 16) | (x << 16); | |||
| x = *buf; x = ((x << 8) & 0xff00ff00) | ((x >> 8) & 0x00ff00ff); *buf = (x >> 16) | (x << 16); | |||
| } | |||
| #else | |||
| #define byteSwap(buf, words) | |||
| #define byteSwapX16(buf) | |||
| #endif | |||
| /* | |||
| * Update context to reflect the concatenation of another buffer full | |||
| * of bytes. | |||
| */ | |||
| static void FLAC__MD5Update(FLAC__MD5Context *ctx, FLAC__byte const *buf, unsigned len) | |||
| { | |||
| FLAC__uint32 t; | |||
| /* Update byte count */ | |||
| t = ctx->bytes[0]; | |||
| if ((ctx->bytes[0] = t + len) < t) | |||
| ctx->bytes[1]++; /* Carry from low to high */ | |||
| t = 64 - (t & 0x3f); /* Space available in ctx->in (at least 1) */ | |||
| if (t > len) { | |||
| memcpy((FLAC__byte *)ctx->in + 64 - t, buf, len); | |||
| return; | |||
| } | |||
| /* First chunk is an odd size */ | |||
| memcpy((FLAC__byte *)ctx->in + 64 - t, buf, t); | |||
| byteSwapX16(ctx->in); | |||
| FLAC__MD5Transform(ctx->buf, ctx->in); | |||
| buf += t; | |||
| len -= t; | |||
| /* Process data in 64-byte chunks */ | |||
| while (len >= 64) { | |||
| memcpy(ctx->in, buf, 64); | |||
| byteSwapX16(ctx->in); | |||
| FLAC__MD5Transform(ctx->buf, ctx->in); | |||
| buf += 64; | |||
| len -= 64; | |||
| } | |||
| /* Handle any remaining bytes of data. */ | |||
| memcpy(ctx->in, buf, len); | |||
| } | |||
| /* | |||
| * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious | |||
| * initialization constants. | |||
| */ | |||
| void FLAC__MD5Init(FLAC__MD5Context *ctx) | |||
| { | |||
| ctx->buf[0] = 0x67452301; | |||
| ctx->buf[1] = 0xefcdab89; | |||
| ctx->buf[2] = 0x98badcfe; | |||
| ctx->buf[3] = 0x10325476; | |||
| ctx->bytes[0] = 0; | |||
| ctx->bytes[1] = 0; | |||
| ctx->internal_buf = 0; | |||
| ctx->capacity = 0; | |||
| } | |||
| /* | |||
| * Final wrapup - pad to 64-byte boundary with the bit pattern | |||
| * 1 0* (64-bit count of bits processed, MSB-first) | |||
| */ | |||
| void FLAC__MD5Final(FLAC__byte digest[16], FLAC__MD5Context *ctx) | |||
| { | |||
| int count = ctx->bytes[0] & 0x3f; /* Number of bytes in ctx->in */ | |||
| FLAC__byte *p = (FLAC__byte *)ctx->in + count; | |||
| /* Set the first char of padding to 0x80. There is always room. */ | |||
| *p++ = 0x80; | |||
| /* Bytes of padding needed to make 56 bytes (-8..55) */ | |||
| count = 56 - 1 - count; | |||
| if (count < 0) { /* Padding forces an extra block */ | |||
| memset(p, 0, count + 8); | |||
| byteSwapX16(ctx->in); | |||
| FLAC__MD5Transform(ctx->buf, ctx->in); | |||
| p = (FLAC__byte *)ctx->in; | |||
| count = 56; | |||
| } | |||
| memset(p, 0, count); | |||
| byteSwap(ctx->in, 14); | |||
| /* Append length in bits and transform */ | |||
| ctx->in[14] = ctx->bytes[0] << 3; | |||
| ctx->in[15] = ctx->bytes[1] << 3 | ctx->bytes[0] >> 29; | |||
| FLAC__MD5Transform(ctx->buf, ctx->in); | |||
| byteSwap(ctx->buf, 4); | |||
| memcpy(digest, ctx->buf, 16); | |||
| //memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ | |||
| if(0 != ctx->internal_buf) { | |||
| free(ctx->internal_buf); | |||
| ctx->internal_buf = 0; | |||
| ctx->capacity = 0; | |||
| } | |||
| } | |||
| /* | |||
| * Convert the incoming audio signal to a byte stream | |||
| */ | |||
| static void format_input_(FLAC__byte *buf, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) | |||
| { | |||
| unsigned channel, sample; | |||
| register FLAC__int32 a_word; | |||
| register FLAC__byte *buf_ = buf; | |||
| #if WORDS_BIGENDIAN | |||
| #else | |||
| if(channels == 2 && bytes_per_sample == 2) { | |||
| FLAC__int16 *buf1_ = ((FLAC__int16*)buf_) + 1; | |||
| memcpy(buf_, signal[0], sizeof(FLAC__int32) * samples); | |||
| for(sample = 0; sample < samples; sample++, buf1_+=2) | |||
| *buf1_ = (FLAC__int16)signal[1][sample]; | |||
| } | |||
| else if(channels == 1 && bytes_per_sample == 2) { | |||
| FLAC__int16 *buf1_ = (FLAC__int16*)buf_; | |||
| for(sample = 0; sample < samples; sample++) | |||
| *buf1_++ = (FLAC__int16)signal[0][sample]; | |||
| } | |||
| else | |||
| #endif | |||
| if(bytes_per_sample == 2) { | |||
| if(channels == 2) { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| a_word = signal[0][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| a_word = signal[1][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| else if(channels == 1) { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| a_word = signal[0][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| else { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| for(channel = 0; channel < channels; channel++) { | |||
| a_word = signal[channel][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else if(bytes_per_sample == 3) { | |||
| if(channels == 2) { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| a_word = signal[0][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| a_word = signal[1][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| else if(channels == 1) { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| a_word = signal[0][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| else { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| for(channel = 0; channel < channels; channel++) { | |||
| a_word = signal[channel][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else if(bytes_per_sample == 1) { | |||
| if(channels == 2) { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| a_word = signal[0][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| a_word = signal[1][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| else if(channels == 1) { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| a_word = signal[0][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| else { | |||
| for(sample = 0; sample < samples; sample++) { | |||
| for(channel = 0; channel < channels; channel++) { | |||
| a_word = signal[channel][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else { /* bytes_per_sample == 4, maybe optimize more later */ | |||
| for(sample = 0; sample < samples; sample++) { | |||
| for(channel = 0; channel < channels; channel++) { | |||
| a_word = signal[channel][sample]; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; a_word >>= 8; | |||
| *buf_++ = (FLAC__byte)a_word; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /* | |||
| * Convert the incoming audio signal to a byte stream and FLAC__MD5Update it. | |||
| */ | |||
| FLAC__bool FLAC__MD5Accumulate(FLAC__MD5Context *ctx, const FLAC__int32 * const signal[], unsigned channels, unsigned samples, unsigned bytes_per_sample) | |||
| { | |||
| const size_t bytes_needed = (size_t)channels * (size_t)samples * (size_t)bytes_per_sample; | |||
| /* overflow check */ | |||
| if((size_t)channels > SIZE_MAX / (size_t)bytes_per_sample) | |||
| return false; | |||
| if((size_t)channels * (size_t)bytes_per_sample > SIZE_MAX / (size_t)samples) | |||
| return false; | |||
| if(ctx->capacity < bytes_needed) { | |||
| FLAC__byte *tmp = (FLAC__byte*)realloc(ctx->internal_buf, bytes_needed); | |||
| if(0 == tmp) { | |||
| free(ctx->internal_buf); | |||
| if(0 == (ctx->internal_buf = (FLAC__byte*)safe_malloc_(bytes_needed))) | |||
| return false; | |||
| } | |||
| ctx->internal_buf = tmp; | |||
| ctx->capacity = bytes_needed; | |||
| } | |||
| format_input_(ctx->internal_buf, signal, channels, samples, bytes_per_sample); | |||
| FLAC__MD5Update(ctx, ctx->internal_buf, bytes_needed); | |||
| return true; | |||
| } | |||
| @@ -0,0 +1,206 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include "include/private/memory.h" | |||
| #include "../assert.h" | |||
| #include "../alloc.h" | |||
| void *FLAC__memory_alloc_aligned(size_t bytes, void **aligned_address) | |||
| { | |||
| void *x; | |||
| FLAC__ASSERT(0 != aligned_address); | |||
| #ifdef FLAC__ALIGN_MALLOC_DATA | |||
| /* align on 32-byte (256-bit) boundary */ | |||
| x = safe_malloc_add_2op_(bytes, /*+*/31); | |||
| #ifdef SIZEOF_VOIDP | |||
| #if SIZEOF_VOIDP == 4 | |||
| /* could do *aligned_address = x + ((unsigned) (32 - (((unsigned)x) & 31))) & 31; */ | |||
| *aligned_address = (void*)(((unsigned)x + 31) & -32); | |||
| #elif SIZEOF_VOIDP == 8 | |||
| *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); | |||
| #else | |||
| # error Unsupported sizeof(void*) | |||
| #endif | |||
| #else | |||
| /* there's got to be a better way to do this right for all archs */ | |||
| if(sizeof(void*) == sizeof(unsigned)) | |||
| *aligned_address = (void*)(((unsigned)x + 31) & -32); | |||
| else if(sizeof(void*) == sizeof(FLAC__uint64)) | |||
| *aligned_address = (void*)(((FLAC__uint64)x + 31) & (FLAC__uint64)(-((FLAC__int64)32))); | |||
| else | |||
| return 0; | |||
| #endif | |||
| #else | |||
| x = safe_malloc_(bytes); | |||
| *aligned_address = x; | |||
| #endif | |||
| return x; | |||
| } | |||
| FLAC__bool FLAC__memory_alloc_aligned_int32_array(unsigned elements, FLAC__int32 **unaligned_pointer, FLAC__int32 **aligned_pointer) | |||
| { | |||
| FLAC__int32 *pu; /* unaligned pointer */ | |||
| union { /* union needed to comply with C99 pointer aliasing rules */ | |||
| FLAC__int32 *pa; /* aligned pointer */ | |||
| void *pv; /* aligned pointer alias */ | |||
| } u; | |||
| FLAC__ASSERT(elements > 0); | |||
| FLAC__ASSERT(0 != unaligned_pointer); | |||
| FLAC__ASSERT(0 != aligned_pointer); | |||
| FLAC__ASSERT(unaligned_pointer != aligned_pointer); | |||
| pu = (FLAC__int32*)FLAC__memory_alloc_aligned(sizeof(*pu) * (size_t)elements, &u.pv); | |||
| if(0 == pu) { | |||
| return false; | |||
| } | |||
| else { | |||
| if(*unaligned_pointer != 0) | |||
| free(*unaligned_pointer); | |||
| *unaligned_pointer = pu; | |||
| *aligned_pointer = u.pa; | |||
| return true; | |||
| } | |||
| } | |||
| FLAC__bool FLAC__memory_alloc_aligned_uint32_array(unsigned elements, FLAC__uint32 **unaligned_pointer, FLAC__uint32 **aligned_pointer) | |||
| { | |||
| FLAC__uint32 *pu; /* unaligned pointer */ | |||
| union { /* union needed to comply with C99 pointer aliasing rules */ | |||
| FLAC__uint32 *pa; /* aligned pointer */ | |||
| void *pv; /* aligned pointer alias */ | |||
| } u; | |||
| FLAC__ASSERT(elements > 0); | |||
| FLAC__ASSERT(0 != unaligned_pointer); | |||
| FLAC__ASSERT(0 != aligned_pointer); | |||
| FLAC__ASSERT(unaligned_pointer != aligned_pointer); | |||
| pu = (FLAC__uint32*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); | |||
| if(0 == pu) { | |||
| return false; | |||
| } | |||
| else { | |||
| if(*unaligned_pointer != 0) | |||
| free(*unaligned_pointer); | |||
| *unaligned_pointer = pu; | |||
| *aligned_pointer = u.pa; | |||
| return true; | |||
| } | |||
| } | |||
| FLAC__bool FLAC__memory_alloc_aligned_uint64_array(unsigned elements, FLAC__uint64 **unaligned_pointer, FLAC__uint64 **aligned_pointer) | |||
| { | |||
| FLAC__uint64 *pu; /* unaligned pointer */ | |||
| union { /* union needed to comply with C99 pointer aliasing rules */ | |||
| FLAC__uint64 *pa; /* aligned pointer */ | |||
| void *pv; /* aligned pointer alias */ | |||
| } u; | |||
| FLAC__ASSERT(elements > 0); | |||
| FLAC__ASSERT(0 != unaligned_pointer); | |||
| FLAC__ASSERT(0 != aligned_pointer); | |||
| FLAC__ASSERT(unaligned_pointer != aligned_pointer); | |||
| pu = (FLAC__uint64*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); | |||
| if(0 == pu) { | |||
| return false; | |||
| } | |||
| else { | |||
| if(*unaligned_pointer != 0) | |||
| free(*unaligned_pointer); | |||
| *unaligned_pointer = pu; | |||
| *aligned_pointer = u.pa; | |||
| return true; | |||
| } | |||
| } | |||
| FLAC__bool FLAC__memory_alloc_aligned_unsigned_array(unsigned elements, unsigned **unaligned_pointer, unsigned **aligned_pointer) | |||
| { | |||
| unsigned *pu; /* unaligned pointer */ | |||
| union { /* union needed to comply with C99 pointer aliasing rules */ | |||
| unsigned *pa; /* aligned pointer */ | |||
| void *pv; /* aligned pointer alias */ | |||
| } u; | |||
| FLAC__ASSERT(elements > 0); | |||
| FLAC__ASSERT(0 != unaligned_pointer); | |||
| FLAC__ASSERT(0 != aligned_pointer); | |||
| FLAC__ASSERT(unaligned_pointer != aligned_pointer); | |||
| pu = (unsigned*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); | |||
| if(0 == pu) { | |||
| return false; | |||
| } | |||
| else { | |||
| if(*unaligned_pointer != 0) | |||
| free(*unaligned_pointer); | |||
| *unaligned_pointer = pu; | |||
| *aligned_pointer = u.pa; | |||
| return true; | |||
| } | |||
| } | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| FLAC__bool FLAC__memory_alloc_aligned_real_array(unsigned elements, FLAC__real **unaligned_pointer, FLAC__real **aligned_pointer) | |||
| { | |||
| FLAC__real *pu; /* unaligned pointer */ | |||
| union { /* union needed to comply with C99 pointer aliasing rules */ | |||
| FLAC__real *pa; /* aligned pointer */ | |||
| void *pv; /* aligned pointer alias */ | |||
| } u; | |||
| FLAC__ASSERT(elements > 0); | |||
| FLAC__ASSERT(0 != unaligned_pointer); | |||
| FLAC__ASSERT(0 != aligned_pointer); | |||
| FLAC__ASSERT(unaligned_pointer != aligned_pointer); | |||
| pu = (FLAC__real*)FLAC__memory_alloc_aligned(sizeof(*pu) * elements, &u.pv); | |||
| if(0 == pu) { | |||
| return false; | |||
| } | |||
| else { | |||
| if(*unaligned_pointer != 0) | |||
| free(*unaligned_pointer); | |||
| *unaligned_pointer = pu; | |||
| *aligned_pointer = u.pa; | |||
| return true; | |||
| } | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,553 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include <stdio.h> | |||
| #include <string.h> /* for strlen() */ | |||
| #include "include/private/stream_encoder_framing.h" | |||
| #include "include/private/crc.h" | |||
| #include "../assert.h" | |||
| #ifdef max | |||
| #undef max | |||
| #endif | |||
| #define max(x,y) ((x)>(y)?(x):(y)) | |||
| static FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method); | |||
| static FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended); | |||
| FLAC__bool FLAC__add_metadata_block(const FLAC__StreamMetadata *metadata, FLAC__BitWriter *bw) | |||
| { | |||
| unsigned i, j; | |||
| const unsigned vendor_string_length = (unsigned)strlen(FLAC__VENDOR_STRING); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->is_last, FLAC__STREAM_METADATA_IS_LAST_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->type, FLAC__STREAM_METADATA_TYPE_LEN)) | |||
| return false; | |||
| /* | |||
| * First, for VORBIS_COMMENTs, adjust the length to reflect our vendor string | |||
| */ | |||
| i = metadata->length; | |||
| if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) { | |||
| FLAC__ASSERT(metadata->data.vorbis_comment.vendor_string.length == 0 || 0 != metadata->data.vorbis_comment.vendor_string.entry); | |||
| i -= metadata->data.vorbis_comment.vendor_string.length; | |||
| i += vendor_string_length; | |||
| } | |||
| FLAC__ASSERT(i < (1u << FLAC__STREAM_METADATA_LENGTH_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, i, FLAC__STREAM_METADATA_LENGTH_LEN)) | |||
| return false; | |||
| switch(metadata->type) { | |||
| case FLAC__METADATA_TYPE_STREAMINFO: | |||
| FLAC__ASSERT(metadata->data.stream_info.min_blocksize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.min_blocksize, FLAC__STREAM_METADATA_STREAMINFO_MIN_BLOCK_SIZE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(metadata->data.stream_info.max_blocksize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.max_blocksize, FLAC__STREAM_METADATA_STREAMINFO_MAX_BLOCK_SIZE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(metadata->data.stream_info.min_framesize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.min_framesize, FLAC__STREAM_METADATA_STREAMINFO_MIN_FRAME_SIZE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(metadata->data.stream_info.max_framesize < (1u << FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.max_framesize, FLAC__STREAM_METADATA_STREAMINFO_MAX_FRAME_SIZE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(FLAC__format_sample_rate_is_valid(metadata->data.stream_info.sample_rate)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.sample_rate, FLAC__STREAM_METADATA_STREAMINFO_SAMPLE_RATE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(metadata->data.stream_info.channels > 0); | |||
| FLAC__ASSERT(metadata->data.stream_info.channels <= (1u << FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.channels-1, FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(metadata->data.stream_info.bits_per_sample > 0); | |||
| FLAC__ASSERT(metadata->data.stream_info.bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.stream_info.bits_per_sample-1, FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.stream_info.total_samples, FLAC__STREAM_METADATA_STREAMINFO_TOTAL_SAMPLES_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.stream_info.md5sum, 16)) | |||
| return false; | |||
| break; | |||
| case FLAC__METADATA_TYPE_PADDING: | |||
| if(!FLAC__bitwriter_write_zeroes(bw, metadata->length * 8)) | |||
| return false; | |||
| break; | |||
| case FLAC__METADATA_TYPE_APPLICATION: | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.application.id, FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.application.data, metadata->length - (FLAC__STREAM_METADATA_APPLICATION_ID_LEN / 8))) | |||
| return false; | |||
| break; | |||
| case FLAC__METADATA_TYPE_SEEKTABLE: | |||
| for(i = 0; i < metadata->data.seek_table.num_points; i++) { | |||
| if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.seek_table.points[i].sample_number, FLAC__STREAM_METADATA_SEEKPOINT_SAMPLE_NUMBER_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.seek_table.points[i].stream_offset, FLAC__STREAM_METADATA_SEEKPOINT_STREAM_OFFSET_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.seek_table.points[i].frame_samples, FLAC__STREAM_METADATA_SEEKPOINT_FRAME_SAMPLES_LEN)) | |||
| return false; | |||
| } | |||
| break; | |||
| case FLAC__METADATA_TYPE_VORBIS_COMMENT: | |||
| if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, vendor_string_length)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)FLAC__VENDOR_STRING, vendor_string_length)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.num_comments)) | |||
| return false; | |||
| for(i = 0; i < metadata->data.vorbis_comment.num_comments; i++) { | |||
| if(!FLAC__bitwriter_write_raw_uint32_little_endian(bw, metadata->data.vorbis_comment.comments[i].length)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.vorbis_comment.comments[i].entry, metadata->data.vorbis_comment.comments[i].length)) | |||
| return false; | |||
| } | |||
| break; | |||
| case FLAC__METADATA_TYPE_CUESHEET: | |||
| FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN % 8 == 0); | |||
| if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)metadata->data.cue_sheet.media_catalog_number, FLAC__STREAM_METADATA_CUESHEET_MEDIA_CATALOG_NUMBER_LEN/8)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint64(bw, metadata->data.cue_sheet.lead_in, FLAC__STREAM_METADATA_CUESHEET_LEAD_IN_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.cue_sheet.is_cd? 1 : 0, FLAC__STREAM_METADATA_CUESHEET_IS_CD_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_RESERVED_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.cue_sheet.num_tracks, FLAC__STREAM_METADATA_CUESHEET_NUM_TRACKS_LEN)) | |||
| return false; | |||
| for(i = 0; i < metadata->data.cue_sheet.num_tracks; i++) { | |||
| const FLAC__StreamMetadata_CueSheet_Track *track = metadata->data.cue_sheet.tracks + i; | |||
| if(!FLAC__bitwriter_write_raw_uint64(bw, track->offset, FLAC__STREAM_METADATA_CUESHEET_TRACK_OFFSET_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, track->number, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUMBER_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN % 8 == 0); | |||
| if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)track->isrc, FLAC__STREAM_METADATA_CUESHEET_TRACK_ISRC_LEN/8)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, track->type, FLAC__STREAM_METADATA_CUESHEET_TRACK_TYPE_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, track->pre_emphasis, FLAC__STREAM_METADATA_CUESHEET_TRACK_PRE_EMPHASIS_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_TRACK_RESERVED_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, track->num_indices, FLAC__STREAM_METADATA_CUESHEET_TRACK_NUM_INDICES_LEN)) | |||
| return false; | |||
| for(j = 0; j < track->num_indices; j++) { | |||
| const FLAC__StreamMetadata_CueSheet_Index *index = track->indices + j; | |||
| if(!FLAC__bitwriter_write_raw_uint64(bw, index->offset, FLAC__STREAM_METADATA_CUESHEET_INDEX_OFFSET_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, index->number, FLAC__STREAM_METADATA_CUESHEET_INDEX_NUMBER_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_zeroes(bw, FLAC__STREAM_METADATA_CUESHEET_INDEX_RESERVED_LEN)) | |||
| return false; | |||
| } | |||
| } | |||
| break; | |||
| case FLAC__METADATA_TYPE_PICTURE: | |||
| { | |||
| size_t len; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.type, FLAC__STREAM_METADATA_PICTURE_TYPE_LEN)) | |||
| return false; | |||
| len = strlen(metadata->data.picture.mime_type); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, len, FLAC__STREAM_METADATA_PICTURE_MIME_TYPE_LENGTH_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, (const FLAC__byte*)metadata->data.picture.mime_type, len)) | |||
| return false; | |||
| len = strlen((const char *)metadata->data.picture.description); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, len, FLAC__STREAM_METADATA_PICTURE_DESCRIPTION_LENGTH_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.picture.description, len)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.width, FLAC__STREAM_METADATA_PICTURE_WIDTH_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.height, FLAC__STREAM_METADATA_PICTURE_HEIGHT_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.depth, FLAC__STREAM_METADATA_PICTURE_DEPTH_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.colors, FLAC__STREAM_METADATA_PICTURE_COLORS_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, metadata->data.picture.data_length, FLAC__STREAM_METADATA_PICTURE_DATA_LENGTH_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.picture.data, metadata->data.picture.data_length)) | |||
| return false; | |||
| } | |||
| break; | |||
| default: | |||
| if(!FLAC__bitwriter_write_byte_block(bw, metadata->data.unknown.data, metadata->length)) | |||
| return false; | |||
| break; | |||
| } | |||
| FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__frame_add_header(const FLAC__FrameHeader *header, FLAC__BitWriter *bw) | |||
| { | |||
| unsigned u, blocksize_hint, sample_rate_hint; | |||
| FLAC__byte crc; | |||
| FLAC__ASSERT(FLAC__bitwriter_is_byte_aligned(bw)); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__FRAME_HEADER_SYNC, FLAC__FRAME_HEADER_SYNC_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_RESERVED_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, (header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER)? 0 : 1, FLAC__FRAME_HEADER_BLOCKING_STRATEGY_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(header->blocksize > 0 && header->blocksize <= FLAC__MAX_BLOCK_SIZE); | |||
| /* when this assertion holds true, any legal blocksize can be expressed in the frame header */ | |||
| FLAC__ASSERT(FLAC__MAX_BLOCK_SIZE <= 65535u); | |||
| blocksize_hint = 0; | |||
| switch(header->blocksize) { | |||
| case 192: u = 1; break; | |||
| case 576: u = 2; break; | |||
| case 1152: u = 3; break; | |||
| case 2304: u = 4; break; | |||
| case 4608: u = 5; break; | |||
| case 256: u = 8; break; | |||
| case 512: u = 9; break; | |||
| case 1024: u = 10; break; | |||
| case 2048: u = 11; break; | |||
| case 4096: u = 12; break; | |||
| case 8192: u = 13; break; | |||
| case 16384: u = 14; break; | |||
| case 32768: u = 15; break; | |||
| default: | |||
| if(header->blocksize <= 0x100) | |||
| blocksize_hint = u = 6; | |||
| else | |||
| blocksize_hint = u = 7; | |||
| break; | |||
| } | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BLOCK_SIZE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(FLAC__format_sample_rate_is_valid(header->sample_rate)); | |||
| sample_rate_hint = 0; | |||
| switch(header->sample_rate) { | |||
| case 88200: u = 1; break; | |||
| case 176400: u = 2; break; | |||
| case 192000: u = 3; break; | |||
| case 8000: u = 4; break; | |||
| case 16000: u = 5; break; | |||
| case 22050: u = 6; break; | |||
| case 24000: u = 7; break; | |||
| case 32000: u = 8; break; | |||
| case 44100: u = 9; break; | |||
| case 48000: u = 10; break; | |||
| case 96000: u = 11; break; | |||
| default: | |||
| if(header->sample_rate <= 255000 && header->sample_rate % 1000 == 0) | |||
| sample_rate_hint = u = 12; | |||
| else if(header->sample_rate % 10 == 0) | |||
| sample_rate_hint = u = 14; | |||
| else if(header->sample_rate <= 0xffff) | |||
| sample_rate_hint = u = 13; | |||
| else | |||
| u = 0; | |||
| break; | |||
| } | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_SAMPLE_RATE_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(header->channels > 0 && header->channels <= (1u << FLAC__STREAM_METADATA_STREAMINFO_CHANNELS_LEN) && header->channels <= FLAC__MAX_CHANNELS); | |||
| switch(header->channel_assignment) { | |||
| case FLAC__CHANNEL_ASSIGNMENT_INDEPENDENT: | |||
| u = header->channels - 1; | |||
| break; | |||
| case FLAC__CHANNEL_ASSIGNMENT_LEFT_SIDE: | |||
| FLAC__ASSERT(header->channels == 2); | |||
| u = 8; | |||
| break; | |||
| case FLAC__CHANNEL_ASSIGNMENT_RIGHT_SIDE: | |||
| FLAC__ASSERT(header->channels == 2); | |||
| u = 9; | |||
| break; | |||
| case FLAC__CHANNEL_ASSIGNMENT_MID_SIDE: | |||
| FLAC__ASSERT(header->channels == 2); | |||
| u = 10; | |||
| break; | |||
| default: | |||
| FLAC__ASSERT(0); | |||
| } | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_CHANNEL_ASSIGNMENT_LEN)) | |||
| return false; | |||
| FLAC__ASSERT(header->bits_per_sample > 0 && header->bits_per_sample <= (1u << FLAC__STREAM_METADATA_STREAMINFO_BITS_PER_SAMPLE_LEN)); | |||
| switch(header->bits_per_sample) { | |||
| case 8 : u = 1; break; | |||
| case 12: u = 2; break; | |||
| case 16: u = 4; break; | |||
| case 20: u = 5; break; | |||
| case 24: u = 6; break; | |||
| default: u = 0; break; | |||
| } | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, u, FLAC__FRAME_HEADER_BITS_PER_SAMPLE_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, 0, FLAC__FRAME_HEADER_ZERO_PAD_LEN)) | |||
| return false; | |||
| if(header->number_type == FLAC__FRAME_NUMBER_TYPE_FRAME_NUMBER) { | |||
| if(!FLAC__bitwriter_write_utf8_uint32(bw, header->number.frame_number)) | |||
| return false; | |||
| } | |||
| else { | |||
| if(!FLAC__bitwriter_write_utf8_uint64(bw, header->number.sample_number)) | |||
| return false; | |||
| } | |||
| if(blocksize_hint) | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, header->blocksize-1, (blocksize_hint==6)? 8:16)) | |||
| return false; | |||
| switch(sample_rate_hint) { | |||
| case 12: | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate / 1000, 8)) | |||
| return false; | |||
| break; | |||
| case 13: | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate, 16)) | |||
| return false; | |||
| break; | |||
| case 14: | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, header->sample_rate / 10, 16)) | |||
| return false; | |||
| break; | |||
| } | |||
| /* write the CRC */ | |||
| if(!FLAC__bitwriter_get_write_crc8(bw, &crc)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, crc, FLAC__FRAME_HEADER_CRC_LEN)) | |||
| return false; | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__subframe_add_constant(const FLAC__Subframe_Constant *subframe, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) | |||
| { | |||
| FLAC__bool ok; | |||
| ok = | |||
| FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_CONSTANT_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN) && | |||
| (wasted_bits? FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1) : true) && | |||
| FLAC__bitwriter_write_raw_int32(bw, subframe->value, subframe_bps) | |||
| ; | |||
| return ok; | |||
| } | |||
| FLAC__bool FLAC__subframe_add_fixed(const FLAC__Subframe_Fixed *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) | |||
| { | |||
| unsigned i; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_FIXED_BYTE_ALIGNED_MASK | (subframe->order<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) | |||
| return false; | |||
| if(wasted_bits) | |||
| if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) | |||
| return false; | |||
| for(i = 0; i < subframe->order; i++) | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) | |||
| return false; | |||
| if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) | |||
| return false; | |||
| switch(subframe->entropy_coding_method.type) { | |||
| case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: | |||
| case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: | |||
| if(!add_residual_partitioned_rice_( | |||
| bw, | |||
| subframe->residual, | |||
| residual_samples, | |||
| subframe->order, | |||
| subframe->entropy_coding_method.data.partitioned_rice.contents->parameters, | |||
| subframe->entropy_coding_method.data.partitioned_rice.contents->raw_bits, | |||
| subframe->entropy_coding_method.data.partitioned_rice.order, | |||
| /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 | |||
| )) | |||
| return false; | |||
| break; | |||
| default: | |||
| FLAC__ASSERT(0); | |||
| } | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__subframe_add_lpc(const FLAC__Subframe_LPC *subframe, unsigned residual_samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) | |||
| { | |||
| unsigned i; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_LPC_BYTE_ALIGNED_MASK | ((subframe->order-1)<<1) | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) | |||
| return false; | |||
| if(wasted_bits) | |||
| if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) | |||
| return false; | |||
| for(i = 0; i < subframe->order; i++) | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, subframe->warmup[i], subframe_bps)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, subframe->qlp_coeff_precision-1, FLAC__SUBFRAME_LPC_QLP_COEFF_PRECISION_LEN)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, subframe->quantization_level, FLAC__SUBFRAME_LPC_QLP_SHIFT_LEN)) | |||
| return false; | |||
| for(i = 0; i < subframe->order; i++) | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, subframe->qlp_coeff[i], subframe->qlp_coeff_precision)) | |||
| return false; | |||
| if(!add_entropy_coding_method_(bw, &subframe->entropy_coding_method)) | |||
| return false; | |||
| switch(subframe->entropy_coding_method.type) { | |||
| case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: | |||
| case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: | |||
| if(!add_residual_partitioned_rice_( | |||
| bw, | |||
| subframe->residual, | |||
| residual_samples, | |||
| subframe->order, | |||
| subframe->entropy_coding_method.data.partitioned_rice.contents->parameters, | |||
| subframe->entropy_coding_method.data.partitioned_rice.contents->raw_bits, | |||
| subframe->entropy_coding_method.data.partitioned_rice.order, | |||
| /*is_extended=*/subframe->entropy_coding_method.type == FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2 | |||
| )) | |||
| return false; | |||
| break; | |||
| default: | |||
| FLAC__ASSERT(0); | |||
| } | |||
| return true; | |||
| } | |||
| FLAC__bool FLAC__subframe_add_verbatim(const FLAC__Subframe_Verbatim *subframe, unsigned samples, unsigned subframe_bps, unsigned wasted_bits, FLAC__BitWriter *bw) | |||
| { | |||
| unsigned i; | |||
| const FLAC__int32 *signal = subframe->data; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, FLAC__SUBFRAME_TYPE_VERBATIM_BYTE_ALIGNED_MASK | (wasted_bits? 1:0), FLAC__SUBFRAME_ZERO_PAD_LEN + FLAC__SUBFRAME_TYPE_LEN + FLAC__SUBFRAME_WASTED_BITS_FLAG_LEN)) | |||
| return false; | |||
| if(wasted_bits) | |||
| if(!FLAC__bitwriter_write_unary_unsigned(bw, wasted_bits-1)) | |||
| return false; | |||
| for(i = 0; i < samples; i++) | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, signal[i], subframe_bps)) | |||
| return false; | |||
| return true; | |||
| } | |||
| FLAC__bool add_entropy_coding_method_(FLAC__BitWriter *bw, const FLAC__EntropyCodingMethod *method) | |||
| { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, method->type, FLAC__ENTROPY_CODING_METHOD_TYPE_LEN)) | |||
| return false; | |||
| switch(method->type) { | |||
| case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE: | |||
| case FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2: | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, method->data.partitioned_rice.order, FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ORDER_LEN)) | |||
| return false; | |||
| break; | |||
| default: | |||
| FLAC__ASSERT(0); | |||
| } | |||
| return true; | |||
| } | |||
| FLAC__bool add_residual_partitioned_rice_(FLAC__BitWriter *bw, const FLAC__int32 residual[], const unsigned residual_samples, const unsigned predictor_order, const unsigned rice_parameters[], const unsigned raw_bits[], const unsigned partition_order, const FLAC__bool is_extended) | |||
| { | |||
| const unsigned plen = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_PARAMETER_LEN : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_PARAMETER_LEN; | |||
| const unsigned pesc = is_extended? FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE2_ESCAPE_PARAMETER : FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_ESCAPE_PARAMETER; | |||
| if(partition_order == 0) { | |||
| unsigned i; | |||
| if(raw_bits[0] == 0) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, rice_parameters[0], plen)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_rice_signed_block(bw, residual, residual_samples, rice_parameters[0])) | |||
| return false; | |||
| } | |||
| else { | |||
| FLAC__ASSERT(rice_parameters[0] == 0); | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, pesc, plen)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, raw_bits[0], FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) | |||
| return false; | |||
| for(i = 0; i < residual_samples; i++) { | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, residual[i], raw_bits[0])) | |||
| return false; | |||
| } | |||
| } | |||
| return true; | |||
| } | |||
| else { | |||
| unsigned i, j, k = 0, k_last = 0; | |||
| unsigned partition_samples; | |||
| const unsigned default_partition_samples = (residual_samples+predictor_order) >> partition_order; | |||
| for(i = 0; i < (1u<<partition_order); i++) { | |||
| partition_samples = default_partition_samples; | |||
| if(i == 0) | |||
| partition_samples -= predictor_order; | |||
| k += partition_samples; | |||
| if(raw_bits[i] == 0) { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, rice_parameters[i], plen)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_rice_signed_block(bw, residual+k_last, k-k_last, rice_parameters[i])) | |||
| return false; | |||
| } | |||
| else { | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, pesc, plen)) | |||
| return false; | |||
| if(!FLAC__bitwriter_write_raw_uint32(bw, raw_bits[i], FLAC__ENTROPY_CODING_METHOD_PARTITIONED_RICE_RAW_LEN)) | |||
| return false; | |||
| for(j = k_last; j < k; j++) { | |||
| if(!FLAC__bitwriter_write_raw_int32(bw, residual[j], raw_bits[i])) | |||
| return false; | |||
| } | |||
| } | |||
| k_last = k; | |||
| } | |||
| return true; | |||
| } | |||
| } | |||
| @@ -0,0 +1,225 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #if HAVE_CONFIG_H | |||
| # include <config.h> | |||
| #endif | |||
| #include <math.h> | |||
| #include "../assert.h" | |||
| #include "../format.h" | |||
| #include "include/private/window.h" | |||
| #ifndef FLAC__INTEGER_ONLY_LIBRARY | |||
| #ifndef M_PI | |||
| /* math.h in VC++ doesn't seem to have this (how Microsoft is that?) */ | |||
| #define M_PI 3.14159265358979323846 | |||
| #endif | |||
| void FLAC__window_bartlett(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| if (L & 1) { | |||
| for (n = 0; n <= N/2; n++) | |||
| window[n] = 2.0f * n / (float)N; | |||
| for (; n <= N; n++) | |||
| window[n] = 2.0f - 2.0f * n / (float)N; | |||
| } | |||
| else { | |||
| for (n = 0; n <= L/2-1; n++) | |||
| window[n] = 2.0f * n / (float)N; | |||
| for (; n <= N; n++) | |||
| window[n] = 2.0f - 2.0f * (N-n) / (float)N; | |||
| } | |||
| } | |||
| void FLAC__window_bartlett_hann(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(0.62f - 0.48f * fabs((float)n/(float)N+0.5f) + 0.38f * cos(2.0f * M_PI * ((float)n/(float)N+0.5f))); | |||
| } | |||
| void FLAC__window_blackman(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(0.42f - 0.5f * cos(2.0f * M_PI * n / N) + 0.08f * cos(4.0f * M_PI * n / N)); | |||
| } | |||
| /* 4-term -92dB side-lobe */ | |||
| void FLAC__window_blackman_harris_4term_92db_sidelobe(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n <= N; n++) | |||
| window[n] = (FLAC__real)(0.35875f - 0.48829f * cos(2.0f * M_PI * n / N) + 0.14128f * cos(4.0f * M_PI * n / N) - 0.01168f * cos(6.0f * M_PI * n / N)); | |||
| } | |||
| void FLAC__window_connes(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| const double N2 = (double)N / 2.; | |||
| FLAC__int32 n; | |||
| for (n = 0; n <= N; n++) { | |||
| double k = ((double)n - N2) / N2; | |||
| k = 1.0f - k * k; | |||
| window[n] = (FLAC__real)(k * k); | |||
| } | |||
| } | |||
| void FLAC__window_flattop(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(1.0f - 1.93f * cos(2.0f * M_PI * n / N) + 1.29f * cos(4.0f * M_PI * n / N) - 0.388f * cos(6.0f * M_PI * n / N) + 0.0322f * cos(8.0f * M_PI * n / N)); | |||
| } | |||
| void FLAC__window_gauss(FLAC__real *window, const FLAC__int32 L, const FLAC__real stddev) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| const double N2 = (double)N / 2.; | |||
| FLAC__int32 n; | |||
| for (n = 0; n <= N; n++) { | |||
| const double k = ((double)n - N2) / (stddev * N2); | |||
| window[n] = (FLAC__real)exp(-0.5f * k * k); | |||
| } | |||
| } | |||
| void FLAC__window_hamming(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(0.54f - 0.46f * cos(2.0f * M_PI * n / N)); | |||
| } | |||
| void FLAC__window_hann(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(0.5f - 0.5f * cos(2.0f * M_PI * n / N)); | |||
| } | |||
| void FLAC__window_kaiser_bessel(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(0.402f - 0.498f * cos(2.0f * M_PI * n / N) + 0.098f * cos(4.0f * M_PI * n / N) - 0.001f * cos(6.0f * M_PI * n / N)); | |||
| } | |||
| void FLAC__window_nuttall(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = (FLAC__real)(0.3635819f - 0.4891775f*cos(2.0f*M_PI*n/N) + 0.1365995f*cos(4.0f*M_PI*n/N) - 0.0106411f*cos(6.0f*M_PI*n/N)); | |||
| } | |||
| void FLAC__window_rectangle(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| FLAC__int32 n; | |||
| for (n = 0; n < L; n++) | |||
| window[n] = 1.0f; | |||
| } | |||
| void FLAC__window_triangle(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| FLAC__int32 n; | |||
| if (L & 1) { | |||
| for (n = 1; n <= L+1/2; n++) | |||
| window[n-1] = 2.0f * n / ((float)L + 1.0f); | |||
| for (; n <= L; n++) | |||
| window[n-1] = - (float)(2 * (L - n + 1)) / ((float)L + 1.0f); | |||
| } | |||
| else { | |||
| for (n = 1; n <= L/2; n++) | |||
| window[n-1] = 2.0f * n / (float)L; | |||
| for (; n <= L; n++) | |||
| window[n-1] = ((float)(2 * (L - n)) + 1.0f) / (float)L; | |||
| } | |||
| } | |||
| void FLAC__window_tukey(FLAC__real *window, const FLAC__int32 L, const FLAC__real p) | |||
| { | |||
| if (p <= 0.0) | |||
| FLAC__window_rectangle(window, L); | |||
| else if (p >= 1.0) | |||
| FLAC__window_hann(window, L); | |||
| else { | |||
| const FLAC__int32 Np = (FLAC__int32)(p / 2.0f * L) - 1; | |||
| FLAC__int32 n; | |||
| /* start with rectangle... */ | |||
| FLAC__window_rectangle(window, L); | |||
| /* ...replace ends with hann */ | |||
| if (Np > 0) { | |||
| for (n = 0; n <= Np; n++) { | |||
| window[n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * n / Np)); | |||
| window[L-Np-1+n] = (FLAC__real)(0.5f - 0.5f * cos(M_PI * (n+Np) / Np)); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| void FLAC__window_welch(FLAC__real *window, const FLAC__int32 L) | |||
| { | |||
| const FLAC__int32 N = L - 1; | |||
| const double N2 = (double)N / 2.; | |||
| FLAC__int32 n; | |||
| for (n = 0; n <= N; n++) { | |||
| const double k = ((double)n - N2) / N2; | |||
| window[n] = (FLAC__real)(1.0f - k * k); | |||
| } | |||
| } | |||
| #endif /* !defined FLAC__INTEGER_ONLY_LIBRARY */ | |||
| @@ -0,0 +1,80 @@ | |||
| /* libFLAC - Free Lossless Audio Codec library | |||
| * Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007 Josh Coalson | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| * modification, are permitted provided that the following conditions | |||
| * are met: | |||
| * | |||
| * - Redistributions of source code must retain the above copyright | |||
| * notice, this list of conditions and the following disclaimer. | |||
| * | |||
| * - Redistributions in binary form must reproduce the above copyright | |||
| * notice, this list of conditions and the following disclaimer in the | |||
| * documentation and/or other materials provided with the distribution. | |||
| * | |||
| * - Neither the name of the Xiph.org Foundation nor the names of its | |||
| * contributors may be used to endorse or promote products derived from | |||
| * this software without specific prior written permission. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||
| * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||
| * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||
| * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR | |||
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |||
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |||
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |||
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | |||
| * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | |||
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | |||
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| */ | |||
| #ifndef FLAC__ORDINALS_H | |||
| #define FLAC__ORDINALS_H | |||
| #if !(defined(_MSC_VER) || defined(__BORLANDC__) || defined(__EMX__)) | |||
| #include <inttypes.h> | |||
| #endif | |||
| typedef signed char FLAC__int8; | |||
| typedef unsigned char FLAC__uint8; | |||
| #if defined(_MSC_VER) || defined(__BORLANDC__) | |||
| typedef __int16 FLAC__int16; | |||
| typedef __int32 FLAC__int32; | |||
| typedef __int64 FLAC__int64; | |||
| typedef unsigned __int16 FLAC__uint16; | |||
| typedef unsigned __int32 FLAC__uint32; | |||
| typedef unsigned __int64 FLAC__uint64; | |||
| #elif defined(__EMX__) | |||
| typedef short FLAC__int16; | |||
| typedef long FLAC__int32; | |||
| typedef long long FLAC__int64; | |||
| typedef unsigned short FLAC__uint16; | |||
| typedef unsigned long FLAC__uint32; | |||
| typedef unsigned long long FLAC__uint64; | |||
| #else | |||
| typedef int16_t FLAC__int16; | |||
| typedef int32_t FLAC__int32; | |||
| typedef int64_t FLAC__int64; | |||
| typedef uint16_t FLAC__uint16; | |||
| typedef uint32_t FLAC__uint32; | |||
| typedef uint64_t FLAC__uint64; | |||
| #endif | |||
| typedef int FLAC__bool; | |||
| typedef FLAC__uint8 FLAC__byte; | |||
| #ifdef true | |||
| #undef true | |||
| #endif | |||
| #ifdef false | |||
| #undef false | |||
| #endif | |||
| #ifndef __cplusplus | |||
| #define true 1 | |||
| #define false 0 | |||
| #endif | |||
| #endif | |||
| @@ -0,0 +1,957 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| //============================================================================== | |||
| static const char* const aiffFormatName = "AIFF file"; | |||
| static const char* const aiffExtensions[] = { ".aiff", ".aif", 0 }; | |||
| //============================================================================== | |||
| const char* const AiffAudioFormat::appleOneShot = "apple one shot"; | |||
| const char* const AiffAudioFormat::appleRootSet = "apple root set"; | |||
| const char* const AiffAudioFormat::appleRootNote = "apple root note"; | |||
| const char* const AiffAudioFormat::appleBeats = "apple beats"; | |||
| const char* const AiffAudioFormat::appleDenominator = "apple denominator"; | |||
| const char* const AiffAudioFormat::appleNumerator = "apple numerator"; | |||
| const char* const AiffAudioFormat::appleTag = "apple tag"; | |||
| const char* const AiffAudioFormat::appleKey = "apple key"; | |||
| //============================================================================== | |||
| namespace AiffFileHelpers | |||
| { | |||
| inline int chunkName (const char* const name) { return (int) ByteOrder::littleEndianInt (name); } | |||
| #if JUCE_MSVC | |||
| #pragma pack (push, 1) | |||
| #endif | |||
| //============================================================================== | |||
| struct InstChunk | |||
| { | |||
| struct Loop | |||
| { | |||
| uint16 type; // these are different in AIFF and WAV | |||
| uint16 startIdentifier; | |||
| uint16 endIdentifier; | |||
| } JUCE_PACKED; | |||
| int8 baseNote; | |||
| int8 detune; | |||
| int8 lowNote; | |||
| int8 highNote; | |||
| int8 lowVelocity; | |||
| int8 highVelocity; | |||
| int16 gain; | |||
| Loop sustainLoop; | |||
| Loop releaseLoop; | |||
| void copyTo (StringPairArray& values) const | |||
| { | |||
| values.set ("MidiUnityNote", String (baseNote)); | |||
| values.set ("Detune", String (detune)); | |||
| values.set ("LowNote", String (lowNote)); | |||
| values.set ("HighNote", String (highNote)); | |||
| values.set ("LowVelocity", String (lowVelocity)); | |||
| values.set ("HighVelocity", String (highVelocity)); | |||
| values.set ("Gain", String ((int16) ByteOrder::swapIfLittleEndian ((uint16) gain))); | |||
| values.set ("NumSampleLoops", String (2)); // always 2 with AIFF, WAV can have more | |||
| values.set ("Loop0Type", String (ByteOrder::swapIfLittleEndian (sustainLoop.type))); | |||
| values.set ("Loop0StartIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.startIdentifier))); | |||
| values.set ("Loop0EndIdentifier", String (ByteOrder::swapIfLittleEndian (sustainLoop.endIdentifier))); | |||
| values.set ("Loop1Type", String (ByteOrder::swapIfLittleEndian (releaseLoop.type))); | |||
| values.set ("Loop1StartIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.startIdentifier))); | |||
| values.set ("Loop1EndIdentifier", String (ByteOrder::swapIfLittleEndian (releaseLoop.endIdentifier))); | |||
| } | |||
| static uint16 getValue16 (const StringPairArray& values, const char* name, const char* def) | |||
| { | |||
| return ByteOrder::swapIfLittleEndian ((uint16) values.getValue (name, def).getIntValue()); | |||
| } | |||
| static int8 getValue8 (const StringPairArray& values, const char* name, const char* def) | |||
| { | |||
| return (int8) values.getValue (name, def).getIntValue(); | |||
| } | |||
| static void create (MemoryBlock& block, const StringPairArray& values) | |||
| { | |||
| if (values.getAllKeys().contains ("MidiUnityNote", true)) | |||
| { | |||
| block.setSize ((sizeof (InstChunk) + 3) & ~(size_t) 3, true); | |||
| InstChunk& inst = *static_cast <InstChunk*> (block.getData()); | |||
| inst.baseNote = getValue8 (values, "MidiUnityNote", "60"); | |||
| inst.detune = getValue8 (values, "Detune", "0"); | |||
| inst.lowNote = getValue8 (values, "LowNote", "0"); | |||
| inst.highNote = getValue8 (values, "HighNote", "127"); | |||
| inst.lowVelocity = getValue8 (values, "LowVelocity", "1"); | |||
| inst.highVelocity = getValue8 (values, "HighVelocity", "127"); | |||
| inst.gain = (int16) getValue16 (values, "Gain", "0"); | |||
| inst.sustainLoop.type = getValue16 (values, "Loop0Type", "0"); | |||
| inst.sustainLoop.startIdentifier = getValue16 (values, "Loop0StartIdentifier", "0"); | |||
| inst.sustainLoop.endIdentifier = getValue16 (values, "Loop0EndIdentifier", "0"); | |||
| inst.releaseLoop.type = getValue16 (values, "Loop1Type", "0"); | |||
| inst.releaseLoop.startIdentifier = getValue16 (values, "Loop1StartIdentifier", "0"); | |||
| inst.releaseLoop.endIdentifier = getValue16 (values, "Loop1EndIdentifier", "0"); | |||
| } | |||
| } | |||
| } JUCE_PACKED; | |||
| //============================================================================== | |||
| struct BASCChunk | |||
| { | |||
| enum Key | |||
| { | |||
| minor = 1, | |||
| major = 2, | |||
| neither = 3, | |||
| both = 4 | |||
| }; | |||
| BASCChunk (InputStream& input) | |||
| { | |||
| zerostruct (*this); | |||
| flags = (uint32) input.readIntBigEndian(); | |||
| numBeats = (uint32) input.readIntBigEndian(); | |||
| rootNote = (uint16) input.readShortBigEndian(); | |||
| key = (uint16) input.readShortBigEndian(); | |||
| timeSigNum = (uint16) input.readShortBigEndian(); | |||
| timeSigDen = (uint16) input.readShortBigEndian(); | |||
| oneShot = (uint16) input.readShortBigEndian(); | |||
| input.read (unknown, sizeof (unknown)); | |||
| } | |||
| void addToMetadata (StringPairArray& metadata) const | |||
| { | |||
| const bool rootNoteSet = rootNote != 0; | |||
| setBoolFlag (metadata, AiffAudioFormat::appleOneShot, oneShot == 2); | |||
| setBoolFlag (metadata, AiffAudioFormat::appleRootSet, rootNoteSet); | |||
| if (rootNoteSet) | |||
| metadata.set (AiffAudioFormat::appleRootNote, String (rootNote)); | |||
| metadata.set (AiffAudioFormat::appleBeats, String (numBeats)); | |||
| metadata.set (AiffAudioFormat::appleDenominator, String (timeSigDen)); | |||
| metadata.set (AiffAudioFormat::appleNumerator, String (timeSigNum)); | |||
| const char* keyString = nullptr; | |||
| switch (key) | |||
| { | |||
| case minor: keyString = "major"; break; | |||
| case major: keyString = "major"; break; | |||
| case neither: keyString = "neither"; break; | |||
| case both: keyString = "both"; break; | |||
| } | |||
| if (keyString != nullptr) | |||
| metadata.set (AiffAudioFormat::appleKey, keyString); | |||
| } | |||
| void setBoolFlag (StringPairArray& values, const char* name, bool shouldBeSet) const | |||
| { | |||
| values.set (name, shouldBeSet ? "1" : "0"); | |||
| } | |||
| uint32 flags; | |||
| uint32 numBeats; | |||
| uint16 rootNote; | |||
| uint16 key; | |||
| uint16 timeSigNum; | |||
| uint16 timeSigDen; | |||
| uint16 oneShot; | |||
| uint8 unknown[66]; | |||
| } JUCE_PACKED; | |||
| #if JUCE_MSVC | |||
| #pragma pack (pop) | |||
| #endif | |||
| //============================================================================== | |||
| static String readCATEChunk (InputStream& input, const uint32 length) | |||
| { | |||
| MemoryBlock mb; | |||
| input.skipNextBytes (4); | |||
| input.readIntoMemoryBlock (mb, (ssize_t) length - 4); | |||
| static const char* appleGenres[] = | |||
| { | |||
| "Rock/Blues", | |||
| "Electronic/Dance", | |||
| "Jazz", | |||
| "Urban", | |||
| "World/Ethnic", | |||
| "Cinematic/New Age", | |||
| "Orchestral", | |||
| "Country/Folk", | |||
| "Experimental", | |||
| "Other Genre", | |||
| nullptr | |||
| }; | |||
| const StringArray genres (appleGenres); | |||
| StringArray tagsArray; | |||
| int bytesLeft = (int) mb.getSize(); | |||
| const char* data = static_cast <const char*> (mb.getData()); | |||
| while (bytesLeft > 0) | |||
| { | |||
| const String tag (CharPointer_UTF8 (data), | |||
| CharPointer_UTF8 (data + bytesLeft)); | |||
| if (tag.isNotEmpty()) | |||
| tagsArray.add (data); | |||
| const int numBytesInTag = genres.contains (tag) ? 118 : 50; | |||
| data += numBytesInTag; | |||
| bytesLeft -= numBytesInTag; | |||
| } | |||
| return tagsArray.joinIntoString (";"); | |||
| } | |||
| //============================================================================== | |||
| namespace MarkChunk | |||
| { | |||
| static bool metaDataContainsZeroIdentifiers (const StringPairArray& values) | |||
| { | |||
| // (zero cue identifiers are valid for WAV but not for AIFF) | |||
| const String cueString ("Cue"); | |||
| const String noteString ("CueNote"); | |||
| const String identifierString ("Identifier"); | |||
| const StringArray& keys = values.getAllKeys(); | |||
| for (int i = 0; i < keys.size(); ++i) | |||
| { | |||
| const String key (keys[i]); | |||
| if (key.startsWith (noteString)) | |||
| continue; // zero identifier IS valid in a COMT chunk | |||
| if (key.startsWith (cueString) && key.contains (identifierString)) | |||
| { | |||
| const int value = values.getValue (key, "-1").getIntValue(); | |||
| if (value == 0) | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| static void create (MemoryBlock& block, const StringPairArray& values) | |||
| { | |||
| const int numCues = values.getValue ("NumCuePoints", "0").getIntValue(); | |||
| if (numCues > 0) | |||
| { | |||
| MemoryOutputStream out (block, false); | |||
| out.writeShortBigEndian ((short) numCues); | |||
| const int numCueLabels = values.getValue ("NumCueLabels", "0").getIntValue(); | |||
| const int idOffset = metaDataContainsZeroIdentifiers (values) ? 1 : 0; // can't have zero IDs in AIFF | |||
| #if JUCE_DEBUG | |||
| Array<int> identifiers; | |||
| #endif | |||
| for (int i = 0; i < numCues; ++i) | |||
| { | |||
| const String prefixCue ("Cue" + String (i)); | |||
| const int identifier = idOffset + values.getValue (prefixCue + "Identifier", "1").getIntValue(); | |||
| #if JUCE_DEBUG | |||
| jassert (! identifiers.contains (identifier)); | |||
| identifiers.add (identifier); | |||
| #endif | |||
| const int offset = values.getValue (prefixCue + "Offset", "0").getIntValue(); | |||
| String label ("CueLabel" + String (i)); | |||
| for (int labelIndex = 0; labelIndex < numCueLabels; ++labelIndex) | |||
| { | |||
| const String prefixLabel ("CueLabel" + String (labelIndex)); | |||
| const int labelIdentifier = idOffset + values.getValue (prefixLabel + "Identifier", "1").getIntValue(); | |||
| if (labelIdentifier == identifier) | |||
| { | |||
| label = values.getValue (prefixLabel + "Text", label); | |||
| break; | |||
| } | |||
| } | |||
| out.writeShortBigEndian ((short) identifier); | |||
| out.writeIntBigEndian (offset); | |||
| const size_t labelLength = jmin ((size_t) 254, label.getNumBytesAsUTF8()); // seems to need null terminator even though it's a pstring | |||
| out.writeByte ((char) labelLength + 1); | |||
| out.write (label.toUTF8(), labelLength); | |||
| out.writeByte (0); | |||
| } | |||
| if ((out.getDataSize() & 1) != 0) | |||
| out.writeByte (0); | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| namespace COMTChunk | |||
| { | |||
| static void create (MemoryBlock& block, const StringPairArray& values) | |||
| { | |||
| const int numNotes = values.getValue ("NumCueNotes", "0").getIntValue(); | |||
| if (numNotes > 0) | |||
| { | |||
| MemoryOutputStream out (block, false); | |||
| out.writeShortBigEndian ((short) numNotes); | |||
| for (int i = 0; i < numNotes; ++i) | |||
| { | |||
| const String prefix ("CueNote" + String (i)); | |||
| out.writeIntBigEndian (values.getValue (prefix + "TimeStamp", "0").getIntValue()); | |||
| out.writeShortBigEndian ((short) values.getValue (prefix + "Identifier", "0").getIntValue()); | |||
| const String comment (values.getValue (prefix + "Text", String::empty)); | |||
| const size_t commentLength = jmin (comment.getNumBytesAsUTF8(), (size_t) 65534); | |||
| out.writeShortBigEndian ((short) commentLength + 1); | |||
| out.write (comment.toUTF8(), commentLength); | |||
| out.writeByte (0); | |||
| if ((out.getDataSize() & 1) != 0) | |||
| out.writeByte (0); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| //============================================================================== | |||
| class AiffAudioFormatReader : public AudioFormatReader | |||
| { | |||
| public: | |||
| AiffAudioFormatReader (InputStream* in) | |||
| : AudioFormatReader (in, TRANS (aiffFormatName)) | |||
| { | |||
| using namespace AiffFileHelpers; | |||
| if (input->readInt() == chunkName ("FORM")) | |||
| { | |||
| const int len = input->readIntBigEndian(); | |||
| const int64 end = input->getPosition() + len; | |||
| const int nextType = input->readInt(); | |||
| if (nextType == chunkName ("AIFF") || nextType == chunkName ("AIFC")) | |||
| { | |||
| bool hasGotVer = false; | |||
| bool hasGotData = false; | |||
| bool hasGotType = false; | |||
| while (input->getPosition() < end) | |||
| { | |||
| const int type = input->readInt(); | |||
| const uint32 length = (uint32) input->readIntBigEndian(); | |||
| const int64 chunkEnd = input->getPosition() + length; | |||
| if (type == chunkName ("FVER")) | |||
| { | |||
| hasGotVer = true; | |||
| const int ver = input->readIntBigEndian(); | |||
| if (ver != 0 && ver != (int) 0xa2805140) | |||
| break; | |||
| } | |||
| else if (type == chunkName ("COMM")) | |||
| { | |||
| hasGotType = true; | |||
| numChannels = (unsigned int) input->readShortBigEndian(); | |||
| lengthInSamples = input->readIntBigEndian(); | |||
| bitsPerSample = (unsigned int) input->readShortBigEndian(); | |||
| bytesPerFrame = (int) ((numChannels * bitsPerSample) >> 3); | |||
| unsigned char sampleRateBytes[10]; | |||
| input->read (sampleRateBytes, 10); | |||
| const int byte0 = sampleRateBytes[0]; | |||
| if ((byte0 & 0x80) != 0 | |||
| || byte0 <= 0x3F || byte0 > 0x40 | |||
| || (byte0 == 0x40 && sampleRateBytes[1] > 0x1C)) | |||
| break; | |||
| unsigned int sampRate = ByteOrder::bigEndianInt (sampleRateBytes + 2); | |||
| sampRate >>= (16414 - ByteOrder::bigEndianShort (sampleRateBytes)); | |||
| sampleRate = (int) sampRate; | |||
| if (length <= 18) | |||
| { | |||
| // some types don't have a chunk large enough to include a compression | |||
| // type, so assume it's just big-endian pcm | |||
| littleEndian = false; | |||
| } | |||
| else | |||
| { | |||
| const int compType = input->readInt(); | |||
| if (compType == chunkName ("NONE") || compType == chunkName ("twos")) | |||
| { | |||
| littleEndian = false; | |||
| } | |||
| else if (compType == chunkName ("sowt")) | |||
| { | |||
| littleEndian = true; | |||
| } | |||
| else if (compType == chunkName ("fl32") || compType == chunkName ("FL32")) | |||
| { | |||
| littleEndian = false; | |||
| usesFloatingPointData = true; | |||
| } | |||
| else | |||
| { | |||
| sampleRate = 0; | |||
| break; | |||
| } | |||
| } | |||
| } | |||
| else if (type == chunkName ("SSND")) | |||
| { | |||
| hasGotData = true; | |||
| const int offset = input->readIntBigEndian(); | |||
| dataChunkStart = input->getPosition() + 4 + offset; | |||
| lengthInSamples = (bytesPerFrame > 0) ? jmin (lengthInSamples, ((int64) length) / (int64) bytesPerFrame) : 0; | |||
| } | |||
| else if (type == chunkName ("MARK")) | |||
| { | |||
| const uint16 numCues = (uint16) input->readShortBigEndian(); | |||
| // these two are always the same for AIFF-read files | |||
| metadataValues.set ("NumCuePoints", String (numCues)); | |||
| metadataValues.set ("NumCueLabels", String (numCues)); | |||
| for (uint16 i = 0; i < numCues; ++i) | |||
| { | |||
| uint16 identifier = (uint16) input->readShortBigEndian(); | |||
| uint32 offset = (uint32) input->readIntBigEndian(); | |||
| uint8 stringLength = (uint8) input->readByte(); | |||
| MemoryBlock textBlock; | |||
| input->readIntoMemoryBlock (textBlock, stringLength); | |||
| // if the stringLength is even then read one more byte as the | |||
| // string needs to be an even number of bytes INCLUDING the | |||
| // leading length character in the pascal string | |||
| if ((stringLength & 1) == 0) | |||
| input->readByte(); | |||
| const String prefixCue ("Cue" + String (i)); | |||
| metadataValues.set (prefixCue + "Identifier", String (identifier)); | |||
| metadataValues.set (prefixCue + "Offset", String (offset)); | |||
| const String prefixLabel ("CueLabel" + String (i)); | |||
| metadataValues.set (prefixLabel + "Identifier", String (identifier)); | |||
| metadataValues.set (prefixLabel + "Text", textBlock.toString()); | |||
| } | |||
| } | |||
| else if (type == chunkName ("COMT")) | |||
| { | |||
| const uint16 numNotes = (uint16) input->readShortBigEndian(); | |||
| metadataValues.set ("NumCueNotes", String (numNotes)); | |||
| for (uint16 i = 0; i < numNotes; ++i) | |||
| { | |||
| uint32 timestamp = (uint32) input->readIntBigEndian(); | |||
| uint16 identifier = (uint16) input->readShortBigEndian(); // may be zero in this case | |||
| uint16 stringLength = (uint16) input->readShortBigEndian(); | |||
| MemoryBlock textBlock; | |||
| input->readIntoMemoryBlock (textBlock, stringLength + (stringLength & 1)); | |||
| const String prefix ("CueNote" + String (i)); | |||
| metadataValues.set (prefix + "TimeStamp", String (timestamp)); | |||
| metadataValues.set (prefix + "Identifier", String (identifier)); | |||
| metadataValues.set (prefix + "Text", textBlock.toString()); | |||
| } | |||
| } | |||
| else if (type == chunkName ("INST")) | |||
| { | |||
| HeapBlock <InstChunk> inst; | |||
| inst.calloc (jmax ((size_t) length + 1, sizeof (InstChunk)), 1); | |||
| input->read (inst, (int) length); | |||
| inst->copyTo (metadataValues); | |||
| } | |||
| else if (type == chunkName ("basc")) | |||
| { | |||
| AiffFileHelpers::BASCChunk (*input).addToMetadata (metadataValues); | |||
| } | |||
| else if (type == chunkName ("cate")) | |||
| { | |||
| metadataValues.set (AiffAudioFormat::appleTag, | |||
| AiffFileHelpers::readCATEChunk (*input, length));; | |||
| } | |||
| else if ((hasGotVer && hasGotData && hasGotType) | |||
| || chunkEnd < input->getPosition() | |||
| || input->isExhausted()) | |||
| { | |||
| break; | |||
| } | |||
| input->setPosition (chunkEnd + (chunkEnd & 1)); // (chunks should be aligned to an even byte address) | |||
| } | |||
| } | |||
| } | |||
| if (metadataValues.size() > 0) | |||
| metadataValues.set ("MetaDataSource", "AIFF"); | |||
| } | |||
| //============================================================================== | |||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) override | |||
| { | |||
| clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, | |||
| startSampleInFile, numSamples, lengthInSamples); | |||
| if (numSamples <= 0) | |||
| return true; | |||
| input->setPosition (dataChunkStart + startSampleInFile * bytesPerFrame); | |||
| while (numSamples > 0) | |||
| { | |||
| const int tempBufSize = 480 * 3 * 4; // (keep this a multiple of 3) | |||
| char tempBuffer [tempBufSize]; | |||
| const int numThisTime = jmin (tempBufSize / bytesPerFrame, numSamples); | |||
| const int bytesRead = input->read (tempBuffer, numThisTime * bytesPerFrame); | |||
| if (bytesRead < numThisTime * bytesPerFrame) | |||
| { | |||
| jassert (bytesRead >= 0); | |||
| zeromem (tempBuffer + bytesRead, (size_t) (numThisTime * bytesPerFrame - bytesRead)); | |||
| } | |||
| if (littleEndian) | |||
| copySampleData<AudioData::LittleEndian> (bitsPerSample, usesFloatingPointData, | |||
| destSamples, startOffsetInDestBuffer, numDestChannels, | |||
| tempBuffer, (int) numChannels, numThisTime); | |||
| else | |||
| copySampleData<AudioData::BigEndian> (bitsPerSample, usesFloatingPointData, | |||
| destSamples, startOffsetInDestBuffer, numDestChannels, | |||
| tempBuffer, (int) numChannels, numThisTime); | |||
| startOffsetInDestBuffer += numThisTime; | |||
| numSamples -= numThisTime; | |||
| } | |||
| return true; | |||
| } | |||
| template <typename Endianness> | |||
| static void copySampleData (unsigned int bitsPerSample, const bool usesFloatingPointData, | |||
| int* const* destSamples, int startOffsetInDestBuffer, int numDestChannels, | |||
| const void* sourceData, int numChannels, int numSamples) noexcept | |||
| { | |||
| switch (bitsPerSample) | |||
| { | |||
| case 8: ReadHelper<AudioData::Int32, AudioData::Int8, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; | |||
| case 16: ReadHelper<AudioData::Int32, AudioData::Int16, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; | |||
| case 24: ReadHelper<AudioData::Int32, AudioData::Int24, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; | |||
| case 32: if (usesFloatingPointData) ReadHelper<AudioData::Float32, AudioData::Float32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); | |||
| else ReadHelper<AudioData::Int32, AudioData::Int32, Endianness>::read (destSamples, startOffsetInDestBuffer, numDestChannels, sourceData, numChannels, numSamples); break; | |||
| default: jassertfalse; break; | |||
| } | |||
| } | |||
| int bytesPerFrame; | |||
| int64 dataChunkStart; | |||
| bool littleEndian; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatReader) | |||
| }; | |||
| //============================================================================== | |||
| class AiffAudioFormatWriter : public AudioFormatWriter | |||
| { | |||
| public: | |||
| AiffAudioFormatWriter (OutputStream* out, double rate, | |||
| unsigned int numChans, unsigned int bits, | |||
| const StringPairArray& metadataValues) | |||
| : AudioFormatWriter (out, TRANS (aiffFormatName), rate, numChans, bits), | |||
| lengthInSamples (0), | |||
| bytesWritten (0), | |||
| writeFailed (false) | |||
| { | |||
| using namespace AiffFileHelpers; | |||
| if (metadataValues.size() > 0) | |||
| { | |||
| // The meta data should have been santised for the AIFF format. | |||
| // If it was originally sourced from a WAV file the MetaDataSource | |||
| // key should be removed (or set to "AIFF") once this has been done | |||
| jassert (metadataValues.getValue ("MetaDataSource", "None") != "WAV"); | |||
| MarkChunk::create (markChunk, metadataValues); | |||
| COMTChunk::create (comtChunk, metadataValues); | |||
| InstChunk::create (instChunk, metadataValues); | |||
| } | |||
| headerPosition = out->getPosition(); | |||
| writeHeader(); | |||
| } | |||
| ~AiffAudioFormatWriter() | |||
| { | |||
| if ((bytesWritten & 1) != 0) | |||
| output->writeByte (0); | |||
| writeHeader(); | |||
| } | |||
| //============================================================================== | |||
| bool write (const int** data, int numSamples) override | |||
| { | |||
| jassert (data != nullptr && *data != nullptr); // the input must contain at least one channel! | |||
| if (writeFailed) | |||
| return false; | |||
| const size_t bytes = (size_t) numSamples * numChannels * bitsPerSample / 8; | |||
| tempBlock.ensureSize ((size_t) bytes, false); | |||
| switch (bitsPerSample) | |||
| { | |||
| case 8: WriteHelper<AudioData::Int8, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; | |||
| case 16: WriteHelper<AudioData::Int16, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; | |||
| case 24: WriteHelper<AudioData::Int24, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; | |||
| case 32: WriteHelper<AudioData::Int32, AudioData::Int32, AudioData::BigEndian>::write (tempBlock.getData(), (int) numChannels, data, numSamples); break; | |||
| default: jassertfalse; break; | |||
| } | |||
| if (bytesWritten + bytes >= (size_t) 0xfff00000 | |||
| || ! output->write (tempBlock.getData(), bytes)) | |||
| { | |||
| // failed to write to disk, so let's try writing the header. | |||
| // If it's just run out of disk space, then if it does manage | |||
| // to write the header, we'll still have a useable file.. | |||
| writeHeader(); | |||
| writeFailed = true; | |||
| return false; | |||
| } | |||
| else | |||
| { | |||
| bytesWritten += bytes; | |||
| lengthInSamples += (uint64) numSamples; | |||
| return true; | |||
| } | |||
| } | |||
| private: | |||
| MemoryBlock tempBlock, markChunk, comtChunk, instChunk; | |||
| uint64 lengthInSamples, bytesWritten; | |||
| int64 headerPosition; | |||
| bool writeFailed; | |||
| void writeHeader() | |||
| { | |||
| using namespace AiffFileHelpers; | |||
| const bool couldSeekOk = output->setPosition (headerPosition); | |||
| (void) couldSeekOk; | |||
| // if this fails, you've given it an output stream that can't seek! It needs | |||
| // to be able to seek back to write the header | |||
| jassert (couldSeekOk); | |||
| const int headerLen = (int) (54 + (markChunk.getSize() > 0 ? markChunk.getSize() + 8 : 0) | |||
| + (comtChunk.getSize() > 0 ? comtChunk.getSize() + 8 : 0) | |||
| + (instChunk.getSize() > 0 ? instChunk.getSize() + 8 : 0)); | |||
| int audioBytes = (int) (lengthInSamples * ((bitsPerSample * numChannels) / 8)); | |||
| audioBytes += (audioBytes & 1); | |||
| output->writeInt (chunkName ("FORM")); | |||
| output->writeIntBigEndian (headerLen + audioBytes - 8); | |||
| output->writeInt (chunkName ("AIFF")); | |||
| output->writeInt (chunkName ("COMM")); | |||
| output->writeIntBigEndian (18); | |||
| output->writeShortBigEndian ((short) numChannels); | |||
| output->writeIntBigEndian ((int) lengthInSamples); | |||
| output->writeShortBigEndian ((short) bitsPerSample); | |||
| uint8 sampleRateBytes[10] = { 0 }; | |||
| if (sampleRate <= 1) | |||
| { | |||
| sampleRateBytes[0] = 0x3f; | |||
| sampleRateBytes[1] = 0xff; | |||
| sampleRateBytes[2] = 0x80; | |||
| } | |||
| else | |||
| { | |||
| int mask = 0x40000000; | |||
| sampleRateBytes[0] = 0x40; | |||
| if (sampleRate >= mask) | |||
| { | |||
| jassertfalse; | |||
| sampleRateBytes[1] = 0x1d; | |||
| } | |||
| else | |||
| { | |||
| int n = (int) sampleRate; | |||
| int i; | |||
| for (i = 0; i <= 32 ; ++i) | |||
| { | |||
| if ((n & mask) != 0) | |||
| break; | |||
| mask >>= 1; | |||
| } | |||
| n = n << (i + 1); | |||
| sampleRateBytes[1] = (uint8) (29 - i); | |||
| sampleRateBytes[2] = (uint8) ((n >> 24) & 0xff); | |||
| sampleRateBytes[3] = (uint8) ((n >> 16) & 0xff); | |||
| sampleRateBytes[4] = (uint8) ((n >> 8) & 0xff); | |||
| sampleRateBytes[5] = (uint8) (n & 0xff); | |||
| } | |||
| } | |||
| output->write (sampleRateBytes, 10); | |||
| if (markChunk.getSize() > 0) | |||
| { | |||
| output->writeInt (chunkName ("MARK")); | |||
| output->writeIntBigEndian ((int) markChunk.getSize()); | |||
| *output << markChunk; | |||
| } | |||
| if (comtChunk.getSize() > 0) | |||
| { | |||
| output->writeInt (chunkName ("COMT")); | |||
| output->writeIntBigEndian ((int) comtChunk.getSize()); | |||
| *output << comtChunk; | |||
| } | |||
| if (instChunk.getSize() > 0) | |||
| { | |||
| output->writeInt (chunkName ("INST")); | |||
| output->writeIntBigEndian ((int) instChunk.getSize()); | |||
| *output << instChunk; | |||
| } | |||
| output->writeInt (chunkName ("SSND")); | |||
| output->writeIntBigEndian (audioBytes + 8); | |||
| output->writeInt (0); | |||
| output->writeInt (0); | |||
| jassert (output->getPosition() == headerLen); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AiffAudioFormatWriter) | |||
| }; | |||
| //============================================================================== | |||
| class MemoryMappedAiffReader : public MemoryMappedAudioFormatReader | |||
| { | |||
| public: | |||
| MemoryMappedAiffReader (const File& f, const AiffAudioFormatReader& reader) | |||
| : MemoryMappedAudioFormatReader (f, reader, reader.dataChunkStart, | |||
| reader.bytesPerFrame * reader.lengthInSamples, reader.bytesPerFrame), | |||
| littleEndian (reader.littleEndian) | |||
| { | |||
| } | |||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) override | |||
| { | |||
| clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, | |||
| startSampleInFile, numSamples, lengthInSamples); | |||
| if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples))) | |||
| { | |||
| jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. | |||
| return false; | |||
| } | |||
| if (littleEndian) | |||
| AiffAudioFormatReader::copySampleData<AudioData::LittleEndian> | |||
| (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, | |||
| numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); | |||
| else | |||
| AiffAudioFormatReader::copySampleData<AudioData::BigEndian> | |||
| (bitsPerSample, usesFloatingPointData, destSamples, startOffsetInDestBuffer, | |||
| numDestChannels, sampleToPointer (startSampleInFile), (int) numChannels, numSamples); | |||
| return true; | |||
| } | |||
| void readMaxLevels (int64 startSampleInFile, int64 numSamples, | |||
| float& min0, float& max0, float& min1, float& max1) | |||
| { | |||
| if (numSamples <= 0) | |||
| { | |||
| min0 = max0 = min1 = max1 = 0; | |||
| return; | |||
| } | |||
| if (map == nullptr || ! mappedSection.contains (Range<int64> (startSampleInFile, startSampleInFile + numSamples))) | |||
| { | |||
| jassertfalse; // you must make sure that the window contains all the samples you're going to attempt to read. | |||
| min0 = max0 = min1 = max1 = 0; | |||
| return; | |||
| } | |||
| switch (bitsPerSample) | |||
| { | |||
| case 8: scanMinAndMax<AudioData::UInt8> (startSampleInFile, numSamples, min0, max0, min1, max1); break; | |||
| case 16: scanMinAndMax<AudioData::Int16> (startSampleInFile, numSamples, min0, max0, min1, max1); break; | |||
| case 24: scanMinAndMax<AudioData::Int24> (startSampleInFile, numSamples, min0, max0, min1, max1); break; | |||
| case 32: if (usesFloatingPointData) scanMinAndMax<AudioData::Float32> (startSampleInFile, numSamples, min0, max0, min1, max1); | |||
| else scanMinAndMax<AudioData::Int32> (startSampleInFile, numSamples, min0, max0, min1, max1); break; | |||
| default: jassertfalse; break; | |||
| } | |||
| } | |||
| private: | |||
| const bool littleEndian; | |||
| template <typename SampleType> | |||
| void scanMinAndMax (int64 startSampleInFile, int64 numSamples, | |||
| float& min0, float& max0, float& min1, float& max1) const noexcept | |||
| { | |||
| scanMinAndMax2<SampleType> (0, startSampleInFile, numSamples, min0, max0); | |||
| if (numChannels > 1) | |||
| scanMinAndMax2<SampleType> (1, startSampleInFile, numSamples, min1, max1); | |||
| else | |||
| min1 = max1 = 0; | |||
| } | |||
| template <typename SampleType> | |||
| void scanMinAndMax2 (int channel, int64 startSampleInFile, int64 numSamples, float& mn, float& mx) const noexcept | |||
| { | |||
| if (littleEndian) | |||
| scanMinAndMaxInterleaved<SampleType, AudioData::LittleEndian> (channel, startSampleInFile, numSamples, mn, mx); | |||
| else | |||
| scanMinAndMaxInterleaved<SampleType, AudioData::BigEndian> (channel, startSampleInFile, numSamples, mn, mx); | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedAiffReader) | |||
| }; | |||
| //============================================================================== | |||
| AiffAudioFormat::AiffAudioFormat() | |||
| : AudioFormat (TRANS (aiffFormatName), StringArray (aiffExtensions)) | |||
| { | |||
| } | |||
| AiffAudioFormat::~AiffAudioFormat() | |||
| { | |||
| } | |||
| Array<int> AiffAudioFormat::getPossibleSampleRates() | |||
| { | |||
| const int rates[] = { 22050, 32000, 44100, 48000, 88200, 96000, 176400, 192000, 0 }; | |||
| return Array <int> (rates); | |||
| } | |||
| Array<int> AiffAudioFormat::getPossibleBitDepths() | |||
| { | |||
| const int depths[] = { 8, 16, 24, 0 }; | |||
| return Array <int> (depths); | |||
| } | |||
| bool AiffAudioFormat::canDoStereo() { return true; } | |||
| bool AiffAudioFormat::canDoMono() { return true; } | |||
| #if JUCE_MAC | |||
| bool AiffAudioFormat::canHandleFile (const File& f) | |||
| { | |||
| if (AudioFormat::canHandleFile (f)) | |||
| return true; | |||
| const OSType type = f.getMacOSType(); | |||
| // (NB: written as hex to avoid four-char-constant warnings) | |||
| return type == 0x41494646 /* AIFF */ || type == 0x41494643 /* AIFC */ | |||
| || type == 0x61696666 /* aiff */ || type == 0x61696663 /* aifc */; | |||
| } | |||
| #endif | |||
| AudioFormatReader* AiffAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) | |||
| { | |||
| ScopedPointer <AiffAudioFormatReader> w (new AiffAudioFormatReader (sourceStream)); | |||
| if (w->sampleRate > 0 && w->numChannels > 0) | |||
| return w.release(); | |||
| if (! deleteStreamIfOpeningFails) | |||
| w->input = nullptr; | |||
| return nullptr; | |||
| } | |||
| MemoryMappedAudioFormatReader* AiffAudioFormat::createMemoryMappedReader (const File& file) | |||
| { | |||
| if (FileInputStream* fin = file.createInputStream()) | |||
| { | |||
| AiffAudioFormatReader reader (fin); | |||
| if (reader.lengthInSamples > 0) | |||
| return new MemoryMappedAiffReader (file, reader); | |||
| } | |||
| return nullptr; | |||
| } | |||
| AudioFormatWriter* AiffAudioFormat::createWriterFor (OutputStream* out, | |||
| double sampleRate, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& metadataValues, | |||
| int /*qualityOptionIndex*/) | |||
| { | |||
| if (getPossibleBitDepths().contains (bitsPerSample)) | |||
| return new AiffAudioFormatWriter (out, sampleRate, numberOfChannels, (unsigned int) bitsPerSample, metadataValues); | |||
| return nullptr; | |||
| } | |||
| @@ -0,0 +1,84 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| //============================================================================== | |||
| /** | |||
| Reads and Writes AIFF format audio files. | |||
| @see AudioFormat | |||
| */ | |||
| class JUCE_API AiffAudioFormat : public AudioFormat | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates an format object. */ | |||
| AiffAudioFormat(); | |||
| /** Destructor. */ | |||
| ~AiffAudioFormat(); | |||
| //============================================================================== | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleOneShot; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleRootSet; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleRootNote; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleBeats; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleDenominator; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleNumerator; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleTag; | |||
| /** Metadata property name used when reading a aiff file with a basc chunk. */ | |||
| static const char* const appleKey; | |||
| //============================================================================== | |||
| Array<int> getPossibleSampleRates() override; | |||
| Array<int> getPossibleBitDepths() override; | |||
| bool canDoStereo() override; | |||
| bool canDoMono() override; | |||
| #if JUCE_MAC | |||
| bool canHandleFile (const File& fileToTest) override; | |||
| #endif | |||
| //============================================================================== | |||
| AudioFormatReader* createReaderFor (InputStream* sourceStream, | |||
| bool deleteStreamIfOpeningFails) override; | |||
| MemoryMappedAudioFormatReader* createMemoryMappedReader (const File&) override; | |||
| AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, | |||
| double sampleRateToUse, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& metadataValues, | |||
| int qualityOptionIndex) override; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(AiffAudioFormat) | |||
| }; | |||
| @@ -0,0 +1,528 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_MAC || JUCE_IOS | |||
| //============================================================================== | |||
| namespace | |||
| { | |||
| const char* const coreAudioFormatName = "CoreAudio supported file"; | |||
| StringArray findFileExtensionsForCoreAudioCodecs() | |||
| { | |||
| StringArray extensionsArray; | |||
| CFArrayRef extensions = nullptr; | |||
| UInt32 sizeOfArray = sizeof (extensions); | |||
| if (AudioFileGetGlobalInfo (kAudioFileGlobalInfo_AllExtensions, 0, 0, &sizeOfArray, &extensions) == noErr) | |||
| { | |||
| const CFIndex numValues = CFArrayGetCount (extensions); | |||
| for (CFIndex i = 0; i < numValues; ++i) | |||
| extensionsArray.add ("." + String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (extensions, i))); | |||
| CFRelease (extensions); | |||
| } | |||
| return extensionsArray; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| const char* const CoreAudioFormat::midiDataBase64 = "midiDataBase64"; | |||
| const char* const CoreAudioFormat::tempo = "tempo"; | |||
| const char* const CoreAudioFormat::timeSig = "time signature"; | |||
| const char* const CoreAudioFormat::keySig = "key signature"; | |||
| //============================================================================== | |||
| struct CoreAudioFormatMetatdata | |||
| { | |||
| static uint32 chunkName (const char* const name) noexcept { return ByteOrder::bigEndianInt (name); } | |||
| //============================================================================== | |||
| struct FileHeader | |||
| { | |||
| FileHeader (InputStream& input) | |||
| { | |||
| fileType = (uint32) input.readIntBigEndian(); | |||
| fileVersion = (uint16) input.readShortBigEndian(); | |||
| fileFlags = (uint16) input.readShortBigEndian(); | |||
| } | |||
| uint32 fileType; | |||
| uint16 fileVersion; | |||
| uint16 fileFlags; | |||
| }; | |||
| //============================================================================== | |||
| struct ChunkHeader | |||
| { | |||
| ChunkHeader (InputStream& input) | |||
| { | |||
| chunkType = (uint32) input.readIntBigEndian(); | |||
| chunkSize = (int64) input.readInt64BigEndian(); | |||
| } | |||
| uint32 chunkType; | |||
| int64 chunkSize; | |||
| }; | |||
| //============================================================================== | |||
| struct AudioDescriptionChunk | |||
| { | |||
| AudioDescriptionChunk (InputStream& input) | |||
| { | |||
| sampleRate = input.readDoubleBigEndian(); | |||
| formatID = (uint32) input.readIntBigEndian(); | |||
| formatFlags = (uint32) input.readIntBigEndian(); | |||
| bytesPerPacket = (uint32) input.readIntBigEndian(); | |||
| framesPerPacket = (uint32) input.readIntBigEndian(); | |||
| channelsPerFrame = (uint32) input.readIntBigEndian(); | |||
| bitsPerChannel = (uint32) input.readIntBigEndian(); | |||
| } | |||
| double sampleRate; | |||
| uint32 formatID; | |||
| uint32 formatFlags; | |||
| uint32 bytesPerPacket; | |||
| uint32 framesPerPacket; | |||
| uint32 channelsPerFrame; | |||
| uint32 bitsPerChannel; | |||
| }; | |||
| //============================================================================== | |||
| struct UserDefinedChunk | |||
| { | |||
| UserDefinedChunk (InputStream& input, int64 size) | |||
| { | |||
| // a user defined chunk contains 16 bytes of a UUID first | |||
| uuid[1] = input.readInt64BigEndian(); | |||
| uuid[0] = input.readInt64BigEndian(); | |||
| input.skipNextBytes (size - 16); | |||
| } | |||
| int64 uuid[2]; | |||
| }; | |||
| //============================================================================== | |||
| static StringPairArray parseMidiChunk (InputStream& input, int64 size) | |||
| { | |||
| const int64 originalPosition = input.getPosition(); | |||
| MemoryBlock midiBlock; | |||
| input.readIntoMemoryBlock (midiBlock, (ssize_t) size); | |||
| MemoryInputStream midiInputStream (midiBlock, false); | |||
| StringPairArray midiMetadata; | |||
| MidiFile midiFile; | |||
| if (midiFile.readFrom (midiInputStream)) | |||
| { | |||
| midiMetadata.set (CoreAudioFormat::midiDataBase64, midiBlock.toBase64Encoding()); | |||
| findTempoEvents (midiFile, midiMetadata); | |||
| findTimeSigEvents (midiFile, midiMetadata); | |||
| findKeySigEvents (midiFile, midiMetadata); | |||
| } | |||
| input.setPosition (originalPosition + size); | |||
| return midiMetadata; | |||
| } | |||
| static void findTempoEvents (MidiFile& midiFile, StringPairArray& midiMetadata) | |||
| { | |||
| MidiMessageSequence tempoEvents; | |||
| midiFile.findAllTempoEvents (tempoEvents); | |||
| const int numTempoEvents = tempoEvents.getNumEvents(); | |||
| MemoryOutputStream tempoSequence; | |||
| for (int i = 0; i < numTempoEvents; ++i) | |||
| { | |||
| const double tempo = getTempoFromTempoMetaEvent (tempoEvents.getEventPointer (i)); | |||
| if (tempo > 0.0) | |||
| { | |||
| if (i == 0) | |||
| midiMetadata.set (CoreAudioFormat::tempo, String (tempo)); | |||
| if (numTempoEvents > 1) | |||
| tempoSequence << String (tempo) << ',' << tempoEvents.getEventTime (i) << ';'; | |||
| } | |||
| } | |||
| if (tempoSequence.getDataSize() > 0) | |||
| midiMetadata.set ("tempo sequence", tempoSequence.toUTF8()); | |||
| } | |||
| static double getTempoFromTempoMetaEvent (MidiMessageSequence::MidiEventHolder* holder) | |||
| { | |||
| if (holder != nullptr) | |||
| { | |||
| const MidiMessage& midiMessage = holder->message; | |||
| if (midiMessage.isTempoMetaEvent()) | |||
| { | |||
| const double tempoSecondsPerQuarterNote = midiMessage.getTempoSecondsPerQuarterNote(); | |||
| if (tempoSecondsPerQuarterNote > 0.0) | |||
| return 60.0 / tempoSecondsPerQuarterNote; | |||
| } | |||
| } | |||
| return 0.0; | |||
| } | |||
| static void findTimeSigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) | |||
| { | |||
| MidiMessageSequence timeSigEvents; | |||
| midiFile.findAllTimeSigEvents (timeSigEvents); | |||
| const int numTimeSigEvents = timeSigEvents.getNumEvents(); | |||
| MemoryOutputStream timeSigSequence; | |||
| for (int i = 0; i < numTimeSigEvents; ++i) | |||
| { | |||
| int numerator, denominator; | |||
| timeSigEvents.getEventPointer(i)->message.getTimeSignatureInfo (numerator, denominator); | |||
| String timeSigString; | |||
| timeSigString << numerator << '/' << denominator; | |||
| if (i == 0) | |||
| midiMetadata.set (CoreAudioFormat::timeSig, timeSigString); | |||
| if (numTimeSigEvents > 1) | |||
| timeSigSequence << timeSigString << ',' << timeSigEvents.getEventTime (i) << ';'; | |||
| } | |||
| if (timeSigSequence.getDataSize() > 0) | |||
| midiMetadata.set ("time signature sequence", timeSigSequence.toUTF8()); | |||
| } | |||
| static void findKeySigEvents (MidiFile& midiFile, StringPairArray& midiMetadata) | |||
| { | |||
| MidiMessageSequence keySigEvents; | |||
| midiFile.findAllKeySigEvents (keySigEvents); | |||
| const int numKeySigEvents = keySigEvents.getNumEvents(); | |||
| MemoryOutputStream keySigSequence; | |||
| for (int i = 0; i < numKeySigEvents; ++i) | |||
| { | |||
| const MidiMessage& message (keySigEvents.getEventPointer (i)->message); | |||
| const int key = jlimit (0, 14, message.getKeySignatureNumberOfSharpsOrFlats() + 7); | |||
| const bool isMajor = message.isKeySignatureMajorKey(); | |||
| static const char* majorKeys[] = { "Cb", "Gb", "Db", "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#" }; | |||
| static const char* minorKeys[] = { "Ab", "Eb", "Bb", "F", "C", "G", "D", "A", "E", "B", "F#", "C#", "G#", "D#", "A#" }; | |||
| String keySigString (isMajor ? majorKeys[key] | |||
| : minorKeys[key]); | |||
| if (! isMajor) | |||
| keySigString << 'm'; | |||
| if (i == 0) | |||
| midiMetadata.set (CoreAudioFormat::keySig, keySigString); | |||
| if (numKeySigEvents > 1) | |||
| keySigSequence << keySigString << ',' << keySigEvents.getEventTime (i) << ';'; | |||
| } | |||
| if (keySigSequence.getDataSize() > 0) | |||
| midiMetadata.set ("key signature sequence", keySigSequence.toUTF8()); | |||
| } | |||
| //============================================================================== | |||
| static StringPairArray parseInformationChunk (InputStream& input) | |||
| { | |||
| StringPairArray infoStrings; | |||
| const uint32 numEntries = (uint32) input.readIntBigEndian(); | |||
| for (uint32 i = 0; i < numEntries; ++i) | |||
| infoStrings.set (input.readString(), input.readString()); | |||
| return infoStrings; | |||
| } | |||
| //============================================================================== | |||
| static bool read (InputStream& input, StringPairArray& metadataValues) | |||
| { | |||
| const int64 originalPos = input.getPosition(); | |||
| const FileHeader cafFileHeader (input); | |||
| const bool isCafFile = cafFileHeader.fileType == chunkName ("caff"); | |||
| if (isCafFile) | |||
| { | |||
| while (! input.isExhausted()) | |||
| { | |||
| const ChunkHeader chunkHeader (input); | |||
| if (chunkHeader.chunkType == chunkName ("desc")) | |||
| { | |||
| AudioDescriptionChunk audioDescriptionChunk (input); | |||
| } | |||
| else if (chunkHeader.chunkType == chunkName ("uuid")) | |||
| { | |||
| UserDefinedChunk userDefinedChunk (input, chunkHeader.chunkSize); | |||
| } | |||
| else if (chunkHeader.chunkType == chunkName ("data")) | |||
| { | |||
| // -1 signifies an unknown data size so the data has to be at the | |||
| // end of the file so we must have finished the header | |||
| if (chunkHeader.chunkSize == -1) | |||
| break; | |||
| input.skipNextBytes (chunkHeader.chunkSize); | |||
| } | |||
| else if (chunkHeader.chunkType == chunkName ("midi")) | |||
| { | |||
| metadataValues.addArray (parseMidiChunk (input, chunkHeader.chunkSize)); | |||
| } | |||
| else if (chunkHeader.chunkType == chunkName ("info")) | |||
| { | |||
| metadataValues.addArray (parseInformationChunk (input)); | |||
| } | |||
| else | |||
| { | |||
| // we aren't decoding this chunk yet so just skip over it | |||
| input.skipNextBytes (chunkHeader.chunkSize); | |||
| } | |||
| } | |||
| } | |||
| input.setPosition (originalPos); | |||
| return isCafFile; | |||
| } | |||
| }; | |||
| //============================================================================== | |||
| class CoreAudioReader : public AudioFormatReader | |||
| { | |||
| public: | |||
| CoreAudioReader (InputStream* const inp) | |||
| : AudioFormatReader (inp, TRANS (coreAudioFormatName)), | |||
| ok (false), lastReadPosition (0) | |||
| { | |||
| usesFloatingPointData = true; | |||
| bitsPerSample = 32; | |||
| if (input != nullptr) | |||
| CoreAudioFormatMetatdata::read (*input, metadataValues); | |||
| OSStatus status = AudioFileOpenWithCallbacks (this, | |||
| &readCallback, | |||
| nullptr, // write needs to be null to avoid permisisions errors | |||
| &getSizeCallback, | |||
| nullptr, // setSize needs to be null to avoid permisisions errors | |||
| 0, // AudioFileTypeID inFileTypeHint | |||
| &audioFileID); | |||
| if (status == noErr) | |||
| { | |||
| status = ExtAudioFileWrapAudioFileID (audioFileID, false, &audioFileRef); | |||
| if (status == noErr) | |||
| { | |||
| AudioStreamBasicDescription sourceAudioFormat; | |||
| UInt32 audioStreamBasicDescriptionSize = sizeof (AudioStreamBasicDescription); | |||
| ExtAudioFileGetProperty (audioFileRef, | |||
| kExtAudioFileProperty_FileDataFormat, | |||
| &audioStreamBasicDescriptionSize, | |||
| &sourceAudioFormat); | |||
| numChannels = sourceAudioFormat.mChannelsPerFrame; | |||
| sampleRate = sourceAudioFormat.mSampleRate; | |||
| UInt32 sizeOfLengthProperty = sizeof (int64); | |||
| ExtAudioFileGetProperty (audioFileRef, | |||
| kExtAudioFileProperty_FileLengthFrames, | |||
| &sizeOfLengthProperty, | |||
| &lengthInSamples); | |||
| destinationAudioFormat.mSampleRate = sampleRate; | |||
| destinationAudioFormat.mFormatID = kAudioFormatLinearPCM; | |||
| destinationAudioFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat | kLinearPCMFormatFlagIsNonInterleaved | kAudioFormatFlagsNativeEndian; | |||
| destinationAudioFormat.mBitsPerChannel = sizeof (float) * 8; | |||
| destinationAudioFormat.mChannelsPerFrame = numChannels; | |||
| destinationAudioFormat.mBytesPerFrame = sizeof (float); | |||
| destinationAudioFormat.mFramesPerPacket = 1; | |||
| destinationAudioFormat.mBytesPerPacket = destinationAudioFormat.mFramesPerPacket * destinationAudioFormat.mBytesPerFrame; | |||
| status = ExtAudioFileSetProperty (audioFileRef, | |||
| kExtAudioFileProperty_ClientDataFormat, | |||
| sizeof (AudioStreamBasicDescription), | |||
| &destinationAudioFormat); | |||
| if (status == noErr) | |||
| { | |||
| bufferList.malloc (1, sizeof (AudioBufferList) + numChannels * sizeof (AudioBuffer)); | |||
| bufferList->mNumberBuffers = numChannels; | |||
| ok = true; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| ~CoreAudioReader() | |||
| { | |||
| ExtAudioFileDispose (audioFileRef); | |||
| AudioFileClose (audioFileID); | |||
| } | |||
| //============================================================================== | |||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) override | |||
| { | |||
| clearSamplesBeyondAvailableLength (destSamples, numDestChannels, startOffsetInDestBuffer, | |||
| startSampleInFile, numSamples, lengthInSamples); | |||
| if (numSamples <= 0) | |||
| return true; | |||
| if (lastReadPosition != startSampleInFile) | |||
| { | |||
| OSStatus status = ExtAudioFileSeek (audioFileRef, startSampleInFile); | |||
| if (status != noErr) | |||
| return false; | |||
| lastReadPosition = startSampleInFile; | |||
| } | |||
| while (numSamples > 0) | |||
| { | |||
| const int numThisTime = jmin (8192, numSamples); | |||
| const size_t numBytes = sizeof (float) * (size_t) numThisTime; | |||
| audioDataBlock.ensureSize (numBytes * numChannels, false); | |||
| float* data = static_cast<float*> (audioDataBlock.getData()); | |||
| for (int j = (int) numChannels; --j >= 0;) | |||
| { | |||
| bufferList->mBuffers[j].mNumberChannels = 1; | |||
| bufferList->mBuffers[j].mDataByteSize = (UInt32) numBytes; | |||
| bufferList->mBuffers[j].mData = data; | |||
| data += numThisTime; | |||
| } | |||
| UInt32 numFramesToRead = (UInt32) numThisTime; | |||
| OSStatus status = ExtAudioFileRead (audioFileRef, &numFramesToRead, bufferList); | |||
| if (status != noErr) | |||
| return false; | |||
| for (int i = numDestChannels; --i >= 0;) | |||
| { | |||
| if (destSamples[i] != nullptr) | |||
| { | |||
| if (i < (int) numChannels) | |||
| memcpy (destSamples[i] + startOffsetInDestBuffer, bufferList->mBuffers[i].mData, numBytes); | |||
| else | |||
| zeromem (destSamples[i] + startOffsetInDestBuffer, numBytes); | |||
| } | |||
| } | |||
| startOffsetInDestBuffer += numThisTime; | |||
| numSamples -= numThisTime; | |||
| lastReadPosition += numThisTime; | |||
| } | |||
| return true; | |||
| } | |||
| bool ok; | |||
| private: | |||
| AudioFileID audioFileID; | |||
| ExtAudioFileRef audioFileRef; | |||
| AudioStreamBasicDescription destinationAudioFormat; | |||
| MemoryBlock audioDataBlock; | |||
| HeapBlock<AudioBufferList> bufferList; | |||
| int64 lastReadPosition; | |||
| static SInt64 getSizeCallback (void* inClientData) | |||
| { | |||
| return static_cast<CoreAudioReader*> (inClientData)->input->getTotalLength(); | |||
| } | |||
| static OSStatus readCallback (void* inClientData, | |||
| SInt64 inPosition, | |||
| UInt32 requestCount, | |||
| void* buffer, | |||
| UInt32* actualCount) | |||
| { | |||
| CoreAudioReader* const reader = static_cast<CoreAudioReader*> (inClientData); | |||
| reader->input->setPosition (inPosition); | |||
| *actualCount = (UInt32) reader->input->read (buffer, (int) requestCount); | |||
| return noErr; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioReader) | |||
| }; | |||
| //============================================================================== | |||
| CoreAudioFormat::CoreAudioFormat() | |||
| : AudioFormat (TRANS (coreAudioFormatName), findFileExtensionsForCoreAudioCodecs()) | |||
| { | |||
| } | |||
| CoreAudioFormat::~CoreAudioFormat() {} | |||
| Array<int> CoreAudioFormat::getPossibleSampleRates() { return Array<int>(); } | |||
| Array<int> CoreAudioFormat::getPossibleBitDepths() { return Array<int>(); } | |||
| bool CoreAudioFormat::canDoStereo() { return true; } | |||
| bool CoreAudioFormat::canDoMono() { return true; } | |||
| //============================================================================== | |||
| AudioFormatReader* CoreAudioFormat::createReaderFor (InputStream* sourceStream, | |||
| bool deleteStreamIfOpeningFails) | |||
| { | |||
| ScopedPointer<CoreAudioReader> r (new CoreAudioReader (sourceStream)); | |||
| if (r->ok) | |||
| return r.release(); | |||
| if (! deleteStreamIfOpeningFails) | |||
| r->input = nullptr; | |||
| return nullptr; | |||
| } | |||
| AudioFormatWriter* CoreAudioFormat::createWriterFor (OutputStream*, | |||
| double /*sampleRateToUse*/, | |||
| unsigned int /*numberOfChannels*/, | |||
| int /*bitsPerSample*/, | |||
| const StringPairArray& /*metadataValues*/, | |||
| int /*qualityOptionIndex*/) | |||
| { | |||
| jassertfalse; // not yet implemented! | |||
| return nullptr; | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,77 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_MAC || JUCE_IOS || DOXYGEN | |||
| //============================================================================== | |||
| /** | |||
| OSX and iOS only - This uses the AudioToolbox framework to read any audio | |||
| format that the system has a codec for. | |||
| This should be able to understand formats such as mp3, m4a, etc. | |||
| @see AudioFormat | |||
| */ | |||
| class JUCE_API CoreAudioFormat : public AudioFormat | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| /** Creates a format object. */ | |||
| CoreAudioFormat(); | |||
| /** Destructor. */ | |||
| ~CoreAudioFormat(); | |||
| //============================================================================== | |||
| /** Metadata property name used when reading a caf file with a MIDI chunk. */ | |||
| static const char* const midiDataBase64; | |||
| /** Metadata property name used when reading a caf file with tempo information. */ | |||
| static const char* const tempo; | |||
| /** Metadata property name used when reading a caf file time signature information. */ | |||
| static const char* const timeSig; | |||
| /** Metadata property name used when reading a caf file time signature information. */ | |||
| static const char* const keySig; | |||
| //============================================================================== | |||
| Array<int> getPossibleSampleRates() override; | |||
| Array<int> getPossibleBitDepths() override; | |||
| bool canDoStereo() override; | |||
| bool canDoMono() override; | |||
| //============================================================================== | |||
| AudioFormatReader* createReaderFor (InputStream*, | |||
| bool deleteStreamIfOpeningFails) override; | |||
| AudioFormatWriter* createWriterFor (OutputStream*, | |||
| double sampleRateToUse, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& metadataValues, | |||
| int qualityOptionIndex) override; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (CoreAudioFormat) | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,545 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_USE_FLAC | |||
| namespace FlacNamespace | |||
| { | |||
| #if JUCE_INCLUDE_FLAC_CODE || ! defined (JUCE_INCLUDE_FLAC_CODE) | |||
| #undef VERSION | |||
| #define VERSION "1.2.1" | |||
| #define FLAC__NO_DLL 1 | |||
| #if JUCE_MSVC | |||
| #pragma warning (disable: 4267 4127 4244 4996 4100 4701 4702 4013 4133 4206 4312 4505 4365 4005 4334 181 111) | |||
| #endif | |||
| #if JUCE_MAC | |||
| #define FLAC__SYS_DARWIN 1 | |||
| #endif | |||
| #ifndef SIZE_MAX | |||
| #define SIZE_MAX 0xffffffff | |||
| #endif | |||
| #if JUCE_CLANG | |||
| #pragma clang diagnostic push | |||
| #pragma clang diagnostic ignored "-Wconversion" | |||
| #pragma clang diagnostic ignored "-Wshadow" | |||
| #endif | |||
| #define __STDC_LIMIT_MACROS 1 | |||
| #include "flac/all.h" | |||
| #include "flac/libFLAC/bitmath.c" | |||
| #include "flac/libFLAC/bitreader.c" | |||
| #include "flac/libFLAC/bitwriter.c" | |||
| #include "flac/libFLAC/cpu.c" | |||
| #include "flac/libFLAC/crc.c" | |||
| #include "flac/libFLAC/fixed.c" | |||
| #include "flac/libFLAC/float.c" | |||
| #include "flac/libFLAC/format.c" | |||
| #include "flac/libFLAC/lpc_flac.c" | |||
| #include "flac/libFLAC/md5.c" | |||
| #include "flac/libFLAC/memory.c" | |||
| #include "flac/libFLAC/stream_decoder.c" | |||
| #include "flac/libFLAC/stream_encoder.c" | |||
| #include "flac/libFLAC/stream_encoder_framing.c" | |||
| #include "flac/libFLAC/window_flac.c" | |||
| #undef VERSION | |||
| #else | |||
| #include <FLAC/all.h> | |||
| #endif | |||
| #if JUCE_CLANG | |||
| #pragma clang diagnostic pop | |||
| #endif | |||
| } | |||
| #undef max | |||
| #undef min | |||
| //============================================================================== | |||
| static const char* const flacFormatName = "FLAC file"; | |||
| static const char* const flacExtensions[] = { ".flac", 0 }; | |||
| //============================================================================== | |||
| class FlacReader : public AudioFormatReader | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| FlacReader (InputStream* const in) | |||
| : AudioFormatReader (in, TRANS (flacFormatName)), | |||
| reservoir (2, 0), | |||
| reservoirStart (0), | |||
| samplesInReservoir (0), | |||
| scanningForLength (false) | |||
| { | |||
| using namespace FlacNamespace; | |||
| lengthInSamples = 0; | |||
| decoder = FLAC__stream_decoder_new(); | |||
| ok = FLAC__stream_decoder_init_stream (decoder, | |||
| readCallback_, seekCallback_, tellCallback_, lengthCallback_, | |||
| eofCallback_, writeCallback_, metadataCallback_, errorCallback_, | |||
| this) == FLAC__STREAM_DECODER_INIT_STATUS_OK; | |||
| if (ok) | |||
| { | |||
| FLAC__stream_decoder_process_until_end_of_metadata (decoder); | |||
| if (lengthInSamples == 0 && sampleRate > 0) | |||
| { | |||
| // the length hasn't been stored in the metadata, so we'll need to | |||
| // work it out the length the hard way, by scanning the whole file.. | |||
| scanningForLength = true; | |||
| FLAC__stream_decoder_process_until_end_of_stream (decoder); | |||
| scanningForLength = false; | |||
| const int64 tempLength = lengthInSamples; | |||
| FLAC__stream_decoder_reset (decoder); | |||
| FLAC__stream_decoder_process_until_end_of_metadata (decoder); | |||
| lengthInSamples = tempLength; | |||
| } | |||
| } | |||
| } | |||
| ~FlacReader() | |||
| { | |||
| FlacNamespace::FLAC__stream_decoder_delete (decoder); | |||
| } | |||
| void useMetadata (const FlacNamespace::FLAC__StreamMetadata_StreamInfo& info) | |||
| { | |||
| sampleRate = info.sample_rate; | |||
| bitsPerSample = info.bits_per_sample; | |||
| lengthInSamples = (unsigned int) info.total_samples; | |||
| numChannels = info.channels; | |||
| reservoir.setSize ((int) numChannels, 2 * (int) info.max_blocksize, false, false, true); | |||
| } | |||
| // returns the number of samples read | |||
| bool readSamples (int** destSamples, int numDestChannels, int startOffsetInDestBuffer, | |||
| int64 startSampleInFile, int numSamples) override | |||
| { | |||
| using namespace FlacNamespace; | |||
| if (! ok) | |||
| return false; | |||
| while (numSamples > 0) | |||
| { | |||
| if (startSampleInFile >= reservoirStart | |||
| && startSampleInFile < reservoirStart + samplesInReservoir) | |||
| { | |||
| const int num = (int) jmin ((int64) numSamples, | |||
| reservoirStart + samplesInReservoir - startSampleInFile); | |||
| jassert (num > 0); | |||
| for (int i = jmin (numDestChannels, reservoir.getNumChannels()); --i >= 0;) | |||
| if (destSamples[i] != nullptr) | |||
| memcpy (destSamples[i] + startOffsetInDestBuffer, | |||
| reservoir.getSampleData (i, (int) (startSampleInFile - reservoirStart)), | |||
| sizeof (int) * (size_t) num); | |||
| startOffsetInDestBuffer += num; | |||
| startSampleInFile += num; | |||
| numSamples -= num; | |||
| } | |||
| else | |||
| { | |||
| if (startSampleInFile >= (int) lengthInSamples) | |||
| { | |||
| samplesInReservoir = 0; | |||
| } | |||
| else if (startSampleInFile < reservoirStart | |||
| || startSampleInFile > reservoirStart + jmax (samplesInReservoir, 511)) | |||
| { | |||
| // had some problems with flac crashing if the read pos is aligned more | |||
| // accurately than this. Probably fixed in newer versions of the library, though. | |||
| reservoirStart = (int) (startSampleInFile & ~511); | |||
| samplesInReservoir = 0; | |||
| FLAC__stream_decoder_seek_absolute (decoder, (FLAC__uint64) reservoirStart); | |||
| } | |||
| else | |||
| { | |||
| reservoirStart += samplesInReservoir; | |||
| samplesInReservoir = 0; | |||
| FLAC__stream_decoder_process_single (decoder); | |||
| } | |||
| if (samplesInReservoir == 0) | |||
| break; | |||
| } | |||
| } | |||
| if (numSamples > 0) | |||
| { | |||
| for (int i = numDestChannels; --i >= 0;) | |||
| if (destSamples[i] != nullptr) | |||
| zeromem (destSamples[i] + startOffsetInDestBuffer, sizeof (int) * (size_t) numSamples); | |||
| } | |||
| return true; | |||
| } | |||
| void useSamples (const FlacNamespace::FLAC__int32* const buffer[], int numSamples) | |||
| { | |||
| if (scanningForLength) | |||
| { | |||
| lengthInSamples += numSamples; | |||
| } | |||
| else | |||
| { | |||
| if (numSamples > reservoir.getNumSamples()) | |||
| reservoir.setSize ((int) numChannels, numSamples, false, false, true); | |||
| const unsigned int bitsToShift = 32 - bitsPerSample; | |||
| for (int i = 0; i < (int) numChannels; ++i) | |||
| { | |||
| const FlacNamespace::FLAC__int32* src = buffer[i]; | |||
| int n = i; | |||
| while (src == 0 && n > 0) | |||
| src = buffer [--n]; | |||
| if (src != nullptr) | |||
| { | |||
| int* const dest = reinterpret_cast<int*> (reservoir.getSampleData(i)); | |||
| for (int j = 0; j < numSamples; ++j) | |||
| dest[j] = src[j] << bitsToShift; | |||
| } | |||
| } | |||
| samplesInReservoir = numSamples; | |||
| } | |||
| } | |||
| //============================================================================== | |||
| static FlacNamespace::FLAC__StreamDecoderReadStatus readCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__byte buffer[], size_t* bytes, void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| *bytes = (size_t) static_cast<const FlacReader*> (client_data)->input->read (buffer, (int) *bytes); | |||
| return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; | |||
| } | |||
| static FlacNamespace::FLAC__StreamDecoderSeekStatus seekCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64 absolute_byte_offset, void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| static_cast<const FlacReader*> (client_data)->input->setPosition ((int) absolute_byte_offset); | |||
| return FLAC__STREAM_DECODER_SEEK_STATUS_OK; | |||
| } | |||
| static FlacNamespace::FLAC__StreamDecoderTellStatus tellCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| *absolute_byte_offset = (uint64) static_cast<const FlacReader*> (client_data)->input->getPosition(); | |||
| return FLAC__STREAM_DECODER_TELL_STATUS_OK; | |||
| } | |||
| static FlacNamespace::FLAC__StreamDecoderLengthStatus lengthCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__uint64* stream_length, void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| *stream_length = (uint64) static_cast<const FlacReader*> (client_data)->input->getTotalLength(); | |||
| return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; | |||
| } | |||
| static FlacNamespace::FLAC__bool eofCallback_ (const FlacNamespace::FLAC__StreamDecoder*, void* client_data) | |||
| { | |||
| return static_cast<const FlacReader*> (client_data)->input->isExhausted(); | |||
| } | |||
| static FlacNamespace::FLAC__StreamDecoderWriteStatus writeCallback_ (const FlacNamespace::FLAC__StreamDecoder*, | |||
| const FlacNamespace::FLAC__Frame* frame, | |||
| const FlacNamespace::FLAC__int32* const buffer[], | |||
| void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| static_cast<FlacReader*> (client_data)->useSamples (buffer, (int) frame->header.blocksize); | |||
| return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; | |||
| } | |||
| static void metadataCallback_ (const FlacNamespace::FLAC__StreamDecoder*, | |||
| const FlacNamespace::FLAC__StreamMetadata* metadata, | |||
| void* client_data) | |||
| { | |||
| static_cast<FlacReader*> (client_data)->useMetadata (metadata->data.stream_info); | |||
| } | |||
| static void errorCallback_ (const FlacNamespace::FLAC__StreamDecoder*, FlacNamespace::FLAC__StreamDecoderErrorStatus, void*) | |||
| { | |||
| } | |||
| private: | |||
| FlacNamespace::FLAC__StreamDecoder* decoder; | |||
| AudioSampleBuffer reservoir; | |||
| int reservoirStart, samplesInReservoir; | |||
| bool ok, scanningForLength; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacReader) | |||
| }; | |||
| //============================================================================== | |||
| class FlacWriter : public AudioFormatWriter | |||
| { | |||
| public: | |||
| FlacWriter (OutputStream* const out, double rate, uint32 numChans, uint32 bits, int qualityOptionIndex) | |||
| : AudioFormatWriter (out, TRANS (flacFormatName), rate, numChans, bits) | |||
| { | |||
| using namespace FlacNamespace; | |||
| encoder = FLAC__stream_encoder_new(); | |||
| if (qualityOptionIndex > 0) | |||
| FLAC__stream_encoder_set_compression_level (encoder, (uint32) jmin (8, qualityOptionIndex)); | |||
| FLAC__stream_encoder_set_do_mid_side_stereo (encoder, numChannels == 2); | |||
| FLAC__stream_encoder_set_loose_mid_side_stereo (encoder, numChannels == 2); | |||
| FLAC__stream_encoder_set_channels (encoder, numChannels); | |||
| FLAC__stream_encoder_set_bits_per_sample (encoder, jmin ((unsigned int) 24, bitsPerSample)); | |||
| FLAC__stream_encoder_set_sample_rate (encoder, (unsigned int) sampleRate); | |||
| FLAC__stream_encoder_set_blocksize (encoder, 0); | |||
| FLAC__stream_encoder_set_do_escape_coding (encoder, true); | |||
| ok = FLAC__stream_encoder_init_stream (encoder, | |||
| encodeWriteCallback, encodeSeekCallback, | |||
| encodeTellCallback, encodeMetadataCallback, | |||
| this) == FLAC__STREAM_ENCODER_INIT_STATUS_OK; | |||
| } | |||
| ~FlacWriter() | |||
| { | |||
| if (ok) | |||
| { | |||
| FlacNamespace::FLAC__stream_encoder_finish (encoder); | |||
| output->flush(); | |||
| } | |||
| else | |||
| { | |||
| output = nullptr; // to stop the base class deleting this, as it needs to be returned | |||
| // to the caller of createWriter() | |||
| } | |||
| FlacNamespace::FLAC__stream_encoder_delete (encoder); | |||
| } | |||
| //============================================================================== | |||
| bool write (const int** samplesToWrite, int numSamples) override | |||
| { | |||
| using namespace FlacNamespace; | |||
| if (! ok) | |||
| return false; | |||
| HeapBlock<int*> channels; | |||
| HeapBlock<int> temp; | |||
| const int bitsToShift = 32 - (int) bitsPerSample; | |||
| if (bitsToShift > 0) | |||
| { | |||
| temp.malloc (numChannels * (size_t) numSamples); | |||
| channels.calloc (numChannels + 1); | |||
| for (unsigned int i = 0; i < numChannels; ++i) | |||
| { | |||
| if (samplesToWrite[i] == nullptr) | |||
| break; | |||
| int* const destData = temp.getData() + i * (size_t) numSamples; | |||
| channels[i] = destData; | |||
| for (int j = 0; j < numSamples; ++j) | |||
| destData[j] = (samplesToWrite[i][j] >> bitsToShift); | |||
| } | |||
| samplesToWrite = const_cast<const int**> (channels.getData()); | |||
| } | |||
| return FLAC__stream_encoder_process (encoder, (const FLAC__int32**) samplesToWrite, (size_t) numSamples) != 0; | |||
| } | |||
| bool writeData (const void* const data, const int size) const | |||
| { | |||
| return output->write (data, (size_t) size); | |||
| } | |||
| static void packUint32 (FlacNamespace::FLAC__uint32 val, FlacNamespace::FLAC__byte* b, const int bytes) | |||
| { | |||
| b += bytes; | |||
| for (int i = 0; i < bytes; ++i) | |||
| { | |||
| *(--b) = (FlacNamespace::FLAC__byte) (val & 0xff); | |||
| val >>= 8; | |||
| } | |||
| } | |||
| void writeMetaData (const FlacNamespace::FLAC__StreamMetadata* metadata) | |||
| { | |||
| using namespace FlacNamespace; | |||
| const FLAC__StreamMetadata_StreamInfo& info = metadata->data.stream_info; | |||
| unsigned char buffer [FLAC__STREAM_METADATA_STREAMINFO_LENGTH]; | |||
| const unsigned int channelsMinus1 = info.channels - 1; | |||
| const unsigned int bitsMinus1 = info.bits_per_sample - 1; | |||
| packUint32 (info.min_blocksize, buffer, 2); | |||
| packUint32 (info.max_blocksize, buffer + 2, 2); | |||
| packUint32 (info.min_framesize, buffer + 4, 3); | |||
| packUint32 (info.max_framesize, buffer + 7, 3); | |||
| buffer[10] = (uint8) ((info.sample_rate >> 12) & 0xff); | |||
| buffer[11] = (uint8) ((info.sample_rate >> 4) & 0xff); | |||
| buffer[12] = (uint8) (((info.sample_rate & 0x0f) << 4) | (channelsMinus1 << 1) | (bitsMinus1 >> 4)); | |||
| buffer[13] = (FLAC__byte) (((bitsMinus1 & 0x0f) << 4) | (unsigned int) ((info.total_samples >> 32) & 0x0f)); | |||
| packUint32 ((FLAC__uint32) info.total_samples, buffer + 14, 4); | |||
| memcpy (buffer + 18, info.md5sum, 16); | |||
| const bool seekOk = output->setPosition (4); | |||
| (void) seekOk; | |||
| // if this fails, you've given it an output stream that can't seek! It needs | |||
| // to be able to seek back to write the header | |||
| jassert (seekOk); | |||
| output->writeIntBigEndian (FLAC__STREAM_METADATA_STREAMINFO_LENGTH); | |||
| output->write (buffer, FLAC__STREAM_METADATA_STREAMINFO_LENGTH); | |||
| } | |||
| //============================================================================== | |||
| static FlacNamespace::FLAC__StreamEncoderWriteStatus encodeWriteCallback (const FlacNamespace::FLAC__StreamEncoder*, | |||
| const FlacNamespace::FLAC__byte buffer[], | |||
| size_t bytes, | |||
| unsigned int /*samples*/, | |||
| unsigned int /*current_frame*/, | |||
| void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| return static_cast<FlacWriter*> (client_data)->writeData (buffer, (int) bytes) | |||
| ? FLAC__STREAM_ENCODER_WRITE_STATUS_OK | |||
| : FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; | |||
| } | |||
| static FlacNamespace::FLAC__StreamEncoderSeekStatus encodeSeekCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64, void*) | |||
| { | |||
| using namespace FlacNamespace; | |||
| return FLAC__STREAM_ENCODER_SEEK_STATUS_UNSUPPORTED; | |||
| } | |||
| static FlacNamespace::FLAC__StreamEncoderTellStatus encodeTellCallback (const FlacNamespace::FLAC__StreamEncoder*, FlacNamespace::FLAC__uint64* absolute_byte_offset, void* client_data) | |||
| { | |||
| using namespace FlacNamespace; | |||
| if (client_data == nullptr) | |||
| return FLAC__STREAM_ENCODER_TELL_STATUS_UNSUPPORTED; | |||
| *absolute_byte_offset = (FLAC__uint64) static_cast<FlacWriter*> (client_data)->output->getPosition(); | |||
| return FLAC__STREAM_ENCODER_TELL_STATUS_OK; | |||
| } | |||
| static void encodeMetadataCallback (const FlacNamespace::FLAC__StreamEncoder*, const FlacNamespace::FLAC__StreamMetadata* metadata, void* client_data) | |||
| { | |||
| static_cast<FlacWriter*> (client_data)->writeMetaData (metadata); | |||
| } | |||
| bool ok; | |||
| private: | |||
| FlacNamespace::FLAC__StreamEncoder* encoder; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacWriter) | |||
| }; | |||
| //============================================================================== | |||
| FlacAudioFormat::FlacAudioFormat() | |||
| : AudioFormat (TRANS (flacFormatName), StringArray (flacExtensions)) | |||
| { | |||
| } | |||
| FlacAudioFormat::~FlacAudioFormat() | |||
| { | |||
| } | |||
| Array<int> FlacAudioFormat::getPossibleSampleRates() | |||
| { | |||
| const int rates[] = { 8000, 11025, 12000, 16000, 22050, 32000, 44100, 48000, | |||
| 88200, 96000, 176400, 192000, 352800, 384000 }; | |||
| return Array<int> (rates, numElementsInArray (rates)); | |||
| } | |||
| Array<int> FlacAudioFormat::getPossibleBitDepths() | |||
| { | |||
| const int depths[] = { 16, 24 }; | |||
| return Array<int> (depths, numElementsInArray (depths)); | |||
| } | |||
| bool FlacAudioFormat::canDoStereo() { return true; } | |||
| bool FlacAudioFormat::canDoMono() { return true; } | |||
| bool FlacAudioFormat::isCompressed() { return true; } | |||
| AudioFormatReader* FlacAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) | |||
| { | |||
| ScopedPointer<FlacReader> r (new FlacReader (in)); | |||
| if (r->sampleRate > 0) | |||
| return r.release(); | |||
| if (! deleteStreamIfOpeningFails) | |||
| r->input = nullptr; | |||
| return nullptr; | |||
| } | |||
| AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, | |||
| double sampleRate, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& /*metadataValues*/, | |||
| int qualityOptionIndex) | |||
| { | |||
| if (getPossibleBitDepths().contains (bitsPerSample)) | |||
| { | |||
| ScopedPointer<FlacWriter> w (new FlacWriter (out, sampleRate, numberOfChannels, | |||
| (uint32) bitsPerSample, qualityOptionIndex)); | |||
| if (w->ok) | |||
| return w.release(); | |||
| } | |||
| return nullptr; | |||
| } | |||
| StringArray FlacAudioFormat::getQualityOptions() | |||
| { | |||
| const char* options[] = { "0 (Fastest)", "1", "2", "3", "4", "5 (Default)","6", "7", "8 (Highest quality)", 0 }; | |||
| return StringArray (options); | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,65 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_USE_FLAC || defined (DOXYGEN) | |||
| //============================================================================== | |||
| /** | |||
| Reads and writes the lossless-compression FLAC audio format. | |||
| To compile this, you'll need to set the JUCE_USE_FLAC flag. | |||
| @see AudioFormat | |||
| */ | |||
| class JUCE_API FlacAudioFormat : public AudioFormat | |||
| { | |||
| public: | |||
| //============================================================================== | |||
| FlacAudioFormat(); | |||
| ~FlacAudioFormat(); | |||
| //============================================================================== | |||
| Array<int> getPossibleSampleRates() override; | |||
| Array<int> getPossibleBitDepths() override; | |||
| bool canDoStereo() override; | |||
| bool canDoMono() override; | |||
| bool isCompressed() override; | |||
| StringArray getQualityOptions() override; | |||
| //============================================================================== | |||
| AudioFormatReader* createReaderFor (InputStream* sourceStream, | |||
| bool deleteStreamIfOpeningFails) override; | |||
| AudioFormatWriter* createWriterFor (OutputStream* streamToWriteTo, | |||
| double sampleRateToUse, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& metadataValues, | |||
| int qualityOptionIndex) override; | |||
| private: | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FlacAudioFormat) | |||
| }; | |||
| #endif | |||
| @@ -0,0 +1,227 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_USE_LAME_AUDIO_FORMAT | |||
| class LAMEEncoderAudioFormat::Writer : public AudioFormatWriter | |||
| { | |||
| public: | |||
| Writer (OutputStream* destStream, const String& formatName, | |||
| const File& lameApp, int vbr, int cbr, | |||
| double sampleRate, unsigned int numberOfChannels, | |||
| unsigned int bitsPerSample, const StringPairArray& metadata) | |||
| : AudioFormatWriter (destStream, formatName, sampleRate, | |||
| numberOfChannels, bitsPerSample), | |||
| vbrLevel (vbr), cbrBitrate (cbr), | |||
| tempWav (".wav") | |||
| { | |||
| WavAudioFormat wavFormat; | |||
| if (FileOutputStream* out = tempWav.getFile().createOutputStream()) | |||
| { | |||
| writer = wavFormat.createWriterFor (out, sampleRate, numChannels, | |||
| bitsPerSample, metadata, 0); | |||
| args.add (lameApp.getFullPathName()); | |||
| args.add ("--quiet"); | |||
| if (cbrBitrate == 0) | |||
| { | |||
| args.add ("--vbr-new"); | |||
| args.add ("-V"); | |||
| args.add (String (vbrLevel)); | |||
| } | |||
| else | |||
| { | |||
| args.add ("--cbr"); | |||
| args.add ("-b"); | |||
| args.add (String (cbrBitrate)); | |||
| } | |||
| addMetadataArg (metadata, "id3title", "--tt"); | |||
| addMetadataArg (metadata, "id3artist", "--ta"); | |||
| addMetadataArg (metadata, "id3album", "--tl"); | |||
| addMetadataArg (metadata, "id3comment", "--tc"); | |||
| addMetadataArg (metadata, "id3date", "--ty"); | |||
| addMetadataArg (metadata, "id3genre", "--tg"); | |||
| addMetadataArg (metadata, "id3trackNumber", "--tn"); | |||
| } | |||
| } | |||
| void addMetadataArg (const StringPairArray& metadata, const char* key, const char* lameFlag) | |||
| { | |||
| const String value (metadata.getValue (key, String::empty)); | |||
| if (value.isNotEmpty()) | |||
| { | |||
| args.add (lameFlag); | |||
| args.add (value); | |||
| } | |||
| } | |||
| ~Writer() | |||
| { | |||
| if (writer != nullptr) | |||
| { | |||
| writer = nullptr; | |||
| if (! convertToMP3()) | |||
| convertToMP3(); // try again | |||
| } | |||
| } | |||
| bool write (const int** samplesToWrite, int numSamples) | |||
| { | |||
| return writer != nullptr && writer->write (samplesToWrite, numSamples); | |||
| } | |||
| private: | |||
| int vbrLevel, cbrBitrate; | |||
| TemporaryFile tempWav; | |||
| ScopedPointer<AudioFormatWriter> writer; | |||
| StringArray args; | |||
| bool runLameChildProcess (const TemporaryFile& tempMP3, const StringArray& args) const | |||
| { | |||
| ChildProcess cp; | |||
| if (cp.start (args)) | |||
| { | |||
| const String childOutput (cp.readAllProcessOutput()); | |||
| DBG (childOutput); (void) childOutput; | |||
| cp.waitForProcessToFinish (10000); | |||
| return tempMP3.getFile().getSize() > 0; | |||
| } | |||
| return false; | |||
| } | |||
| bool convertToMP3() const | |||
| { | |||
| TemporaryFile tempMP3 (".mp3"); | |||
| StringArray args2 (args); | |||
| args2.add (tempWav.getFile().getFullPathName()); | |||
| args2.add (tempMP3.getFile().getFullPathName()); | |||
| DBG (args2.joinIntoString (" ")); | |||
| if (runLameChildProcess (tempMP3, args2)) | |||
| { | |||
| FileInputStream fis (tempMP3.getFile()); | |||
| if (fis.openedOk() && output->writeFromInputStream (fis, -1) > 0) | |||
| { | |||
| output->flush(); | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Writer) | |||
| }; | |||
| //============================================================================== | |||
| static const char* const lameFormatName = "MP3 file"; | |||
| static const char* const lameExtensions[] = { ".mp3", nullptr }; | |||
| LAMEEncoderAudioFormat::LAMEEncoderAudioFormat (const File& lameApplication) | |||
| : AudioFormat (TRANS (lameFormatName), StringArray (lameExtensions)), | |||
| lameApp (lameApplication) | |||
| { | |||
| } | |||
| LAMEEncoderAudioFormat::~LAMEEncoderAudioFormat() | |||
| { | |||
| } | |||
| bool LAMEEncoderAudioFormat::canHandleFile (const File&) | |||
| { | |||
| return false; | |||
| } | |||
| Array<int> LAMEEncoderAudioFormat::getPossibleSampleRates() | |||
| { | |||
| const int rates[] = { 32000, 44100, 48000, 0 }; | |||
| return Array <int> (rates); | |||
| } | |||
| Array<int> LAMEEncoderAudioFormat::getPossibleBitDepths() | |||
| { | |||
| const int depths[] = { 16, 0 }; | |||
| return Array <int> (depths); | |||
| } | |||
| bool LAMEEncoderAudioFormat::canDoStereo() { return true; } | |||
| bool LAMEEncoderAudioFormat::canDoMono() { return true; } | |||
| bool LAMEEncoderAudioFormat::isCompressed() { return true; } | |||
| StringArray LAMEEncoderAudioFormat::getQualityOptions() | |||
| { | |||
| const char* vbrOptions[] = { "VBR quality 0 (best)", "VBR quality 1", "VBR quality 2", "VBR quality 3", | |||
| "VBR quality 4 (normal)", "VBR quality 5", "VBR quality 6", "VBR quality 7", "VBR quality 8", | |||
| "VBR quality 9 (smallest)", | |||
| nullptr }; | |||
| StringArray opts (vbrOptions); | |||
| const int cbrRates[] = { 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320 }; | |||
| for (int i = 0; i < numElementsInArray (cbrRates); ++i) | |||
| opts.add (String (cbrRates[i]) + " Kb/s CBR"); | |||
| return opts; | |||
| } | |||
| AudioFormatReader* LAMEEncoderAudioFormat::createReaderFor (InputStream*, const bool) | |||
| { | |||
| return nullptr; | |||
| } | |||
| AudioFormatWriter* LAMEEncoderAudioFormat::createWriterFor (OutputStream* streamToWriteTo, | |||
| double sampleRateToUse, | |||
| unsigned int numberOfChannels, | |||
| int bitsPerSample, | |||
| const StringPairArray& metadataValues, | |||
| int qualityOptionIndex) | |||
| { | |||
| int vbr = 4; | |||
| int cbr = 0; | |||
| const String qual (getQualityOptions() [qualityOptionIndex]); | |||
| if (qual.contains ("VBR")) | |||
| vbr = qual.retainCharacters ("0123456789").getIntValue(); | |||
| else | |||
| cbr = qual.getIntValue(); | |||
| return new Writer (streamToWriteTo, getFormatName(), lameApp, vbr, cbr, | |||
| sampleRateToUse, numberOfChannels, bitsPerSample, metadataValues); | |||
| } | |||
| #endif | |||
| @@ -0,0 +1,71 @@ | |||
| /* | |||
| ============================================================================== | |||
| This file is part of the JUCE library. | |||
| Copyright (c) 2013 - Raw Material Software Ltd. | |||
| Permission is granted to use this software under the terms of either: | |||
| a) the GPL v2 (or any later version) | |||
| b) the Affero GPL v3 | |||
| Details of these licenses can be found at: www.gnu.org/licenses | |||
| 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. | |||
| ------------------------------------------------------------------------------ | |||
| To release a closed-source product which uses JUCE, commercial licenses are | |||
| available: visit www.juce.com for more information. | |||
| ============================================================================== | |||
| */ | |||
| #if JUCE_USE_LAME_AUDIO_FORMAT || defined (DOXYGEN) | |||
| //============================================================================== | |||
| /** | |||
| An AudioFormat class which can use an installed version of the LAME mp3 | |||
| encoder to encode a file. | |||
| This format can't read MP3s, it just writes them. Internally, the | |||
| AudioFormatWriter object that is returned writes the incoming audio data | |||
| to a temporary WAV file, and then when the writer is deleted, it invokes | |||
| the LAME executable to convert the data to an MP3, whose data is then | |||
| piped into the original OutputStream that was used when first creating | |||
| the writer. | |||
| @see AudioFormat | |||
| */ | |||
| class JUCE_API LAMEEncoderAudioFormat : public AudioFormat | |||
| { | |||
| public: | |||
| /** Creates a LAMEEncoderAudioFormat that expects to find a working LAME | |||
| executable at the location given. | |||
| */ | |||
| LAMEEncoderAudioFormat (const File& lameExecutableToUse); | |||
| ~LAMEEncoderAudioFormat(); | |||
| bool canHandleFile (const File&); | |||
| Array<int> getPossibleSampleRates(); | |||
| Array<int> getPossibleBitDepths(); | |||
| bool canDoStereo(); | |||
| bool canDoMono(); | |||
| bool isCompressed(); | |||
| StringArray getQualityOptions(); | |||
| AudioFormatReader* createReaderFor (InputStream*, bool deleteStreamIfOpeningFails); | |||
| AudioFormatWriter* createWriterFor (OutputStream*, double sampleRateToUse, | |||
| unsigned int numberOfChannels, int bitsPerSample, | |||
| const StringPairArray& metadataValues, int qualityOptionIndex); | |||
| private: | |||
| File lameApp; | |||
| class Writer; | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LAMEEncoderAudioFormat) | |||
| }; | |||
| #endif | |||