{"id":22918258,"url":"https://github.com/candera/podcastifier","last_synced_at":"2025-05-12T17:47:23.226Z","repository":{"id":7203975,"uuid":"8508958","full_name":"candera/podcastifier","owner":"candera","description":"Process a pile of wav files into something like a final podcast episode.","archived":false,"fork":false,"pushed_at":"2017-02-06T07:45:49.000Z","size":378,"stargazers_count":11,"open_issues_count":3,"forks_count":4,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-06T17:07:56.238Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/candera.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-03-01T19:46:46.000Z","updated_at":"2019-11-05T21:55:07.000Z","dependencies_parsed_at":"2022-08-24T20:02:12.045Z","dependency_job_id":null,"html_url":"https://github.com/candera/podcastifier","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Fpodcastifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Fpodcastifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Fpodcastifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/candera%2Fpodcastifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/candera","download_url":"https://codeload.github.com/candera/podcastifier/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253789966,"owners_count":21964776,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-14T06:29:41.207Z","updated_at":"2025-05-12T17:47:21.844Z","avatar_url":"https://github.com/candera.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# podcastifier\n\nPodcastifier is a program for processing and assembling sound files. As you\nprobably can guess from the name, it mostly intended to automate the assembly\nof podcasts. With Podcastifier you can automate assembling a final audio file\nfrom separate sources. You might, for example, assemble a podcast from two\nmp3 files -- one each for intro and outro music -- along with a wav file\ncontaining the actual podcast content. As it creates the final sound file\nPodcastifier can mix sound files and do fades or ducking, all under control\nof a configuration file that you supply.\n\n## Installation\n\nYou can build the Poscastifier jar file with lein:\n\n    $ lein uberjar\n\n## Usage\n\nTo use Poscastifier, just run the jar file as a Java application,\npassing in the config file as the only argument:\n\n    $ java -jar podcastifier-0.1.0-standalone.jar config-file\n\n\n### Configuration File\n\nPodcastifier takes a (EDN)[] configuration file, containing a map.\nThis map needs to have at least the following four entries:\n * :version\n * :output-file\n * :sounds\n * :final\n\nIn addition to these four entries, you can have any other entries\nthat you find useful in the configuration map.\n\n\n#### The Basics\n\nThe `:version` entry just identifies the Podcastifier version and\nshould be set to `7`:\n\n````edn\n{:version 7\n  ;; More to come!}\n````\n\nThe `:output-file` entry specifies where the final sound file will\nbe written. This can be a simple string:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n  ;; More to come!}\n````\n\nOr it can be a vector containing a Clojure format string and associated\nvalues. Any keywords specified in the vector get looked up in the\nconfiguration map, which makes this sort of thing possible:\n\n```edn\n{:version     7\n :show-name   \"mypodcast\"\n :label       \"mary-smith\"\n :number      66\n :output-file [\"%s-%03d-%s.wav\" :show-name  :number  :label]\n ;; Rest ommitted}\n```\n\nThe `:sounds` entry is itself a map of keywords to values and defines\nthe individual sounds that are processed by Podcastifier. If the\nvalue of an `:sounds` entry is a string, Podcastifier will read the\nfile specified by the string. So if you have this:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {:interview \"skype.wav\" :music \"theme.mp3\"}\n ;; More to come!}\n````\n\nThen podcastifier will know about two sound samples, one read from\na wave file with the identifier `:interview` and the other read\nfrom an MP3 file with the identifier `:music`.\n\nAside from `:sounds`, your config file will also need a `:final`\nentry. The value of `:final` should be the ID of a sound and indicates\nwhat sound should be written out. Thus if you simply wanted\nPodcastifier to read in `skype.wav` and write it back out\nto `output.wav` you would have something like this:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {:interview \"skype.wav\"}\n :final :interview}\n````\n\n#### Processing Sounds\n\nNow, Podcastifier would be pretty limited if all you could specify in\nthe `:sounds` part of the file were audio files to read. In fact, you\ncan do a lot more than that: The way to look at the values in the `:sounds`\nmap is as expressions that *evaluate to sound samples*. Strings, as we have\nseen, evaluate to the sound sample read from the file specified by the\nstring. If, on the other hand, you put a vector in as your value, perhaps like this:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {:podcast [\"music.wav\" \"interview.wav\"]}\n :final :podcast}\n````\n\nThen the resulting sound will be all of the sound samples in the vector concatenated\ntogether. Thus, in the example above,  the sound associated with `:podcast` will\nbe the theme music followed immediately by the interview.\n\nA keyword, in turn, evaluates to the sound associated with that keyword, so we could\nrecast our last example as:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {:music \"music.wav\" :podcast [:music \"interview.wav\"]}\n :final :podcast}\n````\n\nIf instead of a vector you supply a set, your sounds will get mixed instead of appended:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {:interview \"skype.wav\"\n          :background-music \"background.mp3\"\n          :podcast #{:interview :background-music}   ;; Mix in the background music.\n :final :podcast}\n````\n\nThings get really interesting if you use a map as your sound value.\nWith a map you can specify all sorts of interesting audio processing.  For example,\nif you wanted have your music fade in and out instead abruptly starting and stopping,\nyou might say this:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {\n   :interview \"skype.wav\"\n   :intro-music {:source \"theme.mp3\" :fade-out #duration 10.0}\n   :outro-music {:source \"theme.mp3\" :fade-in #duration 10.0}\n   :podcast [:intro-music :interview]}\n :final :podcast}\n````\n\nAlternatively you could just use the first 20 seconds of the music\nfor your intro and the last 30 seconds for the outro:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {\n   :interview \"skype.wav\"\n   :intro-music {:source \"theme.mp3\" :end #duration 20.0 :fade-out #duration 10.0}\n   :outro-music {:source \"theme.mp3\" :tail #duration 30.0 :fade-in #duration 10.0}\n   :podcast [:intro-music :interview :outro-music]}\n :final :podcast}\n````\n\nHere are all of the things that you can specify in a sound map:\n * :source The sound to start with, can be any valid sound spefication.\n * :fade-in \u003cduration\u003e Fade in the sound over the given duration.\n * :fade-out \u003cduration\u003e Fade the sound out over the given duration\n * :start \u003cduration\u003e :end \u003cduration\u003e Use the bit of the sound between the given times.\n * :duration \u003cduration\u003e Use only the given duration of sound, starting from the beginning.\n * :tail \u003cduration\u003e Use only the given duration of sound, but from the end.\n * :gain \u003cdb\u003e Apply the gain. A positive gain amplifies the sound, negative quiets it.\n * :end-pad \u003cduration\u003e Extend the end of the sound by \u003cduration\u003e.\n * :start-pad \u003cduration\u003e Extend the beginning of the sound by \u003cduration\u003e.\n * :pre-silence \u003cduration\u003e Add \u003cduration\u003e of silence to the beginning of the sound.\n * :post-silence \u003cduration\u003e Add \u003cduration\u003e of silence to the beginning of the sound.\n * :normalize true Normalize the sound sample.\n * :pan \u003cpan-value\u003e Mixes the first two tracks of the sound into a stereo track with the given stereo separation.\n\nA key part of of specifying sounds as a map is that the `:source` can be any\nsound specification. Put a string in the `:source` slot and\nPodcastifier will read the sound from the given file.\nPut a map inside of your map and Podcastifier will do two layers\nof audio processing. Specify a keyword and\nPodcastifier will recursively look up the key in the `:sounds` map.\n\nThus if you weren't happy with the gain in your podcast theme, you could do\nsomething like this:\n\n````edn\n{:version 7\n :output-file \"output.wav\"\n :sounds {\n  :interview \"skype.wav\"\n  :music {:source \"theme.mp3\" :gain #db 2.0}\n  :intro-music {:source :music :end #duration 20.0 :fade-out #duration 10.0}\n  :outro-music {:source :music :tail #duration 30.0 :fade-in #duration 10.0}\n  :podcast [:intro-music :interview :intro-music]}\n :final :podcast}\n```\n\nFinally, you can also specify some additional processing with a list. For example,\nyou can do a nice  overlapping fade out like this:\n\n````edn\n  :music+interview (fade-out-music :music\n                                   :interview\n                                   :overlap #duration 35.0\n                                   :fade #db -9\n                                   :updown #duration 4.0)\n````\n\nOr a similar fade in:\n\n\n````edn\n  :music+interview (fade-out-music :interview\n                                   :music\n                                   :overlap #duration 35.0\n                                   :fade #db -9\n                                   :updown #duration 4.0)\n````\n\nYou can also \"duck\" one sound over the other like this:\n\n````edn\n  :voice-over (duck :music :voice :offset #duration 55.0 :fade #db -9 :updown #duration 4.0)\n````\n\nTo can also add a muted background sound, like this:\n\n````edn\n :voice+background (background voice bg :extra #duration 7.0)\n````\n\nThe `background` option will mix the two tracks together but fade the 2nd\n(background) track just after first track ends. You can specify the\nlength of time that the background extends beyond the foreground\nwith the `:extra` option.\n\n\n### Bugs\n\n\n## License\n\nCopyright © 2013-2015 Craig Andera and Russ Olsen\n\nDistributed under the Eclipse Public License, the same as Clojure.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcandera%2Fpodcastifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcandera%2Fpodcastifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcandera%2Fpodcastifier/lists"}