├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE.txt ├── Linux └── audiorecorder2 ├── README.md ├── audiorecorder ├── audiorecorder.1 ├── audiorecorder2-dev ├── README.md ├── audiorecorder2-osx.tgz └── audiorecorder2 │ ├── Resources │ ├── audiorecorder_large.png │ ├── audiorecorder_play_large.gif │ ├── audiorecorder_play_small.gif │ ├── audiorecorder_small_1.png │ ├── audiorecorder_small_2.png │ └── audiorecorder_small_3.png │ └── audiorecorder2.rb ├── current_interface.gif ├── history.md ├── macOS └── audiorecorder2-osx.tgz └── numbered_interface.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.shy 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at weevz@uw.edu. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) Andrew Weaver, 2017 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Linux/audiorecorder2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/Linux/audiorecorder2 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # audiorecorder 2 | 3 | #### A free tool for the calibration and recording of analog audio signals 4 | 5 | ## Development information: 6 | For updates and history of development please see [this page](https://github.com/amiaopensource/audiorecorder/blob/master/history.md). 7 | 8 | ## Installation: 9 | 10 | __Audiorecorder2 Note:__ Audiorecorder2 can now be installed along side audiorecorder by adding the flag `--with-audiorecorder2` to the following homebrew install commands. 11 | 12 | audiorecorder can be installed via [Homebrew](https://brew.sh/) on macOS and Linux. 13 | 14 | __MacOS Installation__: 15 | Audiorecorder can be installed via Homebrew with the following commands: 16 | 17 | `brew tap amiaopensource/amiaos` 18 | 19 | `brew install audiorecorder` 20 | 21 | __Linux Installation__ (Tested in Ubuntu 16.04) 22 | 23 | The following division of commands seeks to install up-to-date versions of audiorecorder dependencies while avoiding conflicts. 24 | 25 | __Standard package manager commands__ 26 | 27 | `sudo apt-get install sox` 28 | 29 | `sudo apt-get install libsdl2-2.0` 30 | 31 | Install current [MPV](https://mpv.io/installation/) ppa, then `sudo apt-get install mpv` 32 | 33 | __linuxbrew commands__ 34 | 35 | `brew install dialog` 36 | 37 | If FFmpeg has already been installed with these options then skip next step. 38 | 39 | `brew install ffmpeg --with-freetype` 40 | 41 | sdl2 is necessary to build FFmpeg with FFplay, but removing it after build will avoid conflicts between other installations of sdl2. 42 | 43 | `brew uninstall --ignore-dependencies sdl2` 44 | 45 | `brew install --ignore-dependencies audiorecorder` 46 | 47 | ## Usage: 48 | Usage: [-p] passthrough mode, [-e] edit config, [-m] edit metadata for BWF insertion 49 | 50 | #### Initial setup: 51 | To set up audiorecorder, first run the command `audiorecorder -e`. This will open an interface where settings such as recording bit depth and sample rate can be selected. This is also where the option to embed BEXT metadata can be selected. 52 | 53 | To set the metadata that will be embedded in the BEXT chunk (if activated in the configuration menu) run the command `audiorecorder -m`. This will open an interface where the desired values can be entered. 54 | 55 | #### Explanation of Interface: 56 | 1: Peak volume meters (dB) 57 | 58 | 2: Audio spectrum - lines (from left) represent 1, 5, 10, 15, 20kHz respectively 59 | 60 | 3: Recording time 61 | 62 | 4: Spectrogram 63 | 64 | 5: Audio vecorscope 65 | 66 | 6: Graph of peak volume values (dB) from -30dB to 0dB. Colored area represents -5dB to 0dB. 67 | 68 | audiorecorder interface 69 | 70 | #### Preview: 71 | Running `audiorecorder -p` will open the preview window. This window can be closed with the escape key. This allows you to check your settings and playback machine calibration using the audiorecorder interface without creating a file. It is recommended to use this mode before every recording. Once you are satisfied that everything is calibrated appropriately, move on to record mode. 72 | 73 | #### Record: 74 | Running the command `audiorecorder` will start record mode. You will be prompted for the name of the file created. __Wait until the audiorecorder interface opens__, and then press play on your playback machine. During recording do not resize the window, as this can cause problems with the audio buffer. To stop recording press the escape key. 75 | 76 | Once recording is complete, audiorecorder will process the file, and then an interface will open showing the waveform of the recorded file, as well as giving options to 'preview' and 'trim' the file. 77 | 78 | Press 'Preview' to hear the file you recorded. To trim file, enter the amount (in seconds) to trim from the start and end of the file and press 'Trim'. If 'Start Trim' is left blank, auto-trim will be applied to start of file. If no trim at start is desired enter '0'. After trimming, a preview window will open for your new file. Trim can be run as many times as is necessary. Once you are done, press 'Finish' to finalize and quit. It is at this point that BEXT metadata will be embedded if that option has been selected. 79 | 80 | ## Huge thanks to all contributors to audiorecorder! 81 | 82 | #### Project Maintainers are: 83 | Andrew Weaver (@privatezero) 84 | 85 | Ashley Blewer (@ablwr) 86 | 87 | #### Contributors to date: 88 | privatezero (Andrew Weaver), retokromer (Reto Kromer), dericed (Dave Rice), todrobbins (Tod Robbins), ablwr (Ashley Blewer) 89 | 90 | Special thanks to Matt Boyd at the University of Washington for extensive testing assistance! 91 | 92 | ## License 93 | 94 | The audiorecorder script is covered under a [BSD 3-clause license](https://github.com/amiaopensource/audiorecorder/blob/master/LICENSE.txt) 95 | 96 | Creative Commons License
All documentation for audiorecorder is licensed under a Creative Commons Attribution 4.0 International License. 97 | -------------------------------------------------------------------------------- /audiorecorder: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # audiorecorder 4 | 5 | VERSION=$(TMP=$(brew info audiorecorder | grep ".*\*$" | grep -Eo "/audiorecorder/.* \(") ; echo ${TMP:15:(${#TMP}-17)}) 6 | config="${HOME}/.$(basename "${0}").conf" 7 | bwf_config="${HOME}/.$(basename "${0}")_BWF.conf" 8 | # setup config file if doesn't exist 9 | if ! [ -e "${config}" ] ; then 10 | sample_rate='96 kHz' 11 | bit_depth='24 bit' 12 | channel_layout='1 and 2' 13 | bwf='0' 14 | { 15 | echo "#Please input choices between the quotes from the options supplied" 16 | echo "" 17 | echo "#Enter output directory for digitized files" 18 | echo "output=\"${output}\"" 19 | echo "" 20 | echo "#Sample Rate Choices: 44.1 kHz, 48 kHz, 96 kHz" 21 | echo "sample_rate=\"${sample_rate}\"" 22 | echo "#Bit Depth Choices: 16 bit, 24 bit" 23 | echo "bit_depth=\"${bit_depth}\"" 24 | echo "#Channel Layout Choices: 1, 2, 1 and 2" 25 | echo "channel_layout=\"${channel_layout}\"" 26 | echo "#BWF Choices 0, 1 (0 for off 1 for on)" 27 | echo "bwf=\"${bwf}\"" 28 | } > "${config}" 29 | fi 30 | # setup metadata config file if doesn't exist 31 | if ! [ -e "${bwf_config}" ] ; then 32 | { 33 | echo "#Enter originator information betwen the quotes" 34 | echo "originator=\"${originator}\"" 35 | echo "" 36 | echo "#Enter Coding History information betwen the quotes" 37 | echo "coding_history=\"${coding_history}\"" 38 | } > "${bwf_config}" 39 | fi 40 | 41 | touch ~/.config/mpv/input.conf 42 | . "${config}" 43 | . "${bwf_config}" 44 | #Set mpv to quit with same key as ffplay 45 | echo "ESC quit" > ~/.config/mpv/input.conf 46 | 47 | _usage(){ 48 | cat < in 10/2003, modified in 12/2003 (including 77 | # a code snippet contributed by Tor Sigurdsson), 08/2004 and 12/2004. 78 | bundlepath="Pashua.app/Contents/MacOS/Pashua" 79 | mypath=$(dirname "${0}") 80 | for searchpath in "$mypath/Pashua" "$mypath/$bundlepath" "./$bundlepath" \ 81 | "/Applications/$bundlepath" "$HOME/Applications/$bundlepath" 82 | do 83 | if [ -f "${searchpath}" -a -x "${searchpath}" ] ; then 84 | pashuapath=${searchpath} 85 | break 86 | fi 87 | done 88 | if [ ! "${pashuapath}" ] && [[ "$(uname -s)" = "Darwin" ]]; then 89 | echo "Error: Pashua was not found. Proceeding with Nano. Please install Pashua a more functional interface." 90 | GUI_TEST=1 91 | fi 92 | 93 | #Setup differences for systems 94 | if [[ "$(uname -s)" = "Darwin" ]] ; then 95 | FONTPATH='/Library/Fonts/Andale Mono.ttf' 96 | else 97 | GUI_TEST=1 98 | FONTPATH='/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf' 99 | fi 100 | 101 | _lookup_sample_rate(){ 102 | case "${1}" in 103 | "44.1 kHz") 104 | SAMPLE_RATE_NUMERIC="44100" 105 | ;; 106 | "48 kHz") 107 | SAMPLE_RATE_NUMERIC="48000" 108 | ;; 109 | "96 kHz") 110 | SAMPLE_RATE_NUMERIC="96000" 111 | ;; 112 | esac 113 | } 114 | 115 | _lookup_bit_depth(){ 116 | case "${1}" in 117 | "16 bit") 118 | CODEC="pcm_s16le" 119 | ;; 120 | "24 bit") 121 | CODEC="pcm_s24le" 122 | ;; 123 | esac 124 | } 125 | 126 | _lookup_channel_layout(){ 127 | case "${1}" in 128 | "1") 129 | CHANNEL_LAYOUT="-channel_layout mono" && SOXCHANNELS="1" 130 | ;; 131 | "2") 132 | CHANNEL_LAYOUT="-channel_layout mono" && SOXCHANNELS="2" 133 | ;; 134 | "1 and 2") 135 | CHANNEL_LAYOUT="-channel_layout stereo" && SOXCHANNELS="1 2" 136 | ;; 137 | "3 and 4") 138 | CHANNEL_LAYOUT="-channel_layout stereo" && SOXCHANNELS="3 4" 139 | ;; 140 | esac 141 | } 142 | 143 | _get_ITEM_ID(){ 144 | echo "Please Input Item ID or type Q to exit" 145 | read ITEM_ID 146 | if [[ "${ITEM_ID}" = [Qq] ]] ; then 147 | echo "Goodbye!" 148 | exit 0 149 | fi 150 | if [ -f "${output}/${ITEM_ID}.wav" ] ; then 151 | echo "A file with that name already exists!" 152 | _get_ITEM_ID 153 | fi 154 | } 155 | 156 | _destination_check(){ 157 | if [ -z "${output}" ] || [ ! -d "${output}" ] ; then 158 | echo "Please enter a valid output directory" 159 | read -r output 160 | _destination_check 161 | fi 162 | } 163 | 164 | _metadata_gui(){ 165 | gui_conf=" 166 | # Set transparency: 0 is transparent, 1 is opaque 167 | *.transparency=0.95 168 | # Set window title 169 | *.title = Edit Metadata Values 170 | # intro text 171 | intro.width = 300 172 | intro.type = text 173 | intro.text = Hello 174 | #Originator 175 | originator.type = textfield 176 | originator.label = Enter Originator 177 | originator.default = "${originator}" 178 | #Coding History 179 | coding_history.type = textbox 180 | coding_history.label = Enter Coding History 181 | coding_history.default = "${coding_history}" 182 | cb.type = cancelbutton 183 | cb.label = Cancel 184 | " 185 | 186 | if ! [ "${GUI_TEST}" = 1 ] ; then 187 | pashua_configfile=$(/usr/bin/mktemp /tmp/pashua_XXXXXXXXX) 188 | echo "${gui_conf}" > "${pashua_configfile}" 189 | pashua_run 190 | rm "${pashua_configfile}" 191 | else 192 | nano "${bwf_config}" 193 | . "${bwf_config}" 194 | fi 195 | } 196 | 197 | _master_gui(){ 198 | gui_conf=" 199 | # Set transparency: 0 is transparent, 1 is opaque 200 | *.transparency=0.95 201 | # Set window title 202 | *.title = Welcome to Audio Recorder! 203 | # intro text 204 | intro.width = 300 205 | intro.type = text 206 | intro.text = Hello 207 | #Output Location 208 | output.type = openbrowser 209 | output.label = Output Location 210 | output.default = "${output}" 211 | #Sample Rate 212 | sample_rate.type = radiobutton 213 | sample_rate.label = Select Sample Rate 214 | sample_rate.option = 44.1 kHz 215 | sample_rate.option = 48 kHz 216 | sample_rate.option = 96 kHz 217 | sample_rate.default = "${sample_rate}" 218 | #Bit Depth 219 | bit_depth.type = radiobutton 220 | bit_depth.label = Select Bit Depth 221 | bit_depth.option = 16 bit 222 | bit_depth.option = 24 bit 223 | bit_depth.default = "${bit_depth}" 224 | #Channel Layout 225 | channel_layout.type = radiobutton 226 | channel_layout.label = Select Input Channel(s) 227 | channel_layout.option = 1 228 | channel_layout.option = 2 229 | channel_layout.option = 1 and 2 230 | channel_layout.option = 3 and 4 231 | channel_layout.default = "${channel_layout}" 232 | #Enable BWF 233 | bwf.type = checkbox 234 | bwf.label = Embed BWF Metadata 235 | bwf.default = "${bwf}" 236 | #Cancel Button 237 | cb.type = cancelbutton 238 | cb.label = Cancel 239 | " 240 | 241 | if ! [ "${GUI_TEST}" = 1 ] ; then 242 | pashua_configfile=$(/usr/bin/mktemp /tmp/pashua_XXXXXXXXX) 243 | echo "${gui_conf}" > "${pashua_configfile}" 244 | pashua_run 245 | rm "${pashua_configfile}" 246 | else 247 | nano "${config}" 248 | . "$config" 249 | fi 250 | } 251 | 252 | _post_digitization_gui(){ 253 | post_gui_conf=" 254 | # Set transparency: 0 is transparent, 1 is opaque 255 | *.transparency=0.95 256 | # Set window title 257 | *.title = Press ok to exit 258 | # intro text 259 | intro.width = 300 260 | intro.type = text 261 | intro.text = Please Select Post-digitization Options 262 | intro.x = 150 263 | intro.y = 500 264 | #Waveform 265 | waveform.type = image 266 | waveform.path = AUDIORECORDERTEMP.tiff 267 | waveform.x = 5 268 | waveform.y = 350 269 | #Preview 270 | preview.type = button 271 | preview.label = Preview 272 | preview.x = 10 273 | preview.y = 320 274 | #Trim 275 | trim.type = button 276 | trim.label = Trim 277 | trim.x = 150 278 | trim.y = 320 279 | #Instructions 280 | instructions.type = text 281 | instructions.width = 500 282 | instructions.default = Press 'Preview' to hear the file you recorded.[return][return]To trim file, enter the amount (in seconds) to trim from the start and end of the file and press 'Trim'.[return][return]If 'Start Trim' is left blank, auto-trim will be applied to start of file. If no trim at start is desired enter '0'. After trimming, a preview window will open for your new file. Trim can be run as many times as is neccessary.[return][return]Press 'Finish' to quit. 283 | instructions.x = 10 284 | instructions.y = 150 285 | #Start Trim Length 286 | StartTrimLength.type = textfield 287 | StartTrimLength.label = Start Trim 288 | StartTrimLength.width = 100 289 | StartTrimLength.x = 10 290 | StartTrimLength.y = 60 291 | #End Trim Length 292 | EndTrimLength.type = textfield 293 | EndTrimLength.label = End Trim 294 | EndTrimLength.width = 100 295 | EndTrimLength.x = 10 296 | EndTrimLength.y = 5 297 | exit.type = defaultbutton 298 | exit.label = Finish 299 | " 300 | 301 | if ! [ "${GUI_TEST}" = 1 ] ; then 302 | pashua_configfile=$(/usr/bin/mktemp /tmp/pashua_XXXXXXXXX) 303 | echo "${post_gui_conf}" > "${pashua_configfile}" 304 | pashua_run 305 | rm "${pashua_configfile}" 306 | else 307 | unset preview 308 | unset trim 309 | post_dig_opt=(preview trim exit) 310 | post_dig=$(dialog --title "Select Action" --radiolist "Click or use arrow keys and space bar to select" 15 60 4 "${post_dig_opt[0]}" "file" ON "${post_dig_opt[1]}" "file" OFF "${post_dig_opt[2]}" "audiorecorder" OFF 3>&1 1>&2 2>&3 3>&-) 311 | if [ "${post_dig}" = "trim" ] ; then 312 | StartTrimLength=$(dialog --title "Start Trim Length" --inputbox "Enter seconds to trim from start of file. Leave blank for auto. Enter 0 if no trimming is desired." 8 50 3>&1 1>&2 2>&3 3>&-) 313 | EndTrimLength=$(dialog --title "End Trim Length" --inputbox "Enter seconds to trim from end of file. Leave blank if no trimming is desired." 8 50 3>&1 1>&2 2>&3 3>&-) 314 | fi 315 | case "${post_dig}" in 316 | "preview") 317 | preview="1" 318 | ;; 319 | "trim") 320 | trim="1" 321 | ;; 322 | "exit") 323 | clear 324 | ;; 325 | esac 326 | fi 327 | 328 | if [ "${preview}" = "1" ] ; then 329 | _preview_file && _post_digitization_gui 330 | fi 331 | 332 | if [ "${trim}" = "1" ] ; then 333 | _trim_file && _post_digitization_gui 334 | fi 335 | } 336 | 337 | _embed_bext(){ 338 | echo "Preparing to embed metadata in ${BEXT_TARGET}" 339 | echo "" 340 | if (("${#ITEM_ID}" > 32)) ; then 341 | orig_ref="See description for Identifiers" 342 | else 343 | orig_ref="${ITEM_ID}" 344 | fi 345 | 346 | bwfmetaedit --reject-overwrite --Description="${ITEM_ID}".wav --Originator="${originator}" --OriginatorReference="${orig_ref}" --History="${coding_history}" --IARL="${originator}" --MD5-Embed --OriginationDate=$(date "+%Y-%m-%d") --OriginationTime=$(date "+%H:%M:%S") "${BEXT_TARGET}" 347 | } 348 | 349 | pashua_run() { 350 | # Wrapper function for interfacing to Pashua. Written by Carsten 351 | # Bluem in 10/2003, modified in 12/2003 (including 352 | # a code snippet contributed by Tor Sigurdsson), 08/2004 and 12/2004. 353 | # Write config file 354 | 355 | # Find Pashua binary. We do search both . and dirname "$0" 356 | # , as in a doubleclickable application, cwd is / 357 | # BTW, all these quotes below are necessary to handle paths 358 | # containing spaces. 359 | 360 | # Get result 361 | result=$("$pashuapath" ${pashua_configfile} | sed 's/ /;;;/g') 362 | 363 | # Parse result 364 | for line in $result ; do 365 | key=$(echo ${line} | sed 's/^\([^=]*\)=.*$/\1/') 366 | value=$(echo ${line} | sed 's/^[^=]*=\(.*\)$/\1/' | sed 's/;;;/ /g') 367 | varname=${key} 368 | varvalue="${value}" 369 | eval $varname='$varvalue' 370 | done 371 | } # pashua_run() 372 | 373 | if [ "${runtype}" = "edit" ] ; then 374 | _master_gui 375 | 376 | { 377 | echo "#Please input choices between the quotes from the options supplied" 378 | echo "" 379 | echo "#Enter output directory for digitized files" 380 | echo "output=\"${output}\"" 381 | echo "" 382 | echo "#Sample Rate Choices: 44.1 kHz, 48 kHz, 96 kHz" 383 | echo "sample_rate=\"${sample_rate}\"" 384 | echo "#Bit Depth Choices: 16 bit, 24 bit" 385 | echo "bit_depth=\"${bit_depth}\"" 386 | echo "#Channel Layout Choices: 1, 2, 1 and 2" 387 | echo "channel_layout=\"${channel_layout}\"" 388 | echo "#BWF Choices 0, 1 (0 for off 1 for on)" 389 | echo "bwf=\"${bwf}\"" 390 | } > "${config}" 391 | echo "Audiorecorder settings updated." 392 | exit 0 393 | fi 394 | if [ "${runtype}" = "metadata" ] ; then 395 | _metadata_gui 396 | { 397 | echo "#Enter originator information betwen the quotes" 398 | echo "originator=\"${originator}\"" 399 | echo "" 400 | echo "#Enter Coding History information betwen the quotes" 401 | echo "coding_history=\"${coding_history}\"" 402 | } > "${bwf_config}" 403 | echo "BWF settings updated." 404 | exit 0 405 | fi 406 | 407 | #Verify and configure 408 | if [ -n "${sample_rate}" ] ; then 409 | _lookup_sample_rate "${sample_rate}" 410 | else 411 | echo "No Sample Rate Specified. Setting to 96kH." 412 | SAMPLE_RATE_NUMERIC="96000" 413 | fi 414 | if [ -n "${bit_depth}" ] ; then 415 | _lookup_bit_depth "${bit_depth}" 416 | else 417 | echo "No Bit Depth Specified. Setting to 24." 418 | CODEC="pcm_s24le" 419 | fi 420 | if [ -n "${channel_layout}" ] ; then 421 | _lookup_channel_layout "${channel_layout}" 422 | else 423 | echo "No Channel Layout Specified. Setting Input to Channels 1 and 2" 424 | channel_layout="1 and 2" 425 | fi 426 | 427 | FILTER_CHAIN="asplit=6[out1][a][b][c][d][e],\ 428 | [e]showvolume=w=700:c=0xFFff0000:r=30[e1],\ 429 | [a]showfreqs=mode=bar:cmode=separate:size=300x300:colors=magenta|yellow[a1],\ 430 | [a1]drawbox=12:0:3:300:white@0.2[a2],[a2]drawbox=66:0:3:300:white@0.2[a3],[a3]drawbox=135:0:3:300:white@0.2[a4],[a4]drawbox=202:0:3:300:white@0.2[a5],[a5]drawbox=271:0:3:300:white@0.2[aa],\ 431 | [b]avectorscope=s=300x300:r=30:zoom=5[b1],\ 432 | [b1]drawgrid=x=150:y=150:c=white@0.3[bb],\ 433 | [c]showspectrum=s=400x600:slide=scroll:mode=combined:color=rainbow:scale=lin:saturation=4[cc],\ 434 | [d]astats=metadata=1:reset=1,adrawgraph=lavfi.astats.Overall.Peak_level:max=0:min=-30.0:size=700x256:bg=Black[dd],\ 435 | [dd]drawbox=0:0:700:42:hotpink@0.2:t=42[ddd],\ 436 | [aa][bb]vstack[aabb],[aabb][cc]hstack[aabbcc],[aabbcc][ddd]vstack[aabbccdd],[e1][aabbccdd]vstack[z],\ 437 | [z]drawtext=fontfile="${FONTPATH}": text='%{pts \\: hms}':x=460: y=50:fontcolor=white:fontsize=30:box=1:boxcolor=0x00000000@1[fps],[fps]fps=fps=30[out0]" 438 | 439 | # BEXT mode 440 | if [ "${runtype}" = "bext" ] ; then 441 | ITEM_ID=$(basename "${2}" | cut -d'.' -f1) 442 | BEXT_TARGET="${2}" 443 | _embed_bext 444 | exit 0 445 | fi 446 | 447 | # Passthrough mode 448 | if [ "${runtype}" = "passthrough" ] ; then 449 | rec -r "${SAMPLE_RATE_NUMERIC}" -b 32 -L -e signed-integer --buffer 500000 -p remix ${SOXCHANNELS} | ffmpeg ${CHANNEL_LAYOUT} -i - -f wav -c:a pcm_s16le -ar 44100 - |\ 450 | ffplay -window_title "Monitoring Channel(s): ${channel_layout}" -f lavfi "amovie='pipe\:0',${FILTER_CHAIN}" 2> /dev/null 451 | stty sane 452 | exit 453 | fi 454 | 455 | _destination_check 456 | 457 | # Record Mode 458 | _get_ITEM_ID 459 | 460 | if [ -e AUDIORECORDERTEMP.wav ] ; then 461 | rm -r AUDIORECORDERTEMP.wav 462 | fi 463 | 464 | #ASTATS_OUT=$(mktemp /tmp/temp.XXXX) 465 | rec -r "${SAMPLE_RATE_NUMERIC}" -b 32 -L -e signed-integer --buffer 2000000 -p remix ${SOXCHANNELS} | ffmpeg ${CHANNEL_LAYOUT} -i - -f wav -c:a "${CODEC}" -ar "${SAMPLE_RATE_NUMERIC}" -metadata comment="" -y -rf64 auto AUDIORECORDERTEMP.wav -f wav -c:a pcm_s16le -ar 44100 - |\ 466 | ffplay -window_title "Currently Recording Channel(s): ${channel_layout}" -f lavfi "amovie='pipe\:0',${FILTER_CHAIN}" 2> /dev/null 467 | # Cleanup 468 | stty sane 469 | 470 | ##COMMENTING OUT FOR NOW DUE TO LACK OF ACCURACY### 471 | # Post Recording Stats 472 | #PEAK_COUNT=$(cat "${ASTATS_OUT}" | tail -n -8 | grep "Peak count" | cut -d':' -f2 | cut -c 2- | cut -d'.' -f1) 473 | 474 | #if [ "${PEAK_COUNT}" -gt "4" ] ; then 475 | # echo -e "\033[101mWarning: There are $((${PEAK_COUNT} - 2)) samples with peaked levels in this recording.\033[0m" 476 | #fi 477 | 478 | #ASTATS=$(cat "${ASTATS_OUT}" | tail -n -8 | cut -d']' -f2 | cut -c 2- | head -n 4) 479 | #echo -e "\033[1;32mLevels from recorded file are:\033[0m" 480 | #echo -e "\033[1;32m${ASTATS}\033[0m" 481 | 482 | #Post Record Functions 483 | PRETRIM="${output}"/"${ITEM_ID}"_untrimmed.wav 484 | FINALOUTPUT="${output}"/"${ITEM_ID}".wav 485 | _preview_file(){ 486 | if [ -z "${TRIMCHECK}" ] ; then 487 | mpv --force-window --no-terminal --keep-open=yes --title="Preview" --geometry=620x620 -lavfi-complex "[aid1]asplit=3[ao][a][b],[a]showwaves=600x240:n=1[a1],[a1]drawbox=0:0:600:240:t=120[a2],[b]showwaves=600x240:mode=cline:colors=0x00FFFF:split_channels=1[b2],[a2][b2]overlay[vo]" "${FINALOUTPUT}" 488 | else 489 | mpv --force-window --no-terminal --keep-open=yes --title="Preview" --geometry=620x620 -lavfi-complex "[aid1]asplit=3[ao][a][b],[a]showwaves=600x240:n=1[a1],[a1]drawbox=0:0:600:240:t=120[a2],[b]showwaves=600x240:mode=cline:colors=0x00FFFF:split_channels=1[b2],[a2][b2]overlay[vo]" "${PRETRIM}" 490 | fi 491 | } 492 | 493 | _set_up_trim(){ 494 | if [ -n "${EndTrimLength}" ] ; then 495 | FILELENGTH=$(ffprobe "${1}" 2>&1 | grep Duration | tr -d ' ' | cut -d',' -f1 | cut -d':' -f2-) 496 | FILELENGTH_NORMALIZED=$(echo "${FILELENGTH}" | awk -F: '{ print ($1 * 3600) + ($2 * 60) + $3 }') 497 | ENDTRIM=$(echo "${FILELENGTH_NORMALIZED}" - "${EndTrimLength}" | bc) 498 | if [ -n "${StartTrimLength}" ] && [ -n "${EndTrimLength}" ] ; then 499 | ENDTRIM=$(echo "${FILELENGTH_NORMALIZED}" - "${EndTrimLength}" - "${StartTrimLength}" | bc) 500 | fi 501 | ENDTRIMOPT="-t ${ENDTRIM}" 502 | fi 503 | if [ -n "${StartTrimLength}" ] ; then 504 | STARTTRIMOPT="-ss ${StartTrimLength}" 505 | fi 506 | } 507 | 508 | _trim_file(){ 509 | if [ -n "${StartTrimLength}" ] && ! [[ "${StartTrimLength}" =~ ^[+]?[0-9]+\.?[0-9]*$ ]] ; then 510 | echo "Please use only (positive) numbers for the trim length" 511 | elif [ -n "${EndTrimLength}" ] && ! [[ "${EndTrimLength}" =~ ^[+]?[0-9]+\.?[0-9]*$ ]] ; then 512 | echo "Please use only (positive) numbers for the trim length" 513 | else 514 | if [[ -z "${TRIMCHECK}" ]] ; then 515 | _set_up_trim "${FINALOUTPUT}" 516 | mv "${FINALOUTPUT}" "${PRETRIM}" 517 | else 518 | _set_up_trim "${PRETRIM}" 519 | fi 520 | if [[ -z "${StartTrimLength}" ]] ; then 521 | if [ -n "${EndTrimLength}" ] ; then 522 | ffmpeg -i "${PRETRIM}" -af silenceremove=start_threshold=-57dB:start_duration=1:start_periods=1 -f wav -c:a "${CODEC}" -ar "${SAMPLE_RATE_NUMERIC}" -rf64 auto -y INTERMEDIATE.wav 523 | _set_up_trim INTERMEDIATE.wav 524 | ffmpeg -i INTERMEDIATE.wav -c copy -t "${ENDTRIM}" -rf64 auto -y "${FINALOUTPUT}" 525 | rm INTERMEDIATE.wav 526 | else 527 | ffmpeg -i "${PRETRIM}" -af silenceremove=start_threshold=-57dB:start_duration=1:start_periods=1 -f wav -c:a "${CODEC}" -ar "${SAMPLE_RATE_NUMERIC}" -rf64 auto -y "${FINALOUTPUT}" 528 | fi 529 | else 530 | ffmpeg ${STARTTRIMOPT} -i "${PRETRIM}" -c copy -rf64 auto ${ENDTRIMOPT} -y "${FINALOUTPUT}" 531 | fi 532 | TRIMCHECK='1' 533 | mpv --force-window --no-terminal --keep-open=yes --title="Preview Trim" --geometry=620x620 -lavfi-complex "[aid1]asplit=3[ao][a][b],[a]showwaves=600x240:n=1[a1],[a1]drawbox=0:0:600:240:t=120[a2],[b]showwaves=600x240:mode=cline:colors=0x00FFFF:split_channels=1[b2],[a2][b2]overlay[vo]" "${FINALOUTPUT}" 534 | fi 535 | unset STARTTRIMOPT 536 | unset ENDTRIMOPT 537 | } 538 | 539 | # Cleans up and loads post digitization option GUI 540 | ffmpeg -i AUDIORECORDERTEMP.wav -lavfi showwavespic=split_channels=1:s=500x150:colors=blue -y AUDIORECORDERTEMP.tiff -c copy "${FINALOUTPUT}" 541 | rm AUDIORECORDERTEMP.wav && _post_digitization_gui 542 | 543 | # Embed metadata in BEXT 544 | if [ "${bwf}" = "1" ] ; then 545 | BEXT_TARGET="${FINALOUTPUT}" 546 | _embed_bext 547 | fi 548 | rm AUDIORECORDERTEMP.tiff 549 | 550 | #Cascadia Now! 551 | -------------------------------------------------------------------------------- /audiorecorder.1: -------------------------------------------------------------------------------- 1 | .TH audiorecorder 1 "github.com/amiaopensource/audiorecorder" "2019-05-21" "AMIA Open Source" 2 | .\" Turn off justification for nroff. 3 | .if n .ad l 4 | .\" Turn off hyphenation. 5 | .nh 6 | .SH NAME 7 | audiorecorder - Tool for calibration and recording of analog audio sources 8 | .SH SYNOPSIS 9 | \fBaudiorecorder\fR [\fB-e\fR | \fB-p\fR | \fB-m\fR | \fB-b\fR | \fB-h\fR] 10 | .SH OPTIONS 11 | .TP 12 | .B -e 13 | edit config 14 | .TP 15 | .B -p 16 | passthrough mode 17 | .TP 18 | .B -m 19 | edit metadata to be used in BWF creation 20 | .TP 21 | .B -b 22 | embed BEXT chunk (BWF metadata) in target file 23 | .TP 24 | .B -h 25 | display a help message 26 | .SH LICENSE 27 | .TP 28 | The audiorecorder script is covered under a BSD 3-clause license. 29 | .TP 30 | All documentation for audiorecorder is licensed under a Creative Commons Attribution 4.0 International License. 31 | -------------------------------------------------------------------------------- /audiorecorder2-dev/README.md: -------------------------------------------------------------------------------- 1 | # audiorecorder2 2 | 3 | ## initial sandbox for audiorecorder2 GUI build using Shoes 4 | 5 | Currently does not include absolute paths for dependencies that are required for macOS functionality. Test build is operational in Linux - GUI can be opened (but isn't functional) on macOS 6 | -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2-osx.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2-osx.tgz -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_large.png -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_play_large.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_play_large.gif -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_play_small.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_play_small.gif -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_small_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_small_1.png -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_small_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_small_2.png -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_small_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/audiorecorder2-dev/audiorecorder2/Resources/audiorecorder_small_3.png -------------------------------------------------------------------------------- /audiorecorder2-dev/audiorecorder2/audiorecorder2.rb: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'json' 3 | 4 | # Recording Variables 5 | if RUBY_PLATFORM.include?('linux') 6 | Drawfontpath = '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf' 7 | Soxpath = 'rec' 8 | Ffmpegpath = 'ffmpeg' 9 | Ffplaypath = 'ffplay' 10 | Ffprobepath = 'ffprobe' 11 | Bwfmetaeditpath = 'bwfmetaedit' 12 | Mpvpath = 'mpv' 13 | elsif RUBY_PLATFORM.include?('darwin') 14 | Drawfontpath = '/Library/Fonts/Andale Mono.ttf' 15 | Soxpath = '/usr/local/bin/rec' 16 | Ffmpegpath = '/usr/local/bin/ffmpeg' 17 | Ffplaypath = '/usr/local/bin/ffplay' 18 | Ffprobepath = '/usr/local/bin/ffprobe' 19 | Bwfmetaeditpath = '/usr/local/bin/bwfmetaedit' 20 | Mpvpath = '/usr/local/bin/mpv' 21 | else 22 | Drawfontpath = 'some windows path' 23 | end 24 | 25 | # Check FFmpeg version for filter options 26 | 27 | if system('#{Ffmpegpath} -version | grep "ffmpeg version 4.0.0\|ffmpeg version 4.0.1\|ffmpeg version 4.0.2"') 28 | Avectorscopefilter = 'avectorscope=s=300x300:r=30:zoom=5:mirror=0' 29 | else 30 | Avectorscopefilter = 'avectorscope=s=300x300:r=30:zoom=5' 31 | end 32 | 33 | FILTER_CHAIN = "asplit=6[out1][a][b][c][d][e],\ 34 | [e]showvolume=w=700:c=0xFFff0000:r=30[e1],\ 35 | [a]showfreqs=mode=bar:cmode=separate:size=300x300:colors=magenta|yellow[a1],\ 36 | [a1]drawbox=12:0:3:300:white@0.2[a2],[a2]drawbox=66:0:3:300:white@0.2[a3],[a3]drawbox=135:0:3:300:white@0.2[a4],[a4]drawbox=202:0:3:300:white@0.2[a5],[a5]drawbox=271:0:3:300:white@0.2[aa],\ 37 | [b]#{Avectorscopefilter}[b1],\ 38 | [b1]drawgrid=x=150:y=150:c=white@0.3[bb],\ 39 | [c]showspectrum=s=400x600:slide=scroll:mode=combined:color=rainbow:scale=lin:saturation=4[cc],\ 40 | [d]astats=metadata=1:reset=1,adrawgraph=lavfi.astats.Overall.Peak_level:max=0:min=-30.0:size=700x256:bg=Black[dd],\ 41 | [dd]drawbox=0:0:700:42:hotpink@0.2:t=42[ddd],\ 42 | [aa][bb]vstack[aabb],[aabb][cc]hstack[aabbcc],[aabbcc][ddd]vstack[aabbccdd],[e1][aabbccdd]vstack[z],\ 43 | [z]drawtext=fontfile=#{Drawfontpath}: text='%{pts \\: hms}':x=460: y=50:fontcolor=white:fontsize=30:box=1:boxcolor=0x00000000@1[fps],[fps]fps=fps=30[out0]" 44 | 45 | # Set Configuration 46 | sox_channels = '1 2' 47 | ffmpeg_channels = 'stereo' 48 | $codec_choice = 'pcm_s24le' 49 | $soxbuffer = '50000' 50 | $sample_rate_choice = '96000' 51 | $filename = '' 52 | 53 | # Load options from config file 54 | configuration_file = File.expand_path('~/.audiorecorder2.conf') 55 | if ! File.exist?(configuration_file) 56 | config_options = "destination:\nsamplerate:\nchannels:\ncodec:\norig:\nhist:\nbext:" 57 | File.write(configuration_file, config_options) 58 | end 59 | config = YAML::load_file(configuration_file) 60 | $outputdir = config['destination'] 61 | $sample_rate_choice = config['samplerate'] 62 | sox_channels = config['channels'] 63 | $codec_choice = config['codec'] 64 | $originator = config['orig'] 65 | $history = config['hist'] 66 | $embedbext = config['bext'] 67 | 68 | #BWF Metaedit Function 69 | def EmbedBEXT(targetfile) 70 | moddatetime = File.mtime(targetfile) 71 | moddate = moddatetime.strftime("%Y-%m-%d") 72 | modtime = moddatetime.strftime("%H:%M:%S") 73 | 74 | #Get Input Name for Description and OriginatorReference 75 | file_name = File.basename(targetfile) 76 | originatorreference = File.basename(targetfile, '.wav') 77 | if originatorreference.length > 32 78 | originatorreference = "See Description for Identifiers" 79 | end 80 | bwfcommand = Bwfmetaeditpath + ' --reject-overwrite ' + '--Description=' + "'" + file_name + "'" + ' --Originator=' + "'" + $originator + "'" + ' --History=' + "'" + $history + "'" + ' --OriginatorReference=' + "'" + originatorreference + "'" + ' --OriginationDate=' + moddate + ' --OriginationTime=' + modtime + ' --MD5-Embed ' + "'" + targetfile + "'" 81 | system(bwfcommand) 82 | end 83 | 84 | #Function for adjusting buffer 85 | def BufferCheck(sr) 86 | if sr == '96000' 87 | $soxbuffer = '50000' 88 | elsif sr == '48000' 89 | $soxbuffer = '25000' 90 | elsif sr == '44100' 91 | $soxbuffer = '23000' 92 | end 93 | end 94 | 95 | # GUI App 96 | Shoes.app(title: "AudioRecorder2", width: 600, height: 625) do 97 | style Shoes::Para, font: "Helvetica" 98 | background aliceblue 99 | @logo = image("Resources/audiorecorder_small_1.png", left: 160) 100 | animate(10) do |frame| 101 | if frame.to_i.even? 102 | @logo.path = "Resources/audiorecorder_small_3.png" 103 | else 104 | @logo.path = "Resources/audiorecorder_small_2.png" 105 | end 106 | end 107 | 108 | def PostRecord(targetfile) 109 | window(title: "Post-Record Options", width: 600, height: 560) do 110 | style Shoes::Para, font: "Helvetica" 111 | background aliceblue 112 | trimcheck = nil 113 | @pretrim = $outputdir + '/' + File.basename(targetfile, File.extname(targetfile)) + '_untrimmed' + '.wav' 114 | @finaloutput = targetfile 115 | @trimtemp = $outputdir + '/' + 'trim_tempfile.wav' 116 | stack margin: 15 do 117 | border gainsboro, strokewidth: 6 118 | waveform = image($waveform_pic) 119 | end 120 | @start_trim = nil 121 | @end_trim_length = nil 122 | 123 | def SetUpTrim(input) 124 | ffprobe_command = Ffprobepath + ' -print_format json -show_streams ' + "'" + input + "'" 125 | $ffprobeout = JSON.parse(`#{ffprobe_command}`) 126 | @duration_json = $ffprobeout['streams'][0]['duration'] 127 | @duration =@duration_json.to_f 128 | if ! @end_trim_length.nil? && @start_trim_length != "AUTO" 129 | $end_trim_opt = @duration - @end_trim_length - @start_trim_length 130 | elsif ! @end_trim_length.nil? 131 | $end_trim_opt = @duration - @end_trim_length 132 | end 133 | if @start_trim_length != "AUTO" 134 | $start_trim_opt = ' -ss ' + @start_trim_length.to_s 135 | end 136 | end 137 | stack margin: 15 do 138 | background lightcyan 139 | para "Press 'Preview' to hear the file you recorded. To trim file, enter the amount (in seconds) to trim from the start and end of the file and press 'Trim'.\n\nIf 'Start Trim' is set to Auto, auto-trim will be applied to start of file. If no trim at start is desired set this to be empty. After trimming, a preview window will open for your new file. Trim can be run as many times as is neccessary.\n\nPress 'Finish' to quit" 140 | end 141 | flow do 142 | @start_trim_length = "AUTO" 143 | para 'Start Trim' 144 | start_trim_input = edit_line text = @start_trim_length do 145 | if start_trim_input.text.downcase == "auto" 146 | @start_trim_length = "AUTO" 147 | else 148 | @start_trim_length = start_trim_input.text.to_f 149 | end 150 | end 151 | end 152 | flow do 153 | para 'End Trim' 154 | end_trim_input = edit_line do 155 | @end_trim_length = end_trim_input.text.to_f 156 | end 157 | end 158 | flow do 159 | preview = button "Preview" 160 | preview.click do 161 | if trimcheck.nil? 162 | command = Mpvpath + ' --force-window --no-terminal --keep-open=yes --title="Preview" --geometry=620x620 -lavfi-complex "[aid1]asplit=3[ao][a][b],[a]showwaves=600x240:n=1[a1],[a1]drawbox=0:0:600:240:t=120[a2],[b]showwaves=600x240:mode=cline:colors=0x00FFFF:split_channels=1[b2],[a2][b2]overlay[vo]" ' + '"' + @finaloutput + '"' 163 | system(command) 164 | else 165 | command = Mpvpath + ' --force-window --no-terminal --keep-open=yes --title="Preview" --geometry=620x620 -lavfi-complex "[aid1]asplit=3[ao][a][b],[a]showwaves=600x240:n=1[a1],[a1]drawbox=0:0:600:240:t=120[a2],[b]showwaves=600x240:mode=cline:colors=0x00FFFF:split_channels=1[b2],[a2][b2]overlay[vo]" ' + '"'+ @pretrim + '"' 166 | system(command) 167 | end 168 | end 169 | trim = button "Trim" 170 | trim.click do 171 | #set up trim 172 | if trimcheck.nil? 173 | SetUpTrim(@finaloutput) 174 | File.rename(@finaloutput, @pretrim) 175 | trimcheck = 1 176 | else 177 | SetUpTrim(@pretrim) 178 | end 179 | if @start_trim_length == "AUTO" 180 | if ! @end_trim_length.nil? && @end_trim_length != 0.0 181 | precommand = Ffmpegpath + ' -i ' + '"' + @pretrim + '"' + ' -af silenceremove=start_threshold=-57dB:start_duration=1:start_periods=1 -f wav -c:a ' + $codec_choice + ' -ar ' + $sample_rate_choice + ' -y -rf64 auto ' + @trimtemp 182 | system(precommand) 183 | SetUpTrim(@trimtemp) 184 | postcommand = Ffmpegpath + " -i #{@trimtemp} -c copy -y -rf64 auto " + ' -t ' + $end_trim_opt.to_s + ' "' + @finaloutput + '"' 185 | system(postcommand) 186 | File.delete(@trimtemp) 187 | else 188 | command = Ffmpegpath + ' -i ' + '"' + @pretrim + '"' + ' -af silenceremove=start_threshold=-57dB:start_duration=1:start_periods=1 -f wav -c:a ' + $codec_choice + ' -ar ' + $sample_rate_choice + ' -y -rf64 auto ' + '"' + @finaloutput + '"' 189 | system(command) 190 | end 191 | else 192 | if ! @end_trim_length.nil? && @end_trim_length != 0.0 193 | command = Ffmpegpath + ' ' + $start_trim_opt + ' -i ' + '"' + @pretrim + '"' + ' -c copy -y -rf64 auto ' + ' -t ' + $end_trim_opt.to_s + ' "' + @finaloutput + '"' 194 | system(command) 195 | else 196 | command = Ffmpegpath + ' ' + $start_trim_opt + ' -i ' + '"' + @pretrim + '"' + ' -c copy -y -rf64 auto ' + ' "' + @finaloutput + '"' 197 | system(command) 198 | end 199 | end 200 | command = Mpvpath + ' --force-window --no-terminal --keep-open=yes --title="Preview" --geometry=620x620 -lavfi-complex "[aid1]asplit=3[ao][a][b],[a]showwaves=600x240:n=1[a1],[a1]drawbox=0:0:600:240:t=120[a2],[b]showwaves=600x240:mode=cline:colors=0x00FFFF:split_channels=1[b2],[a2][b2]overlay[vo]" ' + '"' + @finaloutput + '"' 201 | system(command) 202 | end 203 | close = button "Finish" 204 | close.click do 205 | File.delete($waveform_pic) 206 | close() 207 | end 208 | end 209 | end 210 | end 211 | 212 | flow margin: 2 do 213 | para "Select Channel(s)" 214 | if sox_channels == '1 2' 215 | sox_channels_saved = '1 and 2' 216 | elsif sox_channels == '3 4' 217 | sox_channels_saved = '3 and 4' 218 | else 219 | sox_channels_saved = sox_channels 220 | end 221 | channels = list_box items: ["1", "2", "1 and 2", "3 and 4"], 222 | width: 100, choose: sox_channels_saved do |list| 223 | if list.text == '1 and 2' 224 | sox_channels = '1 2' 225 | elsif list.text == '3 and 4' 226 | sox_channels = '3 4' 227 | else 228 | sox_channels = list.text 229 | end 230 | if sox_channels == "1 2" 231 | ffmpeg_channels = 'stereo' 232 | elsif sox_channels == "3 4" 233 | ffmpeg_channels = 'stereo' 234 | else 235 | ffmpeg_channels = 'mono' 236 | end 237 | end 238 | para "\n\n" 239 | para "Sample Rate" 240 | if $sample_rate_choice == '44100' 241 | sample_rate_saved = "44.1 kHz" 242 | elsif $sample_rate_choice == '48000' 243 | sample_rate_saved = "48 kHz" 244 | elsif $sample_rate_choice == '96000' 245 | sample_rate_saved = "96 kHz" 246 | end 247 | samplerate = list_box items: ["44.1 kHz", "48 kHz", "96 kHz"], 248 | width: 100, choose: sample_rate_saved do |list| 249 | if list.text == '44.1 kHz' 250 | $sample_rate_choice = '44100' 251 | elsif list.text == '48 kHz' 252 | $sample_rate_choice = '48000' 253 | elsif list.text == '96 kHz' 254 | $sample_rate_choice = '96000' 255 | end 256 | end 257 | para "\n\n" 258 | para "Bit Depth" 259 | if $codec_choice == 'pcm_s16le' 260 | codec_saved = "16 bit" 261 | elsif $codec_choice == 'pcm_s24le' 262 | codec_saved = "24 bit" 263 | end 264 | bitdepth = list_box items: ["16 bit", "24 bit"], 265 | width: 100, choose: codec_saved do |list| 266 | if list.text == '16 bit' 267 | $codec_choice = 'pcm_s16le' 268 | elsif list.text == '24 bit' 269 | $codec_choice = 'pcm_s24le' 270 | end 271 | end 272 | end 273 | 274 | stack margin: 10 do 275 | button "Choose Output Directory" do 276 | $outputdir = ask_open_folder 277 | @destination.replace "#{$outputdir}" 278 | end 279 | flow do 280 | destination_prompt = para "File will be saved to:" 281 | @destination = para "#{$outputdir}", underline: "single" 282 | end 283 | end 284 | 285 | stack margin: 10 do 286 | button "Choose File Name" do 287 | $filename = ask("Please Enter File Name") 288 | if $filename == '' 289 | @outputfile.replace "#{$filename}" 290 | else 291 | @outputfile.replace "#{$filename}.wav" 292 | end 293 | end 294 | flow do 295 | output_prompt = para "File will be saved as:" 296 | @outputfile = para "#{$filename}", underline: "single" 297 | end 298 | end 299 | 300 | flow do 301 | preview = button "Preview" 302 | preview.click do 303 | BufferCheck($sample_rate_choice) 304 | Soxcommand = Soxpath + ' -r ' + $sample_rate_choice + ' -b 32 -L -e signed-integer --buffer ' + $soxbuffer + ' -p remix ' + sox_channels 305 | FFmpegSTART = Ffmpegpath + ' -channel_layout ' + ffmpeg_channels + ' -i - ' 306 | FFmpegPreview = '-f wav -c:a ' + 'pcm_s16le -dither_method triangular' + ' -ar ' + '44100' + ' -' 307 | FFplaycommand = Ffplaypath + ' -window_title "AudioRecorder" -f lavfi ' + '"' + 'amovie=\'pipe\:0\'' + ',' + FILTER_CHAIN + '"' 308 | ffmpegcommand = FFmpegSTART + FFmpegPreview 309 | command = Soxcommand + ' | ' + ffmpegcommand + ' | ' + FFplaycommand 310 | system(command) 311 | end 312 | 313 | record = button "Record" 314 | record.click do 315 | if ! defined? $record_iteration 316 | $record_iteration = 1 317 | else 318 | $record_iteration = $record_iteration + 1 319 | end 320 | if $outputdir.nil? 321 | $outputdir = '' 322 | end 323 | $waveform_pic = $outputdir + '/' + 'AUDIORECORDERTEMP' + $record_iteration.to_s + '.jpg' 324 | BufferCheck($sample_rate_choice) 325 | @tempfileoutput = '"' + $outputdir + '/' + $filename + '_temp.wav' + '"' 326 | @fileoutput = $outputdir + '/' + $filename + '.wav' 327 | if $filename == '' 328 | alert "Please enter an output file name!" 329 | elsif File.exist?(@fileoutput) 330 | alert "A File named #{$filename} already exists at that location!" 331 | elsif ! File.exist?($outputdir) 332 | alert "Please enter a valid output Directory!" 333 | else 334 | Soxcommand = Soxpath + ' -r ' + $sample_rate_choice + ' -b 32 -L -e signed-integer --buffer ' + $soxbuffer + ' -p remix ' + sox_channels 335 | FFmpegSTART = Ffmpegpath + ' -channel_layout ' + ffmpeg_channels + ' -i - ' 336 | FFmpegRECORD = '-f wav -c:a ' + $codec_choice + ' -dither_method triangular -ar ' + $sample_rate_choice + ' -metadata comment="" -y -rf64 auto ' + @tempfileoutput 337 | FFmpegPreview = ' -f wav -c:a ' + 'pcm_s16le -dither_method triangular' + ' -ar ' + '44100' + ' -' 338 | FFplaycommand = Ffplaypath + ' -window_title "AudioRecorder" -f lavfi ' + '"' + 'amovie=\'pipe\:0\'' + ',' + FILTER_CHAIN + '"' 339 | ffmpegcommand = FFmpegSTART + FFmpegRECORD + FFmpegPreview 340 | syscommand1 = Soxcommand + ' | ' + ffmpegcommand + ' | ' + FFplaycommand 341 | syscommand2 = Ffmpegpath + ' -i ' + @tempfileoutput + ' -lavfi showwavespic=split_channels=1:s=500x150:colors=blue -y ' + $waveform_pic + ' -c copy ' + "'" + @fileoutput + "'" + ' && rm ' + @tempfileoutput 342 | system(syscommand1) && system(syscommand2) 343 | if $embedbext == 'true' 344 | EmbedBEXT(@fileoutput) 345 | end 346 | PostRecord(@fileoutput) 347 | end 348 | end 349 | 350 | button "Edit BWF Metadata" do 351 | window(title: "Edit BWF Metadata", width: 600, height: 500) do 352 | background aliceblue 353 | stack do 354 | para "Please Make Selections" 355 | para "Originator" 356 | originator_choice = edit_line text = $originator do 357 | $originator = originator_choice.text 358 | end 359 | para "Coding History" 360 | history_choice = edit_box text = $history do 361 | $history = history_choice.text 362 | end 363 | flow do 364 | bextswitch = check 365 | if $embedbext == 'true' 366 | bextswitch.checked = true 367 | elsif $embedbext =='false' 368 | bextswitch.checked = false 369 | end 370 | para "Embed BFW Metadata?" 371 | bextswitch.click do 372 | if bextswitch.checked? 373 | $embedbext = 'true' 374 | else 375 | $embedbext = 'false' 376 | end 377 | end 378 | end 379 | button "Save Settings" do 380 | config['destination'] = $outputdir 381 | config['samplerate'] = $sample_rate_choice 382 | config['channels'] = sox_channels 383 | config['codec'] = $codec_choice 384 | config['orig'] = $originator 385 | config['hist'] = $history 386 | config['bext'] = $embedbext 387 | File.open(configuration_file, 'w') {|f| f.write config.to_yaml } 388 | close() 389 | end 390 | close = button "Cancel" 391 | close.click do 392 | close() 393 | end 394 | end 395 | end 396 | end 397 | 398 | button "Save Settings" do 399 | config['destination'] = $outputdir 400 | config['samplerate'] = $sample_rate_choice 401 | config['channels'] = sox_channels 402 | config['codec'] = $codec_choice 403 | config['orig'] = $originator 404 | config['hist'] = $history 405 | config['bext'] = $embedbext 406 | File.open(configuration_file, 'w') {|f| f.write config.to_yaml } 407 | end 408 | 409 | exit = button "Quit" 410 | exit.click do 411 | exit() 412 | end 413 | end 414 | end 415 | 416 | #Cascadia Now! 417 | -------------------------------------------------------------------------------- /current_interface.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/current_interface.gif -------------------------------------------------------------------------------- /history.md: -------------------------------------------------------------------------------- 1 | # Development History 2 | 3 | **2018-08-30:** audiorecorder2 added to homebrew install formula. Can now be installed with option `--with-audiorecorder2` 4 | 5 | **2018-03-15:** In development version 2 (in linux) tested with twenty minute recording at 24 bit/96 kHz stereo recording. File analyzed for interstitial errors using Wavelab Elements 9.5 global analysis with none detected. 6 | 7 | **2017-12-26:** Version 0.1.08 released. Mainly typo fixes. 8 | 9 | **2017-12-14:** Version 0.1.07 released. Fix bug. Stricter man page syntax. 10 | 11 | **2017-10-18:** Version 0.1.06 released. Improved help message and man page. 12 | 13 | **2017-10-01:** Version 0.1.05 released. Deletes BWF Metaedit from the package and uses its homebrew formula instead. 14 | 15 | **2017-08-22:** Version 0.1.04 released. Mainly doc fixes. 16 | 17 | **2017-06-05:** Version 0.1.03 released. Linux install instructions, man page and Code of Conduct have been added. 18 | 19 | **2017-04-25:** Version 0.1.02 released. Includes font support for filters in Linux. (Tested in Ubuntu 16.04). 20 | 21 | **2017-04-11:** Version 0.1.01 released. Contains some basic interface workarounds to decrease reliance on Pashua for user input. This makes audiorecorder able to run in a linux environment (further reliability testing for non macOS systems is underway). 22 | 23 | **2017-03-11:** Major redesign of post-digitization functions and GUI. Audiorecorder now supports trimming of start and end of files via manual specification as well as auto-trim of silence at the start of files. Preview and post-digitization GUI now incorporate waveforms of the digitized audio to aid in trimming. All testing has been negative for dropped samples. 24 | 25 | **2017-03-04:** For the new release changes have been made to streamline the way audiorecorder handles input channels. This will prevent it from being overwhelmed by A/D converters that output many empty tracks along with the desired tracks (such as the Apogee Symphony). In two 55 minute tests and one 30 minute no drops or problems were detected via Wavelab analysis. Due to more efficient management of data the current buffering now has increased the latency for mono captures. A next step will be to see how much buffering can be safely lowered. 26 | 27 | **2017-02-24:** Changes appear to have been successful for adapting audiorecorder to macOS 10.12.03. In testing it was discovered that audiorecorder has issues with A/D converters that have a large number of output tracks (such as sixteen as opposed to two). Current audiorecorder head appears to be stable for two track A/D converters and testing is underway to expand its ability to deal with larger multi-track converters. 28 | 29 | **2017-02-23:** Due to some issues that were reported with macOS 10.12.03, some changes to buffering have been made and are currently being tested. 30 | 31 | **2017-02-15:** Ability to select which channel to record has been added to the GUI. This option was tested with a 25 minute ingest on 2013 Macbook Air with no dropped samples detected audibly or vie Wavelab global analysis. Some glitches with file trimming were resolved. 32 | 33 | **2017-01-20:** Initial post digitization functions have been included via a basic GUI. These include ability to preview file with and without silence trimming, and an option to create a silence trimmed version of file. The spectrograph has also been changed to scroll for easier viewability. Further testing has not detected any dropped samples either audibly or via Wavelab global analysis. 34 | 35 | **2016-12-22:** Current build was tested with a 40 minute transfer on the 2013 Macbook Air with no dropped samples detected either audibly or via Wavelab's global analysis tool. Testing continues... 36 | -------------------------------------------------------------------------------- /macOS/audiorecorder2-osx.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/macOS/audiorecorder2-osx.tgz -------------------------------------------------------------------------------- /numbered_interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amiaopensource/audiorecorder/50f19e52057852cb95068dcd8543942a6ff1cffc/numbered_interface.png --------------------------------------------------------------------------------