aaru-dps / RedBookPlayer

Compact Disc Digital Audio player
8 stars 1 forks source link

[Feature Request] Audio Extraction #20

Closed mnadareski closed 3 years ago

mnadareski commented 3 years ago

Export audio to WAV and/or FLAC

mnadareski commented 3 years ago

Will likely rely on https://github.com/aaru-dps/Aaru/issues/353

mnadareski commented 3 years ago

Potential solution is using CSCore's WAV output.

OpticalDiscBase.cs:

/// <summary>
/// Extract a track to WAV
/// </summary>
/// <param name="trackNumber">Track number to extract</param>
/// <param name="outputDirectory">Output path to write data to</param
public abstract void ExtractTrackToWav(uint trackNumber, string outputDirectory);

/// <summary>
/// Extract all tracks to WAV
/// </summary>
/// <param name="outputDirectory">Output path to write data to</param
public abstract void ExtractAllTracksToWav(string outputDirectory);

CompactDisc.cs:

/// <inheritdoc/>
public override void ExtractTrackToWav(uint trackNumber, string outputDirectory)
{
    if(_image == null)
        return;

    // Get the track with that value, if possible
    Track track = _image.Tracks.FirstOrDefault(t => t.TrackSequence == trackNumber);

    // If the track isn't valid, we can't do anything
    if(track == null || track.TrackType != TrackType.Audio)
        return;

    // Read in the track data to a buffer
    uint length = (uint)(track.TrackEndSector - track.TrackStartSector);
    byte[] buffer = _image.ReadSectors(track.TrackStartSector, length);

    // Build the WAV output
    string filename = Path.Combine(outputDirectory, $"Track {trackNumber.ToString().PadLeft(2, '0')}.wav");
    using(WaveWriter waveWriter = new WaveWriter(filename, new CSCore.WaveFormat()))
    {
        // Write out to the file
        waveWriter.Write(buffer, 0, buffer.Length);
    }
}

/// <inheritdoc/>
public override void ExtractAllTracksToWav(string outputDirectory)
{
    if(_image == null)
        return;

    foreach(Track track in _image.Tracks)
    {
        ExtractTrackToWav(track.TrackSequence, outputDirectory);
    }
}

Player.cs:

/// <summary>
/// Extract a single track from the image to WAV
/// </summary>
/// <param name="trackNumber"></param>
/// <param name="outputDirectory">Output path to write data to</param
public void ExtractSingleTrackToWav(uint trackNumber, string outputDirectory) => _opticalDisc?.ExtractTrackToWav(trackNumber, outputDirectory);

/// <summary>
/// Extract all tracks from the image to WAV
/// </summary>
/// <param name="outputDirectory">Output path to write data to</param
public void ExtractAllTracksToWav(string outputDirectory) => _opticalDisc?.ExtractAllTracksToWav(outputDirectory);

PlayerViewModel.cs:

/// <summary>
/// Extract a single track from the image to WAV
/// </summary>
/// <param name="trackNumber"></param>
/// <param name="outputDirectory">Output path to write data to</param
public void ExtractSingleTrackToWav(uint trackNumber, string outputDirectory) => _player?.ExtractSingleTrackToWav(trackNumber, outputDirectory);

/// <summary>
/// Extract all tracks from the image to WAV
/// </summary>
/// <param name="outputDirectory">Output path to write data to</param
public void ExtractAllTracksToWav(string outputDirectory) => _player?.ExtractAllTracksToWav(outputDirectory);

MainViewModel.cs:ExecuteKeyPress (change to async):

// Save track(s)
else if (e.Key == App.Settings.SaveTrackKey)
{
    if(PlayerView?.ViewModel == null)
        return;

    var dialog = new OpenFolderDialog();
    string path = await dialog.ShowAsync(App.MainWindow);
    if(string.IsNullOrWhiteSpace(path))
        return;

    if(e.KeyModifiers.HasFlag(KeyModifiers.Shift))
        PlayerView.ViewModel.ExtractAllTracksToWav(path);
    else
        PlayerView.ViewModel.ExtractSingleTrackToWav((uint)PlayerView.ViewModel.CurrentTrackNumber, path);
}

SettingsViewModel.cs (after LoadImageKey):

/// <summary>
/// Key assigned to save the current track or all tracks
/// </summary>
public Key SaveTrackKey { get; set; } = Key.S;

SettingsWindow.xaml (after Load Image):

<!-- Save Track -->
<TextBlock Grid.Row="1" Grid.Column="0" Width="120">Save Track(s)</TextBlock>
<ComboBox Grid.Row="1" Grid.Column="1" Name="SaveTrackKeyBind"
          Items="{Binding KeyboardList}" SelectedItem="{Binding SaveTrackKey, Mode=TwoWay}"
          HorizontalAlignment="Right" Margin="8,0,0,0" Width="120"/>