@@ -5,14 +5,20 @@ This program is free software: you can redistribute it and/or modify it under th
55*/
66
77import { Note , Interval , Scale } from '@tonaljs/tonal' ;
8- import { register , _mod } from '@strudel.cycles/core' ;
8+ import { register , _mod , silence , logger , pure , isNote } from '@strudel.cycles/core' ;
99
1010const octavesInterval = ( octaves ) => ( octaves <= 0 ? - 1 : 1 ) + octaves * 7 + 'P' ;
1111
1212function scaleStep ( step , scale ) {
1313 scale = scale . replaceAll ( ':' , ' ' ) ;
1414 step = Math . ceil ( step ) ;
15- const { intervals, tonic } = Scale . get ( scale ) ;
15+ let { intervals, tonic, empty } = Scale . get ( scale ) ;
16+ if ( ( empty && isNote ( scale ) ) || ( ! empty && ! tonic ) ) {
17+ throw new Error ( `incomplete scale. Make sure to use ":" instead of spaces, example: .scale("C:major")` ) ;
18+ } else if ( empty ) {
19+ throw new Error ( `invalid scale "${ scale } "` ) ;
20+ }
21+ tonic = tonic || 'C' ;
1622 const { pc, oct = 3 } = Note . get ( tonic ) ;
1723 const octaveOffset = Math . floor ( step / intervals . length ) ;
1824 const scaleStep = _mod ( step , intervals . length ) ;
@@ -160,16 +166,34 @@ export const scale = register('scale', function (scale, pat) {
160166 if ( Array . isArray ( scale ) ) {
161167 scale = scale . flat ( ) . join ( ' ' ) ;
162168 }
163- return pat . withHap ( ( hap ) => {
164- const isObject = typeof hap . value === 'object' ;
165- let note = isObject ? hap . value . n : hap . value ;
166- if ( isObject ) {
167- delete hap . value . n ; // remove n so it won't cause trouble
168- }
169- const asNumber = Number ( note ) ;
170- if ( ! isNaN ( asNumber ) ) {
171- note = scaleStep ( asNumber , scale ) ;
172- }
173- return hap . withValue ( ( ) => ( isObject ? { ...hap . value , note } : note ) ) . setContext ( { ...hap . context , scale } ) ;
174- } ) ;
169+ return (
170+ pat
171+ . fmap ( ( value ) => {
172+ const isObject = typeof value === 'object' ;
173+ let step = isObject ? value . n : value ;
174+ if ( isObject ) {
175+ delete value . n ; // remove n so it won't cause trouble
176+ }
177+ if ( isNote ( step ) ) {
178+ // legacy..
179+ return pure ( step ) ;
180+ }
181+ const asNumber = Number ( step ) ;
182+ if ( isNaN ( asNumber ) ) {
183+ logger ( `[tonal] invalid scale step "${ step } ", expected number` , 'error' ) ;
184+ return silence ;
185+ }
186+ try {
187+ const note = scaleStep ( asNumber , scale ) ;
188+ value = pure ( isObject ? { ...value , note } : note ) ;
189+ } catch ( err ) {
190+ logger ( `[tonal] ${ err . message } ` , 'error' ) ;
191+ value = silence ;
192+ }
193+ return value ;
194+ } )
195+ . outerJoin ( )
196+ // legacy:
197+ . withHap ( ( hap ) => hap . setContext ( { ...hap . context , scale } ) )
198+ ) ;
175199} ) ;
0 commit comments