提交 8545852f 编写于 作者: Z Zhang Yuexin 提交者: Krzysztof Wicher

Add unix sound device (#754)

* draft version

* resolve partial feedback

* resolve some feedback

* resolve feedback

* resolve some feedbacks
上级 fd81519f
......@@ -5,4 +5,5 @@
internal partial class Interop
{
private const string LibcLibrary = "libc";
private const string AlsaLibrary = "libasound";
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.InteropServices;
internal partial class Interop
{
[DllImport(AlsaLibrary)]
internal static extern IntPtr snd_strerror(int errnum);
// <param name="pcm">snd_pcm_t**</param>
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_open(out IntPtr pcm, string name, snd_pcm_stream_t stream, int mode);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_start(IntPtr pcm);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_pause(IntPtr pcm, int enable);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_resume(IntPtr pcm);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_drain(IntPtr pcm);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_drop(IntPtr pcm);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_close(IntPtr pcm);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_recover(IntPtr pcm, int err, int silent);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_writei(IntPtr pcm, IntPtr buffer, ulong size);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_readi(IntPtr pcm, IntPtr buffer, ulong size);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_set_params(IntPtr pcm, snd_pcm_format_t format, snd_pcm_access_t access, uint channels, uint rate, int soft_resample, uint latency);
// <param name="params">snd_pcm_hw_params_t**</param>
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_hw_params_malloc(out IntPtr @params);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_hw_params_any(IntPtr pcm, IntPtr @params);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_hw_params_set_access(IntPtr pcm, IntPtr @params, snd_pcm_access_t access);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_hw_params_set_format(IntPtr pcm, IntPtr @params, snd_pcm_format_t val);
// <param name="val">1 for mono and 2 for stereo</param>
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_hw_params_set_channels(IntPtr pcm, IntPtr @params, uint val);
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_pcm_hw_params_set_rate_near(IntPtr pcm, IntPtr @params, uint* val, int* dir);
[DllImport(AlsaLibrary)]
internal static extern int snd_pcm_hw_params(IntPtr pcm, IntPtr @params);
// <param name="frames">snd_pcm_uframes_t*</param>
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_pcm_hw_params_get_period_size(IntPtr @params, ulong* frames, int* dir);
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_pcm_hw_params_set_period_size_near(IntPtr pcm, IntPtr @params, ulong* frames, int* dir);
// <param name="mixer">snd_mixer_t**</param>
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_open(out IntPtr mixer, int mode);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_close(IntPtr mixer);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_attach(IntPtr mixer, string name);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_load(IntPtr mixer);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_register(IntPtr mixer, IntPtr options, IntPtr classp);
// <returns>snd_mixer_elem_t*</returns>
[DllImport(AlsaLibrary)]
internal static extern IntPtr snd_mixer_first_elem(IntPtr mixer);
[DllImport(AlsaLibrary)]
internal static extern IntPtr snd_mixer_elem_next(IntPtr elem);
// <param name="elem">snd_mixer_elem_t*</param>
[DllImport(AlsaLibrary)]
internal static extern string snd_mixer_selem_get_name(IntPtr elem);
[DllImport(AlsaLibrary)]
internal static extern void snd_mixer_selem_id_alloca(IntPtr ptr);
// <param name="mixer">snd_mixer_elem_t*</param>
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_mixer_selem_get_playback_volume(IntPtr elem, snd_mixer_selem_channel_id channel, long* value);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_playback_volume(IntPtr elem, snd_mixer_selem_channel_id channel, long value);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_playback_volume_all(IntPtr elem, long value);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_playback_switch_all(IntPtr elem, int value);
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_mixer_selem_get_playback_volume_range(IntPtr elem, long* min, long* max);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_playback_volume_range(IntPtr elem, long min, long max);
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_mixer_selem_get_capture_volume(IntPtr elem, snd_mixer_selem_channel_id channel, long* value);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_capture_volume(IntPtr elem, snd_mixer_selem_channel_id channel, long value);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_capture_volume_all(IntPtr elem, long value);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_capture_switch_all(IntPtr elem, int value);
[DllImport(AlsaLibrary)]
internal unsafe static extern int snd_mixer_selem_get_capture_volume_range(IntPtr elem, long* min, long* max);
[DllImport(AlsaLibrary)]
internal static extern int snd_mixer_selem_set_capture_volume_range(IntPtr elem, long min, long max);
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
internal enum snd_pcm_stream_t
{
SND_PCM_STREAM_PLAYBACK = 0,
SND_PCM_STREAM_CAPTURE = 1,
SND_PCM_STREAM_LAST = SND_PCM_STREAM_CAPTURE,
}
internal enum snd_pcm_format_t
{
SND_PCM_FORMAT_UNKNOWN = -1,
SND_PCM_FORMAT_S8 = 0,
SND_PCM_FORMAT_U8 = 1,
SND_PCM_FORMAT_S16_LE = 2,
SND_PCM_FORMAT_S16_BE = 3,
SND_PCM_FORMAT_U16_LE = 4,
SND_PCM_FORMAT_U16_BE = 5,
SND_PCM_FORMAT_S24_LE = 6,
SND_PCM_FORMAT_S24_BE = 7,
SND_PCM_FORMAT_U24_LE = 8,
SND_PCM_FORMAT_U24_BE = 9,
SND_PCM_FORMAT_S32_LE = 10,
SND_PCM_FORMAT_S32_BE = 11,
SND_PCM_FORMAT_U32_LE = 12,
SND_PCM_FORMAT_U32_BE = 13,
SND_PCM_FORMAT_FLOAT_LE = 14,
SND_PCM_FORMAT_FLOAT_BE = 15,
SND_PCM_FORMAT_FLOAT64_LE = 16,
SND_PCM_FORMAT_FLOAT64_BE = 17,
SND_PCM_FORMAT_IEC958_SUBFRAME_LE = 18,
SND_PCM_FORMAT_IEC958_SUBFRAME_BE = 19,
SND_PCM_FORMAT_MU_LAW = 20,
SND_PCM_FORMAT_A_LAW = 21,
SND_PCM_FORMAT_IMA_ADPCM = 22,
SND_PCM_FORMAT_MPEG = 23,
SND_PCM_FORMAT_GSM = 24,
SND_PCM_FORMAT_SPECIAL = 31,
SND_PCM_FORMAT_S24_3LE = 32,
SND_PCM_FORMAT_S24_3BE = 33,
SND_PCM_FORMAT_U24_3LE = 34,
SND_PCM_FORMAT_U24_3BE = 35,
SND_PCM_FORMAT_S20_3LE = 36,
SND_PCM_FORMAT_S20_3BE = 37,
SND_PCM_FORMAT_U20_3LE = 38,
SND_PCM_FORMAT_U20_3BE = 39,
SND_PCM_FORMAT_S18_3LE = 40,
SND_PCM_FORMAT_S18_3BE = 41,
SND_PCM_FORMAT_U18_3LE = 42,
SND_PCM_FORMAT_U18_3BE = 43,
SND_PCM_FORMAT_G723_24 = 44,
SND_PCM_FORMAT_G723_24_1B = 45,
SND_PCM_FORMAT_G723_40 = 46,
SND_PCM_FORMAT_G723_40_1B = 47,
SND_PCM_FORMAT_DSD_U8 = 48,
SND_PCM_FORMAT_DSD_U16_LE = 49,
SND_PCM_FORMAT_DSD_U32_LE = 50,
SND_PCM_FORMAT_DSD_U16_BE = 51,
SND_PCM_FORMAT_DSD_U32_BE = 52,
SND_PCM_FORMAT_LAST = SND_PCM_FORMAT_DSD_U32_BE,
}
internal enum snd_pcm_access_t
{
SND_PCM_ACCESS_MMAP_INTERLEAVED = 0,
SND_PCM_ACCESS_MMAP_NONINTERLEAVED = 1,
SND_PCM_ACCESS_MMAP_COMPLEX = 2,
SND_PCM_ACCESS_RW_INTERLEAVED = 3,
SND_PCM_ACCESS_RW_NONINTERLEAVED = 4,
SND_PCM_ACCESS_LAST = SND_PCM_ACCESS_RW_NONINTERLEAVED,
}
internal enum snd_mixer_selem_channel_id
{
SND_MIXER_SCHN_UNKNOWN = -1,
SND_MIXER_SCHN_FRONT_LEFT = 0,
SND_MIXER_SCHN_FRONT_RIGHT = 1,
SND_MIXER_SCHN_REAR_LEFT = 2,
SND_MIXER_SCHN_REAR_RIGHT = 3,
SND_MIXER_SCHN_FRONT_CENTER = 4,
SND_MIXER_SCHN_WOOFER = 5,
SND_MIXER_SCHN_SIDE_LEFT = 6,
SND_MIXER_SCHN_SIDE_RIGHT = 7,
SND_MIXER_SCHN_REAR_CENTER = 8,
SND_MIXER_SCHN_LAST = 31,
SND_MIXER_SCHN_MONO = SND_MIXER_SCHN_FRONT_LEFT
}
\ No newline at end of file
......@@ -15,6 +15,8 @@
<Compile Include="../Interop/**/*.cs" />
<Compile Include="VideoDevice/*.cs" />
<Compile Include="VideoDevice/Devices/*.cs" />
<Compile Include="SoundDevice/*.cs" />
<Compile Include="SoundDevice/Devices/*.cs" />
</ItemGroup>
</Project>
......@@ -14,3 +14,10 @@ The project currently includes `VideoDevice`.
```
sudo apt-get install libc6-dev libgdiplus libx11-dev
```
### SoundDevice
1. Install `libasound2-dev`.
```
sudo apt-get install libasound2-dev
```
# SoundDevice
## Getting Started
1. Create a `SoundConnectionSettings` and set the parameters for recording (Optional).
```C#
SoundConnectionSettings settings = new SoundConnectionSettings();
```
2. Create a communications channel to a sound device.
```C#
using SoundDevice device = SoundDevice.Create(settings);
```
3. Play a WAV file
```C#
device.Play("/home/pi/music.wav");
```
4. Record some sounds
```C#
// Record for 10 seconds and save in "/home/pi/record.wav"
device.Record(10, "/home/pi/record.wav");
```
\ No newline at end of file
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Media
{
/// <summary>
/// The connection settings of a sound device.
/// </summary>
public class SoundConnectionSettings
{
/// <summary>
/// The playback device name of the sound device is connected to.
/// </summary>
public string PlaybackDeviceName { get; set; } = "default";
/// <summary>
/// The recording device name of the sound device is connected to.
/// </summary>
public string RecordingDeviceName { get; set; } = "default";
/// <summary>
/// The mixer device name of the sound device is connected to.
/// </summary>
public string MixerDeviceName { get; set; } = "default";
/// <summary>
/// The sample rate of recording.
/// </summary>
public uint RecordingSampleRate { get; set; } = 8000;
/// <summary>
/// The channels of recording.
/// </summary>
public ushort RecordingChannels { get; set; } = 2;
/// <summary>
/// The bits per sample of recording.
/// </summary>
public ushort RecordingBitsPerSample { get; set; } = 16;
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
namespace Iot.Device.Media
{
/// <summary>
/// The communications channel to a sound device.
/// </summary>
public abstract partial class SoundDevice : IDisposable
{
/// <summary>
/// Create a communications channel to a sound device running on Unix.
/// </summary>
/// <param name="settings">The connection settings of a sound device.</param>
/// <returns>A communications channel to a sound device running on Unix.</returns>
public static SoundDevice Create(SoundConnectionSettings settings) => new UnixSoundDevice(settings);
/// <summary>
/// The connection settings of the sound device.
/// </summary>
public abstract SoundConnectionSettings Settings { get; }
/// <summary>
/// The playback volume of the sound device.
/// </summary>
public abstract long PlaybackVolume { get; set; }
/// <summary>
/// The playback mute of the sound device.
/// </summary>
public abstract bool PlaybackMute { get; set; }
/// <summary>
/// The recording volume of the sound device.
/// </summary>
public abstract long RecordingVolume { get; set; }
/// <summary>
/// The recording mute of the sound device.
/// </summary>
public abstract bool RecordingMute { get; set; }
/// <summary>
/// Play WAV file.
/// </summary>
/// <param name="wavPath">WAV file path.</param>
public abstract void Play(string wavPath);
/// <summary>
/// Play WAV file.
/// </summary>
/// <param name="wavStream">WAV stream.</param>
public abstract void Play(Stream wavStream);
/// <summary>
/// Sound recording.
/// </summary>
/// <param name="recordTimeSeconds">Recording duration(In seconds).</param>
/// <param name="outputFilePath">Recording save path.</param>
public abstract void Record(uint recordTimeSeconds, string outputFilePath);
/// <summary>
/// Sound recording.
/// </summary>
/// <param name="recordTimeSeconds">Recording duration(In seconds).</param>
/// <param name="outputStream">Recording save stream.</param>
public abstract void Record(uint recordTimeSeconds, Stream outputStream);
/// <inheritdoc/>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the SoundDevice and optionally releases the managed resources.
/// </summary>
/// <param name="disposing">True to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing) { }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Media
{
internal struct WavHeader
{
public WavHeaderChunk Chunk { get; set; }
public char[] Format { get; set; }
public WavHeaderChunk SubChunk1 { get; set; }
public ushort AudioFormat { get; set; }
public ushort NumChannels { get; set; }
public uint SampleRate { get; set; }
public uint ByteRate { get; set; }
public ushort BlockAlign { get; set; }
public ushort BitsPerSample { get; set; }
public WavHeaderChunk SubChunk2 { get; set; }
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace Iot.Device.Media
{
internal struct WavHeaderChunk
{
public char[] ChunkId { get; set; }
public uint ChunkSize { get; set; }
}
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../Media.csproj" />
</ItemGroup>
</Project>
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.IO;
using Iot.Device.Media;
namespace Alsa.Samples
{
class Program
{
static void Main(string[] args)
{
SoundConnectionSettings settings = new SoundConnectionSettings();
using SoundDevice device = SoundDevice.Create(settings);
string path = Directory.GetCurrentDirectory();
Console.WriteLine("Recording...");
device.Record(10, $"{path}/record.wav");
Console.WriteLine("Playing...");
device.Play($"{path}/record.wav");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册