Skip to content

Commit 476bdf8

Browse files
committed
Add base recording capability for Unix-based OS
1 parent 500258b commit 476bdf8

11 files changed

Lines changed: 184 additions & 55 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace NetCoreAudio.Interfaces
5+
{
6+
public interface IRecorder
7+
{
8+
event EventHandler RecordingFinished;
9+
10+
bool Recording { get; }
11+
12+
Task Record(string fileName);
13+
Task Stop();
14+
}
15+
}

NetCoreAudio/NetCoreAudio.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<RepositoryUrl>https://github.com/mobiletechtracker/NetCoreAudio</RepositoryUrl>
1313
<RepositoryType>GitHub</RepositoryType>
1414
<PackageTags>.NET Core audio</PackageTags>
15-
<Version>1.8.0</Version>
15+
<Version>2.0.0</Version>
1616
<PackageIconUrl></PackageIconUrl>
1717
<PackageLicenseFile>LICENSE</PackageLicenseFile>
1818
<PackageIcon>icon.jpg</PackageIcon>
@@ -27,7 +27,7 @@
2727
<Pack>True</Pack>
2828
<PackagePath></PackagePath>
2929
</None>
30-
<None Include="..\README.md" Pack="true" PackagePath="\"/>
30+
<None Include="..\README.md" Pack="true" PackagePath="\" />
3131
</ItemGroup>
3232

3333
</Project>

