diff --git a/Builds/Linux/Makefile b/Builds/Linux/Makefile index 55d6a5b12b..7f368b7736 100644 --- a/Builds/Linux/Makefile +++ b/Builds/Linux/Makefile @@ -48,6 +48,8 @@ OBJECTS := \ $(OBJDIR)/juce_AudioCDReader_c730f7a6.o \ $(OBJDIR)/juce_AudioFormat_6605d0f9.o \ $(OBJDIR)/juce_AudioFormatManager_949148fe.o \ + $(OBJDIR)/juce_AudioFormatReader_36f0295c.o \ + $(OBJDIR)/juce_AudioFormatWriter_11461d0c.o \ $(OBJDIR)/juce_AudioSubsectionReader_65f61a0a.o \ $(OBJDIR)/juce_AudioThumbnail_cb99b4b4.o \ $(OBJDIR)/juce_AudioThumbnailCache_89a7c678.o \ @@ -92,6 +94,7 @@ OBJECTS := \ $(OBJDIR)/juce_GenericAudioProcessorEditor_2e8ec30d.o \ $(OBJDIR)/juce_Sampler_98f716a4.o \ $(OBJDIR)/juce_Synthesiser_2bffa1dd.o \ + $(OBJDIR)/juce_AbstractFifo_dfc0bd23.o \ $(OBJDIR)/juce_BigInteger_63589133.o \ $(OBJDIR)/juce_DynamicObject_69d02ab3.o \ $(OBJDIR)/juce_Expression_1e9a5aad.o \ @@ -404,6 +407,16 @@ $(OBJDIR)/juce_AudioFormatManager_949148fe.o: ../../src/audio/audio_file_formats @echo "Compiling juce_AudioFormatManager.cpp" @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/juce_AudioFormatReader_36f0295c.o: ../../src/audio/audio_file_formats/juce_AudioFormatReader.cpp + -@mkdir -p $(OBJDIR) + @echo "Compiling juce_AudioFormatReader.cpp" + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + +$(OBJDIR)/juce_AudioFormatWriter_11461d0c.o: ../../src/audio/audio_file_formats/juce_AudioFormatWriter.cpp + -@mkdir -p $(OBJDIR) + @echo "Compiling juce_AudioFormatWriter.cpp" + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + $(OBJDIR)/juce_AudioSubsectionReader_65f61a0a.o: ../../src/audio/audio_file_formats/juce_AudioSubsectionReader.cpp -@mkdir -p $(OBJDIR) @echo "Compiling juce_AudioSubsectionReader.cpp" @@ -624,6 +637,11 @@ $(OBJDIR)/juce_Synthesiser_2bffa1dd.o: ../../src/audio/synthesisers/juce_Synthes @echo "Compiling juce_Synthesiser.cpp" @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" +$(OBJDIR)/juce_AbstractFifo_dfc0bd23.o: ../../src/containers/juce_AbstractFifo.cpp + -@mkdir -p $(OBJDIR) + @echo "Compiling juce_AbstractFifo.cpp" + @$(CXX) $(CXXFLAGS) -o "$@" -c "$<" + $(OBJDIR)/juce_BigInteger_63589133.o: ../../src/containers/juce_BigInteger.cpp -@mkdir -p $(OBJDIR) @echo "Compiling juce_BigInteger.cpp" diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj index 83a14bbac3..e402b7e09a 100644 --- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj +++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 983FCD60625A60993546F850 = { isa = PBXBuildFile; fileRef = 0877D5750D6F21C5231687CA; }; 416D6F00E88DC74879B4DF2B = { isa = PBXBuildFile; fileRef = 7D85530D76756C33795ECCE9; }; 9C709BC2F4F0EE60BF52FACA = { isa = PBXBuildFile; fileRef = 93006D32B18174D9FE0A5E9E; }; + 992F46189ABF711A047186A4 = { isa = PBXBuildFile; fileRef = 9349E14552FEA0371553E808; }; + 5C312E6678456C8293633E0F = { isa = PBXBuildFile; fileRef = 2AD64F53E12B20011B7A0DB7; }; FB21B7E6A7CE55D3C0E3C37E = { isa = PBXBuildFile; fileRef = 59597FA0A88A08937801D198; }; C1147D03F1F4D697CC30DD22 = { isa = PBXBuildFile; fileRef = 27C3C51DF2519B519B76E2EE; }; C5CFF5508299C26380465290 = { isa = PBXBuildFile; fileRef = CB32D4EE59D5CA9DB12F944D; }; @@ -61,6 +63,7 @@ D1407BB28C169F5E1CAC3CC7 = { isa = PBXBuildFile; fileRef = 096CF2243648F17E1BF5421B; }; 07E6E11A658930554FF0C56A = { isa = PBXBuildFile; fileRef = ED5966B95F865C586A3CE08F; }; E8DFABC1603D55B97429A8E4 = { isa = PBXBuildFile; fileRef = 35668D8EEA19957C6C9AC83A; }; + 1F905F44E5FA23A2D5CCDA0A = { isa = PBXBuildFile; fileRef = 4F22276689685D839BD252EA; }; BE25871C34D79FEFFD1B94B6 = { isa = PBXBuildFile; fileRef = 895D742F49DA9F100990879C; }; 4AB5E55BDF79028F82F83D8E = { isa = PBXBuildFile; fileRef = F77C9170829579FABA5679AD; }; 25018C91F79D918FEA084630 = { isa = PBXBuildFile; fileRef = 199DFD1C5A282FE13A585FEA; }; @@ -359,7 +362,9 @@ 013E8938EE1C6B4F63016B55 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormat.h; path = ../../src/audio/audio_file_formats/juce_AudioFormat.h; sourceTree = SOURCE_ROOT; }; 93006D32B18174D9FE0A5E9E = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioFormatManager.cpp; path = ../../src/audio/audio_file_formats/juce_AudioFormatManager.cpp; sourceTree = SOURCE_ROOT; }; 41070806F82EC9C6D1C67689 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormatManager.h; path = ../../src/audio/audio_file_formats/juce_AudioFormatManager.h; sourceTree = SOURCE_ROOT; }; + 9349E14552FEA0371553E808 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioFormatReader.cpp; path = ../../src/audio/audio_file_formats/juce_AudioFormatReader.cpp; sourceTree = SOURCE_ROOT; }; 27356F5E93CEA4D472D83D8E = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormatReader.h; path = ../../src/audio/audio_file_formats/juce_AudioFormatReader.h; sourceTree = SOURCE_ROOT; }; + 2AD64F53E12B20011B7A0DB7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioFormatWriter.cpp; path = ../../src/audio/audio_file_formats/juce_AudioFormatWriter.cpp; sourceTree = SOURCE_ROOT; }; 8BD38C2507C0F8E28930A4F8 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormatWriter.h; path = ../../src/audio/audio_file_formats/juce_AudioFormatWriter.h; sourceTree = SOURCE_ROOT; }; 59597FA0A88A08937801D198 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioSubsectionReader.cpp; path = ../../src/audio/audio_file_formats/juce_AudioSubsectionReader.cpp; sourceTree = SOURCE_ROOT; }; AE7F7F0D959C2E3CF5989C88 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioSubsectionReader.h; path = ../../src/audio/audio_file_formats/juce_AudioSubsectionReader.h; sourceTree = SOURCE_ROOT; }; @@ -477,6 +482,8 @@ 6C6C1C360138D9BD4B27588B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Sampler.h; path = ../../src/audio/synthesisers/juce_Sampler.h; sourceTree = SOURCE_ROOT; }; 35668D8EEA19957C6C9AC83A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Synthesiser.cpp; path = ../../src/audio/synthesisers/juce_Synthesiser.cpp; sourceTree = SOURCE_ROOT; }; 9E6C206F95245BCDE38FB2B5 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Synthesiser.h; path = ../../src/audio/synthesisers/juce_Synthesiser.h; sourceTree = SOURCE_ROOT; }; + 4F22276689685D839BD252EA = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AbstractFifo.cpp; path = ../../src/containers/juce_AbstractFifo.cpp; sourceTree = SOURCE_ROOT; }; + 9584B84F23A4251755D49213 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AbstractFifo.h; path = ../../src/containers/juce_AbstractFifo.h; sourceTree = SOURCE_ROOT; }; 839BE8047CF2F8EBE43ED34F = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Array.h; path = ../../src/containers/juce_Array.h; sourceTree = SOURCE_ROOT; }; EDF52FDF87ACD33FE933142C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ArrayAllocationBase.h; path = ../../src/containers/juce_ArrayAllocationBase.h; sourceTree = SOURCE_ROOT; }; 895D742F49DA9F100990879C = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_BigInteger.cpp; path = ../../src/containers/juce_BigInteger.cpp; sourceTree = SOURCE_ROOT; }; @@ -1048,7 +1055,9 @@ 013E8938EE1C6B4F63016B55, 93006D32B18174D9FE0A5E9E, 41070806F82EC9C6D1C67689, + 9349E14552FEA0371553E808, 27356F5E93CEA4D472D83D8E, + 2AD64F53E12B20011B7A0DB7, 8BD38C2507C0F8E28930A4F8, 59597FA0A88A08937801D198, AE7F7F0D959C2E3CF5989C88, @@ -1195,6 +1204,8 @@ 231431F8B23F01DC6ECD4214, DC641C77950A335A20FD4532 ); name = audio; sourceTree = ""; }; 1CC2889DB696E12FC34E3F50 = { isa = PBXGroup; children = ( + 4F22276689685D839BD252EA, + 9584B84F23A4251755D49213, 839BE8047CF2F8EBE43ED34F, EDF52FDF87ACD33FE933142C, 895D742F49DA9F100990879C, @@ -1915,6 +1926,8 @@ 983FCD60625A60993546F850, 416D6F00E88DC74879B4DF2B, 9C709BC2F4F0EE60BF52FACA, + 992F46189ABF711A047186A4, + 5C312E6678456C8293633E0F, FB21B7E6A7CE55D3C0E3C37E, C1147D03F1F4D697CC30DD22, C5CFF5508299C26380465290, @@ -1961,6 +1974,7 @@ D1407BB28C169F5E1CAC3CC7, 07E6E11A658930554FF0C56A, E8DFABC1603D55B97429A8E4, + 1F905F44E5FA23A2D5CCDA0A, BE25871C34D79FEFFD1B94B6, 4AB5E55BDF79028F82F83D8E, 25018C91F79D918FEA084630, diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj index 383ca444b4..7e5cb5bb0e 100644 --- a/Builds/VisualStudio2005/Juce.vcproj +++ b/Builds/VisualStudio2005/Juce.vcproj @@ -124,7 +124,9 @@ + + @@ -343,6 +345,8 @@ + + diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj index 49322d242b..7876fcd26a 100644 --- a/Builds/VisualStudio2008/Juce.vcproj +++ b/Builds/VisualStudio2008/Juce.vcproj @@ -124,7 +124,9 @@ + + @@ -343,6 +345,8 @@ + + diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj index 17a74e0e72..a01cbaea76 100644 --- a/Builds/VisualStudio2008_DLL/Juce.vcproj +++ b/Builds/VisualStudio2008_DLL/Juce.vcproj @@ -126,7 +126,9 @@ + + @@ -345,6 +347,8 @@ + + diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj index 2682d9d6d0..2742358082 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj +++ b/Builds/VisualStudio2010/Juce.vcxproj @@ -131,6 +131,8 @@ + + @@ -175,6 +177,7 @@ + @@ -500,6 +503,7 @@ + diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters index 6cfafa2d97..c0a7c452f4 100644 --- a/Builds/VisualStudio2010/Juce.vcxproj.filters +++ b/Builds/VisualStudio2010/Juce.vcxproj.filters @@ -202,6 +202,12 @@ Juce\Source\audio\audio_file_formats + + Juce\Source\audio\audio_file_formats + + + Juce\Source\audio\audio_file_formats + Juce\Source\audio\audio_file_formats @@ -373,6 +379,9 @@ Juce\Source\audio\synthesisers + + Juce\Source\containers + Juce\Source\containers @@ -1422,6 +1431,9 @@ Juce\Source\audio\synthesisers + + Juce\Source\containers + Juce\Source\containers diff --git a/Builds/iPhone/Juce.xcodeproj/project.pbxproj b/Builds/iPhone/Juce.xcodeproj/project.pbxproj index 08475319b3..6bac4f1f8f 100644 --- a/Builds/iPhone/Juce.xcodeproj/project.pbxproj +++ b/Builds/iPhone/Juce.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 983FCD60625A60993546F850 = { isa = PBXBuildFile; fileRef = 0877D5750D6F21C5231687CA; }; 416D6F00E88DC74879B4DF2B = { isa = PBXBuildFile; fileRef = 7D85530D76756C33795ECCE9; }; 9C709BC2F4F0EE60BF52FACA = { isa = PBXBuildFile; fileRef = 93006D32B18174D9FE0A5E9E; }; + 992F46189ABF711A047186A4 = { isa = PBXBuildFile; fileRef = 9349E14552FEA0371553E808; }; + 5C312E6678456C8293633E0F = { isa = PBXBuildFile; fileRef = 2AD64F53E12B20011B7A0DB7; }; FB21B7E6A7CE55D3C0E3C37E = { isa = PBXBuildFile; fileRef = 59597FA0A88A08937801D198; }; C1147D03F1F4D697CC30DD22 = { isa = PBXBuildFile; fileRef = 27C3C51DF2519B519B76E2EE; }; C5CFF5508299C26380465290 = { isa = PBXBuildFile; fileRef = CB32D4EE59D5CA9DB12F944D; }; @@ -61,6 +63,7 @@ D1407BB28C169F5E1CAC3CC7 = { isa = PBXBuildFile; fileRef = 096CF2243648F17E1BF5421B; }; 07E6E11A658930554FF0C56A = { isa = PBXBuildFile; fileRef = ED5966B95F865C586A3CE08F; }; E8DFABC1603D55B97429A8E4 = { isa = PBXBuildFile; fileRef = 35668D8EEA19957C6C9AC83A; }; + 1F905F44E5FA23A2D5CCDA0A = { isa = PBXBuildFile; fileRef = 4F22276689685D839BD252EA; }; BE25871C34D79FEFFD1B94B6 = { isa = PBXBuildFile; fileRef = 895D742F49DA9F100990879C; }; 4AB5E55BDF79028F82F83D8E = { isa = PBXBuildFile; fileRef = F77C9170829579FABA5679AD; }; 25018C91F79D918FEA084630 = { isa = PBXBuildFile; fileRef = 199DFD1C5A282FE13A585FEA; }; @@ -359,7 +362,9 @@ 013E8938EE1C6B4F63016B55 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormat.h; path = ../../src/audio/audio_file_formats/juce_AudioFormat.h; sourceTree = SOURCE_ROOT; }; 93006D32B18174D9FE0A5E9E = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioFormatManager.cpp; path = ../../src/audio/audio_file_formats/juce_AudioFormatManager.cpp; sourceTree = SOURCE_ROOT; }; 41070806F82EC9C6D1C67689 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormatManager.h; path = ../../src/audio/audio_file_formats/juce_AudioFormatManager.h; sourceTree = SOURCE_ROOT; }; + 9349E14552FEA0371553E808 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioFormatReader.cpp; path = ../../src/audio/audio_file_formats/juce_AudioFormatReader.cpp; sourceTree = SOURCE_ROOT; }; 27356F5E93CEA4D472D83D8E = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormatReader.h; path = ../../src/audio/audio_file_formats/juce_AudioFormatReader.h; sourceTree = SOURCE_ROOT; }; + 2AD64F53E12B20011B7A0DB7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioFormatWriter.cpp; path = ../../src/audio/audio_file_formats/juce_AudioFormatWriter.cpp; sourceTree = SOURCE_ROOT; }; 8BD38C2507C0F8E28930A4F8 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioFormatWriter.h; path = ../../src/audio/audio_file_formats/juce_AudioFormatWriter.h; sourceTree = SOURCE_ROOT; }; 59597FA0A88A08937801D198 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AudioSubsectionReader.cpp; path = ../../src/audio/audio_file_formats/juce_AudioSubsectionReader.cpp; sourceTree = SOURCE_ROOT; }; AE7F7F0D959C2E3CF5989C88 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AudioSubsectionReader.h; path = ../../src/audio/audio_file_formats/juce_AudioSubsectionReader.h; sourceTree = SOURCE_ROOT; }; @@ -477,6 +482,8 @@ 6C6C1C360138D9BD4B27588B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Sampler.h; path = ../../src/audio/synthesisers/juce_Sampler.h; sourceTree = SOURCE_ROOT; }; 35668D8EEA19957C6C9AC83A = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_Synthesiser.cpp; path = ../../src/audio/synthesisers/juce_Synthesiser.cpp; sourceTree = SOURCE_ROOT; }; 9E6C206F95245BCDE38FB2B5 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Synthesiser.h; path = ../../src/audio/synthesisers/juce_Synthesiser.h; sourceTree = SOURCE_ROOT; }; + 4F22276689685D839BD252EA = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_AbstractFifo.cpp; path = ../../src/containers/juce_AbstractFifo.cpp; sourceTree = SOURCE_ROOT; }; + 9584B84F23A4251755D49213 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_AbstractFifo.h; path = ../../src/containers/juce_AbstractFifo.h; sourceTree = SOURCE_ROOT; }; 839BE8047CF2F8EBE43ED34F = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_Array.h; path = ../../src/containers/juce_Array.h; sourceTree = SOURCE_ROOT; }; EDF52FDF87ACD33FE933142C = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_ArrayAllocationBase.h; path = ../../src/containers/juce_ArrayAllocationBase.h; sourceTree = SOURCE_ROOT; }; 895D742F49DA9F100990879C = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_BigInteger.cpp; path = ../../src/containers/juce_BigInteger.cpp; sourceTree = SOURCE_ROOT; }; @@ -1048,7 +1055,9 @@ 013E8938EE1C6B4F63016B55, 93006D32B18174D9FE0A5E9E, 41070806F82EC9C6D1C67689, + 9349E14552FEA0371553E808, 27356F5E93CEA4D472D83D8E, + 2AD64F53E12B20011B7A0DB7, 8BD38C2507C0F8E28930A4F8, 59597FA0A88A08937801D198, AE7F7F0D959C2E3CF5989C88, @@ -1195,6 +1204,8 @@ 231431F8B23F01DC6ECD4214, DC641C77950A335A20FD4532 ); name = audio; sourceTree = ""; }; 1CC2889DB696E12FC34E3F50 = { isa = PBXGroup; children = ( + 4F22276689685D839BD252EA, + 9584B84F23A4251755D49213, 839BE8047CF2F8EBE43ED34F, EDF52FDF87ACD33FE933142C, 895D742F49DA9F100990879C, @@ -1915,6 +1926,8 @@ 983FCD60625A60993546F850, 416D6F00E88DC74879B4DF2B, 9C709BC2F4F0EE60BF52FACA, + 992F46189ABF711A047186A4, + 5C312E6678456C8293633E0F, FB21B7E6A7CE55D3C0E3C37E, C1147D03F1F4D697CC30DD22, C5CFF5508299C26380465290, @@ -1961,6 +1974,7 @@ D1407BB28C169F5E1CAC3CC7, 07E6E11A658930554FF0C56A, E8DFABC1603D55B97429A8E4, + 1F905F44E5FA23A2D5CCDA0A, BE25871C34D79FEFFD1B94B6, 4AB5E55BDF79028F82F83D8E, 25018C91F79D918FEA084630, diff --git a/Juce.jucer b/Juce.jucer index 847b302025..78be8b2aa1 100644 --- a/Juce.jucer +++ b/Juce.jucer @@ -81,8 +81,12 @@ resource="0" file="src/audio/audio_file_formats/juce_AudioFormatManager.cpp"/> + + + + diff --git a/amalgamation/juce_amalgamated_template.cpp b/amalgamation/juce_amalgamated_template.cpp index 68d0fa47ba..629258a633 100644 --- a/amalgamation/juce_amalgamated_template.cpp +++ b/amalgamation/juce_amalgamated_template.cpp @@ -103,6 +103,7 @@ #include "../src/core/juce_SystemStats.cpp" #include "../src/core/juce_Time.cpp" #include "../src/core/juce_Initialisation.cpp" + #include "../src/containers/juce_AbstractFifo.cpp" #include "../src/containers/juce_BigInteger.cpp" #include "../src/containers/juce_MemoryBlock.cpp" #include "../src/containers/juce_PropertySet.cpp" @@ -164,6 +165,8 @@ #include "../src/utilities/juce_UndoManager.cpp" #include "../src/audio/audio_file_formats/juce_AiffAudioFormat.cpp" #include "../src/audio/audio_file_formats/juce_AudioFormat.cpp" + #include "../src/audio/audio_file_formats/juce_AudioFormatReader.cpp" + #include "../src/audio/audio_file_formats/juce_AudioFormatWriter.cpp" #include "../src/audio/audio_file_formats/juce_AudioFormatManager.cpp" #include "../src/audio/audio_file_formats/juce_AudioSubsectionReader.cpp" #include "../src/audio/audio_file_formats/juce_AudioThumbnail.cpp" diff --git a/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h b/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h index e8bde516bd..f57f6a2696 100644 --- a/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h +++ b/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h @@ -505,7 +505,7 @@ private: s.add ("ZERO_LINK = NO"); if (! isRTAS()) // (dwarf seems to be incompatible with the RTAS libs) - s.add ("DEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\""); + s.add ("DEBUG_INFORMATION_FORMAT = \"dwarf\""); s.add ("PRODUCT_NAME = \"" + config.getTargetBinaryName().toString() + "\""); return s; diff --git a/extras/juce demo/Source/demos/AudioDemoRecordPage.cpp b/extras/juce demo/Source/demos/AudioDemoRecordPage.cpp index 1109e55d01..ecadf7ad77 100644 --- a/extras/juce demo/Source/demos/AudioDemoRecordPage.cpp +++ b/extras/juce demo/Source/demos/AudioDemoRecordPage.cpp @@ -27,112 +27,19 @@ //[MiscUserDefs] You can add your own user definitions and misc code here... + //============================================================================== -/* This is a rough-and-ready circular buffer, used to allow the audio thread to - push data quickly into a queue, allowing a background thread to come along and - write it to disk later. +/** A simple class that acts as an AudioIODeviceCallback and writes the + incoming audio data to a WAV file. */ -class CircularAudioBuffer -{ -public: - CircularAudioBuffer (const int numChannels, const int numSamples) - : buffer (numChannels, numSamples) - { - clear(); - } - - ~CircularAudioBuffer() - { - } - - void clear() - { - buffer.clear(); - - const ScopedLock sl (bufferLock); - bufferValidStart = bufferValidEnd = 0; - } - - void addSamplesToBuffer (const AudioSampleBuffer& sourceBuffer, int numSamples) - { - const int bufferSize = buffer.getNumSamples(); - - bufferLock.enter(); - int newDataStart = bufferValidEnd; - int newDataEnd = newDataStart + numSamples; - const int actualNewDataEnd = newDataEnd; - bufferValidStart = jmax (bufferValidStart, newDataEnd - bufferSize); - bufferLock.exit(); - - newDataStart %= bufferSize; - newDataEnd %= bufferSize; - - if (newDataEnd < newDataStart) - { - for (int i = jmin (buffer.getNumChannels(), sourceBuffer.getNumChannels()); --i >= 0;) - { - buffer.copyFrom (i, newDataStart, sourceBuffer, i, 0, bufferSize - newDataStart); - buffer.copyFrom (i, 0, sourceBuffer, i, bufferSize - newDataStart, newDataEnd); - } - } - else - { - for (int i = jmin (buffer.getNumChannels(), sourceBuffer.getNumChannels()); --i >= 0;) - buffer.copyFrom (i, newDataStart, sourceBuffer, i, 0, newDataEnd - newDataStart); - } - - const ScopedLock sl (bufferLock); - bufferValidEnd = actualNewDataEnd; - } - - int readSamplesFromBuffer (AudioSampleBuffer& destBuffer, int numSamples) - { - const int bufferSize = buffer.getNumSamples(); - - bufferLock.enter(); - int availableDataStart = bufferValidStart; - const int numSamplesDone = jmin (numSamples, bufferValidEnd - availableDataStart); - int availableDataEnd = availableDataStart + numSamplesDone; - bufferValidStart = availableDataEnd; - bufferLock.exit(); - - availableDataStart %= bufferSize; - availableDataEnd %= bufferSize; - - if (availableDataEnd < availableDataStart) - { - for (int i = jmin (buffer.getNumChannels(), destBuffer.getNumChannels()); --i >= 0;) - { - destBuffer.copyFrom (i, 0, buffer, i, availableDataStart, bufferSize - availableDataStart); - destBuffer.copyFrom (i, bufferSize - availableDataStart, buffer, i, 0, availableDataEnd); - } - } - else - { - for (int i = jmin (buffer.getNumChannels(), destBuffer.getNumChannels()); --i >= 0;) - destBuffer.copyFrom (i, 0, buffer, i, availableDataStart, numSamplesDone); - } - - return numSamplesDone; - } - -private: - CriticalSection bufferLock; - AudioSampleBuffer buffer; - int bufferValidStart, bufferValidEnd; -}; - -//============================================================================== -class AudioRecorder : public AudioIODeviceCallback, - public Thread +class AudioRecorder : public AudioIODeviceCallback { public: AudioRecorder() - : Thread ("audio recorder"), - circularBuffer (2, 48000), - recording (false), - sampleRate (0) + : backgroundThread ("Audio Recorder Thread"), + sampleRate (0), activeWriter (0) { + backgroundThread.startThread(); } ~AudioRecorder() @@ -147,24 +54,49 @@ public: if (sampleRate > 0) { - fileToRecord = file; - startThread(); + // Create an OutputStream to write to our destination file... + file.deleteFile(); + ScopedPointer fileStream (file.createOutputStream()); - circularBuffer.clear(); - recording = true; + if (fileStream != 0) + { + // Now create a WAV writer object that writes to our output stream... + WavAudioFormat wavFormat; + AudioFormatWriter* writer = wavFormat.createWriterFor (fileStream, sampleRate, 1, 16, StringPairArray(), 0); + + if (writer != 0) + { + fileStream.release(); // (passes responsibility for deleting the stream to the writer object that is now using it) + + // Now we'll create one of these helper objects which will act as a FIFO buffer, and will + // write the data to disk on our background thread. + threadedWriter = new AudioFormatWriter::ThreadedWriter (writer, backgroundThread, 32768); + + // And now, swap over our active writer pointer so that the audio callback will start using it.. + const ScopedLock sl (writerLock); + activeWriter = threadedWriter; + } + } } } void stop() { - recording = false; + // First, clear this pointer to stop the audio callback from using our writer object.. + { + const ScopedLock sl (writerLock); + activeWriter = 0; + } - stopThread (5000); + // Now we can delete the writer object. It's done in this order because the deletion could + // take a little time while remaining data gets flushed to disk, so it's best to avoid blocking + // the audio callback while this happens. + threadedWriter = 0; } bool isRecording() const { - return isThreadRunning() && recording; + return activeWriter != 0; } //============================================================================== @@ -182,11 +114,10 @@ public: float** outputChannelData, int numOutputChannels, int numSamples) { - if (recording) - { - const AudioSampleBuffer incomingData ((float**) inputChannelData, numInputChannels, numSamples); - circularBuffer.addSamplesToBuffer (incomingData, numSamples); - } + const ScopedLock sl (writerLock); + + if (activeWriter != 0) + activeWriter->write (inputChannelData, numSamples); // We need to clear the output buffers, in case they're full of junk.. for (int i = 0; i < numOutputChannels; ++i) @@ -194,44 +125,13 @@ public: zeromem (outputChannelData[i], sizeof (float) * numSamples); } - //============================================================================== - void run() - { - fileToRecord.deleteFile(); - - OutputStream* outStream = fileToRecord.createOutputStream(); - if (outStream == 0) - return; - - WavAudioFormat wavFormat; - AudioFormatWriter* writer = wavFormat.createWriterFor (outStream, sampleRate, 1, 16, StringPairArray(), 0); - - if (writer == 0) - { - delete outStream; - return; - } - - AudioSampleBuffer tempBuffer (2, 8192); - - while (! threadShouldExit()) - { - int numSamplesReady = circularBuffer.readSamplesFromBuffer (tempBuffer, tempBuffer.getNumSamples()); - - if (numSamplesReady > 0) - tempBuffer.writeToAudioWriter (writer, 0, numSamplesReady); - - Thread::sleep (1); - } - - delete writer; - } - - File fileToRecord; +private: + TimeSliceThread backgroundThread; // the thread that will write our audio data to disk + ScopedPointer threadedWriter; // the FIFO used to buffer the incoming data double sampleRate; - bool recording; - CircularAudioBuffer circularBuffer; + CriticalSection writerLock; + AudioFormatWriter::ThreadedWriter* volatile activeWriter; }; //[/MiscUserDefs] @@ -275,10 +175,9 @@ AudioDemoRecordPage::AudioDemoRecordPage (AudioDeviceManager& deviceManager_) AudioDemoRecordPage::~AudioDemoRecordPage() { //[Destructor_pre]. You can add your own custom destruction code here.. - recorder->stop(); deviceManager.removeAudioCallback (recorder); - delete recorder; deviceManager.removeAudioCallback (liveAudioDisplayComp); + recorder = 0; //[/Destructor_pre] deleteAndZero (liveAudioDisplayComp); diff --git a/extras/juce demo/Source/demos/AudioDemoRecordPage.h b/extras/juce demo/Source/demos/AudioDemoRecordPage.h index 7d1d1fb706..f31b6e17da 100644 --- a/extras/juce demo/Source/demos/AudioDemoRecordPage.h +++ b/extras/juce demo/Source/demos/AudioDemoRecordPage.h @@ -62,7 +62,7 @@ public: private: //[UserVariables] -- You can add your own custom variables in this section. AudioDeviceManager& deviceManager; - AudioRecorder* recorder; + ScopedPointer recorder; //[/UserVariables] //============================================================================== diff --git a/juce_amalgamated.cpp b/juce_amalgamated.cpp index 966faa9b45..20fd65f3e8 100644 --- a/juce_amalgamated.cpp +++ b/juce_amalgamated.cpp @@ -2407,6 +2407,100 @@ END_JUCE_NAMESPACE /*** End of inlined file: juce_Initialisation.cpp ***/ +/*** Start of inlined file: juce_AbstractFifo.cpp ***/ +BEGIN_JUCE_NAMESPACE + +AbstractFifo::AbstractFifo (const int capacity) throw() + : bufferSize (capacity) +{ + jassert (bufferSize > 0); +} + +AbstractFifo::~AbstractFifo() {} + +int AbstractFifo::getTotalSize() const throw() { return bufferSize; } +int AbstractFifo::getFreeSpace() const throw() { return bufferSize - getNumReady(); } +int AbstractFifo::getNumReady() const throw() { return validEnd.get() - validStart.get(); } + +void AbstractFifo::reset() throw() +{ + validEnd = 0; + validStart = 0; +} + +void AbstractFifo::setTotalSize (int newSize) throw() +{ + jassert (newSize > 0); + reset(); + bufferSize = newSize; +} + +void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw() +{ + const int vs = validStart.get(); + const int ve = validEnd.get(); + + const int freeSpace = bufferSize - (ve - vs); + numToWrite = jmin (numToWrite, freeSpace); + + if (numToWrite <= 0) + { + startIndex1 = 0; + startIndex2 = 0; + blockSize1 = 0; + blockSize2 = 0; + } + else + { + startIndex1 = (int) (ve % bufferSize); + startIndex2 = 0; + blockSize1 = jmin (bufferSize - startIndex1, numToWrite); + numToWrite -= blockSize1; + blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, (int) (vs % bufferSize)); + } +} + +void AbstractFifo::finishedWrite (int numWritten) throw() +{ + jassert (numWritten >= 0 && numWritten < bufferSize); + validEnd += numWritten; +} + +void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw() +{ + const int vs = validStart.get(); + const int ve = validEnd.get(); + + const int numReady = ve - vs; + numWanted = jmin (numWanted, numReady); + + if (numWanted <= 0) + { + startIndex1 = 0; + startIndex2 = 0; + blockSize1 = 0; + blockSize2 = 0; + } + else + { + startIndex1 = (int) (vs % bufferSize); + startIndex2 = 0; + blockSize1 = jmin (bufferSize - startIndex1, numWanted); + numWanted -= blockSize1; + blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, (int) (ve % bufferSize)); + } +} + +void AbstractFifo::finishedRead (int numRead) throw() +{ + jassert (numRead >= 0 && numRead < bufferSize); + validStart += numRead; +} + +END_JUCE_NAMESPACE +/*** End of inlined file: juce_AbstractFifo.cpp ***/ + + /*** Start of inlined file: juce_BigInteger.cpp ***/ BEGIN_JUCE_NAMESPACE @@ -20755,7 +20849,7 @@ public: { switch (bitsPerSample) { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 32: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; @@ -20766,7 +20860,7 @@ public: { switch (bitsPerSample) { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 32: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; @@ -21007,6 +21101,37 @@ END_JUCE_NAMESPACE /*** Start of inlined file: juce_AudioFormat.cpp ***/ BEGIN_JUCE_NAMESPACE +AudioFormat::AudioFormat (const String& name, const StringArray& extensions) + : formatName (name), + fileExtensions (extensions) +{ +} + +AudioFormat::~AudioFormat() +{ +} + +bool AudioFormat::canHandleFile (const File& f) +{ + for (int i = 0; i < fileExtensions.size(); ++i) + if (f.hasFileExtension (fileExtensions[i])) + return true; + + return false; +} + +const String& AudioFormat::getFormatName() const { return formatName; } +const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; } +bool AudioFormat::isCompressed() { return false; } +const StringArray AudioFormat::getQualityOptions() { return StringArray(); } + +END_JUCE_NAMESPACE +/*** End of inlined file: juce_AudioFormat.cpp ***/ + + +/*** Start of inlined file: juce_AudioFormatReader.cpp ***/ +BEGIN_JUCE_NAMESPACE + AudioFormatReader::AudioFormatReader (InputStream* const in, const String& formatName_) : sampleRate (0), @@ -21338,6 +21463,13 @@ int64 AudioFormatReader::searchForLevel (int64 startSample, return -1; } +END_JUCE_NAMESPACE +/*** End of inlined file: juce_AudioFormatReader.cpp ***/ + + +/*** Start of inlined file: juce_AudioFormatWriter.cpp ***/ +BEGIN_JUCE_NAMESPACE + AudioFormatWriter::AudioFormatWriter (OutputStream* const out, const String& formatName_, const double rate, @@ -21424,16 +21556,9 @@ bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, return true; } -bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, - int numSamplesToRead, - const int samplesPerBlock) +bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock) { AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); - int* buffers [128]; - zerostruct (buffers); - - for (int i = tempBuffer.getNumChannels(); --i >= 0;) - buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); while (numSamplesToRead > 0) { @@ -21447,30 +21572,7 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, source.getNextAudioBlock (info); - if (! isFloatingPoint()) - { - int** bufferChan = buffers; - - while (*bufferChan != 0) - { - int* b = *bufferChan++; - - // float -> int - for (int j = numToDo; --j >= 0;) - { - const double samp = *(const float*) b; - - if (samp <= -1.0) - *b++ = std::numeric_limits::min(); - else if (samp >= 1.0) - *b++ = std::numeric_limits::max(); - else - *b++ = roundToInt (std::numeric_limits::max() * samp); - } - } - } - - if (! write ((const int**) buffers, numToDo)) + if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo)) return false; numSamplesToRead -= numToDo; @@ -21479,48 +21581,131 @@ bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, return true; } -AudioFormat::AudioFormat (const String& name, - const StringArray& extensions) - : formatName (name), - fileExtensions (extensions) +bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples) { + jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && source.getNumChannels() > 0); + + if (numSamples <= 0) + return true; + + HeapBlock tempBuffer; + HeapBlock chans (numChannels + 1); + chans [numChannels] = 0; + + if (isFloatingPoint()) + { + for (int i = numChannels; --i >= 0;) + chans[i] = reinterpret_cast (source.getSampleData (i, startSample)); + } + else + { + tempBuffer.malloc (numSamples * numChannels); + + for (unsigned int i = 0; i < numChannels; ++i) + { + typedef AudioData::Pointer DestSampleType; + typedef AudioData::Pointer SourceSampleType; + + DestSampleType destData (chans[i] = tempBuffer + i * numSamples); + SourceSampleType sourceData (source.getSampleData (i, startSample)); + destData.convertSamples (sourceData, numSamples); + } + } + + return write ((const int**) chans.getData(), numSamples); } -AudioFormat::~AudioFormat() +class AudioFormatWriter::ThreadedWriter::Buffer : public TimeSliceClient, + public AbstractFifo { -} +public: + Buffer (TimeSliceThread& timeSliceThread_, AudioFormatWriter* writer_, int numChannels, int bufferSize) + : AbstractFifo (bufferSize), + buffer (numChannels, bufferSize), + timeSliceThread (timeSliceThread_), + writer (writer_), isRunning (true) + { + timeSliceThread.addTimeSliceClient (this); + } -const String& AudioFormat::getFormatName() const -{ - return formatName; -} + ~Buffer() + { + isRunning = false; + timeSliceThread.removeTimeSliceClient (this); -const StringArray& AudioFormat::getFileExtensions() const -{ - return fileExtensions; -} + while (useTimeSlice()) + {} + } -bool AudioFormat::canHandleFile (const File& f) -{ - for (int i = 0; i < fileExtensions.size(); ++i) - if (f.hasFileExtension (fileExtensions[i])) + bool write (const float** data, int numSamples) + { + if (numSamples <= 0 || ! isRunning) return true; - return false; + jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this! + + int start1, size1, start2, size2; + prepareToWrite (numSamples, start1, size1, start2, size2); + + if (size1 + size2 < numSamples) + return false; + + for (int i = buffer.getNumChannels(); --i >= 0;) + { + buffer.copyFrom (i, start1, data[i], size1); + buffer.copyFrom (i, start2, data[i] + size1, size2); + } + + finishedWrite (size1 + size2); + timeSliceThread.notify(); + return true; + } + + bool useTimeSlice() + { + const int numToDo = getTotalSize() / 4; + + int start1, size1, start2, size2; + prepareToRead (numToDo, start1, size1, start2, size2); + + if (size1 <= 0) + return false; + + writer->writeFromAudioSampleBuffer (buffer, start1, size1); + + if (size2 > 0) + writer->writeFromAudioSampleBuffer (buffer, start2, size2); + + finishedRead (size1 + size2); + return true; + } + +private: + AudioSampleBuffer buffer; + TimeSliceThread& timeSliceThread; + ScopedPointer writer; + volatile bool isRunning; + + Buffer (const Buffer&); + Buffer& operator= (const Buffer&); +}; + +AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer) + : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, writer->numChannels, numSamplesToBuffer)) +{ } -bool AudioFormat::isCompressed() +AudioFormatWriter::ThreadedWriter::~ThreadedWriter() { - return false; } -const StringArray AudioFormat::getQualityOptions() +bool AudioFormatWriter::ThreadedWriter::write (const float** data, int numSamples) { - return StringArray(); + return buffer->write (data, numSamples); } END_JUCE_NAMESPACE -/*** End of inlined file: juce_AudioFormat.cpp ***/ +/*** End of inlined file: juce_AudioFormatWriter.cpp ***/ /*** Start of inlined file: juce_AudioFormatManager.cpp ***/ @@ -22668,25 +22853,11 @@ QuickTimeAudioFormat::~QuickTimeAudioFormat() { } -const Array QuickTimeAudioFormat::getPossibleSampleRates() -{ - return Array(); -} +const Array QuickTimeAudioFormat::getPossibleSampleRates() { return Array(); } +const Array QuickTimeAudioFormat::getPossibleBitDepths() { return Array(); } -const Array QuickTimeAudioFormat::getPossibleBitDepths() -{ - return Array(); -} - -bool QuickTimeAudioFormat::canDoStereo() -{ - return true; -} - -bool QuickTimeAudioFormat::canDoMono() -{ - return true; -} +bool QuickTimeAudioFormat::canDoStereo() { return true; } +bool QuickTimeAudioFormat::canDoMono() { return true; } AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, const bool deleteStreamIfOpeningFails) @@ -27314,45 +27485,8 @@ void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, const int startSample, const int numSamples) const { - jassert (startSample >= 0 && startSample + numSamples <= size && numChannels > 0); - - if (numSamples > 0) - { - HeapBlock tempBuffer; - HeapBlock chans (numChannels + 1); - chans [numChannels] = 0; - - if (writer->isFloatingPoint()) - { - for (int i = numChannels; --i >= 0;) - chans[i] = reinterpret_cast (channels[i] + startSample); - } - else - { - tempBuffer.malloc (numSamples * numChannels); - - for (int j = 0; j < numChannels; ++j) - { - int* const dest = tempBuffer + j * numSamples; - const float* const src = channels[j] + startSample; - chans[j] = dest; - - for (int i = 0; i < numSamples; ++i) - { - const double samp = src[i]; - - if (samp <= -1.0) - dest[i] = std::numeric_limits::min(); - else if (samp >= 1.0) - dest[i] = std::numeric_limits::max(); - else - dest[i] = roundToInt (std::numeric_limits::max() * samp); - } - } - } - - writer->write ((const int**) chans.getData(), numSamples); - } + jassert (writer != 0); + writer->writeFromAudioSampleBuffer (*this, startSample, numSamples); } END_JUCE_NAMESPACE @@ -69046,8 +69180,6 @@ public: menuCreationTime = lastFocused = lastScroll = Time::getMillisecondCounter(); setWantsKeyboardFocus (true); setMouseClickGrabsKeyboardFocus (false); - - setOpaque (true); setAlwaysOnTop (true); Desktop::getInstance().addGlobalMouseListener (this); @@ -69086,6 +69218,7 @@ public: ScopedPointer mw (new Window()); mw->setLookAndFeel (menu.lookAndFeel); mw->setWantsKeyboardFocus (false); + mw->setOpaque (mw->getLookAndFeel().findColour (PopupMenu::backgroundColourId).isOpaque() || ! Desktop::canUseSemiTransparentWindows()); mw->minimumWidth = minimumWidth; mw->maximumNumColumns = maximumNumColumns; mw->standardItemHeight = standardItemHeight; @@ -69131,6 +69264,9 @@ public: void paint (Graphics& g) { + if (isOpaque()) + g.fillAll (Colours::white); + getLookAndFeel().drawPopupMenuBackground (g, getWidth(), getHeight()); } @@ -128589,18 +128725,17 @@ class FlacWriter : public AudioFormatWriter { public: - FlacWriter (OutputStream* const out, - const double sampleRate_, - const int numChannels_, - const int bitsPerSample_) + FlacWriter (OutputStream* const out, double sampleRate_, + int numChannels_, int bitsPerSample_, int qualityOptionIndex) : AudioFormatWriter (out, TRANS (flacFormatName), - sampleRate_, - numChannels_, - bitsPerSample_) + sampleRate_, numChannels_, bitsPerSample_) { using namespace FlacNamespace; encoder = FLAC__stream_encoder_new(); + if (qualityOptionIndex > 0) + FLAC__stream_encoder_set_compression_level (encoder, 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); @@ -128780,20 +128915,9 @@ const Array FlacAudioFormat::getPossibleBitDepths() return Array (depths); } -bool FlacAudioFormat::canDoStereo() -{ - return true; -} - -bool FlacAudioFormat::canDoMono() -{ - return true; -} - -bool FlacAudioFormat::isCompressed() -{ - return true; -} +bool FlacAudioFormat::canDoStereo() { return true; } +bool FlacAudioFormat::canDoMono() { return true; } +bool FlacAudioFormat::isCompressed() { return true; } AudioFormatReader* FlacAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) @@ -128814,11 +128938,11 @@ AudioFormatWriter* FlacAudioFormat::createWriterFor (OutputStream* out, unsigned int numberOfChannels, int bitsPerSample, const StringPairArray& /*metadataValues*/, - int /*qualityOptionIndex*/) + int qualityOptionIndex) { if (getPossibleBitDepths().contains (bitsPerSample)) { - ScopedPointer w (new FlacWriter (out, sampleRate, numberOfChannels, bitsPerSample)); + ScopedPointer w (new FlacWriter (out, sampleRate, numberOfChannels, bitsPerSample, qualityOptionIndex)); if (w->ok) return w.release(); @@ -187791,20 +187915,13 @@ const Array OggVorbisAudioFormat::getPossibleSampleRates() const Array OggVorbisAudioFormat::getPossibleBitDepths() { - Array depths; - depths.add (32); - return depths; + const int depths[] = { 32, 0 }; + return Array (depths); } -bool OggVorbisAudioFormat::canDoStereo() -{ - return true; -} - -bool OggVorbisAudioFormat::canDoMono() -{ - return true; -} +bool OggVorbisAudioFormat::canDoStereo() { return true; } +bool OggVorbisAudioFormat::canDoMono() { return true; } +bool OggVorbisAudioFormat::isCompressed() { return true; } AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) @@ -187836,11 +187953,6 @@ AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, return w->ok ? w.release() : 0; } -bool OggVorbisAudioFormat::isCompressed() -{ - return true; -} - const StringArray OggVorbisAudioFormat::getQualityOptions() { StringArray s; @@ -242900,6 +243012,7 @@ private: return 0; case WM_WINDOWPOSCHANGED: + doMouseEvent (getCurrentMousePos()); handleMovedOrResized(); if (dontRepaint) diff --git a/juce_amalgamated.h b/juce_amalgamated.h index 1414f2193c..919b3568ea 100644 --- a/juce_amalgamated.h +++ b/juce_amalgamated.h @@ -64,7 +64,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 70 +#define JUCE_BUILDNUMBER 71 /** Current Juce version number. @@ -2761,6 +2761,534 @@ BEGIN_JUCE_NAMESPACE #ifndef __JUCE_JUCE_CORE_INCLUDES_INCLUDEFILES__ #define __JUCE_JUCE_CORE_INCLUDES_INCLUDEFILES__ +#ifndef __JUCE_ABSTRACTFIFO_JUCEHEADER__ + +/*** Start of inlined file: juce_AbstractFifo.h ***/ +#ifndef __JUCE_ABSTRACTFIFO_JUCEHEADER__ +#define __JUCE_ABSTRACTFIFO_JUCEHEADER__ + + +/*** Start of inlined file: juce_Atomic.h ***/ +#ifndef __JUCE_ATOMIC_JUCEHEADER__ +#define __JUCE_ATOMIC_JUCEHEADER__ + +/** + Simple class to hold a primitive value and perform atomic operations on it. + + The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. + There are methods to perform most of the basic atomic operations. +*/ +template +class Atomic +{ +public: + /** Creates a new value, initialised to zero. */ + inline Atomic() throw() + : value (0) + { + } + + /** Creates a new value, with a given initial value. */ + inline Atomic (const Type initialValue) throw() + : value (initialValue) + { + } + + /** Copies another value (atomically). */ + inline Atomic (const Atomic& other) throw() + : value (other.get()) + { + } + + /** Destructor. */ + inline ~Atomic() throw() + { + // This class can only be used for types which are 32 or 64 bits in size. + static_jassert (sizeof (Type) == 4 || sizeof (Type) == 8); + } + + /** Atomically reads and returns the current value. */ + Type get() const throw(); + + /** Copies another value onto this one (atomically). */ + inline Atomic& operator= (const Atomic& other) throw() { exchange (other.get()); return *this; } + + /** Copies another value onto this one (atomically). */ + inline Atomic& operator= (const Type newValue) throw() { exchange (newValue); return *this; } + + /** Atomically sets the current value. */ + void set (Type newValue) throw() { exchange (newValue); } + + /** Atomically sets the current value, returning the value that was replaced. */ + Type exchange (Type value) throw(); + + /** Atomically adds a number to this value, returning the new value. */ + Type operator+= (Type amountToAdd) throw(); + + /** Atomically subtracts a number from this value, returning the new value. */ + Type operator-= (Type amountToSubtract) throw(); + + /** Atomically increments this value, returning the new value. */ + Type operator++() throw(); + + /** Atomically decrements this value, returning the new value. */ + Type operator--() throw(); + + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. + + This operation is the atomic equivalent of doing this: + @code + bool compareAndSetBool (Type newValue, Type valueToCompare) + { + if (get() == valueToCompare) + { + set (newValue); + return true; + } + + return false; + } + @endcode + + @returns true if the comparison was true and the value was replaced; false if + the comparison failed and the value was left unchanged. + @see compareAndSetValue + */ + bool compareAndSetBool (Type newValue, Type valueToCompare) throw(); + + /** Atomically compares this value with a target value, and if it is equal, sets + this to be equal to a new value. + + This operation is the atomic equivalent of doing this: + @code + Type compareAndSetValue (Type newValue, Type valueToCompare) + { + Type oldValue = get(); + if (oldValue == valueToCompare) + set (newValue); + + return oldValue; + } + @endcode + + @returns the old value before it was changed. + @see compareAndSetBool + */ + Type compareAndSetValue (Type newValue, Type valueToCompare) throw(); + + /** Implements a memory read/write barrier. */ + static void memoryBarrier() throw(); + + JUCE_ALIGN(8) + + /** The raw value that this class operates on. + This is exposed publically in case you need to manipulate it directly + for performance reasons. + */ + volatile Type value; + +private: + static inline Type castFrom32Bit (int32 value) throw() { return *(Type*) &value; } + static inline Type castFrom64Bit (int64 value) throw() { return *(Type*) &value; } + static inline int32 castTo32Bit (Type value) throw() { return *(int32*) &value; } + static inline int64 castTo64Bit (Type value) throw() { return *(int64*) &value; } +}; + +/* + The following code is in the header so that the atomics can be inlined where possible... +*/ +#if (JUCE_IOS && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 || ! defined (__IPHONE_3_2))) \ + || (JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) + #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier + + #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + #define JUCE_MAC_ATOMICS_VOLATILE + #else + #define JUCE_MAC_ATOMICS_VOLATILE volatile + #endif + + #if JUCE_PPC || JUCE_IOS + // None of these atomics are available for PPC or for iPhoneOS 3.1 or earlier!! + template static Type OSAtomicAdd64Barrier (Type b, JUCE_MAC_ATOMICS_VOLATILE Type* a) throw() { jassertfalse; return *a += b; } + template static Type OSAtomicIncrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) throw() { jassertfalse; return ++*a; } + template static Type OSAtomicDecrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) throw() { jassertfalse; return --*a; } + template static bool OSAtomicCompareAndSwap64Barrier (Type old, Type newValue, JUCE_MAC_ATOMICS_VOLATILE Type* value) throw() + { jassertfalse; if (old == *value) { *value = newValue; return true; } return false; } + #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 + #endif + +#elif JUCE_GCC + #define JUCE_ATOMICS_GCC 1 // GCC with intrinsics + + #if JUCE_IOS + #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 // (on the iphone, the 64-bit ops will compile but not link) + #endif + +#else + #define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics + + #if JUCE_USE_INTRINSICS || JUCE_64BIT + #pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \ + _InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier) + #define juce_InterlockedExchange(a, b) _InterlockedExchange(a, b) + #define juce_InterlockedIncrement(a) _InterlockedIncrement(a) + #define juce_InterlockedDecrement(a) _InterlockedDecrement(a) + #define juce_InterlockedExchangeAdd(a, b) _InterlockedExchangeAdd(a, b) + #define juce_InterlockedCompareExchange(a, b, c) _InterlockedCompareExchange(a, b, c) + #define juce_InterlockedCompareExchange64(a, b, c) _InterlockedCompareExchange64(a, b, c) + #define juce_MemoryBarrier _ReadWriteBarrier + #else + // (these are defined in juce_win32_Threads.cpp) + long juce_InterlockedExchange (volatile long* a, long b) throw(); + long juce_InterlockedIncrement (volatile long* a) throw(); + long juce_InterlockedDecrement (volatile long* a) throw(); + long juce_InterlockedExchangeAdd (volatile long* a, long b) throw(); + long juce_InterlockedCompareExchange (volatile long* a, long b, long c) throw(); + __int64 juce_InterlockedCompareExchange64 (volatile __int64* a, __int64 b, __int64 c) throw(); + void juce_MemoryBarrier() throw() { long x = 0; juce_InterlockedIncrement (&x); } + #endif + + #if JUCE_64BIT + #pragma intrinsic (_InterlockedExchangeAdd64, _InterlockedExchange64, _InterlockedIncrement64, _InterlockedDecrement64) + #define juce_InterlockedExchangeAdd64(a, b) _InterlockedExchangeAdd64(a, b) + #define juce_InterlockedExchange64(a, b) _InterlockedExchange64(a, b) + #define juce_InterlockedIncrement64(a) _InterlockedIncrement64(a) + #define juce_InterlockedDecrement64(a) _InterlockedDecrement64(a) + #else + // None of these atomics are available in a 32-bit Windows build!! + template static Type juce_InterlockedExchangeAdd64 (volatile Type* a, Type b) throw() { jassertfalse; Type old = *a; *a += b; return old; } + template static Type juce_InterlockedExchange64 (volatile Type* a, Type b) throw() { jassertfalse; Type old = *a; *a = b; return old; } + template static Type juce_InterlockedIncrement64 (volatile Type* a) throw() { jassertfalse; return ++*a; } + template static Type juce_InterlockedDecrement64 (volatile Type* a) throw() { jassertfalse; return --*a; } + #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 + #endif +#endif + +#if JUCE_MSVC + #pragma warning (push) + #pragma warning (disable: 4311) // (truncation warning) +#endif + +template +inline Type Atomic::get() const throw() +{ + #if JUCE_ATOMICS_MAC + return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value)) + : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value)); + #elif JUCE_ATOMICS_WINDOWS + return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchangeAdd ((volatile long*) &value, (long) 0)) + : castFrom64Bit ((int64) juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) 0)); + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) + : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); + #endif +} + +template +inline Type Atomic::exchange (const Type newValue) throw() +{ + #if JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC + Type currentVal = value; + while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } + return currentVal; + #elif JUCE_ATOMICS_WINDOWS + return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchange ((volatile long*) &value, (long) castTo32Bit (newValue))) + : castFrom64Bit ((int64) juce_InterlockedExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue))); + #endif +} + +template +inline Type Atomic::operator+= (const Type amountToAdd) throw() +{ + #if JUCE_ATOMICS_MAC + return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) + : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + #elif JUCE_ATOMICS_WINDOWS + return sizeof (Type) == 4 ? (Type) (juce_InterlockedExchangeAdd ((volatile long*) &value, (long) amountToAdd) + (long) amountToAdd) + : (Type) (juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) amountToAdd) + (__int64) amountToAdd); + #elif JUCE_ATOMICS_GCC + return (Type) __sync_add_and_fetch (&value, amountToAdd); + #endif +} + +template +inline Type Atomic::operator-= (const Type amountToSubtract) throw() +{ + return operator+= (juce_negate (amountToSubtract)); +} + +template +inline Type Atomic::operator++() throw() +{ + #if JUCE_ATOMICS_MAC + return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) + : (Type) OSAtomicIncrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + #elif JUCE_ATOMICS_WINDOWS + return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) + : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); + #elif JUCE_ATOMICS_GCC + return (Type) __sync_add_and_fetch (&value, 1); + #endif +} + +template +inline Type Atomic::operator--() throw() +{ + #if JUCE_ATOMICS_MAC + return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) + : (Type) OSAtomicDecrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + #elif JUCE_ATOMICS_WINDOWS + return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) + : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); + #elif JUCE_ATOMICS_GCC + return (Type) __sync_add_and_fetch (&value, -1); + #endif +} + +template +inline bool Atomic::compareAndSetBool (const Type newValue, const Type valueToCompare) throw() +{ + #if JUCE_ATOMICS_MAC + return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) + : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); + #elif JUCE_ATOMICS_WINDOWS + return compareAndSetValue (newValue, valueToCompare) == valueToCompare; + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) + : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)); + #endif +} + +template +inline Type Atomic::compareAndSetValue (const Type newValue, const Type valueToCompare) throw() +{ + #if JUCE_ATOMICS_MAC + for (;;) // Annoying workaround for OSX only having a bool CAS operation.. + { + if (compareAndSetBool (newValue, valueToCompare)) + return valueToCompare; + + const Type result = value; + if (result != valueToCompare) + return result; + } + + #elif JUCE_ATOMICS_WINDOWS + return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedCompareExchange ((volatile long*) &value, (long) castTo32Bit (newValue), (long) castTo32Bit (valueToCompare))) + : castFrom64Bit ((int64) juce_InterlockedCompareExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue), (__int64) castTo64Bit (valueToCompare))); + #elif JUCE_ATOMICS_GCC + return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) + : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); + #endif +} + +template +inline void Atomic::memoryBarrier() throw() +{ + #if JUCE_ATOMICS_MAC + OSMemoryBarrier(); + #elif JUCE_ATOMICS_GCC + __sync_synchronize(); + #elif JUCE_ATOMICS_WINDOWS + juce_MemoryBarrier(); + #endif +} + +#if JUCE_MSVC + #pragma warning (pop) +#endif + +#endif // __JUCE_ATOMIC_JUCEHEADER__ +/*** End of inlined file: juce_Atomic.h ***/ + +/** + Encapsulates the logic required to implement a lock-free FIFO. + + This class handles the logic needed when building a single-reader, single-writer FIFO. + + It doesn't actually hold any data itself, but your FIFO class can use one of these to manage + its position and status when reading or writing to it. + + To use it, you can call prepareToWrite() to determine the position within your own buffer that + an incoming block of data should be stored, and prepareToRead() to find out when the next + outgoing block should be read from. + + e.g. + @code + class MyFifo + { + public: + MyFifo() : abstractFifo (1024) + { + } + + void addToFifo (const int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToWrite (numItems, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (myBuffer + start1, someData, size1); + + if (size2 > 0) + copySomeData (myBuffer + start2, someData + size1, size2); + + finishedWrite (size1 + size2); + } + + void readFromFifo (int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToRead (numSamples, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (someData, myBuffer + start1, size1); + + if (size2 > 0) + copySomeData (someData + size1, myBuffer + start2, size2); + + finishedRead (size1 + size2); + } + + private: + AbstractFifo abstractFifo; + int myBuffer [1024]; + }; + @endcode +*/ +class JUCE_API AbstractFifo +{ +public: + + /** Creates a FIFO to manage a buffer with the specified capacity. */ + AbstractFifo (int capacity) throw(); + + /** Destructor */ + ~AbstractFifo(); + + /** Returns the total size of the buffer being managed. */ + int getTotalSize() const throw(); + + /** Returns the number of items that can currently be added to the buffer without it overflowing. */ + int getFreeSpace() const throw(); + + /** Returns the number of items that can currently be read from the buffer. */ + int getNumReady() const throw(); + + /** Clears the buffer positions, so that it appears empty. */ + void reset() throw(); + + /** Changes the buffer's total size. + Note that this isn't thread-safe, so don't call it if there's any danger that it + might overlap with a call to any other method in this class! + */ + void setTotalSize (int newSize) throw(); + + /** Returns the location within the buffer at which an incoming block of data should be written. + + Because the section of data that you want to add to the buffer may overlap the end + and wrap around to the start, two blocks within your buffer are returned, and you + should copy your data into the first one, with any remaining data spilling over into + the second. + + If the number of items you ask for is too large to fit within the buffer's free space, then + blockSize1 + blockSize2 may add up to a lower value than numToWrite. + + After calling this method, and writing your data, you must call finishedWrite() to tell the + FIFO how much data you actually added. + + e.g. + @code + void addToFifo (const int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToWrite (numItems, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (myBuffer + start1, someData, size1); + + if (size2 > 0) + copySomeData (myBuffer + start2, someData + size1, size2); + + finishedWrite (size1 + size2); + } + @endcode + + @param numToWrite indicates how many items you'd like to add to the buffer + @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 + @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into + the first block should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex2 + @see finishedWrite + */ + void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw(); + + /** Called after reading from the FIFO, to indicate that this many items have been added. + @see prepareToWrite + */ + void finishedWrite (int numWritten) throw(); + + /** Returns the location within the buffer from which the next block of data should be read. + + Because the section of data that you want to read from the buffer may overlap the end + and wrap around to the start, two blocks within your buffer are returned, and you + should read from both of them. + + If the number of items you ask for is greater than the amount of data available, then + blockSize1 + blockSize2 may add up to a lower value than numWanted. + + After calling this method, and reading the data, you must call finishedRead() to tell the + FIFO how much data you have consumed. + + e.g. + @code + void readFromFifo (int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToRead (numSamples, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (someData, myBuffer + start1, size1); + + if (size2 > 0) + copySomeData (someData + size1, myBuffer + start2, size2); + + finishedRead (size1 + size2); + } + @endcode + + @param numToWrite indicates how many items you'd like to add to the buffer + @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 + @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into + the first block should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex2 + @see finishedRead + */ + void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw(); + + /** Called after reading from the FIFO, to indicate that this many items have now been consumed. + @see prepareToRead + */ + void finishedRead (int numRead) throw(); + + juce_UseDebuggingNewOperator + +private: + int bufferSize; + Atomic validStart, validEnd; + + AbstractFifo (const AbstractFifo&); + AbstractFifo& operator= (const AbstractFifo&); +}; + +#endif // __JUCE_ABSTRACTFIFO_JUCEHEADER__ +/*** End of inlined file: juce_AbstractFifo.h ***/ + + +#endif #ifndef __JUCE_ARRAY_JUCEHEADER__ /*** Start of inlined file: juce_Array.h ***/ @@ -3471,15 +3999,15 @@ private: /*** End of inlined file: juce_CriticalSection.h ***/ /** - Holds a list of simple objects, such as ints, doubles, or pointers. + Holds a resizable array of primitive or copy-by-value objects. Examples of arrays are: Array, Array or Array - The array can be used to hold simple, non-polymorphic objects as well as primitive types - to + The Array class can be used to hold simple, non-polymorphic objects as well as primitive types - to do so, the class must fulfil these requirements: - - it must have a copy constructor and operator= - - it must be able to be relocated in memory by a memcpy without this causing a problem - so no - objects whose functionality relies on pointers or references to themselves can be used. + - it must have a copy constructor and assignment operator + - it must be able to be relocated in memory by a memcpy without this causing any problems - so + objects whose functionality relies on external pointers or references to themselves can be used. You can of course have an array of pointers to any kind of object, e.g. Array , but if you do this, the array doesn't take any ownership of the objects - see the OwnedArray class or the @@ -5757,341 +6285,6 @@ private: #ifndef __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ #define __JUCE_REFERENCECOUNTEDOBJECT_JUCEHEADER__ - -/*** Start of inlined file: juce_Atomic.h ***/ -#ifndef __JUCE_ATOMIC_JUCEHEADER__ -#define __JUCE_ATOMIC_JUCEHEADER__ - -/** - Simple class to hold a primitive value and perform atomic operations on it. - - The type used must be a 32 or 64 bit primitive, like an int, pointer, etc. - There are methods to perform most of the basic atomic operations. -*/ -template -class Atomic -{ -public: - /** Creates a new value, initialised to zero. */ - inline Atomic() throw() - : value (0) - { - } - - /** Creates a new value, with a given initial value. */ - inline Atomic (const Type initialValue) throw() - : value (initialValue) - { - } - - /** Copies another value (atomically). */ - inline Atomic (const Atomic& other) throw() - : value (other.get()) - { - } - - /** Destructor. */ - inline ~Atomic() throw() - { - // This class can only be used for types which are 32 or 64 bits in size. - static_jassert (sizeof (Type) == 4 || sizeof (Type) == 8); - } - - /** Atomically reads and returns the current value. */ - Type get() const throw(); - - /** Copies another value onto this one (atomically). */ - inline Atomic& operator= (const Atomic& other) throw() { exchange (other.get()); return *this; } - - /** Copies another value onto this one (atomically). */ - inline Atomic& operator= (const Type newValue) throw() { exchange (newValue); return *this; } - - /** Atomically sets the current value. */ - void set (Type newValue) throw() { exchange (newValue); } - - /** Atomically sets the current value, returning the value that was replaced. */ - Type exchange (Type value) throw(); - - /** Atomically adds a number to this value, returning the new value. */ - Type operator+= (Type amountToAdd) throw(); - - /** Atomically subtracts a number from this value, returning the new value. */ - Type operator-= (Type amountToSubtract) throw(); - - /** Atomically increments this value, returning the new value. */ - Type operator++() throw(); - - /** Atomically decrements this value, returning the new value. */ - Type operator--() throw(); - - /** Atomically compares this value with a target value, and if it is equal, sets - this to be equal to a new value. - - This operation is the atomic equivalent of doing this: - @code - bool compareAndSetBool (Type newValue, Type valueToCompare) - { - if (get() == valueToCompare) - { - set (newValue); - return true; - } - - return false; - } - @endcode - - @returns true if the comparison was true and the value was replaced; false if - the comparison failed and the value was left unchanged. - @see compareAndSetValue - */ - bool compareAndSetBool (Type newValue, Type valueToCompare) throw(); - - /** Atomically compares this value with a target value, and if it is equal, sets - this to be equal to a new value. - - This operation is the atomic equivalent of doing this: - @code - Type compareAndSetValue (Type newValue, Type valueToCompare) - { - Type oldValue = get(); - if (oldValue == valueToCompare) - set (newValue); - - return oldValue; - } - @endcode - - @returns the old value before it was changed. - @see compareAndSetBool - */ - Type compareAndSetValue (Type newValue, Type valueToCompare) throw(); - - /** Implements a memory read/write barrier. */ - static void memoryBarrier() throw(); - - JUCE_ALIGN(8) - - /** The raw value that this class operates on. - This is exposed publically in case you need to manipulate it directly - for performance reasons. - */ - volatile Type value; - -private: - static inline Type castFrom32Bit (int32 value) throw() { return *(Type*) &value; } - static inline Type castFrom64Bit (int64 value) throw() { return *(Type*) &value; } - static inline int32 castTo32Bit (Type value) throw() { return *(int32*) &value; } - static inline int64 castTo64Bit (Type value) throw() { return *(int64*) &value; } -}; - -/* - The following code is in the header so that the atomics can be inlined where possible... -*/ -#if (JUCE_IOS && (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_3_2 || ! defined (__IPHONE_3_2))) \ - || (JUCE_MAC && (JUCE_PPC || __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 2))) - #define JUCE_ATOMICS_MAC 1 // Older OSX builds using gcc4.1 or earlier - - #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 - #define JUCE_MAC_ATOMICS_VOLATILE - #else - #define JUCE_MAC_ATOMICS_VOLATILE volatile - #endif - - #if JUCE_PPC || JUCE_IOS - // None of these atomics are available for PPC or for iPhoneOS 3.1 or earlier!! - template static Type OSAtomicAdd64Barrier (Type b, JUCE_MAC_ATOMICS_VOLATILE Type* a) throw() { jassertfalse; return *a += b; } - template static Type OSAtomicIncrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) throw() { jassertfalse; return ++*a; } - template static Type OSAtomicDecrement64Barrier (JUCE_MAC_ATOMICS_VOLATILE Type* a) throw() { jassertfalse; return --*a; } - template static bool OSAtomicCompareAndSwap64Barrier (Type old, Type newValue, JUCE_MAC_ATOMICS_VOLATILE Type* value) throw() - { jassertfalse; if (old == *value) { *value = newValue; return true; } return false; } - #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 - #endif - -#elif JUCE_GCC - #define JUCE_ATOMICS_GCC 1 // GCC with intrinsics - - #if JUCE_IOS - #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 // (on the iphone, the 64-bit ops will compile but not link) - #endif - -#else - #define JUCE_ATOMICS_WINDOWS 1 // Windows with intrinsics - - #if JUCE_USE_INTRINSICS || JUCE_64BIT - #pragma intrinsic (_InterlockedExchange, _InterlockedIncrement, _InterlockedDecrement, _InterlockedCompareExchange, \ - _InterlockedCompareExchange64, _InterlockedExchangeAdd, _ReadWriteBarrier) - #define juce_InterlockedExchange(a, b) _InterlockedExchange(a, b) - #define juce_InterlockedIncrement(a) _InterlockedIncrement(a) - #define juce_InterlockedDecrement(a) _InterlockedDecrement(a) - #define juce_InterlockedExchangeAdd(a, b) _InterlockedExchangeAdd(a, b) - #define juce_InterlockedCompareExchange(a, b, c) _InterlockedCompareExchange(a, b, c) - #define juce_InterlockedCompareExchange64(a, b, c) _InterlockedCompareExchange64(a, b, c) - #define juce_MemoryBarrier _ReadWriteBarrier - #else - // (these are defined in juce_win32_Threads.cpp) - long juce_InterlockedExchange (volatile long* a, long b) throw(); - long juce_InterlockedIncrement (volatile long* a) throw(); - long juce_InterlockedDecrement (volatile long* a) throw(); - long juce_InterlockedExchangeAdd (volatile long* a, long b) throw(); - long juce_InterlockedCompareExchange (volatile long* a, long b, long c) throw(); - __int64 juce_InterlockedCompareExchange64 (volatile __int64* a, __int64 b, __int64 c) throw(); - void juce_MemoryBarrier() throw() { long x = 0; juce_InterlockedIncrement (&x); } - #endif - - #if JUCE_64BIT - #pragma intrinsic (_InterlockedExchangeAdd64, _InterlockedExchange64, _InterlockedIncrement64, _InterlockedDecrement64) - #define juce_InterlockedExchangeAdd64(a, b) _InterlockedExchangeAdd64(a, b) - #define juce_InterlockedExchange64(a, b) _InterlockedExchange64(a, b) - #define juce_InterlockedIncrement64(a) _InterlockedIncrement64(a) - #define juce_InterlockedDecrement64(a) _InterlockedDecrement64(a) - #else - // None of these atomics are available in a 32-bit Windows build!! - template static Type juce_InterlockedExchangeAdd64 (volatile Type* a, Type b) throw() { jassertfalse; Type old = *a; *a += b; return old; } - template static Type juce_InterlockedExchange64 (volatile Type* a, Type b) throw() { jassertfalse; Type old = *a; *a = b; return old; } - template static Type juce_InterlockedIncrement64 (volatile Type* a) throw() { jassertfalse; return ++*a; } - template static Type juce_InterlockedDecrement64 (volatile Type* a) throw() { jassertfalse; return --*a; } - #define JUCE_64BIT_ATOMICS_UNAVAILABLE 1 - #endif -#endif - -#if JUCE_MSVC - #pragma warning (push) - #pragma warning (disable: 4311) // (truncation warning) -#endif - -template -inline Type Atomic::get() const throw() -{ - #if JUCE_ATOMICS_MAC - return sizeof (Type) == 4 ? castFrom32Bit ((int32) OSAtomicAdd32Barrier ((int32_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value)) - : castFrom64Bit ((int64) OSAtomicAdd64Barrier ((int64_t) 0, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value)); - #elif JUCE_ATOMICS_WINDOWS - return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchangeAdd ((volatile long*) &value, (long) 0)) - : castFrom64Bit ((int64) juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) 0)); - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_add_and_fetch ((volatile int32*) &value, 0)) - : castFrom64Bit ((int64) __sync_add_and_fetch ((volatile int64*) &value, 0)); - #endif -} - -template -inline Type Atomic::exchange (const Type newValue) throw() -{ - #if JUCE_ATOMICS_MAC || JUCE_ATOMICS_GCC - Type currentVal = value; - while (! compareAndSetBool (newValue, currentVal)) { currentVal = value; } - return currentVal; - #elif JUCE_ATOMICS_WINDOWS - return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedExchange ((volatile long*) &value, (long) castTo32Bit (newValue))) - : castFrom64Bit ((int64) juce_InterlockedExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue))); - #endif -} - -template -inline Type Atomic::operator+= (const Type amountToAdd) throw() -{ - #if JUCE_ATOMICS_MAC - return sizeof (Type) == 4 ? (Type) OSAtomicAdd32Barrier ((int32_t) castTo32Bit (amountToAdd), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : (Type) OSAtomicAdd64Barrier ((int64_t) amountToAdd, (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS - return sizeof (Type) == 4 ? (Type) (juce_InterlockedExchangeAdd ((volatile long*) &value, (long) amountToAdd) + (long) amountToAdd) - : (Type) (juce_InterlockedExchangeAdd64 ((volatile __int64*) &value, (__int64) amountToAdd) + (__int64) amountToAdd); - #elif JUCE_ATOMICS_GCC - return (Type) __sync_add_and_fetch (&value, amountToAdd); - #endif -} - -template -inline Type Atomic::operator-= (const Type amountToSubtract) throw() -{ - return operator+= (juce_negate (amountToSubtract)); -} - -template -inline Type Atomic::operator++() throw() -{ - #if JUCE_ATOMICS_MAC - return sizeof (Type) == 4 ? (Type) OSAtomicIncrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : (Type) OSAtomicIncrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS - return sizeof (Type) == 4 ? (Type) juce_InterlockedIncrement ((volatile long*) &value) - : (Type) juce_InterlockedIncrement64 ((volatile __int64*) &value); - #elif JUCE_ATOMICS_GCC - return (Type) __sync_add_and_fetch (&value, 1); - #endif -} - -template -inline Type Atomic::operator--() throw() -{ - #if JUCE_ATOMICS_MAC - return sizeof (Type) == 4 ? (Type) OSAtomicDecrement32Barrier ((JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : (Type) OSAtomicDecrement64Barrier ((JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS - return sizeof (Type) == 4 ? (Type) juce_InterlockedDecrement ((volatile long*) &value) - : (Type) juce_InterlockedDecrement64 ((volatile __int64*) &value); - #elif JUCE_ATOMICS_GCC - return (Type) __sync_add_and_fetch (&value, -1); - #endif -} - -template -inline bool Atomic::compareAndSetBool (const Type newValue, const Type valueToCompare) throw() -{ - #if JUCE_ATOMICS_MAC - return sizeof (Type) == 4 ? OSAtomicCompareAndSwap32Barrier ((int32_t) castTo32Bit (valueToCompare), (int32_t) castTo32Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int32_t*) &value) - : OSAtomicCompareAndSwap64Barrier ((int64_t) castTo64Bit (valueToCompare), (int64_t) castTo64Bit (newValue), (JUCE_MAC_ATOMICS_VOLATILE int64_t*) &value); - #elif JUCE_ATOMICS_WINDOWS - return compareAndSetValue (newValue, valueToCompare) == valueToCompare; - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? __sync_bool_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue)) - : __sync_bool_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue)); - #endif -} - -template -inline Type Atomic::compareAndSetValue (const Type newValue, const Type valueToCompare) throw() -{ - #if JUCE_ATOMICS_MAC - for (;;) // Annoying workaround for OSX only having a bool CAS operation.. - { - if (compareAndSetBool (newValue, valueToCompare)) - return valueToCompare; - - const Type result = value; - if (result != valueToCompare) - return result; - } - - #elif JUCE_ATOMICS_WINDOWS - return sizeof (Type) == 4 ? castFrom32Bit ((int32) juce_InterlockedCompareExchange ((volatile long*) &value, (long) castTo32Bit (newValue), (long) castTo32Bit (valueToCompare))) - : castFrom64Bit ((int64) juce_InterlockedCompareExchange64 ((volatile __int64*) &value, (__int64) castTo64Bit (newValue), (__int64) castTo64Bit (valueToCompare))); - #elif JUCE_ATOMICS_GCC - return sizeof (Type) == 4 ? castFrom32Bit ((int32) __sync_val_compare_and_swap ((volatile int32*) &value, castTo32Bit (valueToCompare), castTo32Bit (newValue))) - : castFrom64Bit ((int64) __sync_val_compare_and_swap ((volatile int64*) &value, castTo64Bit (valueToCompare), castTo64Bit (newValue))); - #endif -} - -template -inline void Atomic::memoryBarrier() throw() -{ - #if JUCE_ATOMICS_MAC - OSMemoryBarrier(); - #elif JUCE_ATOMICS_GCC - __sync_synchronize(); - #elif JUCE_ATOMICS_WINDOWS - juce_MemoryBarrier(); - #endif -} - -#if JUCE_MSVC - #pragma warning (pop) -#endif - -#endif // __JUCE_ATOMIC_JUCEHEADER__ -/*** End of inlined file: juce_Atomic.h ***/ - /** Adds reference-counting to an object. @@ -31015,6 +31208,12 @@ public: int numSamplesToRead, int samplesPerBlock = 2048); + /** Writes some samples from an AudioSampleBuffer. + + */ + bool writeFromAudioSampleBuffer (const AudioSampleBuffer& source, + int startSample, int numSamples); + /** Returns the sample rate being used. */ double getSampleRate() const throw() { return sampleRate; } @@ -31027,6 +31226,47 @@ public: /** Returns true if it's a floating-point format, false if it's fixed-point. */ bool isFloatingPoint() const throw() { return usesFloatingPointData; } + /** + Provides a FIFO for an AudioFormatWriter, allowing you to push incoming + data into a buffer which will be flushed to disk by a background thread. + */ + class ThreadedWriter + { + public: + /** Creates a ThreadedWriter for a given writer and a thread. + + The writer object which is passed in here will be owned and deleted by + the ThreadedWriter when it is no longer needed. + + To stop the writer and flush the buffer to disk, simply delete this object. + */ + ThreadedWriter (AudioFormatWriter* writer, + TimeSliceThread& backgroundThread, + int numSamplesToBuffer); + + /** Destructor. */ + ~ThreadedWriter(); + + /** Pushes some incoming audio data into the FIFO. + + If there's enough free space in the buffer, this will add the data to it, + + If the FIFO is too full to accept this many samples, the method will return + false - then you could either wait until the background thread has had time to + consume some of the buffered data and try again, or you can give up + and lost this block. + + The data must be an array containing the same number of channels as the + AudioFormatWriter object is using. None of these channels can be null. + */ + bool write (const float** data, int numSamples); + + private: + class Buffer; + friend class ScopedPointer; + ScopedPointer buffer; + }; + juce_UseDebuggingNewOperator protected: @@ -31057,10 +31297,16 @@ protected: for (int i = 0; i < numDestChannels; ++i) { const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels); - dest.convertSamples (SourceType (*source), numSamples); - if (source[1] != 0) + if (*source != 0) + { + dest.convertSamples (SourceType (*source), numSamples); ++source; + } + else + { + dest.clearSamples (numSamples); + } } } }; diff --git a/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp b/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp index 84c82f7671..6f7974f5aa 100644 --- a/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_AiffAudioFormat.cpp @@ -188,7 +188,7 @@ public: { switch (bitsPerSample) { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 32: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; @@ -199,7 +199,7 @@ public: { switch (bitsPerSample) { - case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; + case 8: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 16: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 24: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; case 32: ReadHelper::read (destSamples, startOffsetInDestBuffer, numDestChannels, tempBuffer, numChannels, numThisTime); break; diff --git a/src/audio/audio_file_formats/juce_AudioFormat.cpp b/src/audio/audio_file_formats/juce_AudioFormat.cpp index 2ddf27d882..b06434dd07 100644 --- a/src/audio/audio_file_formats/juce_AudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_AudioFormat.cpp @@ -28,486 +28,10 @@ BEGIN_JUCE_NAMESPACE #include "juce_AudioFormat.h" -#include "../dsp/juce_AudioSampleBuffer.h" //============================================================================== -AudioFormatReader::AudioFormatReader (InputStream* const in, - const String& formatName_) - : sampleRate (0), - bitsPerSample (0), - lengthInSamples (0), - numChannels (0), - usesFloatingPointData (false), - input (in), - formatName (formatName_) -{ -} - -AudioFormatReader::~AudioFormatReader() -{ - delete input; -} - -bool AudioFormatReader::read (int* const* destSamples, - int numDestChannels, - int64 startSampleInSource, - int numSamplesToRead, - const bool fillLeftoverChannelsWithCopies) -{ - jassert (numDestChannels > 0); // you have to actually give this some channels to work with! - - int startOffsetInDestBuffer = 0; - - if (startSampleInSource < 0) - { - const int silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead); - - for (int i = numDestChannels; --i >= 0;) - if (destSamples[i] != 0) - zeromem (destSamples[i], sizeof (int) * silence); - - startOffsetInDestBuffer += silence; - numSamplesToRead -= silence; - startSampleInSource = 0; - } - - if (numSamplesToRead <= 0) - return true; - - if (! readSamples (const_cast (destSamples), - jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer, - startSampleInSource, numSamplesToRead)) - return false; - - if (numDestChannels > (int) numChannels) - { - if (fillLeftoverChannelsWithCopies) - { - int* lastFullChannel = destSamples[0]; - - for (int i = (int) numChannels; --i > 0;) - { - if (destSamples[i] != 0) - { - lastFullChannel = destSamples[i]; - break; - } - } - - if (lastFullChannel != 0) - for (int i = numChannels; i < numDestChannels; ++i) - if (destSamples[i] != 0) - memcpy (destSamples[i], lastFullChannel, sizeof (int) * numSamplesToRead); - } - else - { - for (int i = numChannels; i < numDestChannels; ++i) - if (destSamples[i] != 0) - zeromem (destSamples[i], sizeof (int) * numSamplesToRead); - } - } - - return true; -} - -static void findAudioBufferMaxMin (const float* const buffer, const int num, float& maxVal, float& minVal) throw() -{ - float mn = buffer[0]; - float mx = mn; - - for (int i = 1; i < num; ++i) - { - const float s = buffer[i]; - if (s > mx) mx = s; - if (s < mn) mn = s; - } - - maxVal = mx; - minVal = mn; -} - -void AudioFormatReader::readMaxLevels (int64 startSampleInFile, - int64 numSamples, - float& lowestLeft, float& highestLeft, - float& lowestRight, float& highestRight) -{ - if (numSamples <= 0) - { - lowestLeft = 0; - lowestRight = 0; - highestLeft = 0; - highestRight = 0; - return; - } - - const int bufferSize = (int) jmin (numSamples, (int64) 4096); - HeapBlock tempSpace (bufferSize * 2 + 64); - - int* tempBuffer[3]; - tempBuffer[0] = tempSpace.getData(); - tempBuffer[1] = tempSpace.getData() + bufferSize; - tempBuffer[2] = 0; - - if (usesFloatingPointData) - { - float lmin = 1.0e6f; - float lmax = -lmin; - float rmin = lmin; - float rmax = lmax; - - while (numSamples > 0) - { - const int numToDo = (int) jmin (numSamples, (int64) bufferSize); - read (tempBuffer, 2, startSampleInFile, numToDo, false); - - numSamples -= numToDo; - startSampleInFile += numToDo; - - float bufmin, bufmax; - findAudioBufferMaxMin (reinterpret_cast (tempBuffer[0]), numToDo, bufmax, bufmin); - lmin = jmin (lmin, bufmin); - lmax = jmax (lmax, bufmax); - - if (numChannels > 1) - { - findAudioBufferMaxMin (reinterpret_cast (tempBuffer[1]), numToDo, bufmax, bufmin); - rmin = jmin (rmin, bufmin); - rmax = jmax (rmax, bufmax); - } - } - - if (numChannels <= 1) - { - rmax = lmax; - rmin = lmin; - } - - lowestLeft = lmin; - highestLeft = lmax; - lowestRight = rmin; - highestRight = rmax; - } - else - { - int lmax = std::numeric_limits::min(); - int lmin = std::numeric_limits::max(); - int rmax = std::numeric_limits::min(); - int rmin = std::numeric_limits::max(); - - while (numSamples > 0) - { - const int numToDo = (int) jmin (numSamples, (int64) bufferSize); - read (tempBuffer, 2, startSampleInFile, numToDo, false); - - numSamples -= numToDo; - startSampleInFile += numToDo; - - for (int j = numChannels; --j >= 0;) - { - int bufMax = std::numeric_limits::min(); - int bufMin = std::numeric_limits::max(); - - const int* const b = tempBuffer[j]; - - for (int i = 0; i < numToDo; ++i) - { - const int samp = b[i]; - - if (samp < bufMin) - bufMin = samp; - - if (samp > bufMax) - bufMax = samp; - } - - if (j == 0) - { - lmax = jmax (lmax, bufMax); - lmin = jmin (lmin, bufMin); - } - else - { - rmax = jmax (rmax, bufMax); - rmin = jmin (rmin, bufMin); - } - } - } - - if (numChannels <= 1) - { - rmax = lmax; - rmin = lmin; - } - - lowestLeft = lmin / (float) std::numeric_limits::max(); - highestLeft = lmax / (float) std::numeric_limits::max(); - lowestRight = rmin / (float) std::numeric_limits::max(); - highestRight = rmax / (float) std::numeric_limits::max(); - } -} - -int64 AudioFormatReader::searchForLevel (int64 startSample, - int64 numSamplesToSearch, - const double magnitudeRangeMinimum, - const double magnitudeRangeMaximum, - const int minimumConsecutiveSamples) -{ - if (numSamplesToSearch == 0) - return -1; - - const int bufferSize = 4096; - HeapBlock tempSpace (bufferSize * 2 + 64); - - int* tempBuffer[3]; - tempBuffer[0] = tempSpace.getData(); - tempBuffer[1] = tempSpace.getData() + bufferSize; - tempBuffer[2] = 0; - - int consecutive = 0; - int64 firstMatchPos = -1; - - jassert (magnitudeRangeMaximum > magnitudeRangeMinimum); - - const double doubleMin = jlimit (0.0, (double) std::numeric_limits::max(), magnitudeRangeMinimum * std::numeric_limits::max()); - const double doubleMax = jlimit (doubleMin, (double) std::numeric_limits::max(), magnitudeRangeMaximum * std::numeric_limits::max()); - const int intMagnitudeRangeMinimum = roundToInt (doubleMin); - const int intMagnitudeRangeMaximum = roundToInt (doubleMax); - - while (numSamplesToSearch != 0) - { - const int numThisTime = (int) jmin (abs64 (numSamplesToSearch), (int64) bufferSize); - int64 bufferStart = startSample; - - if (numSamplesToSearch < 0) - bufferStart -= numThisTime; - - if (bufferStart >= (int) lengthInSamples) - break; - - read (tempBuffer, 2, bufferStart, numThisTime, false); - - int num = numThisTime; - while (--num >= 0) - { - if (numSamplesToSearch < 0) - --startSample; - - bool matches = false; - const int index = (int) (startSample - bufferStart); - - if (usesFloatingPointData) - { - const float sample1 = std::abs (((float*) tempBuffer[0]) [index]); - - if (sample1 >= magnitudeRangeMinimum - && sample1 <= magnitudeRangeMaximum) - { - matches = true; - } - else if (numChannels > 1) - { - const float sample2 = std::abs (((float*) tempBuffer[1]) [index]); - - matches = (sample2 >= magnitudeRangeMinimum - && sample2 <= magnitudeRangeMaximum); - } - } - else - { - const int sample1 = abs (tempBuffer[0] [index]); - - if (sample1 >= intMagnitudeRangeMinimum - && sample1 <= intMagnitudeRangeMaximum) - { - matches = true; - } - else if (numChannels > 1) - { - const int sample2 = abs (tempBuffer[1][index]); - - matches = (sample2 >= intMagnitudeRangeMinimum - && sample2 <= intMagnitudeRangeMaximum); - } - } - - if (matches) - { - if (firstMatchPos < 0) - firstMatchPos = startSample; - - if (++consecutive >= minimumConsecutiveSamples) - { - if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples) - return -1; - - return firstMatchPos; - } - } - else - { - consecutive = 0; - firstMatchPos = -1; - } - - if (numSamplesToSearch > 0) - ++startSample; - } - - if (numSamplesToSearch > 0) - numSamplesToSearch -= numThisTime; - else - numSamplesToSearch += numThisTime; - } - - return -1; -} - -//============================================================================== -AudioFormatWriter::AudioFormatWriter (OutputStream* const out, - const String& formatName_, - const double rate, - const unsigned int numChannels_, - const unsigned int bitsPerSample_) - : sampleRate (rate), - numChannels (numChannels_), - bitsPerSample (bitsPerSample_), - usesFloatingPointData (false), - output (out), - formatName (formatName_) -{ -} - -AudioFormatWriter::~AudioFormatWriter() -{ - delete output; -} - -bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, - int64 startSample, - int64 numSamplesToRead) -{ - const int bufferSize = 16384; - AudioSampleBuffer tempBuffer (numChannels, bufferSize); - - int* buffers [128]; - zerostruct (buffers); - - for (int i = tempBuffer.getNumChannels(); --i >= 0;) - buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); - - if (numSamplesToRead < 0) - numSamplesToRead = reader.lengthInSamples; - - while (numSamplesToRead > 0) - { - const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize); - - if (! reader.read (buffers, numChannels, startSample, numToDo, false)) - return false; - - if (reader.usesFloatingPointData != isFloatingPoint()) - { - int** bufferChan = buffers; - - while (*bufferChan != 0) - { - int* b = *bufferChan++; - - if (isFloatingPoint()) - { - // int -> float - const double factor = 1.0 / std::numeric_limits::max(); - - for (int i = 0; i < numToDo; ++i) - ((float*) b)[i] = (float) (factor * b[i]); - } - else - { - // float -> int - for (int i = 0; i < numToDo; ++i) - { - const double samp = *(const float*) b; - - if (samp <= -1.0) - *b++ = std::numeric_limits::min(); - else if (samp >= 1.0) - *b++ = std::numeric_limits::max(); - else - *b++ = roundToInt (std::numeric_limits::max() * samp); - } - } - } - } - - if (! write (const_cast (buffers), numToDo)) - return false; - - numSamplesToRead -= numToDo; - startSample += numToDo; - } - - return true; -} - -bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, - int numSamplesToRead, - const int samplesPerBlock) -{ - AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); - int* buffers [128]; - zerostruct (buffers); - - for (int i = tempBuffer.getNumChannels(); --i >= 0;) - buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); - - while (numSamplesToRead > 0) - { - const int numToDo = jmin (numSamplesToRead, samplesPerBlock); - - AudioSourceChannelInfo info; - info.buffer = &tempBuffer; - info.startSample = 0; - info.numSamples = numToDo; - info.clearActiveBufferRegion(); - - source.getNextAudioBlock (info); - - if (! isFloatingPoint()) - { - int** bufferChan = buffers; - - while (*bufferChan != 0) - { - int* b = *bufferChan++; - - // float -> int - for (int j = numToDo; --j >= 0;) - { - const double samp = *(const float*) b; - - if (samp <= -1.0) - *b++ = std::numeric_limits::min(); - else if (samp >= 1.0) - *b++ = std::numeric_limits::max(); - else - *b++ = roundToInt (std::numeric_limits::max() * samp); - } - } - } - - if (! write ((const int**) buffers, numToDo)) - return false; - - numSamplesToRead -= numToDo; - } - - return true; -} - -//============================================================================== -AudioFormat::AudioFormat (const String& name, - const StringArray& extensions) +AudioFormat::AudioFormat (const String& name, const StringArray& extensions) : formatName (name), fileExtensions (extensions) { @@ -518,16 +42,6 @@ AudioFormat::~AudioFormat() } //============================================================================== -const String& AudioFormat::getFormatName() const -{ - return formatName; -} - -const StringArray& AudioFormat::getFileExtensions() const -{ - return fileExtensions; -} - bool AudioFormat::canHandleFile (const File& f) { for (int i = 0; i < fileExtensions.size(); ++i) @@ -537,15 +51,10 @@ bool AudioFormat::canHandleFile (const File& f) return false; } -bool AudioFormat::isCompressed() -{ - return false; -} - -const StringArray AudioFormat::getQualityOptions() -{ - return StringArray(); -} +const String& AudioFormat::getFormatName() const { return formatName; } +const StringArray& AudioFormat::getFileExtensions() const { return fileExtensions; } +bool AudioFormat::isCompressed() { return false; } +const StringArray AudioFormat::getQualityOptions() { return StringArray(); } END_JUCE_NAMESPACE diff --git a/src/audio/audio_file_formats/juce_AudioFormatReader.cpp b/src/audio/audio_file_formats/juce_AudioFormatReader.cpp new file mode 100644 index 0000000000..b4b36eda0b --- /dev/null +++ b/src/audio/audio_file_formats/juce_AudioFormatReader.cpp @@ -0,0 +1,367 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#include "../../core/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioFormat.h" +#include "../dsp/juce_AudioSampleBuffer.h" + + +//============================================================================== +AudioFormatReader::AudioFormatReader (InputStream* const in, + const String& formatName_) + : sampleRate (0), + bitsPerSample (0), + lengthInSamples (0), + numChannels (0), + usesFloatingPointData (false), + input (in), + formatName (formatName_) +{ +} + +AudioFormatReader::~AudioFormatReader() +{ + delete input; +} + +bool AudioFormatReader::read (int* const* destSamples, + int numDestChannels, + int64 startSampleInSource, + int numSamplesToRead, + const bool fillLeftoverChannelsWithCopies) +{ + jassert (numDestChannels > 0); // you have to actually give this some channels to work with! + + int startOffsetInDestBuffer = 0; + + if (startSampleInSource < 0) + { + const int silence = (int) jmin (-startSampleInSource, (int64) numSamplesToRead); + + for (int i = numDestChannels; --i >= 0;) + if (destSamples[i] != 0) + zeromem (destSamples[i], sizeof (int) * silence); + + startOffsetInDestBuffer += silence; + numSamplesToRead -= silence; + startSampleInSource = 0; + } + + if (numSamplesToRead <= 0) + return true; + + if (! readSamples (const_cast (destSamples), + jmin ((int) numChannels, numDestChannels), startOffsetInDestBuffer, + startSampleInSource, numSamplesToRead)) + return false; + + if (numDestChannels > (int) numChannels) + { + if (fillLeftoverChannelsWithCopies) + { + int* lastFullChannel = destSamples[0]; + + for (int i = (int) numChannels; --i > 0;) + { + if (destSamples[i] != 0) + { + lastFullChannel = destSamples[i]; + break; + } + } + + if (lastFullChannel != 0) + for (int i = numChannels; i < numDestChannels; ++i) + if (destSamples[i] != 0) + memcpy (destSamples[i], lastFullChannel, sizeof (int) * numSamplesToRead); + } + else + { + for (int i = numChannels; i < numDestChannels; ++i) + if (destSamples[i] != 0) + zeromem (destSamples[i], sizeof (int) * numSamplesToRead); + } + } + + return true; +} + +static void findAudioBufferMaxMin (const float* const buffer, const int num, float& maxVal, float& minVal) throw() +{ + float mn = buffer[0]; + float mx = mn; + + for (int i = 1; i < num; ++i) + { + const float s = buffer[i]; + if (s > mx) mx = s; + if (s < mn) mn = s; + } + + maxVal = mx; + minVal = mn; +} + +void AudioFormatReader::readMaxLevels (int64 startSampleInFile, + int64 numSamples, + float& lowestLeft, float& highestLeft, + float& lowestRight, float& highestRight) +{ + if (numSamples <= 0) + { + lowestLeft = 0; + lowestRight = 0; + highestLeft = 0; + highestRight = 0; + return; + } + + const int bufferSize = (int) jmin (numSamples, (int64) 4096); + HeapBlock tempSpace (bufferSize * 2 + 64); + + int* tempBuffer[3]; + tempBuffer[0] = tempSpace.getData(); + tempBuffer[1] = tempSpace.getData() + bufferSize; + tempBuffer[2] = 0; + + if (usesFloatingPointData) + { + float lmin = 1.0e6f; + float lmax = -lmin; + float rmin = lmin; + float rmax = lmax; + + while (numSamples > 0) + { + const int numToDo = (int) jmin (numSamples, (int64) bufferSize); + read (tempBuffer, 2, startSampleInFile, numToDo, false); + + numSamples -= numToDo; + startSampleInFile += numToDo; + + float bufmin, bufmax; + findAudioBufferMaxMin (reinterpret_cast (tempBuffer[0]), numToDo, bufmax, bufmin); + lmin = jmin (lmin, bufmin); + lmax = jmax (lmax, bufmax); + + if (numChannels > 1) + { + findAudioBufferMaxMin (reinterpret_cast (tempBuffer[1]), numToDo, bufmax, bufmin); + rmin = jmin (rmin, bufmin); + rmax = jmax (rmax, bufmax); + } + } + + if (numChannels <= 1) + { + rmax = lmax; + rmin = lmin; + } + + lowestLeft = lmin; + highestLeft = lmax; + lowestRight = rmin; + highestRight = rmax; + } + else + { + int lmax = std::numeric_limits::min(); + int lmin = std::numeric_limits::max(); + int rmax = std::numeric_limits::min(); + int rmin = std::numeric_limits::max(); + + while (numSamples > 0) + { + const int numToDo = (int) jmin (numSamples, (int64) bufferSize); + read (tempBuffer, 2, startSampleInFile, numToDo, false); + + numSamples -= numToDo; + startSampleInFile += numToDo; + + for (int j = numChannels; --j >= 0;) + { + int bufMax = std::numeric_limits::min(); + int bufMin = std::numeric_limits::max(); + + const int* const b = tempBuffer[j]; + + for (int i = 0; i < numToDo; ++i) + { + const int samp = b[i]; + + if (samp < bufMin) + bufMin = samp; + + if (samp > bufMax) + bufMax = samp; + } + + if (j == 0) + { + lmax = jmax (lmax, bufMax); + lmin = jmin (lmin, bufMin); + } + else + { + rmax = jmax (rmax, bufMax); + rmin = jmin (rmin, bufMin); + } + } + } + + if (numChannels <= 1) + { + rmax = lmax; + rmin = lmin; + } + + lowestLeft = lmin / (float) std::numeric_limits::max(); + highestLeft = lmax / (float) std::numeric_limits::max(); + lowestRight = rmin / (float) std::numeric_limits::max(); + highestRight = rmax / (float) std::numeric_limits::max(); + } +} + +int64 AudioFormatReader::searchForLevel (int64 startSample, + int64 numSamplesToSearch, + const double magnitudeRangeMinimum, + const double magnitudeRangeMaximum, + const int minimumConsecutiveSamples) +{ + if (numSamplesToSearch == 0) + return -1; + + const int bufferSize = 4096; + HeapBlock tempSpace (bufferSize * 2 + 64); + + int* tempBuffer[3]; + tempBuffer[0] = tempSpace.getData(); + tempBuffer[1] = tempSpace.getData() + bufferSize; + tempBuffer[2] = 0; + + int consecutive = 0; + int64 firstMatchPos = -1; + + jassert (magnitudeRangeMaximum > magnitudeRangeMinimum); + + const double doubleMin = jlimit (0.0, (double) std::numeric_limits::max(), magnitudeRangeMinimum * std::numeric_limits::max()); + const double doubleMax = jlimit (doubleMin, (double) std::numeric_limits::max(), magnitudeRangeMaximum * std::numeric_limits::max()); + const int intMagnitudeRangeMinimum = roundToInt (doubleMin); + const int intMagnitudeRangeMaximum = roundToInt (doubleMax); + + while (numSamplesToSearch != 0) + { + const int numThisTime = (int) jmin (abs64 (numSamplesToSearch), (int64) bufferSize); + int64 bufferStart = startSample; + + if (numSamplesToSearch < 0) + bufferStart -= numThisTime; + + if (bufferStart >= (int) lengthInSamples) + break; + + read (tempBuffer, 2, bufferStart, numThisTime, false); + + int num = numThisTime; + while (--num >= 0) + { + if (numSamplesToSearch < 0) + --startSample; + + bool matches = false; + const int index = (int) (startSample - bufferStart); + + if (usesFloatingPointData) + { + const float sample1 = std::abs (((float*) tempBuffer[0]) [index]); + + if (sample1 >= magnitudeRangeMinimum + && sample1 <= magnitudeRangeMaximum) + { + matches = true; + } + else if (numChannels > 1) + { + const float sample2 = std::abs (((float*) tempBuffer[1]) [index]); + + matches = (sample2 >= magnitudeRangeMinimum + && sample2 <= magnitudeRangeMaximum); + } + } + else + { + const int sample1 = abs (tempBuffer[0] [index]); + + if (sample1 >= intMagnitudeRangeMinimum + && sample1 <= intMagnitudeRangeMaximum) + { + matches = true; + } + else if (numChannels > 1) + { + const int sample2 = abs (tempBuffer[1][index]); + + matches = (sample2 >= intMagnitudeRangeMinimum + && sample2 <= intMagnitudeRangeMaximum); + } + } + + if (matches) + { + if (firstMatchPos < 0) + firstMatchPos = startSample; + + if (++consecutive >= minimumConsecutiveSamples) + { + if (firstMatchPos < 0 || firstMatchPos >= lengthInSamples) + return -1; + + return firstMatchPos; + } + } + else + { + consecutive = 0; + firstMatchPos = -1; + } + + if (numSamplesToSearch > 0) + ++startSample; + } + + if (numSamplesToSearch > 0) + numSamplesToSearch -= numThisTime; + else + numSamplesToSearch += numThisTime; + } + + return -1; +} + + +END_JUCE_NAMESPACE diff --git a/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp b/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp new file mode 100644 index 0000000000..eb5b4a62e5 --- /dev/null +++ b/src/audio/audio_file_formats/juce_AudioFormatWriter.cpp @@ -0,0 +1,272 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#include "../../core/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AudioFormat.h" +#include "../dsp/juce_AudioSampleBuffer.h" +#include "../../containers/juce_AbstractFifo.h" + + +//============================================================================== +AudioFormatWriter::AudioFormatWriter (OutputStream* const out, + const String& formatName_, + const double rate, + const unsigned int numChannels_, + const unsigned int bitsPerSample_) + : sampleRate (rate), + numChannels (numChannels_), + bitsPerSample (bitsPerSample_), + usesFloatingPointData (false), + output (out), + formatName (formatName_) +{ +} + +AudioFormatWriter::~AudioFormatWriter() +{ + delete output; +} + +bool AudioFormatWriter::writeFromAudioReader (AudioFormatReader& reader, + int64 startSample, + int64 numSamplesToRead) +{ + const int bufferSize = 16384; + AudioSampleBuffer tempBuffer (numChannels, bufferSize); + + int* buffers [128]; + zerostruct (buffers); + + for (int i = tempBuffer.getNumChannels(); --i >= 0;) + buffers[i] = reinterpret_cast (tempBuffer.getSampleData (i, 0)); + + if (numSamplesToRead < 0) + numSamplesToRead = reader.lengthInSamples; + + while (numSamplesToRead > 0) + { + const int numToDo = (int) jmin (numSamplesToRead, (int64) bufferSize); + + if (! reader.read (buffers, numChannels, startSample, numToDo, false)) + return false; + + if (reader.usesFloatingPointData != isFloatingPoint()) + { + int** bufferChan = buffers; + + while (*bufferChan != 0) + { + int* b = *bufferChan++; + + if (isFloatingPoint()) + { + // int -> float + const double factor = 1.0 / std::numeric_limits::max(); + + for (int i = 0; i < numToDo; ++i) + ((float*) b)[i] = (float) (factor * b[i]); + } + else + { + // float -> int + for (int i = 0; i < numToDo; ++i) + { + const double samp = *(const float*) b; + + if (samp <= -1.0) + *b++ = std::numeric_limits::min(); + else if (samp >= 1.0) + *b++ = std::numeric_limits::max(); + else + *b++ = roundToInt (std::numeric_limits::max() * samp); + } + } + } + } + + if (! write (const_cast (buffers), numToDo)) + return false; + + numSamplesToRead -= numToDo; + startSample += numToDo; + } + + return true; +} + +bool AudioFormatWriter::writeFromAudioSource (AudioSource& source, int numSamplesToRead, const int samplesPerBlock) +{ + AudioSampleBuffer tempBuffer (getNumChannels(), samplesPerBlock); + + while (numSamplesToRead > 0) + { + const int numToDo = jmin (numSamplesToRead, samplesPerBlock); + + AudioSourceChannelInfo info; + info.buffer = &tempBuffer; + info.startSample = 0; + info.numSamples = numToDo; + info.clearActiveBufferRegion(); + + source.getNextAudioBlock (info); + + if (! writeFromAudioSampleBuffer (tempBuffer, 0, numToDo)) + return false; + + numSamplesToRead -= numToDo; + } + + return true; +} + +bool AudioFormatWriter::writeFromAudioSampleBuffer (const AudioSampleBuffer& source, int startSample, int numSamples) +{ + jassert (startSample >= 0 && startSample + numSamples <= source.getNumSamples() && source.getNumChannels() > 0); + + if (numSamples <= 0) + return true; + + HeapBlock tempBuffer; + HeapBlock chans (numChannels + 1); + chans [numChannels] = 0; + + if (isFloatingPoint()) + { + for (int i = numChannels; --i >= 0;) + chans[i] = reinterpret_cast (source.getSampleData (i, startSample)); + } + else + { + tempBuffer.malloc (numSamples * numChannels); + + for (unsigned int i = 0; i < numChannels; ++i) + { + typedef AudioData::Pointer DestSampleType; + typedef AudioData::Pointer SourceSampleType; + + DestSampleType destData (chans[i] = tempBuffer + i * numSamples); + SourceSampleType sourceData (source.getSampleData (i, startSample)); + destData.convertSamples (sourceData, numSamples); + } + } + + return write ((const int**) chans.getData(), numSamples); +} + +//============================================================================== +class AudioFormatWriter::ThreadedWriter::Buffer : public TimeSliceClient, + public AbstractFifo +{ +public: + Buffer (TimeSliceThread& timeSliceThread_, AudioFormatWriter* writer_, int numChannels, int bufferSize) + : AbstractFifo (bufferSize), + buffer (numChannels, bufferSize), + timeSliceThread (timeSliceThread_), + writer (writer_), isRunning (true) + { + timeSliceThread.addTimeSliceClient (this); + } + + ~Buffer() + { + isRunning = false; + timeSliceThread.removeTimeSliceClient (this); + + while (useTimeSlice()) + {} + } + + bool write (const float** data, int numSamples) + { + if (numSamples <= 0 || ! isRunning) + return true; + + jassert (timeSliceThread.isThreadRunning()); // you need to get your thread running before pumping data into this! + + int start1, size1, start2, size2; + prepareToWrite (numSamples, start1, size1, start2, size2); + + if (size1 + size2 < numSamples) + return false; + + for (int i = buffer.getNumChannels(); --i >= 0;) + { + buffer.copyFrom (i, start1, data[i], size1); + buffer.copyFrom (i, start2, data[i] + size1, size2); + } + + finishedWrite (size1 + size2); + timeSliceThread.notify(); + return true; + } + + bool useTimeSlice() + { + const int numToDo = getTotalSize() / 4; + + int start1, size1, start2, size2; + prepareToRead (numToDo, start1, size1, start2, size2); + + if (size1 <= 0) + return false; + + writer->writeFromAudioSampleBuffer (buffer, start1, size1); + + if (size2 > 0) + writer->writeFromAudioSampleBuffer (buffer, start2, size2); + + finishedRead (size1 + size2); + return true; + } + +private: + AudioSampleBuffer buffer; + TimeSliceThread& timeSliceThread; + ScopedPointer writer; + volatile bool isRunning; + + Buffer (const Buffer&); + Buffer& operator= (const Buffer&); +}; + +AudioFormatWriter::ThreadedWriter::ThreadedWriter (AudioFormatWriter* writer, TimeSliceThread& backgroundThread, int numSamplesToBuffer) + : buffer (new AudioFormatWriter::ThreadedWriter::Buffer (backgroundThread, writer, writer->numChannels, numSamplesToBuffer)) +{ +} + +AudioFormatWriter::ThreadedWriter::~ThreadedWriter() +{ +} + +bool AudioFormatWriter::ThreadedWriter::write (const float** data, int numSamples) +{ + return buffer->write (data, numSamples); +} + + +END_JUCE_NAMESPACE diff --git a/src/audio/audio_file_formats/juce_AudioFormatWriter.h b/src/audio/audio_file_formats/juce_AudioFormatWriter.h index c922dd0299..64494b68aa 100644 --- a/src/audio/audio_file_formats/juce_AudioFormatWriter.h +++ b/src/audio/audio_file_formats/juce_AudioFormatWriter.h @@ -29,6 +29,7 @@ #include "../../io/files/juce_FileOutputStream.h" #include "juce_AudioFormatReader.h" #include "../audio_sources/juce_AudioSource.h" +#include "../../threads/juce_TimeSliceThread.h" //============================================================================== @@ -127,6 +128,13 @@ public: int numSamplesToRead, int samplesPerBlock = 2048); + + /** Writes some samples from an AudioSampleBuffer. + + */ + bool writeFromAudioSampleBuffer (const AudioSampleBuffer& source, + int startSample, int numSamples); + //============================================================================== /** Returns the sample rate being used. */ double getSampleRate() const throw() { return sampleRate; } @@ -140,6 +148,47 @@ public: /** Returns true if it's a floating-point format, false if it's fixed-point. */ bool isFloatingPoint() const throw() { return usesFloatingPointData; } + //============================================================================== + /** + Provides a FIFO for an AudioFormatWriter, allowing you to push incoming + data into a buffer which will be flushed to disk by a background thread. + */ + class ThreadedWriter + { + public: + /** Creates a ThreadedWriter for a given writer and a thread. + + The writer object which is passed in here will be owned and deleted by + the ThreadedWriter when it is no longer needed. + + To stop the writer and flush the buffer to disk, simply delete this object. + */ + ThreadedWriter (AudioFormatWriter* writer, + TimeSliceThread& backgroundThread, + int numSamplesToBuffer); + + /** Destructor. */ + ~ThreadedWriter(); + + /** Pushes some incoming audio data into the FIFO. + + If there's enough free space in the buffer, this will add the data to it, + + If the FIFO is too full to accept this many samples, the method will return + false - then you could either wait until the background thread has had time to + consume some of the buffered data and try again, or you can give up + and lost this block. + + The data must be an array containing the same number of channels as the + AudioFormatWriter object is using. None of these channels can be null. + */ + bool write (const float** data, int numSamples); + + private: + class Buffer; + friend class ScopedPointer; + ScopedPointer buffer; + }; //============================================================================== juce_UseDebuggingNewOperator @@ -172,10 +221,16 @@ protected: for (int i = 0; i < numDestChannels; ++i) { const DestType dest (addBytesToPointer (destData, i * DestType::getBytesPerSample()), numDestChannels); - dest.convertSamples (SourceType (*source), numSamples); - if (source[1] != 0) + if (*source != 0) + { + dest.convertSamples (SourceType (*source), numSamples); ++source; + } + else + { + dest.clearSamples (numSamples); + } } } }; diff --git a/src/audio/audio_file_formats/juce_OggVorbisAudioFormat.cpp b/src/audio/audio_file_formats/juce_OggVorbisAudioFormat.cpp index e35417aa61..04e129fabc 100644 --- a/src/audio/audio_file_formats/juce_OggVorbisAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_OggVorbisAudioFormat.cpp @@ -411,20 +411,13 @@ const Array OggVorbisAudioFormat::getPossibleSampleRates() const Array OggVorbisAudioFormat::getPossibleBitDepths() { - Array depths; - depths.add (32); - return depths; + const int depths[] = { 32, 0 }; + return Array (depths); } -bool OggVorbisAudioFormat::canDoStereo() -{ - return true; -} - -bool OggVorbisAudioFormat::canDoMono() -{ - return true; -} +bool OggVorbisAudioFormat::canDoStereo() { return true; } +bool OggVorbisAudioFormat::canDoMono() { return true; } +bool OggVorbisAudioFormat::isCompressed() { return true; } AudioFormatReader* OggVorbisAudioFormat::createReaderFor (InputStream* in, const bool deleteStreamIfOpeningFails) @@ -456,11 +449,6 @@ AudioFormatWriter* OggVorbisAudioFormat::createWriterFor (OutputStream* out, return w->ok ? w.release() : 0; } -bool OggVorbisAudioFormat::isCompressed() -{ - return true; -} - const StringArray OggVorbisAudioFormat::getQualityOptions() { StringArray s; diff --git a/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp b/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp index 892b5d4ed3..acd3321b09 100644 --- a/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp +++ b/src/audio/audio_file_formats/juce_QuickTimeAudioFormat.cpp @@ -342,25 +342,11 @@ QuickTimeAudioFormat::~QuickTimeAudioFormat() { } -const Array QuickTimeAudioFormat::getPossibleSampleRates() -{ - return Array(); -} +const Array QuickTimeAudioFormat::getPossibleSampleRates() { return Array(); } +const Array QuickTimeAudioFormat::getPossibleBitDepths() { return Array(); } -const Array QuickTimeAudioFormat::getPossibleBitDepths() -{ - return Array(); -} - -bool QuickTimeAudioFormat::canDoStereo() -{ - return true; -} - -bool QuickTimeAudioFormat::canDoMono() -{ - return true; -} +bool QuickTimeAudioFormat::canDoStereo() { return true; } +bool QuickTimeAudioFormat::canDoMono() { return true; } //============================================================================== AudioFormatReader* QuickTimeAudioFormat::createReaderFor (InputStream* sourceStream, diff --git a/src/audio/dsp/juce_AudioSampleBuffer.cpp b/src/audio/dsp/juce_AudioSampleBuffer.cpp index 5d2b1863fb..6f17a6707f 100644 --- a/src/audio/dsp/juce_AudioSampleBuffer.cpp +++ b/src/audio/dsp/juce_AudioSampleBuffer.cpp @@ -633,45 +633,8 @@ void AudioSampleBuffer::writeToAudioWriter (AudioFormatWriter* writer, const int startSample, const int numSamples) const { - jassert (startSample >= 0 && startSample + numSamples <= size && numChannels > 0); - - if (numSamples > 0) - { - HeapBlock tempBuffer; - HeapBlock chans (numChannels + 1); - chans [numChannels] = 0; - - if (writer->isFloatingPoint()) - { - for (int i = numChannels; --i >= 0;) - chans[i] = reinterpret_cast (channels[i] + startSample); - } - else - { - tempBuffer.malloc (numSamples * numChannels); - - for (int j = 0; j < numChannels; ++j) - { - int* const dest = tempBuffer + j * numSamples; - const float* const src = channels[j] + startSample; - chans[j] = dest; - - for (int i = 0; i < numSamples; ++i) - { - const double samp = src[i]; - - if (samp <= -1.0) - dest[i] = std::numeric_limits::min(); - else if (samp >= 1.0) - dest[i] = std::numeric_limits::max(); - else - dest[i] = roundToInt (std::numeric_limits::max() * samp); - } - } - } - - writer->write ((const int**) chans.getData(), numSamples); - } + jassert (writer != 0); + writer->writeFromAudioSampleBuffer (*this, startSample, numSamples); } END_JUCE_NAMESPACE diff --git a/src/containers/juce_AbstractFifo.cpp b/src/containers/juce_AbstractFifo.cpp new file mode 100644 index 0000000000..0aa450829b --- /dev/null +++ b/src/containers/juce_AbstractFifo.cpp @@ -0,0 +1,123 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#include "../core/juce_StandardHeader.h" + +BEGIN_JUCE_NAMESPACE + +#include "juce_AbstractFifo.h" + + +//============================================================================== +AbstractFifo::AbstractFifo (const int capacity) throw() + : bufferSize (capacity) +{ + jassert (bufferSize > 0); +} + +AbstractFifo::~AbstractFifo() {} + +int AbstractFifo::getTotalSize() const throw() { return bufferSize; } +int AbstractFifo::getFreeSpace() const throw() { return bufferSize - getNumReady(); } +int AbstractFifo::getNumReady() const throw() { return validEnd.get() - validStart.get(); } + +void AbstractFifo::reset() throw() +{ + validEnd = 0; + validStart = 0; +} + +void AbstractFifo::setTotalSize (int newSize) throw() +{ + jassert (newSize > 0); + reset(); + bufferSize = newSize; +} + +//============================================================================== +void AbstractFifo::prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw() +{ + const int vs = validStart.get(); + const int ve = validEnd.get(); + + const int freeSpace = bufferSize - (ve - vs); + numToWrite = jmin (numToWrite, freeSpace); + + if (numToWrite <= 0) + { + startIndex1 = 0; + startIndex2 = 0; + blockSize1 = 0; + blockSize2 = 0; + } + else + { + startIndex1 = (int) (ve % bufferSize); + startIndex2 = 0; + blockSize1 = jmin (bufferSize - startIndex1, numToWrite); + numToWrite -= blockSize1; + blockSize2 = numToWrite <= 0 ? 0 : jmin (numToWrite, (int) (vs % bufferSize)); + } +} + +void AbstractFifo::finishedWrite (int numWritten) throw() +{ + jassert (numWritten >= 0 && numWritten < bufferSize); + validEnd += numWritten; +} + +void AbstractFifo::prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw() +{ + const int vs = validStart.get(); + const int ve = validEnd.get(); + + const int numReady = ve - vs; + numWanted = jmin (numWanted, numReady); + + if (numWanted <= 0) + { + startIndex1 = 0; + startIndex2 = 0; + blockSize1 = 0; + blockSize2 = 0; + } + else + { + startIndex1 = (int) (vs % bufferSize); + startIndex2 = 0; + blockSize1 = jmin (bufferSize - startIndex1, numWanted); + numWanted -= blockSize1; + blockSize2 = numWanted <= 0 ? 0 : jmin (numWanted, (int) (ve % bufferSize)); + } +} + +void AbstractFifo::finishedRead (int numRead) throw() +{ + jassert (numRead >= 0 && numRead < bufferSize); + validStart += numRead; +} + + +END_JUCE_NAMESPACE diff --git a/src/containers/juce_AbstractFifo.h b/src/containers/juce_AbstractFifo.h new file mode 100644 index 0000000000..52871b3ead --- /dev/null +++ b/src/containers/juce_AbstractFifo.h @@ -0,0 +1,220 @@ +/* + ============================================================================== + + This file is part of the JUCE library - "Jules' Utility Class Extensions" + Copyright 2004-10 by Raw Material Software Ltd. + + ------------------------------------------------------------------------------ + + JUCE can be redistributed and/or modified under the terms of the GNU General + Public License (Version 2), as published by the Free Software Foundation. + A copy of the license is included in the JUCE distribution, or can be found + online 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.rawmaterialsoftware.com/juce for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_ABSTRACTFIFO_JUCEHEADER__ +#define __JUCE_ABSTRACTFIFO_JUCEHEADER__ + +#include "../core/juce_Atomic.h" + + +//============================================================================== +/** + Encapsulates the logic required to implement a lock-free FIFO. + + This class handles the logic needed when building a single-reader, single-writer FIFO. + + It doesn't actually hold any data itself, but your FIFO class can use one of these to manage + its position and status when reading or writing to it. + + To use it, you can call prepareToWrite() to determine the position within your own buffer that + an incoming block of data should be stored, and prepareToRead() to find out when the next + outgoing block should be read from. + + e.g. + @code + class MyFifo + { + public: + MyFifo() : abstractFifo (1024) + { + } + + void addToFifo (const int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToWrite (numItems, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (myBuffer + start1, someData, size1); + + if (size2 > 0) + copySomeData (myBuffer + start2, someData + size1, size2); + + finishedWrite (size1 + size2); + } + + void readFromFifo (int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToRead (numSamples, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (someData, myBuffer + start1, size1); + + if (size2 > 0) + copySomeData (someData + size1, myBuffer + start2, size2); + + finishedRead (size1 + size2); + } + + private: + AbstractFifo abstractFifo; + int myBuffer [1024]; + }; + @endcode +*/ +class JUCE_API AbstractFifo +{ +public: + //============================================================================== + /** Creates a FIFO to manage a buffer with the specified capacity. */ + AbstractFifo (int capacity) throw(); + + /** Destructor */ + ~AbstractFifo(); + + //============================================================================== + /** Returns the total size of the buffer being managed. */ + int getTotalSize() const throw(); + + /** Returns the number of items that can currently be added to the buffer without it overflowing. */ + int getFreeSpace() const throw(); + + /** Returns the number of items that can currently be read from the buffer. */ + int getNumReady() const throw(); + + /** Clears the buffer positions, so that it appears empty. */ + void reset() throw(); + + /** Changes the buffer's total size. + Note that this isn't thread-safe, so don't call it if there's any danger that it + might overlap with a call to any other method in this class! + */ + void setTotalSize (int newSize) throw(); + + //============================================================================== + /** Returns the location within the buffer at which an incoming block of data should be written. + + Because the section of data that you want to add to the buffer may overlap the end + and wrap around to the start, two blocks within your buffer are returned, and you + should copy your data into the first one, with any remaining data spilling over into + the second. + + If the number of items you ask for is too large to fit within the buffer's free space, then + blockSize1 + blockSize2 may add up to a lower value than numToWrite. + + After calling this method, and writing your data, you must call finishedWrite() to tell the + FIFO how much data you actually added. + + e.g. + @code + void addToFifo (const int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToWrite (numItems, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (myBuffer + start1, someData, size1); + + if (size2 > 0) + copySomeData (myBuffer + start2, someData + size1, size2); + + finishedWrite (size1 + size2); + } + @endcode + + @param numToWrite indicates how many items you'd like to add to the buffer + @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 + @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into + the first block should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex2 + @see finishedWrite + */ + void prepareToWrite (int numToWrite, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw(); + + /** Called after reading from the FIFO, to indicate that this many items have been added. + @see prepareToWrite + */ + void finishedWrite (int numWritten) throw(); + + /** Returns the location within the buffer from which the next block of data should be read. + + Because the section of data that you want to read from the buffer may overlap the end + and wrap around to the start, two blocks within your buffer are returned, and you + should read from both of them. + + If the number of items you ask for is greater than the amount of data available, then + blockSize1 + blockSize2 may add up to a lower value than numWanted. + + After calling this method, and reading the data, you must call finishedRead() to tell the + FIFO how much data you have consumed. + + e.g. + @code + void readFromFifo (int* someData, int numItems) + { + int start1, size1, start2, size2; + prepareToRead (numSamples, start1, size1, start2, size2); + + if (size1 > 0) + copySomeData (someData, myBuffer + start1, size1); + + if (size2 > 0) + copySomeData (someData + size1, myBuffer + start2, size2); + + finishedRead (size1 + size2); + } + @endcode + + @param numToWrite indicates how many items you'd like to add to the buffer + @param startIndex1 on exit, this will contain the start index in your buffer at which your data should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex1 + @param startIndex2 on exit, this will contain the start index in your buffer at which any data that didn't fit into + the first block should be written + @param blockSize1 on exit, this indicates how many items can be written to the block starting at startIndex2 + @see finishedRead + */ + void prepareToRead (int numWanted, int& startIndex1, int& blockSize1, int& startIndex2, int& blockSize2) throw(); + + /** Called after reading from the FIFO, to indicate that this many items have now been consumed. + @see prepareToRead + */ + void finishedRead (int numRead) throw(); + + + //============================================================================== + juce_UseDebuggingNewOperator + +private: + int bufferSize; + Atomic validStart, validEnd; + + AbstractFifo (const AbstractFifo&); + AbstractFifo& operator= (const AbstractFifo&); +}; + + +#endif // __JUCE_ABSTRACTFIFO_JUCEHEADER__ diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 4c310e63de..bf591cccd4 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 52 -#define JUCE_BUILDNUMBER 70 +#define JUCE_BUILDNUMBER 71 /** Current Juce version number. diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h index 71368e5d44..05e11be9a4 100644 --- a/src/juce_core_includes.h +++ b/src/juce_core_includes.h @@ -26,6 +26,9 @@ #ifndef __JUCE_JUCE_CORE_INCLUDES_INCLUDEFILES__ #define __JUCE_JUCE_CORE_INCLUDES_INCLUDEFILES__ +#ifndef __JUCE_ABSTRACTFIFO_JUCEHEADER__ + #include "containers/juce_AbstractFifo.h" +#endif #ifndef __JUCE_ARRAY_JUCEHEADER__ #include "containers/juce_Array.h" #endif