Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
indiscipline committed Apr 9, 2023
0 parents commit 6d85622
Show file tree
Hide file tree
Showing 10 changed files with 1,521 additions and 0 deletions.
676 changes: 676 additions & 0 deletions LICENSE.md

Large diffs are not rendered by default.

62 changes: 62 additions & 0 deletions README.md
@@ -0,0 +1,62 @@
# Rearchiver <img src="rearchiver.svg" align="right" alt="Rearchiver logo" width="20%"/>
> [Third Hemisphere Studio](https://thirdhemisphere.studio) tooling
Rearchiver prepares your [Reaper](https://reaper.fm) project for archiving:

- Scans the project file for all used WAV files
- Converts WAV to FLAC and changes the links in the input RPP file accordingly
- Optionally deletes converted source files

Only WAV files supported by the `flac` binary can be converted. Currently, this means 32 bit floating point PCM files will be skipped and left uncompressed!

## Usage
Rearchiver relies on **[Flac](https://xiph.org/flac/download.html)** for conversions,
so the `flac` program must be present in your PATH or placed in the same directory as `rearchiver` executable.

By default the edited project file is written to the standard output, use redirection or `-o` to write to a file:

```
# redirect standard output to a file
> rearchiver INPUT.rpp > output.rpp
# write to a file
> rearchiver INPUT.rpp -o output2.rpp
```

Rearchiver is interactive, it will print the pairs of the found WAV files and the proposed names for the FLACs and will ask for your confirmation. Use `-y` to bypass the confirmation.

Additional options are available in help: `rearchiver --help`.

## Disclaimer
Rearchiver tries to be conservative and will not overwrite anything unless asked by the user. However, there might be bugs.\
This software is provided without any guarantees.

## Installation
Rearchiver is tested to work under Windows and GNU/Linux. Probably works on OSX with no changes.

Download a binary from the [release assets](https://github.com/indiscipline/rearchiver/releases/latest) or compile yourself.

### Building manually
Building requires the Nim compiler and a Nim package manager (such as Nimble) to resolve the dependencies.
Third-party libraries Rearchiver relies on: `fusion`, `threading`, `argparse`.
Use `choosenim` to install and manage the Nim compilation toolchain.

To install with Nimble:

```
nimble install https://github.com/indiscipline/rearchiver
rearchiver -h
```

## TODO:
- [] Add support for WavPack as an alternative codec / codec supporting 32 bit float WAV files
- [] Migrate to a proper pool for managing concurrent process execution

## Contributing
The project is open for contributions. Please, try to limit the scope of your changes.

Open an issue for bugs, ideas and feature requests.

## License
Rearchiver is licensed under GNU General Public License version 3.0 or later; See `LICENSE.md` for full details.

[Logo](rearchiver.svg) is licensed under Creative Commons Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
9 changes: 9 additions & 0 deletions config.nims
@@ -0,0 +1,9 @@
--mm:arc
--threads:on


when defined(release) or defined(danger):
--opt:speed
--passC:"-flto"
--passL:"-flto"
--passL:"-s"
339 changes: 339 additions & 0 deletions cozytaskpool/LICENSE

Large diffs are not rendered by default.

39 changes: 39 additions & 0 deletions cozytaskpool/README.md
@@ -0,0 +1,39 @@
# Cozy Task Pool

Just a repeating pattern of launching `tasks` concurrently in threads with `threading/channels`, extracted into its own micro package.
Not very ergonomic, just hides some of the boilerplate of preparing and tearing down everything.

**Requires** `--threads:on` and (`--mm:arc` or `--mm:orc`)

## Installation
Currently not in nimble directory.

```
nimble install https://github.com/indiscipline/cozytaskpool
```

## Usage
Look at the executable part of the source, which is almost the copy of the following block:

```nim
import std/[tasks], threading/channels
var
data = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61]
pool: CozyTaskPool = newTaskPool()
proc log(inputData: int) =
echo "Received some message about ", inputData
proc work(consumer: ptr Chan[Task]; inputData: int) =
doTheWork(inputData)
consumer[].send(toTask( log(inputData) ))
for x in data:
pool.sendTask(toTask( work(pool.resultsAddr(), x) ))
pool.stopPool()
```

## License
Cozy Task Pool is licensed under GNU General Public License version 2.0 or later;
10 changes: 10 additions & 0 deletions cozytaskpool/cozytaskpool.nimble
@@ -0,0 +1,10 @@
version = "0.1.0"
author = "Kirill I"
description = "Cozy Task pool"
license = "GPL-2.0-or-later"
srcDir = "src"


# Dependencies

requires "nim >= 1.6.12"
83 changes: 83 additions & 0 deletions cozytaskpool/src/cozytaskpool.nim
@@ -0,0 +1,83 @@
import std/[tasks, osproc], threading/channels

when not defined(gcArc) and not defined(gcOrc) and not defined(nimdoc):
{.error: "This package requires --mm:arc or --mm:orc".}

type
RunnerArgs = tuple[tasks: ptr Chan[Task], results: ptr Chan[Task]]
ConsumerArgs = tuple[results: ptr Chan[Task], nthreads: Positive]
CozyTaskPool* = object
nthreads: Positive
taskThreads: seq[Thread[RunnerArgs]]
consumerThread: Thread[ConsumerArgs]
tasks: Chan[Task]
results: Chan[Task]
StopFlag = object of CatchableError

proc stop() = raise newException(StopFlag, "")

proc runner(args: RunnerArgs) {.thread.} =
var t: Task
while true:
args.tasks[].recv(t)
try: t.invoke()
except StopFlag: break
args.results[].send(toTask(stop())) # notify consumer thread finished

proc consumer(args: ConsumerArgs) {.thread.} =
var activethreads: Natural = args.nthreads
var t: Task
while activethreads > 0:
args.results[].recv(t)
try: t.invoke()
except StopFlag: dec(activethreads)

func resultsAddr*(pool: CozyTaskPool): ptr Chan[Task] {.inline.} =
pool.results.addr

proc sendTask*(pool: var CozyTaskPool; task: sink Task) {.inline.} =
pool.tasks.send(isolate(task))

proc newTaskPool*(nthreads: Positive = countProcessors()): CozyTaskPool =
result.nthreads = nthreads
result.taskThreads = newSeq[Thread[RunnerArgs]](nthreads)
result.tasks = newChan[Task]()
result.results = newChan[Task]()
createThread(result.consumerThread, consumer, (result.results.addr, nthreads))
for ti in 0..high(result.taskThreads):
createThread(result.taskThreads[ti], runner, (result.tasks.addr, result.results.addr))
result

proc stopPool*(pool: var CozyTaskPool) =
for _ in pool.taskThreads: pool.tasks.send(toTask(stop()))
joinThreads(pool.taskThreads)
joinThread(pool.consumerThread)


when isMainModule:
import std/[tasks, os, unittest], threading/channels

var
data = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61]
checkset: set[byte] = {1, 2, 4, 6, 10, 12, 16, 18, 22, 28, 30, 36, 40, 42, 46, 52, 58, 60}
results: set[byte]

suite "Cozy Task Pool test suite":
setup:
var pool: CozyTaskPool = newTaskPool()

test "Test completion":
proc log(inputData: int) =
results.incl(inputData.byte)
# echo "Received some message about ", inputData

proc work(consumer: ptr Chan[Task]; inputData: int) =
sleep(100)
let r = inputData - 1
consumer[].send(toTask( log(r) ))

for x in data:
pool.sendTask(toTask( work(pool.resultsAddr(), x) ))

pool.stopPool()
check results == checkset
17 changes: 17 additions & 0 deletions rearchiver.nimble
@@ -0,0 +1,17 @@
from std/strformat import `&`

version = "0.1.0"
author = "Kirill I"
description = "Prepare your Reaper project for archiving, converting WAV to FLAC and changing the RPP file accordingly"
license = "GPL-3.0-or-later"
srcDir = "src"
bin = @["rearchiver"]


# Dependencies

requires "nim >= 1.6.6", "argparse >= 3.0.0", "fusion >= 1.1", "threading >= 0.1.0"

task release, "Build release":
let binName = bin[0] & (when defined(windows): ".exe" else: "")
exec(&"nim c --define:release --out:{binName} src/{bin[0]}.nim")
67 changes: 67 additions & 0 deletions rearchiver.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 6d85622

Please sign in to comment.