NetCoreAudio/Players/LinuxPlayer.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NetCoreAudio.Interfaces;
2+
using NetCoreAudio.Utils;
23
using System;
34
using System.IO;
45
using System.Threading.Tasks;
@@ -22,9 +23,11 @@ protected override string GetBashCommand(string fileName)
2223
public override Task SetVolume(byte percent)
2324
{
2425
if (percent > 100)
25-
throw new ArgumentOutOfRangeException(nameof(percent), "Percent can't exceed 100");
26+
throw new ArgumentOutOfRangeException(
27+
nameof(percent), "Percent can't exceed 100");
2628

27-
var tempProcess = StartBashProcess($"amixer -M set 'Master' {percent}%");
29+
var tempProcess = BashUtil.StartBashProcess(
30+
$"amixer -M set 'Master' {percent}%");
2831
tempProcess.WaitForExit();
2932

3033
return Task.CompletedTask;

NetCoreAudio/Players/MacPlayer.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NetCoreAudio.Interfaces;
2+
using NetCoreAudio.Utils;
23
using System;
34
using System.Threading.Tasks;
45

@@ -14,9 +15,11 @@ protected override string GetBashCommand(string fileName)
1415
public override Task SetVolume(byte percent)
1516
{
1617
if (percent > 100)
17-
throw new ArgumentOutOfRangeException(nameof(percent), "Percent can't exceed 100");
18+
throw new ArgumentOutOfRangeException(
19+
nameof(percent), "Percent can't exceed 100");
1820

19-
var tempProcess = StartBashProcess($"osascript -e \"set volume output volume {percent}\"");
21+
var tempProcess = BashUtil.StartBashProcess(
22+
$"osascript -e \"set volume output volume {percent}\"");
2023
tempProcess.WaitForExit();
2124

2225
return Task.CompletedTask;

NetCoreAudio/Players/UnixPlayerBase.cs

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using NetCoreAudio.Interfaces;
2+
using NetCoreAudio.Utils;
23
using System;
34
using System.Diagnostics;
45
using System.Threading.Tasks;
@@ -24,7 +25,8 @@ public async Task Play(string fileName)
2425
{
2526
await Stop();
2627
var BashToolName = GetBashCommand(fileName);
27-
_process = StartBashProcess($"{BashToolName} '{fileName}'");
28+
_process = BashUtil.StartBashProcess(
29+
$"{BashToolName} '{fileName}'");
2830
_process.EnableRaisingEvents = true;
2931
_process.Exited += HandlePlaybackFinished;
3032
_process.ErrorDataReceived += HandlePlaybackFinished;
@@ -36,7 +38,8 @@ public Task Pause()
3638
{
3739
if (Playing && !Paused && _process != null)
3840
{
39-
var tempProcess = StartBashProcess(string.Format(PauseProcessCommand, _process.Id));
41+
var tempProcess = BashUtil.StartBashProcess(
42+
string.Format(PauseProcessCommand, _process.Id));
4043
tempProcess.WaitForExit();
4144
Paused = true;
4245
}
@@ -48,7 +51,8 @@ public Task Resume()
4851
{
4952
if (Playing && Paused && _process != null)
5053
{
51-
var tempProcess = StartBashProcess(string.Format(ResumeProcessCommand, _process.Id));
54+
var tempProcess = BashUtil.StartBashProcess(
55+
string.Format(ResumeProcessCommand, _process.Id));
5256
tempProcess.WaitForExit();
5357
Paused = false;
5458
}
@@ -71,26 +75,6 @@ public Task Stop()
7175
return Task.CompletedTask;
7276
}
7377

74-
protected Process StartBashProcess(string command)
75-
{
76-
var escapedArgs = command.Replace("\"", "\\\"");
77-
78-
var process = new Process()
79-
{
80-
StartInfo = new ProcessStartInfo
81-
{
82-
FileName = "/bin/bash",
83-
Arguments = $"-c \"{escapedArgs}\"",
84-
RedirectStandardOutput = true,
85-
RedirectStandardInput = true,
86-
UseShellExecute = false,
87-
CreateNoWindow = true,
88-
}
89-
};
90-
process.Start();
91-
return process;
92-
}
93-
9478
internal void HandlePlaybackFinished(object sender, EventArgs e)
9579
{
9680
if (Playing)

NetCoreAudio/Players/WindowsPlayer.cs

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,22 @@ namespace NetCoreAudio.Players
1212
internal class WindowsPlayer : IPlayer
1313
{
1414
[DllImport("winmm.dll")]
15-
private static extern int mciSendString(string command, StringBuilder stringReturn, int returnLength, IntPtr hwndCallback);
15+
private static extern int mciSendString(
16+
string command,
17+
StringBuilder stringReturn,
18+
int returnLength,
19+
IntPtr hwndCallback);
1620

1721
[DllImport("winmm.dll")]
18-
private static extern int mciGetErrorString(int errorCode, StringBuilder errorText, int errorTextSize);
22+
private static extern int mciGetErrorString(
23+
int errorCode,
24+
StringBuilder errorText,
25+
int errorTextSize);
1926

2027
[DllImport("winmm.dll")]
21-
public static extern int waveOutSetVolume(IntPtr hwo, uint dwVolume);
28+
public static extern int waveOutSetVolume(
29+
IntPtr hwo,
30+
uint dwVolume);
2231

2332
private Timer _playbackTimer;
2433
private Stopwatch _playStopwatch;
@@ -108,7 +117,8 @@ private Task ExecuteMsiCommand(string commandString)
108117

109118
if (result != 0)
110119
{
111-
var errorSb = new StringBuilder($"Error executing MCI command '{commandString}'. Error code: {result}.");
120+
var errorSb = new StringBuilder(
121+
$"Error executing MCI command '{commandString}'. Error code: {result}.");
112122
var sb2 = new StringBuilder(128);
113123

114124
mciGetErrorString(result, sb2, 128);
@@ -117,7 +127,9 @@ private Task ExecuteMsiCommand(string commandString)
117127
throw new Exception(errorSb.ToString());
118128
}
119129

120-
if (commandString.ToLower().StartsWith("status") && int.TryParse(sb.ToString(), out var length))
130+
if (commandString.ToLower()
131+
.StartsWith("status") &&
132+
int.TryParse(sb.ToString(), out var length))
121133
_playbackTimer.Interval = length;
122134

123135
return Task.CompletedTask;
@@ -128,7 +140,8 @@ public Task SetVolume(byte percent)
128140
// Calculate the volume that's being set
129141
int NewVolume = ushort.MaxValue / 100 * percent;
130142
// Set the same volume for both the left and the right channels
131-
uint NewVolumeAllChannels = ((uint)NewVolume & 0x0000ffff) | ((uint)NewVolume << 16);
143+
uint NewVolumeAllChannels =
144+
((uint)NewVolume & 0x0000ffff) | ((uint)NewVolume << 16);
132145
// Set the volume
133146
waveOutSetVolume(IntPtr.Zero, NewVolumeAllChannels);
134147

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace NetCoreAudio.Recorders
2+
{
3+
internal class LinuxRecorder : UnixRecorderBase
4+
{
5+
protected override string GetBashCommand(string fileName)
6+
{
7+
return $"ffmpeg -f avfoundation -i \":1\" {fileName}";
8+
}
9+
}
10+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System.IO;
2+
3+
namespace NetCoreAudio.Recorders
4+
{
5+
internal class MacRecorder : UnixRecorderBase
6+
{
7+
protected override string GetBashCommand(string fileName)
8+
{
9+
string command = $"arecord -vv --format=cd ";
10+
11+
if (Path.GetExtension(fileName).ToLower().Equals(".mp3"))
12+
{
13+
command += "--file-type raw | lame -r - ";
14+
}
15+
16+
return command += fileName;
17+
}
18+
}
19+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using NetCoreAudio.Interfaces;
2+
using NetCoreAudio.Utils;
3+
using System;
4+
using System.Diagnostics;
5+
using System.Threading.Tasks;
6+
7+
namespace NetCoreAudio.Recorders
8+
{
9+
internal abstract class UnixRecorderBase : IRecorder
10+
{
11+
private Process _process = null;
12+
13+
public event EventHandler RecordingFinished;
14+
15+
public bool Recording { get; set; }
16+
17+
protected abstract string GetBashCommand(string fileName);
18+
19+
public async Task Record(string filePath)
20+
{
21+
await Stop();
22+
var BashToolName = GetBashCommand(filePath);
23+
_process = BashUtil.StartBashProcess(
24+
$"{BashToolName} '{filePath}'");
25+
_process.EnableRaisingEvents = true;
26+
_process.Exited += HandleRecordingFinished;
27+
_process.ErrorDataReceived += HandleRecordingFinished;
28+
_process.Disposed += HandleRecordingFinished;
29+
Recording = true;
30+
}
31+
32+
public Task Stop()
33+
{
34+
if (_process != null)
35+
{
36+
_process.Kill();
37+
_process.Dispose();
38+
_process = null;
39+
}
40+
41+
Recording = false;
42+
43+
return Task.CompletedTask;
44+
}
45+
46+
internal void HandleRecordingFinished(object sender, EventArgs e)
47+
{
48+
if (Recording)
49+
{
50+
Recording = false;
51+
RecordingFinished?.Invoke(this, e);
52+
}
53+
}
54+
}
55+
}

NetCoreAudio/Utils/BashUtil.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System.Diagnostics;
2+
3+
namespace NetCoreAudio.Utils
4+
{
5+
internal static class BashUtil
6+
{
7+
public static Process StartBashProcess(string command)
8+
{
9+
var escapedArgs = command.Replace("\"", "\\\"");
10+
11+
var process = new Process()
12+
{
13+
StartInfo = new ProcessStartInfo
14+
{
15+
FileName = "/bin/bash",
16+
Arguments = $"-c \"{escapedArgs}\"",
17+
RedirectStandardOutput = true,
18+
RedirectStandardInput = true,
19+
UseShellExecute = false,
20+
CreateNoWindow = true,
21+
}
22+
};
23+
process.Start();
24+
return process;
25+
}
26+
}
27+
}

0 commit comments

Comments
 (0)