├── .clang-format
├── .clang-tidy
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ ├── build_and_test.yml
│ └── release.yml
├── .gitignore
├── .gitmodules
├── .travis.yml
├── CHANGELOG.md
├── CMakeLists.txt
├── License.md
├── ReadMe.md
├── appveyor.yml
├── cmake
└── FindFFmpeg.cmake
├── copy_externals.sh
├── docs
├── mc.rtpreceive~.maxref.xml
└── mc.rtpsend~.maxref.xml
├── help
├── mc.rtpsend~.maxhelp
├── rtphelpstarter.js
└── rtpsendreceive.hello-world.maxhelp
├── icon.png
├── package-info.json.in
├── rtpsendreceive.code-workspace
├── screenshot.jpg
├── setup_dev_env.sh
├── source
├── projects
│ ├── mc.rtpreceive_tilde
│ │ ├── CMakeLists.txt
│ │ ├── mc.rtpreceive_tilde.cpp
│ │ ├── mc.rtpreceive_tilde_test.cmake
│ │ └── mc.rtpreceive_tilde_test.cpp
│ └── mc.rtpsend_tilde
│ │ ├── CMakeLists.txt
│ │ ├── mc.rtpsend_tilde.cpp
│ │ ├── mc.rtpsend_tilde_test.cmake
│ │ └── mc.rtpsend_tilde_test.cpp
└── rtpsendreceive_lib
│ ├── CMakeLists.txt
│ ├── lockfree_ringbuffer.hpp
│ ├── lockfree_ringbuffer_test.hpp
│ ├── min_debugutils.hpp
│ ├── rtpreceiver.cpp
│ ├── rtpreceiver.hpp
│ ├── rtpsender.cpp
│ ├── rtpsender.hpp
│ ├── rtpsendreceive_lib.hpp
│ ├── rtpsendreceive_lib_test.cmake
│ ├── rtpsr_classes_test.cpp
│ ├── rtpsrbase.cpp
│ └── rtpsrbase.hpp
├── test_patch
├── receiver_test.maxpat
├── self_loop_test.maxpat
└── sender_test.maxpat
└── vcpkg-helper
├── x64-osx-rel.cmake
├── x64-windows-rel.cmake
├── x64-windows-static-md.cmake
└── x64-windows-static-rel.cmake
/.clang-format:
--------------------------------------------------------------------------------
1 | BasedOnStyle: WebKit
2 | SortIncludes: false
3 | ColumnLimit: 140
4 | UseTab: ForContinuationAndIndentation
5 | TabWidth: 4
6 |
7 | BreakBeforeBraces: Attach
8 |
9 | Cpp11BracedListStyle: true
10 | AllowShortFunctionsOnASingleLine: Empty
11 | AlwaysBreakTemplateDeclarations: true
12 | SpaceAfterTemplateKeyword: false
13 | ConstructorInitializerIndentWidth: 0
14 |
15 | NamespaceIndentation: All
16 | CompactNamespaces: true
17 | FixNamespaceComments: true
18 |
19 | SpacesBeforeTrailingComments: 4
20 | AllowAllArgumentsOnNextLine: false
21 | BinPackArguments: false
22 | BreakBeforeBraces: Custom
23 | BraceWrapping:
24 | AfterClass: false
25 | AfterControlStatement: false
26 | AfterEnum: false
27 | AfterFunction: false
28 | AfterNamespace: false
29 | AfterObjCDeclaration: false
30 | AfterStruct: false
31 | AfterUnion: false
32 | AfterExternBlock: false
33 | BeforeCatch: true
34 | BeforeElse: true
35 | IndentBraces: false
36 | SplitEmptyFunction: false
37 | SplitEmptyRecord: false
38 | SplitEmptyNamespace: false
39 |
40 | AlignConsecutiveAssignments: true
41 | AlignConsecutiveDeclarations: true
42 | AlignEscapedNewlines: Right
43 | AlignTrailingComments: true
44 |
45 | IndentCaseLabels: true
46 | MaxEmptyLinesToKeep: 2
47 |
--------------------------------------------------------------------------------
/.clang-tidy:
--------------------------------------------------------------------------------
1 | Checks: "clang-diagnostic-*,\
2 | clang-analyzer-*,\
3 | cppcoreguidelines-*,\
4 | google-*,\
5 | modernize-*,\
6 | misc-*,\
7 | readability-*,\
8 | performance-*,\
9 | portability-*,\
10 | -cppcoreguidelines-pro-type-member-init,\
11 | -cppcoreguidelines-special-member-functions,\
12 | -cppcoreguidelines-pro-type-reinterpret-cast,\
13 | -performance-unnecessary-value-param,\
14 | -modernize-use-trailing-return-type,\
15 | -misc-non-private-member-variables-in-classes,\
16 | -google-runtime-references\
17 | "
18 | HeaderFilterRegex: 'src.*\.(hpp|h)'
19 | CheckOptions:
20 | - key: readability-identifier-naming.ClassCase
21 | value: CamelCase
22 | - key: readability-identifier-naming.EnumCase
23 | value: CamelCase
24 | - key: readability-identifier-naming.FunctionCase
25 | value: camelBack
26 | - key: readability-identifier-naming.MemberCase
27 | value: lower_case
28 | - key: readability-identifier-naming.ParameterCase
29 | value: lower_case
30 | - key: readability-identifier-naming.UnionCase
31 | value: CamelCase
32 | - key: readability-identifier-naming.VariableCase
33 | value: lower_case
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 |
32 | **Additional context**
33 | Add any other context about the problem here.
34 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/build_and_test.yml:
--------------------------------------------------------------------------------
1 | name: build & test
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - dev
7 | pull_request:
8 |
9 | jobs:
10 | build:
11 | runs-on: ${{ matrix.os }}
12 | timeout-minutes: 120
13 | strategy:
14 | fail-fast: false
15 | matrix:
16 | os:
17 | - macos-latest
18 | - windows-latest
19 | config:
20 | - Debug
21 | - Release
22 | include:
23 | - os: windows-latest
24 | vcpkg-target-triplet: x64-windows-rel
25 | cache-path: \vcpkg\installed
26 | - os: macos-latest
27 | vcpkg-target-triplet: x64-osx-rel
28 | cache-path: /vcpkg/installed
29 |
30 | steps:
31 | - uses: actions/checkout@v3
32 | with:
33 | submodules: true
34 | - uses: ilammy/msvc-dev-cmd@v1
35 | - uses: actions/cache@v3
36 | id: ffmpeg-cache
37 | with:
38 | path: ${{ github.workspace }}${{ matrix.cache-path }}
39 | key: ${{ runner.os }}-vcpkg-${{ hashFiles('**/vcpkg/ports/ffmpeg/vcpkg.json')}}
40 | - if: contains(matrix.os, 'windows')
41 | name: Install ffmpeg via vcpkg
42 | run: |
43 | cp .\vcpkg-helper\${{ matrix.vcpkg-target-triplet }}.cmake .\vcpkg\triplets\
44 | .\vcpkg\bootstrap-vcpkg.bat
45 | .\vcpkg\vcpkg.exe install ffmpeg[avformat,avdevice,avcodec,core]:${{ matrix.vcpkg-target-triplet }}
46 | - if: contains(matrix.os, 'macos')
47 | name: Install ffmpeg via vcpkg(macos)
48 | run: |
49 | cp ./vcpkg-helper/${{ matrix.vcpkg-target-triplet }}.cmake ./vcpkg/triplets/
50 | brew upgrade
51 | brew install yasm nasm
52 | ./vcpkg/bootstrap-vcpkg.sh
53 | ./vcpkg/vcpkg install ffmpeg[avformat,avdevice,avcodec,core]:${{ matrix.vcpkg-target-triplet }}
54 | - name: configure project
55 | run: cmake -B build -DCMAKE_BUILD_CONFIG=${{ matrix.config }} . -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg-target-triplet }}
56 | - name: build
57 | run: cmake --build build --config ${{ matrix.config }} -v
58 | - uses: papeloto/action-zip@v1
59 | with:
60 | files: docs externals help CHANGELOG.md package-info.json ReadMe.md screenshot.jpg License.md
61 | dest: rtpsendreceive-${{matrix.os}}-${{matrix.config}}.zip
62 | - uses: actions/upload-artifact@v3
63 | continue-on-error: true
64 | with:
65 | name: rtpsendreceive-${{matrix.os}}-${{matrix.config}}
66 | path: ${{github.workspace}}/rtpsendreceive-${{matrix.os}}-${{matrix.config}}.zip
67 | # - name: test
68 | # run: cd build && ctest --build-config ${{ matrix.config }}
69 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: release
2 | on:
3 | push:
4 | tags:
5 | - 'v*'
6 |
7 | jobs:
8 | create_release:
9 | name: "Create Release"
10 | runs-on : ubuntu-20.04
11 | env:
12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13 | outputs:
14 | upload_url: ${{ steps.create_release.outputs.upload_url }}
15 | steps:
16 | - uses: actions/checkout@v3
17 | - name: Create Source Release
18 | id: create_release
19 | uses: actions/create-release@v1.0.0
20 | with:
21 | tag_name: ${{ github.ref }}
22 | release_name: Release ${{ github.ref }}
23 | draft: false
24 | prerelease: false
25 |
26 | build_and_upload:
27 | name: "Build and Release"
28 | needs: create_release
29 | runs-on: ${{ matrix.os }}
30 | timeout-minutes: 120
31 | strategy:
32 | fail-fast: false
33 | matrix:
34 | os:
35 | - macos-latest
36 | - windows-latest
37 | config:
38 | - Release
39 | include:
40 | - os: windows-latest
41 | vcpkg-target-triplet: x64-windows-rel
42 | cache-path: \vcpkg\installed
43 | zip_postfix: Windows
44 | - os: macos-latest
45 | vcpkg-target-triplet: x64-osx-rel
46 | cache-path: /vcpkg/installed
47 | zip_postfix: macOS
48 |
49 | steps:
50 | - uses: actions/checkout@v3
51 | with:
52 | submodules: true
53 | - uses: ilammy/msvc-dev-cmd@v1
54 | - uses: actions/cache@v3
55 | id: ffmpeg-cache
56 | with:
57 | path: ${{ github.workspace }}${{ matrix.cache-path }}
58 | key: ${{ runner.os }}-vcpkg-${{ hashFiles('**/vcpkg/ports/ffmpeg/vcpkg.json') }}
59 | - if: contains(matrix.os, 'windows')
60 | name: Install ffmpeg via vcpkg
61 | run: |
62 | cp .\vcpkg-helper\${{ matrix.vcpkg-target-triplet }}.cmake .\vcpkg\triplets\
63 | .\vcpkg\bootstrap-vcpkg.bat
64 | .\vcpkg\vcpkg.exe install ffmpeg[avformat,avdevice,avcodec,core]
65 | name: Install ffmpeg via vcpkg(macos)
66 | run: |
67 | cp ./vcpkg-helper/${{ matrix.vcpkg-target-triplet }}.cmake ./vcpkg/triplets/
68 | brew upgrade
69 | brew install yasm nasm
70 | ./vcpkg/bootstrap-vcpkg.sh
71 | ./vcpkg/vcpkg install ffmpeg[avformat,avdevice,avcodec,core]:${{ matrix.vcpkg-target-triplet }}
72 | - name: configure project
73 | run: cmake -B build -DCMAKE_BUILD_CONFIG=${{ matrix.config }} . -DVCPKG_TARGET_TRIPLET=${{ matrix.vcpkg-target-triplet }}
74 | - name: build
75 | run: cmake --build build --config ${{ matrix.config }} -v
76 |
77 | - name: install
78 | run: |
79 | cmake -E make_directory mc.rtpsendreceive
80 | cmake -E copy_directory docs mc.rtpsendreceive/docs
81 | cmake -E copy_directory externals mc.rtpsendreceive/externals
82 | cmake -E copy_directory help mc.rtpsendreceive/help
83 | cmake -E copy CHANGELOG.md package-info.json ReadMe.md screenshot.jpg License.md mc.rtpsendreceive
84 |
85 | - name: Get the version
86 | id: get_version
87 | run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}
88 | shell: bash
89 |
90 | - uses: papeloto/action-zip@v1
91 | with:
92 | files: mc.rtpsendreceive
93 | dest: rtpsendreceive-${{ steps.get_version.outputs.VERSION }}-binary-${{ matrix.zip_postfix }}.zip
94 |
95 | - name: Upload Compiled Binary
96 | id: upload-release-asset
97 | uses: actions/upload-release-asset@v1.0.1
98 | env:
99 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
100 | with:
101 | upload_url: ${{ needs.create_release.outputs.upload_url }}
102 | asset_path: ./rtpsendreceive-${{ steps.get_version.outputs.VERSION }}-binary-${{ matrix.zip_postfix }}.zip
103 | asset_name: rtpsendreceive-${{ steps.get_version.outputs.VERSION }}-binary-${{ matrix.zip_postfix }}.zip
104 | asset_content_type: application/zip
105 |
106 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | __*
2 | sysbuild
3 | *.sdf
4 | *.suo
5 | *.sln
6 | *.opensdf
7 | log.txt
8 | externals
9 | extensions
10 | support
11 | build
12 | tests
13 | *.o
14 | *.dylib
15 | tmp
16 | .DS_Store
17 | .vscode/
18 |
19 | test_patch/*.mxo
20 | .dsp_cache
21 |
22 |
23 | package-info.json
24 |
25 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "source/min-api"]
2 | path = source/min-api
3 | url = https://github.com/tomoyanonymous/min-api.git
4 | [submodule "source/min-lib"]
5 | path = source/min-lib
6 | url = https://github.com/Cycling74/min-lib.git
7 | [submodule "vcpkg"]
8 | path = vcpkg
9 | url = https://github.com/microsoft/vcpkg
10 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language:
2 | - objective-c
3 | - c++
4 |
5 | compiler:
6 | - clang
7 |
8 | os: osx
9 |
10 | matrix:
11 | include:
12 | # - osx_image: xcode10.1
13 | # env: CONFIG=Debug
14 | # - osx_image: xcode10.1
15 | # env: CONFIG=Release
16 | - osx_image: xcode11.2
17 | env: CONFIG=Debug
18 | - osx_image: xcode11.2
19 | env: CONFIG=Release
20 | - osx_image: xcode11.4
21 | env: CONFIG=Debug
22 | - osx_image: xcode11.4
23 | env: CONFIG=Release
24 |
25 | before_script:
26 | - brew install opus
27 | - git clone https://git.ffmpeg.org/ffmpeg.git /tmp/ffmpeg
28 | - pushd /tmp/ffmpeg
29 | - ./configure --prefix=/usr/local/Cellar/ffmpeg/HEAD_Debug --disable-avfoundation --disable-iconv --disable-filters --disable-devices --disable-shared --enable-static --disable-optimizations --disable-mmx --disable-audiotoolbox --disable-videotoolbox --disable-stripping --disable-appkit --disable-zlib --disable-coreimage --disable-bzlib --disable-securetransport --disable-sdl2 --disable-lzma --enable-libopus --pkg-config-flags=--static --cc=/usr/bin/clang --cxx=/usr/bin/clang++
30 | - make -j
31 | - sudo make install
32 | - popd
33 |
34 |
35 | script:
36 | - mkdir build
37 | - cd build
38 | - cmake -G Xcode ..
39 | - cmake --build . --config ${CONFIG} | sed 's/-Wl,-.*//g'
40 | # - ctest -C ${CONFIG} . -V
41 | - cd ..
42 | - PACKAGE_NAME=`echo $TRAVIS_REPO_SLUG | sed 's/.*\///g'`
43 | - PACKAGE_REV=`echo $TRAVIS_COMMIT | sed -e 's/^[[:alnum:]]\{7\}/&-/g' | sed 's/-.*//'`
44 | - mkdir $PACKAGE_NAME
45 | - if [ -e package-info.json ]; then cp package-info.json $PACKAGE_NAME; fi
46 | - for f in *.md; do [ -e "$f" ] && cp "$f" $PACKAGE_NAME ; done
47 | - if [ -e icon.png ]; then cp icon.png $PACKAGE_NAME; fi
48 | - if [ -e CMakeLists.txt ]; then cp CMakeLists.txt $PACKAGE_NAME; fi
49 | - if [ -d code ]; then cp -r code $PACKAGE_NAME; fi
50 | - if [ -d docs ]; then cp -r docs $PACKAGE_NAME; fi
51 | - if [ -d examples ]; then cp -r examples $PACKAGE_NAME; fi
52 | - if [ -d extensions ]; then cp -r extensions $PACKAGE_NAME; fi
53 | - if [ -d externals ]; then cp -r externals $PACKAGE_NAME; fi
54 | - if [ -d extras ]; then cp -r extras $PACKAGE_NAME; fi
55 | - if [ -d help ]; then cp -r help $PACKAGE_NAME; fi
56 | - if [ -d init ]; then cp -r init $PACKAGE_NAME; fi
57 | - if [ -d java-classes ]; then cp -r java-classes $PACKAGE_NAME; fi
58 | - if [ -d java-doc ]; then cp -r java-doc $PACKAGE_NAME; fi
59 | - if [ -d javascript ]; then cp -r javascript $PACKAGE_NAME; fi
60 | - if [ -d jsui ]; then cp -r jsui $PACKAGE_NAME; fi
61 | - if [ -d media ]; then cp -r media $PACKAGE_NAME; fi
62 | - if [ -d misc ]; then cp -r misc $PACKAGE_NAME; fi
63 | - if [ -d patchers ]; then cp -r patchers $PACKAGE_NAME; fi
64 | - if [ -d support ]; then cp -r support $PACKAGE_NAME; fi
65 | - if [ -d source ]; then cp -r source $PACKAGE_NAME; fi
66 | - if [ -d tests ]; then cp -r tests $PACKAGE_NAME; fi
67 | - if [ -e $PACKAGE_NAME/ReadMe-Public.md ]; then rm -f $PACKAGE_NAME/ReadMe.md; mv $PACKAGE_NAME/ReadMe-Public.md $PACKAGE_NAME/ReadMe.md; fi
68 | - mkdir dist
69 | - CONFIG_LOWERCASE=`echo $CONFIG | tr '[A-Z]' '[a-z]'`
70 | - zip -r dist/$PACKAGE_NAME-mac-$PACKAGE_REV-$CONFIG_LOWERCASE.zip $PACKAGE_NAME
71 |
72 |
73 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG
2 |
3 |
4 | ## 2020-10-28 v0.3.0
5 |
6 | ### Windows version is now available.
7 |
8 | ffmpeg is now installed from vcpkg, registered as submodule.
9 |
10 | ### New Attributes
11 |
12 | ringbuf_framenum: set the buffer size of internal ringbuffer to collect samples asynchrounously between audio thread and network. Note that the number will be multiplied to Signal Vector Size in Audio Setting. Default is x4.
13 |
14 | ### Packet Latency
15 |
16 | "getlatency" message for `mc.rtpreceiver~` outputs packet latency from second outlet. Note that this latency just taken from RTP packets' timestamp and does not include a latency caused from buffering. Generally, large signal vector size may causes a large latency, too small buffer size causes audio glitch. 128~512 is recommended.
17 |
18 |
19 |
20 | ## 2020-10-28 v0.2.3
21 |
22 | C++ class refactorings & cleeanups.
23 |
24 | 2 new Attributes are added.
25 |
26 | use_rtsp(bool, default=true): you can choose either of using rtsp handshaking or raw rtp protocol.
27 |
28 | Note that, when using raw rtp protocol, receiver assumes audio parameter from internal parameters because it does no handshaking. Thus different paramters between sender and receiver in this mode may cause audio glitch or some unexpected behaviour.
29 |
30 | Also, it has a bug that turning on&off of dsp quickly will cause temporary hang for 4~5 second.
31 |
32 | ## 2020-09-03 v0.2.2
33 |
34 | Added GitHub workflow and funding pages.
35 |
36 | ### Fixed Bugs
37 | Fixed hangs when receiver is reinstanciated before finishes connection.
38 |
39 |
40 | ## 2020-09-03 v0.2.1
41 |
42 | ### Fixed Bugs
43 |
44 | Fixed hanging when sender/receiver could not connect to server.
45 | Now sender also transmits packets asynchronously from audio/main thread.
46 |
47 | ## 2020-09-03 v0.2.0
48 |
49 | This release contains many many refactorings.
50 | Especially, receiving packets and pushing samples to object's output works asynchronously on `mc.rtpreceiver~`.
51 | This resolved the problem that receiver blocks audio thread when no packet is arrived.
52 |
53 | Also, an initial connection protocol has been changed from raw rtp to RTSP.
54 |
55 | ### working issue
56 |
57 | - An attribute for choosing audio codec(PCM 16bit and Opus) has been added on this release but Opus is not working properly.
58 | - Receiver's audio codec parameters have not been taken from an infomation given by RTSP initialization.
59 |
60 | ## 2020-08-02 v0.1.3
61 |
62 | - fixed a problem that the app crashes when a frame does not contain format information.
63 |
64 | ## 2020-07-30 v0.1.2
65 |
66 | - working with multiple channels(confirmed up to 8ch)
67 | - fixed some crashes
68 |
69 | ## 2020-05-25 v0.1.0
70 |
71 | SUPER EARLY RELEASE BUILD for macOS.
72 |
73 | Zip file includes all externals, XML for object infos and source codes as well.
74 | To use the packages, drop unzipped folder into `~/Documents/Max 8/Packages`
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.19)
2 |
3 | project(rtpsendreceive
4 | LANGUAGES CXX)
5 |
6 | string(REGEX REPLACE "(.*)/" "" THIS_PACKAGE_NAME "${CMAKE_CURRENT_SOURCE_DIR}")
7 |
8 |
9 | set (CMAKE_CXX_STANDARD 17)
10 | set (CMAKE_CXX_STANDARD_REQUIRED ON)
11 |
12 | if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
13 |
14 | elseif(CMAKE_EXPORT_COMPILE_COMMANDS)
15 | set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES})
16 | endif()
17 |
18 | if (APPLE)
19 | if (${CMAKE_GENERATOR} MATCHES "Xcode")
20 | if (${XCODE_VERSION} VERSION_LESS 10)
21 | message(STATUS "Xcode 10 or higher is required. Please install from the Mac App Store.")
22 | return ()
23 | elseif(${XCODE_VERSION} VERSION_GREATER_EQUAL 12)
24 | set(C74_BUILD_FAT YES)
25 | endif ()
26 | endif ()
27 |
28 | if (NOT CMAKE_OSX_ARCHITECTURES)
29 | if(C74_BUILD_FAT)
30 | set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64" CACHE STRING "macOS architecture" FORCE)
31 | else()
32 | set(CMAKE_OSX_ARCHITECTURES ${CMAKE_SYSTEM_PROCESSOR} CACHE STRING "macOS architecture" FORCE)
33 | endif()
34 | message("CMAKE_OSX_ARCHITECTURES set to ${CMAKE_OSX_ARCHITECTURES}")
35 | endif()
36 | endif()
37 |
38 | # Fetch the correct version of the min-api
39 | message(STATUS "Updating Git Submodules")
40 | execute_process(
41 | COMMAND git submodule update --init --recursive
42 | WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
43 | )
44 |
45 |
46 | # Misc setup and subroutines
47 | include(${CMAKE_CURRENT_SOURCE_DIR}/source/min-api/script/min-package.cmake)
48 |
49 |
50 | # Add unit tests for the API
51 | enable_testing()
52 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/source/min-api)
53 |
54 |
55 | # Add the Lib, if it exists
56 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/source/min-lib/CMakeLists.txt")
57 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/source/min-lib)
58 | endif ()
59 |
60 |
61 | ### PROJECT_SPECIFIC LIBRARY(FFMPEG)
62 | set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
63 |
64 | if(NOT DEFINED VCPKG_TARGET_TRIPLET)
65 | if(APPLE)
66 | set(VCPKG_TARGET_TRIPLET x64-osx)
67 | elseif(WIN32)
68 | set(VCPKG_TARGET_TRIPLET x64-windows-static)
69 | endif()
70 | endif()
71 |
72 | set(VCPKG_PATH
73 | ${CMAKE_CURRENT_SOURCE_DIR}/vcpkg/installed/${VCPKG_TARGET_TRIPLET})
74 | message("vcpkg path: ${VCPKG_PATH}")
75 | find_package(FFmpeg
76 | REQUIRED
77 | ) #add here the list of ffmpeg components required
78 | # memo: building barematal ffmpeg for macos
79 |
80 | if(FFMPEG_FOUND)
81 | # FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers.
82 | # FFMPEG_LIBRARIES - Link these to use the required ffmpeg components.
83 | # FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components.
84 | message("FFMPEG_INCLUDE_DIRS = ${FFMPEG_INCLUDE_DIRS} ")
85 | message("FFMPEG_LIBRARIES = ${FFMPEG_LIBRARIES} ")
86 | message("FFMPEG_DEFINITIONS = ${FFMPEG_DEFINITIONS} ")
87 | endif()
88 | # find_library(OPUS_LIB
89 | # NAMES libopus.a opus
90 | # PATHS /usr/lib /usr/local/lib /usr/local/opt/opus/lib C:/ProgramData/chocolatey/lib/opus-tools/tools/lib
91 | # NO_DEFAULT_PATH
92 | # # REQUIRED
93 | # )
94 | # message("OPUS_LIB =${OPUS_LIB}")
95 |
96 | set(DEPENDENT_LIBS
97 | ${FFMPEG_LIBRARIES}
98 | # ${OPUS_LIB}
99 | )
100 |
101 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/source/rtpsendreceive_lib)
102 |
103 | # Generate a project for every folder in the "source/projects" folder
104 | SUBDIRLIST(PROJECT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/source/projects)
105 | foreach (project_dir ${PROJECT_DIRS})
106 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/source/projects/${project_dir}/CMakeLists.txt")
107 | message("Generating: ${project_dir}")
108 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/source/projects/${project_dir})
109 | endif ()
110 | endforeach ()
111 |
--------------------------------------------------------------------------------
/License.md:
--------------------------------------------------------------------------------
1 | GNU LESSER GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 |
9 | This version of the GNU Lesser General Public License incorporates
10 | the terms and conditions of version 3 of the GNU General Public
11 | License, supplemented by the additional permissions listed below.
12 |
13 | 0. Additional Definitions.
14 |
15 | As used herein, "this License" refers to version 3 of the GNU Lesser
16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU
17 | General Public License.
18 |
19 | "The Library" refers to a covered work governed by this License,
20 | other than an Application or a Combined Work as defined below.
21 |
22 | An "Application" is any work that makes use of an interface provided
23 | by the Library, but which is not otherwise based on the Library.
24 | Defining a subclass of a class defined by the Library is deemed a mode
25 | of using an interface provided by the Library.
26 |
27 | A "Combined Work" is a work produced by combining or linking an
28 | Application with the Library. The particular version of the Library
29 | with which the Combined Work was made is also called the "Linked
30 | Version".
31 |
32 | The "Minimal Corresponding Source" for a Combined Work means the
33 | Corresponding Source for the Combined Work, excluding any source code
34 | for portions of the Combined Work that, considered in isolation, are
35 | based on the Application, and not on the Linked Version.
36 |
37 | The "Corresponding Application Code" for a Combined Work means the
38 | object code and/or source code for the Application, including any data
39 | and utility programs needed for reproducing the Combined Work from the
40 | Application, but excluding the System Libraries of the Combined Work.
41 |
42 | 1. Exception to Section 3 of the GNU GPL.
43 |
44 | You may convey a covered work under sections 3 and 4 of this License
45 | without being bound by section 3 of the GNU GPL.
46 |
47 | 2. Conveying Modified Versions.
48 |
49 | If you modify a copy of the Library, and, in your modifications, a
50 | facility refers to a function or data to be supplied by an Application
51 | that uses the facility (other than as an argument passed when the
52 | facility is invoked), then you may convey a copy of the modified
53 | version:
54 |
55 | a) under this License, provided that you make a good faith effort to
56 | ensure that, in the event an Application does not supply the
57 | function or data, the facility still operates, and performs
58 | whatever part of its purpose remains meaningful, or
59 |
60 | b) under the GNU GPL, with none of the additional permissions of
61 | this License applicable to that copy.
62 |
63 | 3. Object Code Incorporating Material from Library Header Files.
64 |
65 | The object code form of an Application may incorporate material from
66 | a header file that is part of the Library. You may convey such object
67 | code under terms of your choice, provided that, if the incorporated
68 | material is not limited to numerical parameters, data structure
69 | layouts and accessors, or small macros, inline functions and templates
70 | (ten or fewer lines in length), you do both of the following:
71 |
72 | a) Give prominent notice with each copy of the object code that the
73 | Library is used in it and that the Library and its use are
74 | covered by this License.
75 |
76 | b) Accompany the object code with a copy of the GNU GPL and this license
77 | document.
78 |
79 | 4. Combined Works.
80 |
81 | You may convey a Combined Work under terms of your choice that,
82 | taken together, effectively do not restrict modification of the
83 | portions of the Library contained in the Combined Work and reverse
84 | engineering for debugging such modifications, if you also do each of
85 | the following:
86 |
87 | a) Give prominent notice with each copy of the Combined Work that
88 | the Library is used in it and that the Library and its use are
89 | covered by this License.
90 |
91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license
92 | document.
93 |
94 | c) For a Combined Work that displays copyright notices during
95 | execution, include the copyright notice for the Library among
96 | these notices, as well as a reference directing the user to the
97 | copies of the GNU GPL and this license document.
98 |
99 | d) Do one of the following:
100 |
101 | 0) Convey the Minimal Corresponding Source under the terms of this
102 | License, and the Corresponding Application Code in a form
103 | suitable for, and under terms that permit, the user to
104 | recombine or relink the Application with a modified version of
105 | the Linked Version to produce a modified Combined Work, in the
106 | manner specified by section 6 of the GNU GPL for conveying
107 | Corresponding Source.
108 |
109 | 1) Use a suitable shared library mechanism for linking with the
110 | Library. A suitable mechanism is one that (a) uses at run time
111 | a copy of the Library already present on the user's computer
112 | system, and (b) will operate properly with a modified version
113 | of the Library that is interface-compatible with the Linked
114 | Version.
115 |
116 | e) Provide Installation Information, but only if you would otherwise
117 | be required to provide such information under section 6 of the
118 | GNU GPL, and only to the extent that such information is
119 | necessary to install and execute a modified version of the
120 | Combined Work produced by recombining or relinking the
121 | Application with a modified version of the Linked Version. (If
122 | you use option 4d0, the Installation Information must accompany
123 | the Minimal Corresponding Source and Corresponding Application
124 | Code. If you use option 4d1, you must provide the Installation
125 | Information in the manner specified by section 6 of the GNU GPL
126 | for conveying Corresponding Source.)
127 |
128 | 5. Combined Libraries.
129 |
130 | You may place library facilities that are a work based on the
131 | Library side by side in a single library together with other library
132 | facilities that are not Applications and are not covered by this
133 | License, and convey such a combined library under terms of your
134 | choice, if you do both of the following:
135 |
136 | a) Accompany the combined library with a copy of the same work based
137 | on the Library, uncombined with any other library facilities,
138 | conveyed under the terms of this License.
139 |
140 | b) Give prominent notice with the combined library that part of it
141 | is a work based on the Library, and explaining where to find the
142 | accompanying uncombined form of the same work.
143 |
144 | 6. Revised Versions of the GNU Lesser General Public License.
145 |
146 | The Free Software Foundation may publish revised and/or new versions
147 | of the GNU Lesser General Public License from time to time. Such new
148 | versions will be similar in spirit to the present version, but may
149 | differ in detail to address new problems or concerns.
150 |
151 | Each version is given a distinguishing version number. If the
152 | Library as you received it specifies that a certain numbered version
153 | of the GNU Lesser General Public License "or any later version"
154 | applies to it, you have the option of following the terms and
155 | conditions either of that published version or of any later version
156 | published by the Free Software Foundation. If the Library as you
157 | received it does not specify a version number of the GNU Lesser
158 | General Public License, you may choose any version of the GNU Lesser
159 | General Public License ever published by the Free Software Foundation.
160 |
161 | If the Library as you received it specifies that a proxy can decide
162 | whether future versions of the GNU Lesser General Public License shall
163 | apply, that proxy's public statement of acceptance of any version is
164 | permanent authorization for you to choose that version for the
165 | Library.
--------------------------------------------------------------------------------
/ReadMe.md:
--------------------------------------------------------------------------------
1 | # mc.rtpsend~ / mc.rtpreceive~
2 |
3 | master:[](https://github.com/tomoyanonymous/rtpsendreceive/actions?query=workflow%3A%22build+%26+test%22) dev: [](https://github.com/tomoyanonymous/rtpsendreceive/actions?query=workflow%3A%22build+%26+test%22)
4 |
5 | *2022/01/06: This project is not actively maintained. [jit.ndi](https://github.com/pixsper/jit.ndi) also supports the transmission of uncompressed audio over local network and it should be more stable now. If you want to continue to maintain or develop this project, I will support. Just send PR or contact me.*
6 |
7 | External objects for Cycling'74 Max to send MSP signal over network using rtp protocol. An modern alternative to legacy `netsend~` & `netreceive~` objects.
8 |
9 | 
10 |
11 | - Send mc audio signal through 16bit-integer PCM signals.
12 | - Sending signals over NAT (= Global Network) is not supported. Consider use VPN.
13 |
14 | ## notes & todos
15 |
16 | - **When use this external, in Max's "Audio Setting", "IO Vector Size" and "Signal Vector Size" must be the same. If signal vector size is too smaller than IO vector size, it fails to send audio correctly.**
17 | - Currently number of channels are fixed by an attribute "channels", an auto-adaptation depending on input channels is not available due to a limitation of min-api.
18 | - A codec is fixed to Linear PCM 16bit(Big Endian). For future, Opus will be added.
19 |
20 | ## Installation
21 |
22 | Get latest version from [release](https://github.com/tomoyanonymous/rtpsendreceive/releases) page.
23 |
24 | Unzip downloaded folder and drop the root folder into Max package directory (for macOS, `~/Documents/Max 8/Packages`)
25 | if you don't need source anymore, you can delete files other than in `externals`,`docs`,`help` directories.
26 |
27 | ## Build from Source
28 |
29 | You need cmake and C,C++ compiler. This project uses ffmpeg library for rtp streaming.
30 | This project installs ffmpeg using [vcpkg](https://github.com/microsoft/vcpkg), a cross-platform paackage manager registered as submodule at `./vcpkg`.
31 |
32 | For a more detailed workflow, read GitHub Actions workflow in `./.github/workflows/build_and_test.yml`.
33 |
34 | ### macOS
35 |
36 | Currently tested on macOS 10.15.3, XCode 11.4.1.
37 |
38 | Open a terminal and type following commands.
39 |
40 | ```bash
41 | git clone https://github.com/tomoyanonymous/rtpsendreceive.git --recursive
42 | cd rtpsendreceive
43 |
44 | ./vcpkg/bootstrap-vcpkg.sh
45 | ./vcpkg/vcpkg install ffmpeg\[avformat,avdevice,avcodec,core\]
46 |
47 | cmake . -B build
48 | cmake --build build --target all
49 | ```
50 | ### Windows
51 |
52 | Currently tested on Visual Studio Community 2019
53 |
54 | Open "Developer Command Prompt for VS 2019" and type following commands.
55 |
56 | ```cmd
57 |
58 | git clone https://github.com/tomoyanonymous/rtpsendreceive.git --recursive
59 | cd rtpsendreceive
60 |
61 | .\vcpkg\bootstrap-vcpkg.bat
62 | .\vcpkg\vcpkg install ffmpeg[avformat,avdevice,avcodec,core]:x64-windows
63 |
64 | cmake . -B build
65 | cmake --build build --target all
66 | ```
67 |
68 | ## Copyrights
69 |
70 | Tomoya Matsuura 松浦知也
71 |
72 | https://matsuuratomoya.com/en
73 |
74 | ## License
75 |
76 | [LGPL v3.0](./License.md)
77 |
78 | ## Acknowledgements
79 |
80 | The objects are originally made for works cooparated with [stu.inc](http://stu.inc/).
81 |
82 |
83 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | shallow_clone: false
2 |
3 | image:
4 | - Visual Studio 2017
5 | - Visual Studio 2019
6 |
7 | platform:
8 | - x64
9 |
10 | configuration:
11 | - Debug
12 | - Release
13 |
14 | install:
15 | - git submodule update --init --recursive
16 |
17 | build_script:
18 | - mkdir build
19 | - cd build
20 | - SET VS_ARCH=""
21 | - if "%platform%" == "x64" SET VS_ARCH=-A%PLATFORM%
22 | - echo VS_ARCH = %VS_ARCH%
23 | - cmake %VS_ARCH% .. > %APPVEYOR_BUILD_FOLDER%\configure.log
24 | - cmake --build . --config %CONFIGURATION% > %APPVEYOR_BUILD_FOLDER%\build.log
25 | - cd ..
26 | - mkdir %APPVEYOR_PROJECT_NAME%
27 | - if exist docs cp -r docs %APPVEYOR_PROJECT_NAME%
28 | - if exist examples cp -r examples %APPVEYOR_PROJECT_NAME%
29 | - if exist extensions cp -r extensions %APPVEYOR_PROJECT_NAME%
30 | - if exist externals cp -r externals %APPVEYOR_PROJECT_NAME%
31 | - if exist extras cp -r extras %APPVEYOR_PROJECT_NAME%
32 | - if exist help cp -r help %APPVEYOR_PROJECT_NAME%
33 | - if exist init cp -r init %APPVEYOR_PROJECT_NAME%
34 | - if exist java-classes cp -r java-classes %APPVEYOR_PROJECT_NAME%
35 | - if exist java-doc cp -r java-doc %APPVEYOR_PROJECT_NAME%
36 | - if exist jsui cp -r jsui %APPVEYOR_PROJECT_NAME%
37 | - if exist patchers cp -r patchers %APPVEYOR_PROJECT_NAME%
38 | - if exist tests cp -r tests %APPVEYOR_PROJECT_NAME%
39 | - cp icon.png %APPVEYOR_PROJECT_NAME%
40 | - cp License.md %APPVEYOR_PROJECT_NAME%
41 | - cp package-info.json %APPVEYOR_PROJECT_NAME%
42 | - cp ReadMe.md %APPVEYOR_PROJECT_NAME%
43 | - set SHORT_COMMIT=%APPVEYOR_REPO_COMMIT:~0,7%
44 | - 7z a %APPVEYOR_PROJECT_NAME%-win-%platform%-%SHORT_COMMIT%-%CONFIGURATION%.zip %APPVEYOR_PROJECT_NAME% > %APPVEYOR_BUILD_FOLDER%\archive.log
45 |
46 | test_script:
47 | - cd build
48 | - ctest -C %CONFIGURATION% . -V > %APPVEYOR_BUILD_FOLDER%\test.log
49 |
50 | artifacts:
51 | - name: Build
52 | path: '*.zip'
53 | - name: Log files
54 | path: '*.log'
55 |
56 | on_failure:
57 | - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\configure.log
58 | - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\build.log
59 | - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\test.log
60 |
--------------------------------------------------------------------------------
/cmake/FindFFmpeg.cmake:
--------------------------------------------------------------------------------
1 | # retrieved from https://github.com/snikulov/cmake-modules/blob/master/FindFFmpeg.cmake
2 |
3 | # vim: ts=2 sw=2
4 | # - Try to find the required ffmpeg components(default: AVFORMAT, AVUTIL, AVCODEC)
5 | #
6 | # Once done this will define
7 | # FFMPEG_FOUND - System has the all required components.
8 | # FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers.
9 | # FFMPEG_LIBRARIES - Link these to use the required ffmpeg components.
10 | # FFMPEG_DEFINITIONS - Compiler switches required for using the required ffmpeg components.
11 | #
12 | # For each of the components it will additionally set.
13 | # - AVCODEC
14 | # - AVDEVICE
15 | # - AVFORMAT
16 | # - AVFILTER
17 | # - AVUTIL
18 | # - POSTPROC
19 | # - SWSCALE
20 | # the following variables will be defined
21 | # _FOUND - System has
22 | # _INCLUDE_DIRS - Include directory necessary for using the headers
23 | # _LIBRARIES - Link these to use
24 | # _DEFINITIONS - Compiler switches required for using
25 | # _VERSION - The components version
26 | #
27 | # Copyright (c) 2006, Matthias Kretz,
28 | # Copyright (c) 2008, Alexander Neundorf,
29 | # Copyright (c) 2011, Michael Jansen,
30 | #
31 | # Redistribution and use is allowed according to the terms of the BSD license.
32 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file.
33 |
34 | include(FindPackageHandleStandardArgs)
35 |
36 | # The default components were taken from a survey over other FindFFMPEG.cmake files
37 | if (NOT FFmpeg_FIND_COMPONENTS)
38 | set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL)
39 | endif ()
40 |
41 | #
42 | ### Macro: set_component_found
43 | #
44 | # Marks the given component as found if both *_LIBRARIES AND *_INCLUDE_DIRS is present.
45 | #
46 | macro(set_component_found _component )
47 | if (${_component}_LIBRARIES AND ${_component}_INCLUDE_DIRS)
48 | message(STATUS " - ${_component} found.")
49 | set(${_component}_FOUND TRUE)
50 | else ()
51 | message(STATUS " - ${_component} not found.")
52 | endif ()
53 | endmacro()
54 |
55 | #
56 | ### Macro: find_component
57 | #
58 | # Checks for the given component by invoking pkgconfig and then looking up the libraries and
59 | # include directories.
60 | #
61 |
62 | macro(find_component _component _pkgconfig _library _header)
63 |
64 | # if (NOT WIN32)
65 | # # use pkg-config to get the directories and then use these values
66 | # # in the FIND_PATH() and FIND_LIBRARY() calls
67 | # find_package(PkgConfig)
68 | # if (PKG_CONFIG_FOUND)
69 | # pkg_check_modules(PC_${_component} ${_pkgconfig})
70 | # endif ()
71 | # else()
72 | # endif ()
73 |
74 | find_path(${_component}_INCLUDE_DIRS ${_header}
75 | PATHS
76 | ${VCPKG_PATH}/include
77 | ${PC_LIB${_component}_INCLUDEDIR}
78 | ${PC_LIB${_component}_INCLUDE_DIRS}
79 | PATH_SUFFIXES
80 | ffmpeg
81 | )
82 |
83 | find_library(${_component}_LIBRARIES NAMES ${_library}
84 | PATHS
85 | ${VCPKG_PATH}/lib
86 | ${PC_LIB${_component}_LIBDIR}
87 | ${PC_LIB${_component}_LIBRARY_DIRS}
88 | )
89 |
90 | set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER} CACHE STRING "The ${_component} CFLAGS.")
91 | set(${_component}_VERSION ${PC_${_component}_VERSION} CACHE STRING "The ${_component} version number.")
92 |
93 | set_component_found(${_component})
94 |
95 | mark_as_advanced(
96 | ${_component}_INCLUDE_DIRS
97 | ${_component}_LIBRARIES
98 | ${_component}_DEFINITIONS
99 | ${_component}_VERSION)
100 |
101 | endmacro()
102 | if(NOT DEFINED FFMPEG_PLATFORM_DEPENDENT_LIBS)
103 | if(APPLE)
104 | find_library(VT_UNIT VideoToolbox)
105 | if (NOT VT_UNIT)
106 | message(FATAL_ERROR "VideoToolbox not found")
107 | endif()
108 | find_library(AT_UNIT AudioToolbox)
109 | if (NOT AT_UNIT)
110 | message(FATAL_ERROR "AudioToolbox not found")
111 | endif()
112 | find_library(SEC_UNIT Security)
113 | if (NOT SEC_UNIT)
114 | message(FATAL_ERROR "Security not found")
115 | endif()
116 | find_library(CF_UNIT CoreFoundation)
117 | if (NOT CF_UNIT)
118 | message(FATAL_ERROR "CoreFoundation not found")
119 | endif()
120 | find_library(CM_UNIT CoreMedia)
121 | if (NOT CM_UNIT)
122 | message(FATAL_ERROR "CoreMedia not found")
123 | endif()
124 | find_library(CV_UNIT CoreVideo)
125 | if (NOT CV_UNIT)
126 | message(FATAL_ERROR "CoreVideo not found")
127 | endif()
128 | list(APPEND FFMPEG_PLATFORM_DEPENDENT_LIBS ${VT_UNIT} ${AT_UNIT} ${SEC_UNIT} ${CF_UNIT} ${CM_UNIT} ${CV_UNIT})
129 | endif()
130 | if(WIN32)
131 | if(NOT CYGWIN)
132 | list(APPEND FFMPEG_PLATFORM_DEPENDENT_LIBS wsock32 ws2_32 secur32 bcrypt strmiids Vfw32 Shlwapi mfplat mfuuid)
133 | endif()
134 | else()
135 | list(APPEND FFMPEG_PLATFORM_DEPENDENT_LIBS m)
136 | endif()
137 | message("platform dependent libs :${FFMPEG_PLATFORM_DEPENDENT_LIBS}")
138 | endif()
139 |
140 | # Check for cached results. If there are skip the costly part.
141 | if (NOT FFMPEG_LIBRARIES)
142 |
143 |
144 | # Check for all possible component.
145 | find_component(AVCODEC libavcodec avcodec libavcodec/avcodec.h)
146 | find_component(AVFORMAT libavformat avformat libavformat/avformat.h)
147 | find_component(AVDEVICE libavdevice avdevice libavdevice/avdevice.h)
148 | find_component(AVUTIL libavutil avutil libavutil/avutil.h)
149 | find_component(AVFILTER libavfilter avfilter libavfilter/avfilter.h)
150 | find_component(SWSCALE libswscale swscale libswscale/swscale.h)
151 | find_component(POSTPROC libpostproc postproc libpostproc/postprocess.h)
152 | find_component(SWRESAMPLE libswresample swresample libswresample/swresample.h)
153 |
154 | # Check if the required components were found and add their stuff to the FFMPEG_* vars.
155 | foreach (_component ${FFmpeg_FIND_COMPONENTS})
156 | if (${_component}_FOUND)
157 | # message(STATUS "Required component ${_component} present.")
158 | set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${${_component}_LIBRARIES})
159 | set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} ${${_component}_DEFINITIONS})
160 | list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIRS})
161 | else ()
162 | # message(STATUS "Required component ${_component} missing.")
163 | endif ()
164 | endforeach ()
165 |
166 | # Build the include path with duplicates removed.
167 | if (FFMPEG_INCLUDE_DIRS)
168 | list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS)
169 | endif ()
170 | # cache the vars.
171 | set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE)
172 | set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} ${FFMPEG_PLATFORM_DEPENDENT_LIBS} CACHE STRING "The FFmpeg libraries." FORCE)
173 | set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE)
174 |
175 | mark_as_advanced(FFMPEG_INCLUDE_DIRS
176 | FFMPEG_LIBRARIES
177 | FFMPEG_DEFINITIONS)
178 |
179 | endif ()
180 |
181 | # Now set the noncached _FOUND vars for the components.
182 | foreach (_component AVCODEC AVDEVICE AVFORMAT AVUTIL POSTPROCESS SWSCALE)
183 | set_component_found(${_component})
184 | endforeach ()
185 |
186 | # Compile the list of required vars
187 | set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS)
188 | foreach (_component ${FFmpeg_FIND_COMPONENTS})
189 | list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARIES ${_component}_INCLUDE_DIRS)
190 | endforeach ()
191 |
192 | # Give a nice error message if some of the required vars are missing.
193 | find_package_handle_standard_args(FFmpeg DEFAULT_MSG ${_FFmpeg_REQUIRED_VARS})
--------------------------------------------------------------------------------
/copy_externals.sh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh
2 |
3 | /bin/cp -r -f "externals/mc.rtpsend~.mxo" "/Users/${USER}/Documents/Max 8/Projects/rtpsender/externals"
4 | /bin/cp -r -f "externals/mc.rtpreceive~.mxo" "/Users/${USER}/Documents/Max 8/Projects/rtpreceiver/externals"
5 |
6 |
--------------------------------------------------------------------------------
/docs/mc.rtpreceive~.maxref.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | Receive audio stream via rtp protocol
10 | Receive audio stream via rtp protocol.
11 |
12 |
13 |
14 |
15 |
16 | Tomoya Matsuura
17 | Audio
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 | Outputs a packet latency in milliseconds at second outlet
34 | Outputs a packet latency in milliseconds at second outlet.
35 |
36 |
37 |
38 | toggle play and pause
39 | toggle play and pause
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | Size of an internal ring buffer (multiplied with signal vector size
51 | Size of an internal ring buffer (multiplied with signal vector size.)
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | maximum accepted delay for reordering(in microseconds)
61 | maximum accepted delay for reordering(in microseconds)
62 |
63 |
64 |
65 | number of packets for reorder queue
66 | number of packets for reorder queue
67 |
68 |
69 |
70 | if set to false, use raw rtp protocol instead of using rtsp mux/demuxer(Some options are ignored)
71 | if set to false, use raw rtp protocol instead of using rtsp mux/demuxer(Some options are ignored).
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | A main incoming network port number
81 | A main incoming network port number.
82 |
83 |
84 |
85 | minimum port number used for rtsp internal transport
86 | minimum port number used for rtsp internal transport
87 |
88 |
89 |
90 | minimum port number used for rtsp internal transport
91 | minimum port number used for rtsp internal transport
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | Your IP address for an appropriate network interface
101 | Your IP address for an appropriate network interface.
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
--------------------------------------------------------------------------------
/docs/mc.rtpsend~.maxref.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | send audio stream via rtp protocol
10 | send audio stream via rtp protocol
11 |
12 |
13 |
14 |
15 |
16 | Tomoya Matsuura
17 | Audio
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | if set to false, use raw rtp protocol instead of using rtsp mux/demuxer(Some options are ignored)
46 | if set to false, use raw rtp protocol instead of using rtsp mux/demuxer(Some options are ignored).
47 |
48 |
49 |
50 | Retry intervals in milliseconds at connection
51 | Retry intervals in milliseconds at connection
52 |
53 |
54 |
55 | Size of an internal ring buffer (multiplied with signal vector size
56 | Size of an internal ring buffer (multiplied with signal vector size.)
57 |
58 |
59 |
60 | Destination Main Port
61 | Destination Main Port
62 |
63 |
64 |
65 | minimum port number used for rtsp internal transport
66 | minimum port number used for rtsp internal transport
67 |
68 |
69 |
70 | minimum port number used for rtsp internal transport
71 | minimum port number used for rtsp internal transport
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | Destination IP Address(can be a domain name)
81 | Destination IP Address(can be a domain name)
82 |
83 |
84 |
85 | number of packets for reorder queue
86 | number of packets for reorder queue
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/help/mc.rtpsend~.maxhelp:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "appversion" : {
5 | "major" : 8,
6 | "minor" : 1,
7 | "revision" : 5,
8 | "architecture" : "x64",
9 | "modernui" : 1
10 | }
11 | ,
12 | "classnamespace" : "box",
13 | "openrect" : [ 174.0, 125.0, 853.0, 749.0 ],
14 | "bglocked" : 0,
15 | "openinpresentation" : 0,
16 | "default_fontsize" : 12.0,
17 | "default_fontface" : 0,
18 | "default_fontname" : "Arial",
19 | "gridonopen" : 1,
20 | "gridsize" : [ 15.0, 15.0 ],
21 | "gridsnaponopen" : 1,
22 | "objectsnaponopen" : 1,
23 | "statusbarvisible" : 2,
24 | "toolbarvisible" : 1,
25 | "lefttoolbarpinned" : 0,
26 | "toptoolbarpinned" : 0,
27 | "righttoolbarpinned" : 0,
28 | "bottomtoolbarpinned" : 0,
29 | "toolbars_unpinned_last_save" : 0,
30 | "tallnewobj" : 0,
31 | "boxanimatetime" : 200,
32 | "enablehscroll" : 1,
33 | "enablevscroll" : 1,
34 | "devicewidth" : 853.0,
35 | "description" : "Transmit mc audio signal over IP network using rtp protocol. An internal sample format is an uncompressed PCM, not floating-point but a 16bit integer. The rtp packets are send on UDP protocol. User can choose way of initializing connection from either raw RTP (No handshake) or RTSP (handshake with TCP).",
36 | "digest" : "send audio stream via rtp protocol",
37 | "tags" : "",
38 | "style" : "",
39 | "subpatcher_template" : "",
40 | "showrootpatcherontab" : 0,
41 | "showontab" : 0,
42 | "assistshowspatchername" : 0,
43 | "boxes" : [ {
44 | "box" : {
45 | "id" : "obj-9",
46 | "maxclass" : "attrui",
47 | "numinlets" : 1,
48 | "numoutlets" : 1,
49 | "outlettype" : [ "" ],
50 | "patching_rect" : [ 295.0, 115.0, 150.0, 22.0 ]
51 | }
52 |
53 | }
54 | , {
55 | "box" : {
56 | "fontname" : "Arial",
57 | "fontsize" : 13.0,
58 | "id" : "obj-1",
59 | "maxclass" : "newobj",
60 | "numinlets" : 1,
61 | "numoutlets" : 1,
62 | "outlettype" : [ "" ],
63 | "patching_rect" : [ 295.0, 155.0, 174.0, 23.0 ],
64 | "saved_object_attributes" : {
65 | "filename" : "rtphelpstarter.js",
66 | "parameter_enable" : 0
67 | }
68 | ,
69 | "text" : "js rtphelpstarter mc.rtpsend~"
70 | }
71 |
72 | }
73 | , {
74 | "box" : {
75 | "fontname" : "Arial",
76 | "fontsize" : 13.0,
77 | "id" : "obj-5",
78 | "maxclass" : "newobj",
79 | "numinlets" : 0,
80 | "numoutlets" : 0,
81 | "patcher" : {
82 | "fileversion" : 1,
83 | "appversion" : {
84 | "major" : 8,
85 | "minor" : 1,
86 | "revision" : 5,
87 | "architecture" : "x64",
88 | "modernui" : 1
89 | }
90 | ,
91 | "classnamespace" : "box",
92 | "rect" : [ 0.0, 26.0, 853.0, 723.0 ],
93 | "bglocked" : 0,
94 | "openinpresentation" : 0,
95 | "default_fontsize" : 13.0,
96 | "default_fontface" : 0,
97 | "default_fontname" : "Arial",
98 | "gridonopen" : 1,
99 | "gridsize" : [ 15.0, 15.0 ],
100 | "gridsnaponopen" : 1,
101 | "objectsnaponopen" : 1,
102 | "statusbarvisible" : 2,
103 | "toolbarvisible" : 1,
104 | "lefttoolbarpinned" : 0,
105 | "toptoolbarpinned" : 0,
106 | "righttoolbarpinned" : 0,
107 | "bottomtoolbarpinned" : 0,
108 | "toolbars_unpinned_last_save" : 0,
109 | "tallnewobj" : 0,
110 | "boxanimatetime" : 200,
111 | "enablehscroll" : 1,
112 | "enablevscroll" : 1,
113 | "devicewidth" : 0.0,
114 | "description" : "",
115 | "digest" : "",
116 | "tags" : "",
117 | "style" : "",
118 | "subpatcher_template" : "",
119 | "showontab" : 1,
120 | "assistshowspatchername" : 0,
121 | "boxes" : [ ],
122 | "lines" : [ ]
123 | }
124 | ,
125 | "patching_rect" : [ 106.0, 88.0, 50.0, 23.0 ],
126 | "saved_object_attributes" : {
127 | "description" : "",
128 | "digest" : "",
129 | "fontsize" : 13.0,
130 | "globalpatchername" : "",
131 | "tags" : ""
132 | }
133 | ,
134 | "text" : "p ?",
135 | "varname" : "q_tab"
136 | }
137 |
138 | }
139 | , {
140 | "box" : {
141 | "id" : "obj-2",
142 | "maxclass" : "newobj",
143 | "numinlets" : 0,
144 | "numoutlets" : 0,
145 | "patcher" : {
146 | "fileversion" : 1,
147 | "appversion" : {
148 | "major" : 8,
149 | "minor" : 1,
150 | "revision" : 5,
151 | "architecture" : "x64",
152 | "modernui" : 1
153 | }
154 | ,
155 | "classnamespace" : "box",
156 | "rect" : [ 174.0, 151.0, 853.0, 723.0 ],
157 | "bglocked" : 0,
158 | "openinpresentation" : 0,
159 | "default_fontsize" : 12.0,
160 | "default_fontface" : 0,
161 | "default_fontname" : "Arial",
162 | "gridonopen" : 1,
163 | "gridsize" : [ 15.0, 15.0 ],
164 | "gridsnaponopen" : 1,
165 | "objectsnaponopen" : 1,
166 | "statusbarvisible" : 2,
167 | "toolbarvisible" : 1,
168 | "lefttoolbarpinned" : 0,
169 | "toptoolbarpinned" : 0,
170 | "righttoolbarpinned" : 0,
171 | "bottomtoolbarpinned" : 0,
172 | "toolbars_unpinned_last_save" : 0,
173 | "tallnewobj" : 0,
174 | "boxanimatetime" : 200,
175 | "enablehscroll" : 1,
176 | "enablevscroll" : 1,
177 | "devicewidth" : 0.0,
178 | "description" : "",
179 | "digest" : "",
180 | "tags" : "",
181 | "style" : "",
182 | "subpatcher_template" : "",
183 | "showontab" : 1,
184 | "isolateaudio" : 1,
185 | "assistshowspatchername" : 0,
186 | "boxes" : [ {
187 | "box" : {
188 | "fontname" : "Lato",
189 | "fontsize" : 13.0,
190 | "id" : "obj-3",
191 | "maxclass" : "comment",
192 | "numinlets" : 1,
193 | "numoutlets" : 0,
194 | "patching_rect" : [ 10.0, 58.540092766284943, 585.0, 22.0 ],
195 | "text" : "send audio stream via rtp protocol",
196 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ]
197 | }
198 |
199 | }
200 | , {
201 | "box" : {
202 | "id" : "obj-37",
203 | "maxclass" : "scope~",
204 | "numinlets" : 2,
205 | "numoutlets" : 0,
206 | "patching_rect" : [ 30.834952294826508, 589.330088555812836, 346.0, 113.495145857334137 ]
207 | }
208 |
209 | }
210 | , {
211 | "box" : {
212 | "id" : "obj-36",
213 | "maxclass" : "newobj",
214 | "numinlets" : 1,
215 | "numoutlets" : 1,
216 | "outlettype" : [ "bang" ],
217 | "patching_rect" : [ 30.834952294826508, 178.912618577480316, 58.0, 22.0 ],
218 | "text" : "loadbang"
219 | }
220 |
221 | }
222 | , {
223 | "box" : {
224 | "id" : "obj-35",
225 | "maxclass" : "message",
226 | "numinlets" : 2,
227 | "numoutlets" : 1,
228 | "outlettype" : [ "" ],
229 | "patching_rect" : [ 30.834952294826508, 216.504851400852203, 128.0, 22.0 ],
230 | "text" : "increment 1000. 1000."
231 | }
232 |
233 | }
234 | , {
235 | "box" : {
236 | "id" : "obj-32",
237 | "maxclass" : "newobj",
238 | "numinlets" : 1,
239 | "numoutlets" : 2,
240 | "outlettype" : [ "multichannelsignal", "" ],
241 | "patching_rect" : [ 30.834952294826508, 545.388345122337341, 346.0, 22.0 ],
242 | "text" : "mc.rtpreceive~ @address 127.0.0.1 @port 30000 @channels 8"
243 | }
244 |
245 | }
246 | , {
247 | "box" : {
248 | "bubble" : 1,
249 | "fontname" : "Arial",
250 | "fontsize" : 13.0,
251 | "id" : "obj-31",
252 | "linecount" : 3,
253 | "maxclass" : "comment",
254 | "numinlets" : 1,
255 | "numoutlets" : 0,
256 | "patching_rect" : [ 660.378758460283279, 389.825237154960632, 162.0, 54.0 ],
257 | "text" : "Internal buffer size relative to signal vector size"
258 | }
259 |
260 | }
261 | , {
262 | "box" : {
263 | "attr" : "ringbuf_framenum",
264 | "id" : "obj-30",
265 | "maxclass" : "attrui",
266 | "numinlets" : 1,
267 | "numoutlets" : 1,
268 | "outlettype" : [ "" ],
269 | "patching_rect" : [ 508.378758460283279, 405.825237154960632, 150.0, 22.0 ],
270 | "text_width" : 112.980456322431564
271 | }
272 |
273 | }
274 | , {
275 | "box" : {
276 | "bubble" : 1,
277 | "fontname" : "Arial",
278 | "fontsize" : 13.0,
279 | "id" : "obj-29",
280 | "linecount" : 2,
281 | "maxclass" : "comment",
282 | "numinlets" : 1,
283 | "numoutlets" : 0,
284 | "patching_rect" : [ 660.378758460283279, 319.412617206573486, 171.349513351917267, 40.0 ],
285 | "text" : "Acceptable number of packets for reordering"
286 | }
287 |
288 | }
289 | , {
290 | "box" : {
291 | "attr" : "reorder_queue_size",
292 | "id" : "obj-27",
293 | "maxclass" : "attrui",
294 | "numinlets" : 1,
295 | "numoutlets" : 1,
296 | "outlettype" : [ "" ],
297 | "patching_rect" : [ 508.378758460283279, 328.412617206573486, 150.0, 22.0 ],
298 | "text_width" : 112.980456322431564
299 | }
300 |
301 | }
302 | , {
303 | "box" : {
304 | "bubble" : 1,
305 | "fontname" : "Arial",
306 | "fontsize" : 13.0,
307 | "id" : "obj-26",
308 | "linecount" : 3,
309 | "maxclass" : "comment",
310 | "numinlets" : 1,
311 | "numoutlets" : 0,
312 | "patching_rect" : [ 660.378758460283279, 256.752423644065857, 171.349513351917267, 54.0 ],
313 | "text" : "Available Port Range for Audio Transmittion in RTSP mode"
314 | }
315 |
316 | }
317 | , {
318 | "box" : {
319 | "bubble" : 1,
320 | "fontname" : "Arial",
321 | "fontsize" : 13.0,
322 | "id" : "obj-25",
323 | "linecount" : 2,
324 | "maxclass" : "comment",
325 | "numinlets" : 1,
326 | "numoutlets" : 0,
327 | "patching_rect" : [ 313.165046334266663, 396.825237154960632, 177.174755990505219, 40.0 ],
328 | "text" : "Connection Retry Interval (in ms)"
329 | }
330 |
331 | }
332 | , {
333 | "box" : {
334 | "bubble" : 1,
335 | "fontname" : "Arial",
336 | "fontsize" : 13.0,
337 | "id" : "obj-24",
338 | "maxclass" : "comment",
339 | "numinlets" : 1,
340 | "numoutlets" : 0,
341 | "patching_rect" : [ 313.165046334266663, 367.247568130493164, 177.174755990505219, 25.0 ],
342 | "text" : "Use RTSP for handshake"
343 | }
344 |
345 | }
346 | , {
347 | "box" : {
348 | "bubble" : 1,
349 | "fontname" : "Arial",
350 | "fontsize" : 13.0,
351 | "id" : "obj-23",
352 | "maxclass" : "comment",
353 | "numinlets" : 1,
354 | "numoutlets" : 0,
355 | "patching_rect" : [ 313.165046334266663, 328.412617206573486, 177.174755990505219, 25.0 ],
356 | "text" : " Audio Channels"
357 | }
358 |
359 | }
360 | , {
361 | "box" : {
362 | "bubble" : 1,
363 | "fontname" : "Arial",
364 | "fontsize" : 13.0,
365 | "id" : "obj-22",
366 | "maxclass" : "comment",
367 | "numinlets" : 1,
368 | "numoutlets" : 0,
369 | "patching_rect" : [ 313.165046334266663, 290.548540055751801, 177.174755990505219, 25.0 ],
370 | "text" : "Destination Main Port"
371 | }
372 |
373 | }
374 | , {
375 | "box" : {
376 | "bubble" : 1,
377 | "fontname" : "Arial",
378 | "fontsize" : 13.0,
379 | "id" : "obj-28",
380 | "maxclass" : "comment",
381 | "numinlets" : 1,
382 | "numoutlets" : 0,
383 | "patching_rect" : [ 313.165046334266663, 256.752423644065857, 177.174755990505219, 25.0 ],
384 | "text" : "Destination IP Address"
385 | }
386 |
387 | }
388 | , {
389 | "box" : {
390 | "attr" : "max_port",
391 | "id" : "obj-21",
392 | "maxclass" : "attrui",
393 | "numinlets" : 1,
394 | "numoutlets" : 1,
395 | "outlettype" : [ "" ],
396 | "patching_rect" : [ 508.378758460283279, 290.548540055751801, 150.0, 22.0 ],
397 | "text_width" : 72.815532982349396
398 | }
399 |
400 | }
401 | , {
402 | "box" : {
403 | "attr" : "min_port",
404 | "id" : "obj-18",
405 | "maxclass" : "attrui",
406 | "numinlets" : 1,
407 | "numoutlets" : 1,
408 | "outlettype" : [ "" ],
409 | "patching_rect" : [ 508.378758460283279, 256.752423644065857, 150.0, 22.0 ],
410 | "text_width" : 72.815532982349396
411 | }
412 |
413 | }
414 | , {
415 | "box" : {
416 | "attr" : "retry_rate",
417 | "id" : "obj-19",
418 | "maxclass" : "attrui",
419 | "numinlets" : 1,
420 | "numoutlets" : 1,
421 | "outlettype" : [ "" ],
422 | "patching_rect" : [ 161.165046334266663, 405.825237154960632, 150.0, 22.0 ],
423 | "text_width" : 72.815532982349396
424 | }
425 |
426 | }
427 | , {
428 | "box" : {
429 | "attr" : "use_rtsp",
430 | "id" : "obj-20",
431 | "maxclass" : "attrui",
432 | "numinlets" : 1,
433 | "numoutlets" : 1,
434 | "outlettype" : [ "" ],
435 | "patching_rect" : [ 161.165046334266663, 369.90290755033493, 150.0, 22.0 ],
436 | "text_width" : 72.815532982349396
437 | }
438 |
439 | }
440 | , {
441 | "box" : {
442 | "attr" : "channels",
443 | "id" : "obj-17",
444 | "maxclass" : "attrui",
445 | "numinlets" : 1,
446 | "numoutlets" : 1,
447 | "outlettype" : [ "" ],
448 | "patching_rect" : [ 161.165046334266663, 330.097082853317261, 150.0, 22.0 ],
449 | "text_width" : 72.815532982349396
450 | }
451 |
452 | }
453 | , {
454 | "box" : {
455 | "attr" : "port",
456 | "id" : "obj-16",
457 | "maxclass" : "attrui",
458 | "numinlets" : 1,
459 | "numoutlets" : 1,
460 | "outlettype" : [ "" ],
461 | "patching_rect" : [ 161.165046334266663, 294.174753248691559, 150.0, 22.0 ],
462 | "text_width" : 72.815532982349396
463 | }
464 |
465 | }
466 | , {
467 | "box" : {
468 | "attr" : "address",
469 | "id" : "obj-15",
470 | "maxclass" : "attrui",
471 | "numinlets" : 1,
472 | "numoutlets" : 1,
473 | "outlettype" : [ "" ],
474 | "patching_rect" : [ 161.165046334266663, 258.252423644065857, 150.0, 22.0 ],
475 | "text_width" : 72.815532982349396
476 | }
477 |
478 | }
479 | , {
480 | "box" : {
481 | "fontname" : "Lato",
482 | "fontsize" : 13.0,
483 | "id" : "obj-13",
484 | "maxclass" : "comment",
485 | "numinlets" : 1,
486 | "numoutlets" : 0,
487 | "patching_rect" : [ 518.602054506540298, 680.825234413146973, 313.126217305660248, 22.0 ],
488 | "text" : "https://github.com/tomoyanonymous/rtpsendreceive",
489 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ],
490 | "textjustification" : 2
491 | }
492 |
493 | }
494 | , {
495 | "box" : {
496 | "fontname" : "Lato",
497 | "fontsize" : 13.0,
498 | "id" : "obj-12",
499 | "maxclass" : "comment",
500 | "numinlets" : 1,
501 | "numoutlets" : 0,
502 | "patching_rect" : [ 626.369043320417404, 656.825234413146973, 205.359228491783142, 22.0 ],
503 | "text" : "Made by Tomoya Matsuura",
504 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ],
505 | "textjustification" : 2
506 | }
507 |
508 | }
509 | , {
510 | "box" : {
511 | "id" : "obj-11",
512 | "maxclass" : "newobj",
513 | "numinlets" : 2,
514 | "numoutlets" : 1,
515 | "outlettype" : [ "multichannelsignal" ],
516 | "patching_rect" : [ 30.834952294826508, 258.252423644065857, 120.0, 22.0 ],
517 | "text" : "mc.cycle~ @chans 8"
518 | }
519 |
520 | }
521 | , {
522 | "box" : {
523 | "fontname" : "Lato",
524 | "fontsize" : 13.0,
525 | "id" : "obj-10",
526 | "linecount" : 4,
527 | "maxclass" : "comment",
528 | "numinlets" : 1,
529 | "numoutlets" : 0,
530 | "patching_rect" : [ 10.320512861013412, 82.540092766284943, 585.0, 69.0 ],
531 | "text" : "Transmit mc audio signal over IP network using rtp protocol. An internal sample format is an uncompressed PCM, not floating-point but a 16bit integer.\nThe rtp packets are send on UDP protocol. User can choose way of initializing connection from either raw RTP (No handshake) or RTSP (handshake with TCP).",
532 | "textcolor" : [ 0.490196078431373, 0.498039215686275, 0.517647058823529, 1.0 ]
533 | }
534 |
535 | }
536 | , {
537 | "box" : {
538 | "fontname" : "Lato",
539 | "fontsize" : 13.0,
540 | "id" : "obj-9",
541 | "linecount" : 7,
542 | "maxclass" : "comment",
543 | "numinlets" : 1,
544 | "numoutlets" : 0,
545 | "patching_rect" : [ 533.165161103010178, 491.019413828849792, 298.563110709190369, 116.0 ],
546 | "text" : "Note for port assignment:\nOne more port next to main port will be used for RTCP protocol.\nIn RTSP mode, initial handshake is done via main port and a port for audio transmission will be choosen automatically. User can limit the port by min_port and max_port attribute.",
547 | "textcolor" : [ 0.0, 0.0, 0.0, 1.0 ]
548 | }
549 |
550 | }
551 | , {
552 | "box" : {
553 | "fontname" : "Lato",
554 | "fontsize" : 48.0,
555 | "id" : "obj-5",
556 | "maxclass" : "comment",
557 | "numinlets" : 1,
558 | "numoutlets" : 0,
559 | "patching_rect" : [ 10.0, 0.0, 275.941748917102814, 64.0 ],
560 | "text" : "mc.rtpsend~"
561 | }
562 |
563 | }
564 | , {
565 | "box" : {
566 | "id" : "obj-1",
567 | "maxclass" : "newobj",
568 | "numinlets" : 1,
569 | "numoutlets" : 0,
570 | "patching_rect" : [ 30.834952294826508, 491.019413828849792, 333.0, 22.0 ],
571 | "text" : "mc.rtpsend~ @address 127.0.0.1 @port 30000 @channels 8"
572 | }
573 |
574 | }
575 | ],
576 | "lines" : [ {
577 | "patchline" : {
578 | "destination" : [ "obj-1", 0 ],
579 | "source" : [ "obj-11", 0 ]
580 | }
581 |
582 | }
583 | , {
584 | "patchline" : {
585 | "destination" : [ "obj-1", 0 ],
586 | "midpoints" : [ 170.665046334266663, 289.349513351917267, 40.334952294826508, 289.349513351917267 ],
587 | "source" : [ "obj-15", 0 ]
588 | }
589 |
590 | }
591 | , {
592 | "patchline" : {
593 | "destination" : [ "obj-1", 0 ],
594 | "midpoints" : [ 170.665046334266663, 325.349513351917267, 40.334952294826508, 325.349513351917267 ],
595 | "source" : [ "obj-16", 0 ]
596 | }
597 |
598 | }
599 | , {
600 | "patchline" : {
601 | "destination" : [ "obj-1", 0 ],
602 | "midpoints" : [ 170.665046334266663, 365.233008444309235, 40.334952294826508, 365.233008444309235 ],
603 | "source" : [ "obj-17", 0 ]
604 | }
605 |
606 | }
607 | , {
608 | "patchline" : {
609 | "destination" : [ "obj-1", 0 ],
610 | "midpoints" : [ 517.878758460283279, 281.233008444309235, 494.815534353256226, 281.233008444309235, 494.815534353256226, 467.281553983688354, 40.334952294826508, 467.281553983688354 ],
611 | "source" : [ "obj-18", 0 ]
612 | }
613 |
614 | }
615 | , {
616 | "patchline" : {
617 | "destination" : [ "obj-1", 0 ],
618 | "midpoints" : [ 170.665046334266663, 434.233008444309235, 40.334952294826508, 434.233008444309235 ],
619 | "source" : [ "obj-19", 0 ]
620 | }
621 |
622 | }
623 | , {
624 | "patchline" : {
625 | "destination" : [ "obj-1", 0 ],
626 | "midpoints" : [ 170.665046334266663, 398.233008444309235, 40.334952294826508, 398.233008444309235 ],
627 | "source" : [ "obj-20", 0 ]
628 | }
629 |
630 | }
631 | , {
632 | "patchline" : {
633 | "destination" : [ "obj-1", 0 ],
634 | "midpoints" : [ 517.878758460283279, 322.970872402191162, 494.815534353256226, 322.970872402191162, 494.815534353256226, 465.339806437492371, 40.334952294826508, 465.339806437492371 ],
635 | "source" : [ "obj-21", 0 ]
636 | }
637 |
638 | }
639 | , {
640 | "patchline" : {
641 | "destination" : [ "obj-1", 0 ],
642 | "midpoints" : [ 517.878758460283279, 363.106795251369476, 494.815534353256226, 363.106795251369476, 494.815534353256226, 467.281553983688354, 40.334952294826508, 467.281553983688354 ],
643 | "source" : [ "obj-27", 0 ]
644 | }
645 |
646 | }
647 | , {
648 | "patchline" : {
649 | "destination" : [ "obj-1", 0 ],
650 | "midpoints" : [ 517.878758460283279, 467.281553983688354, 40.334952294826508, 467.281553983688354 ],
651 | "source" : [ "obj-30", 0 ]
652 | }
653 |
654 | }
655 | , {
656 | "patchline" : {
657 | "destination" : [ "obj-37", 0 ],
658 | "source" : [ "obj-32", 0 ]
659 | }
660 |
661 | }
662 | , {
663 | "patchline" : {
664 | "destination" : [ "obj-11", 0 ],
665 | "source" : [ "obj-35", 0 ]
666 | }
667 |
668 | }
669 | , {
670 | "patchline" : {
671 | "destination" : [ "obj-35", 0 ],
672 | "source" : [ "obj-36", 0 ]
673 | }
674 |
675 | }
676 | ]
677 | }
678 | ,
679 | "patching_rect" : [ 18.0, 87.5, 47.0, 22.0 ],
680 | "saved_object_attributes" : {
681 | "description" : "",
682 | "digest" : "",
683 | "globalpatchername" : "",
684 | "tags" : ""
685 | }
686 | ,
687 | "text" : "p basic"
688 | }
689 |
690 | }
691 | ],
692 | "lines" : [ {
693 | "patchline" : {
694 | "destination" : [ "obj-1", 0 ],
695 | "source" : [ "obj-9", 0 ]
696 | }
697 |
698 | }
699 | ],
700 | "dependency_cache" : [ {
701 | "name" : "rtphelpstarter.js",
702 | "bootpath" : "~/codes/rtpsendreceive/help",
703 | "patcherrelativepath" : ".",
704 | "type" : "TEXT",
705 | "implicit" : 1
706 | }
707 | , {
708 | "name" : "mc.rtpsend~.mxo",
709 | "type" : "iLaX"
710 | }
711 | , {
712 | "name" : "mc.rtpreceive~.mxo",
713 | "type" : "iLaX"
714 | }
715 | ],
716 | "autosave" : 0
717 | }
718 |
719 | }
720 |
--------------------------------------------------------------------------------
/help/rtphelpstarter.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | var task = new Task(init, this);
4 | task.schedule(100);
5 |
6 | function init()
7 | {
8 |
9 | var b = this.patcher.getnamed("q_tab");
10 | if (b == null)
11 | {
12 | this.patcher.message("script", "newobject", "newobj", "@text","p ?", "@varname", "q_tab", "@patching_rect", 205, 205, 50, 20);
13 | var q=this.patcher.getnamed("q_tab");
14 | q.subpatcher().message("wclose");
15 | q.message("showontab", 1);
16 | }
17 | }
18 |
19 | function resize(x, y)
20 | {
21 | if(x==null)
22 | {
23 | this.patcher.wind.size=[853, 725];
24 | }
25 | else
26 | {
27 | this.patcher.wind.size= [x, y];
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/help/rtpsendreceive.hello-world.maxhelp:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "appversion" : {
5 | "major" : 8,
6 | "minor" : 0,
7 | "revision" : 0,
8 | "architecture" : "x64",
9 | "modernui" : 1
10 | }
11 | ,
12 | "rect" : [ 63.0, 104.0, 540.0, 384.0 ],
13 | "bglocked" : 0,
14 | "openinpresentation" : 0,
15 | "default_fontsize" : 12.0,
16 | "default_fontface" : 0,
17 | "default_fontname" : "Lato Light",
18 | "gridonopen" : 1,
19 | "gridsize" : [ 5.0, 5.0 ],
20 | "gridsnaponopen" : 2,
21 | "objectsnaponopen" : 0,
22 | "statusbarvisible" : 2,
23 | "toolbarvisible" : 1,
24 | "lefttoolbarpinned" : 2,
25 | "toptoolbarpinned" : 2,
26 | "righttoolbarpinned" : 2,
27 | "bottomtoolbarpinned" : 2,
28 | "toolbars_unpinned_last_save" : 15,
29 | "tallnewobj" : 0,
30 | "boxanimatetime" : 200,
31 | "enablehscroll" : 1,
32 | "enablevscroll" : 1,
33 | "devicewidth" : 0.0,
34 | "description" : "",
35 | "digest" : "",
36 | "tags" : "",
37 | "style" : "tap",
38 | "subpatcher_template" : "tap.template",
39 | "boxes" : [ {
40 | "box" : {
41 | "clickedimage" : 1,
42 | "id" : "obj-16",
43 | "maxclass" : "pictctrl",
44 | "name" : "min.edit-button.png",
45 | "numinlets" : 1,
46 | "numoutlets" : 1,
47 | "outlettype" : [ "int" ],
48 | "parameter_enable" : 0,
49 | "patching_rect" : [ 297.0, 144.0, 16.0, 16.0 ],
50 | "presentation_rect" : [ 297.0, 144.0, 16.0, 16.0 ]
51 | }
52 |
53 | }
54 | , {
55 | "box" : {
56 | "hidden" : 1,
57 | "id" : "obj-15",
58 | "linecount" : 2,
59 | "maxclass" : "message",
60 | "numinlets" : 2,
61 | "numoutlets" : 1,
62 | "outlettype" : [ "" ],
63 | "patching_rect" : [ 180.0, 265.0, 291.0, 37.0 ],
64 | "presentation_linecount" : 2,
65 | "presentation_rect" : [ 180.0, 265.0, 291.0, 37.0 ],
66 | "style" : "",
67 | "text" : "generate \"Macintosh HD:/Users/tim/Documents/Max 8/Packages/afts/source/projects/afts.demo\""
68 | }
69 |
70 | }
71 | , {
72 | "box" : {
73 | "hidden" : 1,
74 | "id" : "obj-10",
75 | "maxclass" : "newobj",
76 | "numinlets" : 1,
77 | "numoutlets" : 1,
78 | "outlettype" : [ "" ],
79 | "patching_rect" : [ 180.0, 305.0, 67.0, 23.0 ],
80 | "presentation_rect" : [ 180.0, 305.0, 67.0, 23.0 ],
81 | "style" : "",
82 | "text" : "min.project"
83 | }
84 |
85 | }
86 | , {
87 | "box" : {
88 | "id" : "obj-6",
89 | "maxclass" : "message",
90 | "numinlets" : 2,
91 | "numoutlets" : 1,
92 | "outlettype" : [ "" ],
93 | "patching_rect" : [ 110.0, 200.0, 145.0, 23.0 ],
94 | "presentation_rect" : [ 110.0, 200.0, 145.0, 23.0 ],
95 | "style" : ""
96 | }
97 |
98 | }
99 | , {
100 | "box" : {
101 | "id" : "obj-4",
102 | "maxclass" : "button",
103 | "numinlets" : 1,
104 | "numoutlets" : 1,
105 | "outlettype" : [ "bang" ],
106 | "patching_rect" : [ 155.0, 90.0, 24.0, 24.0 ],
107 | "presentation_rect" : [ 155.0, 90.0, 24.0, 24.0 ],
108 | "style" : ""
109 | }
110 |
111 | }
112 | , {
113 | "box" : {
114 | "fontname" : "Arial",
115 | "fontsize" : 14.0,
116 | "id" : "obj-2",
117 | "maxclass" : "newobj",
118 | "numinlets" : 1,
119 | "numoutlets" : 1,
120 | "outlettype" : [ "" ],
121 | "patching_rect" : [ 155.0, 140.0, 160.0, 24.0 ],
122 | "presentation_rect" : [ 155.0, 140.0, 160.0, 24.0 ],
123 | "style" : "",
124 | "text" : "rtpsendreceive.hello-world aloha"
125 | }
126 |
127 | }
128 | ],
129 | "lines" : [ {
130 | "patchline" : {
131 | "destination" : [ "obj-10", 0 ],
132 | "hidden" : 1,
133 | "source" : [ "obj-15", 0 ]
134 | }
135 |
136 | }
137 | , {
138 | "patchline" : {
139 | "destination" : [ "obj-15", 0 ],
140 | "hidden" : 1,
141 | "source" : [ "obj-16", 0 ]
142 | }
143 |
144 | }
145 | , {
146 | "patchline" : {
147 | "destination" : [ "obj-6", 1 ],
148 | "source" : [ "obj-2", 0 ]
149 | }
150 |
151 | }
152 | , {
153 | "patchline" : {
154 | "destination" : [ "obj-2", 0 ],
155 | "source" : [ "obj-4", 0 ]
156 | }
157 |
158 | }
159 | ],
160 | "dependency_cache" : [ {
161 | "name" : "min.edit-button.png",
162 | "bootpath" : "~/Documents/Max 8/Packages/min-devkit/help",
163 | "patcherrelativepath" : ".",
164 | "type" : "PNG",
165 | "implicit" : 1
166 | }
167 | , {
168 | "name" : "rtpsendreceive.hello-world.mxo",
169 | "type" : "iLaX"
170 | }
171 | , {
172 | "name" : "min.project.mxo",
173 | "type" : "iLaX"
174 | }
175 | ],
176 | "autosave" : 0,
177 | "styles" : [ {
178 | "name" : "tap",
179 | "default" : {
180 | "fontname" : [ "Lato Light" ]
181 | }
182 | ,
183 | "parentstyle" : "",
184 | "multi" : 0
185 | }
186 | ],
187 | "bgfillcolor_type" : "gradient",
188 | "bgfillcolor_color1" : [ 0.376471, 0.384314, 0.4, 1.0 ],
189 | "bgfillcolor_color2" : [ 0.290196, 0.309804, 0.301961, 1.0 ],
190 | "bgfillcolor_color" : [ 0.290196, 0.309804, 0.301961, 1.0 ],
191 | "bgfillcolor_angle" : 270.0,
192 | "bgfillcolor_proportion" : 0.39
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tomoyanonymous/rtpsendreceive/f13ef19b8ee085b96471e6894196c7f0dc1e1ad0/icon.png
--------------------------------------------------------------------------------
/package-info.json.in:
--------------------------------------------------------------------------------
1 | {
2 | "author" : "Tomoya Matsuura",
3 | "description" : "Send & Receive multichannel audio signals via network using rtp protocols",
4 | "homepatcher" : "rtpsendreceive_home.maxpat",
5 | "max_version_min" : "8.0", "max_version_max" : "none",
6 | "name" : "@C74_PACKAGE_NAME@",
7 | "os" : {
8 | "macintosh" : {
9 | "platform" : [ "x64" ],
10 | "min_version" : "none"
11 | },
12 | "windows" : {
13 | "platform" : [ "x64" ],
14 | "min_version" : "none"
15 | }
16 | },
17 | "package_extra" : {
18 | "reverse_domain" : "com.cycling74",
19 | "copyright" : "Copyright (c) Tomoya Matsuura"
20 | },
21 | "tags" : [ ],
22 | "version" : "@GIT_VERSION_MAJ@.@GIT_VERSION_MIN@.@GIT_VERSION_SUB@",
23 | "website" : "https://github.com/tomoyanonymous/rtpsendreceive"
24 | }
25 |
--------------------------------------------------------------------------------
/rtpsendreceive.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": "."
5 | }
6 | ],
7 | "settings": {
8 | "C_Cpp.configurationWarnings": "Disabled",
9 | "clangd.arguments": [
10 | "-clang-tidy",
11 | "-background-index",
12 | "-compile-commands-dir=build",
13 | "-header-insertion=never",
14 | "--query-driver=\"/usr/bin/clang++\""
15 | ],
16 | "cmake.debugConfig": {
17 | "stopAtEntry": false,
18 | "MIMode": "lldb",
19 | "miDebuggerPath": "/Users/tomoya/.vscode/extensions/ms-vscode.cpptools-0.26.3/debugAdapters/lldb/bin/lldb-mi",
20 | "logging": {
21 | // "trace": true,
22 | // "engineLogging": true,
23 | // "traceResponse": true
24 | }
25 | },
26 | "cmake.configureSettings": {
27 | "CMAKE_TOOLCHAIN_FILE": "./vcpkg/scripts/buildsystems/vcpkg.cmake"
28 | },
29 | "clangd.checkUpdates": true
30 | },
31 | "launch": {
32 | "version": "0.2.0",
33 | "configurations": [
34 | {
35 | "type": "lldb",
36 | "request": "launch",
37 | "name": "Sender Debug",
38 | "program": "/Applications/Max.app/Contents/MacOS/Max",
39 | "args": [
40 | "${workspaceFolder}/test_patch/sender_test.maxpat"
41 | ],
42 | "cwd": "${workspaceFolder}",
43 | "preLaunchTask": "build before send_debug"
44 | },
45 | {
46 | "type": "lldb",
47 | "request": "launch",
48 | "name": "Sender Project Debug",
49 | "program": "/Applications/Max.app/Contents/MacOS/Max",
50 | "args": [
51 | "/Users/${env:USER}/Documents/Max 8/Projects/rtpsender/rtpsender.maxproj"
52 | ],
53 | "cwd": "${workspaceFolder}",
54 | "preLaunchTask": "build before send_debug"
55 | },
56 | {
57 | "type": "lldb",
58 | "request": "launch",
59 | "name": "Receiver Debug",
60 | "program": "/Applications/Max.app/Contents/MacOS/Max",
61 | "args": [
62 | "${workspaceFolder}/test_patch/receiver_test.maxpat"
63 | ],
64 | "cwd": "${workspaceFolder}",
65 | "preLaunchTask": "build before receive_debug"
66 | },
67 | {
68 | "type": "lldb",
69 | "request": "launch",
70 | "name": "Receiver Project Debug",
71 | "program": "/Applications/Max.app/Contents/MacOS/Max",
72 | "args": [
73 | "/Users/${env:USER}/Documents/Max 8/Projects/rtpreceiver/rtpreceiver.maxproj"
74 | ],
75 | "cwd": "${workspaceFolder}",
76 | "preLaunchTask": "build before receive_debug"
77 | },
78 | {
79 | "type": "lldb",
80 | "request": "launch",
81 | "name": "Self-loop Debug",
82 | "program": "/Applications/Max.app/Contents/MacOS/Max",
83 | "args": [
84 | "${workspaceFolder}/test_patch/self_loop_test.maxpat"
85 | ],
86 | "cwd": "${workspaceFolder}",
87 | "preLaunchTask": "build_selfloop"
88 | },
89 | {
90 | "name": "cmake default",
91 | "type": "lldb",
92 | "request": "launch",
93 | "program": "${command:cmake.launchTargetPath}",
94 | "args": [],
95 | "cwd": "${workspaceFolder}"
96 | }
97 | ]
98 | },
99 | "tasks": {
100 | // See https://go.microsoft.com/fwlink/?LinkId=733558
101 | // for the documentation about the tasks.json format
102 | "version": "2.0.0",
103 | "tasks": [
104 | {
105 | "label": "copy external into project",
106 | "type": "shell",
107 | "command": "/bin/zsh",
108 | "args": [
109 | "${workspaceFolder}/copy_externals.sh"
110 | ],
111 | "problemMatcher": []
112 | },
113 | {
114 | "label": "build_selfloop",
115 | "type": "shell",
116 | "dependsOn": [
117 | "build before send_debug",
118 | "build before receive_debug"
119 | ]
120 | },
121 | {
122 | "label": "build before send_debug",
123 | "type": "shell",
124 | "command": "cmake",
125 | "args": [
126 | "--build",
127 | "${workspaceFolder}/build",
128 | "--config",
129 | "debug",
130 | "--target",
131 | "mc.rtpsend_tilde",
132 | "-j",
133 | "18"
134 | ],
135 | "group": {
136 | "kind": "build",
137 | "isDefault": true
138 | },
139 | "problemMatcher": []
140 | },
141 | {
142 | "label": "build before receive_debug",
143 | "type": "shell",
144 | "command": "cmake",
145 | "args": [
146 | "--build",
147 | "${workspaceFolder}/build",
148 | "--config",
149 | "debug",
150 | "--target",
151 | "mc.rtpreceive_tilde",
152 | "-j",
153 | "18"
154 | ],
155 | "group": {
156 | "kind": "build",
157 | "isDefault": true
158 | },
159 | "problemMatcher": []
160 | }
161 | ]
162 | }
163 | }
--------------------------------------------------------------------------------
/screenshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tomoyanonymous/rtpsendreceive/f13ef19b8ee085b96471e6894196c7f0dc1e1ad0/screenshot.jpg
--------------------------------------------------------------------------------
/setup_dev_env.sh:
--------------------------------------------------------------------------------
1 | #!/bin/zsh
2 |
3 | xcode-select --install
4 |
5 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
6 |
7 | brew cask install visual-studio-code
8 |
9 | #install ffmpeg built from source (debug&static)
10 |
11 | brew install cmake pkg-config nasm freetype opus
12 |
13 | git clone https://git.ffmpeg.org/ffmpeg.git /tmp/ffmpeg
14 |
15 | pushd /tmp/ffmpeg
16 |
17 | ./configure --prefix=/usr/local/Cellar/ffmpeg/HEAD_Debug --disable-avfoundation --disable-iconv --disable-filters --disable-devices --disable-shared --enable-static --disable-optimizations --disable-mmx --disable-audiotoolbox --disable-videotoolbox --disable-stripping --disable-appkit --disable-zlib --disable-coreimage --disable-bzlib --disable-securetransport --disable-sdl2 --disable-lzma --enable-libopus --pkg-config-flags=--static --cc=/usr/bin/clang --cxx=/usr/bin/clang++
18 |
19 | make -j
20 |
21 | make install
22 |
23 | popd
24 |
25 | git clone https://github.com/tomoyanonymous/rtpsendreceive.git ~/Desktop/rtpsendreceive
26 |
27 | pushd ~/Desktop/rtpsendreceive
28 |
29 | mkdir build && cd build && cmake ..
30 |
31 | cmake --build . --target all
32 |
33 | popd
34 |
35 |
--------------------------------------------------------------------------------
/source/projects/mc.rtpreceive_tilde/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2018 The Min-DevKit Authors. All rights reserved.
2 | # Use of this source code is governed by the MIT License found in the License.md file.
3 |
4 | cmake_minimum_required(VERSION 3.0)
5 |
6 | set(C74_MIN_API_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../min-api)
7 | include(${C74_MIN_API_DIR}/script/min-pretarget.cmake)
8 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE)
9 |
10 |
11 | #############################################################
12 | # MAX EXTERNAL
13 | #############################################################
14 |
15 | set( SOURCE_FILES
16 | ${PROJECT_NAME}.cpp
17 | )
18 | # set(RTPSRLIB ${})
19 | add_library(
20 | ${PROJECT_NAME}
21 | MODULE
22 | ${SOURCE_FILES}
23 | )
24 | target_include_directories(${PROJECT_NAME} PUBLIC
25 | ${C74_INCLUDES}
26 | ${CMAKE_CURRENT_SOURCE_DIR}/../../rtpsendreceive_lib
27 | )
28 | set(RTPSR_LIB "rtpsendreceive")
29 |
30 | message(STATUS testlib!${RTPSR_LIB})
31 | target_link_libraries(${PROJECT_NAME} PUBLIC ${RTPSR_LIB})
32 | target_link_libraries(${PROJECT_NAME} PUBLIC ${FFMPEG_LIBRARIES})
33 |
34 | include(${C74_MIN_API_DIR}/script/min-posttarget.cmake)
35 | message(STATUS ${CMAKE_SOURCE_DIR}/externals/mc.rtpreceive~.mxo)
36 | execute_process(COMMAND ln -s ${CMAKE_SOURCE_DIR}/externals/mc.rtpreceive~.mxo ${CMAKE_SOURCE_DIR}/test_patch/)
37 |
38 | #############################################################
39 | # UNIT TEST
40 | #############################################################
41 |
42 | include(mc.rtpreceive_tilde_test.cmake)
43 |
--------------------------------------------------------------------------------
/source/projects/mc.rtpreceive_tilde/mc.rtpreceive_tilde.cpp:
--------------------------------------------------------------------------------
1 | /// @file
2 | /// @ingroup minexamples
3 | /// @copyright Copyright 2018 The Min-DevKit Authors. All rights
4 | /// reserved.
5 | /// @license Use of this source code is governed by the MIT License
6 | /// found in the License.md file.
7 |
8 | #include "c74_min.h"
9 | #include "min_debugutils.hpp"
10 | #include "rtpreceiver.hpp"
11 |
12 | using namespace c74::min;
13 |
14 | class rtpreceive_tilde : public object, public mc_operator<> {
15 | private:
16 | bool m_initialized {false};
17 |
18 | public:
19 |
20 | MIN_DESCRIPTION {"Receive audio stream via rtp protocol."};
21 | MIN_TAGS {"Audio"};
22 | MIN_AUTHOR {"Tomoya Matsuura"};
23 | MIN_RELATED {"mc.rtpsend_tilde"};
24 | rtpreceive_tilde(const atoms& args = {}) {
25 | // resetReceiver();
26 |
27 | m_initialized = true;
28 | }
29 |
30 | attribute address {this, "address", "127.0.0.1", description {"Your IP address for an appropriate network interface."}};
31 | attribute port {this, "port", 30000, description {"A main incoming network port number."}};
32 | attribute codec {this, "codec", "pcm_s16be", setter {MIN_FUNCTION {auto c = rtpsr::getCodecByName(args[0]);
33 | if (c == rtpsr::Codec::INVALID) {
34 | cerr << "Invalid Codec Name. Using pcm_s16be" << endl;
35 | std::ostream& out = cerr;
36 | c = rtpsr::Codec::PCM_s16BE;
37 | }
38 | return args;
39 | }
40 | }
41 | }
42 | ;
43 | attribute active {this, "active", 0, setter {MIN_FUNCTION {int res = args[0];
44 | if (m_initialized && rtpreceiver != nullptr) { }
45 | return {};
46 | }
47 | }
48 | }
49 | ;
50 | attribute reorder_queue_size {this, "reorder_queue_size", 500.0, description {"number of packets for reorder queue"}};
51 | attribute ringbuf_framenum {
52 | this, "ringbuf_framenum", 4, range {1, 1000}, description {"Size of an internal ring buffer (multiplied with signal vector size.)"}};
53 | attribute max_delay {this, "max_delay", 500000, description {"maximum accepted delay for reordering(in microseconds)"}};
54 | attribute min_port {
55 | this, "min_port", 5000, range {0, 1000000}, description {"minimum port number used for rtsp internal transport"}};
56 | attribute max_port {
57 | this, "max_port", 65000, range {0, 1000000}, description {"minimum port number used for rtsp internal transport"}};
58 |
59 | attribute use_rtsp {this,
60 | "use_rtsp",
61 | true,
62 | description {"if set to false, use raw rtp protocol instead of using rtsp mux/demuxer(Some options are ignored)."}};
63 |
64 | // attribute play {this, "play", true};
65 | attribute channels {this, "channels", 1, range {1, 16}};
66 | inlet<> input {this, "(int) toggle subscription"};
67 | outlet<> m_output {this, "(multichannelsignal) received output", "multichannelsignal"};
68 | outlet<> latency {this, "(double) current latency"};
69 |
70 | // post to max window == but only when the class is loaded the first time
71 | // message<> maxclass_setup {this, "maxclass_setup"};
72 | message<> toggle {this, "int", "toggle play and pause", MIN_FUNCTION {int num = args[0];
73 | // bool state = num > 0;
74 | // bool changed = state != play;
75 | // if (changed && state) {
76 | // resetReceiver(frame_size);
77 | // }
78 | // if (changed && !state) {
79 | // rtpreceiver->pause();
80 | // }
81 | // play = state;
82 | return {};
83 | }
84 | }
85 | ;
86 | message<> setup {this, "maxclass_setup", MIN_FUNCTION {auto thisclass = args[0];
87 | c74::max::class_addmethod(thisclass, (c74::max::method)setOutChans, "multichanneloutputs", c74::max::A_CANT, 0);
88 | c74::max::class_addmethod(thisclass, (c74::max::method)setDspState, "dspstate", c74::max::A_CANT, 0);
89 | return {};
90 | }
91 | }
92 | ;
93 | message<> dspsetup {this, "dspsetup", MIN_FUNCTION {double newvecsize = args[1];
94 | resetReceiver(newvecsize, channels);
95 | return {};
96 | }
97 | }
98 | ;
99 | message<> getlatency {this, "getlatency", MIN_FUNCTION {if (rtpreceiver != nullptr) {latency.send(rtpreceiver->getLatency());
100 | }
101 | return {};
102 | }
103 | , description {
104 | "Outputs a packet latency in milliseconds at second outlet."
105 | }
106 | }
107 | ;
108 |
109 | static long setDspState(void* obj, long state) {
110 | c74::max::object_attr_setlong(obj, c74::max::gensym("active"), state);
111 | return state;
112 | }
113 | void operator()(audio_bundle input, audio_bundle output) {
114 | output.clear();
115 | if (rtpreceiver == nullptr) {
116 | return;
117 | }
118 | if (!rtpreceiver->isConnected()) {
119 | return;
120 | }
121 | int chs = std::min(output.channel_count(), channels.get());
122 | bool readres = rtpreceiver->readFromOutput(iarray);
123 | misscount.second++;
124 | if (!readres) {
125 | misscount.first++;
126 | cout << "stream underflow detected! " << std::to_string(misscount.first) << "/" << std::to_string(misscount.second) << std::endl;
127 | return;
128 | }
129 | for (auto i = 0; i < output.frame_count(); i++) {
130 | for (auto channel = 0; channel < chs; channel++) {
131 | output.samples(channel)[i] = rtpsr::convertSampleToDouble(iarray.at(i * channels.get() + channel));
132 | }
133 | }
134 | }
135 |
136 | private:
137 | std::shared_ptr rtpreceiver {nullptr};
138 | std::vector iarray;
139 | std::pair misscount;
140 | double frame_size = vector_size();
141 |
142 | void resetChannel(int channel) {
143 | resetReceiver(vector_size(), channels);
144 | }
145 |
146 | void resetReceiver(double newvecsize, int channel) {
147 | iarray.resize(newvecsize * channel);
148 | frame_size = newvecsize;
149 | misscount = {0, 0};
150 | rtpsr::Url url {address.get(), port};
151 | if (use_rtsp) {
152 | auto option = std::make_unique(url, samplerate(), channel, (int)newvecsize);
153 | setOptions(*static_cast(option.get()));
154 | resetReceiver(std::move(option));
155 | }
156 | else {
157 | auto option = std::make_unique(url, samplerate(), channel, (int)newvecsize);
158 | setOptions(*static_cast(option.get()));
159 | resetReceiver(std::move(option));
160 | }
161 | }
162 | void resetReceiver(std::unique_ptr s) {
163 | rtpreceiver.reset();
164 | try {
165 | s->port_range = getPortRange();
166 | rtpreceiver = std::make_unique(std::move(s), rtpsr::getCodecByName(codec.get()), ringbuf_framenum, cout);
167 | rtpreceiver->launchLoop();
168 | }
169 | catch (std::exception& e) {
170 | std::cerr << e.what() << std::endl;
171 | }
172 | }
173 | void resetReceiver(std::unique_ptr s) {
174 | rtpreceiver.reset();
175 | try {
176 | rtpreceiver = std::make_unique(std::move(s), rtpsr::getCodecByName(codec.get()), ringbuf_framenum, cout);
177 | rtpreceiver->launchLoop();
178 | }
179 | catch (std::exception& e) {
180 | std::cerr << e.what() << std::endl;
181 | }
182 | }
183 |
184 | void setOptions(rtpsr::RtpOptionsBase& opt) {
185 | opt.reorder_queue_size = reorder_queue_size;
186 | opt.max_delay = max_delay;
187 | }
188 | std::pair getPortRange() {
189 | if (min_port > max_port) {
190 | min_port = max_port.get();
191 | }
192 | return std::pair(min_port.get(), max_port.get());
193 | }
194 | static long setOutChans(void* obj, long outletindex) {
195 | auto chs = c74::max::object_attr_getlong(obj, c74::max::gensym("channels"));
196 | return chs;
197 | }
198 | }
199 | ;
200 |
201 | MIN_EXTERNAL(rtpreceive_tilde);
202 |
--------------------------------------------------------------------------------
/source/projects/mc.rtpreceive_tilde/mc.rtpreceive_tilde_test.cmake:
--------------------------------------------------------------------------------
1 | ## copied and modified target_include_directories from min-api/test/min_object_unittst.cmake
2 |
3 | # Copyright 2018 The Min-API Authors. All rights reserved.
4 | # Use of this source code is governed by the MIT License found in the License.md file.
5 |
6 | cmake_minimum_required(VERSION 3.10)
7 |
8 | set(ORIGINAL_NAME "${PROJECT_NAME}")
9 | set(TEST_NAME "${PROJECT_NAME}_test")
10 | #project(${PROJECT_NAME}_test)
11 |
12 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.cpp")
13 |
14 | enable_testing()
15 |
16 | include_directories(
17 | "${C74_INCLUDES}"
18 | "${C74_MIN_API_DIR}/test"
19 |
20 | # "${C74_MIN_API_DIR}/test/mock"
21 | )
22 |
23 | set(TEST_SOURCE_FILES "")
24 | FOREACH(SOURCE_FILE ${SOURCE_FILES})
25 | set(ORIGINAL_WITH_EXT "${ORIGINAL_NAME}.cpp")
26 | if (SOURCE_FILE STREQUAL ORIGINAL_WITH_EXT)
27 | set(TEST_SOURCE_FILES ${TEST_SOURCE_FILES} ${TEST_NAME}.cpp)
28 | else()
29 | set(TEST_SOURCE_FILES "${TEST_SOURCE_FILES}" ${SOURCE_FILE})
30 | endif()
31 | ENDFOREACH()
32 |
33 | # set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
34 | # set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage")
35 | # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
36 |
37 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../tests")
38 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
39 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
40 |
41 | add_executable(${TEST_NAME} ${TEST_NAME}.cpp ${TEST_SOURCE_FILES})
42 |
43 | if (NOT TARGET mock_kernel)
44 | set(C74_MOCK_TARGET_DIR "${C74_MIN_API_DIR}/test")
45 | add_subdirectory(${C74_MIN_API_DIR}/test/mock ${CMAKE_BINARY_DIR}/mock)
46 | endif ()
47 |
48 | add_dependencies(${TEST_NAME} mock_kernel)
49 |
50 | target_compile_definitions(${TEST_NAME} PUBLIC -DMIN_TEST)
51 |
52 | set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
53 | set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
54 | target_include_directories(${TEST_NAME} PUBLIC
55 | ${C74_INCLUDES}
56 | ${C74_MIN_API_DIR}/test
57 | ${CMAKE_CURRENT_SOURCE_DIR}/../../rtpsendreceive_lib
58 | ${FFMPEG_INCLUDE_DIRS}
59 | )
60 | target_link_libraries(${TEST_NAME} PUBLIC "mock_kernel" rtpsendreceive)
61 |
62 |
63 | if (APPLE)
64 | set_target_properties(${TEST_NAME} PROPERTIES LINK_FLAGS "-Wl,-F'${MAX_SDK_JIT_INCLUDES}', -weak_framework JitterAPI")
65 | endif ()
66 | if (WIN32)
67 | set_target_properties(${TEST_NAME} PROPERTIES COMPILE_PDB_NAME ${TEST_NAME})
68 |
69 | # target_link_libraries(${TEST_NAME} ${MaxAPI_LIB})
70 | # target_link_libraries(${TEST_NAME} ${MaxAudio_LIB})
71 | # target_link_libraries(${TEST_NAME} ${Jitter_LIB})
72 | endif ()
73 |
74 | add_test(NAME ${TEST_NAME}
75 | COMMAND ${TEST_NAME})
76 |
77 | endif ()
--------------------------------------------------------------------------------
/source/projects/mc.rtpreceive_tilde/mc.rtpreceive_tilde_test.cpp:
--------------------------------------------------------------------------------
1 | /// @file
2 | /// @ingroup minexamples
3 | /// @copyright Copyright 2018 The Min-DevKit Authors. All rights
4 | /// reserved.
5 | /// @license Use of this source code is governed by the MIT License
6 | /// found in the License.md file.
7 | #define CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS
8 | #include "c74_min_unittest.h" // required unit test header
9 |
10 | #include "mc.rtpreceive_tilde.cpp" // need the source of our object so that we can access it
11 |
12 | //Instantiation of mc.rtpreceive~ requires calling Max API directly, which is not available in min-unittest framework currently
13 |
14 | // SCENARIO("object produces correct output") {
15 | // ext_main(nullptr); // every unit test must call ext_main() once to configure
16 | // // the class
17 |
18 | // GIVEN("An instance of our object") {
19 | // test_wrapper an_instance;
20 | // rtpreceive_tilde& my_object = an_instance;
21 | // // check that default attr values are correct
22 | // // REQUIRE((my_object.greeting == symbol("hello world")));
23 |
24 | // // now proceed to testing various sequences of events
25 | // WHEN("toggle 1 is received") {
26 | // my_object.toggle(1);
27 |
28 | // THEN("internal play flag is activated") {
29 | // c74::max::t_atom_long attr =
30 | // c74::max::object_attr_getlong(my_object.maxobj(), symbol("play"));
31 | // REQUIRE(attr == 1);
32 | // }
33 | // }
34 | // }
35 | // }
--------------------------------------------------------------------------------
/source/projects/mc.rtpsend_tilde/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2018 The Min-DevKit Authors. All rights reserved.
2 | # Use of this source code is governed by the MIT License found in the License.md file.
3 |
4 | cmake_minimum_required(VERSION 3.0)
5 |
6 | set(C74_MIN_API_DIR ${CMAKE_SOURCE_DIR}/source/min-api)
7 | include(${C74_MIN_API_DIR}/script/min-pretarget.cmake)
8 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE)
9 |
10 |
11 | #############################################################
12 | # MAX EXTERNAL
13 | #############################################################
14 |
15 |
16 | set( SOURCE_FILES
17 | ${PROJECT_NAME}.cpp
18 | )
19 | # set(RTPSRLIB ${})
20 | add_library(
21 | ${PROJECT_NAME}
22 | MODULE
23 | ${SOURCE_FILES}
24 | )
25 |
26 | set(RTPSR_LIB "rtpsendreceive")
27 |
28 | target_include_directories(${PROJECT_NAME} PUBLIC
29 | ${C74_INCLUDES}
30 | ${CMAKE_CURRENT_SOURCE_DIR}/../../rtpsendreceive_lib
31 | )
32 |
33 | message(STATUS testlib!${RTPSR_LIB})
34 | target_link_libraries(${PROJECT_NAME} PUBLIC ${RTPSR_LIB} ${FFMPEG_LIBRARIES})
35 |
36 | include(${C74_MIN_API_DIR}/script/min-posttarget.cmake)
37 | message(STATUS ${CMAKE_SOURCE_DIR}/externals/mc.rtpsend~.mxo)
38 | execute_process(COMMAND ln -s ${CMAKE_SOURCE_DIR}/externals/mc.rtpsend~.mxo ${CMAKE_SOURCE_DIR}/test_patch/)
39 |
40 | #############################################################
41 | # UNIT TEST
42 | #############################################################
43 |
44 | include(mc.rtpsend_tilde_test.cmake)
45 |
--------------------------------------------------------------------------------
/source/projects/mc.rtpsend_tilde/mc.rtpsend_tilde.cpp:
--------------------------------------------------------------------------------
1 | /// @file
2 | /// @ingroup rtpsendreceive
3 | /// @copyright Copyright 2020 Tomoya Matsuura. All rights reserved.
4 | /// @license Use of this source code is governed by the LGPL License
5 | /// found in the License.md file.
6 |
7 |
8 | #include "c74_max.h"
9 | #include "c74_min.h"
10 | #include "rtpsender.hpp"
11 |
12 | using namespace c74::min;
13 |
14 | class rtpsend_tilde : public object, public mc_operator<> {
15 | bool m_initialized {false};
16 |
17 | public:
18 | MIN_DESCRIPTION {"send audio stream via rtp protocol"};
19 | MIN_TAGS {"Audio"};
20 | MIN_AUTHOR {"Tomoya Matsuura"};
21 | MIN_RELATED {"mc.rtpreceive_tilde"};
22 | rtpsend_tilde(const atoms& args = {}) {
23 | instance_count += 1;
24 | m_initialized = true;
25 | }
26 | ~rtpsend_tilde() {
27 | instance_count -= 1;
28 | }
29 | attribute channels {this, "channels", 1, range {1, 16}};
30 | attribute address {this, "address", "127.0.0.1", description {"Destination IP Address(can be a domain name)"}};
31 | attribute port {this, "port", 30000, description {"Destination Main Port"}};
32 | attribute codec {this, "codec", "pcm_s16be", setter {MIN_FUNCTION {auto c = rtpsr::getCodecByName(args[0]);
33 | if (c == rtpsr::Codec::INVALID) {
34 | cerr << "Invalid Codec Name. Using pcm_s16be" << endl;
35 | c = rtpsr::Codec::PCM_s16BE;
36 | }
37 | return args;
38 | }
39 | }
40 | }
41 | ;
42 | attribute ringbuf_framenum {
43 | this, "ringbuf_framenum", 4, range {1, 1000}, description {"Size of an internal ring buffer (multiplied with signal vector size.)"}};
44 | attribute retry_rate {this, "retry_rate", 500.0, description {"Retry intervals in milliseconds at connection"}};
45 | attribute reorder_queue_size {this, "reorder_queue_size", 500.0, description {"number of packets for reorder queue"}};
46 | attribute use_rtsp {this,
47 | "use_rtsp",
48 | true,
49 | description {"if set to false, use raw rtp protocol instead of using rtsp mux/demuxer(Some options are ignored)."}};
50 |
51 | attribute min_port {
52 | this, "min_port", 5000, range {0, 1000000}, description {"minimum port number used for rtsp internal transport"}};
53 | attribute max_port {
54 | this, "max_port", 65000, range {0, 1000000}, description {"minimum port number used for rtsp internal transport"}};
55 |
56 |
57 | attribute active {this, "active", 0, setter {MIN_FUNCTION {int res = args[0];
58 | if (m_initialized && rtpsender != nullptr) {
59 | if (res <= 0) {
60 | rtpsender->init_asyncloop.halt();
61 | rtpsender->asynclooper.halt();
62 | }
63 | else {
64 | rtpsender->launchLoop();
65 | }
66 | }
67 | return {};
68 | }
69 | }
70 | }
71 | ;
72 |
73 | inlet<> m_inlet {this, "(multichannelsignal) input to be transmited"};
74 |
75 | // static long inputChanged(rtpsend_tilde* s, long index, long count) {
76 | // if (count != s->channels) {
77 | // s->channels = count;
78 | // s->resetSender();
79 | // return true;
80 | // } else
81 | // return false;
82 | // }
83 | message<> maxclass_setup {this, "maxclass_setup", MIN_FUNCTION {c74::max::t_class* c = args[0];
84 | c74::max::class_addmethod(c, (c74::max::method)setDspState, "dspstate", c74::max::A_CANT, 0);
85 | return {};
86 | }
87 | }
88 | ;
89 | message<> setup {this, "setup", MIN_FUNCTION {
90 | // double newvecsize = args[1];
91 | return {};
92 | }
93 | }
94 | ;
95 |
96 | message<> dspsetup {this, "dspsetup", MIN_FUNCTION {double newvecsize = args[1];
97 | if (m_initialized) {
98 | resetSender(newvecsize);
99 | }
100 | return {};
101 | }
102 | }
103 | ;
104 |
105 | void operator()(audio_bundle input, audio_bundle output) {
106 |
107 | if (rtpsender == nullptr) {
108 | return;
109 | }
110 | if (!rtpsender->isConnected()) {
111 | return;
112 | }
113 | int chs = std::min(input.channel_count(), channels.get());
114 | for (auto i = 0; i < input.frame_count(); i++) {
115 | for (auto channel = 0; channel < chs; channel++) {
116 | iarray.at(i * chs + channel) = rtpsr::convertDoubleToSample(input.samples(channel)[i]);
117 | }
118 | }
119 | bool write_success = rtpsender->writeToInput(iarray);
120 | if (!write_success) {
121 | // cerr << "ring buffer is full!" << std::endl;
122 | }
123 | }
124 | static long setDspState(void* obj, long state) {
125 | c74::max::object_attr_setlong(obj, c74::max::gensym("active"), state);
126 | return state;
127 | }
128 |
129 | private:
130 | void resetSender(double newvecsize) {
131 | iarray.resize((int)newvecsize * channels);
132 | symbol c = codec;
133 | try {
134 |
135 | if (use_rtsp) {
136 | auto option = std::make_unique(rtpsr::Url {address.get(), port}, samplerate(), channels, (int)newvecsize);
137 | option->port_range = getPortRange();
138 | setOptions(*static_cast(option.get()));
139 |
140 | rtpsender = std::make_unique(std::move(option),
141 | rtpsr::getCodecByName(c),
142 | std::chrono::milliseconds((long long)retry_rate.get()),
143 | ringbuf_framenum,
144 | cout);
145 | }
146 | else {
147 | auto option = std::make_unique(rtpsr::Url {address.get(), port}, samplerate(), channels, (int)newvecsize);
148 | setOptions(*static_cast(option.get()));
149 | rtpsender = std::make_unique(std::move(option),
150 | rtpsr::getCodecByName(c),
151 | std::chrono::milliseconds((long long)retry_rate.get()),
152 | ringbuf_framenum,
153 | cout);
154 | }
155 | }
156 | catch (std::exception& e) {
157 | std::cerr << e.what() << std::endl;
158 | }
159 | }
160 | void setOptions(rtpsr::RtpOptionsBase& opt) {
161 | opt.reorder_queue_size = reorder_queue_size;
162 | }
163 | std::pair getPortRange() {
164 | if (min_port > max_port) {
165 | min_port = max_port.get();
166 | }
167 | return std::pair(min_port.get(), max_port.get());
168 | }
169 |
170 | std::unique_ptr rtpsender;
171 | std::vector iarray;
172 | static int instance_count;
173 | }
174 | ;
175 | int rtpsend_tilde::instance_count = 0;
176 | MIN_EXTERNAL(rtpsend_tilde);
177 |
--------------------------------------------------------------------------------
/source/projects/mc.rtpsend_tilde/mc.rtpsend_tilde_test.cmake:
--------------------------------------------------------------------------------
1 | ## copied and modified target_include_directories from min-api/test/min_object_unittst.cmake
2 |
3 | # Copyright 2018 The Min-API Authors. All rights reserved.
4 | # Use of this source code is governed by the MIT License found in the License.md file.
5 |
6 | cmake_minimum_required(VERSION 3.10)
7 |
8 | set(ORIGINAL_NAME "${PROJECT_NAME}")
9 | set(TEST_NAME "${PROJECT_NAME}_test")
10 | #project(${PROJECT_NAME}_test)
11 |
12 | if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_NAME}.cpp")
13 |
14 | enable_testing()
15 |
16 | include_directories(
17 | "${C74_INCLUDES}"
18 | "${C74_MIN_API_DIR}/test"
19 |
20 | # "${C74_MIN_API_DIR}/test/mock"
21 | )
22 |
23 | set(TEST_SOURCE_FILES "")
24 | FOREACH(SOURCE_FILE ${SOURCE_FILES})
25 | set(ORIGINAL_WITH_EXT "${ORIGINAL_NAME}.cpp")
26 | if (SOURCE_FILE STREQUAL ORIGINAL_WITH_EXT)
27 | set(TEST_SOURCE_FILES ${TEST_SOURCE_FILES} ${TEST_NAME}.cpp)
28 | else()
29 | set(TEST_SOURCE_FILES "${TEST_SOURCE_FILES}" ${SOURCE_FILE})
30 | endif()
31 | ENDFOREACH()
32 |
33 | # set(CMAKE_CXX_FLAGS "-fprofile-arcs -ftest-coverage")
34 | # set(CMAKE_C_FLAGS "-fprofile-arcs -ftest-coverage")
35 | # set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage")
36 |
37 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../tests")
38 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
39 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}")
40 |
41 | add_executable(${TEST_NAME} ${TEST_NAME}.cpp ${TEST_SOURCE_FILES})
42 |
43 | if (NOT TARGET mock_kernel)
44 | set(C74_MOCK_TARGET_DIR "${C74_MIN_API_DIR}/test")
45 | add_subdirectory(${C74_MIN_API_DIR}/test/mock ${CMAKE_BINARY_DIR}/mock)
46 | endif ()
47 |
48 | add_dependencies(${TEST_NAME} mock_kernel)
49 |
50 | target_compile_definitions(${TEST_NAME} PUBLIC -DMIN_TEST)
51 |
52 | set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
53 | set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
54 | target_include_directories(${TEST_NAME} PUBLIC
55 | ${C74_INCLUDES}
56 | ${C74_MIN_API_DIR}/test
57 | ${CMAKE_CURRENT_SOURCE_DIR}/../../rtpsendreceive_lib
58 | ${FFMPEG_INCLUDE_DIRS}
59 | )
60 | target_link_libraries(${TEST_NAME} PUBLIC "mock_kernel" rtpsendreceive)
61 |
62 | if (APPLE)
63 | set_target_properties(${TEST_NAME} PROPERTIES LINK_FLAGS "-Wl,-F'${MAX_SDK_JIT_INCLUDES}', -weak_framework JitterAPI")
64 | endif ()
65 | if (WIN32)
66 | set_target_properties(${TEST_NAME} PROPERTIES COMPILE_PDB_NAME ${TEST_NAME})
67 |
68 | # target_link_libraries(${TEST_NAME} ${MaxAPI_LIB})
69 | # target_link_libraries(${TEST_NAME} ${MaxAudio_LIB})
70 | # target_link_libraries(${TEST_NAME} ${Jitter_LIB})
71 | endif ()
72 |
73 | add_test(NAME ${TEST_NAME}
74 | COMMAND ${TEST_NAME})
75 |
76 | endif ()
--------------------------------------------------------------------------------
/source/projects/mc.rtpsend_tilde/mc.rtpsend_tilde_test.cpp:
--------------------------------------------------------------------------------
1 | /// @file
2 | /// @ingroup rtpsendreceive
3 | /// @copyright Copyright 2020 Tomoya Matsuura. All rights reserved.
4 | /// @license Use of this source code is governed by the LGPL License found in the License.md file.
5 | #define CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS
6 |
7 | #include "c74_min_unittest.h" // required unit test header
8 |
9 | #include "mc.rtpsend_tilde.cpp" // need the source of our object so that we can access it
10 |
11 | // Unit tests are written using the Catch framework as described at
12 | // https://github.com/philsquared/Catch/blob/master/docs/tutorial.md
13 |
14 | SCENARIO("object produces correct output") {
15 | ext_main(nullptr); // every unit test must call ext_main() once to configure the class
16 |
17 | GIVEN("An instance of our object") {
18 |
19 | test_wrapper an_instance;
20 | rtpsend_tilde& my_object = an_instance;
21 |
22 | // check that default attr values are correct
23 | // REQUIRE((my_object.greeting == symbol("hello world")));
24 |
25 | // now proceed to testing various sequences of events
26 | // WHEN("a 'bang' is received") {
27 | // my_object.bang();
28 | // THEN("our greeting is produced at the outlet") {
29 | // auto& output = *c74::max::object_getoutput(my_object, 0);
30 | // REQUIRE((output.size() == 1));
31 | // REQUIRE((output[0].size() == 1));
32 | // REQUIRE((output[0][0] == symbol("hello world")));
33 | // }
34 | // }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Copyright 2018 The Min-DevKit Authors. All rights reserved.
2 | # Use of this source code is governed by the MIT License found in the License.md file.
3 |
4 | cmake_minimum_required(VERSION 3.0)
5 |
6 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE)
7 |
8 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
9 | set(CMAKE_MACOSX_RPATH 1)
10 | endif()
11 |
12 | message("test!!! ${DEPENDENT_LIBS}")
13 |
14 |
15 | add_library(
16 | rtpsendreceive
17 | STATIC
18 | rtpsrbase.cpp rtpsender.cpp rtpreceiver.cpp
19 | )
20 | target_include_directories(rtpsendreceive
21 | PUBLIC
22 | ${FFMPEG_INCLUDE_DIRS})
23 |
24 | target_compile_features(rtpsendreceive PUBLIC cxx_std_17)
25 | target_link_libraries(rtpsendreceive PUBLIC ${DEPENDENT_LIBS})
26 |
27 | include(rtpsendreceive_lib_test.cmake)
28 |
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/lockfree_ringbuffer.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | template
7 | class LockFreeRingbuf {
8 | public:
9 | LockFreeRingbuf() = delete;
10 | explicit LockFreeRingbuf(int size)
11 | : buffer(size, 0) { }
12 | void resize(int size) {
13 | buffer.resize(size);
14 | std::fill(buffer.begin(), buffer.end(), 0);
15 | }
16 | bool read(T& res_ref) {
17 | if (read_index == write_index) {
18 | return false;
19 | }
20 | res_ref = buffer[read_index];
21 | read_index = stepIndex(read_index, 1);
22 | return true;
23 | }
24 | bool write(T elem) {
25 | write_index = stepIndex(write_index, 1);
26 | if (read_index == write_index) {
27 | return false;
28 | }
29 | buffer[write_index] = elem;
30 | return true;
31 | }
32 | // read from read_index to specified size. note that size should be length of
33 | // array, not a byte size
34 | bool readRange(std::vector& res_array, size_t size) {
35 | if (getReadMargin() <= size) {
36 | return false;
37 | }
38 | size_t localread = read_index;
39 | size_t target_index = localread + size;
40 | size_t target_index_ringed = (target_index % buffer.size());
41 | if (target_index > buffer.size()) {
42 | auto&& iter = std::copy(buffer.begin() + localread, buffer.end(), res_array.begin());
43 | std::copy(buffer.begin(), buffer.begin() + target_index_ringed, iter);
44 | }
45 | else {
46 | std::copy_n(buffer.begin() + localread, size, res_array.begin());
47 | }
48 | read_index = target_index_ringed;
49 | return true;
50 | }
51 | bool writeRange(const std::vector& src_array, size_t size) {
52 | if (getWriteMargin() <= size) {
53 | return false;
54 | }
55 | size_t localwrite = write_index;
56 | size_t target_index = localwrite + size;
57 | size_t target_index_ringed = (target_index % buffer.size());
58 | if (target_index > buffer.size()) {
59 | size_t writetoend = (buffer.size() - localwrite);
60 | auto&& iter = std::copy_n(src_array.begin(), buffer.size() - localwrite, buffer.begin() + localwrite);
61 | std::copy_n(src_array.begin() + writetoend, target_index_ringed, buffer.begin());
62 | }
63 | else {
64 | std::copy_n(src_array.begin(), size, buffer.begin() + localwrite);
65 | }
66 | write_index = target_index_ringed;
67 | return true;
68 | }
69 | int getReadMargin() {
70 | size_t localread = read_index;
71 | size_t localwrite = write_index;
72 |
73 | auto margin = localwrite - localread;
74 | if (margin < 0) {
75 | margin = buffer.size() - (localread - localwrite);
76 | }
77 | return margin;
78 | }
79 | int getWriteMargin() {
80 | return buffer.size() - getReadMargin();
81 | }
82 |
83 | private:
84 | std::atomic write_index = 0;
85 | std::atomic read_index = 0;
86 | std::vector buffer;
87 | size_t stepIndex(size_t i, int increment) {
88 | return (i + increment) % buffer.size();
89 | }
90 | };
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/lockfree_ringbuffer_test.hpp:
--------------------------------------------------------------------------------
1 |
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #define CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS
8 | #include "lockfree_ringbuffer.hpp"
9 | #include // required unit test header
10 | TEST_CASE("Lockfree Ringbuffer") {
11 | const int size = 512;
12 | LockFreeRingbuf ringbuf(size);
13 |
14 | const int try_size = 2000;
15 |
16 | constexpr int chunk_write = 30;
17 | constexpr int chunk_read = 30;
18 | std::vector reference;
19 | std::vector answer;
20 | std::thread th1 {[&]() {
21 | std::vector src(chunk_write, 0);
22 | int counter = 0;
23 | while (true) {
24 | for (int i = 0; i < chunk_write; i++) {
25 | int16_t num = counter;
26 | reference.push_back(num);
27 | src.at(i) = num;
28 | counter++;
29 | }
30 | while (true) {
31 | bool writeres = ringbuf.writeRange(src, chunk_write);
32 | if (writeres) {
33 | break;
34 | }
35 | }
36 | if (counter > try_size + chunk_read) {
37 | break;
38 | }
39 | }
40 | return true;
41 | }};
42 | std::thread th2 {[&]() {
43 | std::vector src(chunk_read, 0);
44 | int counter = 0;
45 | while (true) {
46 | bool readres = ringbuf.readRange(src, chunk_read);
47 | if (readres) {
48 | answer.insert(answer.end(), src.begin(), src.end());
49 | counter += chunk_read;
50 | if (counter > try_size) {
51 | break;
52 | }
53 | }
54 | }
55 | return true;
56 | }};
57 | th1.join();
58 | th2.join();
59 | reference.resize(try_size);
60 | answer.resize(try_size);
61 | std::stringstream strstr;
62 | strstr << "different element: ";
63 | for (int i = 0; i < try_size; i++) {
64 | if (reference[i] != answer[i]) {
65 | strstr << i << ", ";
66 | }
67 | }
68 | strstr << std::endl;
69 | std::cerr << strstr.str() << std::endl;
70 | bool res = reference == answer;
71 | REQUIRE(res);
72 | }
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/min_debugutils.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #ifdef DEBUG
4 | #define DEBUG_LOG(STRING) cout << #STRING << std::endl;
5 | else
6 | #define DEBUG_LOG(STRING)
7 | #endif
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpreceiver.cpp:
--------------------------------------------------------------------------------
1 | #include "rtpreceiver.hpp"
2 |
3 | namespace rtpsr {
4 | RtpReceiver::RtpReceiver(std::unique_ptr s, Url const& url, Codec codec, double ringbuf_multiple, std::ostream& logger)
5 | : RtpSRBase(*s, logger) {
6 | auto option = std::make_unique(url, setting_ref.samplerate, setting_ref.channels, setting_ref.framesize);
7 | input = std::make_unique(std::move(option));
8 | output = std::make_unique(setting_ref, frame->nb_samples * ringbuf_multiple);
9 | this->codec = std::make_unique(setting_ref, codec);
10 | init();
11 | }
12 | RtpReceiver::RtpReceiver(std::unique_ptr s, Codec codec, double ringbuf_multiple, std::ostream& logger)
13 | : RtpSRBase(*s, logger) {
14 | input = std::make_unique(std::move(s));
15 | output = std::make_unique(setting_ref, frame->nb_samples * ringbuf_multiple);
16 | this->codec = std::make_unique(setting_ref, codec);
17 | init();
18 | }
19 | RtpReceiver::RtpReceiver(std::unique_ptr s, Codec codec, double ringbuf_multiple, std::ostream& logger)
20 | : RtpSRBase(*s, logger) {
21 | input = std::make_unique(std::move(s));
22 | time_cache = std::chrono::high_resolution_clock::now();
23 | input->ctx->interrupt_callback = AVIOInterruptCB {&RtpReceiver::timeoutCallback, this};
24 | output = std::make_unique(setting_ref, frame->nb_samples * ringbuf_multiple);
25 | this->codec = std::make_unique(setting_ref, codec);
26 | init();
27 | }
28 | int RtpReceiver::timeoutCallback(void* opaque) {
29 | auto receiver = reinterpret_cast(opaque);
30 | auto dur = std::chrono::high_resolution_clock::now() - receiver->time_cache;
31 | if (dur > std::chrono::seconds(2)) {
32 | return 1;
33 | }
34 | return 0;
35 | }
36 |
37 | RtpReceiver::~RtpReceiver() {
38 | std::cerr << "rtpreceiver destructor called" << std::endl;
39 | bool res1 = init_asyncloop.halt();
40 | auto status = asynclooper.wait_for(10000);
41 | if (res1) {
42 | avformat_close_input(&input->ctx);
43 | }
44 | }
45 |
46 | void RtpReceiver::init() {
47 |
48 | auto* rtpinput = dynamic_cast(input.get());
49 | tmpbuf.resize(frame->nb_samples * setting_ref.channels * 2);
50 | dtosbuffer.resize(frame->nb_samples * setting_ref.channels);
51 | input->ctx->max_delay = rtpinput->option->max_delay;
52 | init_asyncloop.launch([&]() {
53 | logger << "rtpreceiver waiting incoming connection..." << std::endl;
54 | while (init_asyncloop.isActive()) {
55 | // this start blocking...
56 | bool connection_res = dynamic_cast(input.get())->tryConnectInput();
57 | if (connection_res) {
58 | initStream();
59 | logger << "rtpreceiver connected" << std::endl;
60 | break;
61 | }
62 | }
63 | isconnected = true;
64 | return true;
65 | });
66 | }
67 | // return value:stopped_index;
68 | bool RtpReceiver::pushToOutput() {
69 | if (frame == nullptr) {
70 | return false;
71 | }
72 | auto* asyncoutput = dynamic_cast(output.get());
73 | auto* frameref = av_frame_get_plane_buffer(frame, 0);
74 | auto size = frame->nb_samples * frame->channels;
75 | tmpbuf.resize(size);
76 | std::memcpy(tmpbuf.data(), frameref->data, size * sizeof(int16_t));
77 | bool res = asyncoutput->tryPushRingBuffer(tmpbuf);
78 | av_packet_unref(packet);
79 | return res;
80 | }
81 | bool RtpReceiver::readFromOutput(std::vector& dest) {
82 | auto* asyncoutput = dynamic_cast(output.get());
83 | return asyncoutput->tryPopRingBuffer(dest);
84 | }
85 | bool RtpReceiver::readFromOutput(std::vector& dest) {
86 | assert(dtosbuffer.size() == dest.size());
87 | int count = 0;
88 | std::generate(dtosbuffer.begin(), dtosbuffer.end(), [&]() { return rtpsr::convertDoubleToSample(dest.at(count++)); });
89 | return readFromOutput(dtosbuffer);
90 | }
91 |
92 | bool RtpReceiver::receiveData() {
93 | time_cache = std::chrono::high_resolution_clock::now();
94 | int res = av_read_frame(input->ctx, packet);
95 | if (res == AVERROR(ETIMEDOUT)) {
96 | logger << avErrorString(res);
97 | return false; // timeout or eof(ignore)
98 | }
99 | if (res == AVERROR_EOF) {
100 | logger << avErrorString(res);
101 | return false; // timeout or eof(ignore)
102 | }
103 | checkAvError(res);
104 | return true;
105 | }
106 | void RtpReceiver::launchLoop() {
107 | asynclooper.launch([&]() {
108 | auto* decoder = dynamic_cast(codec.get());
109 | bool res = true;
110 | if (input->ctx == nullptr) {
111 | return false;
112 | }
113 | try {
114 | init_asyncloop.wait();
115 | timecount = 0;
116 | while (asynclooper.isActive()) {
117 | bool isdecoderfull = false;
118 | // block until data comes
119 | res = receiveData();
120 | isdecoderfull = decoder->sendPacket(packet);
121 | while (true) {
122 | bool isempty = decoder->receiveFrame(frame);
123 | if (isempty) {
124 | break;
125 | }
126 | while (true) {
127 | bool writeres = pushToOutput();
128 | if (!writeres) {
129 | std::cerr << "receiver ring buffer is full!!" << std::endl;
130 | std::this_thread::sleep_for(std::chrono::milliseconds(20));
131 | }
132 | else {
133 | // logger << "receiver pts:" << frame->pts << " |duration : " << frame->nb_samples <<" |latency :" <<
134 | // timecount-frame->pts << std::endl;
135 | double newlatency = timecount - frame->pts;
136 | if (newlatency != latency_cache) {
137 | latency.store(1000.0f * newlatency / setting_ref.samplerate);
138 | }
139 | latency_cache = newlatency;
140 | timecount += frame->nb_samples;
141 | break;
142 | }
143 | }
144 | }
145 | }
146 | }
147 | catch (std::exception& e) {
148 | std::cerr << "Error happend in receive polling loop:" << e.what() << std::endl;
149 | return false;
150 | }
151 | return true;
152 | });
153 | }
154 | bool RtpReceiver::isConnected() {
155 | return isconnected.load();
156 | }
157 | } // namespace rtpsr
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpreceiver.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "rtpsrbase.hpp"
3 |
4 | namespace rtpsr {
5 | struct RtpReceiver : public RtpSRBase {
6 | explicit RtpReceiver(std::unique_ptr s, Url const& url, Codec codec, double ringbuf_multiple=4.2,std::ostream& logger = std::cerr);
7 | explicit RtpReceiver(std::unique_ptr s, Codec codec,double ringbuf_multiple=4.2, std::ostream& logger = std::cerr);
8 | explicit RtpReceiver(std::unique_ptr s, Codec codec, double ringbuf_multiple=4.2,std::ostream& logger = std::cerr);
9 |
10 | ~RtpReceiver();
11 |
12 | void launchLoop() override;
13 | bool readFromOutput(std::vector& dest);
14 | bool readFromOutput(std::vector& dest);
15 | static int timeoutCallback(void * opaque);
16 | bool isConnected() override;
17 | double getLatency() const {return latency.load();};
18 | std::chrono::high_resolution_clock::time_point time_cache;
19 | private:
20 | std::vector dtosbuffer;
21 | std::vector tmpbuf;
22 | int64_t timecount;
23 | std::atomic latency;
24 | double latency_cache;
25 | void init();
26 | bool pushToOutput();
27 | bool receiveData();
28 | };
29 | } // namespace rtpsr
30 |
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsender.cpp:
--------------------------------------------------------------------------------
1 | #include "rtpsender.hpp"
2 |
3 | namespace rtpsr {
4 | RtpSender::RtpSender(std::unique_ptr s, Url const& url, Codec codec, std::chrono::milliseconds init_retry_rate,
5 | double ringbuf_multiple, std::ostream& logger)
6 | : RtpSRBase(*s, logger)
7 | , init_retry_rate(init_retry_rate)
8 | , pollingrate(duration_type(static_cast(s->framesize) * 0.5 * 48000 / s->samplerate)) {
9 | input = std::make_unique(setting_ref, setting_ref.framesize * ringbuf_multiple);
10 | auto option = std::make_unique(url, setting_ref.samplerate, setting_ref.channels, setting_ref.framesize);
11 | output = std::make_unique(std::move(option));
12 | this->codec = std::make_unique(*s, codec);
13 | init();
14 | }
15 | RtpSender::RtpSender(std::unique_ptr option, Codec codec, std::chrono::milliseconds init_retry_rate,
16 | double ringbuf_multiple, std::ostream& logger)
17 | : RtpSRBase(*static_cast(option.get()), logger)
18 | , init_retry_rate(init_retry_rate) {
19 | pollingrate = duration_type(static_cast(setting_ref.framesize) * 0.5 * 48000 / setting_ref.samplerate);
20 | input = std::make_unique(setting_ref, setting_ref.framesize * ringbuf_multiple);
21 | output = std::make_unique(std::move(option));
22 | this->codec = std::make_unique(setting_ref, codec);
23 | init();
24 | }
25 |
26 | RtpSender::RtpSender(std::unique_ptr option, Codec codec, std::chrono::milliseconds init_retry_rate,
27 | double ringbuf_multiple, std::ostream& logger)
28 | : RtpSRBase(*static_cast(option.get()), logger)
29 | , init_retry_rate(init_retry_rate) {
30 | pollingrate = duration_type(static_cast(setting_ref.framesize) * 0.5 * 48000 / setting_ref.samplerate);
31 | input = std::make_unique(setting_ref, setting_ref.framesize * ringbuf_multiple);
32 | output = std::make_unique(std::move(option));
33 | this->codec = std::make_unique(setting_ref, codec);
34 | init();
35 | }
36 |
37 |
38 | RtpSender::~RtpSender() {
39 | bool res1 = init_asyncloop.halt();
40 | bool res2 = asynclooper.halt();
41 | if (res1 && res2) {
42 | checkAvError(av_write_trailer(output->ctx));
43 |
44 | if (output->ctx->pb != nullptr) {
45 | avio_close(output->ctx->pb);
46 | }
47 | avformat_close_input(&input->ctx);
48 | }
49 | }
50 | void RtpSender::init() {
51 | initStream();
52 | dtosbuffer.resize(setting_ref.framesize * 2 * setting_ref.channels);
53 | init_asyncloop.launch([&]() {
54 | while (init_asyncloop.isActive()) {
55 | auto* rtpout = dynamic_cast(output.get());
56 | try {
57 | bool res = rtpout->tryConnect();
58 | if (res) {
59 | logger << "rtpsender: connected" << std::endl;
60 | break;
61 | }
62 | logger << "rtpsender: connection to " << rtpout->option->url.address << ":" << std::to_string(rtpout->option->url.port)
63 | << " refused,retry in " << std::to_string(init_retry_rate.count()) << "ms." << std::endl;
64 | std::this_thread::sleep_for(init_retry_rate);
65 | }
66 | catch (std::runtime_error& e) {
67 | throw e;
68 | break;
69 | }
70 | }
71 | isconnected = true;
72 | return true; // return result;
73 | });
74 | }
75 | bool RtpSender::writeToInput(std::vector const& input) {
76 | auto* asyncinput = dynamic_cast(this->input.get());
77 | return asyncinput->tryPushRingBuffer(input);
78 | }
79 | bool RtpSender::writeToInput(std::vector const& input) {
80 | assert(dtosbuffer.size() == input.size());
81 | int count = 0;
82 | std::generate(dtosbuffer.begin(), dtosbuffer.end(), [&]() { return rtpsr::convertDoubleToSample(input.at(count++)); });
83 | return writeToInput(dtosbuffer);
84 | }
85 | bool RtpSender::fillFrame() {
86 | auto* asyncinput = dynamic_cast(input.get());
87 | size_t packet_framesize = frame->nb_samples * setting_ref.channels;
88 |
89 | framebuf.resize(packet_framesize);
90 | bool res = asyncinput->tryPopRingBuffer(framebuf);
91 | if (res) {
92 | checkAvError(avcodec_fill_audio_frame(frame,
93 | setting_ref.channels,
94 | AV_SAMPLE_FMT_S16,
95 | (uint8_t*)framebuf.data(),
96 | sizeof(sample_t) * packet_framesize,
97 | 0));
98 | return true;
99 | }
100 | return false;
101 | }
102 | void RtpSender::sendData() {
103 | packet->pos = -1;
104 | packet->stream_index = 0;
105 | // packet->duration = setting_ref.framesize;
106 | auto itb = codec->ctx->time_base;
107 | auto otb = output->ctx->streams[0]->time_base;
108 | av_packet_rescale_ts(packet, otb, otb); // maybe unnecessary
109 | checkAvError(av_write_frame(output->ctx, packet));
110 | }
111 | void RtpSender::launchLoop() {
112 | asynclooper.launch([&]() {
113 | try {
114 | while (true) {
115 | if (!asynclooper.isActive()) {
116 | return false;
117 | }
118 | auto res = init_asyncloop.wait_for(20);
119 | if (res == std::future_status::ready) {
120 | break;
121 | }
122 | }
123 | auto* encoder = dynamic_cast(codec.get());
124 | frame->format = AV_SAMPLE_FMT_S16;
125 | frame->channels = setting_ref.channels;
126 | frame->channel_layout = codec->ctx->channel_layout;
127 | frame->nb_samples = setting_ref.framesize;
128 |
129 | while (asynclooper.isActive()) {
130 | bool hasinputframe = fillFrame();
131 | if (!hasinputframe) { }
132 | else {
133 | frame->pts = timecount;
134 | timecount += setting_ref.framesize;
135 | bool isfull = encoder->sendFrame(frame);
136 | if (isfull) {
137 | logger << "sender encoder is full" << std::endl;
138 | }
139 | while (true) {
140 | bool isempty = encoder->receivePacket(packet);
141 | if (isempty) {
142 | av_packet_unref(packet);
143 | break; // frame is fully flushed, finish sending
144 | }
145 | sendData();
146 | av_packet_unref(packet);
147 | }
148 | }
149 |
150 |
151 | std::this_thread::sleep_for(duration_type(64));
152 | }
153 | }
154 | catch (std::exception& e) {
155 | std::cerr << "Error happend in polling loop:" << e.what() << std::endl;
156 | return false;
157 | }
158 | return true;
159 | });
160 | }
161 | bool RtpSender::isConnected() {
162 | return isconnected.load();
163 | }
164 |
165 | } // namespace rtpsr
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsender.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "rtpsrbase.hpp"
3 |
4 | namespace rtpsr {
5 | struct RtpSender : public RtpSRBase {
6 | explicit RtpSender(std::unique_ptr s, Url const& url, Codec codec,
7 | std::chrono::milliseconds init_retry_rate = std::chrono::milliseconds(init_retry_rate_int), double ringbuf_multiple = 4.2,
8 | std::ostream& logger = std::cerr);
9 | // launch with raw rtp version;
10 | explicit RtpSender(std::unique_ptr option, Codec codec,
11 | std::chrono::milliseconds init_retry_rate = std::chrono::milliseconds(init_retry_rate_int), double ringbuf_multiple = 4.2,
12 | std::ostream& logger = std::cerr);
13 | // launch with rtsp version
14 | explicit RtpSender(std::unique_ptr option, Codec codec,
15 | std::chrono::milliseconds init_retry_rate = std::chrono::milliseconds(init_retry_rate_int), double ringbuf_multiple = 4.2,
16 | std::ostream& logger = std::cerr);
17 | ~RtpSender();
18 |
19 | void launchLoop() override;
20 | // communication entrypoint between max
21 | bool writeToInput(std::vector const& input);
22 | bool writeToInput(std::vector const& input);
23 | bool isConnected() override;
24 |
25 | private:
26 | int64_t timecount = 0;
27 | std::vector framebuf;
28 |
29 | // constructor must call init()
30 | void init();
31 | bool fillFrame();
32 | void sendData();
33 | std::vector dtosbuffer;
34 | duration_type pollingrate;
35 | const std::chrono::milliseconds init_retry_rate;
36 | static constexpr int init_retry_rate_int = 500;
37 | };
38 |
39 | } // namespace rtpsr
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsendreceive_lib.hpp:
--------------------------------------------------------------------------------
1 | // A simple ffmpeg wrapper to send/receive audio stream with rtp protocol
2 | // written in C++.
3 | // Copyright 2020 Tomoya Matsuura All rights Reserved.
4 | // This source code is released under LGPL lisence.
5 |
6 | #pragma once
7 |
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 |
16 | #include "libavutil/mem.h"
17 |
18 | #ifndef THREAD_SLEEP
19 | #define THREAD_SLEEP(s) std::this_thread::sleep_for(std::chrono::seconds(s));
20 | #endif
21 |
22 | namespace rtpsr {
23 | using sample_t = int16_t;
24 |
25 | inline double convertSampleToDouble(sample_t s) {
26 | return ((double)s) / INT16_MAX;
27 | }
28 | inline sample_t convertDoubleToSample(double d) {
29 | return (int16_t)(d * INT16_MAX);
30 | }
31 |
32 | struct buffer_data {
33 | sample_t* ptr;
34 | size_t size;
35 | };
36 |
37 | using buffertype = std::vector;
38 |
39 | using readfn_type = int (*)(void*, uint8_t*, int);
40 | using seekfn_type = int64_t (*)(void*, int64_t, int);
41 |
42 | using duration_type = std::chrono::duration>;
43 |
44 | enum class Codec { PCM_s16BE, OPUS, INVALID = -1 };
45 |
46 |
47 | template
48 | class AvSmartPtr {
49 | public:
50 | AvSmartPtr() {
51 | size_t s = sizeof(T);
52 | ptr = av_malloc(s);
53 | }
54 | ~AvSmartPtr() {
55 | av_free(ptr);
56 | }
57 | // do not copy
58 | T operator=(AvSmartPtr i) = delete;
59 |
60 | T* get() {
61 | return reinterpret_cast(ptr);
62 | }
63 | T* operator->() {
64 | return get();
65 | }
66 | T& operator*() {
67 | return *get();
68 | }
69 |
70 | private:
71 | void* ptr;
72 | };
73 |
74 |
75 | inline std::string getCodecName(Codec c) {
76 | switch (c) {
77 | case Codec::PCM_s16BE:
78 | return "pcm_s16be";
79 | break;
80 | case Codec::OPUS:
81 | return "libopus";
82 | break;
83 | default:
84 | return "";
85 | }
86 | }
87 | inline size_t getCodecDataSize(Codec c) {
88 | if (c == Codec::PCM_s16BE) {
89 | return sizeof(int16_t);
90 | }
91 | return sizeof(double);
92 | }
93 |
94 | inline Codec getCodecByName(const std::string& name) {
95 | if (name == "pcm_s16be") {
96 | return Codec::PCM_s16BE;
97 | }
98 | if (name == "opus") {
99 | return Codec::OPUS;
100 | }
101 | return Codec::INVALID;
102 | }
103 | } // namespace rtpsr
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsendreceive_lib_test.cmake:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0)
2 | set(TEST_NAME rtpsr_classes_test)
3 | # project(${PROJECT_NAME})
4 | set(C74_MIN_API_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../min-api)
5 | include(${C74_MIN_API_DIR}/script/min-pretarget.cmake)
6 | set(CMAKE_OSX_DEPLOYMENT_TARGET "10.14" CACHE STRING "Minimum OS X deployment version" FORCE)
7 | enable_testing()
8 |
9 | include_directories(
10 | "${C74_INCLUDES}"
11 | "${C74_MIN_API_DIR}"
12 | "${C74_MIN_API_DIR}/test"
13 | # "${C74_MIN_API_DIR}/test/mock"
14 | )
15 |
16 |
17 | add_executable(${TEST_NAME} ${TEST_NAME}.cpp )
18 | set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD 17)
19 | set_property(TARGET ${TEST_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
20 | target_compile_definitions(
21 | ${TEST_NAME}
22 | PUBLIC
23 | -DMIN_TEST
24 | )
25 |
26 | target_link_libraries(${TEST_NAME} PUBLIC "mock_kernel" rtpsendreceive)
27 |
28 | add_test(NAME ${TEST_NAME}
29 | COMMAND ${TEST_NAME})
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsr_classes_test.cpp:
--------------------------------------------------------------------------------
1 | // A simple ffmpeg wrapper to send/receive audio stream with rtp protocol
2 | // written in C++.
3 | // Copyright 2020 Tomoya Matsuura All rights Reserved.
4 | // This source code is released under LGPL 3.0 lisence.
5 | #include "rtpsender.hpp"
6 | #include "rtpreceiver.hpp"
7 |
8 | #include
9 | #define CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS
10 | #include "c74_min_unittest.h" // required unit test header
11 |
12 | TEST_CASE("RTSP loopback") {
13 | // av_log_set_level(AV_LOG_TRACE);
14 | auto setting_r = std::make_unique(rtpsr::RtpSRSetting{48000, 1, 128});
15 | auto setting_s = std::make_unique(rtpsr::RtpSRSetting{48000, 1, 128});
16 |
17 | rtpsr::Url url = {"127.0.0.1", 30000};
18 |
19 | auto receiver = std::make_unique(std::move(setting_r), url, rtpsr::Codec::PCM_s16BE);
20 | auto sender = std::make_unique(std::move(setting_s), url, rtpsr::Codec::PCM_s16BE);
21 |
22 | std::vector ref;
23 | std::vector input;
24 | for (int i = 0; i < 128; i++) {
25 | int16_t s = std::rand() / RAND_MAX;
26 | auto ds = ((double)s) * 2 - 1.0;
27 | ref.emplace_back(ds);
28 | input.emplace_back(s);
29 | }
30 | sender->writeToInput(input);
31 | sender->launchLoop();
32 | receiver->launchLoop();
33 | std::vector answer_s(input.size(), 0);
34 | std::vector answer;
35 | while (true) {
36 | auto res = receiver->readFromOutput(answer_s);
37 | if (res)
38 | break;
39 | std::this_thread::sleep_for(std::chrono::milliseconds(500));
40 | }
41 | sender.reset();
42 | receiver.reset();
43 | for (int i = 0; i < input.size(); i++) {
44 | answer.emplace_back(rtpsr::convertSampleToDouble(answer_s[i]));
45 | }
46 |
47 | REQUIRE(REQUIRE_VECTOR_APPROX(answer, ref) == true);
48 | }
49 | // TEST_CASE("RTSP Opus loopback") {
50 | // try {
51 | // // av_log_set_level(AV_LOG_TRACE);
52 | // rtpsr::RtpSRSetting setting = {48000, 1, 256};
53 | // rtpsr::Url url = {"127.0.0.1", 30000};
54 |
55 | // rtpsr::RtpReceiver receiver(setting, url, rtpsr::Codec::OPUS);
56 | // rtpsr::RtpSender sender(setting, url, rtpsr::Codec::OPUS);
57 | // std::vector ref;
58 | // for (int i = 0; i < 128; i++) {
59 | // auto r = ((double)std::rand() / RAND_MAX) * 2 - 1.0;
60 | // ref.emplace_back(r);
61 | // sender.getInput().setBuffer(r, i, 0);
62 | // }
63 | // sender.sendData();
64 | // receiver.receiveData();
65 | // std::vector answer;
66 |
67 | // for (int i = 0; i < 128; i++) {
68 | // answer.emplace_back(receiver.getOutput().readBuffer(i, 0));
69 | // }
70 |
71 | // REQUIRE(REQUIRE_VECTOR_APPROX(answer, ref) == true);
72 |
73 | // // std::exit(EXIT_SUCCESS);
74 | // } catch (std::exception &err) {
75 | // std::cerr << err.what() << "\n";
76 | // std::exit(EXIT_FAILURE);
77 | // }
78 | // }
79 |
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsrbase.cpp:
--------------------------------------------------------------------------------
1 | #include "rtpsrbase.hpp"
2 |
3 |
4 | #include
5 |
6 | namespace rtpsr {
7 | void checkAvError(int error_code) {
8 | if (error_code < 0) {
9 | throw std::runtime_error(avErrorString(error_code));
10 | }
11 | }
12 | std::string avErrorString(int error_code) {
13 | if (error_code < 0) {
14 | std::string res("", 4096);
15 | av_make_error_string(res.data(), 4096, error_code);
16 | return res;
17 | }
18 | return "";
19 | }
20 | AVOptionBase::AVOptionBase(container_t&& init) {
21 | for (auto& [key, val] : init) {
22 | assert(!std::holds_alternative(val));
23 | if (std::holds_alternative(val)) {
24 | checkAvError(av_dict_set_int(&this->params, key.c_str(), std::get(val), 0));
25 | allocated |= true;
26 | }
27 | if (std::holds_alternative(val)) {
28 | checkAvError(av_dict_set(&this->params, key.c_str(), std::get(val).c_str(), 0));
29 | allocated |= true;
30 | }
31 | }
32 | container = std::move(init);
33 | }
34 | AVOptionBase::~AVOptionBase() {
35 | if (allocated) {
36 | av_dict_free(¶ms);
37 | }
38 | }
39 | std::string getSdpUrl(std::string const& address, int port) {
40 | return "udp://" + address + ":" + std::to_string(port) + "/live.sdp";
41 | }
42 | std::string getSdpUrl(Url url) {
43 | return getSdpUrl(url.address, url.port);
44 | }
45 | RtpOptionsBase::RtpOptionsBase(Url url)
46 | : url(std::move(url)) {
47 | dict.emplace("protocol_whitelist", "file,udp,rtp,tcp,rtsp");
48 | }
49 | void RtpOptionsBase::generateOptions() {
50 | dict.clear();
51 | dict.emplace("reorder_queue_size", (int)reorder_queue_size);
52 | dict.emplace("packet_size", (int)packet_size);
53 | }
54 | RtpOption::RtpOption(Url url, double samplerate, int channels, int buffersize)
55 | : RtpOptionsBase(std::move(url))
56 | , RtpSRSetting({samplerate, channels, buffersize}) {
57 | buffer_size = buffersize * channels * 4;
58 | }
59 | void RtpOption::generateOptions() {
60 | RtpOptionsBase::generateOptions();
61 | dict.emplace("buffer_size", (int)buffer_size);
62 | // todo::filter_source;
63 | }
64 |
65 | RtspOption::RtspOption(Url url, double samplerate, int channels, int buffersize)
66 | : RtpOptionsBase(std::move(url))
67 | , RtpSRSetting({samplerate, channels, buffersize}) {
68 | // dict.emplace("allowed_media_types",nullptr);
69 | dict.emplace("protocol_whitelist", "file,udp,rtp,tcp,rtsp");
70 | }
71 | void RtspOption::generateOptions() {
72 | RtpOptionsBase::generateOptions();
73 | dict.emplace("rtsp_transport", getProtocolString(rtsp_transport));
74 | dict.emplace("min_port", port_range.first);
75 | dict.emplace("max_port", port_range.second);
76 | dict.emplace("listen_timeout", listen_timeout);
77 | // for compatibility of old ffmpeg
78 | dict.emplace("timeout", listen_timeout);
79 | dict.emplace("stimeout", socket_timeout);
80 | }
81 | void RtspInOption::generateOptions() {
82 | RtspOption::generateOptions();
83 | dict.emplace("rtsp_flags", "listen");
84 | }
85 | std::string RtspOption::getProtocolString(TransPortProtocol p) {
86 | switch (p) {
87 | using Protocol = RtspOption::TransPortProtocol;
88 | case Protocol::UDP:
89 | return "udp";
90 | case Protocol::TCP:
91 | return "tcp";
92 | case Protocol::UDP_MULTICAST:
93 | return "udp_multicast";
94 | case Protocol::HTTP:
95 | return "http";
96 | case Protocol::HTTPS:
97 | return "https";
98 | }
99 | }
100 |
101 | std::string RtpInOption::makeDummySdp() {
102 | std::string sdp_content =
103 | R"(SDP:
104 | v=0
105 | o=- 0 0 IN IP4 $address$
106 | s=DUMMY SDP
107 | c=IN IP4 $address$
108 | t=0 0
109 | a=tool:libavformat 58.29.100
110 | m=audio $port$ RTP/AVP 97
111 | b=AS:$bitrate$
112 | a=rtpmap:97 L16/$samplerate$/$channels$)";
113 | std::vector> replace_pairs = {{"\\$port\\$", std::to_string(url.port)},
114 | {"\\$address\\$", url.address},
115 | {"\\$channels\\$", std::to_string(channels)},
116 | {"\\$bitrate\\$", std::to_string((samplerate / 1000) * 16 * channels)},
117 | {"\\$samplerate\\$", std::to_string(samplerate)}};
118 | std::for_each(replace_pairs.begin(), replace_pairs.end(), [&](auto pair) {
119 | sdp_content = std::regex_replace(sdp_content, std::regex(pair.first), pair.second);
120 | });
121 | return sdp_content;
122 | }
123 | int RtpInOption::readDummySdp(void* userdata, uint8_t* avio_buf, int buf_size) {
124 | auto octx = static_cast(userdata);
125 | if (octx->pos == octx->data.end()) {
126 | return 0;
127 | }
128 | auto dist = static_cast(std::distance(octx->pos, octx->data.end()));
129 | auto count = std::min(buf_size, dist);
130 | std::copy(octx->pos, octx->pos + count, avio_buf);
131 | octx->pos += count;
132 | return count;
133 | }
134 |
135 | // IO Format
136 |
137 | IOFormat::IOFormat()
138 | : ctx(avformat_alloc_context()) { }
139 | IOFormat::~IOFormat() {
140 | avformat_free_context(ctx);
141 | }
142 | // InFormat
143 | InFormat::InFormat() = default;
144 | // OutFormat
145 | OutFormat::OutFormat() = default;
146 |
147 | // old cutomcallback format
148 | CustomCbInFormat::CustomCbInFormat(RtpSRSetting const& s)
149 | : InFormat() {
150 | buffer.resize(setting.framesize * setting.channels);
151 | auto bufsize = getBufSize(s);
152 | auto* aviobuf = static_cast(av_malloc(bufsize));
153 | auto* avioctx = avio_alloc_context(aviobuf, bufsize, 0, this, readPacket, nullptr, nullptr);
154 | ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
155 | ctx->pb = avioctx;
156 | }
157 |
158 |
159 | int CustomCbInFormat::readPacket(void* userdata, uint8_t* avio_buf, int buf_size) {
160 | auto* sender = reinterpret_cast(userdata);
161 | auto* address = sender->buffer.data();
162 | getBufSize(sender->setting);
163 | memcpy(avio_buf, address, buf_size);
164 | return buf_size;
165 | }
166 | CustomCbOutFormat::CustomCbOutFormat(RtpSRSetting const& s)
167 | : OutFormat() {
168 | buffer.resize(setting.framesize);
169 |
170 | auto bufsize = getBufSize(s);
171 | auto* aviobuf = static_cast(av_malloc(bufsize));
172 | auto* avioctx = avio_alloc_context(aviobuf, bufsize, 1, this, nullptr, writePacket, nullptr);
173 | ctx->flags |= AVFMT_FLAG_CUSTOM_IO;
174 | ctx->pb = avioctx;
175 | }
176 | int CustomCbOutFormat::writePacket(void* userdata, uint8_t* avio_buf, int buf_size) {
177 | auto* receiver = reinterpret_cast(userdata);
178 | auto* address = receiver->buffer.data();
179 | memcpy(address, avio_buf, buf_size);
180 | return buf_size;
181 | }
182 |
183 | // CustomCbAsyncFormat
184 | bool CustomCbAsyncFormat::tryPushRingBuffer(std::vector const& input) {
185 | return buffer.writeRange(input, input.size());
186 | }
187 | bool CustomCbAsyncFormat::tryPopRingBuffer(std::vector& dest) {
188 | return buffer.readRange(dest, dest.size());
189 | }
190 | CustomCbAsyncInFormat::CustomCbAsyncInFormat(RtpSRSetting const& s, size_t buffer_size)
191 | : InFormat()
192 | , CustomCbAsyncFormat(std::max((int)buffer_size, s.framesize) * s.channels) { }
193 | CustomCbAsyncOutFormat::CustomCbAsyncOutFormat(RtpSRSetting const& s, size_t buffer_size)
194 | : OutFormat()
195 | , CustomCbAsyncFormat(std::max((int)buffer_size, s.framesize) * s.channels) { }
196 |
197 | // Rtp Format base(for now, nothing)
198 |
199 |
200 | // Rtp Input
201 | RtpFormatBase::RtpFormatBase(std::unique_ptr opt)
202 | : option(std::move(opt)) {
203 | avformat_network_init();
204 | }
205 | RtspInFormat::RtspInFormat(std::unique_ptr rtpoptions)
206 | : RtpInFormatBase(std::move(rtpoptions)) { }
207 |
208 | bool RtspInFormat::tryConnectInput() {
209 | option->generateOptions();
210 |
211 | auto* ifmt = av_find_input_format("rtsp");
212 | auto res = avformat_open_input(&ctx, getSdpUrl(option->url).c_str(), ifmt, option->getParam());
213 | if (res < 0) {
214 | try {
215 | checkAvError(res);
216 | }
217 | catch (std::exception& e) {
218 | std::cerr << e.what() << "\nFailed to launch server at Specified address, wait connection at 127.0.0.1..." << std::endl;
219 | option->url = rtpsr::Url {"127.0.0.1", option->url.port};
220 | }
221 | return false;
222 | }
223 | return true;
224 | }
225 | RtpInFormat::RtpInFormat(std::unique_ptr rtpoptions)
226 | : RtpInFormatBase(std::move(rtpoptions)) {
227 | sdp_avio = (unsigned char*)av_mallocz(aviobufsize);
228 | }
229 | RtpInFormat::~RtpInFormat() {
230 | if (sdp_avio != nullptr) {
231 | // av_free(sdp_avio);
232 | }
233 | }
234 |
235 | bool RtpInFormat::tryConnectInput() {
236 | auto* opt = dynamic_cast(option.get());
237 | opt->generateOptions();
238 | const auto* ifmt = av_find_input_format("sdp");
239 | auto sdp = opt->makeDummySdp();
240 | sdp_opaque = std::make_unique();
241 | sdp_opaque->data = SdpOpaque::Vector(sdp.c_str(), sdp.c_str() + strlen(sdp.c_str()));
242 | sdp_opaque->pos = sdp_opaque->data.begin();
243 | ctx->pb = avio_alloc_context(sdp_avio, aviobufsize, 0, sdp_opaque.get(), RtpInOption::readDummySdp, nullptr, nullptr);
244 | auto res = avformat_open_input(&ctx, "internal.sdp", ifmt, option->getParam());
245 | if (res == -60 || res == -61 || res == -22) {
246 | return false;
247 | }
248 | if (res < 0) {
249 | checkAvError(res);
250 | }
251 | return true;
252 | }
253 |
254 | RtspOutFormat::RtspOutFormat(std::unique_ptr rtpoptions)
255 | : RtpOutFormatBase(std::move(rtpoptions)) { }
256 | RtpOutFormat::RtpOutFormat(std::unique_ptr rtpoptions)
257 | : RtpOutFormatBase(std::move(rtpoptions)) { }
258 |
259 |
260 | bool RtspOutFormat::tryConnect() {
261 | option->generateOptions();
262 | auto url_tmp = getSdpUrl(option->url);
263 | auto* fmt_output = av_guess_format("rtsp", url_tmp.c_str(), nullptr);
264 | ctx->oformat = fmt_output;
265 | ctx->url = (char*)av_malloc(url_tmp.size() + sizeof(char));
266 | av_strlcpy(ctx->url, url_tmp.c_str(), url_tmp.size() + sizeof(char));
267 | checkAvError(avformat_init_output(ctx, option->getParam()));
268 | checkAvError(avio_open(&ctx->pb, ctx->url, AVIO_FLAG_WRITE));
269 | int rcode = avformat_write_header(ctx, nullptr);
270 | if (rcode >= 0) {
271 | return true;
272 | }
273 | if (rcode == -60 || rcode == -61 || rcode == -22) {
274 | return false;
275 | }
276 | checkAvError(rcode);
277 | return false;
278 | }
279 |
280 | bool RtpOutFormat::tryConnect() {
281 | option->generateOptions();
282 | auto url_tmp = getSdpUrl(option->url);
283 | const auto* fmt_output = av_guess_format("rtp", url_tmp.c_str(), "audio/L16");
284 |
285 | auto codec_id = av_guess_codec(fmt_output, "pcm_s16be", nullptr, "audio/L16", AVMEDIA_TYPE_AUDIO);
286 | ctx->audio_codec_id = codec_id;
287 | ctx->oformat = fmt_output;
288 | ctx->url = static_cast(av_malloc(url_tmp.size() + sizeof(char)));
289 | av_strlcpy(ctx->url, url_tmp.c_str(), url_tmp.size() + sizeof(char));
290 | checkAvError(avformat_init_output(ctx, option->getParam()));
291 | checkAvError(avio_open(&ctx->pb, ctx->url, AVIO_FLAG_WRITE));
292 | int rcode = avformat_write_header(ctx, nullptr);
293 | if (rcode >= 0) {
294 | return true;
295 | }
296 | if (rcode == -60 || rcode == -61 || rcode == -22) {
297 | return false;
298 | }
299 | checkAvError(rcode);
300 | return false;
301 | }
302 |
303 | CodecBase::CodecBase(RtpSRSetting const& s, Codec c, bool isencoder)
304 | : codec(c) {
305 | auto* avcodec = (isencoder) ? avcodec_find_encoder_by_name(getCodecName(codec).c_str())
306 | : avcodec_find_decoder_by_name(getCodecName(codec).c_str());
307 | ctx = avcodec_alloc_context3(avcodec);
308 | ctx->bit_rate = bitrate;
309 | ctx->sample_rate = s.samplerate;
310 | ctx->frame_size = s.framesize;
311 | ctx->channels = s.channels;
312 | ctx->channel_layout = av_get_default_channel_layout(s.channels);
313 | ctx->sample_fmt = AV_SAMPLE_FMT_S16;
314 | avcodec_open2(ctx, avcodec, nullptr);
315 | }
316 | bool CodecBase::checkIsErrAgain(int error_code) {
317 | if (error_code == AVERROR(EAGAIN)) {
318 | return true; // return isempty
319 | }
320 | checkAvError(error_code);
321 | return false;
322 | }
323 |
324 | Decoder::Decoder(RtpSRSetting const& s, Codec c)
325 | : CodecBase(s, c, false) { }
326 | Encoder::Encoder(RtpSRSetting const& s, Codec c)
327 | : CodecBase(s, c, true) {
328 | ctx->frame_size = s.framesize;
329 | }
330 | bool Decoder::sendPacket(AVPacket* packet) {
331 | return checkIsErrAgain(avcodec_send_packet(ctx, packet));
332 | }
333 | bool Decoder::receiveFrame(AVFrame* frame) {
334 | return checkIsErrAgain(avcodec_receive_frame(ctx, frame));
335 | }
336 |
337 | bool Encoder::sendFrame(AVFrame* frame) {
338 | return checkIsErrAgain(avcodec_send_frame(ctx, frame));
339 | }
340 | bool Encoder::receivePacket(AVPacket* packet) {
341 | return checkIsErrAgain(avcodec_receive_packet(ctx, packet));
342 | }
343 |
344 | bool AsyncLooper::halt() {
345 | if (active) {
346 | active = false;
347 | wait();
348 | }
349 | return true;
350 | }
351 | bool AsyncLooper::forceHalt() {
352 | active = false;
353 | return true;
354 | }
355 | void AsyncLooper::wait() {
356 | future.wait();
357 | bool res = future.get();
358 | }
359 |
360 | bool AsyncLooper::isActive() const {
361 | return active;
362 | }
363 | std::future_status AsyncLooper::wait_for(int mills) {
364 | return future.wait_for(std::chrono::milliseconds(mills));
365 | }
366 |
367 | // RtpSRBase
368 |
369 | RtpSRBase::RtpSRBase(RtpSRSetting const& s, std::ostream& logger)
370 | : setting_ref(s)
371 | , logger(logger) {
372 | frame = av_frame_alloc();
373 | frame->nb_samples = s.framesize;
374 | packet = av_packet_alloc();
375 | av_new_packet(packet, getBufSize(s));
376 | }
377 |
378 | RtpSRBase::~RtpSRBase() {
379 | av_frame_free(&frame);
380 | av_packet_free(&packet);
381 | }
382 | void RtpSRBase::initStream() const {
383 | auto* instream = avformat_new_stream(input->ctx, codec->ctx->codec);
384 | auto* outstream = avformat_new_stream(output->ctx, codec->ctx->codec);
385 | checkAvError(avcodec_parameters_from_context(instream->codecpar, codec->ctx));
386 | checkAvError(avcodec_parameters_from_context(outstream->codecpar, codec->ctx));
387 | instream->start_time = 0;
388 | outstream->start_time = 0;
389 | }
390 | } // namespace rtpsr
--------------------------------------------------------------------------------
/source/rtpsendreceive_lib/rtpsrbase.hpp:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | extern "C" {
4 | #include "libavcodec/avcodec.h"
5 | #include "libavformat/avformat.h"
6 | #include "libavutil/avassert.h"
7 | #include "libavutil/avstring.h"
8 | #include "libavutil/intreadwrite.h"
9 | #include "libavutil/mathematics.h"
10 | #include "libavutil/opt.h"
11 | #include "libavutil/timestamp.h"
12 | #include "libavutil/channel_layout.h"
13 | }
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 |
20 | #include "rtpsendreceive_lib.hpp"
21 | #include "lockfree_ringbuffer.hpp"
22 |
23 |
24 | namespace rtpsr {
25 |
26 |
27 | void checkAvError(int error_code);
28 | std::string avErrorString(int error_code);
29 | struct SdpOpaque {
30 | using Vector = std::vector;
31 | Vector data;
32 | Vector::iterator pos;
33 | };
34 |
35 | struct AVOptionBase {
36 | using keytype = std::string;
37 | using valtype = std::variant;
38 | using container_t = std::unordered_map;
39 | AVOptionBase() = delete;
40 | explicit AVOptionBase(container_t&& init);
41 | ~AVOptionBase();
42 | AVDictionary** get() {
43 | return ¶ms;
44 | }
45 |
46 | private:
47 | AVDictionary* params = nullptr;
48 | container_t container = {};
49 | bool allocated = false;
50 | };
51 |
52 | struct RtpSRSetting {
53 | double samplerate;
54 | int channels;
55 | int framesize;
56 | };
57 |
58 | inline size_t getBufSize(RtpSRSetting const& s) {
59 | // how sample format?
60 | return sizeof(double) * s.framesize * s.channels;
61 | }
62 | struct Url {
63 | std::string address = "127.0.0.1";
64 | int port = 30000;
65 | };
66 | std::string getSdpUrl(std::string const& address, int port);
67 | std::string getSdpUrl(Url url);
68 |
69 | enum class RtpFormatKind { RTP = 0, RTSP = 1 };
70 |
71 | inline std::string toString(RtpFormatKind k) {
72 | switch (k) {
73 | case RtpFormatKind::RTP:
74 | return "rtp";
75 | case RtpFormatKind::RTSP:
76 | return "rtsp";
77 | default:
78 | assert(false);
79 | return "";
80 | }
81 | }
82 |
83 | struct RtpOptionsBase {
84 | explicit RtpOptionsBase(Url url);
85 | virtual ~RtpOptionsBase() = default;
86 | Url url;
87 |
88 | // number of packet of reorder queue
89 | size_t reorder_queue_size = 1000;
90 | size_t packet_size = 1000;
91 | // maximum reorder delay(in microseconds) to be set to audioformatcontext
92 | int max_delay = 500000;
93 | size_t buffer_size;
94 |
95 | virtual RtpFormatKind getKind() = 0;
96 | virtual void generateOptions();
97 | AVDictionary** getParam() {
98 | avoptions = std::make_unique(std::move(dict));
99 | return avoptions->get();
100 | }
101 | AVOptionBase::container_t dict;
102 | std::unique_ptr avoptions;
103 | };
104 | class RtpOption : public RtpSRSetting, public RtpOptionsBase {
105 | public:
106 | explicit RtpOption(Url url, double samplerate, int channels, int buffersize);
107 | RtpFormatKind getKind() override {
108 | return RtpFormatKind::RTP;
109 | };
110 | void generateOptions() override;
111 |
112 | bool filter_source = false;
113 | };
114 | class RtspOption : public RtpSRSetting, public RtpOptionsBase {
115 | public:
116 | explicit RtspOption(Url url, double samplerate, int channels, int buffersize);
117 |
118 | RtpFormatKind getKind() override {
119 | return RtpFormatKind::RTSP;
120 | };
121 | void generateOptions() override;
122 | enum class TransPortProtocol { UDP = 0, TCP, UDP_MULTICAST, HTTP, HTTPS } rtsp_transport = TransPortProtocol::UDP;
123 | int listen_timeout = 1000; // unit:seconds
124 | int socket_timeout = 500000; // unit:microseconds
125 | std::pair port_range = {5000, 65000};
126 |
127 | private:
128 | static std::string getProtocolString(TransPortProtocol p);
129 | };
130 | class RtpInOption : public RtpOption {
131 | public:
132 | explicit RtpInOption(Url url, double samplerate, int channels, int buffersize)
133 | : RtpOption(url, samplerate, channels, buffersize) { }
134 | std::string makeDummySdp();
135 | static int readDummySdp(void* userdata, uint8_t* avio_buf, int buf_size);
136 | bool use_customio = false;
137 | bool rtcp_to_source = false;
138 | };
139 | class RtpOutOption : public RtpOption {
140 | public:
141 | explicit RtpOutOption(Url url, double samplerate, int channels, int buffersize)
142 | : RtpOption(url, samplerate, channels, buffersize) { }
143 | };
144 |
145 | class RtspOutOption : public RtspOption {
146 | public:
147 | explicit RtspOutOption(Url url, double samplerate, int channels, int buffersize)
148 | : RtspOption(url, samplerate, channels, buffersize) { }
149 | };
150 | class RtspInOption : public RtspOption {
151 | public:
152 | explicit RtspInOption(Url url, double samplerate, int channels, int buffersize)
153 | : RtspOption(url, samplerate, channels, buffersize) { }
154 | void generateOptions() override;
155 | };
156 |
157 | // IO Format
158 | struct IOFormat {
159 | IOFormat();
160 | virtual ~IOFormat();
161 | AVFormatContext* ctx;
162 | RtpSRSetting setting;
163 | };
164 | struct InFormat : public IOFormat {
165 | InFormat();
166 | ~InFormat() override = default;
167 | virtual void connectInput() {};
168 | };
169 | struct OutFormat : public IOFormat {
170 | OutFormat();
171 | virtual ~OutFormat() = default;
172 | virtual void startOutput() {};
173 | };
174 |
175 | // Custom Callback Async Format
176 | // Uses LockFreeRingbuffer. This does not uses libavformat. Data are assumed to be taken from avframe directly.
177 | class CustomCbAsyncFormat {
178 | public:
179 | virtual ~CustomCbAsyncFormat() = default;
180 | explicit CustomCbAsyncFormat(size_t buffer_size)
181 | : buffer(buffer_size) { }
182 | virtual bool tryPushRingBuffer(std::vector const& input);
183 | virtual bool tryPopRingBuffer(std::vector& dest);
184 |
185 | private:
186 | LockFreeRingbuf buffer;
187 | };
188 |
189 | class CustomCbAsyncInFormat final : public InFormat, public CustomCbAsyncFormat {
190 | public:
191 | explicit CustomCbAsyncInFormat(RtpSRSetting const& s, size_t buffer_size);
192 | ~CustomCbAsyncInFormat() final = default;
193 | };
194 | class CustomCbAsyncOutFormat final : public OutFormat, public CustomCbAsyncFormat {
195 | public:
196 | explicit CustomCbAsyncOutFormat(RtpSRSetting const& s, size_t buffer_size);
197 | ~CustomCbAsyncOutFormat() final = default;
198 | };
199 |
200 | // old classes for synchronous reading
201 | struct CustomCbFormat {
202 | rtpsr::buffertype buffer;
203 | };
204 | struct CustomCbInFormat : public InFormat, public CustomCbFormat {
205 | explicit CustomCbInFormat(RtpSRSetting const& s);
206 | ~CustomCbInFormat() = default;
207 | static int readPacket(void* userdata, uint8_t* avio_buf, int buf_size);
208 |
209 | void setBuffer(double sample, int pos, int channel) {
210 | buffer[pos * setting.channels + channel] = static_cast(sample * INT16_MAX);
211 | }
212 | };
213 | struct CustomCbOutFormat : public OutFormat, public CustomCbFormat {
214 | explicit CustomCbOutFormat(RtpSRSetting const& s);
215 | ~CustomCbOutFormat() { }
216 | static int writePacket(void* userdata, uint8_t* avio_buf, int buf_size);
217 | template
218 | T readBuffer(int pos, int channel) {
219 | return static_cast(buffer.at(pos * setting.channels + channel));
220 | };
221 | template<>
222 | rtpsr::sample_t readBuffer(int pos, int channel) {
223 | return buffer.at(pos * setting.channels + channel);
224 | };
225 | template<>
226 | double readBuffer(int pos, int channel) {
227 | return static_cast(buffer.at(pos * setting.channels + channel)) / INT16_MAX;
228 | }
229 | };
230 |
231 | // rtp in out format.
232 | struct RtpFormatBase {
233 | RtpFormatBase(std::unique_ptr opt);
234 | std::unique_ptr option;
235 | };
236 | struct RtpInFormatBase : public InFormat, public RtpFormatBase {
237 | RtpInFormatBase(std::unique_ptr opt)
238 | : RtpFormatBase(std::move(opt)) {
239 |
240 | };
241 | virtual bool tryConnectInput() = 0;
242 | };
243 |
244 | struct RtspInFormat : public RtpInFormatBase {
245 | RtspInFormat() = delete;
246 | explicit RtspInFormat(std::unique_ptr rtpoptions);
247 | bool tryConnectInput() override;
248 | };
249 |
250 | struct RtpInFormat : public RtpInFormatBase {
251 | RtpInFormat() = delete;
252 | explicit RtpInFormat(std::unique_ptr rtpoptions);
253 | ~RtpInFormat() override;
254 | bool tryConnectInput() override;
255 | std::unique_ptr sdp_opaque;
256 | unsigned char* sdp_avio;
257 | inline static constexpr size_t aviobufsize = 4096;
258 | };
259 |
260 | struct RtpOutFormatBase : public OutFormat, public RtpFormatBase {
261 | RtpOutFormatBase(std::unique_ptr opt)
262 | : RtpFormatBase(std::move(opt)) {};
263 | virtual bool tryConnect() = 0;
264 | };
265 | struct RtspOutFormat : public RtpOutFormatBase {
266 | RtspOutFormat() = delete;
267 | explicit RtspOutFormat(std::unique_ptr rtpoptions);
268 | bool tryConnect() override;
269 | };
270 |
271 | struct RtpOutFormat : public RtpOutFormatBase {
272 | RtpOutFormat() = delete;
273 | explicit RtpOutFormat(std::unique_ptr rtpoptions);
274 | bool tryConnect() override;
275 | };
276 |
277 | struct CodecBase {
278 | explicit CodecBase(RtpSRSetting const& s, Codec c = Codec::PCM_s16BE, bool isencoder = true);
279 | virtual ~CodecBase() = default;
280 | AVCodecContext* ctx = nullptr;
281 | rtpsr::Codec codec;
282 | int64_t bitrate = 192000; // bitrate for lossy compression
283 | static bool checkIsErrAgain(int error_code);
284 | };
285 | struct Decoder : public CodecBase {
286 | explicit Decoder(RtpSRSetting const& s, Codec c);
287 | ~Decoder() override {
288 | avcodec_free_context(&ctx);
289 | }
290 | bool sendPacket(AVPacket* packet);
291 | bool receiveFrame(AVFrame* frame);
292 | };
293 | struct Encoder : public CodecBase {
294 | explicit Encoder(RtpSRSetting const& s, Codec c);
295 | ~Encoder() override {
296 | avcodec_free_context(&ctx);
297 | }
298 | bool sendFrame(AVFrame* frame);
299 | bool receivePacket(AVPacket* packet);
300 | };
301 | struct AsyncLoopState {
302 | std::atomic active = false;
303 | std::future future;
304 | };
305 |
306 | class AsyncLooper {
307 | public:
308 | AsyncLooper() = default;
309 | using callbacktype = void (*)(AsyncLooper const&);
310 |
311 | template
312 | auto launch(F&& fn) {
313 | active = true;
314 | future = std::async(std::launch::async, std::move(fn));
315 | return future;
316 | }
317 | bool halt();
318 | bool forceHalt();
319 | void wait();
320 | std::future_status wait_for(int mills);
321 | bool isActive() const;
322 |
323 | private:
324 | std::atomic active = false;
325 | std::shared_future future;
326 | };
327 |
328 | struct RtpSRBase {
329 | explicit RtpSRBase(RtpSRSetting const& s, std::ostream& logger = std::cerr);
330 | ~RtpSRBase();
331 | virtual void launchLoop() = 0;
332 | virtual bool isConnected() = 0;
333 | AsyncLooper asynclooper;
334 | AsyncLooper init_asyncloop;
335 |
336 | protected:
337 | void initStream() const;
338 | const RtpSRSetting& setting_ref;
339 | std::unique_ptr input;
340 | std::unique_ptr output;
341 | std::unique_ptr codec;
342 | AVPacket* packet;
343 | AVFrame* frame;
344 | std::ostream& logger;
345 | std::atomic isconnected = false;
346 | };
347 |
348 | } // namespace rtpsr
--------------------------------------------------------------------------------
/test_patch/receiver_test.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "appversion" : {
5 | "major" : 8,
6 | "minor" : 0,
7 | "revision" : 5,
8 | "architecture" : "x64",
9 | "modernui" : 1
10 | }
11 | ,
12 | "classnamespace" : "box",
13 | "rect" : [ 161.0, 165.0, 805.0, 629.0 ],
14 | "bglocked" : 0,
15 | "openinpresentation" : 0,
16 | "default_fontsize" : 12.0,
17 | "default_fontface" : 0,
18 | "default_fontname" : "Arial",
19 | "gridonopen" : 1,
20 | "gridsize" : [ 15.0, 15.0 ],
21 | "gridsnaponopen" : 1,
22 | "objectsnaponopen" : 1,
23 | "statusbarvisible" : 2,
24 | "toolbarvisible" : 1,
25 | "lefttoolbarpinned" : 0,
26 | "toptoolbarpinned" : 0,
27 | "righttoolbarpinned" : 0,
28 | "bottomtoolbarpinned" : 0,
29 | "toolbars_unpinned_last_save" : 0,
30 | "tallnewobj" : 0,
31 | "boxanimatetime" : 200,
32 | "enablehscroll" : 1,
33 | "enablevscroll" : 1,
34 | "devicewidth" : 0.0,
35 | "description" : "",
36 | "digest" : "",
37 | "tags" : "",
38 | "style" : "",
39 | "subpatcher_template" : "",
40 | "boxes" : [ {
41 | "box" : {
42 | "id" : "obj-7",
43 | "lastchannelcount" : 4,
44 | "maxclass" : "mc.live.gain~",
45 | "numinlets" : 1,
46 | "numoutlets" : 4,
47 | "outlettype" : [ "multichannelsignal", "", "float", "list" ],
48 | "parameter_enable" : 1,
49 | "patching_rect" : [ 63.0, 262.0, 48.0, 136.0 ],
50 | "saved_attribute_attributes" : {
51 | "valueof" : {
52 | "parameter_mmin" : -70.0,
53 | "parameter_longname" : "mc.live.gain~[1]",
54 | "parameter_mmax" : 6.0,
55 | "parameter_shortname" : "mc.live.gain~",
56 | "parameter_type" : 0,
57 | "parameter_unitstyle" : 4
58 | }
59 |
60 | }
61 | ,
62 | "varname" : "mc.live.gain~[1]"
63 | }
64 |
65 | }
66 | , {
67 | "box" : {
68 | "id" : "obj-9",
69 | "maxclass" : "toggle",
70 | "numinlets" : 1,
71 | "numoutlets" : 1,
72 | "outlettype" : [ "int" ],
73 | "parameter_enable" : 0,
74 | "patching_rect" : [ 250.0, 129.0, 24.0, 24.0 ]
75 | }
76 |
77 | }
78 | , {
79 | "box" : {
80 | "id" : "obj-3",
81 | "maxclass" : "attrui",
82 | "numinlets" : 1,
83 | "numoutlets" : 1,
84 | "outlettype" : [ "" ],
85 | "patching_rect" : [ 69.0, 85.0, 150.0, 22.0 ]
86 | }
87 |
88 | }
89 | , {
90 | "box" : {
91 | "id" : "obj-1",
92 | "maxclass" : "newobj",
93 | "numinlets" : 1,
94 | "numoutlets" : 1,
95 | "outlettype" : [ "" ],
96 | "patching_rect" : [ 63.0, 209.0, 260.0, 22.0 ],
97 | "text" : "mc.rtpreceive~.mxo @channels 2 @port 30000"
98 | }
99 |
100 | }
101 | ],
102 | "lines" : [ {
103 | "patchline" : {
104 | "destination" : [ "obj-7", 0 ],
105 | "source" : [ "obj-1", 0 ]
106 | }
107 |
108 | }
109 | , {
110 | "patchline" : {
111 | "destination" : [ "obj-1", 0 ],
112 | "source" : [ "obj-3", 0 ]
113 | }
114 |
115 | }
116 | , {
117 | "patchline" : {
118 | "destination" : [ "obj-1", 0 ],
119 | "source" : [ "obj-9", 0 ]
120 | }
121 |
122 | }
123 | ],
124 | "parameters" : {
125 | "obj-7" : [ "mc.live.gain~[1]", "mc.live.gain~", 0 ],
126 | "parameterbanks" : {
127 |
128 | }
129 |
130 | }
131 | ,
132 | "dependency_cache" : [ {
133 | "name" : "mc.rtpreceive~.mxo",
134 | "type" : "iLaX"
135 | }
136 | ],
137 | "autosave" : 0
138 | }
139 |
140 | }
141 |
--------------------------------------------------------------------------------
/test_patch/self_loop_test.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "appversion" : {
5 | "major" : 8,
6 | "minor" : 1,
7 | "revision" : 5,
8 | "architecture" : "x64",
9 | "modernui" : 1
10 | }
11 | ,
12 | "classnamespace" : "box",
13 | "rect" : [ 215.0, 124.0, 1349.0, 811.0 ],
14 | "bglocked" : 0,
15 | "openinpresentation" : 0,
16 | "default_fontsize" : 12.0,
17 | "default_fontface" : 0,
18 | "default_fontname" : "Arial",
19 | "gridonopen" : 1,
20 | "gridsize" : [ 15.0, 15.0 ],
21 | "gridsnaponopen" : 1,
22 | "objectsnaponopen" : 1,
23 | "statusbarvisible" : 2,
24 | "toolbarvisible" : 1,
25 | "lefttoolbarpinned" : 0,
26 | "toptoolbarpinned" : 0,
27 | "righttoolbarpinned" : 0,
28 | "bottomtoolbarpinned" : 0,
29 | "toolbars_unpinned_last_save" : 0,
30 | "tallnewobj" : 0,
31 | "boxanimatetime" : 200,
32 | "enablehscroll" : 1,
33 | "enablevscroll" : 1,
34 | "devicewidth" : 0.0,
35 | "description" : "",
36 | "digest" : "",
37 | "tags" : "",
38 | "style" : "",
39 | "subpatcher_template" : "",
40 | "assistshowspatchername" : 0,
41 | "boxes" : [ {
42 | "box" : {
43 | "id" : "obj-46",
44 | "maxclass" : "message",
45 | "numinlets" : 2,
46 | "numoutlets" : 1,
47 | "outlettype" : [ "" ],
48 | "patching_rect" : [ 1038.0, 251.0, 50.0, 22.0 ]
49 | }
50 |
51 | }
52 | , {
53 | "box" : {
54 | "id" : "obj-41",
55 | "maxclass" : "message",
56 | "numinlets" : 2,
57 | "numoutlets" : 1,
58 | "outlettype" : [ "" ],
59 | "patching_rect" : [ 1028.0, 152.0, 63.0, 22.0 ],
60 | "text" : "getlatency"
61 | }
62 |
63 | }
64 | , {
65 | "box" : {
66 | "id" : "obj-43",
67 | "maxclass" : "newobj",
68 | "numinlets" : 9,
69 | "numoutlets" : 1,
70 | "outlettype" : [ "signal" ],
71 | "patching_rect" : [ 154.75, 591.0, 103.0, 22.0 ],
72 | "text" : "selector~ 8 1"
73 | }
74 |
75 | }
76 | , {
77 | "box" : {
78 | "id" : "obj-44",
79 | "maxclass" : "newobj",
80 | "numinlets" : 1,
81 | "numoutlets" : 8,
82 | "outlettype" : [ "signal", "signal", "signal", "signal", "signal", "signal", "signal", "signal" ],
83 | "patching_rect" : [ 165.25, 545.0, 92.5, 22.0 ],
84 | "text" : "mc.unpack~ 8"
85 | }
86 |
87 | }
88 | , {
89 | "box" : {
90 | "id" : "obj-36",
91 | "maxclass" : "newobj",
92 | "numinlets" : 9,
93 | "numoutlets" : 1,
94 | "outlettype" : [ "signal" ],
95 | "patching_rect" : [ 678.5, 567.0, 103.0, 22.0 ],
96 | "text" : "selector~ 8 1"
97 | }
98 |
99 | }
100 | , {
101 | "box" : {
102 | "id" : "obj-38",
103 | "maxclass" : "message",
104 | "numinlets" : 2,
105 | "numoutlets" : 1,
106 | "outlettype" : [ "" ],
107 | "patching_rect" : [ 293.0, 172.0, 29.5, 22.0 ],
108 | "text" : "0"
109 | }
110 |
111 | }
112 | , {
113 | "box" : {
114 | "id" : "obj-37",
115 | "maxclass" : "attrui",
116 | "numinlets" : 1,
117 | "numoutlets" : 1,
118 | "outlettype" : [ "" ],
119 | "patching_rect" : [ 1051.0, 122.0, 150.0, 22.0 ]
120 | }
121 |
122 | }
123 | , {
124 | "box" : {
125 | "id" : "obj-39",
126 | "maxclass" : "toggle",
127 | "numinlets" : 1,
128 | "numoutlets" : 1,
129 | "outlettype" : [ "int" ],
130 | "parameter_enable" : 0,
131 | "patching_rect" : [ 339.0, 228.0, 24.0, 24.0 ]
132 | }
133 |
134 | }
135 | , {
136 | "box" : {
137 | "id" : "obj-35",
138 | "maxclass" : "newobj",
139 | "numinlets" : 2,
140 | "numoutlets" : 1,
141 | "outlettype" : [ "bang" ],
142 | "patching_rect" : [ 315.0, 265.0, 63.0, 22.0 ],
143 | "text" : "metro 600"
144 | }
145 |
146 | }
147 | , {
148 | "box" : {
149 | "id" : "obj-34",
150 | "maxclass" : "newobj",
151 | "numinlets" : 5,
152 | "numoutlets" : 4,
153 | "outlettype" : [ "int", "", "", "int" ],
154 | "patching_rect" : [ 297.0, 315.0, 69.0, 22.0 ],
155 | "text" : "counter 1 8"
156 | }
157 |
158 | }
159 | , {
160 | "box" : {
161 | "id" : "obj-33",
162 | "maxclass" : "newobj",
163 | "numinlets" : 2,
164 | "numoutlets" : 8,
165 | "outlettype" : [ "signal", "signal", "signal", "signal", "signal", "signal", "signal", "signal" ],
166 | "patching_rect" : [ 33.0, 275.0, 92.5, 22.0 ],
167 | "text" : "gate~ 8"
168 | }
169 |
170 | }
171 | , {
172 | "box" : {
173 | "id" : "obj-32",
174 | "maxclass" : "newobj",
175 | "numinlets" : 2,
176 | "numoutlets" : 1,
177 | "outlettype" : [ "signal" ],
178 | "patching_rect" : [ 354.0, 208.0, 66.0, 22.0 ],
179 | "text" : "cycle~ 100"
180 | }
181 |
182 | }
183 | , {
184 | "box" : {
185 | "id" : "obj-12",
186 | "maxclass" : "scope~",
187 | "numinlets" : 2,
188 | "numoutlets" : 0,
189 | "patching_rect" : [ 967.0, 347.0, 130.0, 130.0 ]
190 | }
191 |
192 | }
193 | , {
194 | "box" : {
195 | "attr" : "use_rtsp",
196 | "id" : "obj-30",
197 | "maxclass" : "attrui",
198 | "numinlets" : 1,
199 | "numoutlets" : 1,
200 | "outlettype" : [ "" ],
201 | "patching_rect" : [ 280.0, 495.0, 150.0, 22.0 ]
202 | }
203 |
204 | }
205 | , {
206 | "box" : {
207 | "attr" : "use_rtsp",
208 | "id" : "obj-29",
209 | "maxclass" : "attrui",
210 | "numinlets" : 1,
211 | "numoutlets" : 1,
212 | "outlettype" : [ "" ],
213 | "patching_rect" : [ 551.0, 24.0, 150.0, 22.0 ]
214 | }
215 |
216 | }
217 | , {
218 | "box" : {
219 | "attr" : "address",
220 | "id" : "obj-27",
221 | "maxclass" : "attrui",
222 | "numinlets" : 1,
223 | "numoutlets" : 1,
224 | "outlettype" : [ "" ],
225 | "patching_rect" : [ 787.0, 61.0, 219.0, 22.0 ]
226 | }
227 |
228 | }
229 | , {
230 | "box" : {
231 | "id" : "obj-31",
232 | "maxclass" : "newobj",
233 | "numinlets" : 1,
234 | "numoutlets" : 1,
235 | "outlettype" : [ "" ],
236 | "patching_rect" : [ 399.5, 61.0, 57.0, 22.0 ],
237 | "text" : "tosymbol"
238 | }
239 |
240 | }
241 | , {
242 | "box" : {
243 | "id" : "obj-28",
244 | "items" : [ "pcm_s16be", ",", "opus" ],
245 | "maxclass" : "umenu",
246 | "numinlets" : 1,
247 | "numoutlets" : 3,
248 | "outlettype" : [ "int", "", "" ],
249 | "parameter_enable" : 0,
250 | "patching_rect" : [ 345.0, 24.0, 128.0, 22.0 ]
251 | }
252 |
253 | }
254 | , {
255 | "box" : {
256 | "attr" : "codec",
257 | "id" : "obj-26",
258 | "maxclass" : "attrui",
259 | "numinlets" : 1,
260 | "numoutlets" : 1,
261 | "outlettype" : [ "" ],
262 | "patching_rect" : [ 97.0, 432.0, 150.0, 22.0 ]
263 | }
264 |
265 | }
266 | , {
267 | "box" : {
268 | "attr" : "codec",
269 | "id" : "obj-23",
270 | "maxclass" : "attrui",
271 | "numinlets" : 1,
272 | "numoutlets" : 1,
273 | "outlettype" : [ "" ],
274 | "patching_rect" : [ 430.0, 161.0, 150.0, 22.0 ]
275 | }
276 |
277 | }
278 | , {
279 | "box" : {
280 | "basictuning" : 440,
281 | "clipheight" : 29.0,
282 | "data" : {
283 | "clips" : [ {
284 | "absolutepath" : "cello-f2.aif",
285 | "filename" : "cello-f2.aif",
286 | "filekind" : "audiofile",
287 | "id" : "u581000496",
288 | "loop" : 1,
289 | "content_state" : {
290 | "play" : [ 0 ],
291 | "pitchcorrection" : [ 0 ],
292 | "originallengthms" : [ 0.0 ],
293 | "mode" : [ "basic" ],
294 | "basictuning" : [ 440 ],
295 | "timestretch" : [ 0 ],
296 | "followglobaltempo" : [ 0 ],
297 | "speed" : [ 1.0 ],
298 | "pitchshift" : [ 1.0 ],
299 | "originallength" : [ 0.0, "ticks" ],
300 | "formantcorrection" : [ 0 ],
301 | "formant" : [ 1.0 ],
302 | "originaltempo" : [ 120.0 ],
303 | "slurtime" : [ 0.0 ],
304 | "quality" : [ "basic" ],
305 | "loop" : 1
306 | }
307 |
308 | }
309 | ]
310 | }
311 | ,
312 | "followglobaltempo" : 0,
313 | "formantcorrection" : 0,
314 | "id" : "obj-25",
315 | "maxclass" : "playlist~",
316 | "mode" : "basic",
317 | "numinlets" : 1,
318 | "numoutlets" : 5,
319 | "originallength" : [ 0.0, "ticks" ],
320 | "originaltempo" : 120.0,
321 | "outlettype" : [ "signal", "signal", "signal", "", "dictionary" ],
322 | "parameter_enable" : 0,
323 | "patching_rect" : [ 255.0, 208.0, 63.0, 30.0 ],
324 | "pitchcorrection" : 0,
325 | "quality" : "basic",
326 | "timestretch" : [ 0 ]
327 | }
328 |
329 | }
330 | , {
331 | "box" : {
332 | "basictuning" : 440,
333 | "clipheight" : 29.0,
334 | "data" : {
335 | "clips" : [ {
336 | "absolutepath" : "eroica.aiff",
337 | "filename" : "eroica.aiff",
338 | "filekind" : "audiofile",
339 | "id" : "u981000501",
340 | "loop" : 1,
341 | "content_state" : {
342 | "play" : [ 0 ],
343 | "pitchcorrection" : [ 0 ],
344 | "originallengthms" : [ 0.0 ],
345 | "mode" : [ "basic" ],
346 | "basictuning" : [ 440 ],
347 | "timestretch" : [ 0 ],
348 | "followglobaltempo" : [ 0 ],
349 | "speed" : [ 1.0 ],
350 | "pitchshift" : [ 1.0 ],
351 | "originallength" : [ 0.0, "ticks" ],
352 | "formantcorrection" : [ 0 ],
353 | "formant" : [ 1.0 ],
354 | "originaltempo" : [ 120.0 ],
355 | "slurtime" : [ 0.0 ],
356 | "quality" : [ "basic" ],
357 | "loop" : 1
358 | }
359 |
360 | }
361 | ]
362 | }
363 | ,
364 | "followglobaltempo" : 0,
365 | "formantcorrection" : 0,
366 | "id" : "obj-16",
367 | "maxclass" : "playlist~",
368 | "mode" : "basic",
369 | "numinlets" : 1,
370 | "numoutlets" : 5,
371 | "originallength" : [ 0.0, "ticks" ],
372 | "originaltempo" : 120.0,
373 | "outlettype" : [ "signal", "signal", "signal", "", "dictionary" ],
374 | "parameter_enable" : 0,
375 | "patching_rect" : [ 187.0, 208.0, 49.0, 30.0 ],
376 | "pitchcorrection" : 0,
377 | "quality" : "basic",
378 | "timestretch" : [ 0 ]
379 | }
380 |
381 | }
382 | , {
383 | "box" : {
384 | "id" : "obj-24",
385 | "maxclass" : "message",
386 | "numinlets" : 2,
387 | "numoutlets" : 1,
388 | "outlettype" : [ "" ],
389 | "patching_rect" : [ 630.0, 208.0, 57.0, 22.0 ],
390 | "text" : "chans $1"
391 | }
392 |
393 | }
394 | , {
395 | "box" : {
396 | "id" : "obj-20",
397 | "maxclass" : "number",
398 | "numinlets" : 1,
399 | "numoutlets" : 2,
400 | "outlettype" : [ "", "bang" ],
401 | "parameter_enable" : 0,
402 | "patching_rect" : [ 731.0, 45.0, 50.0, 22.0 ]
403 | }
404 |
405 | }
406 | , {
407 | "box" : {
408 | "attr" : "channels",
409 | "id" : "obj-14",
410 | "maxclass" : "attrui",
411 | "numinlets" : 1,
412 | "numoutlets" : 1,
413 | "outlettype" : [ "" ],
414 | "patching_rect" : [ 796.0, 115.0, 150.0, 22.0 ]
415 | }
416 |
417 | }
418 | , {
419 | "box" : {
420 | "id" : "obj-11",
421 | "maxclass" : "newobj",
422 | "numinlets" : 1,
423 | "numoutlets" : 8,
424 | "outlettype" : [ "signal", "signal", "signal", "signal", "signal", "signal", "signal", "signal" ],
425 | "patching_rect" : [ 689.0, 521.0, 92.5, 22.0 ],
426 | "text" : "mc.unpack~ 8"
427 | }
428 |
429 | }
430 | , {
431 | "box" : {
432 | "id" : "obj-1",
433 | "maxclass" : "newobj",
434 | "numinlets" : 2,
435 | "numoutlets" : 0,
436 | "patching_rect" : [ 231.0, 647.0, 35.0, 22.0 ],
437 | "text" : "dac~"
438 | }
439 |
440 | }
441 | , {
442 | "box" : {
443 | "id" : "obj-9",
444 | "lastchannelcount" : 8,
445 | "maxclass" : "mc.live.gain~",
446 | "numinlets" : 1,
447 | "numoutlets" : 4,
448 | "outlettype" : [ "multichannelsignal", "", "float", "list" ],
449 | "parameter_enable" : 1,
450 | "patching_rect" : [ 33.0, 375.0, 48.0, 136.0 ],
451 | "saved_attribute_attributes" : {
452 | "valueof" : {
453 | "parameter_longname" : "mc.live.gain~[1]",
454 | "parameter_mmax" : 6.0,
455 | "parameter_mmin" : -70.0,
456 | "parameter_shortname" : "mc.live.gain~",
457 | "parameter_type" : 0,
458 | "parameter_unitstyle" : 4
459 | }
460 |
461 | }
462 | ,
463 | "varname" : "mc.live.gain~[1]"
464 | }
465 |
466 | }
467 | , {
468 | "box" : {
469 | "id" : "obj-5",
470 | "maxclass" : "newobj",
471 | "numinlets" : 1,
472 | "numoutlets" : 2,
473 | "outlettype" : [ "int", "int" ],
474 | "patching_rect" : [ 136.0, 101.0, 29.5, 22.0 ],
475 | "text" : "t i i"
476 | }
477 |
478 | }
479 | , {
480 | "box" : {
481 | "id" : "obj-2",
482 | "maxclass" : "toggle",
483 | "numinlets" : 1,
484 | "numoutlets" : 1,
485 | "outlettype" : [ "int" ],
486 | "parameter_enable" : 0,
487 | "patching_rect" : [ 686.0, 145.0, 24.0, 24.0 ]
488 | }
489 |
490 | }
491 | , {
492 | "box" : {
493 | "id" : "obj-22",
494 | "maxclass" : "newobj",
495 | "numinlets" : 1,
496 | "numoutlets" : 4,
497 | "outlettype" : [ "int", "float", "int", "int" ],
498 | "patching_rect" : [ 136.0, 55.0, 61.0, 22.0 ],
499 | "text" : "dspstate~"
500 | }
501 |
502 | }
503 | , {
504 | "box" : {
505 | "id" : "obj-21",
506 | "maxclass" : "toggle",
507 | "numinlets" : 1,
508 | "numoutlets" : 1,
509 | "outlettype" : [ "int" ],
510 | "parameter_enable" : 0,
511 | "patching_rect" : [ 136.0, 165.0, 24.0, 24.0 ]
512 | }
513 |
514 | }
515 | , {
516 | "box" : {
517 | "id" : "obj-19",
518 | "maxclass" : "newobj",
519 | "numinlets" : 8,
520 | "numoutlets" : 1,
521 | "outlettype" : [ "multichannelsignal" ],
522 | "patching_rect" : [ 33.0, 309.0, 92.5, 22.0 ],
523 | "text" : "mc.pack~ 8"
524 | }
525 |
526 | }
527 | , {
528 | "box" : {
529 | "id" : "obj-18",
530 | "maxclass" : "newobj",
531 | "numinlets" : 1,
532 | "numoutlets" : 1,
533 | "outlettype" : [ "bang" ],
534 | "patching_rect" : [ 33.0, 120.0, 58.0, 22.0 ],
535 | "text" : "loadbang"
536 | }
537 |
538 | }
539 | , {
540 | "box" : {
541 | "id" : "obj-17",
542 | "maxclass" : "message",
543 | "numinlets" : 2,
544 | "numoutlets" : 1,
545 | "outlettype" : [ "" ],
546 | "patching_rect" : [ 33.0, 171.0, 51.0, 22.0 ],
547 | "text" : "loop 1 1"
548 | }
549 |
550 | }
551 | , {
552 | "box" : {
553 | "basictuning" : 440,
554 | "clipheight" : 29.0,
555 | "data" : {
556 | "clips" : [ {
557 | "absolutepath" : "cherokee.aif",
558 | "filename" : "cherokee.aif",
559 | "filekind" : "audiofile",
560 | "id" : "u208000513",
561 | "loop" : 1,
562 | "content_state" : {
563 | "play" : [ 0 ],
564 | "pitchcorrection" : [ 0 ],
565 | "originallengthms" : [ 0.0 ],
566 | "mode" : [ "basic" ],
567 | "basictuning" : [ 440 ],
568 | "timestretch" : [ 0 ],
569 | "followglobaltempo" : [ 0 ],
570 | "speed" : [ 1.0 ],
571 | "pitchshift" : [ 1.0 ],
572 | "originallength" : [ 0.0, "ticks" ],
573 | "formantcorrection" : [ 0 ],
574 | "formant" : [ 1.0 ],
575 | "originaltempo" : [ 120.0 ],
576 | "slurtime" : [ 0.0 ],
577 | "quality" : [ "basic" ],
578 | "loop" : 1
579 | }
580 |
581 | }
582 | ]
583 | }
584 | ,
585 | "followglobaltempo" : 0,
586 | "formantcorrection" : 0,
587 | "id" : "obj-15",
588 | "maxclass" : "playlist~",
589 | "mode" : "basic",
590 | "numinlets" : 1,
591 | "numoutlets" : 5,
592 | "originallength" : [ 0.0, "ticks" ],
593 | "originaltempo" : 120.0,
594 | "outlettype" : [ "signal", "signal", "signal", "", "dictionary" ],
595 | "parameter_enable" : 0,
596 | "patching_rect" : [ 116.5, 208.0, 61.0, 30.0 ],
597 | "pitchcorrection" : 0,
598 | "quality" : "basic",
599 | "timestretch" : [ 0 ]
600 | }
601 |
602 | }
603 | , {
604 | "box" : {
605 | "basictuning" : 440,
606 | "data" : {
607 | "clips" : [ {
608 | "absolutepath" : "drumLoop.aif",
609 | "filename" : "drumLoop.aif",
610 | "filekind" : "audiofile",
611 | "id" : "u300000518",
612 | "loop" : 1,
613 | "content_state" : {
614 | "play" : [ 0 ],
615 | "pitchcorrection" : [ 0 ],
616 | "originallengthms" : [ 0.0 ],
617 | "mode" : [ "basic" ],
618 | "basictuning" : [ 440 ],
619 | "timestretch" : [ 0 ],
620 | "followglobaltempo" : [ 0 ],
621 | "speed" : [ 1.0 ],
622 | "pitchshift" : [ 1.0 ],
623 | "originallength" : [ 0.0, "ticks" ],
624 | "formantcorrection" : [ 0 ],
625 | "formant" : [ 1.0 ],
626 | "originaltempo" : [ 120.0 ],
627 | "slurtime" : [ 0.0 ],
628 | "quality" : [ "basic" ],
629 | "loop" : 1
630 | }
631 |
632 | }
633 | ]
634 | }
635 | ,
636 | "followglobaltempo" : 0,
637 | "formantcorrection" : 0,
638 | "id" : "obj-13",
639 | "maxclass" : "playlist~",
640 | "mode" : "basic",
641 | "numinlets" : 1,
642 | "numoutlets" : 5,
643 | "originallength" : [ 0.0, "ticks" ],
644 | "originaltempo" : 120.0,
645 | "outlettype" : [ "signal", "signal", "signal", "", "dictionary" ],
646 | "parameter_enable" : 0,
647 | "patching_rect" : [ 33.0, 208.0, 70.0, 30.0 ],
648 | "pitchcorrection" : 0,
649 | "quality" : "basic",
650 | "timestretch" : [ 0 ]
651 | }
652 |
653 | }
654 | , {
655 | "box" : {
656 | "id" : "obj-10",
657 | "maxclass" : "message",
658 | "numinlets" : 2,
659 | "numoutlets" : 1,
660 | "outlettype" : [ "" ],
661 | "patching_rect" : [ 761.0, 347.0, 50.0, 22.0 ]
662 | }
663 |
664 | }
665 | , {
666 | "box" : {
667 | "id" : "obj-8",
668 | "maxclass" : "newobj",
669 | "numinlets" : 1,
670 | "numoutlets" : 1,
671 | "outlettype" : [ "int" ],
672 | "patching_rect" : [ 753.0, 275.0, 106.0, 22.0 ],
673 | "text" : "mc.channelcount~"
674 | }
675 |
676 | }
677 | , {
678 | "box" : {
679 | "id" : "obj-7",
680 | "lastchannelcount" : 2,
681 | "maxclass" : "mc.live.gain~",
682 | "numinlets" : 1,
683 | "numoutlets" : 4,
684 | "outlettype" : [ "multichannelsignal", "", "float", "list" ],
685 | "parameter_enable" : 1,
686 | "patching_rect" : [ 689.0, 275.0, 48.0, 136.0 ],
687 | "saved_attribute_attributes" : {
688 | "valueof" : {
689 | "parameter_longname" : "mc.live.gain~",
690 | "parameter_mmax" : 6.0,
691 | "parameter_mmin" : -70.0,
692 | "parameter_shortname" : "mc.live.gain~",
693 | "parameter_type" : 0,
694 | "parameter_unitstyle" : 4
695 | }
696 |
697 | }
698 | ,
699 | "varname" : "mc.live.gain~"
700 | }
701 |
702 | }
703 | , {
704 | "box" : {
705 | "attr" : "channels",
706 | "id" : "obj-6",
707 | "maxclass" : "attrui",
708 | "numinlets" : 1,
709 | "numoutlets" : 1,
710 | "outlettype" : [ "" ],
711 | "patching_rect" : [ 544.0, 422.0, 150.0, 22.0 ]
712 | }
713 |
714 | }
715 | , {
716 | "box" : {
717 | "id" : "obj-4",
718 | "maxclass" : "newobj",
719 | "numinlets" : 1,
720 | "numoutlets" : 2,
721 | "outlettype" : [ "multichannelsignal", "" ],
722 | "patching_rect" : [ 689.0, 189.0, 346.0, 22.0 ],
723 | "text" : "mc.rtpreceive~ @address 127.0.0.1 @channels 2 @port 30000"
724 | }
725 |
726 | }
727 | , {
728 | "box" : {
729 | "id" : "obj-3",
730 | "maxclass" : "newobj",
731 | "numinlets" : 1,
732 | "numoutlets" : 0,
733 | "patching_rect" : [ 33.0, 746.0, 333.0, 22.0 ],
734 | "text" : "mc.rtpsend~ @channels 2 @address 127.0.0.1 @port 30000"
735 | }
736 |
737 | }
738 | ],
739 | "lines" : [ {
740 | "patchline" : {
741 | "destination" : [ "obj-36", 8 ],
742 | "source" : [ "obj-11", 7 ]
743 | }
744 |
745 | }
746 | , {
747 | "patchline" : {
748 | "destination" : [ "obj-36", 7 ],
749 | "source" : [ "obj-11", 6 ]
750 | }
751 |
752 | }
753 | , {
754 | "patchline" : {
755 | "destination" : [ "obj-36", 6 ],
756 | "source" : [ "obj-11", 5 ]
757 | }
758 |
759 | }
760 | , {
761 | "patchline" : {
762 | "destination" : [ "obj-36", 5 ],
763 | "source" : [ "obj-11", 4 ]
764 | }
765 |
766 | }
767 | , {
768 | "patchline" : {
769 | "destination" : [ "obj-36", 4 ],
770 | "source" : [ "obj-11", 3 ]
771 | }
772 |
773 | }
774 | , {
775 | "patchline" : {
776 | "destination" : [ "obj-36", 3 ],
777 | "source" : [ "obj-11", 2 ]
778 | }
779 |
780 | }
781 | , {
782 | "patchline" : {
783 | "destination" : [ "obj-36", 2 ],
784 | "source" : [ "obj-11", 1 ]
785 | }
786 |
787 | }
788 | , {
789 | "patchline" : {
790 | "destination" : [ "obj-36", 1 ],
791 | "source" : [ "obj-11", 0 ]
792 | }
793 |
794 | }
795 | , {
796 | "patchline" : {
797 | "destination" : [ "obj-33", 1 ],
798 | "source" : [ "obj-13", 0 ]
799 | }
800 |
801 | }
802 | , {
803 | "patchline" : {
804 | "destination" : [ "obj-4", 0 ],
805 | "source" : [ "obj-14", 0 ]
806 | }
807 |
808 | }
809 | , {
810 | "patchline" : {
811 | "destination" : [ "obj-13", 0 ],
812 | "order" : 3,
813 | "source" : [ "obj-17", 0 ]
814 | }
815 |
816 | }
817 | , {
818 | "patchline" : {
819 | "destination" : [ "obj-15", 0 ],
820 | "order" : 2,
821 | "source" : [ "obj-17", 0 ]
822 | }
823 |
824 | }
825 | , {
826 | "patchline" : {
827 | "destination" : [ "obj-16", 0 ],
828 | "order" : 1,
829 | "source" : [ "obj-17", 0 ]
830 | }
831 |
832 | }
833 | , {
834 | "patchline" : {
835 | "destination" : [ "obj-25", 0 ],
836 | "order" : 0,
837 | "source" : [ "obj-17", 0 ]
838 | }
839 |
840 | }
841 | , {
842 | "patchline" : {
843 | "destination" : [ "obj-17", 0 ],
844 | "source" : [ "obj-18", 0 ]
845 | }
846 |
847 | }
848 | , {
849 | "patchline" : {
850 | "destination" : [ "obj-9", 0 ],
851 | "source" : [ "obj-19", 0 ]
852 | }
853 |
854 | }
855 | , {
856 | "patchline" : {
857 | "destination" : [ "obj-4", 0 ],
858 | "source" : [ "obj-2", 0 ]
859 | }
860 |
861 | }
862 | , {
863 | "patchline" : {
864 | "destination" : [ "obj-14", 0 ],
865 | "order" : 0,
866 | "source" : [ "obj-20", 0 ]
867 | }
868 |
869 | }
870 | , {
871 | "patchline" : {
872 | "destination" : [ "obj-24", 0 ],
873 | "order" : 1,
874 | "source" : [ "obj-20", 0 ]
875 | }
876 |
877 | }
878 | , {
879 | "patchline" : {
880 | "destination" : [ "obj-6", 0 ],
881 | "order" : 2,
882 | "source" : [ "obj-20", 0 ]
883 | }
884 |
885 | }
886 | , {
887 | "patchline" : {
888 | "destination" : [ "obj-13", 0 ],
889 | "order" : 3,
890 | "source" : [ "obj-21", 0 ]
891 | }
892 |
893 | }
894 | , {
895 | "patchline" : {
896 | "destination" : [ "obj-15", 0 ],
897 | "order" : 2,
898 | "source" : [ "obj-21", 0 ]
899 | }
900 |
901 | }
902 | , {
903 | "patchline" : {
904 | "destination" : [ "obj-16", 0 ],
905 | "order" : 1,
906 | "source" : [ "obj-21", 0 ]
907 | }
908 |
909 | }
910 | , {
911 | "patchline" : {
912 | "destination" : [ "obj-25", 0 ],
913 | "order" : 0,
914 | "source" : [ "obj-21", 0 ]
915 | }
916 |
917 | }
918 | , {
919 | "patchline" : {
920 | "destination" : [ "obj-5", 0 ],
921 | "source" : [ "obj-22", 0 ]
922 | }
923 |
924 | }
925 | , {
926 | "patchline" : {
927 | "destination" : [ "obj-4", 0 ],
928 | "source" : [ "obj-23", 0 ]
929 | }
930 |
931 | }
932 | , {
933 | "patchline" : {
934 | "destination" : [ "obj-19", 0 ],
935 | "source" : [ "obj-24", 0 ]
936 | }
937 |
938 | }
939 | , {
940 | "patchline" : {
941 | "destination" : [ "obj-3", 0 ],
942 | "source" : [ "obj-26", 0 ]
943 | }
944 |
945 | }
946 | , {
947 | "patchline" : {
948 | "destination" : [ "obj-4", 0 ],
949 | "source" : [ "obj-27", 0 ]
950 | }
951 |
952 | }
953 | , {
954 | "patchline" : {
955 | "destination" : [ "obj-31", 0 ],
956 | "source" : [ "obj-28", 1 ]
957 | }
958 |
959 | }
960 | , {
961 | "patchline" : {
962 | "destination" : [ "obj-4", 0 ],
963 | "source" : [ "obj-29", 0 ]
964 | }
965 |
966 | }
967 | , {
968 | "patchline" : {
969 | "destination" : [ "obj-3", 0 ],
970 | "source" : [ "obj-30", 0 ]
971 | }
972 |
973 | }
974 | , {
975 | "patchline" : {
976 | "destination" : [ "obj-23", 0 ],
977 | "order" : 0,
978 | "source" : [ "obj-31", 0 ]
979 | }
980 |
981 | }
982 | , {
983 | "patchline" : {
984 | "destination" : [ "obj-26", 0 ],
985 | "midpoints" : [ 409.0, 417.0, 106.5, 417.0 ],
986 | "order" : 1,
987 | "source" : [ "obj-31", 0 ]
988 | }
989 |
990 | }
991 | , {
992 | "patchline" : {
993 | "destination" : [ "obj-19", 7 ],
994 | "source" : [ "obj-33", 7 ]
995 | }
996 |
997 | }
998 | , {
999 | "patchline" : {
1000 | "destination" : [ "obj-19", 6 ],
1001 | "source" : [ "obj-33", 6 ]
1002 | }
1003 |
1004 | }
1005 | , {
1006 | "patchline" : {
1007 | "destination" : [ "obj-19", 5 ],
1008 | "source" : [ "obj-33", 5 ]
1009 | }
1010 |
1011 | }
1012 | , {
1013 | "patchline" : {
1014 | "destination" : [ "obj-19", 4 ],
1015 | "source" : [ "obj-33", 4 ]
1016 | }
1017 |
1018 | }
1019 | , {
1020 | "patchline" : {
1021 | "destination" : [ "obj-19", 3 ],
1022 | "source" : [ "obj-33", 3 ]
1023 | }
1024 |
1025 | }
1026 | , {
1027 | "patchline" : {
1028 | "destination" : [ "obj-19", 2 ],
1029 | "source" : [ "obj-33", 2 ]
1030 | }
1031 |
1032 | }
1033 | , {
1034 | "patchline" : {
1035 | "destination" : [ "obj-19", 1 ],
1036 | "source" : [ "obj-33", 1 ]
1037 | }
1038 |
1039 | }
1040 | , {
1041 | "patchline" : {
1042 | "destination" : [ "obj-19", 0 ],
1043 | "source" : [ "obj-33", 0 ]
1044 | }
1045 |
1046 | }
1047 | , {
1048 | "patchline" : {
1049 | "destination" : [ "obj-33", 0 ],
1050 | "order" : 2,
1051 | "source" : [ "obj-34", 0 ]
1052 | }
1053 |
1054 | }
1055 | , {
1056 | "patchline" : {
1057 | "destination" : [ "obj-36", 0 ],
1058 | "order" : 0,
1059 | "source" : [ "obj-34", 0 ]
1060 | }
1061 |
1062 | }
1063 | , {
1064 | "patchline" : {
1065 | "destination" : [ "obj-43", 0 ],
1066 | "order" : 1,
1067 | "source" : [ "obj-34", 0 ]
1068 | }
1069 |
1070 | }
1071 | , {
1072 | "patchline" : {
1073 | "destination" : [ "obj-34", 0 ],
1074 | "source" : [ "obj-35", 0 ]
1075 | }
1076 |
1077 | }
1078 | , {
1079 | "patchline" : {
1080 | "destination" : [ "obj-1", 1 ],
1081 | "source" : [ "obj-36", 0 ]
1082 | }
1083 |
1084 | }
1085 | , {
1086 | "patchline" : {
1087 | "destination" : [ "obj-4", 0 ],
1088 | "source" : [ "obj-37", 0 ]
1089 | }
1090 |
1091 | }
1092 | , {
1093 | "patchline" : {
1094 | "destination" : [ "obj-33", 0 ],
1095 | "source" : [ "obj-38", 0 ]
1096 | }
1097 |
1098 | }
1099 | , {
1100 | "patchline" : {
1101 | "destination" : [ "obj-35", 0 ],
1102 | "source" : [ "obj-39", 0 ]
1103 | }
1104 |
1105 | }
1106 | , {
1107 | "patchline" : {
1108 | "destination" : [ "obj-12", 0 ],
1109 | "order" : 0,
1110 | "source" : [ "obj-4", 0 ]
1111 | }
1112 |
1113 | }
1114 | , {
1115 | "patchline" : {
1116 | "destination" : [ "obj-46", 1 ],
1117 | "source" : [ "obj-4", 1 ]
1118 | }
1119 |
1120 | }
1121 | , {
1122 | "patchline" : {
1123 | "destination" : [ "obj-7", 0 ],
1124 | "order" : 2,
1125 | "source" : [ "obj-4", 0 ]
1126 | }
1127 |
1128 | }
1129 | , {
1130 | "patchline" : {
1131 | "destination" : [ "obj-8", 0 ],
1132 | "order" : 1,
1133 | "source" : [ "obj-4", 0 ]
1134 | }
1135 |
1136 | }
1137 | , {
1138 | "patchline" : {
1139 | "destination" : [ "obj-4", 0 ],
1140 | "source" : [ "obj-41", 0 ]
1141 | }
1142 |
1143 | }
1144 | , {
1145 | "patchline" : {
1146 | "destination" : [ "obj-1", 0 ],
1147 | "source" : [ "obj-43", 0 ]
1148 | }
1149 |
1150 | }
1151 | , {
1152 | "patchline" : {
1153 | "destination" : [ "obj-43", 8 ],
1154 | "source" : [ "obj-44", 7 ]
1155 | }
1156 |
1157 | }
1158 | , {
1159 | "patchline" : {
1160 | "destination" : [ "obj-43", 7 ],
1161 | "source" : [ "obj-44", 6 ]
1162 | }
1163 |
1164 | }
1165 | , {
1166 | "patchline" : {
1167 | "destination" : [ "obj-43", 6 ],
1168 | "source" : [ "obj-44", 5 ]
1169 | }
1170 |
1171 | }
1172 | , {
1173 | "patchline" : {
1174 | "destination" : [ "obj-43", 5 ],
1175 | "source" : [ "obj-44", 4 ]
1176 | }
1177 |
1178 | }
1179 | , {
1180 | "patchline" : {
1181 | "destination" : [ "obj-43", 4 ],
1182 | "source" : [ "obj-44", 3 ]
1183 | }
1184 |
1185 | }
1186 | , {
1187 | "patchline" : {
1188 | "destination" : [ "obj-43", 3 ],
1189 | "source" : [ "obj-44", 2 ]
1190 | }
1191 |
1192 | }
1193 | , {
1194 | "patchline" : {
1195 | "destination" : [ "obj-43", 2 ],
1196 | "source" : [ "obj-44", 1 ]
1197 | }
1198 |
1199 | }
1200 | , {
1201 | "patchline" : {
1202 | "destination" : [ "obj-43", 1 ],
1203 | "source" : [ "obj-44", 0 ]
1204 | }
1205 |
1206 | }
1207 | , {
1208 | "patchline" : {
1209 | "destination" : [ "obj-2", 0 ],
1210 | "source" : [ "obj-5", 1 ]
1211 | }
1212 |
1213 | }
1214 | , {
1215 | "patchline" : {
1216 | "destination" : [ "obj-21", 0 ],
1217 | "source" : [ "obj-5", 0 ]
1218 | }
1219 |
1220 | }
1221 | , {
1222 | "patchline" : {
1223 | "destination" : [ "obj-3", 0 ],
1224 | "source" : [ "obj-6", 0 ]
1225 | }
1226 |
1227 | }
1228 | , {
1229 | "patchline" : {
1230 | "destination" : [ "obj-11", 0 ],
1231 | "source" : [ "obj-7", 0 ]
1232 | }
1233 |
1234 | }
1235 | , {
1236 | "patchline" : {
1237 | "destination" : [ "obj-10", 1 ],
1238 | "source" : [ "obj-8", 0 ]
1239 | }
1240 |
1241 | }
1242 | , {
1243 | "patchline" : {
1244 | "destination" : [ "obj-3", 0 ],
1245 | "order" : 1,
1246 | "source" : [ "obj-9", 0 ]
1247 | }
1248 |
1249 | }
1250 | , {
1251 | "patchline" : {
1252 | "destination" : [ "obj-44", 0 ],
1253 | "order" : 0,
1254 | "source" : [ "obj-9", 0 ]
1255 | }
1256 |
1257 | }
1258 | ],
1259 | "parameters" : {
1260 | "obj-7" : [ "mc.live.gain~", "mc.live.gain~", 0 ],
1261 | "obj-9" : [ "mc.live.gain~[1]", "mc.live.gain~", 0 ],
1262 | "parameterbanks" : {
1263 |
1264 | }
1265 | ,
1266 | "inherited_shortname" : 1
1267 | }
1268 | ,
1269 | "dependency_cache" : [ {
1270 | "name" : "drumLoop.aif",
1271 | "bootpath" : "C74:/media/msp",
1272 | "type" : "AIFF",
1273 | "implicit" : 1
1274 | }
1275 | , {
1276 | "name" : "cherokee.aif",
1277 | "bootpath" : "C74:/media/msp",
1278 | "type" : "AIFF",
1279 | "implicit" : 1
1280 | }
1281 | , {
1282 | "name" : "eroica.aiff",
1283 | "bootpath" : "C74:/docs/tutorial-patchers/msp-tut",
1284 | "type" : "AIFF",
1285 | "implicit" : 1
1286 | }
1287 | , {
1288 | "name" : "cello-f2.aif",
1289 | "bootpath" : "C74:/media/msp",
1290 | "type" : "AIFF",
1291 | "implicit" : 1
1292 | }
1293 | , {
1294 | "name" : "mc.rtpsend~.mxo",
1295 | "type" : "iLaX"
1296 | }
1297 | , {
1298 | "name" : "mc.rtpreceive~.mxo",
1299 | "type" : "iLaX"
1300 | }
1301 | ],
1302 | "autosave" : 0
1303 | }
1304 |
1305 | }
1306 |
--------------------------------------------------------------------------------
/test_patch/sender_test.maxpat:
--------------------------------------------------------------------------------
1 | {
2 | "patcher" : {
3 | "fileversion" : 1,
4 | "appversion" : {
5 | "major" : 8,
6 | "minor" : 0,
7 | "revision" : 5,
8 | "architecture" : "x64",
9 | "modernui" : 1
10 | }
11 | ,
12 | "classnamespace" : "box",
13 | "rect" : [ 161.0, 165.0, 805.0, 629.0 ],
14 | "bglocked" : 0,
15 | "openinpresentation" : 0,
16 | "default_fontsize" : 12.0,
17 | "default_fontface" : 0,
18 | "default_fontname" : "Arial",
19 | "gridonopen" : 1,
20 | "gridsize" : [ 15.0, 15.0 ],
21 | "gridsnaponopen" : 1,
22 | "objectsnaponopen" : 1,
23 | "statusbarvisible" : 2,
24 | "toolbarvisible" : 1,
25 | "lefttoolbarpinned" : 0,
26 | "toptoolbarpinned" : 0,
27 | "righttoolbarpinned" : 0,
28 | "bottomtoolbarpinned" : 0,
29 | "toolbars_unpinned_last_save" : 0,
30 | "tallnewobj" : 0,
31 | "boxanimatetime" : 200,
32 | "enablehscroll" : 1,
33 | "enablevscroll" : 1,
34 | "devicewidth" : 0.0,
35 | "description" : "",
36 | "digest" : "",
37 | "tags" : "",
38 | "style" : "",
39 | "subpatcher_template" : "",
40 | "boxes" : [ {
41 | "box" : {
42 | "id" : "obj-2",
43 | "maxclass" : "newobj",
44 | "numinlets" : 1,
45 | "numoutlets" : 1,
46 | "outlettype" : [ "" ],
47 | "patching_rect" : [ 169.0, 208.0, 100.0, 22.0 ]
48 | }
49 |
50 | }
51 | , {
52 | "box" : {
53 | "data" : {
54 | "clips" : [ {
55 | "absolutepath" : "drumLoop.aif",
56 | "filename" : "drumLoop.aif",
57 | "filekind" : "audiofile",
58 | "loop" : 0,
59 | "content_state" : {
60 | "originaltempo" : [ 120.0 ],
61 | "pitchshift" : [ 1.0 ],
62 | "quality" : [ "basic" ],
63 | "mode" : [ "basic" ],
64 | "originallength" : [ 0.0, "ticks" ],
65 | "pitchcorrection" : [ 0 ],
66 | "originallengthms" : [ 0.0 ],
67 | "basictuning" : [ 440 ],
68 | "timestretch" : [ 0 ],
69 | "formant" : [ 1.0 ],
70 | "followglobaltempo" : [ 0 ],
71 | "formantcorrection" : [ 0 ],
72 | "play" : [ 0 ],
73 | "slurtime" : [ 0.0 ],
74 | "speed" : [ 1.0 ]
75 | }
76 |
77 | }
78 | ]
79 | }
80 | ,
81 | "id" : "obj-6",
82 | "maxclass" : "playlist~",
83 | "numinlets" : 1,
84 | "numoutlets" : 5,
85 | "outlettype" : [ "signal", "signal", "signal", "", "dictionary" ],
86 | "patching_rect" : [ 69.0, 79.0, 150.0, 30.0 ]
87 | }
88 |
89 | }
90 | , {
91 | "box" : {
92 | "id" : "obj-5",
93 | "lastchannelcount" : 4,
94 | "maxclass" : "mc.live.gain~",
95 | "numinlets" : 1,
96 | "numoutlets" : 4,
97 | "outlettype" : [ "multichannelsignal", "", "float", "list" ],
98 | "parameter_enable" : 1,
99 | "patching_rect" : [ 69.0, 290.0, 48.0, 136.0 ],
100 | "saved_attribute_attributes" : {
101 | "valueof" : {
102 | "parameter_mmax" : 6.0,
103 | "parameter_shortname" : "mc.live.gain~",
104 | "parameter_type" : 0,
105 | "parameter_unitstyle" : 4,
106 | "parameter_mmin" : -70.0,
107 | "parameter_longname" : "mc.live.gain~"
108 | }
109 |
110 | }
111 | ,
112 | "varname" : "mc.live.gain~"
113 | }
114 |
115 | }
116 | , {
117 | "box" : {
118 | "id" : "obj-4",
119 | "maxclass" : "newobj",
120 | "numinlets" : 4,
121 | "numoutlets" : 1,
122 | "outlettype" : [ "multichannelsignal" ],
123 | "patching_rect" : [ 69.0, 202.0, 70.0, 22.0 ],
124 | "text" : "mc.pack~ 4"
125 | }
126 |
127 | }
128 | , {
129 | "box" : {
130 | "id" : "obj-3",
131 | "maxclass" : "attrui",
132 | "numinlets" : 1,
133 | "numoutlets" : 1,
134 | "outlettype" : [ "" ],
135 | "patching_rect" : [ 296.0, 277.0, 150.0, 22.0 ]
136 | }
137 |
138 | }
139 | , {
140 | "box" : {
141 | "id" : "obj-1",
142 | "maxclass" : "newobj",
143 | "numinlets" : 1,
144 | "numoutlets" : 0,
145 | "patching_rect" : [ 69.0, 476.0, 359.0, 22.0 ],
146 | "text" : "mc.rtpsend~.mxo @channels 2 @address 127.0.0.1 @port 30000"
147 | }
148 |
149 | }
150 | ],
151 | "lines" : [ {
152 | "patchline" : {
153 | "destination" : [ "obj-1", 0 ],
154 | "source" : [ "obj-3", 0 ]
155 | }
156 |
157 | }
158 | , {
159 | "patchline" : {
160 | "destination" : [ "obj-5", 0 ],
161 | "source" : [ "obj-4", 0 ]
162 | }
163 |
164 | }
165 | , {
166 | "patchline" : {
167 | "destination" : [ "obj-1", 0 ],
168 | "source" : [ "obj-5", 0 ]
169 | }
170 |
171 | }
172 | , {
173 | "patchline" : {
174 | "destination" : [ "obj-4", 0 ],
175 | "source" : [ "obj-6", 0 ]
176 | }
177 |
178 | }
179 | ],
180 | "parameters" : {
181 | "obj-5" : [ "mc.live.gain~", "mc.live.gain~", 0 ],
182 | "parameterbanks" : {
183 |
184 | }
185 |
186 | }
187 | ,
188 | "dependency_cache" : [ {
189 | "name" : "drumLoop.aif",
190 | "bootpath" : "C74:/media/msp",
191 | "type" : "AIFF",
192 | "implicit" : 1
193 | }
194 | , {
195 | "name" : "mc.rtpsend~.mxo",
196 | "type" : "iLaX"
197 | }
198 | ],
199 | "autosave" : 0
200 | }
201 |
202 | }
203 |
--------------------------------------------------------------------------------
/vcpkg-helper/x64-osx-rel.cmake:
--------------------------------------------------------------------------------
1 | set(VCPKG_TARGET_ARCHITECTURE x64)
2 | set(VCPKG_CRT_LINKAGE dynamic)
3 | set(VCPKG_LIBRARY_LINKAGE static)
4 |
5 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin)
6 | set(VCPKG_BUILD_TYPE release)
7 |
--------------------------------------------------------------------------------
/vcpkg-helper/x64-windows-rel.cmake:
--------------------------------------------------------------------------------
1 | set(VCPKG_TARGET_ARCHITECTURE x64)
2 | set(VCPKG_CRT_LINKAGE dynamic)
3 | set(VCPKG_LIBRARY_LINKAGE dynamic)
4 | set(VCPKG_BUILD_TYPE release)
5 |
6 |
--------------------------------------------------------------------------------
/vcpkg-helper/x64-windows-static-md.cmake:
--------------------------------------------------------------------------------
1 | set(VCPKG_TARGET_ARCHITECTURE x64)
2 | set(VCPKG_CRT_LINKAGE dynamic)
3 | set(VCPKG_LIBRARY_LINKAGE static)
4 | set(VCPKG_BUILD_TYPE release)
5 |
6 |
--------------------------------------------------------------------------------
/vcpkg-helper/x64-windows-static-rel.cmake:
--------------------------------------------------------------------------------
1 | set(VCPKG_TARGET_ARCHITECTURE x64)
2 | set(VCPKG_CRT_LINKAGE static)
3 | set(VCPKG_LIBRARY_LINKAGE static)
4 | set(VCPKG_BUILD_TYPE release)
5 |
6 |
--------------------------------------------------------------------------------