Source code for music21.audioSearch.recording
# ------------------------------------------------------------------------------
# Name: audioSearch.recording.py
# Purpose: routines for making recordings from microphone input
#
# Authors: Jordi Bartolome
# Michael Scott Asato Cuthbert
#
# Copyright: Copyright © 2011-25 Michael Scott Asato Cuthbert
# License: BSD, see license.txt
# ------------------------------------------------------------------------------
'''
modules for audio searching that directly record from the microphone.
Requires PyAudio and portaudio to be installed (https://portaudio.com/download.html)
Windows users will get pyaudio and portaudio with `pip install pyaudio`
macOS users should have Homebrew installed and run `brew install portaudio`
before running `pip install pyaudio`
There is no official support for Linux/BSD etc. in music21, but package managers like `apt`
tend to have libraries like `portaudio19` and `python3-pyaudio`.
'''
from __future__ import annotations
from importlib.util import find_spec
import unittest
import wave
from music21.common.types import DocOrder
from music21 import environment
from music21 import exceptions21
environLocal = environment.Environment('audioSearch.recording')
default_recordChannels = 1
default_recordSampleRate = 44100
default_recordChunkLength = 1024
[docs]
def samplesFromRecording(seconds=10.0, storeFile=True,
recordFormat=None,
recordChannels=default_recordChannels,
recordSampleRate=default_recordSampleRate,
recordChunkLength=default_recordChunkLength): # pragma: no cover
'''
records `seconds` length of sound in the given format (default Wave)
and optionally stores it to disk using the filename of `storeFile`
Returns a list of samples.
'''
# noinspection PyPackageRequirements
import pyaudio # type: ignore # pylint: disable=import-error
recordFormatDefault = pyaudio.paInt16
if recordFormat is None:
recordFormat = recordFormatDefault
if recordFormat == pyaudio.paInt8:
raise RecordingException('cannot perform samplesFromRecording on 8-bit samples')
p_audio = pyaudio.PyAudio()
st = p_audio.open(format=recordFormat,
channels=recordChannels,
rate=recordSampleRate,
input=True,
frames_per_buffer=recordChunkLength)
recordingLength = int(recordSampleRate * float(seconds) / recordChunkLength)
storedWaveSampleList = []
# time_start = time.time()
for i in range(recordingLength):
data = st.read(recordChunkLength)
storedWaveSampleList.append(data)
st.close()
p_audio.terminate()
if storeFile is not False:
if isinstance(storeFile, str):
waveFilename = storeFile
else:
waveFilename = str(environLocal.getRootTempDir() / 'recordingTemp.wav')
# write recording to disk
data = b''.join(storedWaveSampleList)
try:
# wave.open does not take a path-like object as of 3.12
wf = wave.open(waveFilename, 'wb')
wf.setnchannels(recordChannels)
wf.setsampwidth(p_audio.get_sample_size(recordFormat))
wf.setframerate(recordSampleRate)
wf.writeframes(data)
wf.close()
except IOError:
raise RecordingException(f'Cannot open {waveFilename} for writing.')
return storedWaveSampleList
class RecordingException(exceptions21.Music21Exception):
pass
# -----------------------------------------
class Test(unittest.TestCase):
pass
class TestExternal(unittest.TestCase): # pragma: no cover
loader = find_spec('pyaudio')
if loader is not None: # pragma: no cover
pyaudio_installed = True
else:
pyaudio_installed = False
@unittest.skipUnless(pyaudio_installed, 'pyaudio must be installed')
def testRecording(self):
'''
record one second of data and print 10 records
'''
sampleList = samplesFromRecording(seconds=1, storeFile=False)
print(sampleList[30:40])
# ------------------------------------------------------------------------------
# define presented order in documentation
_DOC_ORDER: DocOrder = []
if __name__ == '__main__':
import music21
music21.mainTest(Test)