GitHub issue #3996: Audio.encode_example() throws an error when writing example from array
State: closed | Created: 2022-03-23T17:11:47Z

## Describe the bug
When trying to do `Audio().encode_example()` with preexisting array (see [this line](https://github.com/huggingface/datasets/blob/master/src/datasets/features/audio.py#L73)), `sf.write()` throws you an error:
`TypeError: No format specified and unable to get format from file extension: <_io.BytesIO object at 0x7f4218c0db30>`

## Steps to reproduce the bug
### Sample code to reproduce the bug
```python
# download sample file
!wget https://huggingface.co/datasets/polinaeterna/test_encode_example/resolve/main/common_voice_vi_21824030.mp3

arr, sr = librosa.load("common_voice_vi_21824030.mp3")

Audio().encode_example({
    "path": "common_voice_vi_21824030.mp3",
    "array": arr,
    "sampling_rate":sr
})
```

## Expected results
An encoded example (`{"bytes": b'....', "path": 'path'}`)

## Actual results
```python
TypeError                                 Traceback (most recent call last)
Input In [3], in <module>
      1 arr, sr = librosa.load("common_voice_vi_21824030.mp3")
----> 3 Audio().encode_example({
      4     "path": "common_voice_vi_21824030.mp3",
      5     "array": arr,
      6     "sampling_rate":sr
      7 })

File ~/workspace/datasets/src/datasets/features/audio.py:75, in Audio.encode_example(self, value)
     73 elif isinstance(value, dict) and "array" in value:
     74     buffer = BytesIO()
---> 75     sf.write(buffer, value["array"], value["sampling_rate"])
     76     return {"bytes": buffer.getvalue(), "path": value.get("path")}
     77 elif value.get("bytes") is not None or value.get("path") is not None:

File ~/miniconda3/envs/datasets/lib/python3.8/site-packages/soundfile.py:314, in write(file, data, samplerate, subtype, endian, format, closefd)
    312 else:
    313     channels = data.shape[1]
--> 314 with SoundFile(file, 'w', samplerate, channels,
    315                subtype, endian, format, closefd) as f:
    316     f.write(data)

File ~/miniconda3/envs/datasets/lib/python3.8/site-packages/soundfile.py:627, in SoundFile.__init__(self, file, mode, samplerate, channels, subtype, endian, format, closefd)
    625 mode_int = _check_mode(mode)
    626 self._mode = mode
--> 627 self._info = _create_info_struct(file, mode, samplerate, channels,
    628                                  format, subtype, endian)
    629 self._file = self._open(file, mode_int, closefd)
    630 if set(mode).issuperset('r+') and self.seekable():
    631     # Move write position to 0 (like in Python file objects)

File ~/miniconda3/envs/datasets/lib/python3.8/site-packages/soundfile.py:1416, in _create_info_struct(file, mode, samplerate, channels, format, subtype, endian)
   1414 original_format = format
   1415 if format is None:
-> 1416     format = _get_format_from_filename(file, mode)
   1417     assert isinstance(format, (_unicode, str))
   1418 else:

File ~/miniconda3/envs/datasets/lib/python3.8/site-packages/soundfile.py:1457, in _get_format_from_filename(file, mode)
   1455     pass
   1456 if format.upper() not in _formats and 'r' not in mode:
-> 1457     raise TypeError("No format specified and unable to get format from "
   1458                     "file extension: {0!r}".format(file))
   1459 return format

TypeError: No format specified and unable to get format from file extension: <_io.BytesIO object at 0x7fd8daf88180>
```

## Environment info
<!-- You can run the command `datasets-cli env` and copy-and-paste its output below. -->
- `datasets` version: datasets master
- Platform: Ubuntu 20.04
- Python version: python 3.8.12
- PyArrow version: 6.0.1

## Solution

I guess we just need to add `format` arg in [this line](https://github.com/huggingface/datasets/blob/master/src/datasets/features/audio.py#L75) like this:
```python
sf.write(buffer, value["array"], value["sampling_rate"], format="wav")
```


BTW discovered this when trying to decode audio in mp3 format without torchaudio (would be useful for TensorFlow users), like this:
```python
from datasets import load_dataset, Features, Audio

ds = load_dataset("common_voice", "vi", split="test")
ds = ds.remove_columns("audio")
ds.select(range(3))  # 3 samples just for testing

def load_mp3_with_librosa(example):
    arr, sr = librosa.load(example["path"])
    example["audio"] = {
        "path": example["path"],
        "array": arr,
        "sampling_rate": sr
    }
    return example

updated_dataset = ds.map(lambda example: load_mp3_with_librosa(example),
                         features=Features(
                             {"audio": Audio(decode=False)}
                         ))
```

@lhoestq @mariosasko @albertvillanova am I right in my logic? do we agree that we can set wav as the format? 🤗