Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 28 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,9 @@ Wavesync is a Ruby-based CLI tool that scans your music library and automaticall

## Supported devices

Out of the box, Wavesync supports:

- teenage engineering TP-7
- Elektron Octatrack MKII

Custom devices can be added via a YAML configuration file.

## Supported file types

Wavesync supports the following file types in your source library:
Expand All @@ -35,47 +31,44 @@ brew install taglib

https://teenage.engineering/guides/fieldkit

## Usage
## Configuration

### Command-line options
Wavesync is configured via a YAML file. By default it looks for `~/wavesync.yml`. You can also pass a path explicitly with the `-c` flag.

- `-s, --source PATH`: Path to your source music library
- `-t, --target PATH`: Path to the target sync directory
- `-d, --device DEVICE_MODEL`: Target device model (TP-7 or Octatrack)
- `-c, --config PATH`: Path to custom device configuration YAML file (optional)
### wavesync.yml format

### Examples

Sync to TP-7:
```bash
./bin/wavesync -s ~/Music/Library -t /Users/username/Library/Containers/engineering.teenage.fieldkit/Data/Documents/TP-7\ MTP\ Device-F1ELN21A/library -d TP-7
```yaml
library: ~/Music/Library
devices:
- name: TP-7
model: TP-7
path: ~/Library/Containers/engineering.teenage.fieldkit/Data/Documents/TP-7 MTP Device-F1ELN21A/library
- name: Octatrack
model: Octatrack
path: /Volumes/OCTATRACK/LIBRARY/AUDIO
```

Sync to Octatrack:
```bash
./bin/wavesync -s ~/Music/Library -t /Volumes/OCTATRACK/LIBRARY/AUDIO -d Octatrack
```
- `library`: path to your source music library
- `devices`: list of devices to sync to, each with:
- `name`: a label for this device, used with the `-d` command-line option
- `model`: device model (`TP-7` or `Octatrack`)
- `path`: path to the device's library directory

## Custom device configuration
Wavesync will exit with an error if a device model in the config is not supported.

Create a YAML file to define custom devices:
## Usage

```yaml
devices:
- name: MyCustomDevice
sample_rates:
- 44100
- 48000
file_types:
- wav
- mp3
```
```bash
# Use the default config at ~/wavesync.yml
wavesync

Then use it with the `-c` flag:
# Use a config at a specific path
wavesync -c /path/to/wavesync.yml

```bash
wavesync -s ~/Music -t /Volumes/DEVICE -d MyCustomDevice -c path/to/config.yml
# Sync to a specific device only (by name as defined in config)
wavesync -d Octatrack
```

## Sample Rate Selection

When a source file's sample rate isn't supported by the target device, Wavesync selects the closest supported rate. For files with equal distance to two rates, it chooses the higher rate to minimize quality loss.
Expand All @@ -88,4 +81,4 @@ Example: If a 96kHz file is synced to an Octatrack (which only supports 44.1kHz)

```bash
rake test
```
```
1 change: 1 addition & 0 deletions lib/wavesync.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ module Wavesync

require 'wavesync/acid_chunk'
require 'wavesync/audio'
require 'wavesync/config'
require 'wavesync/device'
require 'wavesync/ui'
require 'wavesync/path_resolver'
Expand Down
44 changes: 23 additions & 21 deletions lib/wavesync/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,41 +9,43 @@ def self.start
parser = OptionParser.new do |opts|
opts.banner = 'Usage: wavesync [options]'

opts.on('-s', '--source PATH', 'Source music library') do |v|
options[:source] = v
opts.on('-d', '--device NAME', 'Name of device to sync (as defined in config)') do |value|
options[:device] = value
end

opts.on('-t', '--target PATH', 'Target sync directory') do |v|
options[:target] = v
end

opts.on('-d', '--device DEVICE_MODEL', 'Target device model (Octatrack or TP-7)') do |v|
options[:device] = v
end

opts.on('-c', '--config PATH', 'Path to device config YAML file') do |v|
options[:config] = v
opts.on('-c', '--config PATH', 'Path to wavesync config YAML file') do |value|
options[:config] = value
end
end

parser.parse!

Wavesync::Device.configure(path: options[:config]) if options[:config]
config_path = options[:config] || Wavesync::Config::DEFAULT_PATH
config = Wavesync::Config.load(config_path)

unless options[:source] && options[:target] && options[:device]
puts parser
exit 1
device_configs = config.device_configs
if options[:device]
device_configs = device_configs.select { |device_config| device_config[:name] == options[:device] }
if device_configs.empty?
known = config.device_configs.map { |device_config| device_config[:name] }.join(', ')
puts "Unknown device \"#{options[:device]}\". Devices in config: #{known}"
exit 1
end
end

device = Wavesync::Device.find_by(name: options[:device])
device_configs.each do |device_config|
next if Wavesync::Device.find_by(name: device_config[:model])

unless device
puts "Device #{options[:device]} does not exist."
supported = Wavesync::Device.all.map(&:name).join(', ')
puts "Unknown device model \"#{device_config[:model]}\" in config. Supported models: #{supported}"
exit 1
end

scanner = Wavesync::Scanner.new(options[:source])
scanner.sync(options[:target], device)
scanner = Wavesync::Scanner.new(config.library)

device_configs.each do |device_config|
scanner.sync(device_config[:path], Wavesync::Device.find_by(name: device_config[:model]))
end
end
end
end
23 changes: 23 additions & 0 deletions lib/wavesync/config.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# frozen_string_literal: true

require 'yaml'

module Wavesync
class Config
DEFAULT_PATH = File.join(Dir.home, 'wavesync.yml')

attr_reader :library, :device_configs

def self.load(path = DEFAULT_PATH)
data = YAML.load_file(File.expand_path(path))
new(data)
end

def initialize(data)
@library = File.expand_path(data.fetch('library'))
@device_configs = data.fetch('devices').map do |device|
{ name: device['name'], model: device['model'], path: File.expand_path(device['path']) }
end
end
end
end
11 changes: 1 addition & 10 deletions lib/wavesync/device.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,8 @@ def initialize(name:, sample_rates:, bit_depths:, file_types:, bpm_source: nil)
@bpm_source = bpm_source
end

class << self
attr_writer :config_path
end

def self.config_path
@config_path ||= File.expand_path('../../config/devices.yml', __dir__)
end

def self.configure(path:)
self.config_path = path
@all = nil
File.expand_path('../../config/devices.yml', __dir__)
end

def self.all
Expand Down
10 changes: 0 additions & 10 deletions test/wavesync/device_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,6 @@

module Wavesync
class DeviceTest < Wavesync::TestCase
CONFIG_PATH = File.expand_path('../../config/devices.yml', __dir__)

def setup
Device.configure(path: CONFIG_PATH)
end

test 'initialization sets attributes' do
device = Device.new(
name: 'Test',
Expand All @@ -25,10 +19,6 @@ def setup
assert_equal [16], device.bit_depths
end

test '#config_path returns config path' do
assert_equal CONFIG_PATH, Device.config_path
end

test '#all loads devices from YAML' do
devices = Device.all
assert_equal 2, devices.size
Expand Down
3 changes: 0 additions & 3 deletions test/wavesync/path_resolver_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@

module Wavesync
class PathResolverTest < Wavesync::TestCase
CONFIG_PATH = File.expand_path('../../config/devices.yml', __dir__)

def setup
Device.configure(path: CONFIG_PATH)
@source_library = '/home/user/music'
@target_library = '/media/device/music'
end
Expand Down